Skip to content

Commit

Permalink
Query: Convert "APPLY" to "JOIN" with skip via RowNumber (#21282)
Browse files Browse the repository at this point in the history
Resolves #17326

This enables filtered include with skip to be translated for Sqlite
  • Loading branch information
smitpatel authored Jun 16, 2020
1 parent b3f2aa7 commit 2c0a6ae
Show file tree
Hide file tree
Showing 6 changed files with 199 additions and 136 deletions.
51 changes: 43 additions & 8 deletions src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1835,9 +1835,10 @@ private void AddJoin(
if (joinType == JoinType.CrossApply
|| joinType == JoinType.OuterApply)
{
// Doing for limit only since limit + offset may need sum
var limit = innerSelectExpression.Limit;
var offset = innerSelectExpression.Offset;
innerSelectExpression.Limit = null;
innerSelectExpression.Offset = null;

joinPredicate = TryExtractJoinKey(innerSelectExpression);
if (joinPredicate != null)
Expand All @@ -1851,10 +1852,14 @@ private void AddJoin(
{
innerSelectExpression.ApplyLimit(limit);
}
if (offset != null)
{
innerSelectExpression.ApplyOffset(offset);
}
}
else
{
if (limit != null)
if (limit != null || offset != null)
{
var partitions = new List<SqlExpression>();
GetPartitions(joinPredicate, partitions);
Expand All @@ -1864,19 +1869,44 @@ private void AddJoin(
? innerSelectExpression._identifier.Select(e => new OrderingExpression(e.Column, true))
: new[] { new OrderingExpression(new SqlFragmentExpression("(SELECT 1)"), true) };

var rowNumberExpression = new RowNumberExpression(partitions, orderings.ToList(), limit.TypeMapping);
var rowNumberExpression = new RowNumberExpression(partitions, orderings.ToList(), (limit ?? offset).TypeMapping);
innerSelectExpression.ClearOrdering();

var projectionMappings = innerSelectExpression.PushdownIntoSubquery();
var subquery = (SelectExpression)innerSelectExpression.Tables[0];

joinPredicate = new SqlRemappingVisitor(
projectionMappings, subquery)
.Remap(joinPredicate);
joinPredicate = new SqlRemappingVisitor(projectionMappings, subquery).Remap(joinPredicate);

var outerColumn = subquery.GenerateOuterColumn(rowNumberExpression, "row");
var predicate = new SqlBinaryExpression(
ExpressionType.LessThanOrEqual, outerColumn, limit, typeof(bool), joinPredicate.TypeMapping);
SqlExpression offsetPredicate = null;
SqlExpression limitPredicate = null;
if (offset != null)
{
offsetPredicate = new SqlBinaryExpression(
ExpressionType.LessThan, offset, outerColumn, typeof(bool), joinPredicate.TypeMapping);
}
if (limit != null)
{
if (offset != null)
{
limit = offset is SqlConstantExpression offsetConstant
&& limit is SqlConstantExpression limitConstant
? (SqlExpression)new SqlConstantExpression(
Constant((int)offsetConstant.Value + (int)limitConstant.Value),
limit.TypeMapping)
: new SqlBinaryExpression(ExpressionType.Add, offset, limit, limit.Type, limit.TypeMapping);
}

limitPredicate = new SqlBinaryExpression(
ExpressionType.LessThanOrEqual, outerColumn, limit, typeof(bool), joinPredicate.TypeMapping);
}

var predicate = offsetPredicate != null
? limitPredicate != null
? new SqlBinaryExpression(
ExpressionType.AndAlso, offsetPredicate, limitPredicate, typeof(bool), joinPredicate.TypeMapping)
: offsetPredicate
: limitPredicate;
innerSelectExpression.ApplyPredicate(predicate);
}

Expand All @@ -1889,6 +1919,11 @@ private void AddJoin(
{
innerSelectExpression.ApplyLimit(limit);
}

if (offset != null)
{
innerSelectExpression.ApplyOffset(offset);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4133,6 +4133,12 @@ public override Task All_client_and_server_top_level(bool async)
public override Task All_client_or_server_top_level(bool async)
=> base.All_client_or_server_top_level(async);

[ConditionalTheory(Skip = "Issue #17246")]
public override Task Single_non_scalar_projection_after_skip_uses_join(bool async)
{
return base.Single_non_scalar_projection_after_skip_uses_join(async);
}

private void AssertSql(params string[] expected)
=> Fixture.TestSqlLoggerFactory.AssertBaseline(expected);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5970,5 +5970,15 @@ public virtual Task Using_static_string_Equals_with_StringComparison_throws_info
ss => ss.Set<Customer>().Where(c => string.Equals(c.CustomerID, "ALFKI", StringComparison.InvariantCulture))),
CoreStrings.QueryUnableToTranslateStringEqualsWithStringComparison);
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Single_non_scalar_projection_after_skip_uses_join(bool async)
{
return AssertQuery(
async,
ss => ss.Set<Customer>().Select(c => c.Orders.OrderBy(o => o.OrderDate).Skip(2).FirstOrDefault()),
entryCount: 86);
}
}
}
Loading

0 comments on commit 2c0a6ae

Please sign in to comment.