diff --git a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.Helper.cs b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.Helper.cs index f109b31fefc..05937c1185e 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.Helper.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.Helper.cs @@ -128,26 +128,29 @@ public ProjectionMemberToIndexConvertingExpressionVisitor( private sealed class ProjectionIndexRemappingExpressionVisitor : ExpressionVisitor { - private readonly SelectExpression _queryExpression; + private readonly SelectExpression _oldSelectExpression; + private readonly SelectExpression _newSelectExpression; private readonly int[] _indexMap; public ProjectionIndexRemappingExpressionVisitor( - SelectExpression queryExpression, int[] indexMap) + SelectExpression oldSelectExpression, SelectExpression newSelectExpression, int[] indexMap) { - _queryExpression = queryExpression; + _oldSelectExpression = oldSelectExpression; + _newSelectExpression = newSelectExpression; _indexMap = indexMap; } [return: NotNullIfNotNull("expression")] public override Expression? Visit(Expression? expression) { - if (expression is ProjectionBindingExpression projectionBindingExpression) + if (expression is ProjectionBindingExpression projectionBindingExpression + && ReferenceEquals(projectionBindingExpression.QueryExpression, _oldSelectExpression)) { Check.DebugAssert(projectionBindingExpression.Index != null, "ProjectionBindingExpression must have index."); return new ProjectionBindingExpression( - _queryExpression, + _newSelectExpression, _indexMap[projectionBindingExpression.Index.Value], projectionBindingExpression.Type); } @@ -541,9 +544,9 @@ public SplitCollectionInfo( private sealed class ClientProjectionRemappingExpressionVisitor : ExpressionVisitor { - private readonly object[] _clientProjectionIndexMap; + private readonly List _clientProjectionIndexMap; - public ClientProjectionRemappingExpressionVisitor(object[] clientProjectionIndexMap) + public ClientProjectionRemappingExpressionVisitor(List clientProjectionIndexMap) { _clientProjectionIndexMap = clientProjectionIndexMap; } @@ -562,7 +565,7 @@ public ClientProjectionRemappingExpressionVisitor(object[] clientProjectionIndex if (value is Expression innerShaper) { - return innerShaper; + return Visit(innerShaper); } throw new InvalidCastException(); diff --git a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs index be091ac7343..b47a8cffe36 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs @@ -403,20 +403,23 @@ public Expression ApplyProjection( if (_clientProjections.Count > 0) { - if (_clientProjections.Any(e => e is ShapedQueryExpression) - && (Limit != null + EntityShaperNullableMarkingExpressionVisitor? entityShaperNullableMarkingExpressionVisitor = null; + if (_clientProjections.Any(e => e is ShapedQueryExpression)) + { + if (Limit != null || Offset != null || IsDistinct - || GroupBy.Count > 0)) - { - PushdownIntoSubqueryInternal(); + || GroupBy.Count > 0) + { + PushdownIntoSubqueryInternal(); + } + entityShaperNullableMarkingExpressionVisitor = new EntityShaperNullableMarkingExpressionVisitor(); } - var projectionCount = _clientProjections.Count; var newClientProjections = new List(); - var clientProjectionIndexMap = new object[projectionCount]; + var clientProjectionIndexMap = new List(); var remappingRequired = false; - for (var i = 0; i < projectionCount; i++) + for (var i = 0; i < _clientProjections.Count; i++) { var value = _clientProjections[i]; switch (value) @@ -425,7 +428,7 @@ public Expression ApplyProjection( { var result = AddEntityProjection(entityProjection); newClientProjections.Add(result); - clientProjectionIndexMap[i] = newClientProjections.Count - 1; + clientProjectionIndexMap.Add(newClientProjections.Count - 1); break; } @@ -434,7 +437,7 @@ public Expression ApplyProjection( { var result = Constant(AddToProjection(sqlExpression, _aliasForClientProjections[i])); newClientProjections.Add(result); - clientProjectionIndexMap[i] = newClientProjections.Count - 1; + clientProjectionIndexMap.Add(newClientProjections.Count - 1); break; } @@ -485,12 +488,18 @@ public Expression ApplyProjection( innerShaperExpression); } - innerShaperExpression = innerSelectExpression.ApplyProjection( - innerShaperExpression, shapedQueryExpression.ResultCardinality, querySplittingBehavior); AddJoin(JoinType.OuterApply, ref innerSelectExpression); - - innerShaperExpression = CopyProjectionToOuter(innerSelectExpression, innerShaperExpression); - clientProjectionIndexMap[i] = innerShaperExpression; + var offset = _clientProjections.Count; + var count = innerSelectExpression._clientProjections.Count; + _clientProjections.AddRange(innerSelectExpression._clientProjections.Select(e => MakeNullable(e, nullable: true))); + _aliasForClientProjections.AddRange(innerSelectExpression._aliasForClientProjections); + innerShaperExpression = new ProjectionIndexRemappingExpressionVisitor( + innerSelectExpression, + this, + Enumerable.Range(offset, count).ToArray()) + .Visit(innerShaperExpression); + innerShaperExpression = entityShaperNullableMarkingExpressionVisitor!.Visit(innerShaperExpression); + clientProjectionIndexMap.Add(innerShaperExpression); remappingRequired = true; break; @@ -651,7 +660,7 @@ static Expression RemoveConvert(Expression expression) var result = new SplitCollectionInfo( parentIdentifier, childIdentifier, childIdentifierValueComparers, innerSelectExpression, innerShaperExpression); - clientProjectionIndexMap[i] = result; + clientProjectionIndexMap.Add(result); } else { @@ -746,7 +755,7 @@ static Expression RemoveConvert(Expression expression) parentIdentifier, outerIdentifier, selfIdentifier, parentIdentifierValueComparers, outerIdentifierValueComparers, selfIdentifierValueComparers, innerShaperExpression); - clientProjectionIndexMap[i] = result; + clientProjectionIndexMap.Add(result); } remappingRequired = true; @@ -835,8 +844,8 @@ Expression CopyProjectionToOuter(SelectExpression innerSelectExpression, Express innerSelectExpression._clientProjections.Clear(); innerSelectExpression._aliasForClientProjections.Clear(); - innerShaperExpression = new ProjectionIndexRemappingExpressionVisitor(this, indexMap).Visit(innerShaperExpression); - innerShaperExpression = new EntityShaperNullableMarkingExpressionVisitor().Visit(innerShaperExpression); + innerShaperExpression = new ProjectionIndexRemappingExpressionVisitor(innerSelectExpression, this, indexMap).Visit(innerShaperExpression); + innerShaperExpression = entityShaperNullableMarkingExpressionVisitor!.Visit(innerShaperExpression); return innerShaperExpression; } @@ -1784,7 +1793,7 @@ private Expression AddJoin( innerSelectExpression._clientProjections.Clear(); innerSelectExpression._aliasForClientProjections.Clear(); - innerShaper = new ProjectionIndexRemappingExpressionVisitor(this, indexMap).Visit(innerShaper); + innerShaper = new ProjectionIndexRemappingExpressionVisitor(innerSelectExpression, this, indexMap).Visit(innerShaper); } else { @@ -1814,7 +1823,7 @@ private Expression AddJoin( innerSelectExpression._clientProjections.Clear(); innerSelectExpression._aliasForClientProjections.Clear(); - innerShaper = new ProjectionIndexRemappingExpressionVisitor(this, indexMap).Visit(innerShaper); + innerShaper = new ProjectionIndexRemappingExpressionVisitor(innerSelectExpression, this, indexMap).Visit(innerShaper); } else { diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsCollectionsQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsCollectionsQuerySqlServerTest.cs index e21022ac324..a28a0cb21e3 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsCollectionsQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsCollectionsQuerySqlServerTest.cs @@ -1091,7 +1091,7 @@ public override async Task Lift_projection_mapping_when_pushing_down_subquery(bo AssertSql( @"@__p_0='25' -SELECT [t].[Id], [t0].[Id], [t0].[c], [l1].[Id] +SELECT [t].[Id], [t0].[Id], [l1].[Id], [t0].[c] FROM ( SELECT TOP(@__p_0) [l].[Id] FROM [LevelOne] AS [l] @@ -1142,22 +1142,21 @@ public override async Task Select_subquery_single_nested_subquery(bool async) await base.Select_subquery_single_nested_subquery(async); AssertSql( - @"SELECT [t1].[Id], [t1].[Id0], [t1].[c] + @"SELECT [l].[Id], [t0].[Id], [t1].[Id], [t0].[c] FROM [LevelOne] AS [l] -OUTER APPLY ( - SELECT [t].[Id], [t0].[Id] AS [Id0], [t].[c] +LEFT JOIN ( + SELECT [t].[c], [t].[Id], [t].[OneToMany_Optional_Inverse2Id] FROM ( - SELECT TOP(1) 1 AS [c], [l0].[Id] + SELECT 1 AS [c], [l0].[Id], [l0].[OneToMany_Optional_Inverse2Id], ROW_NUMBER() OVER(PARTITION BY [l0].[OneToMany_Optional_Inverse2Id] ORDER BY [l0].[Id]) AS [row] FROM [LevelTwo] AS [l0] - WHERE [l].[Id] = [l0].[OneToMany_Optional_Inverse2Id] - ORDER BY [l0].[Id] ) AS [t] - LEFT JOIN ( - SELECT [l1].[Id], [l1].[OneToMany_Optional_Inverse3Id] - FROM [LevelThree] AS [l1] - ) AS [t0] ON [t].[Id] = [t0].[OneToMany_Optional_Inverse3Id] -) AS [t1] -ORDER BY [l].[Id]"); + WHERE [t].[row] <= 1 +) AS [t0] ON [l].[Id] = [t0].[OneToMany_Optional_Inverse2Id] +LEFT JOIN ( + SELECT [l1].[Id], [l1].[OneToMany_Optional_Inverse3Id] + FROM [LevelThree] AS [l1] +) AS [t1] ON [t0].[Id] = [t1].[OneToMany_Optional_Inverse3Id] +ORDER BY [l].[Id], [t0].[Id], [t1].[Id]"); } public override async Task Select_subquery_single_nested_subquery2(bool async) @@ -1165,26 +1164,25 @@ public override async Task Select_subquery_single_nested_subquery2(bool async) await base.Select_subquery_single_nested_subquery2(async); AssertSql( - @"SELECT [l].[Id], [t2].[Id], [t2].[Id0], [t2].[c], [t2].[Id1] + @"SELECT [l].[Id], [t2].[Id], [t2].[Id0], [t2].[Id1], [t2].[c] FROM [LevelOne] AS [l] LEFT JOIN ( - SELECT [t1].[Id], [t1].[Id0], [t1].[c], [l0].[Id] AS [Id1], [l0].[OneToMany_Optional_Inverse2Id] + SELECT [l0].[Id], [t0].[Id] AS [Id0], [t1].[Id] AS [Id1], [t0].[c], [l0].[OneToMany_Optional_Inverse2Id] FROM [LevelTwo] AS [l0] - OUTER APPLY ( - SELECT [t].[Id], [t0].[Id] AS [Id0], [t].[c] + LEFT JOIN ( + SELECT [t].[c], [t].[Id], [t].[OneToMany_Optional_Inverse3Id] FROM ( - SELECT TOP(1) 1 AS [c], [l1].[Id] + SELECT 1 AS [c], [l1].[Id], [l1].[OneToMany_Optional_Inverse3Id], ROW_NUMBER() OVER(PARTITION BY [l1].[OneToMany_Optional_Inverse3Id] ORDER BY [l1].[Id]) AS [row] FROM [LevelThree] AS [l1] - WHERE [l0].[Id] = [l1].[OneToMany_Optional_Inverse3Id] - ORDER BY [l1].[Id] ) AS [t] - LEFT JOIN ( - SELECT [l2].[Id], [l2].[OneToMany_Optional_Inverse4Id] - FROM [LevelFour] AS [l2] - ) AS [t0] ON [t].[Id] = [t0].[OneToMany_Optional_Inverse4Id] - ) AS [t1] + WHERE [t].[row] <= 1 + ) AS [t0] ON [l0].[Id] = [t0].[OneToMany_Optional_Inverse3Id] + LEFT JOIN ( + SELECT [l2].[Id], [l2].[OneToMany_Optional_Inverse4Id] + FROM [LevelFour] AS [l2] + ) AS [t1] ON [t0].[Id] = [t1].[OneToMany_Optional_Inverse4Id] ) AS [t2] ON [l].[Id] = [t2].[OneToMany_Optional_Inverse2Id] -ORDER BY [l].[Id], [t2].[Id1], [t2].[Id], [t2].[Id0]"); +ORDER BY [l].[Id], [t2].[Id], [t2].[Id0], [t2].[Id1]"); } public override async Task Filtered_include_basic_Where(bool async) @@ -1710,25 +1708,26 @@ public override async Task Complex_query_with_let_collection_projection_FirstOrD await base.Complex_query_with_let_collection_projection_FirstOrDefault(async); AssertSql( - @"SELECT [t1].[Id], [t1].[Name], [t1].[Id0], [t1].[c] + @"SELECT [l].[Id], [t0].[Id], [t1].[Name], [t1].[Id], [t0].[c] FROM [LevelOne] AS [l] -OUTER APPLY ( - SELECT [t].[Id], [t0].[Name], [t0].[Id] AS [Id0], [t].[c] +LEFT JOIN ( + SELECT [t].[c], [t].[Id], [t].[OneToMany_Optional_Inverse2Id] FROM ( - SELECT TOP(1) 1 AS [c], [l0].[Id] + SELECT 1 AS [c], [l0].[Id], [l0].[OneToMany_Optional_Inverse2Id], ROW_NUMBER() OVER(PARTITION BY [l0].[OneToMany_Optional_Inverse2Id] ORDER BY [l0].[Id]) AS [row] FROM [LevelTwo] AS [l0] - WHERE ([l].[Id] = [l0].[OneToMany_Optional_Inverse2Id]) AND (([l0].[Name] <> N'Foo') OR [l0].[Name] IS NULL) + WHERE ([l0].[Name] <> N'Foo') OR [l0].[Name] IS NULL ) AS [t] - OUTER APPLY ( - SELECT [l1].[Name], [l1].[Id] - FROM [LevelOne] AS [l1] - WHERE EXISTS ( - SELECT 1 - FROM [LevelTwo] AS [l2] - WHERE ([l1].[Id] = [l2].[OneToMany_Optional_Inverse2Id]) AND ([l2].[Id] = [t].[Id])) - ) AS [t0] + WHERE [t].[row] <= 1 +) AS [t0] ON [l].[Id] = [t0].[OneToMany_Optional_Inverse2Id] +OUTER APPLY ( + SELECT [l1].[Name], [l1].[Id] + FROM [LevelOne] AS [l1] + WHERE EXISTS ( + SELECT 1 + FROM [LevelTwo] AS [l2] + WHERE ([l1].[Id] = [l2].[OneToMany_Optional_Inverse2Id]) AND ([l2].[Id] = [t0].[Id])) ) AS [t1] -ORDER BY [l].[Id]"); +ORDER BY [l].[Id], [t0].[Id], [t1].[Id]"); } public override async Task SelectMany_DefaultIfEmpty_multiple_times_with_joins_projecting_a_collection(bool async) diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServerTest.cs index 4d05481fb3a..13741ba9a59 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServerTest.cs @@ -3108,34 +3108,32 @@ public override void Member_pushdown_chain_3_levels_deep_entity() base.Member_pushdown_chain_3_levels_deep_entity(); AssertSql( - @"SELECT [t4].[Id], [t4].[Level3_Optional_Id], [t4].[Level3_Required_Id], [t4].[Name], [t4].[OneToMany_Optional_Inverse4Id], [t4].[OneToMany_Optional_Self_Inverse4Id], [t4].[OneToMany_Required_Inverse4Id], [t4].[OneToMany_Required_Self_Inverse4Id], [t4].[OneToOne_Optional_PK_Inverse4Id], [t4].[OneToOne_Optional_Self4Id], [t4].[c], [t4].[c0] + @"SELECT [t0].[c], [t1].[c], [t3].[Id], [t3].[Level3_Optional_Id], [t3].[Level3_Required_Id], [t3].[Name], [t3].[OneToMany_Optional_Inverse4Id], [t3].[OneToMany_Optional_Self_Inverse4Id], [t3].[OneToMany_Required_Inverse4Id], [t3].[OneToMany_Required_Self_Inverse4Id], [t3].[OneToOne_Optional_PK_Inverse4Id], [t3].[OneToOne_Optional_Self4Id] FROM [LevelOne] AS [l] -OUTER APPLY ( - SELECT [t2].[Id], [t2].[Level3_Optional_Id], [t2].[Level3_Required_Id], [t2].[Name], [t2].[OneToMany_Optional_Inverse4Id], [t2].[OneToMany_Optional_Self_Inverse4Id], [t2].[OneToMany_Required_Inverse4Id], [t2].[OneToMany_Required_Self_Inverse4Id], [t2].[OneToOne_Optional_PK_Inverse4Id], [t2].[OneToOne_Optional_Self4Id], [t2].[c], [t].[c] AS [c0] +LEFT JOIN ( + SELECT [t].[c], [t].[Id], [t].[Level1_Optional_Id] FROM ( - SELECT TOP(1) 1 AS [c], [l0].[Id] + SELECT 1 AS [c], [l0].[Id], [l0].[Level1_Optional_Id], ROW_NUMBER() OVER(PARTITION BY [l0].[Level1_Optional_Id] ORDER BY [l0].[Id]) AS [row] FROM [LevelTwo] AS [l0] - WHERE [l0].[Level1_Optional_Id] = [l].[Id] - ORDER BY [l0].[Id] ) AS [t] - OUTER APPLY ( - SELECT [t1].[Id], [t1].[Level3_Optional_Id], [t1].[Level3_Required_Id], [t1].[Name], [t1].[OneToMany_Optional_Inverse4Id], [t1].[OneToMany_Optional_Self_Inverse4Id], [t1].[OneToMany_Required_Inverse4Id], [t1].[OneToMany_Required_Self_Inverse4Id], [t1].[OneToOne_Optional_PK_Inverse4Id], [t1].[OneToOne_Optional_Self4Id], [t0].[c] - FROM ( - SELECT TOP(1) 1 AS [c], [l1].[Id] - FROM [LevelThree] AS [l1] - WHERE [l1].[Level2_Required_Id] = [t].[Id] - ORDER BY [l1].[Id] - ) AS [t0] - LEFT JOIN ( - SELECT [t3].[Id], [t3].[Level3_Optional_Id], [t3].[Level3_Required_Id], [t3].[Name], [t3].[OneToMany_Optional_Inverse4Id], [t3].[OneToMany_Optional_Self_Inverse4Id], [t3].[OneToMany_Required_Inverse4Id], [t3].[OneToMany_Required_Self_Inverse4Id], [t3].[OneToOne_Optional_PK_Inverse4Id], [t3].[OneToOne_Optional_Self4Id] - FROM ( - SELECT [l2].[Id], [l2].[Level3_Optional_Id], [l2].[Level3_Required_Id], [l2].[Name], [l2].[OneToMany_Optional_Inverse4Id], [l2].[OneToMany_Optional_Self_Inverse4Id], [l2].[OneToMany_Required_Inverse4Id], [l2].[OneToMany_Required_Self_Inverse4Id], [l2].[OneToOne_Optional_PK_Inverse4Id], [l2].[OneToOne_Optional_Self4Id], ROW_NUMBER() OVER(PARTITION BY [l2].[Level3_Required_Id] ORDER BY [l2].[Id]) AS [row] - FROM [LevelFour] AS [l2] - ) AS [t3] - WHERE [t3].[row] <= 1 - ) AS [t1] ON [t0].[Id] = [t1].[Level3_Required_Id] + WHERE [t].[row] <= 1 +) AS [t0] ON [l].[Id] = [t0].[Level1_Optional_Id] +LEFT JOIN ( + SELECT [t2].[c], [t2].[Id], [t2].[Level2_Required_Id] + FROM ( + SELECT 1 AS [c], [l1].[Id], [l1].[Level2_Required_Id], ROW_NUMBER() OVER(PARTITION BY [l1].[Level2_Required_Id] ORDER BY [l1].[Id]) AS [row] + FROM [LevelThree] AS [l1] ) AS [t2] -) AS [t4] + WHERE [t2].[row] <= 1 +) AS [t1] ON [t0].[Id] = [t1].[Level2_Required_Id] +LEFT JOIN ( + SELECT [t4].[Id], [t4].[Level3_Optional_Id], [t4].[Level3_Required_Id], [t4].[Name], [t4].[OneToMany_Optional_Inverse4Id], [t4].[OneToMany_Optional_Self_Inverse4Id], [t4].[OneToMany_Required_Inverse4Id], [t4].[OneToMany_Required_Self_Inverse4Id], [t4].[OneToOne_Optional_PK_Inverse4Id], [t4].[OneToOne_Optional_Self4Id] + FROM ( + SELECT [l2].[Id], [l2].[Level3_Optional_Id], [l2].[Level3_Required_Id], [l2].[Name], [l2].[OneToMany_Optional_Inverse4Id], [l2].[OneToMany_Optional_Self_Inverse4Id], [l2].[OneToMany_Required_Inverse4Id], [l2].[OneToMany_Required_Self_Inverse4Id], [l2].[OneToOne_Optional_PK_Inverse4Id], [l2].[OneToOne_Optional_Self4Id], ROW_NUMBER() OVER(PARTITION BY [l2].[Level3_Required_Id] ORDER BY [l2].[Id]) AS [row] + FROM [LevelFour] AS [l2] + ) AS [t4] + WHERE [t4].[row] <= 1 +) AS [t3] ON [t1].[Id] = [t3].[Level3_Required_Id] ORDER BY [l].[Id]"); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindIncludeQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindIncludeQuerySqlServerTest.cs index 93a4ab72640..d6fea44efc9 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindIncludeQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindIncludeQuerySqlServerTest.cs @@ -1318,19 +1318,19 @@ public override async Task Include_in_let_followed_by_FirstOrDefault(bool async) await base.Include_in_let_followed_by_FirstOrDefault(async); AssertSql( - @"SELECT [c].[CustomerID], [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t0].[OrderID0], [t0].[ProductID], [t0].[Discount], [t0].[Quantity], [t0].[UnitPrice] + @"SELECT [c].[CustomerID], [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [o0].[OrderID], [o0].[ProductID], [o0].[Discount], [o0].[Quantity], [o0].[UnitPrice] FROM [Customers] AS [c] -OUTER APPLY ( - SELECT [t].[OrderID], [t].[CustomerID], [t].[EmployeeID], [t].[OrderDate], [o0].[OrderID] AS [OrderID0], [o0].[ProductID], [o0].[Discount], [o0].[Quantity], [o0].[UnitPrice] +LEFT JOIN ( + SELECT [t].[OrderID], [t].[CustomerID], [t].[EmployeeID], [t].[OrderDate] FROM ( - SELECT TOP(1) [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] + SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], ROW_NUMBER() OVER(PARTITION BY [o].[CustomerID] ORDER BY [o].[OrderDate]) AS [row] FROM [Orders] AS [o] - WHERE [o].[CustomerID] = [c].[CustomerID] - ORDER BY [o].[OrderDate] ) AS [t] - LEFT JOIN [Order Details] AS [o0] ON [t].[OrderID] = [o0].[OrderID] -) AS [t0] -WHERE [c].[CustomerID] LIKE N'F%'"); + WHERE [t].[row] <= 1 +) AS [t0] ON [c].[CustomerID] = [t0].[CustomerID] +LEFT JOIN [Order Details] AS [o0] ON [t0].[OrderID] = [o0].[OrderID] +WHERE [c].[CustomerID] LIKE N'F%' +ORDER BY [c].[CustomerID], [t0].[OrderID], [o0].[OrderID], [o0].[ProductID]"); } public override async Task Repro9735(bool async) diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSelectQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSelectQuerySqlServerTest.cs index 38366e2e622..5fe39cdfe1c 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSelectQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindSelectQuerySqlServerTest.cs @@ -1535,7 +1535,7 @@ public override async Task Do_not_erase_projection_mapping_when_adding_single_pr await base.Do_not_erase_projection_mapping_when_adding_single_projection(async); AssertSql( - @"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [t].[OrderID], [t].[ProductID], [t].[Discount], [t].[Quantity], [t].[UnitPrice], [t].[ProductID0], [t].[Discontinued], [t].[ProductName], [t].[SupplierID], [t].[UnitPrice0], [t].[UnitsInStock], [t0].[OrderID], [t0].[ProductID], [t0].[Discount], [t0].[Quantity], [t0].[UnitPrice], [t0].[ProductID0], [t0].[Discontinued], [t0].[ProductName], [t0].[SupplierID], [t0].[UnitPrice0], [t0].[UnitsInStock], [t2].[OrderID], [t2].[ProductID], [t2].[Discount], [t2].[Quantity], [t2].[UnitPrice], [t2].[ProductID0], [t2].[Discontinued], [t2].[ProductName], [t2].[SupplierID], [t2].[UnitPrice0], [t2].[UnitsInStock] + @"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [t].[OrderID], [t].[ProductID], [t].[Discount], [t].[Quantity], [t].[UnitPrice], [t].[ProductID0], [t].[Discontinued], [t].[ProductName], [t].[SupplierID], [t].[UnitPrice0], [t].[UnitsInStock], [t0].[OrderID], [t0].[ProductID], [t0].[ProductID0], [t2].[OrderID], [t2].[ProductID], [t2].[Discount], [t2].[Quantity], [t2].[UnitPrice], [t2].[ProductID0], [t2].[Discontinued], [t2].[ProductName], [t2].[SupplierID], [t2].[UnitPrice0], [t2].[UnitsInStock], [t0].[Discount], [t0].[Quantity], [t0].[UnitPrice], [t0].[Discontinued], [t0].[ProductName], [t0].[SupplierID], [t0].[UnitPrice0], [t0].[UnitsInStock] FROM [Orders] AS [o] LEFT JOIN ( SELECT [o0].[OrderID], [o0].[ProductID], [o0].[Discount], [o0].[Quantity], [o0].[UnitPrice], [p].[ProductID] AS [ProductID0], [p].[Discontinued], [p].[ProductName], [p].[SupplierID], [p].[UnitPrice] AS [UnitPrice0], [p].[UnitsInStock] @@ -1724,25 +1724,24 @@ public override async Task Collection_include_over_result_of_single_non_scalar(b await base.Collection_include_over_result_of_single_non_scalar(async); 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].[OrderID], [t].[CustomerID], [t].[EmployeeID], [t].[OrderDate], [t].[OrderID0], [t].[ProductID], [t].[Discount], [t].[Quantity], [t].[UnitPrice], [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t0].[OrderID0], [t0].[ProductID], [t0].[Discount], [t0].[Quantity], [t0].[UnitPrice] + @"SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [t].[OrderID], [t].[CustomerID], [t].[EmployeeID], [t].[OrderDate], [t].[OrderID0], [t].[ProductID], [t].[Discount], [t].[Quantity], [t].[UnitPrice], [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [o2].[OrderID], [o2].[ProductID], [o2].[Discount], [o2].[Quantity], [o2].[UnitPrice] FROM [Customers] AS [c] LEFT JOIN ( SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [o0].[OrderID] AS [OrderID0], [o0].[ProductID], [o0].[Discount], [o0].[Quantity], [o0].[UnitPrice] FROM [Orders] AS [o] LEFT JOIN [Order Details] AS [o0] ON [o].[OrderID] = [o0].[OrderID] ) AS [t] ON [c].[CustomerID] = [t].[CustomerID] -OUTER APPLY ( - SELECT [t1].[OrderID], [t1].[CustomerID], [t1].[EmployeeID], [t1].[OrderDate], [o1].[OrderID] AS [OrderID0], [o1].[ProductID], [o1].[Discount], [o1].[Quantity], [o1].[UnitPrice] +LEFT JOIN ( + SELECT [t1].[OrderID], [t1].[CustomerID], [t1].[EmployeeID], [t1].[OrderDate] FROM ( - SELECT TOP(1) [o2].[OrderID], [o2].[CustomerID], [o2].[EmployeeID], [o2].[OrderDate] - FROM [Orders] AS [o2] - WHERE [c].[CustomerID] = [o2].[CustomerID] - ORDER BY [o2].[OrderDate] + SELECT [o1].[OrderID], [o1].[CustomerID], [o1].[EmployeeID], [o1].[OrderDate], ROW_NUMBER() OVER(PARTITION BY [o1].[CustomerID] ORDER BY [o1].[OrderDate]) AS [row] + FROM [Orders] AS [o1] ) AS [t1] - LEFT JOIN [Order Details] AS [o1] ON [t1].[OrderID] = [o1].[OrderID] -) AS [t0] + WHERE [t1].[row] <= 1 +) AS [t0] ON [c].[CustomerID] = [t0].[CustomerID] +LEFT JOIN [Order Details] AS [o2] ON [t0].[OrderID] = [o2].[OrderID] WHERE [c].[CustomerID] LIKE N'F%' -ORDER BY [c].[CustomerID], [t].[OrderID], [t].[OrderID0], [t].[ProductID]"); +ORDER BY [c].[CustomerID], [t].[OrderID], [t].[OrderID0], [t].[ProductID], [t0].[OrderID], [o2].[OrderID], [o2].[ProductID]"); } public override async Task Collection_projection_selecting_outer_element_followed_by_take(bool async) diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/ComplexNavigationsCollectionsQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/ComplexNavigationsCollectionsQuerySqliteTest.cs index 5ee85cdb63e..54c62980632 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/ComplexNavigationsCollectionsQuerySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/ComplexNavigationsCollectionsQuerySqliteTest.cs @@ -81,17 +81,5 @@ public override async Task Skip_Take_Select_collection_Skip_Take(bool async) SqliteStrings.ApplyNotSupported, (await Assert.ThrowsAsync( () => base.Skip_Take_Select_collection_Skip_Take(async))).Message); - - public override async Task Select_subquery_single_nested_subquery(bool async) - => Assert.Equal( - SqliteStrings.ApplyNotSupported, - (await Assert.ThrowsAsync( - () => base.Select_subquery_single_nested_subquery(async))).Message); - - public override async Task Select_subquery_single_nested_subquery2(bool async) - => Assert.Equal( - SqliteStrings.ApplyNotSupported, - (await Assert.ThrowsAsync( - () => base.Select_subquery_single_nested_subquery2(async))).Message); } } diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/ComplexNavigationsCollectionsSharedTypeQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/ComplexNavigationsCollectionsSharedTypeQuerySqliteTest.cs index 9c5c6fb4436..2edb8e71709 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/ComplexNavigationsCollectionsSharedTypeQuerySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/ComplexNavigationsCollectionsSharedTypeQuerySqliteTest.cs @@ -71,17 +71,5 @@ public override async Task Skip_Take_Select_collection_Skip_Take(bool async) SqliteStrings.ApplyNotSupported, (await Assert.ThrowsAsync( () => base.Skip_Take_Select_collection_Skip_Take(async))).Message); - - public override async Task Select_subquery_single_nested_subquery(bool async) - => Assert.Equal( - SqliteStrings.ApplyNotSupported, - (await Assert.ThrowsAsync( - () => base.Select_subquery_single_nested_subquery(async))).Message); - - public override async Task Select_subquery_single_nested_subquery2(bool async) - => Assert.Equal( - SqliteStrings.ApplyNotSupported, - (await Assert.ThrowsAsync( - () => base.Select_subquery_single_nested_subquery2(async))).Message); } } diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/ComplexNavigationsQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/ComplexNavigationsQuerySqliteTest.cs index 763b9182c77..e766ef765ea 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/ComplexNavigationsQuerySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/ComplexNavigationsQuerySqliteTest.cs @@ -20,11 +20,5 @@ public override async Task Let_let_contains_from_outer_let(bool async) SqliteStrings.ApplyNotSupported, (await Assert.ThrowsAsync( () => base.Let_let_contains_from_outer_let(async))).Message); - - public override void Member_pushdown_chain_3_levels_deep_entity() - => Assert.Equal( - SqliteStrings.ApplyNotSupported, - (Assert.Throws( - () => base.Member_pushdown_chain_3_levels_deep_entity())).Message); } } diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/NorthwindIncludeNoTrackingQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/NorthwindIncludeNoTrackingQuerySqliteTest.cs index c3259424e8a..aac78736446 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/NorthwindIncludeNoTrackingQuerySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/NorthwindIncludeNoTrackingQuerySqliteTest.cs @@ -52,11 +52,5 @@ public override async Task Include_collection_with_last_no_orderby(bool async) RelationalStrings.LastUsedWithoutOrderBy(nameof(Enumerable.Last)), (await Assert.ThrowsAsync( () => base.Include_collection_with_last_no_orderby(async))).Message); - - public override async Task Include_in_let_followed_by_FirstOrDefault(bool async) - => Assert.Equal( - SqliteStrings.ApplyNotSupported, - (await Assert.ThrowsAsync( - () => base.Include_in_let_followed_by_FirstOrDefault(async))).Message); } } diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/NorthwindIncludeQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/NorthwindIncludeQuerySqliteTest.cs index d22958f8a9e..b5550f1b9dc 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/NorthwindIncludeQuerySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/NorthwindIncludeQuerySqliteTest.cs @@ -41,11 +41,5 @@ public override async Task Include_collection_with_outer_apply_with_filter_non_e SqliteStrings.ApplyNotSupported, (await Assert.ThrowsAsync( () => base.Include_collection_with_outer_apply_with_filter_non_equality(async))).Message); - - public override async Task Include_in_let_followed_by_FirstOrDefault(bool async) - => Assert.Equal( - SqliteStrings.ApplyNotSupported, - (await Assert.ThrowsAsync( - () => base.Include_in_let_followed_by_FirstOrDefault(async))).Message); } } diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/NorthwindSelectQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/NorthwindSelectQuerySqliteTest.cs index 34d1db97a3b..2847d12f919 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/NorthwindSelectQuerySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/NorthwindSelectQuerySqliteTest.cs @@ -259,12 +259,6 @@ public override async Task Take_on_correlated_collection_in_first(bool async) (await Assert.ThrowsAsync( () => base.Take_on_correlated_collection_in_first(async))).Message); - public override async Task Collection_include_over_result_of_single_non_scalar(bool async) - => Assert.Equal( - SqliteStrings.ApplyNotSupported, - (await Assert.ThrowsAsync( - () => base.Collection_include_over_result_of_single_non_scalar(async))).Message); - private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); } diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/NorthwindStringIncludeQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/NorthwindStringIncludeQuerySqliteTest.cs index 00798a23c99..2734a3bfeb0 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/NorthwindStringIncludeQuerySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/NorthwindStringIncludeQuerySqliteTest.cs @@ -46,11 +46,5 @@ public override async Task Include_collection_with_last_no_orderby(bool async) RelationalStrings.LastUsedWithoutOrderBy(nameof(Enumerable.Last)), (await Assert.ThrowsAsync( () => base.Include_collection_with_last_no_orderby(async))).Message); - - public override async Task Include_in_let_followed_by_FirstOrDefault(bool async) - => Assert.Equal( - SqliteStrings.ApplyNotSupported, - (await Assert.ThrowsAsync( - () => base.Include_in_let_followed_by_FirstOrDefault(async))).Message); } }