From cb043a2cf26479f36be35db5788f5baba91cd7e4 Mon Sep 17 00:00:00 2001 From: Smit Patel Date: Wed, 21 Aug 2019 11:25:58 -0700 Subject: [PATCH] Query: Expand navigations in GroupBy case Resolves #15249 --- ....CustomShaperCompilingExpressionVisitor.cs | 5 + .../Query/SqlExpressions/SelectExpression.cs | 4 +- .../NavigationExpandingExpressionVisitor.cs | 39 +++++--- .../Query/QueryNoClientEvalTestBase.cs | 2 +- .../Query/AsyncGearsOfWarQueryTestBase.cs | 8 +- .../Query/AsyncSimpleQueryTestBase.cs | 2 +- .../Query/ComplexNavigationsQueryTestBase.cs | 18 ++-- .../Query/GroupByQueryTestBase.cs | 28 +++--- ...omplexNavigationsWeakQuerySqlServerTest.cs | 54 +++++++---- .../Query/GroupByQuerySqlServerTest.cs | 97 +++++++++---------- 10 files changed, 145 insertions(+), 112 deletions(-) diff --git a/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.CustomShaperCompilingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.CustomShaperCompilingExpressionVisitor.cs index 2a928128edc..e1fc08df398 100644 --- a/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.CustomShaperCompilingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.CustomShaperCompilingExpressionVisitor.cs @@ -530,6 +530,11 @@ protected override Expression VisitExtension(Expression extensionExpression) Expression.Constant(((LambdaExpression)Visit(collectionShaper.InnerShaper)).Compile())); } + if (extensionExpression is GroupByShaperExpression) + { + throw new InvalidOperationException("Client side GroupBy is not supported."); + } + return base.VisitExtension(extensionExpression); } diff --git a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs index 661d59d8bc0..0167873acaa 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs @@ -1092,7 +1092,7 @@ private void AddJoin( } // Verify what are the cases of pushdown for inner & outer both sides - if (Limit != null || Offset != null || IsDistinct || GroupBy.Count > 1) + if (Limit != null || Offset != null || IsDistinct || GroupBy.Count > 0) { var sqlRemappingVisitor = new SqlRemappingVisitor(PushdownIntoSubquery(), (SelectExpression)Tables[0]); innerSelectExpression = sqlRemappingVisitor.Remap(innerSelectExpression); @@ -1105,7 +1105,7 @@ private void AddJoin( || innerSelectExpression.IsDistinct || innerSelectExpression.Predicate != null || innerSelectExpression.Tables.Count > 1 - || innerSelectExpression.GroupBy.Count > 1) + || innerSelectExpression.GroupBy.Count > 0) { joinPredicate = new SqlRemappingVisitor( innerSelectExpression.PushdownIntoSubquery(), (SelectExpression)innerSelectExpression.Tables[0]) diff --git a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs index 66dc1af5ebe..69830318d8e 100644 --- a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs +++ b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs @@ -893,37 +893,50 @@ private Expression ProcessGroupBy( LambdaExpression elementSelector, LambdaExpression resultSelector) { - source = (NavigationExpansionExpression)_pendingSelectorExpandingExpressionVisitor.Visit(source); - var queryable = Reduce(source); + var keySelectorBody = ExpandNavigationsInLambdaExpression(source, keySelector); Expression result; if (elementSelector == null) { + source = (NavigationExpansionExpression)_pendingSelectorExpandingExpressionVisitor.Visit(source); + // TODO: Flow include in future + //source = (NavigationExpansionExpression)new IncludeApplyingExpressionVisitor( + // this, _queryCompilationContext.IsTracking).Visit(source); + keySelector = GenerateLambda(keySelectorBody, source.CurrentParameter); + elementSelector = GenerateLambda(source.PendingSelector, source.CurrentParameter); if (resultSelector == null) { result = Expression.Call( - QueryableMethods.GroupByWithKeySelector.MakeGenericMethod( - queryable.Type.TryGetSequenceType(), keySelector.ReturnType), - queryable, - Expression.Quote(keySelector)); + QueryableMethods.GroupByWithKeyElementSelector.MakeGenericMethod( + source.CurrentParameter.Type, keySelector.ReturnType, elementSelector.ReturnType), + source.Source, + Expression.Quote(keySelector), + Expression.Quote(elementSelector)); } else { result = Expression.Call( - QueryableMethods.GroupByWithKeyResultSelector.MakeGenericMethod( - queryable.Type.TryGetSequenceType(), keySelector.ReturnType, resultSelector.ReturnType), - queryable, + QueryableMethods.GroupByWithKeyElementResultSelector.MakeGenericMethod( + source.CurrentParameter.Type, keySelector.ReturnType, elementSelector.ReturnType, resultSelector.ReturnType), + source.Source, Expression.Quote(keySelector), + Expression.Quote(elementSelector), Expression.Quote(resultSelector)); } } else { + source = (NavigationExpansionExpression)ProcessSelect(source, elementSelector); + source = (NavigationExpansionExpression)_pendingSelectorExpandingExpressionVisitor.Visit(source); + source = (NavigationExpansionExpression)new IncludeApplyingExpressionVisitor( + this, _queryCompilationContext.IsTracking).Visit(source); + keySelector = GenerateLambda(keySelectorBody, source.CurrentParameter); + elementSelector = GenerateLambda(source.PendingSelector, source.CurrentParameter); if (resultSelector == null) { result = Expression.Call( QueryableMethods.GroupByWithKeyElementSelector.MakeGenericMethod( - queryable.Type.TryGetSequenceType(), keySelector.ReturnType, elementSelector.ReturnType), - queryable, + source.CurrentParameter.Type, keySelector.ReturnType, elementSelector.ReturnType), + source.Source, Expression.Quote(keySelector), Expression.Quote(elementSelector)); } @@ -931,8 +944,8 @@ private Expression ProcessGroupBy( { result = Expression.Call( QueryableMethods.GroupByWithKeyElementResultSelector.MakeGenericMethod( - queryable.Type.TryGetSequenceType(), keySelector.ReturnType, elementSelector.ReturnType, resultSelector.ReturnType), - queryable, + source.CurrentParameter.Type, keySelector.ReturnType, elementSelector.ReturnType, resultSelector.ReturnType), + source.Source, Expression.Quote(keySelector), Expression.Quote(elementSelector), Expression.Quote(resultSelector)); diff --git a/test/EFCore.Relational.Specification.Tests/Query/QueryNoClientEvalTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/QueryNoClientEvalTestBase.cs index dc7e8520d14..dea268a8426 100644 --- a/test/EFCore.Relational.Specification.Tests/Query/QueryNoClientEvalTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Query/QueryNoClientEvalTestBase.cs @@ -200,7 +200,7 @@ public virtual void Throws_when_group_join() } } - [ConditionalFact(Skip = "Issue #15249")] + [ConditionalFact(Skip = "Issue#17068")] public virtual void Throws_when_group_by() { using (var context = CreateContext()) diff --git a/test/EFCore.Specification.Tests/Query/AsyncGearsOfWarQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/AsyncGearsOfWarQueryTestBase.cs index 8a661e36dcc..4bea8c7bf83 100644 --- a/test/EFCore.Specification.Tests/Query/AsyncGearsOfWarQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/AsyncGearsOfWarQueryTestBase.cs @@ -21,7 +21,7 @@ protected AsyncGearsOfWarQueryTestBase(TFixture fixture) protected GearsOfWarContext CreateContext() => Fixture.CreateContext(); - [ConditionalFact(Skip = "Issue #15249")] + [ConditionalFact(Skip = "Issue#17068")] public virtual async Task Include_with_group_by_on_entity_qsre() { using (var ctx = CreateContext()) @@ -39,7 +39,7 @@ public virtual async Task Include_with_group_by_on_entity_qsre() } } - [ConditionalFact(Skip = "Issue #15249")] + [ConditionalFact(Skip = "Issue#17068")] public virtual async Task Include_with_group_by_on_entity_qsre_with_composite_key() { using (var ctx = CreateContext()) @@ -57,7 +57,7 @@ public virtual async Task Include_with_group_by_on_entity_qsre_with_composite_ke } } - [ConditionalFact(Skip = "Issue #15249")] + [ConditionalFact(Skip = "Issue#17068")] public virtual async Task Include_with_group_by_on_entity_navigation() { using (var ctx = CreateContext()) @@ -75,7 +75,7 @@ public virtual async Task Include_with_group_by_on_entity_navigation() } } - [ConditionalFact(Skip = "Issue #15249")] + [ConditionalFact(Skip = "Issue#17068")] public virtual async Task Include_groupby_constant() { using (var ctx = CreateContext()) diff --git a/test/EFCore.Specification.Tests/Query/AsyncSimpleQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/AsyncSimpleQueryTestBase.cs index c3b8e411931..6015f78e36c 100644 --- a/test/EFCore.Specification.Tests/Query/AsyncSimpleQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/AsyncSimpleQueryTestBase.cs @@ -29,7 +29,7 @@ protected AsyncSimpleQueryTestBase(TFixture fixture) protected NorthwindContext CreateContext() => Fixture.CreateContext(); - [ConditionalFact(Skip = "Issue #15249")] + [ConditionalFact(Skip = "Issue#17068")] public virtual async Task GroupBy_tracking_after_dispose() { List> groups; diff --git a/test/EFCore.Specification.Tests/Query/ComplexNavigationsQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/ComplexNavigationsQueryTestBase.cs index e46ed89fb78..cd7fae99fa1 100644 --- a/test/EFCore.Specification.Tests/Query/ComplexNavigationsQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/ComplexNavigationsQueryTestBase.cs @@ -390,7 +390,7 @@ public virtual Task Simple_level1_level2_include(bool isAsync) l1s => l1s.Include(l1 => l1.OneToOne_Required_PK1.OneToOne_Required_PK2), elementSorter: e => e.Id); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Simple_level1_level2_GroupBy_Count(bool isAsync) { @@ -408,7 +408,7 @@ public virtual Task Simple_level1_level2_GroupBy_Count(bool isAsync) .Select(g => g.Count())); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Simple_level1_level2_GroupBy_Having_Count(bool isAsync) { @@ -1888,7 +1888,7 @@ where Maybe(l1.OneToOne_Optional_FK1, () => l1.OneToOne_Optional_FK1.Name) != "L elementSorter: l1 => l1.Id); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory(Skip = "issue #17068")] [MemberData(nameof(IsAsyncData))] public virtual Task Include_with_groupjoin_skip_and_take(bool isAsync) { @@ -5098,7 +5098,7 @@ public virtual void Entries_for_detached_entities_are_removed() } } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory(Skip = "Issue#12088")] [MemberData(nameof(IsAsyncData))] public virtual Task Include_reference_with_groupby_in_subquery(bool isAsync) { @@ -5114,7 +5114,7 @@ public virtual Task Include_reference_with_groupby_in_subquery(bool isAsync) }); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory(Skip = "Issue#12088")] [MemberData(nameof(IsAsyncData))] public virtual Task Include_collection_with_groupby_in_subquery(bool isAsync) { @@ -5130,7 +5130,7 @@ public virtual Task Include_collection_with_groupby_in_subquery(bool isAsync) }); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory(Skip = "Issue#12088")] [MemberData(nameof(IsAsyncData))] public virtual Task Multi_include_with_groupby_in_subquery(bool isAsync) { @@ -5149,7 +5149,7 @@ public virtual Task Multi_include_with_groupby_in_subquery(bool isAsync) expectedIncludes); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory(Skip = "Issue#12088")] [MemberData(nameof(IsAsyncData))] public virtual Task Include_collection_with_groupby_in_subquery_and_filter_before_groupby(bool isAsync) { @@ -5166,7 +5166,7 @@ public virtual Task Include_collection_with_groupby_in_subquery_and_filter_befor }); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory(Skip = "Issue#12088")] [MemberData(nameof(IsAsyncData))] public virtual Task Include_collection_with_groupby_in_subquery_and_filter_after_groupby(bool isAsync) { @@ -5388,7 +5388,7 @@ public virtual Task Include_after_SelectMany_and_multiple_reference_navigations( }); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory(Skip = "Issue#16752")] [MemberData(nameof(IsAsyncData))] public virtual Task Include_after_SelectMany_and_reference_navigation_with_another_SelectMany_with_Distinct(bool isAsync) { diff --git a/test/EFCore.Specification.Tests/Query/GroupByQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/GroupByQueryTestBase.cs index f186483b115..3af81640936 100644 --- a/test/EFCore.Specification.Tests/Query/GroupByQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/GroupByQueryTestBase.cs @@ -37,13 +37,15 @@ public virtual Task GroupBy_Property_Select_Average(bool isAsync) os => os.GroupBy(o => o.CustomerID).Select(g => g.Average(o => o.OrderID))); } - [ConditionalTheory(Skip = "issue #15249")] + [ConditionalTheory(Skip = "issue #17068")] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Property_Select_Average_with_navigation_expansion(bool isAsync) { return AssertQueryScalar( isAsync, - os => os.Where(o => o.Customer.City != "London").GroupBy(o => o.CustomerID, (k, es) => new { k, es }).Select(g => g.es.Average(o => o.OrderID))); + os => os.Where(o => o.Customer.City != "London") + .GroupBy(o => o.CustomerID, (k, es) => new { k, es }) + .Select(g => g.es.Average(o => o.OrderID))); } [ConditionalTheory] @@ -1452,7 +1454,7 @@ on o.CustomerID equals c.CustomerID e => e.Key); } - [ConditionalTheory(Skip = "Issue#15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_required_navigation_member_Aggregate(bool isAsync) { @@ -1602,7 +1604,7 @@ from c in grouping.DefaultIfEmpty() e => e.Value); } - [ConditionalTheory(Skip = "Issue#15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_optional_navigation_member_Aggregate(bool isAsync) { @@ -1664,7 +1666,7 @@ on o1.OrderID equals o2.OrderID e => e.Key); } - [ConditionalTheory(Skip = "Issue#15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_multi_navigation_members_Aggregate(bool isAsync) { @@ -1729,7 +1731,7 @@ public virtual Task Select_anonymous_GroupBy_Aggregate(bool isAsync) })); } - [ConditionalTheory(Skip = "Issue#15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_principal_key_property_optimization(bool isAsync) { @@ -1930,7 +1932,7 @@ public virtual Task GroupBy_filter_count_OrderBy_count_Select_sum(bool isAsync) })); } - [ConditionalTheory(Skip = "Issue#15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task GroupBy_Aggregate_Join(bool isAsync) { @@ -1955,7 +1957,7 @@ join o in os on a.LastOrderID equals o.OrderID entryCount: 126); } - [ConditionalTheory(Skip = "Issue#15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Join_GroupBy_Aggregate_multijoins(bool isAsync) { @@ -1981,7 +1983,7 @@ join o in os on a.LastOrderID equals o.OrderID entryCount: 126); } - [ConditionalTheory(Skip = "Issue#15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Join_GroupBy_Aggregate_single_join(bool isAsync) { @@ -2006,7 +2008,7 @@ on c.CustomerID equals a.CustomerID entryCount: 63); } - [ConditionalTheory(Skip = "Issue#15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Join_GroupBy_Aggregate_with_another_join(bool isAsync) { @@ -2034,7 +2036,7 @@ from g in grouping entryCount: 63); } - [ConditionalTheory(Skip = "Issue#15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Join_GroupBy_Aggregate_in_subquery(bool isAsync) { @@ -2064,10 +2066,10 @@ on o.CustomerID equals i.c.CustomerID i.c, i.c.CustomerID }, - entryCount: 133); + entryCount: 187); } - [ConditionalTheory(Skip = "Issue#15249")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Join_GroupBy_Aggregate_on_key(bool isAsync) { diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsWeakQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsWeakQuerySqlServerTest.cs index 0a5fb902e95..895c96e5e0e 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsWeakQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsWeakQuerySqlServerTest.cs @@ -72,18 +72,25 @@ public override async Task Simple_level1_level2_GroupBy_Count(bool isAsync) AssertSql( @"SELECT COUNT(*) -FROM [Level1] AS [l1] +FROM [Level1] AS [l] LEFT JOIN ( - SELECT [l1.OneToOne_Required_PK1].* - FROM [Level1] AS [l1.OneToOne_Required_PK1] - WHERE [l1.OneToOne_Required_PK1].[OneToMany_Required_Inverse2Id] IS NOT NULL AND ([l1.OneToOne_Required_PK1].[Level1_Required_Id] IS NOT NULL AND [l1.OneToOne_Required_PK1].[OneToOne_Required_PK_Date] IS NOT NULL) -) AS [t] ON [l1].[Id] = [t].[Id] + SELECT [l0].[Id], [l0].[OneToOne_Required_PK_Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Level2_Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l1].[Id] AS [Id0] + FROM [Level1] AS [l0] + INNER JOIN [Level1] AS [l1] ON [l0].[Id] = [l1].[Id] + WHERE [l0].[OneToMany_Required_Inverse2Id] IS NOT NULL AND ([l0].[Level1_Required_Id] IS NOT NULL AND [l0].[OneToOne_Required_PK_Date] IS NOT NULL) +) AS [t] ON [l].[Id] = [t].[Id] LEFT JOIN ( - SELECT [l1.OneToOne_Required_PK1.OneToOne_Required_PK2].* - FROM [Level1] AS [l1.OneToOne_Required_PK1.OneToOne_Required_PK2] - WHERE ([l1.OneToOne_Required_PK1.OneToOne_Required_PK2].[OneToMany_Required_Inverse2Id] IS NOT NULL AND ([l1.OneToOne_Required_PK1.OneToOne_Required_PK2].[Level1_Required_Id] IS NOT NULL AND [l1.OneToOne_Required_PK1.OneToOne_Required_PK2].[OneToOne_Required_PK_Date] IS NOT NULL)) AND ([l1.OneToOne_Required_PK1.OneToOne_Required_PK2].[OneToMany_Required_Inverse3Id] IS NOT NULL AND [l1.OneToOne_Required_PK1.OneToOne_Required_PK2].[Level2_Required_Id] IS NOT NULL) -) AS [t0] ON [t].[Id] = [t0].[Id] -GROUP BY [t0].[Level3_Name]"); + SELECT [l2].[Id], [l2].[Level2_Optional_Id], [l2].[Level2_Required_Id], [l2].[Level3_Name], [l2].[OneToMany_Optional_Inverse3Id], [l2].[OneToMany_Required_Inverse3Id], [l2].[OneToOne_Optional_PK_Inverse3Id], [t0].[Id] AS [Id0], [t0].[Id0] AS [Id00] + FROM [Level1] AS [l2] + INNER JOIN ( + SELECT [l3].[Id], [l3].[OneToOne_Required_PK_Date], [l3].[Level1_Optional_Id], [l3].[Level1_Required_Id], [l3].[Level2_Name], [l3].[OneToMany_Optional_Inverse2Id], [l3].[OneToMany_Required_Inverse2Id], [l3].[OneToOne_Optional_PK_Inverse2Id], [l4].[Id] AS [Id0] + FROM [Level1] AS [l3] + INNER JOIN [Level1] AS [l4] ON [l3].[Id] = [l4].[Id] + WHERE [l3].[OneToMany_Required_Inverse2Id] IS NOT NULL AND ([l3].[Level1_Required_Id] IS NOT NULL AND [l3].[OneToOne_Required_PK_Date] IS NOT NULL) + ) AS [t0] ON [l2].[Id] = [t0].[Id] + WHERE [l2].[OneToMany_Required_Inverse3Id] IS NOT NULL AND [l2].[Level2_Required_Id] IS NOT NULL +) AS [t1] ON [t].[Id] = [t1].[Id] +GROUP BY [t1].[Level3_Name]"); } public override async Task Simple_level1_level2_GroupBy_Having_Count(bool isAsync) @@ -92,18 +99,25 @@ public override async Task Simple_level1_level2_GroupBy_Having_Count(bool isAsyn AssertSql( @"SELECT COUNT(*) -FROM [Level1] AS [l1] +FROM [Level1] AS [l] LEFT JOIN ( - SELECT [l1.OneToOne_Required_PK1].* - FROM [Level1] AS [l1.OneToOne_Required_PK1] - WHERE [l1.OneToOne_Required_PK1].[OneToMany_Required_Inverse2Id] IS NOT NULL AND ([l1.OneToOne_Required_PK1].[Level1_Required_Id] IS NOT NULL AND [l1.OneToOne_Required_PK1].[OneToOne_Required_PK_Date] IS NOT NULL) -) AS [t] ON [l1].[Id] = [t].[Id] + SELECT [l0].[Id], [l0].[OneToOne_Required_PK_Date], [l0].[Level1_Optional_Id], [l0].[Level1_Required_Id], [l0].[Level2_Name], [l0].[OneToMany_Optional_Inverse2Id], [l0].[OneToMany_Required_Inverse2Id], [l0].[OneToOne_Optional_PK_Inverse2Id], [l1].[Id] AS [Id0] + FROM [Level1] AS [l0] + INNER JOIN [Level1] AS [l1] ON [l0].[Id] = [l1].[Id] + WHERE [l0].[OneToMany_Required_Inverse2Id] IS NOT NULL AND ([l0].[Level1_Required_Id] IS NOT NULL AND [l0].[OneToOne_Required_PK_Date] IS NOT NULL) +) AS [t] ON [l].[Id] = [t].[Id] LEFT JOIN ( - SELECT [l1.OneToOne_Required_PK1.OneToOne_Required_PK2].* - FROM [Level1] AS [l1.OneToOne_Required_PK1.OneToOne_Required_PK2] - WHERE ([l1.OneToOne_Required_PK1.OneToOne_Required_PK2].[OneToMany_Required_Inverse2Id] IS NOT NULL AND ([l1.OneToOne_Required_PK1.OneToOne_Required_PK2].[Level1_Required_Id] IS NOT NULL AND [l1.OneToOne_Required_PK1.OneToOne_Required_PK2].[OneToOne_Required_PK_Date] IS NOT NULL)) AND ([l1.OneToOne_Required_PK1.OneToOne_Required_PK2].[OneToMany_Required_Inverse3Id] IS NOT NULL AND [l1.OneToOne_Required_PK1.OneToOne_Required_PK2].[Level2_Required_Id] IS NOT NULL) -) AS [t0] ON [t].[Id] = [t0].[Id] -GROUP BY [t0].[Level3_Name] + SELECT [l2].[Id], [l2].[Level2_Optional_Id], [l2].[Level2_Required_Id], [l2].[Level3_Name], [l2].[OneToMany_Optional_Inverse3Id], [l2].[OneToMany_Required_Inverse3Id], [l2].[OneToOne_Optional_PK_Inverse3Id], [t0].[Id] AS [Id0], [t0].[Id0] AS [Id00] + FROM [Level1] AS [l2] + INNER JOIN ( + SELECT [l3].[Id], [l3].[OneToOne_Required_PK_Date], [l3].[Level1_Optional_Id], [l3].[Level1_Required_Id], [l3].[Level2_Name], [l3].[OneToMany_Optional_Inverse2Id], [l3].[OneToMany_Required_Inverse2Id], [l3].[OneToOne_Optional_PK_Inverse2Id], [l4].[Id] AS [Id0] + FROM [Level1] AS [l3] + INNER JOIN [Level1] AS [l4] ON [l3].[Id] = [l4].[Id] + WHERE [l3].[OneToMany_Required_Inverse2Id] IS NOT NULL AND ([l3].[Level1_Required_Id] IS NOT NULL AND [l3].[OneToOne_Required_PK_Date] IS NOT NULL) + ) AS [t0] ON [l2].[Id] = [t0].[Id] + WHERE [l2].[OneToMany_Required_Inverse3Id] IS NOT NULL AND [l2].[Level2_Required_Id] IS NOT NULL +) AS [t1] ON [t].[Id] = [t1].[Id] +GROUP BY [t1].[Level3_Name] HAVING MIN(COALESCE([t].[Id], 0)) > 0"); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/GroupByQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/GroupByQuerySqlServerTest.cs index bf57f313ef6..7bb6c7fc746 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/GroupByQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/GroupByQuerySqlServerTest.cs @@ -922,10 +922,10 @@ public override async Task GroupBy_required_navigation_member_Aggregate(bool isA await base.GroupBy_required_navigation_member_Aggregate(isAsync); AssertSql( - @"SELECT [od.Order].[CustomerID] AS [CustomerId], COUNT(*) AS [Count] -FROM [Order Details] AS [od] -INNER JOIN [Orders] AS [od.Order] ON [od].[OrderID] = [od.Order].[OrderID] -GROUP BY [od.Order].[CustomerID]"); + @"SELECT [o].[CustomerID] AS [CustomerId], COUNT(*) AS [Count] +FROM [Order Details] AS [o0] +INNER JOIN [Orders] AS [o] ON [o0].[OrderID] = [o].[OrderID] +GROUP BY [o].[CustomerID]"); } public override async Task Join_complex_GroupBy_Aggregate(bool isAsync) @@ -1015,10 +1015,10 @@ public override async Task GroupBy_optional_navigation_member_Aggregate(bool isA await base.GroupBy_optional_navigation_member_Aggregate(isAsync); AssertSql( - @"SELECT [o.Customer].[Country], COUNT(*) AS [Count] + @"SELECT [c].[Country], COUNT(*) AS [Count] FROM [Orders] AS [o] -LEFT JOIN [Customers] AS [o.Customer] ON [o].[CustomerID] = [o.Customer].[CustomerID] -GROUP BY [o.Customer].[Country]"); +LEFT JOIN [Customers] AS [c] ON [o].[CustomerID] = [c].[CustomerID] +GROUP BY [c].[Country]"); } public override async Task GroupJoin_complex_GroupBy_Aggregate(bool isAsync) @@ -1065,11 +1065,11 @@ public override async Task GroupBy_multi_navigation_members_Aggregate(bool isAsy await base.GroupBy_multi_navigation_members_Aggregate(isAsync); AssertSql( - @"SELECT [od.Order].[CustomerID], [od.Product].[ProductName], COUNT(*) AS [Count] -FROM [Order Details] AS [od] -INNER JOIN [Products] AS [od.Product] ON [od].[ProductID] = [od.Product].[ProductID] -INNER JOIN [Orders] AS [od.Order] ON [od].[OrderID] = [od.Order].[OrderID] -GROUP BY [od.Order].[CustomerID], [od.Product].[ProductName]"); + @"SELECT [o].[CustomerID], [p].[ProductName], COUNT(*) AS [Count] +FROM [Order Details] AS [o0] +INNER JOIN [Orders] AS [o] ON [o0].[OrderID] = [o].[OrderID] +INNER JOIN [Products] AS [p] ON [o0].[ProductID] = [p].[ProductID] +GROUP BY [o].[CustomerID], [p].[ProductName]"); } public override async Task Union_simple_groupby(bool isAsync) @@ -1106,9 +1106,10 @@ public override async Task GroupBy_principal_key_property_optimization(bool isAs await base.GroupBy_principal_key_property_optimization(isAsync); AssertSql( - @"SELECT [o].[CustomerID] AS [Key], COUNT(*) AS [Count] + @"SELECT [c].[CustomerID] AS [Key], COUNT(*) AS [Count] FROM [Orders] AS [o] -GROUP BY [o].[CustomerID]"); +LEFT JOIN [Customers] AS [c] ON [o].[CustomerID] = [c].[CustomerID] +GROUP BY [c].[CustomerID]"); } public override async Task GroupBy_OrderBy_key(bool isAsync) @@ -1268,34 +1269,31 @@ public override async Task GroupBy_Aggregate_Join(bool isAsync) await base.GroupBy_Aggregate_Join(isAsync); AssertSql( - @"SELECT [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate] -FROM [Orders] AS [o0]", - // - @"SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] -FROM [Customers] AS [c]", - // - @"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] -FROM [Orders] AS [o] -ORDER BY [o].[CustomerID]"); + @"SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] +FROM ( + SELECT [o0].[CustomerID], MAX([o0].[OrderID]) AS [c] + FROM [Orders] AS [o0] + GROUP BY [o0].[CustomerID] + HAVING COUNT(*) > 5 +) AS [t] +INNER JOIN [Customers] AS [c] ON [t].[CustomerID] = [c].[CustomerID] +INNER JOIN [Orders] AS [o] ON [t].[c] = [o].[OrderID]"); } public override async Task Join_GroupBy_Aggregate_multijoins(bool isAsync) { await base.Join_GroupBy_Aggregate_multijoins(isAsync); - // This could be lifted. Blocked by bug #10812 AssertSql( - @"SELECT [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate] -FROM [Orders] AS [o0]", - // - @"SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [t].[CustomerID], [t].[LastOrderID] + @"SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] FROM [Customers] AS [c] INNER JOIN ( - SELECT [o].[CustomerID], MAX([o].[OrderID]) AS [LastOrderID] - FROM [Orders] AS [o] - GROUP BY [o].[CustomerID] + SELECT [o0].[CustomerID], MAX([o0].[OrderID]) AS [c] + FROM [Orders] AS [o0] + GROUP BY [o0].[CustomerID] HAVING COUNT(*) > 5 -) AS [t] ON [c].[CustomerID] = [t].[CustomerID]"); +) AS [t] ON [c].[CustomerID] = [t].[CustomerID] +INNER JOIN [Orders] AS [o] ON [t].[c] = [o].[OrderID]"); } public override async Task Join_GroupBy_Aggregate_single_join(bool isAsync) @@ -1303,10 +1301,10 @@ public override async Task Join_GroupBy_Aggregate_single_join(bool isAsync) await base.Join_GroupBy_Aggregate_single_join(isAsync); AssertSql( - @"SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [t].[CustomerID], [t].[LastOrderID] + @"SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [t].[c] AS [LastOrderID] FROM [Customers] AS [c] INNER JOIN ( - SELECT [o].[CustomerID], MAX([o].[OrderID]) AS [LastOrderID] + SELECT [o].[CustomerID], MAX([o].[OrderID]) AS [c] FROM [Orders] AS [o] GROUP BY [o].[CustomerID] HAVING COUNT(*) > 5 @@ -1318,10 +1316,10 @@ public override async Task Join_GroupBy_Aggregate_with_another_join(bool isAsync await base.Join_GroupBy_Aggregate_with_another_join(isAsync); AssertSql( - @"SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [t].[CustomerID], [t].[LastOrderID], [o0].[OrderID] + @"SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [t].[c] AS [LastOrderID], [o0].[OrderID] FROM [Customers] AS [c] INNER JOIN ( - SELECT [o].[CustomerID], MAX([o].[OrderID]) AS [LastOrderID] + SELECT [o].[CustomerID], MAX([o].[OrderID]) AS [c] FROM [Orders] AS [o] GROUP BY [o].[CustomerID] HAVING COUNT(*) > 5 @@ -1334,17 +1332,18 @@ public override async Task Join_GroupBy_Aggregate_in_subquery(bool isAsync) await base.Join_GroupBy_Aggregate_in_subquery(isAsync); AssertSql( - @"SELECT [c0].[CustomerID], [c0].[Address], [c0].[City], [c0].[CompanyName], [c0].[ContactName], [c0].[ContactTitle], [c0].[Country], [c0].[Fax], [c0].[Phone], [c0].[PostalCode], [c0].[Region], [t0].[CustomerID], [t0].[LastOrderID] -FROM [Customers] AS [c0] -INNER JOIN ( - SELECT [o1].[CustomerID], MAX([o1].[OrderID]) AS [LastOrderID] - FROM [Orders] AS [o1] - GROUP BY [o1].[CustomerID] - HAVING COUNT(*) > 5 -) AS [t0] ON [c0].[CustomerID] = [t0].[CustomerID]", - // - @"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] + @"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [t0].[CustomerID], [t0].[Address], [t0].[City], [t0].[CompanyName], [t0].[ContactName], [t0].[ContactTitle], [t0].[Country], [t0].[Fax], [t0].[Phone], [t0].[PostalCode], [t0].[Region] FROM [Orders] AS [o] +INNER JOIN ( + SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [t].[CustomerID] AS [CustomerID0], [t].[c] + FROM [Customers] AS [c] + INNER JOIN ( + SELECT [o0].[CustomerID], MAX([o0].[OrderID]) AS [c] + FROM [Orders] AS [o0] + GROUP BY [o0].[CustomerID] + HAVING COUNT(*) > 5 + ) AS [t] ON [c].[CustomerID] = [t].[CustomerID] +) AS [t0] ON [o].[CustomerID] = [t0].[CustomerID] WHERE [o].[OrderID] < 10400"); } @@ -1353,14 +1352,14 @@ public override async Task Join_GroupBy_Aggregate_on_key(bool isAsync) await base.Join_GroupBy_Aggregate_on_key(isAsync); AssertSql( - @"SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [t].[Key], [t].[LastOrderID] + @"SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [t].[c] AS [LastOrderID] FROM [Customers] AS [c] INNER JOIN ( - SELECT [o].[CustomerID] AS [Key], MAX([o].[OrderID]) AS [LastOrderID] + SELECT [o].[CustomerID], MAX([o].[OrderID]) AS [c] FROM [Orders] AS [o] GROUP BY [o].[CustomerID] HAVING COUNT(*) > 5 -) AS [t] ON [c].[CustomerID] = [t].[Key]"); +) AS [t] ON [c].[CustomerID] = [t].[CustomerID]"); } public override async Task GroupBy_with_result_selector(bool isAsync)