|
From: <pa...@us...> - 2010-11-25 03:34:12
|
Revision: 5269
http://nhibernate.svn.sourceforge.net/nhibernate/?rev=5269&view=rev
Author: patearl
Date: 2010-11-25 03:34:05 +0000 (Thu, 25 Nov 2010)
Log Message:
-----------
Utilize simple SQL equality in expressions that may involve null values. (NH-2402)
Fixed missing cases in Linq null (in)equality handling.
Fixed Linq query caching when the same variable alternates between null and non-null values. (part of NH-2397)
Modified Paths:
--------------
trunk/nhibernate/src/NHibernate/Linq/Visitors/ExpressionKeyVisitor.cs
trunk/nhibernate/src/NHibernate/Linq/Visitors/HqlGeneratorExpressionTreeVisitor.cs
trunk/nhibernate/src/NHibernate.Test/DbScripts/MsSql2008DialectLinqReadonlyCreateScript.sql
trunk/nhibernate/src/NHibernate.Test/Linq/Entities/AnotherEntity.cs
trunk/nhibernate/src/NHibernate.Test/Linq/Mappings/AnotherEntity.hbm.xml
trunk/nhibernate/src/NHibernate.Test/Linq/NorthwindDbCreator.cs
trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj
Modified: trunk/nhibernate/src/NHibernate/Linq/Visitors/ExpressionKeyVisitor.cs
===================================================================
--- trunk/nhibernate/src/NHibernate/Linq/Visitors/ExpressionKeyVisitor.cs 2010-11-21 17:36:14 UTC (rev 5268)
+++ trunk/nhibernate/src/NHibernate/Linq/Visitors/ExpressionKeyVisitor.cs 2010-11-25 03:34:05 UTC (rev 5269)
@@ -77,7 +77,11 @@
if (_constantToParameterMap.TryGetValue(expression, out param))
{
- _string.Append(param.Name);
+ // Nulls generate different query plans. X = variable generates a different query depending on if variable is null or not.
+ if (param.Value == null)
+ _string.Append("NULL");
+ else
+ _string.Append(param.Name);
}
else
{
Modified: trunk/nhibernate/src/NHibernate/Linq/Visitors/HqlGeneratorExpressionTreeVisitor.cs
===================================================================
--- trunk/nhibernate/src/NHibernate/Linq/Visitors/HqlGeneratorExpressionTreeVisitor.cs 2010-11-21 17:36:14 UTC (rev 5268)
+++ trunk/nhibernate/src/NHibernate/Linq/Visitors/HqlGeneratorExpressionTreeVisitor.cs 2010-11-25 03:34:05 UTC (rev 5269)
@@ -226,34 +226,22 @@
return _hqlTreeBuilder.Equality(lhs, rhs);
}
- // Also check for nullability
- if (expression.Left.Type.IsNullableOrReference() || expression.Right.Type.IsNullableOrReference())
+ // Check for nulls on left or right.
+ if (expression.Right is ConstantExpression
+ && expression.Right.Type.IsNullableOrReference()
+ && ((ConstantExpression)expression.Right).Value == null)
{
- // TODO - yuck. This clone is needed because the AST tree nodes are not immutable,
- // and sharing nodes between multiple branches will cause issues in the hqlSqlWalker phase -
- // a node, x, gets visited during the walk and updated to refer to a real property. Later in
- // the walk, x get revisited (since we copied it here), but now the type doesn't match what
- // the parser expects. So we can't share. Implementing Clone() on HqlTreeNode would be better
- // that doing a full visit of the Expression tree. Allowing shared nodes in the AST would be better
- // still, but might be more work
- var lhs2 = VisitExpression(expression.Left).AsExpression();
- var rhs2 = VisitExpression(expression.Right).AsExpression();
-
- if (expression.Right is ConstantExpression
- && expression.Right.Type.IsNullableOrReference()
- && ((ConstantExpression)expression.Right).Value == null)
- {
- return _hqlTreeBuilder.IsNull(lhs2);
- }
-
- return _hqlTreeBuilder.BooleanOr(
- _hqlTreeBuilder.BooleanAnd(
- _hqlTreeBuilder.IsNull(lhs),
- _hqlTreeBuilder.IsNull(rhs)),
- _hqlTreeBuilder.Equality(lhs2, rhs2)
- );
+ return _hqlTreeBuilder.IsNull(lhs);
}
+ if (expression.Left is ConstantExpression
+ && expression.Left.Type.IsNullableOrReference()
+ && ((ConstantExpression)expression.Left).Value == null)
+ {
+ return _hqlTreeBuilder.IsNull(rhs);
+ }
+
+ // Nothing was null, use standard equality.
return _hqlTreeBuilder.Equality(lhs, rhs);
case ExpressionType.NotEqual:
@@ -274,36 +262,24 @@
}
- // Also check for nullability
- if (expression.Left.Type.IsNullableOrReference() || expression.Right.Type.IsNullableOrReference())
+ // Check for nulls on left or right.
+ if (expression.Right is ConstantExpression
+ && expression.Right.Type.IsNullableOrReference()
+ && ((ConstantExpression)expression.Right).Value == null)
{
- var lhs2 = VisitExpression(expression.Left).AsExpression();
- var rhs2 = VisitExpression(expression.Right).AsExpression();
- var lhs3 = VisitExpression(expression.Left).AsExpression();
- var rhs3 = VisitExpression(expression.Right).AsExpression();
-
- if (expression.Right is ConstantExpression
- && expression.Right.Type.IsNullableOrReference()
- && ((ConstantExpression)expression.Right).Value == null)
- {
- return _hqlTreeBuilder.IsNotNull(lhs2);
- }
-
- return
- _hqlTreeBuilder.BooleanOr(
- _hqlTreeBuilder.BooleanOr(
- _hqlTreeBuilder.BooleanAnd(
- _hqlTreeBuilder.IsNull(lhs),
- _hqlTreeBuilder.IsNotNull(rhs)),
- _hqlTreeBuilder.BooleanAnd(
- _hqlTreeBuilder.IsNotNull(lhs2),
- _hqlTreeBuilder.IsNull(rhs2))
- ),
- _hqlTreeBuilder.Inequality(lhs3, rhs3));
+ return _hqlTreeBuilder.IsNotNull(lhs);
}
- return _hqlTreeBuilder.Inequality(lhs, rhs);
+ if (expression.Left is ConstantExpression
+ && expression.Left.Type.IsNullableOrReference()
+ && ((ConstantExpression)expression.Left).Value == null)
+ {
+ return _hqlTreeBuilder.IsNotNull(rhs);
+ }
+ // Nothing was null, use standard inequality.
+ return _hqlTreeBuilder.Inequality(lhs, rhs);
+
case ExpressionType.And:
return _hqlTreeBuilder.BitwiseAnd(lhs, rhs);
Modified: trunk/nhibernate/src/NHibernate.Test/DbScripts/MsSql2008DialectLinqReadonlyCreateScript.sql
===================================================================
(Binary files differ)
Modified: trunk/nhibernate/src/NHibernate.Test/Linq/Entities/AnotherEntity.cs
===================================================================
--- trunk/nhibernate/src/NHibernate.Test/Linq/Entities/AnotherEntity.cs 2010-11-21 17:36:14 UTC (rev 5268)
+++ trunk/nhibernate/src/NHibernate.Test/Linq/Entities/AnotherEntity.cs 2010-11-25 03:34:05 UTC (rev 5269)
@@ -4,5 +4,6 @@
{
public virtual int Id { get; set; }
public virtual string Output { get; set; }
+ public virtual string Input { get; set; }
}
}
\ No newline at end of file
Modified: trunk/nhibernate/src/NHibernate.Test/Linq/Mappings/AnotherEntity.hbm.xml
===================================================================
--- trunk/nhibernate/src/NHibernate.Test/Linq/Mappings/AnotherEntity.hbm.xml 2010-11-21 17:36:14 UTC (rev 5268)
+++ trunk/nhibernate/src/NHibernate.Test/Linq/Mappings/AnotherEntity.hbm.xml 2010-11-25 03:34:05 UTC (rev 5269)
@@ -5,5 +5,6 @@
<generator class="native" />
</id>
<property name="Output" />
+ <property name="Input" />
</class>
</hibernate-mapping>
Modified: trunk/nhibernate/src/NHibernate.Test/Linq/NorthwindDbCreator.cs
===================================================================
--- trunk/nhibernate/src/NHibernate.Test/Linq/NorthwindDbCreator.cs 2010-11-21 17:36:14 UTC (rev 5268)
+++ trunk/nhibernate/src/NHibernate.Test/Linq/NorthwindDbCreator.cs 2010-11-25 03:34:05 UTC (rev 5269)
@@ -20,7 +20,7 @@
IsActive = true,
Entity = new AnotherEntity()
{
- Output = "this is output..."
+ Output = "output"
}
},
new Role()
@@ -143,6 +143,16 @@
animals[1].Children = new[] { animals[5] }.ToList();
+ List<AnotherEntity> otherEntities = new List<AnotherEntity>();
+ // AnotherEntity with only Output set is created above.
+ otherEntities.Add(new AnotherEntity { Input = "input" });
+ otherEntities.Add(new AnotherEntity { Input = "i/o", Output = "i/o" });
+ otherEntities.Add(new AnotherEntity()); // Input and Output both null.
+ otherEntities.Add(new AnotherEntity { Input = "input", Output = "output" });
+
+ foreach (AnotherEntity otherEntity in otherEntities)
+ session.Save(otherEntity);
+
foreach (Role role in roles)
session.Save(role);
Modified: trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj
===================================================================
--- trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj 2010-11-21 17:36:14 UTC (rev 5268)
+++ trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj 2010-11-25 03:34:05 UTC (rev 5269)
@@ -448,6 +448,7 @@
<Compile Include="Linq\MethodCallTests.cs" />
<Compile Include="Linq\MiscellaneousTextFixture.cs" />
<Compile Include="Linq\NorthwindDbCreator.cs" />
+ <Compile Include="Linq\NullComparisonTests.cs" />
<Compile Include="Linq\ObjectDumper.cs" />
<Compile Include="Linq\OrderByTests.cs" />
<Compile Include="Linq\PagingTests.cs" />
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|