Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't simplify filter with custom functions over projection #800

Merged
merged 11 commits into from
Jun 26, 2019
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public abstract class DbCommandTree
private readonly MetadataWorkspace _metadata;
private readonly DataSpace _dataSpace;
private readonly bool _useDatabaseNullSemantics;
private readonly bool _disableFilterOverProjectionSimplificationForCustomFunctions;

internal DbCommandTree()
{
Expand All @@ -30,7 +31,7 @@ internal DbCommandTree()
// <param name="dataSpace"> The logical 'space' that metadata in the expressions used in this command tree must belong to. </param>
// <param name="useDatabaseNullSemantics">A boolean that indicates whether database null semantics are exhibited when comparing
// two operands, both of which are potentially nullable. The default value is true.</param>
internal DbCommandTree(MetadataWorkspace metadata, DataSpace dataSpace, bool useDatabaseNullSemantics = true)
internal DbCommandTree(MetadataWorkspace metadata, DataSpace dataSpace, bool useDatabaseNullSemantics = true, bool disableFilterOverProjectionSimplificationForCustomFunctions = false)
{
// Ensure the metadata workspace is non-null
DebugCheck.NotNull(metadata);
Expand All @@ -44,6 +45,7 @@ internal DbCommandTree(MetadataWorkspace metadata, DataSpace dataSpace, bool use
_metadata = metadata;
_dataSpace = dataSpace;
_useDatabaseNullSemantics = useDatabaseNullSemantics;
_disableFilterOverProjectionSimplificationForCustomFunctions = disableFilterOverProjectionSimplificationForCustomFunctions;
}

/// <summary>
Expand All @@ -68,6 +70,11 @@ public bool UseDatabaseNullSemantics
get { return _useDatabaseNullSemantics; }
}

public bool DisableFilterOverProjectionSimplificationForCustomFunctions
{
get { return _disableFilterOverProjectionSimplificationForCustomFunctions; }
}

/// <summary>
/// Gets the name and corresponding type of each parameter that can be referenced within this
/// <see
Expand Down
41 changes: 35 additions & 6 deletions src/EntityFramework/Core/Common/CommandTrees/DbQueryCommandTree.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ public sealed class DbQueryCommandTree : DbCommandTree
/// <param name="validate"> When set to false the validation of the tree is turned off. </param>
/// <param name="useDatabaseNullSemantics">A boolean that indicates whether database null semantics are exhibited when comparing
/// two operands, both of which are potentially nullable.</param>
/// <param name="disableFilterOverProjectionSimplificationForCustomFunctions">A boolean that indicates whether
/// filter over projection simplification should be used.</param>
/// <exception cref="ArgumentNullException">
/// <paramref name="metadata" />
/// or
Expand All @@ -39,8 +41,8 @@ public sealed class DbQueryCommandTree : DbCommandTree
/// <paramref name="dataSpace" />
/// does not represent a valid data space
/// </exception>
public DbQueryCommandTree(MetadataWorkspace metadata, DataSpace dataSpace, DbExpression query, bool validate, bool useDatabaseNullSemantics)
: base(metadata, dataSpace, useDatabaseNullSemantics)
public DbQueryCommandTree(MetadataWorkspace metadata, DataSpace dataSpace, DbExpression query, bool validate, bool useDatabaseNullSemantics, bool disableFilterOverProjectionSimplificationForCustomFunctions)
: base(metadata, dataSpace, useDatabaseNullSemantics, disableFilterOverProjectionSimplificationForCustomFunctions)
{
// Ensure the query expression is non-null
Check.NotNull(query, "query");
Expand All @@ -57,6 +59,32 @@ public DbQueryCommandTree(MetadataWorkspace metadata, DataSpace dataSpace, DbExp
_query = query;
}

/// <summary>
/// Constructs a new DbQueryCommandTree that uses the specified metadata workspace.
/// </summary>
/// <param name="metadata"> The metadata workspace that the command tree should use. </param>
/// <param name="dataSpace"> The logical 'space' that metadata in the expressions used in this command tree must belong to. </param>
/// <param name="query">
/// A <see cref="DbExpression" /> that defines the logic of the query.
/// </param>
/// <param name="validate"> When set to false the validation of the tree is turned off. </param>
/// <param name="useDatabaseNullSemantics">A boolean that indicates whether database null semantics are exhibited when comparing
/// two operands, both of which are potentially nullable.</param>
/// <exception cref="ArgumentNullException">
/// <paramref name="metadata" />
/// or
/// <paramref name="query" />
/// is null
/// </exception>
/// <exception cref="ArgumentException">
/// <paramref name="dataSpace" />
/// does not represent a valid data space
/// </exception>
public DbQueryCommandTree(MetadataWorkspace metadata, DataSpace dataSpace, DbExpression query, bool validate, bool useDatabaseNullSemantics)
: this(metadata, dataSpace, query, validate, useDatabaseNullSemantics, false)
{
}

/// <summary>
/// Constructs a new DbQueryCommandTree that uses the specified metadata workspace, using database null semantics.
/// </summary>
Expand All @@ -77,7 +105,7 @@ public DbQueryCommandTree(MetadataWorkspace metadata, DataSpace dataSpace, DbExp
/// does not represent a valid data space
/// </exception>
public DbQueryCommandTree(MetadataWorkspace metadata, DataSpace dataSpace, DbExpression query, bool validate)
: this(metadata, dataSpace, query, validate, true)
: this(metadata, dataSpace, query, validate, true, false)
{
}

Expand All @@ -100,7 +128,7 @@ public DbQueryCommandTree(MetadataWorkspace metadata, DataSpace dataSpace, DbExp
/// does not represent a valid data space
/// </exception>
public DbQueryCommandTree(MetadataWorkspace metadata, DataSpace dataSpace, DbExpression query)
: this(metadata, dataSpace, query, true, true)
: this(metadata, dataSpace, query, true, true, false)
{
}

Expand Down Expand Up @@ -147,15 +175,16 @@ internal override string PrintTree(ExpressionPrinter printer)
}

internal static DbQueryCommandTree FromValidExpression(MetadataWorkspace metadata, DataSpace dataSpace, DbExpression query,
bool useDatabaseNullSemantics)
bool useDatabaseNullSemantics, bool disableFilterOverProjectionSimplificationForCustomFunctions)
{
return new DbQueryCommandTree(metadata, dataSpace, query,
#if DEBUG
true,
#else
false,
#endif
useDatabaseNullSemantics);
useDatabaseNullSemantics,
disableFilterOverProjectionSimplificationForCustomFunctions);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ private DbQueryCommandTree Simplify(DbQueryCommandTree view)
queryExpression = simplifier(queryExpression);

view = DbQueryCommandTree.FromValidExpression(
view.MetadataWorkspace, view.DataSpace, queryExpression, view.UseDatabaseNullSemantics);
view.MetadataWorkspace, view.DataSpace, queryExpression, view.UseDatabaseNullSemantics, view.DisableFilterOverProjectionSimplificationForCustomFunctions);
return view;
}

Expand Down
1 change: 1 addition & 0 deletions src/EntityFramework/Core/Common/DbProviderServices.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ private static readonly ConcurrentDictionary<ExecutionStrategyKey, Func<IDbExecu

private readonly ResolverChain _resolvers = new ResolverChain();


/// <summary>
/// Constructs an EF provider that will use the <see cref="IDbDependencyResolver" /> obtained from
/// the app domain <see cref="DbConfiguration" /> Singleton for resolving EF dependencies such
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ private static ParseResult ConvertQueryStatementToDbCommandTree(Statement astSta
return new ParseResult(
DbQueryCommandTree.FromValidExpression(
sr.TypeResolver.Perspective.MetadataWorkspace, sr.TypeResolver.Perspective.TargetDataspace, converted,
useDatabaseNullSemantics: true),
useDatabaseNullSemantics: true, disableFilterOverProjectionSimplificationForCustomFunctions: false),
functionDefs);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,7 @@ internal DbQueryCommandTree GenerateFunctionView(out DiscriminatorMap discrimina
// Generate parameterized command, where command parameters are semantically the c-space function parameters.
return DbQueryCommandTree.FromValidExpression(
_containerMapping.StorageMappingItemCollection.Workspace, TargetPerspective.TargetPerspectiveDataSpace, queryExpression,
useDatabaseNullSemantics: true);
useDatabaseNullSemantics: true, disableFilterOverProjectionSimplificationForCustomFunctions: false);
}

private IEnumerable<DbExpression> GetParametersForTargetFunctionCall()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ internal DbQueryCommandTree GenerateCqt()

return DbQueryCommandTree.FromValidExpression(
m_mappingItemCollection.Workspace, TargetPerspective.TargetPerspectiveDataSpace, query,
useDatabaseNullSemantics: true);
useDatabaseNullSemantics: true, disableFilterOverProjectionSimplificationForCustomFunctions: false);
}

// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ internal override ObjectQueryExecutionPlan GetExecutionPlan(MergeOption? forMerg
ObjectQueryExecutionPlan plan = null;
var cacheEntry = _cacheEntry;
var useCSharpNullComparisonBehavior = ObjectContext.ContextOptions.UseCSharpNullComparisonBehavior;
var disableFilterOverProjectionSimplificationForCustomFunctions = ObjectContext.ContextOptions.DisableFilterOverProjectionSimplificationForCustomFunctions;
if (cacheEntry != null)
{
// The cache entry has already been retrieved, so compute the effective merge option with the following precedence:
Expand All @@ -77,7 +78,7 @@ internal override ObjectQueryExecutionPlan GetExecutionPlan(MergeOption? forMerg

// Prepare the execution plan using the command tree and the computed effective merge option
var tree = DbQueryCommandTree.FromValidExpression(
ObjectContext.MetadataWorkspace, DataSpace.CSpace, queryExpression, !useCSharpNullComparisonBehavior);
ObjectContext.MetadataWorkspace, DataSpace.CSpace, queryExpression, !useCSharpNullComparisonBehavior, disableFilterOverProjectionSimplificationForCustomFunctions);
plan = _objectQueryExecutionPlanFactory.Prepare(
ObjectContext, tree, ElementType, mergeOption, EffectiveStreamingBehavior, converter.PropagatedSpan, parameters,
converter.AliasGenerator);
Expand Down Expand Up @@ -110,7 +111,7 @@ internal override ObjectQueryExecutionPlan GetExecutionPlan(MergeOption? forMerg
var queryExpression = converter.Convert();
var parameters = converter.GetParameters();
var tree = DbQueryCommandTree.FromValidExpression(
ObjectContext.MetadataWorkspace, DataSpace.CSpace, queryExpression, !useCSharpNullComparisonBehavior);
ObjectContext.MetadataWorkspace, DataSpace.CSpace, queryExpression, !useCSharpNullComparisonBehavior, disableFilterOverProjectionSimplificationForCustomFunctions);

// If a cache entry for this compiled query's cache key was not successfully retrieved, then it must be created now.
// Note that this is only possible after converting the LINQ expression and discovering the propagated merge option,
Expand Down
8 changes: 6 additions & 2 deletions src/EntityFramework/Core/Objects/ELinq/ELinqQueryState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ internal class ELinqQueryState : ObjectQueryState
private Func<bool> _recompileRequired;
private IEnumerable<Tuple<ObjectParameter, QueryParameterExpression>> _linqParameters;
private bool _useCSharpNullComparisonBehavior;
private bool _disableFilterOverProjectionSimplificationForCustomFunctions;
private readonly ObjectQueryExecutionPlanFactory _objectQueryExecutionPlanFactory;

#endregion
Expand Down Expand Up @@ -53,6 +54,7 @@ internal ELinqQueryState(

_expression = expression;
_useCSharpNullComparisonBehavior = context.ContextOptions.UseCSharpNullComparisonBehavior;
_disableFilterOverProjectionSimplificationForCustomFunctions = context.ContextOptions.DisableFilterOverProjectionSimplificationForCustomFunctions;
_objectQueryExecutionPlanFactory = objectQueryExecutionPlanFactory ?? new ObjectQueryExecutionPlanFactory();
}

Expand Down Expand Up @@ -102,7 +104,8 @@ internal override ObjectQueryExecutionPlan GetExecutionPlan(MergeOption? forMerg
if ((explicitMergeOption.HasValue &&
explicitMergeOption.Value != plan.MergeOption)
|| _recompileRequired()
|| ObjectContext.ContextOptions.UseCSharpNullComparisonBehavior != _useCSharpNullComparisonBehavior)
|| ObjectContext.ContextOptions.UseCSharpNullComparisonBehavior != _useCSharpNullComparisonBehavior
|| ObjectContext.ContextOptions.DisableFilterOverProjectionSimplificationForCustomFunctions != _disableFilterOverProjectionSimplificationForCustomFunctions)
{
plan = null;
}
Expand Down Expand Up @@ -133,6 +136,7 @@ internal override ObjectQueryExecutionPlan GetExecutionPlan(MergeOption? forMerg
converter.PropagatedMergeOption);

_useCSharpNullComparisonBehavior = ObjectContext.ContextOptions.UseCSharpNullComparisonBehavior;
_disableFilterOverProjectionSimplificationForCustomFunctions = ObjectContext.ContextOptions.DisableFilterOverProjectionSimplificationForCustomFunctions;

// If parameters were aggregated from referenced (non-LINQ) ObjectQuery instances then add them to the parameters collection
_linqParameters = converter.GetParameters();
Expand Down Expand Up @@ -186,7 +190,7 @@ internal override ObjectQueryExecutionPlan GetExecutionPlan(MergeOption? forMerg
if (plan == null)
{
var tree = DbQueryCommandTree.FromValidExpression(
ObjectContext.MetadataWorkspace, DataSpace.CSpace, queryExpression, !_useCSharpNullComparisonBehavior);
ObjectContext.MetadataWorkspace, DataSpace.CSpace, queryExpression, !_useCSharpNullComparisonBehavior, _disableFilterOverProjectionSimplificationForCustomFunctions);
plan = _objectQueryExecutionPlanFactory.Prepare(
ObjectContext, tree, ElementType, mergeOption, EffectiveStreamingBehavior, converter.PropagatedSpan, null,
converter.AliasGenerator);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ internal override ObjectQueryExecutionPlan GetExecutionPlan(MergeOption? forMerg
Debug.Assert(queryExpression != null, "EntitySqlQueryState.Parse returned null expression?");
var tree = DbQueryCommandTree.FromValidExpression(
ObjectContext.MetadataWorkspace, DataSpace.CSpace, queryExpression,
useDatabaseNullSemantics: true);
useDatabaseNullSemantics: true, disableFilterOverProjectionSimplificationForCustomFunctions: false);
plan = _objectQueryExecutionPlanFactory.Prepare(
ObjectContext, tree, ElementType, mergeOption, EffectiveStreamingBehavior, Span, null,
DbExpressionBuilder.AliasGenerator);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public virtual ObjectQueryExecutionPlan Prepare(
if (ObjectSpanRewriter.TryRewrite(tree, span, mergeOption, aliasGenerator, out spannedQuery, out spanInfo))
{
tree = DbQueryCommandTree.FromValidExpression(
tree.MetadataWorkspace, tree.DataSpace, spannedQuery, tree.UseDatabaseNullSemantics);
tree.MetadataWorkspace, tree.DataSpace, spannedQuery, tree.UseDatabaseNullSemantics, tree.DisableFilterOverProjectionSimplificationForCustomFunctions);
}
else
{
Expand Down
2 changes: 1 addition & 1 deletion src/EntityFramework/Core/Objects/ObjectContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2869,7 +2869,7 @@ internal virtual Tuple<ObjectQueryExecutionPlan, int> PrepareRefreshQuery(

// Initialize the command tree used to issue the refresh query.
var tree = DbQueryCommandTree.FromValidExpression(
MetadataWorkspace, DataSpace.CSpace, refreshQuery, useDatabaseNullSemantics: true);
MetadataWorkspace, DataSpace.CSpace, refreshQuery, useDatabaseNullSemantics: true, disableFilterOverProjectionSimplificationForCustomFunctions: false);

// Evaluate the refresh query using ObjectQuery<T> and process the results to update the ObjectStateManager.
var mergeOption = (RefreshMode.StoreWins == refreshMode
Expand Down
2 changes: 2 additions & 0 deletions src/EntityFramework/Core/Objects/ObjectContextOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,5 +62,7 @@ internal ObjectContextOptions()
/// </remarks>
/// <returns>true if the C# NullComparison behavior should be used; otherwise, false.</returns>
public bool UseCSharpNullComparisonBehavior { get; set; }

public bool DisableFilterOverProjectionSimplificationForCustomFunctions { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,7 @@ private CTreeGenerator(Command itree, Node toConvert)
// Create the query command tree using database null semantics because this class is only
// used during the CodeGen phase which occurs after the NullSemantics phase of plan compiler.
_queryTree = DbQueryCommandTree.FromValidExpression(
itree.MetadataWorkspace, DataSpace.SSpace, queryExpression, useDatabaseNullSemantics: true);
itree.MetadataWorkspace, DataSpace.SSpace, queryExpression, useDatabaseNullSemantics: true, disableFilterOverProjectionSimplificationForCustomFunctions: false);
}

#endregion
Expand Down
7 changes: 7 additions & 0 deletions src/EntityFramework/Core/Query/PlanCompiler/FilterOpRules.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace System.Data.Entity.Core.Query.PlanCompiler
{
using System.Collections.Generic;
using System.Data.Entity.Core.Common;
using System.Data.Entity.Core.Query.InternalTrees;
using System.Diagnostics.CodeAnalysis;

Expand Down Expand Up @@ -144,6 +145,12 @@ private static bool ProcessFilterOverProject(RuleProcessingContext context, Node
return false;
}

// check to see that this predicate doesn't reference user-defined functions
if (trc.IncludeCustomFunctionOp(predicateNode, varMap))
{
return false;
}

//
// Try to remap the predicate in terms of the definitions of the Vars
//
Expand Down
5 changes: 5 additions & 0 deletions src/EntityFramework/Core/Query/PlanCompiler/PlanCompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,11 @@ internal ConstraintManager ConstraintManager
}
}

internal bool DisableFilterOverProjectionSimplificationForCustomFunctions
{
get { return m_ctree.DisableFilterOverProjectionSimplificationForCustomFunctions; }
}

#if DEBUG
/// <summary>
/// Get the current plan compiler phase
Expand Down
Loading