internal ExplicitDiscriminatorMap(DiscriminatorMap template)
 {
     m_typeMap = template.TypeMap;
     m_discriminatorProperty = template.Discriminator.Property;
     m_properties = template.PropertyMap.Select(propertyValuePair => propertyValuePair.Key)
         .ToList().AsReadOnly();
 }
Ejemplo n.º 2
0
        /// <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));
        }
Ejemplo n.º 3
0
        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());
            }
        }
Ejemplo n.º 4
0
        /// <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;
        }
Ejemplo n.º 5
0
        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>
        /// 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);
        }
Ejemplo n.º 9
0
        /// <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);
        }
Ejemplo n.º 10
0
        /// <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);
        }