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>(); }
// <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(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; }
// <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; }
/// <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; }
internal abstract CqlBlock ToCqlBlock( bool[] requiredSlots, CqlIdentifiers identifiers, ref int blockAliasNum, ref List <WithRelationship> withRelationships);
internal ViewgenContext( ViewTarget viewTarget, EntitySetBase extent, IList<Cell> extentCells, CqlIdentifiers identifiers, ConfigViewGenerator config, MemberDomainMap queryDomainMap, MemberDomainMap updateDomainMap, EntityContainerMapping entityContainerMapping) { foreach (var 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 var 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 var leftKB = new FragmentQueryKBChaseSupport(); 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 var rightKB = new FragmentQueryKBChaseSupport(); var rightDomainMap = viewTarget == ViewTarget.QueryView ? updateDomainMap : queryDomainMap; foreach (var leftCellWrapper in m_cellWrappers) { var rightExtent = leftCellWrapper.RightExtent; rightKB.CreateVariableConstraints(rightExtent, rightDomainMap, m_edmItemCollection); rightKB.CreateAssociationConstraints(rightExtent, rightDomainMap, m_edmItemCollection); } if (m_viewTarget == ViewTarget.UpdateView) { CreateConstraintsForForeignKeyAssociationsAffectingThisWrapper(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); }
// 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: 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 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) { var totalSlots = requiredSlots.Length; Debug.Assert( OpType == CellTreeOpType.IJ || OpType == CellTreeOpType.LOJ || OpType == CellTreeOpType.FOJ, "Only these join operations handled"); var children = new List <CqlBlock>(); var additionalChildSlots = new List <Tuple <QualifiedSlot, MemberPath> >(); // First get the children nodes (FROM part) foreach (var 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. var childProjectedSlots = child.GetProjectedSlots(); AndWith(childProjectedSlots, requiredSlots); var childBlock = child.ToCqlBlock(childProjectedSlots, identifiers, ref blockAliasNum, ref withRelationships); children.Add(childBlock); for (var 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) var slotInfos = new SlotInfo[totalSlots + additionalChildSlots.Count]; for (var 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 var 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 var onClauses = new List <JoinCqlBlock.OnClause>(); for (var i = 1; i < children.Count; i++) { var child = children[i]; var onClause = new JoinCqlBlock.OnClause(); foreach (var keySlotNum in 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)) { var errorLog = new ErrorLog(); errorLog.AddEntry( new ErrorLog.Record( 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); }
// 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; }
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; }
internal void GetIdentifiers(CqlIdentifiers identifiers) { this.m_cQuery.GetIdentifiers(identifiers); this.m_sQuery.GetIdentifiers(identifiers); }
/// <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) { }
/// <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); }
// <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); }
/// <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; }
// 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] var childSlot = child.SlotValue(slotNum); var constantSlot = childSlot as ConstantProjectedSlot; if (constantSlot != null && constantSlot.CellConstant.IsNull()) { // NULL being generated by a child - don't need to project return; } var originBool = BoolExpression.False; for (var i = 0; i < NumBoolSlots; i++) { var boolSlotNum = BoolIndexToSlot(i); if (child.IsProjected(boolSlotNum)) { // OR it to the expression var boolExpr = new QualifiedCellIdBoolean(child, identifiers, i); originBool = BoolExpression.CreateOr(originBool, BoolExpression.CreateLiteral(boolExpr, RightDomainMap)); } } // Qualify the slotNum with the child.CqlAlias for the THEN var 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 var unrequiredSlotInfo = new SlotInfo(false, false, null, GetMemberPath(slotNum)); return(unrequiredSlotInfo); } // For a required slot, determine the child who is contributing to this value var childDefiningSlot = -1; CaseStatement caseForOuterJoins = null; for (var childNum = 0; childNum < children.Count; childNum++) { var 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) { var 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; } } var 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)); } } // 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. var 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 var slotInfo = new SlotInfo(true, true, slot, memberPath, enforceNotNull); return(slotInfo); }
// 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); }
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; }
// 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, EntityContainerMapping containerMapping) { DebugCheck.NotNull(cells); DebugCheck.NotNull(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 var container = containerMapping.StorageEntityContainer; Debug.Assert(container != null); var viewGenResults = new ViewGenResults(); var tmpLog = EnsureAllCSpaceContainerSetsAreMapped(cells, containerMapping); if (tmpLog.Count > 0) { viewGenResults.AddErrors(tmpLog); Helpers.StringTraceLine(viewGenResults.ErrorsToString()); return viewGenResults; } var foreignKeyConstraints = ForeignConstraint.GetForeignConstraints(container); var partitioner = new CellPartitioner(cells, foreignKeyConstraints); var cellGroups = partitioner.GroupRelatedCells(); foreach (var cellGroup in cellGroups) { ViewGenerator viewGenerator = null; var 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; }
// 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); }
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 override CqlBlock ToCqlBlock( bool[] requiredSlots, CqlIdentifiers identifiers, ref int blockAliasNum, ref List <WithRelationship> withRelationships) { // Get the projected slots and the boolean expressions var totalSlots = requiredSlots.Length; var cellQuery = LeftCellWrapper.RightCellQuery; var projectedSlots = new SlotInfo[totalSlots]; Debug.Assert( cellQuery.NumProjectedSlots + cellQuery.NumBoolVars == totalSlots, "Wrong number of projected slots in node"); Debug.Assert( cellQuery.NumProjectedSlots == ProjectedSlotMap.Count, "Different number of slots in cell query and what we have mappings for"); // Add the regular fields for (var i = 0; i < cellQuery.NumProjectedSlots; i++) { var slot = cellQuery.ProjectedSlotAt(i); // If the slot is not null, we will project it // For extents, we say that all requiredlots are the only the // ones that are CLR non-null. Recall that "real" nulls are // handled by having a CellConstant.Null in ConstantSlot if (requiredSlots[i] && slot == null) { var memberPath = ProjectedSlotMap[i]; var defaultValue = new ConstantProjectedSlot(Domain.GetDefaultValueForMemberPath(memberPath, GetLeaves(), ViewgenContext.Config)); cellQuery.FixMissingSlotAsDefaultConstant(i, defaultValue); slot = defaultValue; } var slotInfo = new SlotInfo( requiredSlots[i], slot != null, slot, ProjectedSlotMap[i]); projectedSlots[i] = slotInfo; } // Add the boolean fields for (var boolNum = 0; boolNum < cellQuery.NumBoolVars; boolNum++) { var expr = cellQuery.GetBoolVar(boolNum); BooleanProjectedSlot boolSlot; if (expr != null) { boolSlot = new BooleanProjectedSlot(expr, identifiers, boolNum); } else { boolSlot = new BooleanProjectedSlot(BoolExpression.False, identifiers, boolNum); } var slotIndex = BoolIndexToSlot(boolNum); var slotInfo = new SlotInfo( requiredSlots[slotIndex], expr != null, boolSlot, null); projectedSlots[slotIndex] = slotInfo; } // See if we are generating a query view and whether there are any colocated foreign keys for which // we have to add With statements. IEnumerable <SlotInfo> totalProjectedSlots = projectedSlots; if ((cellQuery.Extent.EntityContainer.DataSpace == DataSpace.SSpace) && (m_cellWrapper.LeftExtent.BuiltInTypeKind == BuiltInTypeKind.EntitySet)) { var associationSetMaps = ViewgenContext.EntityContainerMapping.GetRelationshipSetMappingsFor(m_cellWrapper.LeftExtent, cellQuery.Extent); var foreignKeySlots = new List <SlotInfo>(); foreach (var colocatedAssociationSetMap in associationSetMaps) { WithRelationship withRelationship; if (TryGetWithRelationship( colocatedAssociationSetMap, m_cellWrapper.LeftExtent, cellQuery.SourceExtentMemberPath, ref foreignKeySlots, out withRelationship)) { withRelationships.Add(withRelationship); totalProjectedSlots = projectedSlots.Concat(foreignKeySlots); } } } var result = new ExtentCqlBlock( cellQuery.Extent, cellQuery.SelectDistinctFlag, totalProjectedSlots.ToArray(), cellQuery.WhereClause, identifiers, ++blockAliasNum); return(result); }
// 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; }
internal CaseCqlBlock( SlotInfo[] slots, int caseSlot, CqlBlock child, BoolExpression whereClause, CqlIdentifiers identifiers, int blockAliasNum) : base(slots, new List<CqlBlock>(new[] { child }), whereClause, identifiers, blockAliasNum) { m_caseSlotInfo = slots[caseSlot]; }
// 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; } }
// 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); var children = new List <CqlBlock>(); var additionalChildSlots = new List <Tuple <CqlBlock, SlotInfo> >(); var totalSlots = requiredSlots.Length; foreach (var child in Children) { // Unlike Join, we pass the requiredSlots from the parent as the requirement. var childProjectedSlots = child.GetProjectedSlots(); AndWith(childProjectedSlots, requiredSlots); var childBlock = child.ToCqlBlock(childProjectedSlots, identifiers, ref blockAliasNum, ref withRelationships); for (var qualifiedSlotNumber = childProjectedSlots.Length; qualifiedSlotNumber < childBlock.Slots.Count; qualifiedSlotNumber++) { additionalChildSlots.Add(Tuple.Create(childBlock, childBlock.Slots[qualifiedSlotNumber])); } // if required, but not projected, add NULL var paddedSlotInfo = new SlotInfo[childBlock.Slots.Count]; for (var 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 var memberPath = childBlock.MemberPath(slotNum); paddedSlotInfo[slotNum] = new SlotInfo( true /* is required */, true /* is projected */, new ConstantProjectedSlot(Constant.Null), 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 (var childBlock in children) { var childSlots = new SlotInfo[totalSlots + additionalChildSlots.Count]; childBlock.Slots.CopyTo(childSlots, 0); var index = totalSlots; foreach (var additionalChildSlotInfo in additionalChildSlots) { var slotInfo = additionalChildSlotInfo.Item2; if (additionalChildSlotInfo.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); } //move on to the next slot added by children. index++; } childBlock.Slots = new ReadOnlyCollection <SlotInfo>(childSlots); } } // Create the slotInfos and then Union CqlBlock var 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 var firstChild = children[0]; for (var slotNum = 0; slotNum < totalSlots; slotNum++) { var slotInfo = firstChild.Slots[slotNum]; // A required slot is somehow projected by a child in Union, so set isProjected to be the same as isRequired. var isRequired = requiredSlots[slotNum]; slotInfos[slotNum] = new SlotInfo(isRequired, isRequired, slotInfo.SlotValue, slotInfo.OutputMember); } for (var 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: 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(); }
private CqlBlock UnionToCqlBlock( bool[] requiredSlots, CqlIdentifiers identifiers, ref int blockAliasNum, ref List <WithRelationship> withRelationships) { List <CqlBlock> children = new List <CqlBlock>(); List <Tuple <CqlBlock, SlotInfo> > tupleList = new List <Tuple <CqlBlock, SlotInfo> >(); int length1 = requiredSlots.Length; foreach (CellTreeNode child in this.Children) { bool[] projectedSlots = child.GetProjectedSlots(); OpCellTreeNode.AndWith(projectedSlots, requiredSlots); CqlBlock cqlBlock = child.ToCqlBlock(projectedSlots, identifiers, ref blockAliasNum, ref withRelationships); for (int length2 = projectedSlots.Length; length2 < cqlBlock.Slots.Count; ++length2) { tupleList.Add(Tuple.Create <CqlBlock, SlotInfo>(cqlBlock, cqlBlock.Slots[length2])); } SlotInfo[] slotInfoArray = new SlotInfo[cqlBlock.Slots.Count]; for (int slotNum = 0; slotNum < length1; ++slotNum) { if (requiredSlots[slotNum] && !projectedSlots[slotNum]) { if (this.IsBoolSlot(slotNum)) { slotInfoArray[slotNum] = new SlotInfo(true, true, (ProjectedSlot) new BooleanProjectedSlot(BoolExpression.False, identifiers, this.SlotToBoolIndex(slotNum)), (MemberPath)null); } else { MemberPath outputMember = cqlBlock.MemberPath(slotNum); slotInfoArray[slotNum] = new SlotInfo(true, true, (ProjectedSlot) new ConstantProjectedSlot(Constant.Null), outputMember); } } else { slotInfoArray[slotNum] = cqlBlock.Slots[slotNum]; } } cqlBlock.Slots = new ReadOnlyCollection <SlotInfo>((IList <SlotInfo>)slotInfoArray); children.Add(cqlBlock); } if (tupleList.Count != 0) { foreach (CqlBlock cqlBlock in children) { SlotInfo[] array = new SlotInfo[length1 + tupleList.Count]; cqlBlock.Slots.CopyTo(array, 0); int index = length1; foreach (Tuple <CqlBlock, SlotInfo> tuple in tupleList) { SlotInfo slotInfo = tuple.Item2; array[index] = !tuple.Item1.Equals((object)cqlBlock) ? new SlotInfo(true, true, (ProjectedSlot) new ConstantProjectedSlot(Constant.Null), slotInfo.OutputMember) : new SlotInfo(true, true, slotInfo.SlotValue, slotInfo.OutputMember); ++index; } cqlBlock.Slots = new ReadOnlyCollection <SlotInfo>((IList <SlotInfo>)array); } } SlotInfo[] slotInfos = new SlotInfo[length1 + tupleList.Count]; CqlBlock cqlBlock1 = children[0]; for (int index = 0; index < length1; ++index) { SlotInfo slot = cqlBlock1.Slots[index]; bool requiredSlot = requiredSlots[index]; slotInfos[index] = new SlotInfo(requiredSlot, requiredSlot, slot.SlotValue, slot.OutputMember); } for (int index = length1; index < length1 + tupleList.Count; ++index) { SlotInfo slot = cqlBlock1.Slots[index]; slotInfos[index] = new SlotInfo(true, true, slot.SlotValue, slot.OutputMember); } return((CqlBlock) new UnionCqlBlock(slotInfos, children, identifiers, ++blockAliasNum)); }