Skip to content

Commit

Permalink
Stop pushing down to subquery for non-Concat set operations without l…
Browse files Browse the repository at this point in the history
…imit/offset

Closes #30684
  • Loading branch information
roji committed Apr 13, 2023
1 parent 6e1ab07 commit a35a709
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 8 deletions.
17 changes: 9 additions & 8 deletions src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2280,25 +2280,26 @@ private void ApplySetOperation(SetOperationType setOperationType, SelectExpressi
var entityProjectionValueComparers = new List<ValueComparer>();
var otherExpressions = new List<SqlExpression>();

if (select1.Orderings.Count != 0
|| select1.Limit != null
|| select1.Offset != null)
// Push down into a subquery if limit/offset are defined. If not, any orderings can be discarded as set operations don't preserve
// them.
// Note that in some databases it may be possible to preserve the internal ordering of the set operands for Concat, but we don't
// currently support that.
if (select1.Limit != null || select1.Offset != null)
{
// If we are pushing down here, we need to make sure to assign unique alias to subquery also.
var subqueryAlias = GenerateUniqueAlias(_usedAliases, "t");
select1.PushdownIntoSubquery();
select1._tables[0].Alias = subqueryAlias;
select1._tableReferences[0].Alias = subqueryAlias;
select1.ClearOrdering();
}
select1.ClearOrdering();

if (select2.Orderings.Count != 0
|| select2.Limit != null
|| select2.Offset != null)
// Do the same for the other side of the set operation
if (select2.Limit != null || select2.Offset != null)
{
select2.PushdownIntoSubquery();
select2.ClearOrdering();
}
select2.ClearOrdering();

if (_clientProjections.Count > 0
|| select2._clientProjections.Count > 0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -761,6 +761,38 @@ public virtual Task Union_over_scalarsubquery_scalarsubquery(bool async)
ss => ss.Set<Order>().Select(o => o.OrderDetails.Count())
.Union(ss.Set<Order>().Select(o => o.OrderDetails.Count())));

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Union_over_OrderBy_Take1(bool async)
=> AssertQueryScalar(
async,
ss => ss.Set<Order>().OrderBy(o => o.OrderID).Take(5).Select(o => o.OrderDate)
.Union(ss.Set<Order>().Select(o => o.OrderDate)));

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Union_over_OrderBy_without_Skip_Take1(bool async)
=> AssertQueryScalar(
async,
ss => ss.Set<Order>().OrderBy(o => o.OrderID).Select(o => o.OrderDate)
.Union(ss.Set<Order>().Select(o => o.OrderDate)));

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Union_over_OrderBy_Take2(bool async)
=> AssertQueryScalar(
async,
ss => ss.Set<Order>().Select(o => o.OrderDate)
.Union(ss.Set<Order>().OrderBy(o => o.OrderID).Take(5).Select(o => o.OrderDate)));

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Union_over_OrderBy_without_Skip_Take2(bool async)
=> AssertQueryScalar(
async,
ss => ss.Set<Order>().Select(o => o.OrderDate)
.Union(ss.Set<Order>().OrderBy(o => o.OrderID).Select(o => o.OrderDate)));

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task OrderBy_Take_Union(bool async)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1020,6 +1020,74 @@ SELECT COUNT(*)
""");
}

public override async Task Union_over_OrderBy_Take1(bool async)
{
await base.Union_over_OrderBy_Take1(async);

AssertSql(
"""
@__p_0='5'

SELECT [t].[OrderDate]
FROM (
SELECT TOP(@__p_0) [o].[OrderDate], [o].[OrderID]
FROM [Orders] AS [o]
ORDER BY [o].[OrderID]
) AS [t]
UNION
SELECT [o0].[OrderDate]
FROM [Orders] AS [o0]
""");
}

public override async Task Union_over_OrderBy_without_Skip_Take1(bool async)
{
await base.Union_over_OrderBy_without_Skip_Take1(async);

AssertSql(
"""
SELECT [o].[OrderDate]
FROM [Orders] AS [o]
UNION
SELECT [o0].[OrderDate]
FROM [Orders] AS [o0]
""");
}

public override async Task Union_over_OrderBy_Take2(bool async)
{
await base.Union_over_OrderBy_Take2(async);

AssertSql(
"""
@__p_0='5'

SELECT [o].[OrderDate]
FROM [Orders] AS [o]
UNION
SELECT [t0].[OrderDate]
FROM (
SELECT TOP(@__p_0) [o0].[OrderDate], [o0].[OrderID]
FROM [Orders] AS [o0]
ORDER BY [o0].[OrderID]
) AS [t0]
""");
}

public override async Task Union_over_OrderBy_without_Skip_Take2(bool async)
{
await base.Union_over_OrderBy_without_Skip_Take2(async);

AssertSql(
"""
SELECT [o].[OrderDate]
FROM [Orders] AS [o]
UNION
SELECT [o0].[OrderDate]
FROM [Orders] AS [o0]
""");
}

public override async Task OrderBy_Take_Union(bool async)
{
await base.OrderBy_Take_Union(async);
Expand Down

0 comments on commit a35a709

Please sign in to comment.