You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by pt...@apache.org on 2019/08/06 11:17:10 UTC
[ignite] branch master updated: IGNITE-11985 .NET: Fix LINQ nulls
handling
This is an automated email from the ASF dual-hosted git repository.
ptupitsyn pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git
The following commit(s) were added to refs/heads/master by this push:
new b3e04fd IGNITE-11985 .NET: Fix LINQ nulls handling
b3e04fd is described below
commit b3e04fdad6e8a1cad3666a8ca58e05f0112fe4fb
Author: Pavel Tupitsyn <pt...@apache.org>
AuthorDate: Tue Aug 6 14:16:52 2019 +0300
IGNITE-11985 .NET: Fix LINQ nulls handling
Use SQL `IS [NOT] DISTINCT FROM` instead of `=` and `<>` to fix null comparisons in LINQ
---
.../Query/Linq/CacheLinqTest.CompiledQuery.cs | 65 +++++++++++++++++++++-
.../Cache/Query/Linq/CacheLinqTest.Functions.cs | 7 ++-
.../Impl/CacheQueryExpressionVisitor.cs | 22 ++------
3 files changed, 72 insertions(+), 22 deletions(-)
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.CompiledQuery.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.CompiledQuery.cs
index 1cd377e..75532be 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.CompiledQuery.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.CompiledQuery.cs
@@ -26,7 +26,10 @@
// ReSharper disable UnusedMemberInSuper.Global
namespace Apache.Ignite.Core.Tests.Cache.Query.Linq
{
+ using System;
using System.Linq;
+ using Apache.Ignite.Core.Cache;
+ using Apache.Ignite.Core.Cache.Query;
using Apache.Ignite.Linq;
using NUnit.Framework;
@@ -211,5 +214,65 @@ namespace Apache.Ignite.Core.Tests.Cache.Query.Linq
Assert.AreEqual(17, compiled2(18, 16, "ers", 13, 17).Single().Key);
}
+
+ [Test]
+ public void TestCompiledQueryStringEquals()
+ {
+ var persons = GetPersonCache().AsCacheQueryable();
+ var qry0 = CompiledQuery.Compile((string empName) => persons.Where(x => x.Value.Name == empName));
+ Assert.AreEqual(1, qry0(" Person_1 ").Count());
+ Assert.AreEqual(0, qry0(null).Count());
+ }
+
+ [Test]
+ public void TestCompiledQueryStringNotEquals()
+ {
+ var persons = GetPersonCache().AsCacheQueryable();
+ var qry0 = CompiledQuery.Compile((string empName) => persons.Where(x => x.Value.Name != empName));
+ Assert.AreEqual(PersonCount - 1, qry0(" Person_1 ").Count());
+ Assert.AreEqual(PersonCount, qry0(null).Count());
+ }
+
+ [Test]
+ public void TestCompiledQueryStringEqualsNull()
+ {
+ var roles = GetRoleCache().AsCacheQueryable();
+
+ var nullNameRoles = CompiledQuery.Compile((string roleName) => roles.Where(x => x.Value.Name == roleName));
+ Assert.AreEqual(1, nullNameRoles(null).Count());
+ }
+
+ [Test]
+ public void TestCompiledQueryStringNotEqualsNull()
+ {
+ var roles = GetRoleCache().AsCacheQueryable();
+
+ var nonNullNameRoles = CompiledQuery.Compile((string roleName) => roles.Where(x => x.Value.Name != roleName));
+ Assert.AreEqual(RoleCount - 1, nonNullNameRoles(null).Count());
+ }
+
+ [Test]
+ public void TestCompiledQueryStringEqualsFreeForm()
+ {
+ var persons = GetPersonCache().AsCacheQueryable().Where(emp => emp.Value.Name == "unused");
+ var compiledQuery = CompiledQuery.Compile(persons);
+
+ Func<string, IQueryCursor<ICacheEntry<int, Person>>> parametrizedCompiledQuery =
+ empName => compiledQuery(empName);
+
+ Assert.AreEqual(1, parametrizedCompiledQuery(" Person_0 ").Count());
+ }
+
+ [Test]
+ public void TestCompiledQueryStringNotEqualsFreeForm()
+ {
+ var persons = GetPersonCache().AsCacheQueryable().Where(emp => emp.Value.Name != "unused");
+ var compiledQuery = CompiledQuery.Compile(persons);
+
+ Func<string, IQueryCursor<ICacheEntry<int, Person>>> parametrizedCompiledQuery =
+ empName => compiledQuery(empName);
+
+ Assert.AreEqual(PersonCount - 1, parametrizedCompiledQuery(" Person_0 ").Count());
+ }
}
-}
\ No newline at end of file
+}
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Functions.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Functions.cs
index a6632d9..dd6b2ce 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Functions.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Query/Linq/CacheLinqTest.Functions.cs
@@ -55,8 +55,9 @@ namespace Apache.Ignite.Core.Tests.Cache.Query.Linq
Assert.AreEqual(key, cache.Where(x => -x.Key > -key).ToArray().Length);
Assert.AreEqual(1, GetRoleCache().AsCacheQueryable().Where(x => x.Key.Foo < 2).ToArray().Length);
- Assert.AreEqual(2, GetRoleCache().AsCacheQueryable().Where(x => x.Key.Bar > 2 && x.Value.Name != "11")
- .ToArray().Length);
+ var cacheEntries = GetRoleCache().AsCacheQueryable().Where(x => x.Key.Bar > 2 && x.Value.Name != "11")
+ .ToArray();
+ Assert.AreEqual(3, cacheEntries.Length);
}
/// <summary>
@@ -256,4 +257,4 @@ namespace Apache.Ignite.Core.Tests.Cache.Query.Linq
Assert.AreEqual(expectedIds, actualIds);
}
}
-}
\ No newline at end of file
+}
diff --git a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryExpressionVisitor.cs b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryExpressionVisitor.cs
index c5c887f..cc14260 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryExpressionVisitor.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Linq/Impl/CacheQueryExpressionVisitor.cs
@@ -182,31 +182,17 @@ namespace Apache.Ignite.Linq.Impl
{
case ExpressionType.Equal:
{
- var rightConst = expression.Right as ConstantExpression;
+ // Use `IS [NOT] DISTINCT FROM` for correct null comparison semantics.
+ // E.g. when user says `.Where(x => x == null)`, it should work, but with `=` it does not.
+ ResultBuilder.Append(" IS NOT DISTINCT FROM ");
- if (rightConst != null && rightConst.Value == null)
- {
- // Special case for nulls, since "= null" does not work in SQL
- ResultBuilder.Append(" is null)");
- return expression;
- }
-
- ResultBuilder.Append(" = ");
break;
}
case ExpressionType.NotEqual:
{
- var rightConst = expression.Right as ConstantExpression;
-
- if (rightConst != null && rightConst.Value == null)
- {
- // Special case for nulls, since "<> null" does not work in SQL
- ResultBuilder.Append(" is not null)");
- return expression;
- }
+ ResultBuilder.Append(" IS DISTINCT FROM ");
- ResultBuilder.Append(" <> ");
break;
}