internal ExplicitDiscriminatorMap(DiscriminatorMap template) { m_typeMap = template.TypeMap; m_discriminatorProperty = template.Discriminator.Property; m_properties = template.PropertyMap.Select(propertyValuePair => propertyValuePair.Key) .ToList().AsReadOnly(); }
/// <summary> /// Creates generated view object for the combination of the <paramref name="extent"/> and the <paramref name="type"/>. /// This constructor is used for regular cell-based view generation. /// </summary> internal static GeneratedView CreateGeneratedView(EntitySetBase extent, EdmType type, DbQueryCommandTree commandTree, string eSQL, StorageMappingItemCollection mappingItemCollection, ConfigViewGenerator config) { // If config.GenerateEsql is specified, eSQL must be non-null. // If config.GenerateEsql is false, commandTree is non-null except the case when loading pre-compiled eSQL views. Debug.Assert(!config.GenerateEsql || !String.IsNullOrEmpty(eSQL), "eSQL must be specified"); DiscriminatorMap discriminatorMap = null; if (commandTree != null) { commandTree = ViewSimplifier.SimplifyView(extent, commandTree); // See if the view matches the "discriminated" pattern (allows simplification of generated store commands) if (extent.BuiltInTypeKind == BuiltInTypeKind.EntitySet) { if (DiscriminatorMap.TryCreateDiscriminatorMap((EntitySet)extent, commandTree.Query, out discriminatorMap)) { Debug.Assert(discriminatorMap != null, "discriminatorMap == null after it has been created"); } } } return(new GeneratedView(extent, type, commandTree, eSQL, discriminatorMap, mappingItemCollection, config)); }
private GeneratedView(EntitySetBase extent, EdmType type, DbQueryCommandTree commandTree, string eSQL, DiscriminatorMap discriminatorMap, StorageMappingItemCollection mappingItemCollection, ConfigViewGenerator config) { // At least one of the commandTree or eSQL must be specified. // Both are specified in the case of user-defined views. Debug.Assert(commandTree != null || !String.IsNullOrEmpty(eSQL), "commandTree or eSQL must be specified"); m_extent = extent; m_type = type; m_commandTree = commandTree; m_eSQL = eSQL; m_discriminatorMap = discriminatorMap; m_mappingItemCollection = mappingItemCollection; m_config = config; if (m_config.IsViewTracing) { StringBuilder trace = new StringBuilder(1024); this.ToCompactString(trace); Helpers.FormatTraceLine("CQL view for {0}", trace.ToString()); } }
/// <summary> /// Given an extent and its corresponding view, invokes the parser to check if the view definition is syntactically correct. /// Iff parsing succeeds: <paramref name="commandTree"/> and <paramref name="discriminatorMap"/> are set to the parse result and method returns true, /// otherwise if parser has thrown a catchable exception, it is returned via <paramref name="parserException"/> parameter, /// otherwise exception is re-thrown. /// </summary> private static bool TryParseView(string eSQL, bool isUserSpecified, EntitySetBase extent, StorageMappingItemCollection mappingItemCollection, ConfigViewGenerator config, out DbQueryCommandTree commandTree, out DiscriminatorMap discriminatorMap, out Exception parserException) { commandTree = null; discriminatorMap = null; parserException = null; // We do not catch any internal exceptions any more config.StartSingleWatch(PerfType.ViewParsing); try { // If it is a user specified view, allow all queries. Otherwise parse the view in the restricted mode. ParserOptions.CompilationMode compilationMode = ParserOptions.CompilationMode.RestrictedViewGenerationMode; if (isUserSpecified) { compilationMode = ParserOptions.CompilationMode.UserViewGenerationMode; } Debug.Assert(!String.IsNullOrEmpty(eSQL), "eSQL query is not specified"); commandTree = (DbQueryCommandTree)ExternalCalls.CompileView(eSQL, mappingItemCollection, compilationMode); // For non user-specified views, perform simplification. if (!isUserSpecified) { commandTree = ViewSimplifier.SimplifyView(extent, commandTree); } // See if the view matches the "discriminated" pattern (allows simplification of generated store commands) if (extent.BuiltInTypeKind == BuiltInTypeKind.EntitySet) { if (DiscriminatorMap.TryCreateDiscriminatorMap((EntitySet)extent, commandTree.Query, out discriminatorMap)) { Debug.Assert(discriminatorMap != null, "discriminatorMap == null after it has been created"); } } } catch (Exception e) { // Catching all the exception types since Query parser seems to be throwing veriety of // exceptions - EntityException, ArgumentException, ArgumentNullException etc. if (EntityUtil.IsCatchableExceptionType(e)) { parserException = e; } else { throw; } } finally { config.StopSingleWatch(PerfType.ViewParsing); } Debug.Assert(commandTree != null || parserException != null, "Either commandTree or parserException is expected."); // Note: m_commandTree might have been initialized by a previous call to this method, so in consequent calls it might occur that // both m_commandTree and parserException are not null - this would mean that the last parse attempt failed, but m_commandTree value is // preserved from the previous call. return parserException == null; }
/// <summary> /// Determines whether the given query view matches the discriminator map pattern. /// </summary> internal static bool TryCreateDiscriminatorMap(EntitySet entitySet, DbExpression queryView, out DiscriminatorMap discriminatorMap) { discriminatorMap = null; if (queryView.ExpressionKind != DbExpressionKind.Project) { return false; } var project = (DbProjectExpression)queryView; if (project.Projection.ExpressionKind != DbExpressionKind.Case) { return false; } var caseExpression = (DbCaseExpression)project.Projection; if (project.Projection.ResultType.EdmType.BuiltInTypeKind != BuiltInTypeKind.EntityType) { return false; } // determine value domain by walking filter if (project.Input.Expression.ExpressionKind != DbExpressionKind.Filter) { return false; } var filterExpression = (DbFilterExpression)project.Input.Expression; HashSet<object> discriminatorDomain = new HashSet<object>(); if (!ViewSimplifier.TryMatchDiscriminatorPredicate(filterExpression, (equalsExp, discriminatorValue) => discriminatorDomain.Add(discriminatorValue))) { return false; } var typeMap = new List<KeyValuePair<object, EntityType>>(); var propertyMap = new Dictionary<EdmProperty, DbExpression>(); var relPropertyMap = new Dictionary<Query.InternalTrees.RelProperty, DbExpression>(); var typeToRelPropertyMap = new Dictionary<EntityType, List<Query.InternalTrees.RelProperty>>(); DbPropertyExpression discriminator = null; EdmProperty discriminatorProperty = null; for (int i = 0; i < caseExpression.When.Count; i++) { var when = caseExpression.When[i]; var then = caseExpression.Then[i]; var projectionVariableName = project.Input.VariableName; DbPropertyExpression currentDiscriminator; object discriminatorValue; if (!ViewSimplifier.TryMatchPropertyEqualsValue(when, projectionVariableName, out currentDiscriminator, out discriminatorValue)) { return false; } // must be the same discriminator in every case if (null == discriminatorProperty) { discriminatorProperty = (EdmProperty)currentDiscriminator.Property; } else if (discriminatorProperty != currentDiscriminator.Property) { return false; } discriminator = currentDiscriminator; // right hand side must be entity type constructor EntityType currentType; if (!TryMatchEntityTypeConstructor(then, propertyMap, relPropertyMap, typeToRelPropertyMap, out currentType)) { return false; } // remember type + discriminator value typeMap.Add(new KeyValuePair<object, EntityType>(discriminatorValue, currentType)); // remove discriminator value from domain discriminatorDomain.Remove(discriminatorValue); } // make sure only one member of discriminator domain remains... if (1 != discriminatorDomain.Count) { return false; } // check default case EntityType elseType; if (null == caseExpression.Else || !TryMatchEntityTypeConstructor(caseExpression.Else, propertyMap, relPropertyMap, typeToRelPropertyMap, out elseType)) { return false; } typeMap.Add(new KeyValuePair<object, EntityType>(discriminatorDomain.Single(), elseType)); // Account for cases where some type in the hierarchy specifies a rel-property, but another // type in the hierarchy does not if (!CheckForMissingRelProperties(relPropertyMap, typeToRelPropertyMap)) { return false; } // since the store may right-pad strings, ensure discriminator values are unique in their trimmed // form var discriminatorValues = typeMap.Select(map => map.Key); int uniqueValueCount = discriminatorValues.Distinct(TrailingSpaceComparer.Instance).Count(); int valueCount = typeMap.Count; if (uniqueValueCount != valueCount) { return false; } discriminatorMap = new DiscriminatorMap(discriminator, typeMap, propertyMap, relPropertyMap, entitySet); return true; }
private DbExpression GenerateStructuralTypeResultMappingView(DbExpression storeFunctionInvoke, IList<EdmSchemaError> errors, out DiscriminatorMap discriminatorMap) { Debug.Assert(m_structuralTypeMappings != null && m_structuralTypeMappings.Count > 0, "m_structuralTypeMappings != null && m_structuralTypeMappings.Count > 0"); discriminatorMap = null; // Process explicit structural type mappings. The mapping is based on the direct call of the store function // wrapped into a projection constructing the mapped structural types. DbExpression queryExpression = storeFunctionInvoke; if (m_structuralTypeMappings.Count == 1) { var mapping = m_structuralTypeMappings[0]; var type = mapping.Item1; var conditions = mapping.Item2; var propertyMappings = mapping.Item3; if (conditions.Count > 0) { queryExpression = queryExpression.Where((row) => GenerateStructuralTypeConditionsPredicate(conditions, row)); } var binding = queryExpression.BindAs("row"); var entityTypeMappingView = GenerateStructuralTypeMappingView(type, propertyMappings, binding.Variable, errors); if (entityTypeMappingView == null) { return null; } queryExpression = binding.Project(entityTypeMappingView); } else { var binding = queryExpression.BindAs("row"); // Make sure type projection is performed over a closed set where each row is guaranteed to produce a known type. // To do this, filter the store function output using the type conditions. Debug.Assert(m_structuralTypeMappings.All(m => m.Item2.Count > 0), "In multi-type mapping each type must have conditions."); List<DbExpression> structuralTypePredicates = m_structuralTypeMappings.Select(m => GenerateStructuralTypeConditionsPredicate(m.Item2, binding.Variable)).ToList(); queryExpression = binding.Filter(Helpers.BuildBalancedTreeInPlace( structuralTypePredicates.ToArray(), // clone, otherwise BuildBalancedTreeInPlace will change it (prev, next) => prev.Or(next))); binding = queryExpression.BindAs("row"); List<DbExpression> structuralTypeMappingViews = new List<DbExpression>(m_structuralTypeMappings.Count); foreach (var mapping in m_structuralTypeMappings) { var type = mapping.Item1; var propertyMappings = mapping.Item3; var structuralTypeMappingView = GenerateStructuralTypeMappingView(type, propertyMappings, binding.Variable, errors); if (structuralTypeMappingView == null) { continue; } else { structuralTypeMappingViews.Add(structuralTypeMappingView); } } Debug.Assert(structuralTypeMappingViews.Count == structuralTypePredicates.Count, "structuralTypeMappingViews.Count == structuralTypePredicates.Count"); if (structuralTypeMappingViews.Count != m_structuralTypeMappings.Count) { Debug.Assert(errors.Count > 0, "errors.Count > 0"); return null; } // Because we are projecting over the closed set, we can convert the last WHEN THEN into ELSE. DbExpression typeConstructors = DbExpressionBuilder.Case( structuralTypePredicates.Take(m_structuralTypeMappings.Count - 1), structuralTypeMappingViews.Take(m_structuralTypeMappings.Count - 1), structuralTypeMappingViews[m_structuralTypeMappings.Count - 1]); queryExpression = binding.Project(typeConstructors); if (DiscriminatorMap.TryCreateDiscriminatorMap(this.FunctionImport.EntitySet, queryExpression, out discriminatorMap)) { Debug.Assert(discriminatorMap != null, "discriminatorMap == null after it has been created"); } } return queryExpression; }
internal DbQueryCommandTree GenerateFunctionView(IList<EdmSchemaError> errors, out DiscriminatorMap discriminatorMap) { Debug.Assert(errors != null, "errors != null"); 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(this.TargetFunction != null, "this.TargetFunction != null"); DbExpression storeFunctionInvoke = this.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, errors, out discriminatorMap); Debug.Assert(queryExpression == null || TypeSemantics.IsPromotableTo(queryExpression.ResultType, this.FunctionImport.ReturnParameter.TypeUsage), "TypeSemantics.IsPromotableTo(queryExpression.ResultType, this.FunctionImport.ReturnParameter.TypeUsage)"); } else { queryExpression = GenerateScalarResultMappingView(storeFunctionInvoke); Debug.Assert(queryExpression == null || TypeSemantics.IsEqual(queryExpression.ResultType, this.FunctionImport.ReturnParameter.TypeUsage), "TypeSemantics.IsEqual(queryExpression.ResultType, this.FunctionImport.ReturnParameter.TypeUsage)"); } if (queryExpression == null) { // In case of errors during view generation, return. return null; } // Generate parameterized command, where command parameters are semantically the c-space function parameters. return DbQueryCommandTree.FromValidExpression(m_mappingItemCollection.Workspace, TargetPerspective.TargetPerspectiveDataSpace, queryExpression); }
/// <summary> /// Determines whether the given query view matches the discriminator map pattern. /// </summary> internal static bool TryCreateDiscriminatorMap(EntitySet entitySet, DbExpression queryView, out DiscriminatorMap discriminatorMap) { discriminatorMap = null; if (queryView.ExpressionKind != DbExpressionKind.Project) { return(false); } var project = (DbProjectExpression)queryView; if (project.Projection.ExpressionKind != DbExpressionKind.Case) { return(false); } var caseExpression = (DbCaseExpression)project.Projection; if (project.Projection.ResultType.EdmType.BuiltInTypeKind != BuiltInTypeKind.EntityType) { return(false); } // determine value domain by walking filter if (project.Input.Expression.ExpressionKind != DbExpressionKind.Filter) { return(false); } var filterExpression = (DbFilterExpression)project.Input.Expression; HashSet <object> discriminatorDomain = new HashSet <object>(); if (!ViewSimplifier.TryMatchDiscriminatorPredicate(filterExpression, (equalsExp, discriminatorValue) => discriminatorDomain.Add(discriminatorValue))) { return(false); } var typeMap = new List <KeyValuePair <object, EntityType> >(); var propertyMap = new Dictionary <EdmProperty, DbExpression>(); var relPropertyMap = new Dictionary <Query.InternalTrees.RelProperty, DbExpression>(); var typeToRelPropertyMap = new Dictionary <EntityType, List <Query.InternalTrees.RelProperty> >(); DbPropertyExpression discriminator = null; EdmProperty discriminatorProperty = null; for (int i = 0; i < caseExpression.When.Count; i++) { var when = caseExpression.When[i]; var then = caseExpression.Then[i]; var projectionVariableName = project.Input.VariableName; DbPropertyExpression currentDiscriminator; object discriminatorValue; if (!ViewSimplifier.TryMatchPropertyEqualsValue(when, projectionVariableName, out currentDiscriminator, out discriminatorValue)) { return(false); } // must be the same discriminator in every case if (null == discriminatorProperty) { discriminatorProperty = (EdmProperty)currentDiscriminator.Property; } else if (discriminatorProperty != currentDiscriminator.Property) { return(false); } discriminator = currentDiscriminator; // right hand side must be entity type constructor EntityType currentType; if (!TryMatchEntityTypeConstructor(then, propertyMap, relPropertyMap, typeToRelPropertyMap, out currentType)) { return(false); } // remember type + discriminator value typeMap.Add(new KeyValuePair <object, EntityType>(discriminatorValue, currentType)); // remove discriminator value from domain discriminatorDomain.Remove(discriminatorValue); } // make sure only one member of discriminator domain remains... if (1 != discriminatorDomain.Count) { return(false); } // check default case EntityType elseType; if (null == caseExpression.Else || !TryMatchEntityTypeConstructor(caseExpression.Else, propertyMap, relPropertyMap, typeToRelPropertyMap, out elseType)) { return(false); } typeMap.Add(new KeyValuePair <object, EntityType>(discriminatorDomain.Single(), elseType)); // Account for cases where some type in the hierarchy specifies a rel-property, but another // type in the hierarchy does not if (!CheckForMissingRelProperties(relPropertyMap, typeToRelPropertyMap)) { return(false); } // since the store may right-pad strings, ensure discriminator values are unique in their trimmed // form var discriminatorValues = typeMap.Select(map => map.Key); int uniqueValueCount = discriminatorValues.Distinct(TrailingSpaceComparer.Instance).Count(); int valueCount = typeMap.Count; if (uniqueValueCount != valueCount) { return(false); } discriminatorMap = new DiscriminatorMap(discriminator, typeMap, propertyMap, relPropertyMap, entitySet); return(true); }
/// <summary> /// Given an extent and its corresponding view, invokes the parser to check if the view definition is syntactically correct. /// Iff parsing succeeds: <paramref name="commandTree"/> and <paramref name="discriminatorMap"/> are set to the parse result and method returns true, /// otherwise if parser has thrown a catchable exception, it is returned via <paramref name="parserException"/> parameter, /// otherwise exception is re-thrown. /// </summary> private static bool TryParseView(string eSQL, bool isUserSpecified, EntitySetBase extent, StorageMappingItemCollection mappingItemCollection, ConfigViewGenerator config, out DbQueryCommandTree commandTree, out DiscriminatorMap discriminatorMap, out Exception parserException) { commandTree = null; discriminatorMap = null; parserException = null; // We do not catch any internal exceptions any more config.StartSingleWatch(PerfType.ViewParsing); try { // If it is a user specified view, allow all queries. Otherwise parse the view in the restricted mode. ParserOptions.CompilationMode compilationMode = ParserOptions.CompilationMode.RestrictedViewGenerationMode; if (isUserSpecified) { compilationMode = ParserOptions.CompilationMode.UserViewGenerationMode; } Debug.Assert(!String.IsNullOrEmpty(eSQL), "eSQL query is not specified"); commandTree = (DbQueryCommandTree)ExternalCalls.CompileView(eSQL, mappingItemCollection, compilationMode); // For non user-specified views, perform simplification. if (!isUserSpecified) { commandTree = ViewSimplifier.SimplifyView(extent, commandTree); } // See if the view matches the "discriminated" pattern (allows simplification of generated store commands) if (extent.BuiltInTypeKind == BuiltInTypeKind.EntitySet) { if (DiscriminatorMap.TryCreateDiscriminatorMap((EntitySet)extent, commandTree.Query, out discriminatorMap)) { Debug.Assert(discriminatorMap != null, "discriminatorMap == null after it has been created"); } } } catch (Exception e) { // Catching all the exception types since Query parser seems to be throwing veriety of // exceptions - EntityException, ArgumentException, ArgumentNullException etc. if (EntityUtil.IsCatchableExceptionType(e)) { parserException = e; } else { throw; } } finally { config.StopSingleWatch(PerfType.ViewParsing); } Debug.Assert(commandTree != null || parserException != null, "Either commandTree or parserException is expected."); // Note: m_commandTree might have been initialized by a previous call to this method, so in consequent calls it might occur that // both m_commandTree and parserException are not null - this would mean that the last parse attempt failed, but m_commandTree value is // preserved from the previous call. return(parserException == null); }