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 2022/12/01 19:29:12 UTC
[ignite-3] branch main updated: IGNITE-18131 .NET: LINQ: Improve Distinct support (#1401)
This is an automated email from the ASF dual-hosted git repository.
ptupitsyn pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git
The following commit(s) were added to refs/heads/main by this push:
new e891461401 IGNITE-18131 .NET: LINQ: Improve Distinct support (#1401)
e891461401 is described below
commit e89146140165fe782eb2f248267e1e0ba13514fa
Author: Pavel Tupitsyn <pt...@apache.org>
AuthorDate: Thu Dec 1 22:29:07 2022 +0300
IGNITE-18131 .NET: LINQ: Improve Distinct support (#1401)
* Use column alias when projecting a complex expression into a new type to support some `.Distinct()` use cases.
* Add more tests.
---
.../Linq/LinqSqlGenerationTests.KvView.cs | 2 +-
.../Linq/LinqSqlGenerationTests.cs | 13 ++--
.../Linq/LinqTests.Aggregate.cs | 2 +-
.../Apache.Ignite.Tests/Linq/LinqTests.Cast.cs | 4 +-
.../Apache.Ignite.Tests/Linq/LinqTests.GroupBy.cs | 6 +-
.../Linq/LinqTests.UnionIntersectExcept.cs | 2 +-
.../dotnet/Apache.Ignite.Tests/Linq/LinqTests.cs | 85 ++++++++++++++++++++++
.../Internal/Linq/IgniteQueryExpressionVisitor.cs | 53 +++++++-------
.../Internal/Linq/IgniteQueryModelVisitor.cs | 2 +-
9 files changed, 126 insertions(+), 43 deletions(-)
diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqSqlGenerationTests.KvView.cs b/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqSqlGenerationTests.KvView.cs
index 4f5f800623..aba2deb438 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqSqlGenerationTests.KvView.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqSqlGenerationTests.KvView.cs
@@ -42,7 +42,7 @@ public partial class LinqSqlGenerationTests
[Test]
public void TestSelectTwoColumnsKv() =>
AssertSqlKv(
- "select (_T0.KEY + ?), _T0.VAL from PUBLIC.tbl1 as _T0",
+ "select (_T0.KEY + ?) as KEY, _T0.VAL from PUBLIC.tbl1 as _T0",
q => q.Select(x => new { Key = x.Key.Key + 1, x.Value.Val }).ToList());
[Test]
diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqSqlGenerationTests.cs b/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqSqlGenerationTests.cs
index 0c178937d3..4d63da2b12 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqSqlGenerationTests.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqSqlGenerationTests.cs
@@ -101,7 +101,7 @@ public partial class LinqSqlGenerationTests
[Test]
public void TestSelectOrderByOffsetLimit() =>
AssertSql(
- "select _T0.KEY, _T0.VAL, (_T0.KEY + ?) " +
+ "select _T0.KEY, _T0.VAL, (_T0.KEY + ?) as KEY2 " +
"from PUBLIC.tbl1 as _T0 " +
"order by ((_T0.KEY + ?)) asc, (_T0.VAL) desc " +
"limit ? offset ?",
@@ -149,10 +149,9 @@ public partial class LinqSqlGenerationTests
}
[Test]
- [Ignore("IGNITE-18131 Distinct support")]
public void TestSelectOrderDistinct() =>
AssertSql(
- "select distinct _T0.KEY, (_T0.KEY + ?) from PUBLIC.tbl1 as _T0 order by ((_T0.KEY + ?)) asc",
+ "select * from (select distinct _T0.KEY, (_T0.KEY + ?) as KEY2 from PUBLIC.tbl1 as _T0) as _T1 order by (_T1.KEY2) asc",
q => q.Select(x => new { x.Key, Key2 = x.Key + 1})
.Distinct()
.OrderBy(x => x.Key2)
@@ -246,8 +245,8 @@ public partial class LinqSqlGenerationTests
[Test]
public void TestUnion() =>
AssertSql(
- "select (_T0.KEY + ?), _T0.VAL from PUBLIC.tbl1 as _T0 " +
- "union (select (_T1.KEY + ?), _T1.VAL from PUBLIC.tbl1 as _T1)",
+ "select (_T0.KEY + ?) as KEY, _T0.VAL from PUBLIC.tbl1 as _T0 " +
+ "union (select (_T1.KEY + ?) as KEY, _T1.VAL from PUBLIC.tbl1 as _T1)",
q => q.Select(x => new { Key = x.Key + 1, x.Val })
.Union(q.Select(x => new { Key = x.Key + 100, x.Val }))
.ToList());
@@ -255,8 +254,8 @@ public partial class LinqSqlGenerationTests
[Test]
public void TestIntersect() =>
AssertSql(
- "select (_T0.KEY + ?), concat(_T0.VAL, ?) from PUBLIC.tbl1 as _T0 " +
- "intersect (select (_T1.KEY + ?), concat(_T1.VAL, ?) from PUBLIC.tbl1 as _T1)",
+ "select (_T0.KEY + ?) as KEY, concat(_T0.VAL, ?) as VAL from PUBLIC.tbl1 as _T0 " +
+ "intersect (select (_T1.KEY + ?) as KEY, concat(_T1.VAL, ?) as VAL from PUBLIC.tbl1 as _T1)",
q => q.Select(x => new { Key = x.Key + 1, Val = x.Val + "_" })
.Intersect(q.Select(x => new { Key = x.Key + 100, Val = x.Val + "!" }))
.ToList());
diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqTests.Aggregate.cs b/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqTests.Aggregate.cs
index e186ceac80..4912bfdd93 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqTests.Aggregate.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqTests.Aggregate.cs
@@ -110,7 +110,7 @@ public partial class LinqTests
Assert.AreEqual(2, res[2].Max);
StringAssert.Contains(
- "select _T0.KEY, count(*), sum(_T0.KEY), avg(_T0.KEY), min(_T0.KEY), max(_T0.KEY) " +
+ "select _T0.KEY, count(*) as COUNT, sum(_T0.KEY) as SUM, avg(_T0.KEY) as AVG, min(_T0.KEY) as MIN, max(_T0.KEY) as MAX " +
"from PUBLIC.TBL_INT32 as _T0 " +
"group by (_T0.KEY) " +
"order by (_T0.KEY) asc",
diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqTests.Cast.cs b/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqTests.Cast.cs
index f539304012..63a2078995 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqTests.Cast.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqTests.Cast.cs
@@ -49,8 +49,8 @@ public partial class LinqTests
Assert.AreEqual(900d / 2000, res[0].Double);
StringAssert.Contains(
- "select cast(_T0.VAL as tinyint), cast(_T0.VAL as smallint), cast(_T0.VAL as bigint), " +
- "(cast(_T0.VAL as real) / ?), (cast(_T0.VAL as double) / ?) " +
+ "select cast(_T0.VAL as tinyint) as BYTE, cast(_T0.VAL as smallint) as SHORT, cast(_T0.VAL as bigint) as LONG, " +
+ "(cast(_T0.VAL as real) / ?) as FLOAT, (cast(_T0.VAL as double) / ?) as DOUBLE " +
"from PUBLIC.TBL_INT32 as _T0 " +
"order by (cast(_T0.VAL as bigint)) desc",
query.ToString());
diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqTests.GroupBy.cs b/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqTests.GroupBy.cs
index 7ae5615831..16f002c35e 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqTests.GroupBy.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqTests.GroupBy.cs
@@ -83,7 +83,7 @@ public partial class LinqTests
Assert.AreEqual(4.0d, res[1].Avg);
StringAssert.Contains(
- "select _T0.VAL, count(*), sum(cast(_T0.KEY as int)), avg(cast(_T0.KEY as int)) " +
+ "select _T0.VAL, count(*) as COUNT, sum(cast(_T0.KEY as int)) as SUM, avg(cast(_T0.KEY as int)) as AVG " +
"from PUBLIC.TBL_INT8 as _T0 " +
"group by (_T0.VAL) " +
"order by (_T0.VAL) asc",
@@ -140,7 +140,7 @@ public partial class LinqTests
Assert.AreEqual(10, res.Count);
StringAssert.Contains(
- "select _T0.VAL, count(*) " +
+ "select _T0.VAL, count(*) as COUNT " +
"from PUBLIC.TBL1 as _T1 " +
"inner join PUBLIC.TBL_INT32 as _T0 on (cast(_T0.KEY as bigint) = _T1.KEY) " +
"group by (_T0.VAL) " +
@@ -179,7 +179,7 @@ public partial class LinqTests
Assert.AreEqual(900, res[0].MaxPrice);
StringAssert.Contains(
- "select _T0.VAL, max(_T1.VAL) " +
+ "select _T0.VAL, max(_T1.VAL) as MAXPRICE " +
"from PUBLIC.TBL1 as _T0 " +
"inner join PUBLIC.TBL_INT32 as _T1 on (cast(_T1.KEY as bigint) = _T0.KEY) " +
"group by (_T0.VAL) " +
diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqTests.UnionIntersectExcept.cs b/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqTests.UnionIntersectExcept.cs
index f6bc7a9be2..ad1772cebd 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqTests.UnionIntersectExcept.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqTests.UnionIntersectExcept.cs
@@ -89,7 +89,7 @@ public partial class LinqTests
CollectionAssert.AreEquivalent(new[] { 4, 9 }, res.Select(x => x.Key));
StringAssert.Contains(
- "select cast(_T0.KEY as int) " +
+ "select cast(_T0.KEY as int) as KEY " +
"from PUBLIC.TBL_INT8 as _T0 " +
"where (cast(_T0.KEY as int) > ?) " +
"union (select _T1.KEY from PUBLIC.TBL_INT32 as _T1 where ((_T1.KEY > ?) and (_T1.KEY < ?)))",
diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqTests.cs b/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqTests.cs
index 379e5bcddc..8176c3995f 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqTests.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/Linq/LinqTests.cs
@@ -288,6 +288,7 @@ public partial class LinqTests : IgniteTestsBase
}
[Test]
+ [Ignore("IGNITE-18311")]
public void TestOrderBySkipTakeBeforeSelect()
{
var query = PocoView.AsQueryable()
@@ -327,6 +328,90 @@ public partial class LinqTests : IgniteTestsBase
query.ToString());
}
+ [Test]
+ public void TestDistinctOneField()
+ {
+ var query = PocoByteView.AsQueryable()
+ .Select(x => x.Val)
+ .Distinct();
+
+ List<sbyte> res = query.ToList();
+
+ CollectionAssert.AreEquivalent(new[] { 0, 1, 2, 3 }, res);
+
+ StringAssert.Contains("select distinct _T0.VAL from PUBLIC.TBL_INT8 as _T0", query.ToString());
+ }
+
+ [Test]
+ public void TestDistinctEntireObject()
+ {
+ var query = PocoByteView.AsQueryable()
+ .Distinct();
+
+ List<PocoByte> res = query.ToList();
+
+ Assert.AreEqual(10, res.Count);
+
+ StringAssert.Contains("select distinct _T0.KEY, _T0.VAL from PUBLIC.TBL_INT8 as _T0", query.ToString());
+ }
+
+ [Test]
+ public void TestDistinctProjection()
+ {
+ var query = PocoByteView.AsQueryable()
+ .Select(x => new { Id = x.Val + 10, V = x.Val })
+ .Distinct();
+
+ var res = query.ToList();
+
+ Assert.AreEqual(4, res.Count);
+
+ StringAssert.Contains(
+ "select distinct (cast(_T0.VAL as int) + ?) as ID, _T0.VAL " +
+ "from PUBLIC.TBL_INT8 as _T0",
+ query.ToString());
+ }
+
+ [Test]
+ public void TestDistinctAfterOrderBy()
+ {
+ var query = PocoByteView.AsQueryable()
+ .Select(x => new { Id = x.Val + 10, V = x.Val })
+ .OrderByDescending(x => x.V)
+ .Distinct();
+
+ var res = query.ToList();
+
+ Assert.AreEqual(4, res.Count);
+ Assert.AreEqual(13, res[0].Id);
+
+ StringAssert.Contains(
+ "select distinct (cast(_T0.VAL as int) + ?) as ID, _T0.VAL " +
+ "from PUBLIC.TBL_INT8 as _T0 " +
+ "order by (_T0.VAL) desc",
+ query.ToString());
+ }
+
+ [Test]
+ public void TestDistinctBeforeOrderBy()
+ {
+ var query = PocoByteView.AsQueryable()
+ .Select(x => new { Id = x.Val + 10, V = x.Val })
+ .Distinct()
+ .OrderByDescending(x => x.Id);
+
+ var res = query.ToList();
+
+ Assert.AreEqual(4, res.Count);
+ Assert.AreEqual(13, res[0].Id);
+
+ StringAssert.Contains(
+ "select * from " +
+ "(select distinct (cast(_T0.VAL as int) + ?) as ID, _T0.VAL from PUBLIC.TBL_INT8 as _T0) as _T1 " +
+ "order by (_T1.ID) desc",
+ query.ToString());
+ }
+
[Test]
public void TestCustomColumnNameMapping()
{
diff --git a/modules/platforms/dotnet/Apache.Ignite/Internal/Linq/IgniteQueryExpressionVisitor.cs b/modules/platforms/dotnet/Apache.Ignite/Internal/Linq/IgniteQueryExpressionVisitor.cs
index 1e033cedda..1d2d413984 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/Linq/IgniteQueryExpressionVisitor.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Linq/IgniteQueryExpressionVisitor.cs
@@ -302,7 +302,32 @@ internal sealed class IgniteQueryExpressionVisitor : ThrowingExpressionVisitor
/** <inheritdoc /> */
protected override Expression VisitNew(NewExpression expression)
{
- VisitArguments(expression.Arguments);
+ var first = true;
+
+ for (var i = 0; i < expression.Arguments.Count; i++)
+ {
+ var arg = expression.Arguments[i];
+ if (!first)
+ {
+ if (_useStar)
+ {
+ throw new NotSupportedException("Aggregate functions do not support multiple fields");
+ }
+
+ ResultBuilder.TrimEnd().Append(", ");
+ }
+
+ first = false;
+
+ Visit(arg);
+
+ // When projection uses projection comes from a complex expression, append an alias.
+ var param = expression.Members?[i];
+ if (param != null && arg is not MemberExpression)
+ {
+ ResultBuilder.AppendWithSpace("as ").Append(param.Name.ToUpperInvariant());
+ }
+ }
return expression;
}
@@ -588,32 +613,6 @@ internal sealed class IgniteQueryExpressionVisitor : ThrowingExpressionVisitor
}
}
- /// <summary>
- /// Visits multiple arguments.
- /// </summary>
- /// <param name="arguments">The arguments.</param>
- private void VisitArguments(IEnumerable<Expression> arguments)
- {
- var first = true;
-
- foreach (var e in arguments)
- {
- if (!first)
- {
- if (_useStar)
- {
- throw new NotSupportedException("Aggregate functions do not support multiple fields");
- }
-
- ResultBuilder.TrimEnd().Append(", ");
- }
-
- first = false;
-
- Visit(e);
- }
- }
-
/// <summary>
/// Visits the group by member.
/// </summary>
diff --git a/modules/platforms/dotnet/Apache.Ignite/Internal/Linq/IgniteQueryModelVisitor.cs b/modules/platforms/dotnet/Apache.Ignite/Internal/Linq/IgniteQueryModelVisitor.cs
index d16d02ab0f..c685d0bacc 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/Linq/IgniteQueryModelVisitor.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/Linq/IgniteQueryModelVisitor.cs
@@ -186,7 +186,7 @@ internal sealed class IgniteQueryModelVisitor : QueryModelVisitorBase
{
_builder.Append("from (");
- VisitQueryModel(subQuery.QueryModel);
+ VisitQueryModel(subQuery.QueryModel, includeAllFields: true);
_builder.TrimEnd()
.Append(") as ")