/// <summary> /// Recursively qualifies all <see cref="ProjectedSlot"/>s and returns a new deeply qualified <see cref="CaseStatement"/>. /// </summary> internal CaseStatement DeepQualify(CqlBlock block) { // Go through the whenthens and else and make a new case statement with qualified slots as needed. var result = new CaseStatement(m_memberPath); foreach (var whenThen in m_clauses) { var newClause = whenThen.ReplaceWithQualifiedSlot(block); result.m_clauses.Add(newClause); } if (m_elseValue != null) { result.m_elseValue = m_elseValue.DeepQualify(block); } result.m_simplified = m_simplified; return result; }
internal override DbExpression AsCqt(bool isTopLevel) { Debug.Assert(m_caseSlotInfo.OutputMember != null, "We only construct real slots not boolean slots"); // The FROM part: FROM (childBlock) Debug.Assert(Children.Count == 1, "CaseCqlBlock can have exactly one child."); CqlBlock childBlock = this.Children[0]; DbExpression cqt = childBlock.AsCqt(false); // Get the WHERE part only when the expression is not simply TRUE. if (!BoolExpression.EqualityComparer.Equals(this.WhereClause, BoolExpression.True)) { cqt = cqt.Where(row => this.WhereClause.AsCqt(row)); } // The SELECT part. return(cqt.Select(row => GenerateProjectionCqt(row, isTopLevel))); }
internal override ProjectedSlot DeepQualify(CqlBlock block) { return this; // Nothing to create }
/// <summary> /// Creates new <see cref="ProjectedSlot"/> that is qualified with <paramref name="block"/>.CqlAlias. /// If current slot is composite (such as <see cref="CaseStatementProjectedSlot"/>, then this method recursively qualifies all parts /// and returns a new deeply qualified slot (as opposed to <see cref="CqlBlock.QualifySlotWithBlockAlias"/>). /// </summary> internal override ProjectedSlot DeepQualify(CqlBlock block) { // We take the slot inside this and change the block QualifiedSlot result = new QualifiedSlot(block, m_slot); return result; }
/// <summary> /// Creates a qualified slot "block_alias.slot_alias" /// </summary> internal QualifiedSlot(CqlBlock block, ProjectedSlot slot) { Debug.Assert(block != null && slot != null, "Null input to QualifiedSlot constructor"); m_block = block; m_slot = slot; // Note: slot can be another qualified slot. }
/// <summary> /// Creates a qualified slot "block_alias.slot_alias" /// </summary> internal QualifiedSlot(CqlBlock block, ProjectedSlot slot) { Debug.Assert(block != null && slot != null, "Null input to QualifiedSlot constructor"); m_block = block; m_slot = slot; // Note: slot can be another qualified slot. }
/// <summary> /// Given the slot (<paramref name="foundSlot"/>) and its corresponding case statement (<paramref name="thisCaseStatement"/>), /// generates the slotinfos for the cql block producing the case statement. /// </summary> private SlotInfo[] CreateSlotInfosForCaseStatement(bool[] parentRequiredSlots, int foundSlot, CqlBlock childBlock, CaseStatement thisCaseStatement, IEnumerable<WithRelationship> withRelationships) { int numSlotsAddedByChildBlock = childBlock.Slots.Count - TotalSlots; SlotInfo[] slotInfos = new SlotInfo[TotalSlots + numSlotsAddedByChildBlock]; for (int slotNum = 0; slotNum < TotalSlots; slotNum++) { bool isProjected = childBlock.IsProjected(slotNum); bool isRequiredByParent = parentRequiredSlots[slotNum]; ProjectedSlot slot = childBlock.SlotValue(slotNum); MemberPath outputMember = GetOutputMemberPath(slotNum); if (slotNum == foundSlot) { // We need a case statement instead for this slot that we // are handling right now Debug.Assert(isRequiredByParent, "Case result not needed by parent"); // Get a case statement with all slots replaced by aliases slots CaseStatement newCaseStatement = thisCaseStatement.DeepQualify(childBlock); slot = new CaseStatementProjectedSlot(newCaseStatement, withRelationships); isProjected = true; // We are projecting this slot now } else if (isProjected && isRequiredByParent) { // We only alias something that is needed and is being projected by the child. // It is a qualified slot into the child block. slot = childBlock.QualifySlotWithBlockAlias(slotNum); } // For slots, if it is not required by the parent, we want to // set the isRequiredByParent for this slot to be // false. Furthermore, we do not want to introduce any "NULL // AS something" at this stage for slots not being // projected. So if the child does not project that slot, we // declare it as not being required by the parent (if such a // NULL was needed, it would have been pushed all the way // down to a non-case block. // Essentially, from a Case statement's parent perspective, // it is saying "If you can produce a slot either by yourself // or your children, please do. Otherwise, do not concoct anything" SlotInfo slotInfo = new SlotInfo(isRequiredByParent && isProjected, isProjected, slot, outputMember); slotInfos[slotNum] = slotInfo; } for (int i = TotalSlots; i < TotalSlots + numSlotsAddedByChildBlock; i++) { QualifiedSlot childAddedSlot = childBlock.QualifySlotWithBlockAlias(i); slotInfos[i] = new SlotInfo(true, true, childAddedSlot, childBlock.MemberPath(i)); } return slotInfos; }
/// <summary> /// Given the <paramref name="viewBlock"/> tree generated by the cell merging process and the <paramref name="parentRequiredSlots"/>, /// generates the block tree for the case statement at or past the startSlotNum, i.e., only for case statements that are beyond startSlotNum. /// </summary> private CqlBlock ConstructCaseBlocks(CqlBlock viewBlock, int startSlotNum, bool[] parentRequiredSlots, IEnumerable<WithRelationship> withRelationships) { int numMembers = m_projectedSlotMap.Count; // Find the next slot for which we have a case statement, i.e., // which was in the multiconstants int foundSlot = FindNextCaseStatementSlot(startSlotNum, parentRequiredSlots, numMembers); if (foundSlot == -1) { // We have bottomed out - no more slots to generate cases for // Just get the base view block return viewBlock; } // Compute the requiredSlots for this member, i.e., what slots are needed to produce this member. MemberPath thisMember = m_projectedSlotMap[foundSlot]; bool[] thisRequiredSlots = new bool[TotalSlots]; GetRequiredSlotsForCaseMember(thisMember, thisRequiredSlots); Debug.Assert(thisRequiredSlots.Length == parentRequiredSlots.Length && thisRequiredSlots.Length == TotalSlots, "Number of slots in array should not vary across blocks"); // Merge parent's requirements with this requirements for (int i = 0; i < TotalSlots; i++) { // We do ask the children to generate the slot that we are // producing if it is available if (parentRequiredSlots[i]) { thisRequiredSlots[i] = true; } } // If current case statement depends on its slot value, then make sure the value is produced by the child block. CaseStatement thisCaseStatement = m_caseStatements[thisMember]; thisRequiredSlots[foundSlot] = thisCaseStatement.DependsOnMemberValue; // Recursively, determine the block tree for slots beyond foundSlot. CqlBlock childBlock = ConstructCaseBlocks(viewBlock, foundSlot + 1, thisRequiredSlots, null); // For each slot, create a SlotInfo object SlotInfo[] slotInfos = CreateSlotInfosForCaseStatement(parentRequiredSlots, foundSlot, childBlock, thisCaseStatement, withRelationships); m_currentBlockNum++; // We have a where clause only at the top level BoolExpression whereClause = startSlotNum == 0 ? m_topLevelWhereClause : BoolExpression.True; if (startSlotNum == 0) { // only slot #0 is required by parent; reset all 'required by parent' booleans introduced above for (int i = 1; i < slotInfos.Length; i++) { slotInfos[i].ResetIsRequiredByParent(); } } CaseCqlBlock result = new CaseCqlBlock(slotInfos, foundSlot, childBlock, whereClause, m_identifiers, m_currentBlockNum); return result; }
/// <summary> /// Given the <paramref name="viewBlock"/> tree, generates the case statement blocks on top of it (using <see cref="m_caseStatements"/>) and returns the resulting tree. /// One block per case statement is generated. Generated blocks are nested, with the <paramref name="viewBlock"/> is the innermost input. /// </summary> private CqlBlock ConstructCaseBlocks(CqlBlock viewBlock, IEnumerable<WithRelationship> withRelationships) { // Get the 0th slot only, i.e., the extent bool[] topSlots = new bool[TotalSlots]; topSlots[0] = true; // all booleans in the top-level WHERE clause are required and get bubbled up // this makes some _fromX booleans be marked as 'required by parent' m_topLevelWhereClause.GetRequiredSlots(m_projectedSlotMap, topSlots); CqlBlock result = ConstructCaseBlocks(viewBlock, 0, topSlots, withRelationships); return result; }
/// <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]; }
/// <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]; }
/// <summary> /// Creates new <see cref="ProjectedSlot"/> that is qualified with <paramref name="block"/>.CqlAlias. /// If current slot is composite (such as <see cref="CaseStatementProjectedSlot"/>, then this method recursively qualifies all parts /// and returns a new deeply qualified slot (as opposed to <see cref="CqlBlock.QualifySlotWithBlockAlias"/>). /// </summary> internal override ProjectedSlot DeepQualify(CqlBlock block) { CaseStatement newStatement = m_caseStatement.DeepQualify(block); return new CaseStatementProjectedSlot(newStatement, null); }
internal WhenThen ReplaceWithQualifiedSlot(CqlBlock block) { // Change the THEN part var newValue = m_value.DeepQualify(block); return new WhenThen(m_condition, newValue); }