Skip to content

Commit

Permalink
Query: Lift Subqueries when outer QM's select can be pushed down into…
Browse files Browse the repository at this point in the history
… subqueryQM

Apply QueryOptimizer recursively on all QueryModels
Introduce SubQueryExpressionVisitor<IQueryModelVisitor> to apply QMV's recursively.
  • Loading branch information
smitpatel committed Mar 10, 2017
1 parent 7b9803d commit 9d27856
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 181 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -487,14 +487,14 @@ private static Expression UnfoldStructuralComparison(ExpressionType expressionTy
if (leftExpressions.Length == 1
&& expressionType == ExpressionType.Equal)
{
var translatedExpression = TransformNullComparison(leftExpressions[0], rightExpressions[0], binaryExpression.NodeType)
?? Expression.MakeBinary(expressionType, leftExpressions[0], rightExpressions[0]);
return Expression.AndAlso(translatedExpression, Expression.Constant(true, translatedExpression.Type));
var translatedExpression = TransformNullComparison(leftExpressions[0], rightExpressions[0], expressionType)
?? Expression.Equal(leftExpressions[0], rightExpressions[0]);
return Expression.AndAlso(translatedExpression, Expression.Constant(true, typeof(bool)));
}

return leftExpressions
.Zip(rightExpressions, (l, r) =>
TransformNullComparison(l, r, binaryExpression.NodeType)
TransformNullComparison(l, r, expressionType)
?? Expression.MakeBinary(expressionType, l, r))
.Aggregate((e1, e2) =>
expressionType == ExpressionType.Equal
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Linq.Expressions;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Utilities;
using Remotion.Linq;
using Remotion.Linq.Clauses.Expressions;

namespace Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal
{
/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public class TransformingQueryModelExpressionVisitor<TVisitor> : ExpressionVisitorBase
where TVisitor : IQueryModelVisitor
{
/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
protected virtual TVisitor TransformingQueryModelVisitor { get; }

/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public TransformingQueryModelExpressionVisitor([NotNull] TVisitor transformingQueryModelVisitor)
{
Check.NotNull(transformingQueryModelVisitor, nameof(transformingQueryModelVisitor));

TransformingQueryModelVisitor = transformingQueryModelVisitor;
}

/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
protected override Expression VisitSubQuery(SubQueryExpression expression)
{
TransformingQueryModelVisitor.VisitQueryModel(expression.QueryModel);

return base.VisitSubQuery(expression);
}
}
}
12 changes: 8 additions & 4 deletions src/EFCore/Query/Internal/QueryOptimizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Extensions.Internal;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal;
using Microsoft.EntityFrameworkCore.Query.ResultOperators;
using Microsoft.EntityFrameworkCore.Utilities;
using Remotion.Linq;
Expand Down Expand Up @@ -76,6 +77,7 @@ public virtual void Optimize(
_queryAnnotations = queryAnnotations;

VisitQueryModel(queryModel);
queryModel.TransformExpressions(e => new TransformingQueryModelExpressionVisitor<QueryOptimizer>(this).Visit(e));
}

/// <summary>
Expand Down Expand Up @@ -175,11 +177,13 @@ protected override void FlattenSubQuery(

VisitQueryModel(subQueryModel);

if (subQueryModel.ResultOperators
.All(ro => ro is CastResultOperator)
if ((subQueryModel.ResultOperators.All(ro => ro is CastResultOperator)
&& !subQueryModel.BodyClauses.Any(bc => bc is OrderByClause)
|| queryModel.IsIdentityQuery()
&& !queryModel.ResultOperators.Any())
|| (!queryModel.BodyClauses.Any()
&& !queryModel.ResultOperators.Any()
&& !subQueryModel.ResultOperators.Any(ro => ro is GroupResultOperator)))
{
string itemName;

Expand Down Expand Up @@ -257,14 +261,14 @@ public override void VisitResultOperator(ResultOperatorBase resultOperator, Quer
{
var oldQuerySource = queryModel.MainFromClause;

var entityQueryProvider
var entityQueryProvider
= ((oldQuerySource.FromExpression as ConstantExpression)?.Value as IQueryable)?.Provider as IAsyncQueryProvider;

if (entityQueryProvider != null)
{
queryModel.ResultOperators.RemoveAt(index);

var newMainFromClause
var newMainFromClause
= new MainFromClause(
oldQuerySource.ItemName,
entityType.ClrType,
Expand Down
Loading

0 comments on commit 9d27856

Please sign in to comment.