Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / ndp / fx / src / DataEntity / System / Data / Map / ViewGeneration / Structures / OpCellTreeNode.cs / 2 / OpCellTreeNode.cs
//---------------------------------------------------------------------- //// Copyright (c) Microsoft Corporation. All rights reserved. // // // @owner [....] // @backupOwner [....] //--------------------------------------------------------------------- using System.Data.Common.Utils; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Data.Mapping.ViewGeneration.CqlGeneration; using System.Data.Mapping.ViewGeneration.QueryRewriting; using System.Text; using System.Linq; using System.Diagnostics; using System.Data.Metadata.Edm; namespace System.Data.Mapping.ViewGeneration.Structures { using AttributeSet = Set; // This class represents th intermediate nodes in the tree (non-leaf nodes) internal class OpCellTreeNode : CellTreeNode { #region Constructors // effects: Creates a node with operation opType and no children internal OpCellTreeNode(CellNormalizer normalizer, CellTreeOpType opType) : base(normalizer) { m_opType = opType; m_attrs = new AttributeSet(MemberPath.EqualityComparer); m_children = new List (); } internal OpCellTreeNode(CellNormalizer normalizer, CellTreeOpType opType, params CellTreeNode[] children) : this(normalizer, opType, (IEnumerable )children) { } // effects: Given a sequence of children node and the opType, creates // an OpCellTreeNode and returns it internal OpCellTreeNode(CellNormalizer normalizer, CellTreeOpType opType, IEnumerable children) : this(normalizer, opType) { // Add the children one by one so that we can get the attrs etc fixed foreach (CellTreeNode child in children) { Add(child); } } #endregion #region Fields private Set m_attrs; // attributes from whole subtree below private List m_children; private CellTreeOpType m_opType; private FragmentQuery m_leftFragmentQuery; private FragmentQuery m_rightFragmentQuery; #endregion #region Properties // effects: See CellTreeNode.OpType internal override CellTreeOpType OpType { get { return m_opType; } } // fragment query superseeds SelectionDomain internal override FragmentQuery LeftFragmentQuery { get { if (m_leftFragmentQuery == null) { Debug.Assert(Children.Count > 0); FragmentQuery leftFragmentQuery = Children[0].LeftFragmentQuery; FragmentQueryProcessor leftQP = CellNormalizer.LeftFragmentQP; for (int i = 1; i < Children.Count; i++) { FragmentQuery nextLeftQuery = Children[i].LeftFragmentQuery; switch (OpType) { case CellTreeOpType.IJ: leftFragmentQuery = leftQP.Intersect(leftFragmentQuery, nextLeftQuery); break; case CellTreeOpType.LOJ: // Left outer join means keeping the domain of the leftmost child break; case CellTreeOpType.LASJ: // not used in basic view generation but current validation calls Simplify, so add this for debugging leftFragmentQuery = leftQP.Difference(leftFragmentQuery, nextLeftQuery); break; default: // All other operators (Union, FOJ) require union of the domains leftFragmentQuery = leftQP.Union(leftFragmentQuery, nextLeftQuery); break; } } m_leftFragmentQuery = leftFragmentQuery; } return m_leftFragmentQuery; } } internal override FragmentQuery RightFragmentQuery { get { if (m_rightFragmentQuery == null) { Debug.Assert(Children.Count > 0); FragmentQuery rightFragmentQuery = Children[0].RightFragmentQuery; FragmentQueryProcessor rightQP = CellNormalizer.RightFragmentQP; for (int i = 1; i < Children.Count; i++) { FragmentQuery nextRightQuery = Children[i].RightFragmentQuery; switch (OpType) { case CellTreeOpType.IJ: rightFragmentQuery = rightQP.Intersect(rightFragmentQuery, nextRightQuery); break; case CellTreeOpType.LOJ: // Left outer join means keeping the domain of the leftmost child break; case CellTreeOpType.LASJ: // not used in basic view generation but current validation calls Simplify, so add this for debugging rightFragmentQuery = rightQP.Difference(rightFragmentQuery, nextRightQuery); break; default: // All other operators (Union, FOJ) require union of the domains rightFragmentQuery = rightQP.Union(rightFragmentQuery, nextRightQuery); break; } } m_rightFragmentQuery = rightFragmentQuery; } return m_rightFragmentQuery; } } // effects: See CellTreeNode.RightDomainMap internal override MemberDomainMap RightDomainMap { get { // Get the information from one of the children Debug.Assert(m_children[0].RightDomainMap != null, "EdmMember domain map missing"); return m_children[0].RightDomainMap; } } // effects: See CellTreeNode.Attributes internal override Set Attributes { get { return m_attrs; } } // effects: See CellTreeNode.Children internal override List Children { get { return m_children; } } internal override int NumProjectedSlots { get { // All children have the same number of slots Debug.Assert(m_children.Count > 1, "No children for op node?"); return m_children[0].NumProjectedSlots; } } internal override int NumBoolSlots { get { Debug.Assert(m_children.Count > 1, "No children for op node?"); return m_children[0].NumBoolSlots; } } #endregion #region Methods internal override TOutput Accept (SimpleCellTreeVisitor visitor, TInput param) { return visitor.VisitOpNode(this, param); } internal override TOutput Accept (CellTreeVisitor visitor, TInput param) { switch (OpType) { case CellTreeOpType.IJ: return visitor.VisitInnerJoin(this, param); case CellTreeOpType.LOJ: return visitor.VisitLeftOuterJoin(this, param); case CellTreeOpType.Union: return visitor.VisitUnion(this, param); case CellTreeOpType.FOJ: return visitor.VisitFullOuterJoin(this, param); case CellTreeOpType.LASJ: return visitor.VisitLeftAntiSemiJoin(this, param); default: Debug.Fail("Unexpected optype: " + OpType); // To satsfy the compiler return visitor.VisitInnerJoin(this, param); } } // effects: Add child to the end of the current children list // while ensuring the constants and attributes of the child are // propagated into this (i.e., unioned) internal void Add(CellTreeNode child) { Insert(m_children.Count, child); } // effects: Add child at the beginning of the current children list // while ensuring the constants and attributes of the child are // propagated into this (i.e., unioned) internal void AddFirst(CellTreeNode child) { Insert(0, child); } // effects: Inserts child at "index" while ensuring the constants // and attributes of the child are propagated into this private void Insert(int index, CellTreeNode child) { m_attrs.Unite(child.Attributes); m_children.Insert(index, child); // reset fragmentQuery so it's recomputed when property FragmentQuery is accessed m_leftFragmentQuery = null; m_rightFragmentQuery = null; } // effects: Given the required slots by the parent, // generates a CqlBlock tree for the tree rooted below node internal override CqlBlock ToCqlBlock(bool[] requiredSlots, CqlIdentifiers identifiers, ref int blockAliasNum, ref List withStatements) { // Dispatch depending on whether we have a union node or join node CqlBlock result; if (OpType == CellTreeOpType.Union) { result = UnionToCqlBlock(requiredSlots, identifiers, ref blockAliasNum, ref withStatements); } else { result = JoinToCqlBlock(requiredSlots, identifiers, ref blockAliasNum, ref withStatements); } return result; } internal override bool IsProjectedSlot(int slot) { // If any childtree projects it, return true foreach (CellTreeNode childNode in Children) { if (childNode.IsProjectedSlot(slot)) { return true; } } return false; } #endregion #region Union CqlBLock Methods // 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 withStatements) { Debug.Assert(OpType == CellTreeOpType.Union); List children = new List (); List aliasedSlotsForAddedByChildren = new List (); int totalSlots = requiredSlots.Length; foreach (CellTreeNode child in Children) { // Unlike Join, we pass the requiredSlots from the parent as the requirement // CqlBlock childBlock = child.ToCqlBlock(requiredSlots, identifiers, ref blockAliasNum); bool[] childProjectedSlots = child.GetProjectedSlots(); AndWith(childProjectedSlots, requiredSlots); CqlBlock childBlock = child.ToCqlBlock(childProjectedSlots, identifiers, ref blockAliasNum, ref withStatements); for (int aliasedSlotNumber = childProjectedSlots.Length; aliasedSlotNumber < childBlock.Slots.Count; aliasedSlotNumber++) { SlotInfo slotInfo = childBlock.Slots[aliasedSlotNumber]; aliasedSlotsForAddedByChildren.Add(new AliasedSlot(childBlock, slotInfo.SlotValue, slotInfo.MemberPath, aliasedSlotNumber)); } // if required, but not projected, add NULL SlotInfo[] paddedSlotInfo = new SlotInfo[childBlock.Slots.Count]; ReadOnlyCollection childSlotInfo = childBlock.Slots; 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 paddedSlotInfo[slotNum] = new SlotInfo(true /* is required */, true /* is projected */, new ConstantSlot(CellConstant.Null), childSlotInfo[slotNum].MemberPath); } } else { paddedSlotInfo[slotNum] = childSlotInfo[slotNum]; } } //Add the slots that were added by children //for (int slotNum = totalSlots; slotNum < childBlock.Slots.Count; slotNum++) //{ // paddedSlotInfo[slotNum] = childSlotInfo[slotNum]; //} childBlock.Slots = new ReadOnlyCollection (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 unformly for others( as nulls) //since this is a union operation if (aliasedSlotsForAddedByChildren.Count != 0) { foreach (CqlBlock childBlock in children) { SlotInfo[] childSlots = new SlotInfo[totalSlots + aliasedSlotsForAddedByChildren.Count]; childBlock.Slots.CopyTo(childSlots,0); int index = totalSlots; foreach (AliasedSlot aliasedSlot in aliasedSlotsForAddedByChildren) { if (aliasedSlot.Block.Equals(childBlock)) { childSlots[index] = new SlotInfo(true /* is required */, true /* is projected */, aliasedSlot.InnerSlot, aliasedSlot.MemberPath); } else { childSlots[index] = new SlotInfo(true /* is required */, true /* is projected */, new ConstantSlot(CellConstant.Null), aliasedSlot.MemberPath); } //move on to the next slot added by children. index++; } childBlock.Slots = new ReadOnlyCollection (childSlots); } } // Create the slotInfos and then Union CqlBlock SlotInfo[] slotInfos = new SlotInfo[totalSlots + aliasedSlotsForAddedByChildren.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++) { ProjectedSlot slot = firstChild.ProjectedSlot(slotNum); MemberPath memberPath = GetMemberPath(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]; SlotInfo slotInfo = new SlotInfo(isRequired, isRequired, slot, memberPath); slotInfos[slotNum] = slotInfo; } for (int i = 0, slotNum = totalSlots; slotNum < totalSlots + aliasedSlotsForAddedByChildren.Count; slotNum++, i++) { slotInfos[slotNum] = new SlotInfo(true, true, aliasedSlotsForAddedByChildren[i], aliasedSlotsForAddedByChildren[i].MemberPath); } CqlBlock block = new UnionCqlBlock(slotInfos, children, identifiers, ++blockAliasNum); return block; } private static void AndWith(bool[] boolArray, bool[] another) { Debug.Assert(boolArray.Length == another.Length); for (int i = 0; i < boolArray.Length; i++) { boolArray[i] &= another[i]; } } #endregion #region Join CqlBLock Methods // 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 withStatements) { int totalSlots = requiredSlots.Length; Debug.Assert(OpType == CellTreeOpType.IJ || OpType == CellTreeOpType.LOJ || OpType == CellTreeOpType.FOJ, "Only these join operations handled"); List children = new List (); List aliasedSlotsForAddedByChildren = new List (); // 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); //List subAddedSlotsByChild = new List (); CqlBlock childBlock = child.ToCqlBlock(childProjectedSlots, identifiers, ref blockAliasNum, ref withStatements); //addedSlotsByChild.AddRange(subAddedSlotsByChild); children.Add(childBlock); //foreach (SlotInfo slotInfo in subAddedSlotsByChild) for(int aliasedSlotNumber = childProjectedSlots.Length; aliasedSlotNumber < childBlock.Slots.Count;aliasedSlotNumber++) { SlotInfo slotInfo = childBlock.Slots[aliasedSlotNumber]; aliasedSlotsForAddedByChildren.Add(new AliasedSlot(childBlock, slotInfo.SlotValue, slotInfo.MemberPath, aliasedSlotNumber)); } 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 + aliasedSlotsForAddedByChildren.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 + aliasedSlotsForAddedByChildren.Count; slotNum++, i++) { slotInfos[slotNum] = new SlotInfo(true, true, aliasedSlotsForAddedByChildren[i], aliasedSlotsForAddedByChildren[i].MemberPath); } // Generate the ON conditions: For each child, generate an ON // clause with the 0th child on the key fields List onClauses = new List (); for (int i = 1; i < children.Count; i++) { CqlBlock child = children[i]; JoinCqlBlock.OnClause onClause = new JoinCqlBlock.OnClause(); foreach (int keySlotNum in KeySlots) { SlotInfo slotInfo = slotInfos[keySlotNum]; Debug.Assert(child.IsProjected(keySlotNum), "Key is not in child"); Debug.Assert(children[0].IsProjected(keySlotNum), "Key is not in 0th child"); AliasedSlot first = new AliasedSlot(children[0], slotInfo.SlotValue, slotInfo.MemberPath, keySlotNum); AliasedSlot second = new AliasedSlot(child, slotInfo.SlotValue, slotInfo.MemberPath, keySlotNum); onClause.Add(first, second); } onClauses.Add(onClause); } CqlBlock result = new JoinCqlBlock(OpType, slotInfos, children, onClauses, identifiers, ++blockAliasNum); return result; } // 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 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 an aliased 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 CaseStatementSlot(caseForOuterJoins, null); } else if (childDefiningSlot >= 0) { slot = new AliasedSlot(children[childDefiningSlot], children[childDefiningSlot].ProjectedSlot(slotNum), memberPath, 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 ConstantSlot(CellConstantDomain.GetDefaultValueForMemberPath(memberPath, GetLeaves(), CellNormalizer.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. 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; } // requires: children to be a list of nodes that are children of an // Inner Join node. slotNum does not correspond to the key slot // effects: Determines the child number from which the slot should be // picked up. private static int GetInnerJoinChildForSlot(List children, int slotNum) { // Picks the child with the non-constant slot first. If none, picks a non-null constant slot. // If not een that, picks any one int result = -1; for (int i = 0; i < children.Count; i++) { CqlBlock child = children[i]; if (false == child.IsProjected(slotNum)) { continue; } ProjectedSlot slot = child.ProjectedSlot(slotNum); ConstantSlot constantSlot = slot as ConstantSlot; JoinTreeSlot joinSlot = slot as JoinTreeSlot; if (joinSlot != null) { // Pick the non-constant slot result = i; } else if (constantSlot != null && constantSlot.CellConstant.IsNull()) { if (result == -1) { // In case, all are null result = i; } } else { // Just pick anything result = i; } } return result; } // 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.ProjectedSlot(slotNum); ConstantSlot constantSlot = childSlot as ConstantSlot; 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)); } } // Make an aliased slot corresponding to child[slotNum] for the THEN MemberPath outputMember = GetMemberPath(slotNum); AliasedSlot slot = new AliasedSlot(child, childSlot, outputMember, slotNum); caseForOuterJoins.AddWhenThen(originBool, slot); } #endregion #region String methods // effects: Given an optype, returns a SQL-acceptable string // corresponding to the op internal static string OpToCql(CellTreeOpType opType) { switch (opType) { case CellTreeOpType.FOJ: return "FULL OUTER JOIN"; case CellTreeOpType.IJ: return "INNER JOIN"; //case CellTreeOpType.LASJ: return "LEFT ANTISEMIJOIN"; case CellTreeOpType.LOJ: return "LEFT OUTER JOIN"; case CellTreeOpType.Union: return "UNION ALL"; } Debug.Fail("Unknown operator"); return null; } internal override void ToCompactString(StringBuilder stringBuilder) { // Debug.Assert(m_children.Count > 1, "Tree not flattened?"); stringBuilder.Append("("); for (int i = 0; i < m_children.Count; i++) { CellTreeNode child = m_children[i]; child.ToCompactString(stringBuilder); if (i != m_children.Count - 1) { StringUtil.FormatStringBuilder(stringBuilder, " {0} ", OpType); } } stringBuilder.Append(")"); } #endregion } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. //---------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // // // @owner [....] // @backupOwner [....] //--------------------------------------------------------------------- using System.Data.Common.Utils; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Data.Mapping.ViewGeneration.CqlGeneration; using System.Data.Mapping.ViewGeneration.QueryRewriting; using System.Text; using System.Linq; using System.Diagnostics; using System.Data.Metadata.Edm; namespace System.Data.Mapping.ViewGeneration.Structures { using AttributeSet = Set; // This class represents th intermediate nodes in the tree (non-leaf nodes) internal class OpCellTreeNode : CellTreeNode { #region Constructors // effects: Creates a node with operation opType and no children internal OpCellTreeNode(CellNormalizer normalizer, CellTreeOpType opType) : base(normalizer) { m_opType = opType; m_attrs = new AttributeSet(MemberPath.EqualityComparer); m_children = new List (); } internal OpCellTreeNode(CellNormalizer normalizer, CellTreeOpType opType, params CellTreeNode[] children) : this(normalizer, opType, (IEnumerable )children) { } // effects: Given a sequence of children node and the opType, creates // an OpCellTreeNode and returns it internal OpCellTreeNode(CellNormalizer normalizer, CellTreeOpType opType, IEnumerable children) : this(normalizer, opType) { // Add the children one by one so that we can get the attrs etc fixed foreach (CellTreeNode child in children) { Add(child); } } #endregion #region Fields private Set m_attrs; // attributes from whole subtree below private List m_children; private CellTreeOpType m_opType; private FragmentQuery m_leftFragmentQuery; private FragmentQuery m_rightFragmentQuery; #endregion #region Properties // effects: See CellTreeNode.OpType internal override CellTreeOpType OpType { get { return m_opType; } } // fragment query superseeds SelectionDomain internal override FragmentQuery LeftFragmentQuery { get { if (m_leftFragmentQuery == null) { Debug.Assert(Children.Count > 0); FragmentQuery leftFragmentQuery = Children[0].LeftFragmentQuery; FragmentQueryProcessor leftQP = CellNormalizer.LeftFragmentQP; for (int i = 1; i < Children.Count; i++) { FragmentQuery nextLeftQuery = Children[i].LeftFragmentQuery; switch (OpType) { case CellTreeOpType.IJ: leftFragmentQuery = leftQP.Intersect(leftFragmentQuery, nextLeftQuery); break; case CellTreeOpType.LOJ: // Left outer join means keeping the domain of the leftmost child break; case CellTreeOpType.LASJ: // not used in basic view generation but current validation calls Simplify, so add this for debugging leftFragmentQuery = leftQP.Difference(leftFragmentQuery, nextLeftQuery); break; default: // All other operators (Union, FOJ) require union of the domains leftFragmentQuery = leftQP.Union(leftFragmentQuery, nextLeftQuery); break; } } m_leftFragmentQuery = leftFragmentQuery; } return m_leftFragmentQuery; } } internal override FragmentQuery RightFragmentQuery { get { if (m_rightFragmentQuery == null) { Debug.Assert(Children.Count > 0); FragmentQuery rightFragmentQuery = Children[0].RightFragmentQuery; FragmentQueryProcessor rightQP = CellNormalizer.RightFragmentQP; for (int i = 1; i < Children.Count; i++) { FragmentQuery nextRightQuery = Children[i].RightFragmentQuery; switch (OpType) { case CellTreeOpType.IJ: rightFragmentQuery = rightQP.Intersect(rightFragmentQuery, nextRightQuery); break; case CellTreeOpType.LOJ: // Left outer join means keeping the domain of the leftmost child break; case CellTreeOpType.LASJ: // not used in basic view generation but current validation calls Simplify, so add this for debugging rightFragmentQuery = rightQP.Difference(rightFragmentQuery, nextRightQuery); break; default: // All other operators (Union, FOJ) require union of the domains rightFragmentQuery = rightQP.Union(rightFragmentQuery, nextRightQuery); break; } } m_rightFragmentQuery = rightFragmentQuery; } return m_rightFragmentQuery; } } // effects: See CellTreeNode.RightDomainMap internal override MemberDomainMap RightDomainMap { get { // Get the information from one of the children Debug.Assert(m_children[0].RightDomainMap != null, "EdmMember domain map missing"); return m_children[0].RightDomainMap; } } // effects: See CellTreeNode.Attributes internal override Set Attributes { get { return m_attrs; } } // effects: See CellTreeNode.Children internal override List Children { get { return m_children; } } internal override int NumProjectedSlots { get { // All children have the same number of slots Debug.Assert(m_children.Count > 1, "No children for op node?"); return m_children[0].NumProjectedSlots; } } internal override int NumBoolSlots { get { Debug.Assert(m_children.Count > 1, "No children for op node?"); return m_children[0].NumBoolSlots; } } #endregion #region Methods internal override TOutput Accept (SimpleCellTreeVisitor visitor, TInput param) { return visitor.VisitOpNode(this, param); } internal override TOutput Accept (CellTreeVisitor visitor, TInput param) { switch (OpType) { case CellTreeOpType.IJ: return visitor.VisitInnerJoin(this, param); case CellTreeOpType.LOJ: return visitor.VisitLeftOuterJoin(this, param); case CellTreeOpType.Union: return visitor.VisitUnion(this, param); case CellTreeOpType.FOJ: return visitor.VisitFullOuterJoin(this, param); case CellTreeOpType.LASJ: return visitor.VisitLeftAntiSemiJoin(this, param); default: Debug.Fail("Unexpected optype: " + OpType); // To satsfy the compiler return visitor.VisitInnerJoin(this, param); } } // effects: Add child to the end of the current children list // while ensuring the constants and attributes of the child are // propagated into this (i.e., unioned) internal void Add(CellTreeNode child) { Insert(m_children.Count, child); } // effects: Add child at the beginning of the current children list // while ensuring the constants and attributes of the child are // propagated into this (i.e., unioned) internal void AddFirst(CellTreeNode child) { Insert(0, child); } // effects: Inserts child at "index" while ensuring the constants // and attributes of the child are propagated into this private void Insert(int index, CellTreeNode child) { m_attrs.Unite(child.Attributes); m_children.Insert(index, child); // reset fragmentQuery so it's recomputed when property FragmentQuery is accessed m_leftFragmentQuery = null; m_rightFragmentQuery = null; } // effects: Given the required slots by the parent, // generates a CqlBlock tree for the tree rooted below node internal override CqlBlock ToCqlBlock(bool[] requiredSlots, CqlIdentifiers identifiers, ref int blockAliasNum, ref List withStatements) { // Dispatch depending on whether we have a union node or join node CqlBlock result; if (OpType == CellTreeOpType.Union) { result = UnionToCqlBlock(requiredSlots, identifiers, ref blockAliasNum, ref withStatements); } else { result = JoinToCqlBlock(requiredSlots, identifiers, ref blockAliasNum, ref withStatements); } return result; } internal override bool IsProjectedSlot(int slot) { // If any childtree projects it, return true foreach (CellTreeNode childNode in Children) { if (childNode.IsProjectedSlot(slot)) { return true; } } return false; } #endregion #region Union CqlBLock Methods // 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 withStatements) { Debug.Assert(OpType == CellTreeOpType.Union); List children = new List (); List aliasedSlotsForAddedByChildren = new List (); int totalSlots = requiredSlots.Length; foreach (CellTreeNode child in Children) { // Unlike Join, we pass the requiredSlots from the parent as the requirement // CqlBlock childBlock = child.ToCqlBlock(requiredSlots, identifiers, ref blockAliasNum); bool[] childProjectedSlots = child.GetProjectedSlots(); AndWith(childProjectedSlots, requiredSlots); CqlBlock childBlock = child.ToCqlBlock(childProjectedSlots, identifiers, ref blockAliasNum, ref withStatements); for (int aliasedSlotNumber = childProjectedSlots.Length; aliasedSlotNumber < childBlock.Slots.Count; aliasedSlotNumber++) { SlotInfo slotInfo = childBlock.Slots[aliasedSlotNumber]; aliasedSlotsForAddedByChildren.Add(new AliasedSlot(childBlock, slotInfo.SlotValue, slotInfo.MemberPath, aliasedSlotNumber)); } // if required, but not projected, add NULL SlotInfo[] paddedSlotInfo = new SlotInfo[childBlock.Slots.Count]; ReadOnlyCollection childSlotInfo = childBlock.Slots; 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 paddedSlotInfo[slotNum] = new SlotInfo(true /* is required */, true /* is projected */, new ConstantSlot(CellConstant.Null), childSlotInfo[slotNum].MemberPath); } } else { paddedSlotInfo[slotNum] = childSlotInfo[slotNum]; } } //Add the slots that were added by children //for (int slotNum = totalSlots; slotNum < childBlock.Slots.Count; slotNum++) //{ // paddedSlotInfo[slotNum] = childSlotInfo[slotNum]; //} childBlock.Slots = new ReadOnlyCollection (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 unformly for others( as nulls) //since this is a union operation if (aliasedSlotsForAddedByChildren.Count != 0) { foreach (CqlBlock childBlock in children) { SlotInfo[] childSlots = new SlotInfo[totalSlots + aliasedSlotsForAddedByChildren.Count]; childBlock.Slots.CopyTo(childSlots,0); int index = totalSlots; foreach (AliasedSlot aliasedSlot in aliasedSlotsForAddedByChildren) { if (aliasedSlot.Block.Equals(childBlock)) { childSlots[index] = new SlotInfo(true /* is required */, true /* is projected */, aliasedSlot.InnerSlot, aliasedSlot.MemberPath); } else { childSlots[index] = new SlotInfo(true /* is required */, true /* is projected */, new ConstantSlot(CellConstant.Null), aliasedSlot.MemberPath); } //move on to the next slot added by children. index++; } childBlock.Slots = new ReadOnlyCollection (childSlots); } } // Create the slotInfos and then Union CqlBlock SlotInfo[] slotInfos = new SlotInfo[totalSlots + aliasedSlotsForAddedByChildren.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++) { ProjectedSlot slot = firstChild.ProjectedSlot(slotNum); MemberPath memberPath = GetMemberPath(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]; SlotInfo slotInfo = new SlotInfo(isRequired, isRequired, slot, memberPath); slotInfos[slotNum] = slotInfo; } for (int i = 0, slotNum = totalSlots; slotNum < totalSlots + aliasedSlotsForAddedByChildren.Count; slotNum++, i++) { slotInfos[slotNum] = new SlotInfo(true, true, aliasedSlotsForAddedByChildren[i], aliasedSlotsForAddedByChildren[i].MemberPath); } CqlBlock block = new UnionCqlBlock(slotInfos, children, identifiers, ++blockAliasNum); return block; } private static void AndWith(bool[] boolArray, bool[] another) { Debug.Assert(boolArray.Length == another.Length); for (int i = 0; i < boolArray.Length; i++) { boolArray[i] &= another[i]; } } #endregion #region Join CqlBLock Methods // 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 withStatements) { int totalSlots = requiredSlots.Length; Debug.Assert(OpType == CellTreeOpType.IJ || OpType == CellTreeOpType.LOJ || OpType == CellTreeOpType.FOJ, "Only these join operations handled"); List children = new List (); List aliasedSlotsForAddedByChildren = new List (); // 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); //List subAddedSlotsByChild = new List (); CqlBlock childBlock = child.ToCqlBlock(childProjectedSlots, identifiers, ref blockAliasNum, ref withStatements); //addedSlotsByChild.AddRange(subAddedSlotsByChild); children.Add(childBlock); //foreach (SlotInfo slotInfo in subAddedSlotsByChild) for(int aliasedSlotNumber = childProjectedSlots.Length; aliasedSlotNumber < childBlock.Slots.Count;aliasedSlotNumber++) { SlotInfo slotInfo = childBlock.Slots[aliasedSlotNumber]; aliasedSlotsForAddedByChildren.Add(new AliasedSlot(childBlock, slotInfo.SlotValue, slotInfo.MemberPath, aliasedSlotNumber)); } 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 + aliasedSlotsForAddedByChildren.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 + aliasedSlotsForAddedByChildren.Count; slotNum++, i++) { slotInfos[slotNum] = new SlotInfo(true, true, aliasedSlotsForAddedByChildren[i], aliasedSlotsForAddedByChildren[i].MemberPath); } // Generate the ON conditions: For each child, generate an ON // clause with the 0th child on the key fields List onClauses = new List (); for (int i = 1; i < children.Count; i++) { CqlBlock child = children[i]; JoinCqlBlock.OnClause onClause = new JoinCqlBlock.OnClause(); foreach (int keySlotNum in KeySlots) { SlotInfo slotInfo = slotInfos[keySlotNum]; Debug.Assert(child.IsProjected(keySlotNum), "Key is not in child"); Debug.Assert(children[0].IsProjected(keySlotNum), "Key is not in 0th child"); AliasedSlot first = new AliasedSlot(children[0], slotInfo.SlotValue, slotInfo.MemberPath, keySlotNum); AliasedSlot second = new AliasedSlot(child, slotInfo.SlotValue, slotInfo.MemberPath, keySlotNum); onClause.Add(first, second); } onClauses.Add(onClause); } CqlBlock result = new JoinCqlBlock(OpType, slotInfos, children, onClauses, identifiers, ++blockAliasNum); return result; } // 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 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 an aliased 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 CaseStatementSlot(caseForOuterJoins, null); } else if (childDefiningSlot >= 0) { slot = new AliasedSlot(children[childDefiningSlot], children[childDefiningSlot].ProjectedSlot(slotNum), memberPath, 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 ConstantSlot(CellConstantDomain.GetDefaultValueForMemberPath(memberPath, GetLeaves(), CellNormalizer.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. 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; } // requires: children to be a list of nodes that are children of an // Inner Join node. slotNum does not correspond to the key slot // effects: Determines the child number from which the slot should be // picked up. private static int GetInnerJoinChildForSlot(List children, int slotNum) { // Picks the child with the non-constant slot first. If none, picks a non-null constant slot. // If not een that, picks any one int result = -1; for (int i = 0; i < children.Count; i++) { CqlBlock child = children[i]; if (false == child.IsProjected(slotNum)) { continue; } ProjectedSlot slot = child.ProjectedSlot(slotNum); ConstantSlot constantSlot = slot as ConstantSlot; JoinTreeSlot joinSlot = slot as JoinTreeSlot; if (joinSlot != null) { // Pick the non-constant slot result = i; } else if (constantSlot != null && constantSlot.CellConstant.IsNull()) { if (result == -1) { // In case, all are null result = i; } } else { // Just pick anything result = i; } } return result; } // 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.ProjectedSlot(slotNum); ConstantSlot constantSlot = childSlot as ConstantSlot; 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)); } } // Make an aliased slot corresponding to child[slotNum] for the THEN MemberPath outputMember = GetMemberPath(slotNum); AliasedSlot slot = new AliasedSlot(child, childSlot, outputMember, slotNum); caseForOuterJoins.AddWhenThen(originBool, slot); } #endregion #region String methods // effects: Given an optype, returns a SQL-acceptable string // corresponding to the op internal static string OpToCql(CellTreeOpType opType) { switch (opType) { case CellTreeOpType.FOJ: return "FULL OUTER JOIN"; case CellTreeOpType.IJ: return "INNER JOIN"; //case CellTreeOpType.LASJ: return "LEFT ANTISEMIJOIN"; case CellTreeOpType.LOJ: return "LEFT OUTER JOIN"; case CellTreeOpType.Union: return "UNION ALL"; } Debug.Fail("Unknown operator"); return null; } internal override void ToCompactString(StringBuilder stringBuilder) { // Debug.Assert(m_children.Count > 1, "Tree not flattened?"); stringBuilder.Append("("); for (int i = 0; i < m_children.Count; i++) { CellTreeNode child = m_children[i]; child.ToCompactString(stringBuilder); if (i != m_children.Count - 1) { StringUtil.FormatStringBuilder(stringBuilder, " {0} ", OpType); } } stringBuilder.Append(")"); } #endregion } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- CommonObjectSecurity.cs
- MetabaseServerConfig.cs
- ButtonField.cs
- DataSetFieldSchema.cs
- WSSecurityOneDotZeroSendSecurityHeader.cs
- Base64Stream.cs
- MessageQueue.cs
- ExpressionConverter.cs
- AuthenticationSchemesHelper.cs
- InkPresenterAutomationPeer.cs
- DispatcherHooks.cs
- CellCreator.cs
- IxmlLineInfo.cs
- TypeConverterAttribute.cs
- Renderer.cs
- MultiSelector.cs
- DeviceContexts.cs
- SimpleApplicationHost.cs
- FormsAuthenticationModule.cs
- CompleteWizardStep.cs
- SettingsAttributes.cs
- EnumUnknown.cs
- QueryProcessor.cs
- WindowsPrincipal.cs
- RowParagraph.cs
- MappingItemCollection.cs
- CodeDomSerializationProvider.cs
- MenuCommandsChangedEventArgs.cs
- SiteOfOriginPart.cs
- DbDataRecord.cs
- ExtensionElementCollection.cs
- TextTrailingCharacterEllipsis.cs
- Part.cs
- EndpointAddressMessageFilter.cs
- HttpProfileBase.cs
- CustomUserNameSecurityTokenAuthenticator.cs
- ExpandButtonVisibilityConverter.cs
- HttpException.cs
- SQlBooleanStorage.cs
- SemanticValue.cs
- DataSourceGeneratorException.cs
- IndexedGlyphRun.cs
- SlotInfo.cs
- GridErrorDlg.cs
- TraceEventCache.cs
- TemplateBuilder.cs
- Figure.cs
- UpWmlPageAdapter.cs
- WebPartZoneBase.cs
- CodeCastExpression.cs
- wmiprovider.cs
- LinkUtilities.cs
- Collection.cs
- FileCodeGroup.cs
- SqlProfileProvider.cs
- TypedAsyncResult.cs
- ToolStripActionList.cs
- Intellisense.cs
- PerformanceCounterCategory.cs
- FileAuthorizationModule.cs
- IsolatedStorageFileStream.cs
- DataGridLinkButton.cs
- BitmapInitialize.cs
- XamlReader.cs
- DataFormats.cs
- SystemWebSectionGroup.cs
- HttpVersion.cs
- SqlTriggerContext.cs
- Resources.Designer.cs
- VerificationException.cs
- DataRowExtensions.cs
- WindowsListView.cs
- XmlSchemaChoice.cs
- RandomNumberGenerator.cs
- EditorPartChrome.cs
- ObjectStateFormatter.cs
- EntityRecordInfo.cs
- MultiAsyncResult.cs
- MULTI_QI.cs
- Subordinate.cs
- WorkflowOperationAsyncResult.cs
- TextSelection.cs
- CodeExpressionCollection.cs
- DefaultPrintController.cs
- EdmTypeAttribute.cs
- TcpProcessProtocolHandler.cs
- DataListItemCollection.cs
- __ComObject.cs
- CollectionsUtil.cs
- SqlConnection.cs
- ErrorProvider.cs
- XmlNodeReader.cs
- IisNotInstalledException.cs
- DivideByZeroException.cs
- UserControl.cs
- _HelperAsyncResults.cs
- Fx.cs
- Context.cs
- TabletCollection.cs
- RoleGroup.cs