internal QueryRewriter(EdmType generatedType, ViewgenContext context, ViewGenMode typesGenerationMode)
        {
            Debug.Assert(typesGenerationMode != ViewGenMode.GenerateAllViews);

            _typesGenerationMode = typesGenerationMode;
            _context = context;
            _generatedType = generatedType;
            _domainMap = context.MemberMaps.LeftDomainMap;
            _config = context.Config;
            _identifiers = context.CqlIdentifiers;
            _qp = new RewritingProcessor<Tile<FragmentQuery>>(new DefaultTileProcessor<FragmentQuery>(context.LeftFragmentQP));
            _extentPath = new MemberPath(context.Extent);
            _keyAttributes = new List<MemberPath>(MemberPath.GetKeyMembers(context.Extent, _domainMap));

            // populate _fragmentQueries and _views
            foreach (var leftCellWrapper in _context.AllWrappersForExtent)
            {
                var query = leftCellWrapper.FragmentQuery;
                Tile<FragmentQuery> tile = CreateTile(query);
                _fragmentQueries.Add(query);
                _views.Add(tile);
            }
            Debug.Assert(_views.Count > 0);

            AdjustMemberDomainsForUpdateViews();

            // must be done after adjusting domains
            _domainQuery = GetDomainQuery(FragmentQueries, generatedType);

            _usedViews = new HashSet<FragmentQuery>();
        }
Beispiel #2
0
 /// <summary>
 /// Initializes a <see cref="CqlBlock"/> with the SELECT (<paramref name="slotInfos"/>), FROM (<paramref name="children"/>), 
 /// WHERE (<paramref name="whereClause"/>), AS (<paramref name="blockAliasNum"/>).
 /// </summary>
 protected CqlBlock(SlotInfo[] slotInfos, List<CqlBlock> children, BoolExpression whereClause, CqlIdentifiers identifiers, int blockAliasNum)
 {
     m_slots = new ReadOnlyCollection<SlotInfo>(slotInfos);
     m_children = new ReadOnlyCollection<CqlBlock>(children);
     m_whereClause = whereClause;
     m_blockAlias = identifiers.GetBlockAlias(blockAliasNum);
 }
        /// <summary>
        /// Creates a boolean slot for expression that comes from originalCellNum, i.e., 
        /// the value of the slot is <paramref name="expr"/> and the name is "_from{<paramref name="originalCellNum"/>}", e.g., _from2
        /// </summary>
        internal BooleanProjectedSlot(BoolExpression expr, CqlIdentifiers identifiers, int originalCellNum)
        {
            m_expr = expr;
            m_originalCell = new CellIdBoolean(identifiers, originalCellNum);

            Debug.Assert(!(expr.AsLiteral is CellIdBoolean) ||
                         BoolLiteral.EqualityComparer.Equals((CellIdBoolean)expr.AsLiteral, m_originalCell), "Cellid boolean for the slot and cell number disagree");
        }
 /// <summary>
 /// Creates a join block (type given by <paramref name="opType"/>) with SELECT (<paramref name="slotInfos"/>), FROM (<paramref name="children"/>),
 /// ON (<paramref name="onClauses"/> - one for each child except 0th), WHERE (true), AS (<paramref name="blockAliasNum"/>).
 /// </summary>
 internal JoinCqlBlock(CellTreeOpType opType,
                       SlotInfo[] slotInfos,
                       List<CqlBlock> children,
                       List<OnClause> onClauses,
                       CqlIdentifiers identifiers,
                       int blockAliasNum)
     : base(slotInfos, children, BoolExpression.True, identifiers, blockAliasNum)
 {
     m_opType = opType;
     m_onClauses = onClauses;
 }
Beispiel #5
0
 /// <summary>
 /// Creates an cql block representing the <paramref name="extent"/> (the FROM part).
 /// SELECT is given by <paramref name="slots"/>, WHERE by <paramref name="whereClause"/> and AS by <paramref name="blockAliasNum"/>.
 /// </summary>
 internal ExtentCqlBlock(EntitySetBase extent,
                         CellQuery.SelectDistinct selectDistinct,
                         SlotInfo[] slots,
                         BoolExpression whereClause,
                         CqlIdentifiers identifiers,
                         int blockAliasNum)
     : base(slots, EmptyChildren, whereClause, identifiers, blockAliasNum)
 {
     m_extent = extent;
     m_nodeTableAlias = identifiers.GetBlockAlias();
     m_selectDistinct = selectDistinct;
 }
Beispiel #6
0
 /// <summary>
 /// Given the generated <paramref name="view"/>, the <paramref name="caseStatements"/> for the multiconstant fields,
 /// the <paramref name="projectedSlotMap"/> that maps different paths of the entityset (for which the view is being generated) to slot indexes in the view,
 /// creates an object that is capable of generating the Cql for <paramref name="view"/>.
 /// </summary>
 internal CqlGenerator(CellTreeNode view,
                       Dictionary<MemberPath,
                       CaseStatement> caseStatements,
                       CqlIdentifiers identifiers,
                       MemberProjectionIndex projectedSlotMap,
                       int numCellsInView,
                       BoolExpression topLevelWhereClause,
                       StorageMappingItemCollection mappingItemCollection)
 {
     m_view = view;
     m_caseStatements = caseStatements;
     m_projectedSlotMap = projectedSlotMap;
     m_numBools = numCellsInView; // We have that many booleans
     m_topLevelWhereClause = topLevelWhereClause;
     m_identifiers = identifiers;
     m_mappingItemCollection = mappingItemCollection;
 }
        private ErrorLog GenerateQueryViewForExtentAndType(
            CqlIdentifiers identifiers, ViewSet views, EntitySetBase entity, EntityTypeBase type, ViewGenMode mode)
        {
            Debug.Assert(mode != ViewGenMode.GenerateAllViews);

            // Keep track of the mapping exceptions that we have generated
            var errorLog = new ErrorLog();

            if (m_config.IsViewTracing)
            {
                Helpers.StringTraceLine(String.Empty);
                Helpers.StringTraceLine(String.Empty);
                Helpers.FormatTraceLine(
                    "================= Generating {0} Query View for: {1} ===========================",
                    (mode == ViewGenMode.OfTypeViews) ? "OfType" : "OfTypeOnly",
                    entity.Name);
                Helpers.StringTraceLine(String.Empty);
                Helpers.StringTraceLine(String.Empty);
            }

            try
            {
                // (1) view generation (checks that extents are fully mapped)
                var context = CreateViewgenContext(entity, ViewTarget.QueryView, identifiers);
                var queryRewriter = GenerateViewsForExtentAndType(type, context, identifiers, views, mode);
            }
            catch (InternalMappingException exception)
            {
                // All exceptions have mapping errors in them
                Debug.Assert(exception.ErrorLog.Count > 0, "Incorrectly created mapping exception");
                errorLog.Merge(exception.ErrorLog);
            }

            return errorLog;
        }
        // requires: schema refers to C-side or S-side schema for the cells
        // inside this. if schema.IsQueryView is true, the left side of cells refers
        // to the C side (and vice-versa for the right side)
        // effects: Generates the relevant views for the schema side and
        // returns them. If allowMissingAttributes is true and attributes
        // are missing on the schema side, substitutes them with NULL
        // Modifies views to contain the generated views for different
        // extents specified by cells and the the schemaContext
        private ErrorLog GenerateDirectionalViews(ViewTarget viewTarget, CqlIdentifiers identifiers, ViewSet views)
        {
            var isQueryView = viewTarget == ViewTarget.QueryView;

            // Partition cells by extent.
            var extentCellMap = GroupCellsByExtent(m_cellGroup, viewTarget);

            // Keep track of the mapping exceptions that we have generated
            var errorLog = new ErrorLog();

            // Generate views for each extent
            foreach (var extent in extentCellMap.Keys)
            {
                if (m_config.IsViewTracing)
                {
                    Helpers.StringTraceLine(String.Empty);
                    Helpers.StringTraceLine(String.Empty);
                    Helpers.FormatTraceLine(
                        "================= Generating {0} View for: {1} ===========================",
                        isQueryView ? "Query" : "Update", extent.Name);
                    Helpers.StringTraceLine(String.Empty);
                    Helpers.StringTraceLine(String.Empty);
                }
                try
                {
                    // (1) view generation (checks that extents are fully mapped)
                    var queryRewriter = GenerateDirectionalViewsForExtent(viewTarget, extent, identifiers, views);

                    // (2) validation for update views
                    if (viewTarget == ViewTarget.UpdateView
                        &&
                        m_config.IsValidationEnabled)
                    {
                        if (m_config.IsViewTracing)
                        {
                            Helpers.StringTraceLine(String.Empty);
                            Helpers.StringTraceLine(String.Empty);
                            Helpers.FormatTraceLine(
                                "----------------- Validation for generated update view for: {0} -----------------",
                                extent.Name);
                            Helpers.StringTraceLine(String.Empty);
                            Helpers.StringTraceLine(String.Empty);
                        }

                        var validator = new RewritingValidator(queryRewriter.ViewgenContext, queryRewriter.BasicView);
                        validator.Validate();
                    }
                }
                catch (InternalMappingException exception)
                {
                    // All exceptions have mapping errors in them
                    Debug.Assert(
                        exception.ErrorLog.Count > 0,
                        "Incorrectly created mapping exception");
                    errorLog.Merge(exception.ErrorLog);
                }
            }
            return errorLog;
        }
        // effects: Generates views for the particular cellgroup in this. Returns an
        // error log describing the errors that were encountered (if none
        // were encountered, the ErrorLog.Count is 0). Places the generated
        // views in result
        internal ErrorLog GenerateAllBidirectionalViews(ViewSet views, CqlIdentifiers identifiers)
        {
            // Allow missing attributes for now to make entity splitting run through
            // we cannot do this for query views in general: need to obtain the exact enumerated domain

            if (m_config.IsNormalTracing)
            {
                var builder = new StringBuilder();
                Cell.CellsToBuilder(builder, m_cellGroup);
                Helpers.StringTraceLine(builder.ToString());
            }

            m_config.SetTimeForFinishedActivity(PerfType.CellCreation);
            // Check if the cellgroup is consistent and all known S constraints are
            // satisified by the known C constraints
            var validator = new CellGroupValidator(m_cellGroup, m_config);
            var errorLog = validator.Validate();

            if (errorLog.Count > 0)
            {
                errorLog.PrintTrace();
                return errorLog;
            }

            m_config.SetTimeForFinishedActivity(PerfType.KeyConstraint);

            // We generate update views first since they perform the main
            // validation checks
            if (m_config.GenerateUpdateViews)
            {
                errorLog = GenerateDirectionalViews(ViewTarget.UpdateView, identifiers, views);
                if (errorLog.Count > 0)
                {
                    return errorLog; // If we have discovered errors here, do not generate query views
                }
            }

            // Make sure that the foreign key constraints are not violated
            if (m_config.IsValidationEnabled)
            {
                CheckForeignKeyConstraints(errorLog);
            }
            m_config.SetTimeForFinishedActivity(PerfType.ForeignConstraint);

            if (errorLog.Count > 0)
            {
                errorLog.PrintTrace();
                return errorLog; // If we have discovered errors here, do not generate query views
            }

            // Query views - do not allow missing attributes
            // For the S-side, we add NOT ... for each scalar constant so
            // that if we have C, P in the mapping but the store has C, P, S,
            // we can handle it in the query views
            m_updateDomainMap.ExpandDomainsToIncludeAllPossibleValues();

            errorLog = GenerateDirectionalViews(ViewTarget.QueryView, identifiers, views);

            return errorLog;
        }
        internal ErrorLog GenerateQueryViewForSingleExtent(
            ViewSet views, CqlIdentifiers identifiers, EntitySetBase entity, EntityTypeBase type, ViewGenMode mode)
        {
            Debug.Assert(mode != ViewGenMode.GenerateAllViews);

            if (m_config.IsNormalTracing)
            {
                var builder = new StringBuilder();
                Cell.CellsToBuilder(builder, m_cellGroup);
                Helpers.StringTraceLine(builder.ToString());
            }

            // Check if the cellgroup is consistent and all known S constraints are
            // satisified by the known C constraints
            var validator = new CellGroupValidator(m_cellGroup, m_config);
            var errorLog = validator.Validate();
            if (errorLog.Count > 0)
            {
                errorLog.PrintTrace();
                return errorLog;
            }

            // Make sure that the foreign key constraints are not violated
            if (m_config.IsValidationEnabled)
            {
                CheckForeignKeyConstraints(errorLog);
            }

            if (errorLog.Count > 0)
            {
                errorLog.PrintTrace();
                return errorLog; // If we have discovered errors here, do not generate query views
            }

            // For the S-side, we add NOT ... for each scalar constant so
            // that if we have C, P in the mapping but the store has C, P, S,
            // we can handle it in the query views
            m_updateDomainMap.ExpandDomainsToIncludeAllPossibleValues();

            foreach (var cell in m_cellGroup)
            {
                cell.SQuery.WhereClause.FixDomainMap(m_updateDomainMap);
            }

            errorLog = GenerateQueryViewForExtentAndType(identifiers, views, entity, type, mode);

            return errorLog;
        }
        internal ViewgenContext(ViewTarget viewTarget, EntitySetBase extent, IEnumerable<Cell> extentCells,
                                CqlIdentifiers identifiers, ConfigViewGenerator config, MemberDomainMap queryDomainMap,
                                MemberDomainMap updateDomainMap, StorageEntityContainerMapping entityContainerMapping)
        {
            foreach (Cell cell in extentCells)
            {
                Debug.Assert(extent.Equals(cell.GetLeftQuery(viewTarget).Extent));
                Debug.Assert(cell.CQuery.NumProjectedSlots == cell.SQuery.NumProjectedSlots);
            }

            m_extent = extent;
            m_viewTarget = viewTarget;
            m_config = config;
            m_edmItemCollection = entityContainerMapping.StorageMappingItemCollection.EdmItemCollection;
            m_entityContainerMapping = entityContainerMapping;
            m_identifiers = identifiers;

            // create a copy of updateDomainMap so generation of query views later on is not affected
            // it is modified in QueryRewriter.AdjustMemberDomainsForUpdateViews
            updateDomainMap = updateDomainMap.MakeCopy();

            // Create a signature generator that handles all the
            // multiconstant work and generating the signatures
            MemberDomainMap domainMap = viewTarget == ViewTarget.QueryView ? queryDomainMap : updateDomainMap;

            m_memberMaps = new MemberMaps(viewTarget, MemberProjectionIndex.Create(extent, m_edmItemCollection), queryDomainMap, updateDomainMap);

            // Create left fragment KB: includes constraints for the extent to be constructed
            FragmentQueryKB leftKB = new FragmentQueryKB();
            leftKB.CreateVariableConstraints(extent, domainMap, m_edmItemCollection);
            m_leftFragmentQP = new FragmentQueryProcessor(leftKB);
            m_rewritingCache = new Dictionary<FragmentQuery, Tile<FragmentQuery>>(
                FragmentQuery.GetEqualityComparer(m_leftFragmentQP));

            // Now using the signatures, create new cells such that
            // "extent's" query (C or S) is described in terms of multiconstants
            if (!CreateLeftCellWrappers(extentCells, viewTarget))
            {
                return;
            }

            // Create right fragment KB: includes constraints for all extents and association roles of right queries
            FragmentQueryKB rightKB = new FragmentQueryKB();
            MemberDomainMap rightDomainMap = viewTarget == ViewTarget.QueryView ? updateDomainMap : queryDomainMap;
            foreach (LeftCellWrapper leftCellWrapper in m_cellWrappers)
            {
                EntitySetBase rightExtent = leftCellWrapper.RightExtent;
                rightKB.CreateVariableConstraints(rightExtent, rightDomainMap, m_edmItemCollection);
                rightKB.CreateAssociationConstraints(rightExtent, rightDomainMap, m_edmItemCollection);
            }
            
            if (m_viewTarget == ViewTarget.UpdateView)
            {
                CreateConstraintsForForeignKeyAssociationsAffectingThisWarapper(rightKB, rightDomainMap);
            }

            m_rightFragmentQP = new FragmentQueryProcessor(rightKB);

            // Check for concurrency control tokens
            if (m_viewTarget == ViewTarget.QueryView)
            {
                CheckConcurrencyControlTokens();
            }
            // For backward compatibility -
            // order wrappers by increasing domain size, decreasing number of attributes
            m_cellWrappers.Sort(LeftCellWrapper.Comparer);
        }
Beispiel #12
0
 // effects: Given a leaf cell node and the slots required by the parent, returns
 // a CqlBlock corresponding to the tree rooted at this
 internal abstract CqlBlock ToCqlBlock(bool[] requiredSlots, CqlIdentifiers identifiers, ref int blockAliasNum,
                                       ref List <WithRelationship> withRelationships);
        // requires: node corresponds to an IJ, LOJ, FOJ node
        // effects: Given a union node and the slots required by the parent,
        // generates a CqlBlock for the subtree rooted at node
        private CqlBlock JoinToCqlBlock(bool[] requiredSlots, CqlIdentifiers identifiers, ref int blockAliasNum, ref List <WithRelationship> withRelationships)
        {
            int totalSlots = requiredSlots.Length;

            Debug.Assert(OpType == CellTreeOpType.IJ ||
                         OpType == CellTreeOpType.LOJ ||
                         OpType == CellTreeOpType.FOJ, "Only these join operations handled");

            List <CqlBlock> children = new List <CqlBlock>();
            List <Tuple <QualifiedSlot, MemberPath> > additionalChildSlots = new List <Tuple <QualifiedSlot, MemberPath> >();

            // First get the children nodes (FROM part)
            foreach (CellTreeNode child in Children)
            {
                // Determine the slots that are projected by this child.
                // These are the required slots as well - unlike Union, we do not need the child to project any extra nulls.
                bool[] childProjectedSlots = child.GetProjectedSlots();
                AndWith(childProjectedSlots, requiredSlots);
                CqlBlock childBlock = child.ToCqlBlock(childProjectedSlots, identifiers, ref blockAliasNum, ref withRelationships);
                children.Add(childBlock);
                for (int qualifiedSlotNumber = childProjectedSlots.Length; qualifiedSlotNumber < childBlock.Slots.Count; qualifiedSlotNumber++)
                {
                    additionalChildSlots.Add(Tuple.Create(childBlock.QualifySlotWithBlockAlias(qualifiedSlotNumber), childBlock.MemberPath(qualifiedSlotNumber)));
                }
                Debug.Assert(totalSlots == child.NumBoolSlots + child.NumProjectedSlots,
                             "Number of required slots is different from what each node in the tree has?");
            }

            // Now get the slots that are projected out by this node (SELECT part)
            SlotInfo[] slotInfos = new SlotInfo[totalSlots + additionalChildSlots.Count];
            for (int slotNum = 0; slotNum < totalSlots; slotNum++)
            {
                // Note: this call could create a CaseStatementSlot (i.e., slotInfo.SlotValue is CaseStatementSlot)
                // which uses "from" booleans that need to be projected by children
                SlotInfo slotInfo = GetJoinSlotInfo(OpType, requiredSlots[slotNum], children, slotNum, identifiers);
                slotInfos[slotNum] = slotInfo;
            }

            for (int i = 0, slotNum = totalSlots; slotNum < totalSlots + additionalChildSlots.Count; slotNum++, i++)
            {
                slotInfos[slotNum] = new SlotInfo(true, true, additionalChildSlots[i].Item1, additionalChildSlots[i].Item2);
            }

            // Generate the ON conditions: For each child, generate an ON
            // clause with the 0th child on the key fields
            List <JoinCqlBlock.OnClause> onClauses = new List <JoinCqlBlock.OnClause>();

            for (int i = 1; i < children.Count; i++)
            {
                CqlBlock child = children[i];
                JoinCqlBlock.OnClause onClause = new JoinCqlBlock.OnClause();
                foreach (int keySlotNum in this.KeySlots)
                {
                    if (ViewgenContext.Config.IsValidationEnabled)
                    {
                        Debug.Assert(children[0].IsProjected(keySlotNum), "Key is not in 0th child");
                        Debug.Assert(child.IsProjected(keySlotNum), "Key is not in child");
                    }
                    else
                    {
                        if (!child.IsProjected(keySlotNum) || !children[0].IsProjected(keySlotNum))
                        {
                            ErrorLog errorLog = new ErrorLog();
                            errorLog.AddEntry(new ErrorLog.Record(true, ViewGenErrorCode.NoJoinKeyOrFKProvidedInMapping,
                                                                  Strings.Viewgen_NoJoinKeyOrFK, ViewgenContext.AllWrappersForExtent, String.Empty));
                            ExceptionHelpers.ThrowMappingException(errorLog, ViewgenContext.Config);
                        }
                    }
                    var firstSlot    = children[0].QualifySlotWithBlockAlias(keySlotNum);
                    var secondSlot   = child.QualifySlotWithBlockAlias(keySlotNum);
                    var outputMember = slotInfos[keySlotNum].OutputMember;
                    onClause.Add(firstSlot, outputMember, secondSlot, outputMember);
                }
                onClauses.Add(onClause);
            }

            CqlBlock result = new JoinCqlBlock(OpType, slotInfos, children, onClauses, identifiers, ++blockAliasNum);

            return(result);
        }
        private QueryRewriter GenerateViewsForExtentAndType(
            EdmType generatedType, ViewgenContext context, CqlIdentifiers identifiers, ViewSet views, ViewGenMode mode)
        {
            Debug.Assert(mode != ViewGenMode.GenerateAllViews, "By definition this method can not handle generating views for all extents");

            var queryRewriter = new QueryRewriter(generatedType, context, mode);
            queryRewriter.GenerateViewComponents();

            // Get the basic view
            var basicView = queryRewriter.BasicView;

            if (m_config.IsNormalTracing)
            {
                Helpers.StringTrace("Basic View: ");
                Helpers.StringTraceLine(basicView.ToString());
            }

            var simplifiedView = GenerateSimplifiedView(basicView, queryRewriter.UsedCells);

            if (m_config.IsNormalTracing)
            {
                Helpers.StringTraceLine(String.Empty);
                Helpers.StringTrace("Simplified View: ");
                Helpers.StringTraceLine(simplifiedView.ToString());
            }

            var cqlGen = new CqlGenerator(
                simplifiedView,
                queryRewriter.CaseStatements,
                identifiers,
                context.MemberMaps.ProjectedSlotMap,
                queryRewriter.UsedCells.Count,
                queryRewriter.TopLevelWhereClause,
                m_entityContainerMapping.StorageMappingItemCollection);

            string eSQLView;
            DbQueryCommandTree commandTree;
            if (m_config.GenerateEsql)
            {
                eSQLView = cqlGen.GenerateEsql();
                commandTree = null;
            }
            else
            {
                eSQLView = null;
                commandTree = cqlGen.GenerateCqt();
            }

            var generatedView = GeneratedView.CreateGeneratedView(
                context.Extent, generatedType, commandTree, eSQLView, m_entityContainerMapping.StorageMappingItemCollection, m_config);
            views.Add(context.Extent, generatedView);

            return queryRewriter;
        }
        // requires: caseForOuterJoins corresponds the slot "slotNum"
        // effects: Adds a WhenThen corresponding to child to caseForOuterJoins.
        private void AddCaseForOuterJoins(CaseStatement caseForOuterJoins, CqlBlock child, int slotNum, CqlIdentifiers identifiers)
        {
            // Determine the cells that the slot comes from
            // and make an OR expression, e.g., WHEN _from0 or _from2 or ... THEN child[slotNum]

            ProjectedSlot         childSlot    = child.SlotValue(slotNum);
            ConstantProjectedSlot constantSlot = childSlot as ConstantProjectedSlot;

            if (constantSlot != null && constantSlot.CellConstant.IsNull())
            {
                // NULL being generated by a child - don't need to project
                return;
            }

            BoolExpression originBool = BoolExpression.False;

            for (int i = 0; i < NumBoolSlots; i++)
            {
                int boolSlotNum = BoolIndexToSlot(i);
                if (child.IsProjected(boolSlotNum))
                {
                    // OR it to the expression
                    QualifiedCellIdBoolean boolExpr = new QualifiedCellIdBoolean(child, identifiers, i);
                    originBool = BoolExpression.CreateOr(originBool, BoolExpression.CreateLiteral(boolExpr, RightDomainMap));
                }
            }
            // Qualify the slotNum with the child.CqlAlias for the THEN
            QualifiedSlot slot = child.QualifySlotWithBlockAlias(slotNum);

            caseForOuterJoins.AddWhenThen(originBool, slot);
        }
        // effects: Generates a SlotInfo object for a slot of a join node. It
        // uses the type of the join operation (opType), whether the slot is
        // required by the parent or not (isRequiredSlot), the children of
        // this node (children) and the number of the slotNum
        private SlotInfo GetJoinSlotInfo(CellTreeOpType opType, bool isRequiredSlot,
                                         List <CqlBlock> children, int slotNum, CqlIdentifiers identifiers)
        {
            if (false == isRequiredSlot)
            {
                // The slot will not be used. So we can set the projected slot to be null
                SlotInfo unrequiredSlotInfo = new SlotInfo(false, false, null, GetMemberPath(slotNum));
                return(unrequiredSlotInfo);
            }

            // For a required slot, determine the child who is contributing to this value
            int           childDefiningSlot = -1;
            CaseStatement caseForOuterJoins = null;

            for (int childNum = 0; childNum < children.Count; childNum++)
            {
                CqlBlock child = children[childNum];
                if (false == child.IsProjected(slotNum))
                {
                    continue;
                }
                // For keys, we can pick any child block. So the first
                // one that we find is fine as well
                if (IsKeySlot(slotNum))
                {
                    childDefiningSlot = childNum;
                    break;
                }
                else if (opType == CellTreeOpType.IJ)
                {
                    // For Inner Joins, most of the time, the entries will be
                    // the same in all the children. However, in some cases,
                    // we will end up with NULL in one child and an actual
                    // value in another -- we should pick up the actual value in that case
                    childDefiningSlot = GetInnerJoinChildForSlot(children, slotNum);
                    break;
                }
                else
                {
                    // For LOJs, we generate a case statement if more than
                    // one child generates the value - until then we do not
                    // create the caseForOuterJoins object
                    if (childDefiningSlot != -1)
                    {
                        // We really need a case statement now
                        // We have the value being generated by another child
                        // We need to fetch the variable from the appropriate child
                        Debug.Assert(false == IsBoolSlot(slotNum), "Boolean slots cannot come from two children");
                        if (caseForOuterJoins == null)
                        {
                            MemberPath outputMember = GetMemberPath(slotNum);
                            caseForOuterJoins = new CaseStatement(outputMember);
                            // Add the child that we had not added in the first shot
                            AddCaseForOuterJoins(caseForOuterJoins, children[childDefiningSlot], slotNum, identifiers);
                        }
                        AddCaseForOuterJoins(caseForOuterJoins, child, slotNum, identifiers);
                    }
                    childDefiningSlot = childNum;
                }
            }

            MemberPath    memberPath = GetMemberPath(slotNum);
            ProjectedSlot slot       = null;

            // Generate the slot value -- case statement slot, or a qualified slot or null or false.
            // If case statement slot has nothing, treat it as null/empty.
            if (caseForOuterJoins != null && (caseForOuterJoins.Clauses.Count > 0 || caseForOuterJoins.ElseValue != null))
            {
                caseForOuterJoins.Simplify();
                slot = new CaseStatementProjectedSlot(caseForOuterJoins, null);
            }
            else if (childDefiningSlot >= 0)
            {
                slot = children[childDefiningSlot].QualifySlotWithBlockAlias(slotNum);
            }
            else
            {
                // need to produce output slot, but don't have a value
                // output NULL for fields or False for bools
                if (IsBoolSlot(slotNum))
                {
                    slot = new BooleanProjectedSlot(BoolExpression.False, identifiers, SlotToBoolIndex(slotNum));
                }
                else
                {
                    slot = new ConstantProjectedSlot(Domain.GetDefaultValueForMemberPath(memberPath, GetLeaves(), ViewgenContext.Config), memberPath);
                }
            }

            // We need to ensure that _from variables are never null since
            // view generation uses 2-valued boolean logic.
            // They can become null in outer joins. We compensate for it by
            // adding AND NOT NULL condition on boolean slots coming from outer joins.
            bool enforceNotNull = IsBoolSlot(slotNum) &&
                                  ((opType == CellTreeOpType.LOJ && childDefiningSlot > 0) ||
                                   opType == CellTreeOpType.FOJ);
            // We set isProjected to be true since we have come up with some value for it
            SlotInfo slotInfo = new SlotInfo(true, true, slot, memberPath, enforceNotNull);

            return(slotInfo);
        }
Beispiel #17
0
 // effects: Determines all the identifiers used in this and adds them to identifiers
 internal void GetIdentifiers(CqlIdentifiers identifiers)
 {
     foreach (var projectedSlot in m_projectedSlots)
     {
         var slot = projectedSlot as MemberProjectedSlot;
         if (slot != null)
         {
             slot.MemberPath.GetIdentifiers(identifiers);
         }
     }
     m_extentMemberPath.GetIdentifiers(identifiers);
 }
 // effects: Creates a cell creator object for an entity container's
 // mappings (specified in "maps")
 internal CellCreator(StorageEntityContainerMapping containerMapping)
 {
     m_containerMapping = containerMapping;
     m_identifiers = new CqlIdentifiers();
 }
Beispiel #19
0
 /// <summary>
 /// Creates a union block with SELECT (<paramref name="slotInfos"/>), FROM (<paramref name="children"/>), WHERE (true), AS (<paramref name="blockAliasNum"/>).
 /// </summary>
 internal UnionCqlBlock(SlotInfo[] slotInfos, List<CqlBlock> children, CqlIdentifiers identifiers, int blockAliasNum)
     : base(slotInfos, children, BoolExpression.True, identifiers, blockAliasNum)
 { }
        // effects: Generates a view for an extent "extent" that belongs to
        // schema "schema". extentCells are the cells for this extent.
        // Adds the view corrsponding to the extent to "views"
        private QueryRewriter GenerateDirectionalViewsForExtent(
            ViewTarget viewTarget, EntitySetBase extent, CqlIdentifiers identifiers, ViewSet views)
        {
            // First normalize the cells in terms of multiconstants, etc
            // and then generate the view for the extent
            var context = CreateViewgenContext(extent, viewTarget, identifiers);
            QueryRewriter queryRewriter = null;

            if (m_config.GenerateViewsForEachType)
            {
                // generate views for each OFTYPE(Extent, Type) combination
                foreach (
                    var type in
                        MetadataHelper.GetTypeAndSubtypesOf(
                            extent.ElementType, m_entityContainerMapping.StorageMappingItemCollection.EdmItemCollection, false
                            /*includeAbstractTypes*/))
                {
                    if (m_config.IsViewTracing
                        && false == type.Equals(extent.ElementType))
                    {
                        Helpers.FormatTraceLine("CQL View for {0} and type {1}", extent.Name, type.Name);
                    }
                    queryRewriter = GenerateViewsForExtentAndType(type, context, identifiers, views, ViewGenMode.OfTypeViews);
                }
            }
            else
            {
                // generate the view for Extent only
                queryRewriter = GenerateViewsForExtentAndType(extent.ElementType, context, identifiers, views, ViewGenMode.OfTypeViews);
            }
            if (viewTarget == ViewTarget.QueryView)
            {
                m_config.SetTimeForFinishedActivity(PerfType.QueryViews);
            }
            else
            {
                m_config.SetTimeForFinishedActivity(PerfType.UpdateViews);
            }

            // cache this rewriter (and context inside it) for future use in FK checking
            m_queryRewriterCache[extent] = queryRewriter;
            return queryRewriter;
        }
        // requires: node corresponds to a Union node
        // effects: Given a union node and the slots required by the parent,
        // generates a CqlBlock for the subtree rooted at node
        private CqlBlock UnionToCqlBlock(bool[] requiredSlots, CqlIdentifiers identifiers, ref int blockAliasNum, ref List <WithRelationship> withRelationships)
        {
            Debug.Assert(OpType == CellTreeOpType.Union);

            List <CqlBlock> children = new List <CqlBlock>();
            List <Tuple <CqlBlock, SlotInfo> > additionalChildSlots = new List <Tuple <CqlBlock, SlotInfo> >();

            int totalSlots = requiredSlots.Length;

            foreach (CellTreeNode child in Children)
            {
                // Unlike Join, we pass the requiredSlots from the parent as the requirement.
                bool[] childProjectedSlots = child.GetProjectedSlots();
                AndWith(childProjectedSlots, requiredSlots);
                CqlBlock childBlock = child.ToCqlBlock(childProjectedSlots, identifiers, ref blockAliasNum, ref withRelationships);
                for (int qualifiedSlotNumber = childProjectedSlots.Length; qualifiedSlotNumber < childBlock.Slots.Count; qualifiedSlotNumber++)
                {
                    additionalChildSlots.Add(Tuple.Create(childBlock, childBlock.Slots[qualifiedSlotNumber]));
                }

                // if required, but not projected, add NULL
                SlotInfo[] paddedSlotInfo = new SlotInfo[childBlock.Slots.Count];
                for (int slotNum = 0; slotNum < totalSlots; slotNum++)
                {
                    if (requiredSlots[slotNum] && !childProjectedSlots[slotNum])
                    {
                        if (IsBoolSlot(slotNum))
                        {
                            paddedSlotInfo[slotNum] = new SlotInfo(true /* is required */, true /* is projected */,
                                                                   new BooleanProjectedSlot(BoolExpression.False, identifiers, SlotToBoolIndex(slotNum)), null /* member path*/);
                        }
                        else
                        {
                            // NULL as projected slot
                            MemberPath memberPath = childBlock.MemberPath(slotNum);
                            paddedSlotInfo[slotNum] = new SlotInfo(true /* is required */, true /* is projected */,
                                                                   new ConstantProjectedSlot(Constant.Null, memberPath), memberPath);
                        }
                    }
                    else
                    {
                        paddedSlotInfo[slotNum] = childBlock.Slots[slotNum];
                    }
                }
                childBlock.Slots = new ReadOnlyCollection <SlotInfo>(paddedSlotInfo);
                children.Add(childBlock);
                Debug.Assert(totalSlots == child.NumBoolSlots + child.NumProjectedSlots,
                             "Number of required slots is different from what each node in the tree has?");
            }

            // We need to add the slots added by each child uniformly for others (as nulls) since this is a union operation.
            if (additionalChildSlots.Count != 0)
            {
                foreach (CqlBlock childBlock in children)
                {
                    SlotInfo[] childSlots = new SlotInfo[totalSlots + additionalChildSlots.Count];
                    childBlock.Slots.CopyTo(childSlots, 0);
                    int index = totalSlots;
                    foreach (var addtionalChildSlotInfo in additionalChildSlots)
                    {
                        var slotInfo = addtionalChildSlotInfo.Item2;
                        if (addtionalChildSlotInfo.Item1.Equals(childBlock))
                        {
                            childSlots[index] = new SlotInfo(true /* is required */, true /* is projected */, slotInfo.SlotValue, slotInfo.OutputMember);
                        }
                        else
                        {
                            childSlots[index] = new SlotInfo(true /* is required */, true /* is projected */,
                                                             new ConstantProjectedSlot(Constant.Null, slotInfo.OutputMember), slotInfo.OutputMember);
                        }
                        //move on to the next slot added by children.
                        index++;
                    }
                    childBlock.Slots = new ReadOnlyCollection <SlotInfo>(childSlots);
                }
            }

            // Create the slotInfos and then Union CqlBlock
            SlotInfo[] slotInfos = new SlotInfo[totalSlots + additionalChildSlots.Count];

            // We pick the slot references from the first child, just as convention
            // In a union, values come from both sides
            CqlBlock firstChild = children[0];

            for (int slotNum = 0; slotNum < totalSlots; slotNum++)
            {
                SlotInfo slotInfo = firstChild.Slots[slotNum];
                // A required slot is somehow projected by a child in Union, so set isProjected to be the same as isRequired.
                bool isRequired = requiredSlots[slotNum];
                slotInfos[slotNum] = new SlotInfo(isRequired, isRequired, slotInfo.SlotValue, slotInfo.OutputMember);
            }

            for (int slotNum = totalSlots; slotNum < totalSlots + additionalChildSlots.Count; slotNum++)
            {
                var aslot = firstChild.Slots[slotNum];
                slotInfos[slotNum] = new SlotInfo(true, true, aslot.SlotValue, aslot.OutputMember);
            }

            CqlBlock block = new UnionCqlBlock(slotInfos, children, identifiers, ++blockAliasNum);

            return(block);
        }
        // effects: Returns a context corresponding to extent (if one does not exist, creates one)
        private ViewgenContext CreateViewgenContext(EntitySetBase extent, ViewTarget viewTarget, CqlIdentifiers identifiers)
        {
            QueryRewriter queryRewriter;
            if (!m_queryRewriterCache.TryGetValue(extent, out queryRewriter))
            {
                // collect the cells that belong to this extent (just a few of them since we segment the mapping first)
                var cellsForExtent = m_cellGroup.Where(c => c.GetLeftQuery(viewTarget).Extent == extent);

                return new ViewgenContext(
                    viewTarget, extent, cellsForExtent, identifiers, m_config, m_queryDomainMap, m_updateDomainMap, m_entityContainerMapping);
            }
            else
            {
                return queryRewriter.ViewgenContext;
            }
        }
Beispiel #23
0
 // effects: Determines all the identifiers used in this and adds them to identifiers
 internal void GetIdentifiers(CqlIdentifiers identifiers)
 {
     m_cQuery.GetIdentifiers(identifiers);
     m_sQuery.GetIdentifiers(identifiers);
 }
        // effects: Given a list of cells in the schema, generates the query and
        // update mapping views for OFTYPE(Extent, Type) combinations in this schema
        // container. Returns a list of generated query and update views.
        // If it is false and some columns in a table are unmapped, an
        // exception is raised
        private static ViewGenResults GenerateViewsFromCells(List<Cell> cells, ConfigViewGenerator config,
                                                                   CqlIdentifiers identifiers,
                                                                   StorageEntityContainerMapping containerMapping)
        {
            EntityUtil.CheckArgumentNull(cells, "cells");
            EntityUtil.CheckArgumentNull(config, "config");
            Debug.Assert(cells.Count > 0, "There must be at least one cell in the container mapping");


            // Go through each table and determine their foreign key constraints
            EntityContainer container = containerMapping.StorageEntityContainer;
            Debug.Assert(container != null);

            ViewGenResults viewGenResults = new ViewGenResults();
            ErrorLog tmpLog = EnsureAllCSpaceContainerSetsAreMapped(cells, config, containerMapping);
            if (tmpLog.Count > 0)
            {
                viewGenResults.AddErrors(tmpLog);
                Helpers.StringTraceLine(viewGenResults.ErrorsToString());
                return viewGenResults;
            }

            List<ForeignConstraint> foreignKeyConstraints = ForeignConstraint.GetForeignConstraints(container);

            CellPartitioner partitioner = new CellPartitioner(cells, foreignKeyConstraints);
            List<CellGroup> cellGroups = partitioner.GroupRelatedCells();
            foreach (CellGroup cellGroup in cellGroups)
            {
                ViewGenerator viewGenerator = null;
                ErrorLog groupErrorLog = new ErrorLog();
                try
                {
                    viewGenerator = new ViewGenerator(cellGroup, config, foreignKeyConstraints, containerMapping);
                }
                catch (InternalMappingException exception)
                {
                    // All exceptions have mapping errors in them
                    Debug.Assert(exception.ErrorLog.Count > 0, "Incorrectly created mapping exception");
                    groupErrorLog = exception.ErrorLog;
                }

                if (groupErrorLog.Count == 0)
                {
                    Debug.Assert(viewGenerator != null);
                    groupErrorLog = viewGenerator.GenerateAllBidirectionalViews(viewGenResults.Views, identifiers);
                }

                if (groupErrorLog.Count != 0)
                {
                    viewGenResults.AddErrors(groupErrorLog);
                }
            }
            // We used to print the errors here. Now we trace them as they are being thrown
            //if (viewGenResults.HasErrors && config.IsViewTracing) {
            //    Helpers.StringTraceLine(viewGenResults.ErrorsToString());
            //}
            return viewGenResults;
        }
 /// <summary>
 /// Creates a boolean of the form "<paramref name="block"/>.<paramref name="originalCellNum"/>".
 /// </summary>
 internal QualifiedCellIdBoolean(CqlBlock block, CqlIdentifiers identifiers, int originalCellNum)
     : base(identifiers, originalCellNum)
 {
     m_block = block;
 }
 /// <summary>
 /// Creates a <see cref="CqlBlock"/> containing the case statememt for the <paramref name="caseSlot"/> and projecting other slots as is from its child (input). CqlBlock with SELECT (slots),
 /// </summary>
 /// <param name="caseSlot">indicates which slot in <paramref name="slots"/> corresponds to the case statement being generated by this block</param>
 internal CaseCqlBlock(SlotInfo[] slots, int caseSlot, CqlBlock child, BoolExpression whereClause, CqlIdentifiers identifiers, int blockAliasNum)
     : base(slots, new List<CqlBlock>(new CqlBlock[] { child }), whereClause, identifiers, blockAliasNum)
 {
     m_caseSlotInfo = slots[caseSlot];
 }
Beispiel #27
0
 // effects: Determines all the identifiers used in this and adds them to identifiers
 internal void GetIdentifiers(CqlIdentifiers identifiers)
 {
     m_cQuery.GetIdentifiers(identifiers);
     m_sQuery.GetIdentifiers(identifiers);
 }
Beispiel #28
0
 /// <summary>
 /// Creates a boolean expression for the variable name specified by <paramref name="index"/>, e.g., 0 results in from0, 1 into from1.
 /// </summary>
 internal CellIdBoolean(CqlIdentifiers identifiers, int index)
 {
     Debug.Assert(index >= 0);
     m_index = index;
     m_slotName = identifiers.GetFromVariable(index);
 }
Beispiel #29
0
 /// <summary>
 /// Creates a boolean expression for the variable name specified by <paramref name="index"/>, e.g., 0 results in from0, 1 into from1.
 /// </summary>
 internal CellIdBoolean(CqlIdentifiers identifiers, int index)
 {
     Debug.Assert(index >= 0);
     m_index    = index;
     m_slotName = identifiers.GetFromVariable(index);
 }