/// <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) { var numSlotsAddedByChildBlock = childBlock.Slots.Count - TotalSlots; var slotInfos = new SlotInfo[TotalSlots + numSlotsAddedByChildBlock]; for (var slotNum = 0; slotNum < TotalSlots; slotNum++) { var isProjected = childBlock.IsProjected(slotNum); var isRequiredByParent = parentRequiredSlots[slotNum]; var slot = childBlock.SlotValue(slotNum); var 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 var 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" var slotInfo = new SlotInfo(isRequiredByParent && isProjected, isProjected, slot, outputMember); slotInfos[slotNum] = slotInfo; } for (var i = TotalSlots; i < TotalSlots + numSlotsAddedByChildBlock; i++) { var childAddedSlot = childBlock.QualifySlotWithBlockAlias(i); slotInfos[i] = new SlotInfo(true, true, childAddedSlot, childBlock.MemberPath(i)); } return slotInfos; }
// 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); }