|
From: <pa...@us...> - 2011-04-21 05:13:49
|
Revision: 5732
http://nhibernate.svn.sourceforge.net/nhibernate/?rev=5732&view=rev
Author: patearl
Date: 2011-04-21 05:13:43 +0000 (Thu, 21 Apr 2011)
Log Message:
-----------
Linq: Handle methods that return true or false when provided with null. Significant code simplification. (NH-2583 again)
Modified Paths:
--------------
trunk/nhibernate/src/NHibernate/Linq/Visitors/WhereJoinDetector.cs
Modified: trunk/nhibernate/src/NHibernate/Linq/Visitors/WhereJoinDetector.cs
===================================================================
--- trunk/nhibernate/src/NHibernate/Linq/Visitors/WhereJoinDetector.cs 2011-04-21 03:42:15 UTC (rev 5731)
+++ trunk/nhibernate/src/NHibernate/Linq/Visitors/WhereJoinDetector.cs 2011-04-21 05:13:43 UTC (rev 5732)
@@ -1,3 +1,4 @@
+// FIXME - Are there other things that can convert N into T? What about the ?: operator?
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
@@ -28,6 +29,8 @@
private static readonly int[,] AND = new int[8, 8];
private static readonly int[,] OR = new int[8, 8];
private static readonly int[] NOT = new int[8];
+ private static readonly int[] ISNULL = new int[8];
+ private static readonly int[] ISNOTNULL = new int[8];
/// <summary>
/// Setup of <see cref="AND"/>, <see cref="OR"/>, <see cref="NOT"/>.
@@ -38,6 +41,12 @@
NOT[T] = F;
NOT[N] = N;
NOT[F] = T;
+ ISNULL[T] = F;
+ ISNULL[N] = T;
+ ISNULL[F] = F;
+ ISNOTNULL[T] = T;
+ ISNOTNULL[N] = F;
+ ISNOTNULL[F] = T;
foreach (var p in new[] { T, N, F })
{
@@ -66,15 +75,21 @@
foreach (var p in allValues)
{
int[] splitP = split[p];
- // We only need to compute NOT for compound values.
+ // We only need to compute unary operations for compound values.
if (splitP.Length > 1)
{
int notResult = 0;
+ int isNullResult = 0;
+ int isNotNullResult = 0;
foreach (var p0 in splitP)
{
notResult |= NOT[p0];
+ isNullResult |= ISNULL[p0];
+ isNotNullResult |= ISNOTNULL[p0];
}
NOT[p] = notResult;
+ ISNULL[p] = isNullResult;
+ ISNOTNULL[p] = isNotNullResult;
}
foreach (var q in allValues)
{
@@ -106,7 +121,6 @@
private Stack<Dictionary<string, int>> _memberExpressionMappings = new Stack<Dictionary<string, int>>();
// The following two are used for member expressions traversal.
- private Stack<HashSet<string>> _collectedPathMemberExpressionsInExpressions = new Stack<HashSet<string>>();
private int _memberExpressionDepth = 0;
internal
@@ -115,6 +129,24 @@
{
}
+ internal static void Find(Expression expression, NameGenerator nameGenerator, IIsEntityDecider isEntityDecider, Dictionary<string, NhJoinClause> joins, Dictionary<MemberExpression, QuerySourceReferenceExpression> expressionMap)
+ {
+ WhereJoinDetector f = new WhereJoinDetector(nameGenerator, isEntityDecider, joins, expressionMap);
+
+ f._memberExpressionMappings.Push(new Dictionary<string, int>());
+
+ f.VisitExpression(expression);
+
+ foreach (var mapping in f._memberExpressionMappings.Pop())
+ {
+ // If outer join can never produce true, we can safely inner join.
+ if ((mapping.Value & T) == 0)
+ {
+ f.MakeInnerIfJoined(mapping.Key);
+ }
+ }
+ }
+
protected override Expression VisitBinaryExpression(BinaryExpression expression)
{
ArgumentUtility.CheckNotNull("expression", expression);
@@ -131,7 +163,7 @@
var newRight = VisitExpression(expression.Right);
var rightMapping = _memberExpressionMappings.Pop();
- BinaryMapping(_memberExpressionMappings.Peek(), leftMapping, rightMapping, AND);
+ BinaryMapping(leftMapping, rightMapping, AND);
// The following is copy-pasted from Relinq's visitor, as I had to split the code above.
var newConversion = (LambdaExpression)VisitExpression(expression.Conversion);
@@ -149,7 +181,7 @@
var newRight = VisitExpression(expression.Right);
var rightMapping = _memberExpressionMappings.Pop();
- BinaryMapping(_memberExpressionMappings.Peek(), leftMapping, rightMapping, OR);
+ BinaryMapping(leftMapping, rightMapping, OR);
// Again, the following is copy-pasted from Relinq's visitor, as I had to split the code above.
var newConversion = (LambdaExpression)VisitExpression(expression.Conversion);
@@ -157,39 +189,22 @@
baseResult = Expression.MakeBinary(expression.NodeType, newLeft, newRight, expression.IsLiftedToNull, expression.Method, newConversion);
}
else if (expression.Type == typeof(bool)
- && (expression.NodeType == ExpressionType.Equal && !IsNullConstantExpression(expression.Right) && !IsNullConstantExpression(expression.Left)
- || expression.NodeType == ExpressionType.NotEqual && !IsNullConstantExpression(expression.Right) && !IsNullConstantExpression(expression.Left)
- || expression.NodeType == ExpressionType.LessThan
- || expression.NodeType == ExpressionType.LessThanOrEqual
- || expression.NodeType == ExpressionType.GreaterThan
- || expression.NodeType == ExpressionType.GreaterThanOrEqual))
+ && expression.NodeType == ExpressionType.NotEqual
+ && (IsNullConstantExpression(expression.Right) || IsNullConstantExpression(expression.Left)))
{
- // Cases (e), (f).2, (g).2
- _collectedPathMemberExpressionsInExpressions.Push(new HashSet<string>());
-
- baseResult = base.VisitBinaryExpression(expression);
-
- FixedMapping(_memberExpressionMappings.Peek(), _collectedPathMemberExpressionsInExpressions.Pop(), N);
- }
- else if (expression.Type == typeof(bool)
- && expression.NodeType == ExpressionType.NotEqual)
- {
// Case (h)
- _collectedPathMemberExpressionsInExpressions.Push(new HashSet<string>());
-
+ _memberExpressionMappings.Push(new Dictionary<string, int>());
baseResult = base.VisitBinaryExpression(expression);
-
- FixedMapping(_memberExpressionMappings.Peek(), _collectedPathMemberExpressionsInExpressions.Pop(), F);
+ UnaryMapping(_memberExpressionMappings.Pop(), ISNOTNULL);
}
else if (expression.Type == typeof(bool)
- && expression.NodeType == ExpressionType.Equal)
+ && expression.NodeType == ExpressionType.Equal
+ && (IsNullConstantExpression(expression.Right) || IsNullConstantExpression(expression.Left)))
{
// Case (i)
- _collectedPathMemberExpressionsInExpressions.Push(new HashSet<string>());
-
+ _memberExpressionMappings.Push(new Dictionary<string, int>());
baseResult = base.VisitBinaryExpression(expression);
-
- FixedMapping(_memberExpressionMappings.Peek(), _collectedPathMemberExpressionsInExpressions.Pop(), T);
+ UnaryMapping(_memberExpressionMappings.Pop(), ISNULL);
}
else // +, * etc.
{
@@ -199,51 +214,6 @@
return baseResult;
}
- private static void FixedMapping(Dictionary<string, int> resultMapping, IEnumerable<string> collectedPathMemberExpressionsInExpression, int value)
- {
- foreach (var me in collectedPathMemberExpressionsInExpression)
- {
- // This ORing behavior was added without much thought to fix NHibernate.Test.NHSpecificTest.NH2378.Fixture.ShortEntityCanBeQueryCorrectlyUsingLinqProvider.
- if (resultMapping.ContainsKey(me))
- resultMapping[me] |= value;
- else
- resultMapping.Add(me, value);
- }
- }
-
- private static void BinaryMapping(Dictionary<string, int> resultMapping, Dictionary<string, int> leftMapping, Dictionary<string, int> rightMapping, int[,] op)
- {
- // Compute mapping for all member expressions in leftMapping. If the member expression is missing
- // in rightMapping, use TNF as a "pessimistic approximation" instead (inside the ?: operator). See
- // the text for an explanation of this.
- foreach (var lhs in leftMapping)
- {
- resultMapping.Add(lhs.Key, op[lhs.Value, rightMapping.ContainsKey(lhs.Key) ? rightMapping[lhs.Key] : TNF]);
- }
- // Compute mapping for all member expressions *only* in rightMapping (we did the common ones above).
- // Again, use TNF as pessimistic approximation to result of left subcondition.
- foreach (var rhs in rightMapping)
- {
- if (!leftMapping.ContainsKey(rhs.Key))
- {
- resultMapping[rhs.Key] = op[rhs.Value, TNF];
- }
- }
- }
-
- private static bool IsNullConstantExpression(Expression expression)
- {
- if (expression is ConstantExpression)
- {
- var constant = (ConstantExpression)expression;
- return constant.Value == null;
- }
- else
- {
- return false;
- }
- }
-
protected override Expression VisitUnaryExpression(UnaryExpression expression)
{
ArgumentUtility.CheckNotNull("expression", expression);
@@ -254,12 +224,7 @@
// Case (c) from text at NH-2583.
_memberExpressionMappings.Push(new Dictionary<string, int>());
baseResult = VisitExpression(expression.Operand);
- var opMapping = _memberExpressionMappings.Pop();
-
- foreach (var m in opMapping)
- {
- _memberExpressionMappings.Peek().Add(m.Key, NOT[m.Value]);
- }
+ UnaryMapping(_memberExpressionMappings.Pop(), NOT);
}
else
{
@@ -284,104 +249,105 @@
protected override Expression VisitMethodCallExpression(MethodCallExpression expression)
{
- // Similar logic to VisitMemberExpression, but for handling boolean method results instead of boolean members.
- bool addOwnMemberExpressionMapping = false;
- if (_memberExpressionDepth == 0 && _collectedPathMemberExpressionsInExpressions.Count == 0)
- {
- if (expression.Type != typeof(bool))
- throw new AssertionFailure("Was expecting a boolean member expression.");
- addOwnMemberExpressionMapping = true;
- _collectedPathMemberExpressionsInExpressions.Push(new HashSet<string>());
- }
+ _memberExpressionMappings.Push(new Dictionary<string, int>());
+ Expression result = base.VisitMethodCallExpression(expression);
+ // We would usually get NULL if one of our inner member expresions was null. (Mapped to N)
+ // However, it's possible a method call will convert the null value from the failed join into any one of True, False, or Null. (Mapped to TNF)
+ // This could be optimized by actually checking what the method does. For example StartsWith("s") would leave null as null and would still allow us to inner join.
+ FixedMapping(_memberExpressionMappings.Pop(), TNF);
+ return result;
+ }
+ protected override Expression VisitMemberExpression(MemberExpression expression)
+ {
+ ArgumentUtility.CheckNotNull("expression", expression);
+
+ Expression newExpression;
try
{
- return base.VisitMethodCallExpression(expression);
+ _memberExpressionDepth++;
+ newExpression = base.VisitExpression(expression.Expression);
}
finally
{
- if (addOwnMemberExpressionMapping)
- {
- // We would often get the same mapping as the not-null (in)equality clause in VisitBinaryExpression.
- // However, it's possible a method call will convert the null value from the failed join into any one of True, False, or Null.
- FixedMapping(_memberExpressionMappings.Peek(), _collectedPathMemberExpressionsInExpressions.Pop(), TNF);
- }
+ _memberExpressionDepth--;
}
- }
+ bool isEntity = _isEntityDecider.IsEntity(expression.Type);
- protected override Expression VisitMemberExpression(MemberExpression expression)
- {
- ArgumentUtility.CheckNotNull("expression", expression);
+ if (isEntity)
+ {
+ // See (h) why we do not check for _memberExpressionDepth here!
+ AddPossibility(ExpressionKeyVisitor.Visit(expression, null), N);
+ }
- // For the boolean part of expressions like: a => a.B.C == 1 && a.D.E
- // There wouldn't have been a parent operator to collect paths for a.D.E.
- // Below we add the same mapping as if we were doing a.D.E == true.
- bool addOwnMemberExpressionMapping = false;
- if (_memberExpressionDepth == 0 && _collectedPathMemberExpressionsInExpressions.Count == 0)
+ if (_memberExpressionDepth > 0 && isEntity)
{
- if (expression.Type != typeof(bool))
- throw new AssertionFailure("Was expecting a boolean member expression.");
- addOwnMemberExpressionMapping = true;
- _collectedPathMemberExpressionsInExpressions.Push(new HashSet<string>());
+ return AddJoin(expression);
}
+ else
+ {
+ if (newExpression != expression.Expression)
+ return Expression.MakeMemberAccess(newExpression, expression.Member);
+ return expression;
+ }
+ }
- try
+ private void FixedMapping(Dictionary<string, int> sourceMapping, int value)
+ {
+ foreach (var me in sourceMapping.Keys)
{
- Expression newExpression;
- try
- {
- _memberExpressionDepth++;
- newExpression = base.VisitExpression(expression.Expression);
- }
- finally
- {
- _memberExpressionDepth--;
- }
- bool isEntity = _isEntityDecider.IsEntity(expression.Type);
+ AddPossibility(me, value);
+ }
+ }
- if (isEntity)
- {
- // See (h) why we do not check for _memberExpressionDepth here!
- _collectedPathMemberExpressionsInExpressions.Peek().Add(ExpressionKeyVisitor.Visit(expression, null));
- }
-
- if (_memberExpressionDepth > 0 && isEntity)
- {
- return AddJoin(expression);
- }
- else
- {
- if (newExpression != expression.Expression)
- return Expression.MakeMemberAccess(newExpression, expression.Member);
- return expression;
- }
+ private void BinaryMapping(Dictionary<string, int> leftMapping, Dictionary<string, int> rightMapping, int[,] op)
+ {
+ // Compute mapping for all member expressions in leftMapping. If the member expression is missing
+ // in rightMapping, use TNF as a "pessimistic approximation" instead (inside the ?: operator). See
+ // the text for an explanation of this.
+ foreach (var lhs in leftMapping)
+ {
+ AddPossibility(lhs.Key, op[lhs.Value, rightMapping.ContainsKey(lhs.Key) ? rightMapping[lhs.Key] : TNF]);
}
- finally
+ // Compute mapping for all member expressions *only* in rightMapping (we did the common ones above).
+ // Again, use TNF as pessimistic approximation to result of left subcondition.
+ foreach (var rhs in rightMapping)
{
- if (addOwnMemberExpressionMapping)
+ if (!leftMapping.ContainsKey(rhs.Key))
{
- // Same mapping as the not-null (in)equality clause in VisitBinaryExpression.
- FixedMapping(_memberExpressionMappings.Peek(), _collectedPathMemberExpressionsInExpressions.Pop(), N);
+ AddPossibility(rhs.Key, op[rhs.Value, TNF]);
}
}
}
- internal static void Find(Expression expression, NameGenerator nameGenerator, IIsEntityDecider isEntityDecider, Dictionary<string, NhJoinClause> joins, Dictionary<MemberExpression, QuerySourceReferenceExpression> expressionMap)
+ private void UnaryMapping(Dictionary<string, int> sourceMapping, int[] op)
{
- WhereJoinDetector f = new WhereJoinDetector(nameGenerator, isEntityDecider, joins, expressionMap);
+ foreach (var item in sourceMapping)
+ {
+ AddPossibility(item.Key, op[item.Value]);
+ }
+ }
- f._memberExpressionMappings.Push(new Dictionary<string, int>());
-
- f.VisitExpression(expression);
-
- foreach (var mapping in f._memberExpressionMappings.Pop())
+ private static bool IsNullConstantExpression(Expression expression)
+ {
+ if (expression is ConstantExpression)
{
- // If outer join can never produce true, we can safely inner join.
- if ((mapping.Value & T) == 0)
- {
- f.MakeInnerIfJoined(mapping.Key);
- }
+ var constant = (ConstantExpression)expression;
+ return constant.Value == null;
}
+ else
+ {
+ return false;
+ }
}
+
+ private void AddPossibility(string memberPath, int value)
+ {
+ Dictionary<string, int> mapping = _memberExpressionMappings.Peek();
+ if (mapping.ContainsKey(memberPath))
+ mapping[memberPath] |= value;
+ else
+ mapping[memberPath] = value;
+ }
}
}
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|