private DbQueryCommandTree Simplify(DbQueryCommandTree view) { DbExpression query = PatternMatchRuleProcessor.Create(PatternMatchRule.Create(ViewSimplifier._patternCollapseNestedProjection, new Func <DbExpression, DbExpression>(ViewSimplifier.CollapseNestedProjection)), PatternMatchRule.Create(ViewSimplifier._patternCase, new Func <DbExpression, DbExpression>(ViewSimplifier.SimplifyCaseStatement)), PatternMatchRule.Create(ViewSimplifier._patternNestedTphDiscriminator, new Func <DbExpression, DbExpression>(ViewSimplifier.SimplifyNestedTphDiscriminator)), PatternMatchRule.Create(ViewSimplifier._patternEntityConstructor, new Func <DbExpression, DbExpression>(this.AddFkRelatedEntityRefs)))(view.Query); view = DbQueryCommandTree.FromValidExpression(view.MetadataWorkspace, view.DataSpace, query, view.UseDatabaseNullSemantics); return(view); }
internal DbQueryCommandTree GenerateFunctionView(out DiscriminatorMap discriminatorMap) { DebugCheck.NotNull(m_mappingItemCollection); discriminatorMap = null; // Prepare the direct call of the store function as StoreFunction(@EdmFunc_p1, ..., @EdmFunc_pN). // Note that function call arguments are command parameters created from the m_edmFunction parameters. Debug.Assert(TargetFunction != null, "this.TargetFunction != null"); DbExpression storeFunctionInvoke = TargetFunction.Invoke(GetParametersForTargetFunctionCall()); // Generate the query expression producing c-space result from s-space function call(s). DbExpression queryExpression; if (m_structuralTypeMappings != null) { queryExpression = GenerateStructuralTypeResultMappingView(storeFunctionInvoke, out discriminatorMap); Debug.Assert( queryExpression != null && TypeSemantics.IsPromotableTo(queryExpression.ResultType, FunctionImport.ReturnParameter.TypeUsage), "TypeSemantics.IsPromotableTo(queryExpression.ResultType, this.FunctionImport.ReturnParameter.TypeUsage)"); } else { queryExpression = GenerateScalarResultMappingView(storeFunctionInvoke); Debug.Assert( queryExpression != null && TypeSemantics.IsEqual(queryExpression.ResultType, FunctionImport.ReturnParameter.TypeUsage), "TypeSemantics.IsEqual(queryExpression.ResultType, this.FunctionImport.ReturnParameter.TypeUsage)"); } // Generate parameterized command, where command parameters are semantically the c-space function parameters. return(DbQueryCommandTree.FromValidExpression( m_mappingItemCollection.Workspace, TargetPerspective.TargetPerspectiveDataSpace, queryExpression)); }
public virtual ObjectQueryExecutionPlan Prepare( ObjectContext context, DbQueryCommandTree tree, Type elementType, MergeOption mergeOption, Span span, IEnumerable <Tuple <ObjectParameter, QueryParameterExpression> > compiledQueryParameters, AliasGenerator aliasGenerator) { var treeResultType = tree.Query.ResultType; // Rewrite this tree for Span? DbExpression spannedQuery = null; SpanIndex spanInfo; if (ObjectSpanRewriter.TryRewrite(tree, span, mergeOption, aliasGenerator, out spannedQuery, out spanInfo)) { tree = DbQueryCommandTree.FromValidExpression(tree.MetadataWorkspace, tree.DataSpace, spannedQuery); } else { spanInfo = null; } var entityDefinition = CreateCommandDefinition(context, tree); var cacheManager = context.Perspective.MetadataWorkspace.GetQueryCacheManager(); var shaperFactory = Translator.TranslateColumnMap( _translator, elementType, cacheManager, entityDefinition.CreateColumnMap(null), context.MetadataWorkspace, spanInfo, mergeOption, false); // attempt to determine entity information for this query (e.g. which entity type and which entity set) EntitySet singleEntitySet = null; // determine if the entity set is unambiguous given the entity type if (treeResultType.EdmType.BuiltInTypeKind == BuiltInTypeKind.CollectionType && entityDefinition.EntitySets != null) { foreach (var entitySet in entityDefinition.EntitySets) { if (entitySet != null && entitySet.ElementType.IsAssignableFrom(((CollectionType)treeResultType.EdmType).TypeUsage.EdmType)) { if (singleEntitySet == null) { // found a single match singleEntitySet = entitySet; } else { // there's more than one matching entity set singleEntitySet = null; break; } } } } return(new ObjectQueryExecutionPlan( entityDefinition, shaperFactory, treeResultType, mergeOption, singleEntitySet, compiledQueryParameters)); }
/// <summary> /// The only entry point. /// Rewrites the given tree by replacing expressions that are not directly supported on SQL Server 2000 /// with alterntive translations. /// </summary> /// <param name="originalTree">The tree to rewrite</param> /// <returns>The new tree</returns> internal static DbQueryCommandTree Rewrite(DbQueryCommandTree originalTree) { Debug.Assert(originalTree != null, "OriginalTree is null"); Sql8ExpressionRewriter rewriter = new Sql8ExpressionRewriter(originalTree.MetadataWorkspace); DbExpression newQuery = rewriter.VisitExpression(originalTree.Query); return(DbQueryCommandTree.FromValidExpression(originalTree.MetadataWorkspace, originalTree.DataSpace, newQuery)); }
internal DbQueryCommandTree GenerateFunctionView( out DiscriminatorMap discriminatorMap) { discriminatorMap = (DiscriminatorMap)null; DbExpression storeFunctionInvoke = (DbExpression)this.TargetFunction.Invoke(this.GetParametersForTargetFunctionCall()); return(DbQueryCommandTree.FromValidExpression(this._containerMapping.StorageMappingItemCollection.Workspace, DataSpace.SSpace, this.m_structuralTypeMappings == null ? this.GenerateScalarResultMappingView(storeFunctionInvoke) : this.GenerateStructuralTypeResultMappingView(storeFunctionInvoke, out discriminatorMap), true)); }
/// <summary> /// Returns Cqtl query that represents a query/update mapping view for the view information that was supplied in the constructor. /// </summary> internal DbQueryCommandTree GenerateCqt() { // Generate a CqlBlock tree and then convert that to CQT. CqlBlock blockTree = GenerateCqlBlockTree(); DbExpression query = blockTree.AsCqt(true); Debug.Assert(query != null, "Null CQT generated for query/update view."); return(DbQueryCommandTree.FromValidExpression(m_mappingItemCollection.Workspace, TargetPerspective.TargetPerspectiveDataSpace, query)); }
// <summary> // Returns Cqtl query that represents a query/update mapping view for the view information that was supplied in the constructor. // </summary> internal DbQueryCommandTree GenerateCqt() { // Generate a CqlBlock tree and then convert that to CQT. var blockTree = GenerateCqlBlockTree(); var query = blockTree.AsCqt(true); Debug.Assert(query != null, "Null CQT generated for query/update view."); return(DbQueryCommandTree.FromValidExpression( m_mappingItemCollection.Workspace, TargetPerspective.TargetPerspectiveDataSpace, query, useDatabaseNullSemantics: true, disableFilterOverProjectionSimplificationForCustomFunctions: false)); }
public virtual ObjectQueryExecutionPlan Prepare( ObjectContext context, DbQueryCommandTree tree, Type elementType, MergeOption mergeOption, bool streaming, Span span, IEnumerable <Tuple <ObjectParameter, QueryParameterExpression> > compiledQueryParameters, AliasGenerator aliasGenerator) { TypeUsage resultType = tree.Query.ResultType; DbExpression newQuery; SpanIndex spanInfo; if (ObjectSpanRewriter.TryRewrite(tree, span, mergeOption, aliasGenerator, out newQuery, out spanInfo)) { tree = DbQueryCommandTree.FromValidExpression(tree.MetadataWorkspace, tree.DataSpace, newQuery, tree.UseDatabaseNullSemantics); } else { spanInfo = (SpanIndex)null; } EntityCommandDefinition commandDefinition = ObjectQueryExecutionPlanFactory.CreateCommandDefinition(context, tree); ShaperFactory resultShaperFactory = System.Data.Entity.Core.Common.Internal.Materialization.Translator.TranslateColumnMap(this._translator, elementType, commandDefinition.CreateColumnMap((DbDataReader)null), context.MetadataWorkspace, spanInfo, mergeOption, streaming, false); EntitySet singleEntitySet = (EntitySet)null; if (resultType.EdmType.BuiltInTypeKind == BuiltInTypeKind.CollectionType && commandDefinition.EntitySets != null) { foreach (EntitySet entitySet in commandDefinition.EntitySets) { if (entitySet != null && entitySet.ElementType.IsAssignableFrom(((CollectionType)resultType.EdmType).TypeUsage.EdmType)) { if (singleEntitySet == null) { singleEntitySet = entitySet; } else { singleEntitySet = (EntitySet)null; break; } } } } return(new ObjectQueryExecutionPlan((DbCommandDefinition)commandDefinition, resultShaperFactory, resultType, mergeOption, streaming, singleEntitySet, compiledQueryParameters)); }
private DbQueryCommandTree Simplify(DbQueryCommandTree view) { var simplifier = PatternMatchRuleProcessor.Create( // determines if an expression is of the form outerProject(outerProjection(innerProject(innerNew))) PatternMatchRule.Create(_patternCollapseNestedProjection, CollapseNestedProjection), // A case statement can potentially be simplified PatternMatchRule.Create(_patternCase, SimplifyCaseStatement), // Nested TPH discriminator pattern can be converted to the expected TPH discriminator pattern PatternMatchRule.Create(_patternNestedTphDiscriminator, SimplifyNestedTphDiscriminator), // Entity constructors may be augmented with FK-based related entity refs PatternMatchRule.Create(_patternEntityConstructor, AddFkRelatedEntityRefs) ); var queryExpression = view.Query; queryExpression = simplifier(queryExpression); view = DbQueryCommandTree.FromValidExpression(view.MetadataWorkspace, view.DataSpace, queryExpression); return(view); }
internal override ObjectQueryExecutionPlan GetExecutionPlan(MergeOption?forMergeOption) { Debug.Assert(Span == null, "Include span specified on compiled LINQ-based ObjectQuery instead of within the expression tree?"); // If this query has already been prepared, its current execution plan may no longer be valid. var plan = _cachedPlan; if (plan != null) { // Was a merge option specified in the call to Execute(MergeOption) or set via ObjectQuery.MergeOption? var explicitMergeOption = GetMergeOption(forMergeOption, UserSpecifiedMergeOption); // If a merge option was explicitly specified, and it does not match the plan's merge option, then the plan is no longer valid. // If the context flag UseCSharpNullComparisonBehavior was modified, then the plan is no longer valid. if ((explicitMergeOption.HasValue && explicitMergeOption.Value != plan.MergeOption) || _recompileRequired() || ObjectContext.ContextOptions.UseCSharpNullComparisonBehavior != _useCSharpNullComparisonBehavior) { plan = null; } } // The plan may have been invalidated above, or this query may never have been prepared. if (plan == null) { // Metadata is required to generate the execution plan. ObjectContext.EnsureMetadata(); // Reset internal state _recompileRequired = null; ResetParameters(); // Translate LINQ expression to a DbExpression var converter = CreateExpressionConverter(); var queryExpression = converter.Convert(); // This delegate tells us when a part of the expression tree has changed requiring a recompile. _recompileRequired = converter.RecompileRequired; // Determine the merge option, with the following precedence: // 1. A merge option was specified explicitly as the argument to Execute(MergeOption). // 2. The user has set the MergeOption property on the ObjectQuery instance. // 3. A merge option has been extracted from the 'root' query and propagated to the root of the expression tree. // 4. The global default merge option. var mergeOption = EnsureMergeOption( forMergeOption, UserSpecifiedMergeOption, converter.PropagatedMergeOption); _useCSharpNullComparisonBehavior = ObjectContext.ContextOptions.UseCSharpNullComparisonBehavior; // If parameters were aggregated from referenced (non-LINQ) ObjectQuery instances then add them to the parameters collection _linqParameters = converter.GetParameters(); if (_linqParameters != null && _linqParameters.Any()) { var currentParams = EnsureParameters(); currentParams.SetReadOnly(false); foreach (var pair in _linqParameters) { // Note that it is safe to add the parameter directly only // because parameters are cloned before they are added to the // converter's parameter collection, or they came from this // instance's parameter collection in the first place. var convertedParam = pair.Item1; currentParams.Add(convertedParam); } currentParams.SetReadOnly(true); } // Try retrieving the execution plan from the global query cache (if plan caching is enabled). QueryCacheManager cacheManager = null; LinqQueryCacheKey cacheKey = null; if (PlanCachingEnabled && !_recompileRequired()) { // Create a new cache key that reflects the current state of the Parameters collection // and the Span object (if any), and uses the specified merge option. string expressionKey; if (ExpressionKeyGen.TryGenerateKey(queryExpression, out expressionKey)) { cacheKey = new LinqQueryCacheKey( expressionKey, (null == Parameters ? 0 : Parameters.Count), (null == Parameters ? null : Parameters.GetCacheKey()), (null == converter.PropagatedSpan ? null : converter.PropagatedSpan.GetCacheKey()), mergeOption, _useCSharpNullComparisonBehavior, ElementType); cacheManager = ObjectContext.MetadataWorkspace.GetQueryCacheManager(); ObjectQueryExecutionPlan executionPlan = null; if (cacheManager.TryCacheLookup(cacheKey, out executionPlan)) { plan = executionPlan; } } } // If execution plan wasn't retrieved from the cache, build a new one and cache it. if (plan == null) { var tree = DbQueryCommandTree.FromValidExpression(ObjectContext.MetadataWorkspace, DataSpace.CSpace, queryExpression); plan = _objectQueryExecutionPlanFactory.Prepare( ObjectContext, tree, ElementType, mergeOption, converter.PropagatedSpan, null, converter.AliasGenerator); // If caching is enabled then update the cache now. // Note: the logic is the same as in EntitySqlQueryState. if (cacheKey != null) { var newEntry = new QueryCacheEntry(cacheKey, plan); QueryCacheEntry foundEntry = null; if (cacheManager.TryLookupAndAdd(newEntry, out foundEntry)) { // If TryLookupAndAdd returns 'true' then the entry was already present in the cache when the attempt to add was made. // In this case the existing execution plan should be used. plan = (ObjectQueryExecutionPlan)foundEntry.GetTarget(); } } } // Remember the current plan in the local cache, so that we don't have to recalc the key and look into the global cache // if the same instance of query gets executed more than once. _cachedPlan = plan; } // Evaluate parameter values for the query. if (_linqParameters != null) { foreach (var pair in _linqParameters) { var parameter = pair.Item1; var parameterExpression = pair.Item2; if (null != parameterExpression) { parameter.Value = parameterExpression.EvaluateParameter(null); } } } return(plan); }
internal override ObjectQueryExecutionPlan GetExecutionPlan( MergeOption?forMergeOption) { ObjectQueryExecutionPlan queryExecutionPlan1 = this._cachedPlan; if (queryExecutionPlan1 != null) { MergeOption?mergeOption = ObjectQueryState.GetMergeOption(forMergeOption, this.UserSpecifiedMergeOption); if (mergeOption.HasValue && mergeOption.Value != queryExecutionPlan1.MergeOption || (this._recompileRequired() || this.ObjectContext.ContextOptions.UseCSharpNullComparisonBehavior != this._useCSharpNullComparisonBehavior)) { queryExecutionPlan1 = (ObjectQueryExecutionPlan)null; } } if (queryExecutionPlan1 == null) { this._recompileRequired = (Func <bool>)null; this.ResetParameters(); ExpressionConverter expressionConverter = this.CreateExpressionConverter(); DbExpression dbExpression = expressionConverter.Convert(); this._recompileRequired = expressionConverter.RecompileRequired; MergeOption mergeOption = ObjectQueryState.EnsureMergeOption(forMergeOption, this.UserSpecifiedMergeOption, expressionConverter.PropagatedMergeOption); this._useCSharpNullComparisonBehavior = this.ObjectContext.ContextOptions.UseCSharpNullComparisonBehavior; this._linqParameters = expressionConverter.GetParameters(); if (this._linqParameters != null && this._linqParameters.Any <Tuple <ObjectParameter, QueryParameterExpression> >()) { ObjectParameterCollection parameterCollection = this.EnsureParameters(); parameterCollection.SetReadOnly(false); foreach (Tuple <ObjectParameter, QueryParameterExpression> linqParameter in this._linqParameters) { ObjectParameter objectParameter = linqParameter.Item1; parameterCollection.Add(objectParameter); } parameterCollection.SetReadOnly(true); } QueryCacheManager queryCacheManager = (QueryCacheManager)null; LinqQueryCacheKey key1 = (LinqQueryCacheKey)null; string key2; if (this.PlanCachingEnabled && !this._recompileRequired() && ExpressionKeyGen.TryGenerateKey(dbExpression, out key2)) { key1 = new LinqQueryCacheKey(key2, this.Parameters == null ? 0 : this.Parameters.Count, this.Parameters == null ? (string)null : this.Parameters.GetCacheKey(), expressionConverter.PropagatedSpan == null ? (string)null : expressionConverter.PropagatedSpan.GetCacheKey(), mergeOption, this.EffectiveStreamingBehavior, this._useCSharpNullComparisonBehavior, this.ElementType); queryCacheManager = this.ObjectContext.MetadataWorkspace.GetQueryCacheManager(); ObjectQueryExecutionPlan queryExecutionPlan2 = (ObjectQueryExecutionPlan)null; if (queryCacheManager.TryCacheLookup <LinqQueryCacheKey, ObjectQueryExecutionPlan>(key1, out queryExecutionPlan2)) { queryExecutionPlan1 = queryExecutionPlan2; } } if (queryExecutionPlan1 == null) { queryExecutionPlan1 = this._objectQueryExecutionPlanFactory.Prepare(this.ObjectContext, DbQueryCommandTree.FromValidExpression(this.ObjectContext.MetadataWorkspace, DataSpace.CSpace, dbExpression, !this._useCSharpNullComparisonBehavior), this.ElementType, mergeOption, this.EffectiveStreamingBehavior, expressionConverter.PropagatedSpan, (IEnumerable <Tuple <ObjectParameter, QueryParameterExpression> >)null, expressionConverter.AliasGenerator); if (key1 != null) { QueryCacheEntry inQueryCacheEntry = new QueryCacheEntry((QueryCacheKey)key1, (object)queryExecutionPlan1); QueryCacheEntry outQueryCacheEntry = (QueryCacheEntry)null; if (queryCacheManager.TryLookupAndAdd(inQueryCacheEntry, out outQueryCacheEntry)) { queryExecutionPlan1 = (ObjectQueryExecutionPlan)outQueryCacheEntry.GetTarget(); } } } this._cachedPlan = queryExecutionPlan1; } if (this._linqParameters != null) { foreach (Tuple <ObjectParameter, QueryParameterExpression> linqParameter in this._linqParameters) { ObjectParameter objectParameter = linqParameter.Item1; QueryParameterExpression parameterExpression = linqParameter.Item2; if (parameterExpression != null) { objectParameter.Value = parameterExpression.EvaluateParameter((object[])null); } } } return(queryExecutionPlan1); }
internal DbQueryCommandTree GenerateCqt() { return(DbQueryCommandTree.FromValidExpression(this.m_mappingItemCollection.Workspace, DataSpace.SSpace, this.GenerateCqlBlockTree().AsCqt(true), true)); }
internal override ObjectQueryExecutionPlan GetExecutionPlan(MergeOption?forMergeOption) { Debug.Assert(Span == null, "Include span specified on compiled LINQ-based ObjectQuery instead of within the expression tree?"); Debug.Assert(_cachedPlan == null, "Cached plan should not be set on compiled LINQ queries"); 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: // 1. The merge option specified as the argument to Execute(MergeOption), and so to this method // 2. The merge option set using ObjectQuery.MergeOption // 3. The propagated merge option as recorded in the cache entry // 4. The global default merge option. var mergeOption = EnsureMergeOption(forMergeOption, UserSpecifiedMergeOption, cacheEntry.PropagatedMergeOption); // Ask for the corresponding execution plan plan = cacheEntry.GetExecutionPlan(mergeOption, useCSharpNullComparisonBehavior); if (plan == null) { // Convert the LINQ expression to produce a command tree var converter = CreateExpressionConverter(); var queryExpression = converter.Convert(); var parameters = converter.GetParameters(); // Prepare the execution plan using the command tree and the computed effective merge option var tree = DbQueryCommandTree.FromValidExpression( ObjectContext.MetadataWorkspace, DataSpace.CSpace, queryExpression, !useCSharpNullComparisonBehavior, disableFilterOverProjectionSimplificationForCustomFunctions); plan = _objectQueryExecutionPlanFactory.Prepare( ObjectContext, tree, ElementType, mergeOption, EffectiveStreamingBehavior, converter.PropagatedSpan, parameters, converter.AliasGenerator); // Update and retrieve the execution plan plan = cacheEntry.SetExecutionPlan(plan, useCSharpNullComparisonBehavior); } } else { // This instance does not yet have a reference to a cache entry. // First, attempt to retrieve an existing cache entry. var cacheManager = ObjectContext.MetadataWorkspace.GetQueryCacheManager(); var cacheKey = new CompiledQueryCacheKey(_cacheToken); if (cacheManager.TryCacheLookup(cacheKey, out cacheEntry)) { // An entry was found in the cache, so compute the effective merge option based on its propagated merge option, // and use the UseCSharpNullComparisonBehavior flag to retrieve the corresponding execution plan. _cacheEntry = cacheEntry; var mergeOption = EnsureMergeOption(forMergeOption, UserSpecifiedMergeOption, cacheEntry.PropagatedMergeOption); plan = cacheEntry.GetExecutionPlan(mergeOption, useCSharpNullComparisonBehavior); } // If no cache entry was found or if the cache entry did not contain the required execution plan, the plan is still null at this point. if (plan == null) { // The execution plan needs to be produced, so create an appropriate expression converter and generate the query command tree. var converter = CreateExpressionConverter(); var queryExpression = converter.Convert(); var parameters = converter.GetParameters(); var tree = DbQueryCommandTree.FromValidExpression( 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, // which is required in order to create the cache entry. if (cacheEntry == null) { // Create the cache entry using this instance's cache token and the propagated merge option (which may be null) cacheEntry = new CompiledQueryCacheEntry(cacheKey, converter.PropagatedMergeOption); // Attempt to add the entry to the cache. If an entry was added in the meantime, use that entry instead. QueryCacheEntry foundEntry; if (cacheManager.TryLookupAndAdd(cacheEntry, out foundEntry)) { cacheEntry = (CompiledQueryCacheEntry)foundEntry; } // We now have a cache entry, so hold onto it for future use. _cacheEntry = cacheEntry; } // Recompute the effective merge option in case a cache entry was just constructed above var mergeOption = EnsureMergeOption(forMergeOption, UserSpecifiedMergeOption, cacheEntry.PropagatedMergeOption); // Ask the (retrieved or constructed) cache entry for the corresponding execution plan. plan = cacheEntry.GetExecutionPlan(mergeOption, useCSharpNullComparisonBehavior); if (plan == null) { // The plan is not present, so prepare it now using the computed effective merge option plan = _objectQueryExecutionPlanFactory.Prepare( ObjectContext, tree, ElementType, mergeOption, EffectiveStreamingBehavior, converter.PropagatedSpan, parameters, converter.AliasGenerator); // Update the execution plan on the cache entry. // If the execution plan was set in the meantime, SetExecutionPlan will return that value, otherwise it will return 'plan'. plan = cacheEntry.SetExecutionPlan(plan, useCSharpNullComparisonBehavior); } } } // Get parameters from the plan and set them. var currentParams = EnsureParameters(); if (plan.CompiledQueryParameters != null && plan.CompiledQueryParameters.Any()) { currentParams.SetReadOnly(false); currentParams.Clear(); foreach (var pair in plan.CompiledQueryParameters) { // Parameters retrieved from the CompiledQueryParameters collection must be cloned before being added to the query. // The cached plan is shared and when used in multithreaded scenarios failing to clone the parameter would result // in the code below updating the values of shared parameter instances saved in the cached plan and used by all // queries using that plan, regardless of the values they were actually invoked with, causing incorrect results // when those queries were later executed. // var convertedParam = pair.Item1.ShallowCopy(); var parameterExpression = pair.Item2; currentParams.Add(convertedParam); if (parameterExpression != null) { convertedParam.Value = parameterExpression.EvaluateParameter(_parameterValues); } } } currentParams.SetReadOnly(true); Debug.Assert(plan != null, "Failed to produce an execution plan?"); return(plan); }
internal override ObjectQueryExecutionPlan GetExecutionPlan( MergeOption?forMergeOption) { MergeOption mergeOption = ObjectQueryState.EnsureMergeOption(forMergeOption, this.UserSpecifiedMergeOption); ObjectQueryExecutionPlan queryExecutionPlan1 = this._cachedPlan; if (queryExecutionPlan1 != null) { if (queryExecutionPlan1.MergeOption == mergeOption && queryExecutionPlan1.Streaming == this.EffectiveStreamingBehavior) { return(queryExecutionPlan1); } queryExecutionPlan1 = (ObjectQueryExecutionPlan)null; } QueryCacheManager queryCacheManager = (QueryCacheManager)null; EntitySqlQueryCacheKey key = (EntitySqlQueryCacheKey)null; if (this.PlanCachingEnabled) { key = new EntitySqlQueryCacheKey(this.ObjectContext.DefaultContainerName, this._queryText, this.Parameters == null ? 0 : this.Parameters.Count, this.Parameters == null ? (string)null : this.Parameters.GetCacheKey(), this.Span == null ? (string)null : this.Span.GetCacheKey(), mergeOption, this.EffectiveStreamingBehavior, this.ElementType); queryCacheManager = this.ObjectContext.MetadataWorkspace.GetQueryCacheManager(); ObjectQueryExecutionPlan queryExecutionPlan2 = (ObjectQueryExecutionPlan)null; if (queryCacheManager.TryCacheLookup <EntitySqlQueryCacheKey, ObjectQueryExecutionPlan>(key, out queryExecutionPlan2)) { queryExecutionPlan1 = queryExecutionPlan2; } } if (queryExecutionPlan1 == null) { queryExecutionPlan1 = this._objectQueryExecutionPlanFactory.Prepare(this.ObjectContext, DbQueryCommandTree.FromValidExpression(this.ObjectContext.MetadataWorkspace, DataSpace.CSpace, this.Parse(), true), this.ElementType, mergeOption, this.EffectiveStreamingBehavior, this.Span, (IEnumerable <Tuple <ObjectParameter, QueryParameterExpression> >)null, DbExpressionBuilder.AliasGenerator); if (key != null) { QueryCacheEntry inQueryCacheEntry = new QueryCacheEntry((QueryCacheKey)key, (object)queryExecutionPlan1); QueryCacheEntry outQueryCacheEntry = (QueryCacheEntry)null; if (queryCacheManager.TryLookupAndAdd(inQueryCacheEntry, out outQueryCacheEntry)) { queryExecutionPlan1 = (ObjectQueryExecutionPlan)outQueryCacheEntry.GetTarget(); } } } if (this.Parameters != null) { this.Parameters.SetReadOnly(true); } this._cachedPlan = queryExecutionPlan1; return(queryExecutionPlan1); }
internal override ObjectQueryExecutionPlan GetExecutionPlan(MergeOption?forMergeOption) { // Determine the required merge option, with the following precedence: // 1. The merge option specified to Execute(MergeOption) as forMergeOption. // 2. The merge option set via ObjectQuery.MergeOption. // 3. The global default merge option. var mergeOption = EnsureMergeOption(forMergeOption, UserSpecifiedMergeOption); // If a cached plan is present, then it can be reused if it has the required merge option and streaming behavior // (since span and parameters cannot change between executions). However, if the cached // plan does not have the required merge option we proceed as if it were not present. var plan = _cachedPlan; if (plan != null) { if (plan.MergeOption == mergeOption && plan.Streaming == EffectiveStreamingBehavior) { return(plan); } else { plan = null; } } // There is no cached plan (or it was cleared), so the execution plan must be retrieved from // the global query cache (if plan caching is enabled) or rebuilt for the required merge option. QueryCacheManager cacheManager = null; EntitySqlQueryCacheKey cacheKey = null; if (PlanCachingEnabled) { // Create a new cache key that reflects the current state of the Parameters collection // and the Span object (if any), and uses the specified merge option. cacheKey = new EntitySqlQueryCacheKey( ObjectContext.DefaultContainerName, _queryText, (null == Parameters ? 0 : Parameters.Count), (null == Parameters ? null : Parameters.GetCacheKey()), (null == Span ? null : Span.GetCacheKey()), mergeOption, EffectiveStreamingBehavior, ElementType); cacheManager = ObjectContext.MetadataWorkspace.GetQueryCacheManager(); ObjectQueryExecutionPlan executionPlan = null; if (cacheManager.TryCacheLookup(cacheKey, out executionPlan)) { plan = executionPlan; } } if (plan == null) { // Either caching is not enabled or the execution plan was not found in the cache var queryExpression = Parse(); Debug.Assert(queryExpression != null, "EntitySqlQueryState.Parse returned null expression?"); var tree = DbQueryCommandTree.FromValidExpression( ObjectContext.MetadataWorkspace, DataSpace.CSpace, queryExpression, useDatabaseNullSemantics: true); plan = _objectQueryExecutionPlanFactory.Prepare( ObjectContext, tree, ElementType, mergeOption, EffectiveStreamingBehavior, Span, null, DbExpressionBuilder.AliasGenerator); // If caching is enabled then update the cache now. // Note: the logic is the same as in ELinqQueryState. if (cacheKey != null) { var newEntry = new QueryCacheEntry(cacheKey, plan); QueryCacheEntry foundEntry = null; if (cacheManager.TryLookupAndAdd(newEntry, out foundEntry)) { // If TryLookupAndAdd returns 'true' then the entry was already present in the cache when the attempt to add was made. // In this case the existing execution plan should be used. plan = (ObjectQueryExecutionPlan)foundEntry.GetTarget(); } } } if (Parameters != null) { Parameters.SetReadOnly(true); } // Update the cached plan with the newly retrieved/prepared plan _cachedPlan = plan; // Return the execution plan return(plan); }
internal static ObjectQueryExecutionPlan Prepare(ObjectContext context, DbQueryCommandTree tree, Type elementType, MergeOption mergeOption, Span span, CompiledQueryParameters compiledQueryParameters, AliasGenerator aliasGenerator) { TypeUsage treeResultType = tree.Query.ResultType; // Rewrite this tree for Span? DbExpression spannedQuery = null; SpanIndex spanInfo; if (ObjectSpanRewriter.TryRewrite(tree, span, mergeOption, aliasGenerator, out spannedQuery, out spanInfo)) { tree = DbQueryCommandTree.FromValidExpression(tree.MetadataWorkspace, tree.DataSpace, spannedQuery); } else { spanInfo = null; } DbConnection connection = context.Connection; DbCommandDefinition definition = null; // The connection is required to get to the CommandDefinition builder. if (connection == null) { throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.ObjectQuery_InvalidConnection); } DbProviderServices services = DbProviderServices.GetProviderServices(connection); try { definition = services.CreateCommandDefinition(tree); } catch (EntityCommandCompilationException) { // If we're running against EntityCommand, we probably already caught the providers' // exception and wrapped it, we don't want to do that again, so we'll just rethrow // here instead. throw; } catch (Exception e) { // we should not be wrapping all exceptions if (EntityUtil.IsCatchableExceptionType(e)) { // we don't wan't folks to have to know all the various types of exceptions that can // occur, so we just rethrow a CommandDefinitionException and make whatever we caught // the inner exception of it. throw EntityUtil.CommandCompilation(System.Data.Entity.Strings.EntityClient_CommandDefinitionPreparationFailed, e); } throw; } if (definition == null) { throw EntityUtil.ProviderDoesNotSupportCommandTrees(); } EntityCommandDefinition entityDefinition = (EntityCommandDefinition)definition; QueryCacheManager cacheManager = context.Perspective.MetadataWorkspace.GetQueryCacheManager(); ShaperFactory shaperFactory = ShaperFactory.Create(elementType, cacheManager, entityDefinition.CreateColumnMap(null), context.MetadataWorkspace, spanInfo, mergeOption, false); // attempt to determine entity information for this query (e.g. which entity type and which entity set) //EntityType rootEntityType = null; EntitySet singleEntitySet = null; if (treeResultType.EdmType.BuiltInTypeKind == BuiltInTypeKind.CollectionType) { // determine if the entity set is unambiguous given the entity type if (null != entityDefinition.EntitySets) { foreach (EntitySet entitySet in entityDefinition.EntitySets) { if (null != entitySet) { if (entitySet.ElementType.IsAssignableFrom(((CollectionType)treeResultType.EdmType).TypeUsage.EdmType)) { if (singleEntitySet == null) { // found a single match singleEntitySet = entitySet; } else { // there's more than one matching entity set singleEntitySet = null; break; } } } } } } return(new ObjectQueryExecutionPlan(definition, shaperFactory, treeResultType, mergeOption, singleEntitySet, compiledQueryParameters)); }
internal override ObjectQueryExecutionPlan GetExecutionPlan( MergeOption?forMergeOption) { ObjectQueryExecutionPlan queryExecutionPlan = (ObjectQueryExecutionPlan)null; CompiledQueryCacheEntry compiledQueryCacheEntry = this._cacheEntry; bool comparisonBehavior = this.ObjectContext.ContextOptions.UseCSharpNullComparisonBehavior; if (compiledQueryCacheEntry != null) { MergeOption mergeOption = ObjectQueryState.EnsureMergeOption(forMergeOption, this.UserSpecifiedMergeOption, compiledQueryCacheEntry.PropagatedMergeOption); queryExecutionPlan = compiledQueryCacheEntry.GetExecutionPlan(mergeOption, comparisonBehavior); if (queryExecutionPlan == null) { ExpressionConverter expressionConverter = this.CreateExpressionConverter(); DbExpression query = expressionConverter.Convert(); IEnumerable <Tuple <ObjectParameter, QueryParameterExpression> > parameters = expressionConverter.GetParameters(); ObjectQueryExecutionPlan newPlan = this._objectQueryExecutionPlanFactory.Prepare(this.ObjectContext, DbQueryCommandTree.FromValidExpression(this.ObjectContext.MetadataWorkspace, DataSpace.CSpace, query, !comparisonBehavior), this.ElementType, mergeOption, this.EffectiveStreamingBehavior, expressionConverter.PropagatedSpan, parameters, expressionConverter.AliasGenerator); queryExecutionPlan = compiledQueryCacheEntry.SetExecutionPlan(newPlan, comparisonBehavior); } } else { QueryCacheManager queryCacheManager = this.ObjectContext.MetadataWorkspace.GetQueryCacheManager(); CompiledQueryCacheKey key = new CompiledQueryCacheKey(this._cacheToken); if (queryCacheManager.TryCacheLookup <CompiledQueryCacheKey, CompiledQueryCacheEntry>(key, out compiledQueryCacheEntry)) { this._cacheEntry = compiledQueryCacheEntry; MergeOption mergeOption = ObjectQueryState.EnsureMergeOption(forMergeOption, this.UserSpecifiedMergeOption, compiledQueryCacheEntry.PropagatedMergeOption); queryExecutionPlan = compiledQueryCacheEntry.GetExecutionPlan(mergeOption, comparisonBehavior); } if (queryExecutionPlan == null) { ExpressionConverter expressionConverter = this.CreateExpressionConverter(); DbExpression query = expressionConverter.Convert(); IEnumerable <Tuple <ObjectParameter, QueryParameterExpression> > parameters = expressionConverter.GetParameters(); DbQueryCommandTree tree = DbQueryCommandTree.FromValidExpression(this.ObjectContext.MetadataWorkspace, DataSpace.CSpace, query, !comparisonBehavior); if (compiledQueryCacheEntry == null) { compiledQueryCacheEntry = new CompiledQueryCacheEntry((QueryCacheKey)key, expressionConverter.PropagatedMergeOption); QueryCacheEntry outQueryCacheEntry; if (queryCacheManager.TryLookupAndAdd((QueryCacheEntry)compiledQueryCacheEntry, out outQueryCacheEntry)) { compiledQueryCacheEntry = (CompiledQueryCacheEntry)outQueryCacheEntry; } this._cacheEntry = compiledQueryCacheEntry; } MergeOption mergeOption = ObjectQueryState.EnsureMergeOption(forMergeOption, this.UserSpecifiedMergeOption, compiledQueryCacheEntry.PropagatedMergeOption); queryExecutionPlan = compiledQueryCacheEntry.GetExecutionPlan(mergeOption, comparisonBehavior); if (queryExecutionPlan == null) { ObjectQueryExecutionPlan newPlan = this._objectQueryExecutionPlanFactory.Prepare(this.ObjectContext, tree, this.ElementType, mergeOption, this.EffectiveStreamingBehavior, expressionConverter.PropagatedSpan, parameters, expressionConverter.AliasGenerator); queryExecutionPlan = compiledQueryCacheEntry.SetExecutionPlan(newPlan, comparisonBehavior); } } } ObjectParameterCollection parameterCollection = this.EnsureParameters(); if (queryExecutionPlan.CompiledQueryParameters != null && queryExecutionPlan.CompiledQueryParameters.Any <Tuple <ObjectParameter, QueryParameterExpression> >()) { parameterCollection.SetReadOnly(false); parameterCollection.Clear(); foreach (Tuple <ObjectParameter, QueryParameterExpression> compiledQueryParameter in queryExecutionPlan.CompiledQueryParameters) { ObjectParameter objectParameter = compiledQueryParameter.Item1.ShallowCopy(); QueryParameterExpression parameterExpression = compiledQueryParameter.Item2; parameterCollection.Add(objectParameter); if (parameterExpression != null) { objectParameter.Value = parameterExpression.EvaluateParameter(this._parameterValues); } } } parameterCollection.SetReadOnly(true); return(queryExecutionPlan); }