From: <pa...@us...> - 2011-04-18 17:25:07
|
Revision: 5717 http://nhibernate.svn.sourceforge.net/nhibernate/?rev=5717&view=rev Author: patearl Date: 2011-04-18 17:25:00 +0000 (Mon, 18 Apr 2011) Log Message: ----------- Linq: More changes related to NH-2583. Use a stack to help eliminate member variable leakage. Handle stand-alone boolean member and method call expressions. Eliminate an earlier invalid attempted fix. 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-17 22:28:45 UTC (rev 5716) +++ trunk/nhibernate/src/NHibernate/Linq/Visitors/WhereJoinDetector.cs 2011-04-18 17:25:00 UTC (rev 5717) @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Linq.Expressions; using NHibernate.Linq.ReWriters; @@ -102,10 +103,10 @@ // The following is used for all *condition* traversal (but not *expressions* that are not conditions). // This is the "mapping" described in the text at NH-2583. - private Dictionary<string, int> _memberExpressionMapping = new Dictionary<string, int>(); + private Stack<Dictionary<string, int>> _memberExpressionMappings = new Stack<Dictionary<string, int>>(); // The following two are used for member expressions traversal. - private HashSet<string> _collectedPathMemberExpressionsInExpression = new HashSet<string>(); + private Stack<HashSet<string>> _collectedPathMemberExpressionsInExpressions = new Stack<HashSet<string>>(); private int _memberExpressionDepth = 0; internal @@ -122,16 +123,16 @@ if (expression.NodeType == ExpressionType.AndAlso && expression.Type == typeof(bool)) { // Case (a) from the text at NH-2583. + _memberExpressionMappings.Push(new Dictionary<string, int>()); var newLeft = VisitExpression(expression.Left); + var leftMapping = _memberExpressionMappings.Pop(); - var leftMapping = _memberExpressionMapping; - + _memberExpressionMappings.Push(new Dictionary<string, int>()); var newRight = VisitExpression(expression.Right); + var rightMapping = _memberExpressionMappings.Pop(); - var rightMapping = _memberExpressionMapping; + BinaryMapping(_memberExpressionMappings.Peek(), leftMapping, rightMapping, AND); - _memberExpressionMapping = 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); if (newLeft != expression.Left || newRight != expression.Right || newConversion != expression.Conversion) @@ -140,16 +141,16 @@ else if (expression.NodeType == ExpressionType.OrElse && expression.Type == typeof(bool)) { // Case (b) + _memberExpressionMappings.Push(new Dictionary<string, int>()); var newLeft = VisitExpression(expression.Left); + var leftMapping = _memberExpressionMappings.Pop(); - var leftMapping = _memberExpressionMapping; - + _memberExpressionMappings.Push(new Dictionary<string, int>()); var newRight = VisitExpression(expression.Right); + var rightMapping = _memberExpressionMappings.Pop(); - var rightMapping = _memberExpressionMapping; + BinaryMapping(_memberExpressionMappings.Peek(), leftMapping, rightMapping, OR); - _memberExpressionMapping = 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); if (newLeft != expression.Left || newRight != expression.Right || newConversion != expression.Conversion) @@ -164,63 +165,60 @@ || expression.NodeType == ExpressionType.GreaterThanOrEqual)) { // Cases (e), (f).2, (g).2 - _collectedPathMemberExpressionsInExpression = new HashSet<string>(); + _collectedPathMemberExpressionsInExpressions.Push(new HashSet<string>()); baseResult = base.VisitBinaryExpression(expression); - _memberExpressionMapping = FixedMapping(_collectedPathMemberExpressionsInExpression, N); + FixedMapping(_memberExpressionMappings.Peek(), _collectedPathMemberExpressionsInExpressions.Pop(), N); } else if (expression.Type == typeof(bool) && expression.NodeType == ExpressionType.NotEqual) { // Case (h) - _collectedPathMemberExpressionsInExpression = new HashSet<string>(); + _collectedPathMemberExpressionsInExpressions.Push(new HashSet<string>()); baseResult = base.VisitBinaryExpression(expression); - _memberExpressionMapping = FixedMapping(_collectedPathMemberExpressionsInExpression, F); + FixedMapping(_memberExpressionMappings.Peek(), _collectedPathMemberExpressionsInExpressions.Pop(), F); } else if (expression.Type == typeof(bool) && expression.NodeType == ExpressionType.Equal) { // Case (i) - _collectedPathMemberExpressionsInExpression = new HashSet<string>(); + _collectedPathMemberExpressionsInExpressions.Push(new HashSet<string>()); baseResult = base.VisitBinaryExpression(expression); - _memberExpressionMapping = FixedMapping(_collectedPathMemberExpressionsInExpression, T); + FixedMapping(_memberExpressionMappings.Peek(), _collectedPathMemberExpressionsInExpressions.Pop(), T); } else // +, * etc. { // Case (j) - _collectedPathMemberExpressionsInExpression = new HashSet<string>(); - baseResult = base.VisitBinaryExpression(expression); - - _memberExpressionMapping = FixedMapping(_collectedPathMemberExpressionsInExpression, TNF); } return baseResult; } - private static Dictionary<string, int> FixedMapping(IEnumerable<string> collectedPathMemberExpressionsInExpression, int value) + private static void FixedMapping(Dictionary<string, int> resultMapping, IEnumerable<string> collectedPathMemberExpressionsInExpression, int value) { - var memberExpressionMapping = new Dictionary<string, int>(); foreach (var me in collectedPathMemberExpressionsInExpression) { - memberExpressionMapping.Add(me, value); + // 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); } - return memberExpressionMapping; } - private static Dictionary<string, int> BinaryMapping(Dictionary<string, int> leftMapping, Dictionary<string, int> rightMapping, int[,] op) + private static void BinaryMapping(Dictionary<string, int> resultMapping, Dictionary<string, int> leftMapping, Dictionary<string, int> rightMapping, int[,] op) { - var result = new Dictionary<string, int>(); // 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) { - result.Add(lhs.Key, op[lhs.Value, rightMapping.ContainsKey(lhs.Key) ? rightMapping[lhs.Key] : TNF]); + 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. @@ -228,10 +226,9 @@ { if (!leftMapping.ContainsKey(rhs.Key)) { - result[rhs.Key] = op[rhs.Value, TNF]; + resultMapping[rhs.Key] = op[rhs.Value, TNF]; } } - return result; } private static bool IsNullConstantExpression(Expression expression) @@ -255,14 +252,13 @@ if (expression.NodeType == ExpressionType.Not && expression.Type == typeof(bool)) { // Case (c) from text at NH-2583. + _memberExpressionMappings.Push(new Dictionary<string, int>()); baseResult = VisitExpression(expression.Operand); + var opMapping = _memberExpressionMappings.Pop(); - var opMapping = _memberExpressionMapping; - _memberExpressionMapping = new Dictionary<string, int>(); - foreach (var m in opMapping) { - _memberExpressionMapping.Add(m.Key, NOT[m.Value]); + _memberExpressionMappings.Peek().Add(m.Key, NOT[m.Value]); } } else @@ -286,63 +282,98 @@ // return expression; //} - protected override Expression VisitMemberExpression(MemberExpression expression) + protected override Expression VisitMethodCallExpression(MethodCallExpression expression) { - ArgumentUtility.CheckNotNull("expression", 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 expected a boolean member expression."); + addOwnMemberExpressionMapping = true; + _collectedPathMemberExpressionsInExpressions.Push(new HashSet<string>()); + } - Expression newExpression; try { - _memberExpressionDepth++; - newExpression = base.VisitExpression(expression.Expression); + return base.VisitMethodCallExpression(expression); } finally { - _memberExpressionDepth--; + if (addOwnMemberExpressionMapping) + { + // Same mapping as the not-null (in)equality clause in VisitBinaryExpression. + FixedMapping(_memberExpressionMappings.Peek(), _collectedPathMemberExpressionsInExpressions.Pop(), N); + } } - bool isEntity = _isEntityDecider.IsEntity(expression.Type); - - if (isEntity) - { - // See (h) why we do not check for _memberExpressionDepth here! - _collectedPathMemberExpressionsInExpression.Add(ExpressionKeyVisitor.Visit(expression, null)); - } + } - if (_memberExpressionDepth > 0 && isEntity) + protected override Expression VisitMemberExpression(MemberExpression expression) + { + ArgumentUtility.CheckNotNull("expression", expression); + + // 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) { - return AddJoin(expression); + if (expression.Type != typeof(bool)) + throw new AssertionFailure("Was expected a boolean member expression."); + addOwnMemberExpressionMapping = true; + _collectedPathMemberExpressionsInExpressions.Push(new HashSet<string>()); } - else - { - if (newExpression != expression.Expression) - return Expression.MakeMemberAccess(newExpression, expression.Member); - return expression; - } - } - protected override Expression VisitConstantExpression(ConstantExpression expression) - { - if (expression.Type == typeof(bool)) + try { - if ((bool)expression.Value) + Expression newExpression; + try { - _memberExpressionMapping = FixedMapping(new string[0], T); + _memberExpressionDepth++; + newExpression = base.VisitExpression(expression.Expression); } + finally + { + _memberExpressionDepth--; + } + bool isEntity = _isEntityDecider.IsEntity(expression.Type); + + 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 { - _memberExpressionMapping = FixedMapping(new string[0], F); + if (newExpression != expression.Expression) + return Expression.MakeMemberAccess(newExpression, expression.Member); + return expression; } } - return expression; + finally + { + if (addOwnMemberExpressionMapping) + { + // Same mapping as the not-null (in)equality clause in VisitBinaryExpression. + FixedMapping(_memberExpressionMappings.Peek(), _collectedPathMemberExpressionsInExpressions.Pop(), N); + } + } } 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._memberExpressionMapping) + foreach (var mapping in f._memberExpressionMappings.Pop()) { // If outer join can never produce true, we can safely inner join. if ((mapping.Value & T) == 0) @@ -351,6 +382,5 @@ } } } - } } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <pa...@us...> - 2011-04-19 17:36:14
|
Revision: 5719 http://nhibernate.svn.sourceforge.net/nhibernate/?rev=5719&view=rev Author: patearl Date: 2011-04-19 17:36:06 +0000 (Tue, 19 Apr 2011) Log Message: ----------- Linq: Fixed typo in assertion error message. 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-18 21:24:23 UTC (rev 5718) +++ trunk/nhibernate/src/NHibernate/Linq/Visitors/WhereJoinDetector.cs 2011-04-19 17:36:06 UTC (rev 5719) @@ -289,7 +289,7 @@ if (_memberExpressionDepth == 0 && _collectedPathMemberExpressionsInExpressions.Count == 0) { if (expression.Type != typeof(bool)) - throw new AssertionFailure("Was expected a boolean member expression."); + throw new AssertionFailure("Was expecting a boolean member expression."); addOwnMemberExpressionMapping = true; _collectedPathMemberExpressionsInExpressions.Push(new HashSet<string>()); } @@ -319,7 +319,7 @@ if (_memberExpressionDepth == 0 && _collectedPathMemberExpressionsInExpressions.Count == 0) { if (expression.Type != typeof(bool)) - throw new AssertionFailure("Was expected a boolean member expression."); + throw new AssertionFailure("Was expecting a boolean member expression."); addOwnMemberExpressionMapping = true; _collectedPathMemberExpressionsInExpressions.Push(new HashSet<string>()); } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <pa...@us...> - 2011-04-21 03:42:21
|
Revision: 5731 http://nhibernate.svn.sourceforge.net/nhibernate/?rev=5731&view=rev Author: patearl Date: 2011-04-21 03:42:15 +0000 (Thu, 21 Apr 2011) Log Message: ----------- Linq: Handle methods that return true or false when provided with null. (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-20 21:59:24 UTC (rev 5730) +++ trunk/nhibernate/src/NHibernate/Linq/Visitors/WhereJoinDetector.cs 2011-04-21 03:42:15 UTC (rev 5731) @@ -302,8 +302,9 @@ { if (addOwnMemberExpressionMapping) { - // Same mapping as the not-null (in)equality clause in VisitBinaryExpression. - FixedMapping(_memberExpressionMappings.Peek(), _collectedPathMemberExpressionsInExpressions.Pop(), N); + // 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); } } } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
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. |