private bool TryMergeCellQueries( CellTreeOpType opType, ref CellTreeNode node1, CellTreeNode node2) { LeafCellTreeNode leafCellTreeNode1 = node1 as LeafCellTreeNode; LeafCellTreeNode leafCellTreeNode2 = node2 as LeafCellTreeNode; CellQuery mergedQuery1; CellQuery mergedQuery2; if (!CellTreeSimplifier.TryMergeTwoCellQueries(leafCellTreeNode1.LeftCellWrapper.RightCellQuery, leafCellTreeNode2.LeftCellWrapper.RightCellQuery, opType, out mergedQuery1) || !CellTreeSimplifier.TryMergeTwoCellQueries(leafCellTreeNode1.LeftCellWrapper.LeftCellQuery, leafCellTreeNode2.LeftCellWrapper.LeftCellQuery, opType, out mergedQuery2)) { return(false); } OpCellTreeNode opCellTreeNode = new OpCellTreeNode(this.m_viewgenContext, opType); opCellTreeNode.Add(node1); opCellTreeNode.Add(node2); if (opType != CellTreeOpType.FOJ) { ; } LeftCellWrapper cellWrapper = new LeftCellWrapper(this.m_viewgenContext.ViewTarget, opCellTreeNode.Attributes, opCellTreeNode.LeftFragmentQuery, mergedQuery2, mergedQuery1, this.m_viewgenContext.MemberMaps, leafCellTreeNode1.LeftCellWrapper.Cells.Concat <Cell>(leafCellTreeNode2.LeftCellWrapper.Cells)); node1 = (CellTreeNode) new LeafCellTreeNode(this.m_viewgenContext, cellWrapper, opCellTreeNode.RightFragmentQuery); return(true); }
private CellTreeNode IsolateUnions(CellTreeNode rootNode) { if (rootNode.Children.Count <= 1) { return(rootNode); } for (int index = 0; index < rootNode.Children.Count; ++index) { rootNode.Children[index] = this.IsolateUnions(rootNode.Children[index]); } OpCellTreeNode opCellTreeNode1 = new OpCellTreeNode(this.m_viewgenContext, CellTreeOpType.Union); ModifiableIteratorCollection <CellTreeNode> iteratorCollection = new ModifiableIteratorCollection <CellTreeNode>((IEnumerable <CellTreeNode>)rootNode.Children); while (!iteratorCollection.IsEmpty) { OpCellTreeNode opCellTreeNode2 = new OpCellTreeNode(this.m_viewgenContext, CellTreeOpType.FOJ); CellTreeNode child = iteratorCollection.RemoveOneElement(); opCellTreeNode2.Add(child); foreach (CellTreeNode element in iteratorCollection.Elements()) { if (!this.IsDisjoint((CellTreeNode)opCellTreeNode2, element)) { opCellTreeNode2.Add(element); iteratorCollection.RemoveCurrentOfIterator(); iteratorCollection.ResetIterator(); } } opCellTreeNode1.Add((CellTreeNode)opCellTreeNode2); } return(opCellTreeNode1.Flatten()); }
// requires: node1 and node2 are two children of the same parent // connected by opType // effects: Given two cell tree nodes, node1 and node2, runs the // TM/SP rule on them to merge them (if they belong to the same // extent). Returns true if the merge succeeds private bool TryMergeCellQueries( CellTreeOpType opType, ref CellTreeNode node1, CellTreeNode node2) { var leaf1 = node1 as LeafCellTreeNode; var leaf2 = node2 as LeafCellTreeNode; Debug.Assert(leaf1 != null, "Merge only possible on leaf nodes (1)"); Debug.Assert(leaf2 != null, "Merge only possible on leaf nodes (2)"); CellQuery mergedLeftCellQuery; CellQuery mergedRightCellQuery; if ( !TryMergeTwoCellQueries( leaf1.LeftCellWrapper.RightCellQuery, leaf2.LeftCellWrapper.RightCellQuery, opType, out mergedRightCellQuery)) { return(false); } if ( !TryMergeTwoCellQueries( leaf1.LeftCellWrapper.LeftCellQuery, leaf2.LeftCellWrapper.LeftCellQuery, opType, out mergedLeftCellQuery)) { return(false); } // Create a temporary node and add the two children // so that we can get the merged selectiondomains and attributes // Note that temp.SelectionDomain below determines the domain // based on the opType, e.g., for IJ, it intersects the // multiconstants of all the children var temp = new OpCellTreeNode(m_viewgenContext, opType); temp.Add(node1); temp.Add(node2); // Note: We are losing the original cell number information here and the line number information // But we will not raise any // We do not create CellExpressions with LOJ, FOJ - canBooleansOverlap is true for validation var inputOpType = opType; if (opType == CellTreeOpType.FOJ || opType == CellTreeOpType.LOJ) { inputOpType = CellTreeOpType.IJ; } var wrapper = new LeftCellWrapper( m_viewgenContext.ViewTarget, temp.Attributes, temp.LeftFragmentQuery, mergedLeftCellQuery, mergedRightCellQuery, m_viewgenContext.MemberMaps, leaf1.LeftCellWrapper.Cells.Concat(leaf2.LeftCellWrapper.Cells)); node1 = new LeafCellTreeNode(m_viewgenContext, wrapper, temp.RightFragmentQuery); return(true); }
internal override CellTreeNode VisitTerm(TermExpr <DomainConstraint <BoolLiteral, Constant> > expression) { var oneOf = (MemberRestriction)expression.Identifier.Variable.Identifier; var range = expression.Identifier.Range; // create a disjunction var disjunctionNode = new OpCellTreeNode(_viewgenContext, CellTreeOpType.Union); CellTreeNode singleNode = null; foreach (var value in range) { if (TryGetCellTreeNode(oneOf.RestrictedMemberSlot.MemberPath, value, out singleNode)) { disjunctionNode.Add(singleNode); } // else, there is no rewriting for this member value, i.e., it is empty } switch (disjunctionNode.Children.Count) { case 0: return(null); // empty rewriting case 1: return(singleNode); default: return(disjunctionNode); } }
internal override CellTreeNode VisitTerm( TermExpr <DomainConstraint <BoolLiteral, Constant> > expression) { MemberRestriction identifier = (MemberRestriction)expression.Identifier.Variable.Identifier; Set <Constant> range = expression.Identifier.Range; OpCellTreeNode opCellTreeNode = new OpCellTreeNode(this._viewgenContext, CellTreeOpType.Union); CellTreeNode singleNode = (CellTreeNode)null; foreach (Constant constant in range) { if (this.TryGetCellTreeNode(identifier.RestrictedMemberSlot.MemberPath, constant, out singleNode)) { opCellTreeNode.Add(singleNode); } } switch (opCellTreeNode.Children.Count) { case 0: return((CellTreeNode)null); case 1: return(singleNode); default: return((CellTreeNode)opCellTreeNode); } }
private CellTreeNode SimplifyTreeByMergingNodes(CellTreeNode rootNode) { if (rootNode is LeafCellTreeNode) { return(rootNode); } rootNode = this.RestructureTreeForMerges(rootNode); List <CellTreeNode> children = rootNode.Children; for (int index = 0; index < children.Count; ++index) { children[index] = this.SimplifyTreeByMergingNodes(children[index]); } bool flag1 = CellTreeNode.IsAssociativeOp(rootNode.OpType); List <CellTreeNode> cellTreeNodeList = !flag1?CellTreeSimplifier.GroupNonAssociativeLeafChildren(children) : CellTreeSimplifier.GroupLeafChildrenByExtent(children); OpCellTreeNode opCellTreeNode = new OpCellTreeNode(this.m_viewgenContext, rootNode.OpType); CellTreeNode node1 = (CellTreeNode)null; bool flag2 = false; foreach (CellTreeNode node2 in cellTreeNodeList) { if (node1 == null) { node1 = node2; } else { bool flag3 = false; if (!flag2 && node1.OpType == CellTreeOpType.Leaf && node2.OpType == CellTreeOpType.Leaf) { flag3 = this.TryMergeCellQueries(rootNode.OpType, ref node1, node2); } if (!flag3) { opCellTreeNode.Add(node1); node1 = node2; if (!flag1) { flag2 = true; } } } } opCellTreeNode.Add(node1); return(opCellTreeNode.AssociativeFlatten()); }
// effects: Determines if the childNode can be added as a child of the // groupNode using te operation "opTypeToIsolate". E.g., if // opTypeToIsolate is inner join, we can add child to group node if // childNode and groupNode have the same multiconstantsets, i.e., they have // the same selection condition // Modifies groupNode to contain groupNode at the appropriate // position (for LOJs, the child could be added to the beginning) private bool TryAddChildToGroup( CellTreeOpType opTypeToIsolate, CellTreeNode childNode, OpCellTreeNode groupNode) { switch (opTypeToIsolate) { case CellTreeOpType.IJ: // For Inner join, the constants of the node and // the child must be the same, i.e., if the cells // are producing exactly same tuples (same selection) if (IsEquivalentTo(childNode, groupNode)) { groupNode.Add(childNode); return(true); } break; case CellTreeOpType.LOJ: // If one cell's selection condition subsumes // another, we can use LOJ. We need to check for // "subsumes" on both sides if (IsContainedIn(childNode, groupNode)) { groupNode.Add(childNode); return(true); } else if (IsContainedIn(groupNode, childNode)) { // child subsumes the whole group -- add it first groupNode.AddFirst(childNode); return(true); } break; case CellTreeOpType.Union: // If the selection conditions are disjoint, we can use UNION ALL // We cannot use active domain here; disjointness is guaranteed only // if we check the entire selection domain if (IsDisjoint(childNode, groupNode)) { groupNode.Add(childNode); return(true); } break; } return(false); }
private bool TryAddChildToGroup( CellTreeOpType opTypeToIsolate, CellTreeNode childNode, OpCellTreeNode groupNode) { switch (opTypeToIsolate) { case CellTreeOpType.Union: if (this.IsDisjoint(childNode, (CellTreeNode)groupNode)) { groupNode.Add(childNode); return(true); } break; case CellTreeOpType.LOJ: if (this.IsContainedIn(childNode, (CellTreeNode)groupNode)) { groupNode.Add(childNode); return(true); } if (this.IsContainedIn((CellTreeNode)groupNode, childNode)) { groupNode.AddFirst(childNode); return(true); } break; case CellTreeOpType.IJ: if (this.IsEquivalentTo(childNode, (CellTreeNode)groupNode)) { groupNode.Add(childNode); return(true); } break; } return(false); }
internal CellTreeNode CreateViewExpression() { OpCellTreeNode opCellTreeNode = new OpCellTreeNode(this.m_viewgenContext, CellTreeOpType.FOJ); foreach (LeftCellWrapper usedCell in this.m_usedCells) { LeafCellTreeNode leafCellTreeNode = new LeafCellTreeNode(this.m_viewgenContext, usedCell); opCellTreeNode.Add((CellTreeNode)leafCellTreeNode); } CellTreeNode rootNode = this.IsolateByOperator(this.IsolateByOperator(this.IsolateByOperator(this.IsolateUnions(this.GroupByRightExtent((CellTreeNode)opCellTreeNode)), CellTreeOpType.Union), CellTreeOpType.IJ), CellTreeOpType.LOJ); if (this.m_viewgenContext.ViewTarget == ViewTarget.QueryView) { rootNode = this.ConvertUnionsToNormalizedLOJs(rootNode); } return(rootNode); }
internal override CellTreeNode VisitAnd(AndExpr <DomainConstraint <BoolLiteral, Constant> > expression) { var childrenTrees = AcceptChildren(expression.Children); var node = new OpCellTreeNode(_viewgenContext, CellTreeOpType.IJ); foreach (var childNode in childrenTrees) { if (childNode == null) { return(null); // unsatisfiable } if (childNode != _topLevelTree) { node.Add(childNode); } } return(node.Children.Count == 0 ? _topLevelTree : node); }
// according to case statements, where WHEN ... THEN was replaced by ELSE private Dictionary <MemberValueBinding, CellTreeNode> CreateMemberValueTrees(bool complementElse) { var memberValueTrees = new Dictionary <MemberValueBinding, CellTreeNode>(); foreach (var column in _domainMap.ConditionMembers(_viewgenContext.Extent)) { var domain = new List <Constant>(_domainMap.GetDomain(column)); // all domain members but the last var memberCover = new OpCellTreeNode(_viewgenContext, CellTreeOpType.Union); for (var i = 0; i < domain.Count; i++) { var domainValue = domain[i]; var memberValue = new MemberValueBinding(column, domainValue); var memberConditionQuery = QueryRewriter.CreateMemberConditionQuery(column, domainValue, _keyAttributes, _domainMap); Tile <FragmentQuery> rewriting; if (_viewgenContext.TryGetCachedRewriting(memberConditionQuery, out rewriting)) { // turn rewriting into a cell tree var cellTreeNode = QueryRewriter.TileToCellTree(rewriting, _viewgenContext); memberValueTrees[memberValue] = cellTreeNode; // collect a union of all domain constants but the last if (i < domain.Count - 1) { memberCover.Add(cellTreeNode); } } else { Debug.Fail(String.Format(CultureInfo.InvariantCulture, "No cached rewriting for {0}={1}", column, domainValue)); } } if (complementElse && domain.Count > 1) { var lastDomainValue = domain[domain.Count - 1]; var lastMemberValue = new MemberValueBinding(column, lastDomainValue); memberValueTrees[lastMemberValue] = new OpCellTreeNode(_viewgenContext, CellTreeOpType.LASJ, _basicView, memberCover); } } return(memberValueTrees); }
internal CellTreeNode IsolateByOperator( CellTreeNode rootNode, CellTreeOpType opTypeToIsolate) { List <CellTreeNode> children = rootNode.Children; if (children.Count <= 1) { return(rootNode); } for (int index = 0; index < children.Count; ++index) { children[index] = this.IsolateByOperator(children[index], opTypeToIsolate); } if (rootNode.OpType != CellTreeOpType.FOJ && rootNode.OpType != CellTreeOpType.LOJ || rootNode.OpType == opTypeToIsolate) { return(rootNode); } OpCellTreeNode opCellTreeNode = new OpCellTreeNode(this.m_viewgenContext, rootNode.OpType); ModifiableIteratorCollection <CellTreeNode> iteratorCollection = new ModifiableIteratorCollection <CellTreeNode>((IEnumerable <CellTreeNode>)children); while (!iteratorCollection.IsEmpty) { OpCellTreeNode groupNode = new OpCellTreeNode(this.m_viewgenContext, opTypeToIsolate); CellTreeNode child = iteratorCollection.RemoveOneElement(); groupNode.Add(child); foreach (CellTreeNode element in iteratorCollection.Elements()) { if (this.TryAddChildToGroup(opTypeToIsolate, element, groupNode)) { iteratorCollection.RemoveCurrentOfIterator(); if (opTypeToIsolate == CellTreeOpType.LOJ) { iteratorCollection.ResetIterator(); } } } opCellTreeNode.Add((CellTreeNode)groupNode); } return(opCellTreeNode.Flatten()); }
// requires: The tree rooted at cellTreeNode is an FOJ tree of // LeafCellTreeNodes only, i.e., there is an FOJ node with the // children being LeafCellTreeNodes // // effects: Given a tree rooted at rootNode, ensures that cells // of the same right extent are placed in their own subtree below // cellTreeNode. That is, if there are 3 cells of extent A and 2 of // extent B (i.e., 5 cells with an FOJ on it), the resulting tree has // an FOJ node with two children -- FOJ nodes. These FOJ nodes have 2 // and 3 children internal CellTreeNode GroupByRightExtent(CellTreeNode rootNode) { // A dictionary that maps an extent to the nodes are from that extent // We want a ref comparer here var extentMap = new KeyToListMap <EntitySetBase, LeafCellTreeNode>(EqualityComparer <EntitySetBase> .Default); // CR_Meek_Low: method can be simplified (Map<Extent, OpCellTreeNode>, populate as you go) // (becomes self-documenting) // For each leaf child, find the extent of the child and place it // in extentMap foreach (LeafCellTreeNode childNode in rootNode.Children) { // A cell may contain P, P.PA -- we return P // CHANGE_ADYA_FEATURE_COMPOSITION Need to fix for composition!! var extent = childNode.LeftCellWrapper.RightCellQuery.Extent; // relation or extent to group by Debug.Assert(extent != null, "Each cell must have a right extent"); // Add the childNode as a child of the FOJ tree for "extent" extentMap.Add(extent, childNode); } // Now go through the extent map and create FOJ nodes for each extent // Place the nodes for that extent in the newly-created FOJ subtree // Also add the op node for every node as a child of the final result var result = new OpCellTreeNode(m_viewgenContext, CellTreeOpType.FOJ); foreach (var extent in extentMap.Keys) { var extentFojNode = new OpCellTreeNode(m_viewgenContext, CellTreeOpType.FOJ); foreach (var childNode in extentMap.ListForKey(extent)) { extentFojNode.Add(childNode); } result.Add(extentFojNode); } // We call Flatten to remove any unnecessary nestings // where an OpNode has only 1 child. return(result.Flatten()); }
internal CellTreeNode GroupByRightExtent(CellTreeNode rootNode) { KeyToListMap <EntitySetBase, LeafCellTreeNode> keyToListMap = new KeyToListMap <EntitySetBase, LeafCellTreeNode>((IEqualityComparer <EntitySetBase>)EqualityComparer <EntitySetBase> .Default); foreach (LeafCellTreeNode child in rootNode.Children) { EntitySetBase extent = child.LeftCellWrapper.RightCellQuery.Extent; keyToListMap.Add(extent, child); } OpCellTreeNode opCellTreeNode1 = new OpCellTreeNode(this.m_viewgenContext, CellTreeOpType.FOJ); foreach (EntitySetBase key in keyToListMap.Keys) { OpCellTreeNode opCellTreeNode2 = new OpCellTreeNode(this.m_viewgenContext, CellTreeOpType.FOJ); foreach (LeafCellTreeNode leafCellTreeNode in keyToListMap.ListForKey(key)) { opCellTreeNode2.Add((CellTreeNode)leafCellTreeNode); } opCellTreeNode1.Add((CellTreeNode)opCellTreeNode2); } return(opCellTreeNode1.Flatten()); }
internal override CellTreeNode VisitAnd( AndExpr <DomainConstraint <BoolLiteral, Constant> > expression) { IEnumerable <CellTreeNode> cellTreeNodes = this.AcceptChildren((IEnumerable <BoolExpr <DomainConstraint <BoolLiteral, Constant> > >)expression.Children); OpCellTreeNode opCellTreeNode = new OpCellTreeNode(this._viewgenContext, CellTreeOpType.IJ); foreach (CellTreeNode child in cellTreeNodes) { if (child == null) { return((CellTreeNode)null); } if (child != this._topLevelTree) { opCellTreeNode.Add(child); } } if (opCellTreeNode.Children.Count != 0) { return((CellTreeNode)opCellTreeNode); } return(this._topLevelTree); }
// effects: Given the set of used cells for an extent, returns a // view to generate that extent internal CellTreeNode CreateViewExpression() { // Create an initial FOJ group with all the used cells as children var fojNode = new OpCellTreeNode(m_viewgenContext, CellTreeOpType.FOJ); // Add all the used cells as children to fojNode. This is a valid // view for the extent. We later try to optimize it foreach (var cell in m_usedCells) { var cellNode = new LeafCellTreeNode(m_viewgenContext, cell); fojNode.Add(cellNode); } //rootNode = GroupByNesting(rootNode); // Group cells by the "right" extent (recall that we are // generating the view for the left extent) so that cells of the // same extent are in the same subtree var rootNode = GroupByRightExtent(fojNode); // Change some of the FOJs to Unions, IJs and LOJs rootNode = IsolateUnions(rootNode); // The isolation with Union is different from IsolateUnions -- // the above isolation finds collections of chidren in a // node and connects them by union. The below one only considers // two children at a time rootNode = IsolateByOperator(rootNode, CellTreeOpType.Union); rootNode = IsolateByOperator(rootNode, CellTreeOpType.IJ); rootNode = IsolateByOperator(rootNode, CellTreeOpType.LOJ); if (m_viewgenContext.ViewTarget == ViewTarget.QueryView) { rootNode = ConvertUnionsToNormalizedLOJs(rootNode); } return rootNode; }
private Dictionary <RewritingValidator.MemberValueBinding, CellTreeNode> CreateMemberValueTrees( bool complementElse) { Dictionary <RewritingValidator.MemberValueBinding, CellTreeNode> dictionary = new Dictionary <RewritingValidator.MemberValueBinding, CellTreeNode>(); foreach (MemberPath conditionMember in this._domainMap.ConditionMembers(this._viewgenContext.Extent)) { List <Constant> constantList = new List <Constant>(this._domainMap.GetDomain(conditionMember)); OpCellTreeNode opCellTreeNode = new OpCellTreeNode(this._viewgenContext, CellTreeOpType.Union); for (int index1 = 0; index1 < constantList.Count; ++index1) { Constant domainValue = constantList[index1]; RewritingValidator.MemberValueBinding index2 = new RewritingValidator.MemberValueBinding(conditionMember, domainValue); Tile <FragmentQuery> rewriting; if (this._viewgenContext.TryGetCachedRewriting(QueryRewriter.CreateMemberConditionQuery(conditionMember, domainValue, this._keyAttributes, this._domainMap), out rewriting)) { CellTreeNode cellTree = QueryRewriter.TileToCellTree(rewriting, this._viewgenContext); dictionary[index2] = cellTree; if (index1 < constantList.Count - 1) { opCellTreeNode.Add(cellTree); } } } if (complementElse && constantList.Count > 1) { Constant constant = constantList[constantList.Count - 1]; RewritingValidator.MemberValueBinding index = new RewritingValidator.MemberValueBinding(conditionMember, constant); dictionary[index] = (CellTreeNode) new OpCellTreeNode(this._viewgenContext, CellTreeOpType.LASJ, new CellTreeNode[2] { this._basicView, (CellTreeNode)opCellTreeNode }); } } return(dictionary); }
// effects: Given the set of used cells for an extent, returns a // view to generate that extent internal CellTreeNode CreateViewExpression() { // Create an initial FOJ group with all the used cells as children var fojNode = new OpCellTreeNode(m_viewgenContext, CellTreeOpType.FOJ); // Add all the used cells as children to fojNode. This is a valid // view for the extent. We later try to optimize it foreach (var cell in m_usedCells) { var cellNode = new LeafCellTreeNode(m_viewgenContext, cell); fojNode.Add(cellNode); } //rootNode = GroupByNesting(rootNode); // Group cells by the "right" extent (recall that we are // generating the view for the left extent) so that cells of the // same extent are in the same subtree var rootNode = GroupByRightExtent(fojNode); // Change some of the FOJs to Unions, IJs and LOJs rootNode = IsolateUnions(rootNode); // The isolation with Union is different from IsolateUnions -- // the above isolation finds collections of chidren in a // node and connects them by union. The below one only considers // two children at a time rootNode = IsolateByOperator(rootNode, CellTreeOpType.Union); rootNode = IsolateByOperator(rootNode, CellTreeOpType.IJ); rootNode = IsolateByOperator(rootNode, CellTreeOpType.LOJ); if (m_viewgenContext.ViewTarget == ViewTarget.QueryView) { rootNode = ConvertUnionsToNormalizedLOJs(rootNode); } return(rootNode); }
// requires: cellTreeNode has a tree such that all its intermediate nodes // are FOJ nodes only // effects: Converts the tree rooted at rootNode (recursively) in // following way and returns a new rootNode -- it partitions // rootNode's children such that no two different partitions have // any overlapping constants. These partitions are connected by Union // nodes (since there is no overlapping). // Note: Method may modify rootNode's contents and children private CellTreeNode IsolateUnions(CellTreeNode rootNode) { if (rootNode.Children.Count <= 1) { // No partitioning of children needs to be done return(rootNode); } Debug.Assert(rootNode.OpType == CellTreeOpType.FOJ, "So far, we have FOJs only"); // Recursively, transform the subtrees rooted at cellTreeNode's children for (var i = 0; i < rootNode.Children.Count; i++) { // Method modifies input as well rootNode.Children[i] = IsolateUnions(rootNode.Children[i]); } // Different children groups are connected by a Union // node -- the secltion domain of one group is disjoint from // another group's selection domain, i.e., group A1 contributes // tuples to the extent which are disjoint from the tuples by // A2. So we can connect these groups by union alls. // Inside each group, we continue to connect children of the same // group using FOJ var unionNode = new OpCellTreeNode(m_viewgenContext, CellTreeOpType.Union); // childrenSet keeps track of the children that need to be procesed/partitioned var childrenSet = new ModifiableIteratorCollection <CellTreeNode>(rootNode.Children); while (false == childrenSet.IsEmpty) { // Start a new group // Make an FOJ node to connect children of the same group var fojNode = new OpCellTreeNode(m_viewgenContext, CellTreeOpType.FOJ); // Add one of the root's children as a child to the foj node var someChild = childrenSet.RemoveOneElement(); fojNode.Add(someChild); // We now want a transitive closure of the overlap between the // the children node. We keep checking each child with the // fojNode and add it as a child of fojNode if there is an // overlap. Note that when a node is added to the fojNode, // its constants are propagated to the fojNode -- so we do // get transitive closure in terms of intersection foreach (var child in childrenSet.Elements()) { if (!IsDisjoint(fojNode, child)) { fojNode.Add(child); childrenSet.RemoveCurrentOfIterator(); // To ensure that we get all overlapping node, we // need to restart checking all the children childrenSet.ResetIterator(); } } // Now we have a group of children nodes rooted at // fojNode. Add this fojNode to the union unionNode.Add(fojNode); } // The union node as the root of the view var result = unionNode.Flatten(); return(result); }
// effects: Determines if the childNode can be added as a child of the // groupNode using te operation "opTypeToIsolate". E.g., if // opTypeToIsolate is inner join, we can add child to group node if // childNode and groupNode have the same multiconstantsets, i.e., they have // the same selection condition // Modifies groupNode to contain groupNode at the appropriate // position (for LOJs, the child could be added to the beginning) private bool TryAddChildToGroup( CellTreeOpType opTypeToIsolate, CellTreeNode childNode, OpCellTreeNode groupNode) { switch (opTypeToIsolate) { case CellTreeOpType.IJ: // For Inner join, the constants of the node and // the child must be the same, i.e., if the cells // are producing exactly same tuples (same selection) if (IsEquivalentTo(childNode, groupNode)) { groupNode.Add(childNode); return true; } break; case CellTreeOpType.LOJ: // If one cell's selection condition subsumes // another, we can use LOJ. We need to check for // "subsumes" on both sides if (IsContainedIn(childNode, groupNode)) { groupNode.Add(childNode); return true; } else if (IsContainedIn(groupNode, childNode)) { // child subsumes the whole group -- add it first groupNode.AddFirst(childNode); return true; } break; case CellTreeOpType.Union: // If the selection conditions are disjoint, we can use UNION ALL // We cannot use active domain here; disjointness is guaranteed only // if we check the entire selection domain if (IsDisjoint(childNode, groupNode)) { groupNode.Add(childNode); return true; } break; } return false; }
// requires: opTypeToIsolate must be LOJ, IJ, or Union // effects: Given a tree rooted at rootNode, determines if there // are any FOJs that can be replaced by opTypeToIsolate. If so, // does that and a returns a new tree with the replaced operators // Note: Method may modify rootNode's contents and children internal CellTreeNode IsolateByOperator(CellTreeNode rootNode, CellTreeOpType opTypeToIsolate) { Debug.Assert( opTypeToIsolate == CellTreeOpType.IJ || opTypeToIsolate == CellTreeOpType.LOJ || opTypeToIsolate == CellTreeOpType.Union, "IsolateJoins can only be called for IJs, LOJs, and Unions"); var children = rootNode.Children; if (children.Count <= 1) { // No child or one child - do nothing return rootNode; } // Replace the FOJs with IJs/LOJs/Unions in the children's subtrees first for (var i = 0; i < children.Count; i++) { // Method modifies input as well children[i] = IsolateByOperator(children[i], opTypeToIsolate); } // Only FOJs and LOJs can be coverted (to IJs, Unions, LOJs) -- // so if the node is not that, we can ignore it (or if the node is already of // the same type that we want) if (rootNode.OpType != CellTreeOpType.FOJ && rootNode.OpType != CellTreeOpType.LOJ || rootNode.OpType == opTypeToIsolate) { return rootNode; } // Create a new node with the same type as the input cell node type var newRootNode = new OpCellTreeNode(m_viewgenContext, rootNode.OpType); // We start a new "group" with one of the children X - we create // a newChildNode with type "opTypeToIsolate". Then we // determine if any of the remaining children should be in the // same group as X. // childrenSet keeps track of the children that need to be procesed/partitioned var childrenSet = new ModifiableIteratorCollection<CellTreeNode>(children); // Find groups with same or subsumed constants and create a join // or union node for them. We do this so that some of the FOJs // can be replaced by union and join nodes // while (false == childrenSet.IsEmpty) { // Start a new "group" with some child node (for the opTypeToIsolate node type) var groupNode = new OpCellTreeNode(m_viewgenContext, opTypeToIsolate); var someChild = childrenSet.RemoveOneElement(); groupNode.Add(someChild); // Go through the remaining children and determine if their // constants are subsets/equal/disjoint w.r.t the joinNode // constants. foreach (var child in childrenSet.Elements()) { // Check if we can add the child as part of this // groupNode (with opTypeToIsolate being LOJ, IJ, or Union) if (TryAddChildToGroup(opTypeToIsolate, child, groupNode)) { childrenSet.RemoveCurrentOfIterator(); // For LOJ, suppose that child A did not subsume B or // vice-versa. But child C subsumes both. To ensure // that we can get A, B, C in the same group, we // reset the iterator so that when C is added in B's // loop, we can reconsider A. // // For IJ, adding a child to groupNode does not change the range of it, // so there is no need to reconsider previously skipped children. // // For Union, adding a child to groupNode increases the range of the groupNode, // hence previously skipped (because they weren't disjoint with groupNode) children will continue // being ignored because they would still have an overlap with one of the nodes inside groupNode. if (opTypeToIsolate == CellTreeOpType.LOJ) { childrenSet.ResetIterator(); } } } // The new Union/LOJ/IJ node needs to be connected to the root newRootNode.Add(groupNode); } return newRootNode.Flatten(); }
// requires: The tree rooted at cellTreeNode is an FOJ tree of // LeafCellTreeNodes only, i.e., there is an FOJ node with the // children being LeafCellTreeNodes // // effects: Given a tree rooted at rootNode, ensures that cells // of the same right extent are placed in their own subtree below // cellTreeNode. That is, if there are 3 cells of extent A and 2 of // extent B (i.e., 5 cells with an FOJ on it), the resulting tree has // an FOJ node with two children -- FOJ nodes. These FOJ nodes have 2 // and 3 children internal CellTreeNode GroupByRightExtent(CellTreeNode rootNode) { // A dictionary that maps an extent to the nodes are from that extent // We want a ref comparer here var extentMap = new KeyToListMap<EntitySetBase, LeafCellTreeNode>(EqualityComparer<EntitySetBase>.Default); // CR_Meek_Low: method can be simplified (Map<Extent, OpCellTreeNode>, populate as you go) // (becomes self-documenting) // For each leaf child, find the extent of the child and place it // in extentMap foreach (LeafCellTreeNode childNode in rootNode.Children) { // A cell may contain P, P.PA -- we return P // CHANGE_ADYA_FEATURE_COMPOSITION Need to fix for composition!! var extent = childNode.LeftCellWrapper.RightCellQuery.Extent; // relation or extent to group by Debug.Assert(extent != null, "Each cell must have a right extent"); // Add the childNode as a child of the FOJ tree for "extent" extentMap.Add(extent, childNode); } // Now go through the extent map and create FOJ nodes for each extent // Place the nodes for that extent in the newly-created FOJ subtree // Also add the op node for every node as a child of the final result var result = new OpCellTreeNode(m_viewgenContext, CellTreeOpType.FOJ); foreach (var extent in extentMap.Keys) { var extentFojNode = new OpCellTreeNode(m_viewgenContext, CellTreeOpType.FOJ); foreach (var childNode in extentMap.ListForKey(extent)) { extentFojNode.Add(childNode); } result.Add(extentFojNode); } // We call Flatten to remove any unnecessary nestings // where an OpNode has only 1 child. return result.Flatten(); }
// effects: Simplifies the tree rooted at rootNode and returns a new // tree -- it ensures that the returned tree has at most one node for // any particular extent unless the tree has nodes of the same extent // embedded two leaves below LASJ or LOJ, e.g., if we have a tree // (where Ni indicates a node for extent i - one Ni can be different // from anohter Ni: // [N0 IJ N1] LASJ N0 --> This will not be simplified // canBooleansOverlap indicates whether an original input cell // contributes to multiple nodes in this tree, e.g., V1 IJ V2 UNION V2 IJ V3 private CellTreeNode SimplifyTreeByMergingNodes(CellTreeNode rootNode) { if (rootNode is LeafCellTreeNode) { // View already simple! return(rootNode); } Debug.Assert( rootNode.OpType == CellTreeOpType.LOJ || rootNode.OpType == CellTreeOpType.IJ || rootNode.OpType == CellTreeOpType.FOJ || rootNode.OpType == CellTreeOpType.Union || rootNode.OpType == CellTreeOpType.LASJ, "Only handle these operations"); // Before we apply any rule, check if we can improve the opportunity to // collapse the nodes rootNode = RestructureTreeForMerges(rootNode); var children = rootNode.Children; Debug.Assert(children.Count > 0, "OpCellTreeNode has no children?"); // Apply recursively for (var i = 0; i < children.Count; i++) { children[i] = SimplifyTreeByMergingNodes(children[i]); } // Essentially, we have a node with IJ, LOJ, U or FOJ type that // has some children. Check if some of the children can be merged // with one another using the corresponding TM/SP rule // Ops such as IJ, Union and FOJ are associative, i.e., A op (B // op C) is the same as (A op B) op C. This is not true for LOJ // and LASJ var isAssociativeOp = CellTreeNode.IsAssociativeOp(rootNode.OpType); if (isAssociativeOp) { // Group all the leaf cells of an extent together so that we can // later simply run through them without running nested loops // We do not do this for LOJ/LASJ nodes since LOJ (LASJ) is not commutative // (or associative); children = GroupLeafChildrenByExtent(children); } else { children = GroupNonAssociativeLeafChildren(children); } // childrenSet keeps track of the children that need to be procesed/partitioned var newNode = new OpCellTreeNode(m_viewgenContext, rootNode.OpType); CellTreeNode lastChild = null; var skipRest = false; foreach (var child in children) { if (lastChild == null) { // First time in the loop. Just set lastChild lastChild = child; continue; } var mergedOk = false; // try to merge lastChild and child if (false == skipRest && lastChild.OpType == CellTreeOpType.Leaf && child.OpType == CellTreeOpType.Leaf) { // Both are cell queries. Can try to merge them // We do not add lastChild since it could merge // further. It will be added in a later loop or outside the loop mergedOk = TryMergeCellQueries(rootNode.OpType, ref lastChild, child); } if (false == mergedOk) { // No merge occurred. Simply add the previous child as it // is (Note lastChild will be added in the next loop or if // the loop finishes, outside the loop newNode.Add(lastChild); lastChild = child; if (false == isAssociativeOp) { // LOJ is not associative: // (P loj PA) loj PO != P loj (PA loj PO). The RHS does not have // Persons who have orders but no addresses skipRest = true; } } } newNode.Add(lastChild); var result = newNode.AssociativeFlatten(); return(result); }
// effects: Simplifies the tree rooted at rootNode and returns a new // tree -- it ensures that the returned tree has at most one node for // any particular extent unless the tree has nodes of the same extent // embedded two leaves below LASJ or LOJ, e.g., if we have a tree // (where Ni indicates a node for extent i - one Ni can be different // from anohter Ni: // [N0 IJ N1] LASJ N0 --> This will not be simplified // canBooleansOverlap indicates whether an original input cell // contributes to multiple nodes in this tree, e.g., V1 IJ V2 UNION V2 IJ V3 private CellTreeNode SimplifyTreeByMergingNodes(CellTreeNode rootNode) { if (rootNode is LeafCellTreeNode) { // View already simple! return rootNode; } Debug.Assert(rootNode.OpType == CellTreeOpType.LOJ || rootNode.OpType == CellTreeOpType.IJ || rootNode.OpType == CellTreeOpType.FOJ || rootNode.OpType == CellTreeOpType.Union || rootNode.OpType == CellTreeOpType.LASJ, "Only handle these operations"); // Before we apply any rule, check if we can improve the opportunity to // collapse the nodes rootNode = RestructureTreeForMerges(rootNode); List<CellTreeNode> children = rootNode.Children; Debug.Assert(children.Count > 0, "OpCellTreeNode has no children?"); // Apply recursively for (int i = 0; i < children.Count; i++) { children[i] = SimplifyTreeByMergingNodes(children[i]); } // Essentially, we have a node with IJ, LOJ, U or FOJ type that // has some children. Check if some of the children can be merged // with one another using the corresponding TM/SP rule // Ops such as IJ, Union and FOJ are associative, i.e., A op (B // op C) is the same as (A op B) op C. This is not true for LOJ // and LASJ bool isAssociativeOp = CellTreeNode.IsAssociativeOp(rootNode.OpType); if (isAssociativeOp) { // Group all the leaf cells of an extent together so that we can // later simply run through them without running nested loops // We do not do this for LOJ/LASJ nodes since LOJ (LASJ) is not commutative // (or associative); children = GroupLeafChildrenByExtent(children); } else { children = GroupNonAssociativeLeafChildren(children); } // childrenSet keeps track of the children that need to be procesed/partitioned OpCellTreeNode newNode = new OpCellTreeNode(m_viewgenContext, rootNode.OpType); CellTreeNode lastChild = null; bool skipRest = false; foreach (CellTreeNode child in children) { if (lastChild == null) { // First time in the loop. Just set lastChild lastChild = child; continue; } bool mergedOk = false; // try to merge lastChild and child if (false == skipRest && lastChild.OpType == CellTreeOpType.Leaf && child.OpType == CellTreeOpType.Leaf) { // Both are cell queries. Can try to merge them // We do not add lastChild since it could merge // further. It will be added in a later loop or outside the loop mergedOk = TryMergeCellQueries(rootNode.OpType, ref lastChild, child); } if (false == mergedOk) { // No merge occurred. Simply add the previous child as it // is (Note lastChild will be added in the next loop or if // the loop finishes, outside the loop newNode.Add(lastChild); lastChild = child; if (false == isAssociativeOp) { // LOJ is not associative: // (P loj PA) loj PO != P loj (PA loj PO). The RHS does not have // Persons who have orders but no addresses skipRest = true; } } } newNode.Add(lastChild); CellTreeNode result = newNode.AssociativeFlatten(); return result; }
// requires: node1 and node2 are two children of the same parent // connected by opType // effects: Given two cell tree nodes, node1 and node2, runs the // TM/SP rule on them to merge them (if they belong to the same // extent). Returns true if the merge succeeds private bool TryMergeCellQueries(CellTreeOpType opType, ref CellTreeNode node1, CellTreeNode node2) { LeafCellTreeNode leaf1 = node1 as LeafCellTreeNode; LeafCellTreeNode leaf2 = node2 as LeafCellTreeNode; Debug.Assert(leaf1 != null, "Merge only possible on leaf nodes (1)"); Debug.Assert(leaf2 != null, "Merge only possible on leaf nodes (2)"); CellQuery mergedLeftCellQuery; CellQuery mergedRightCellQuery; if (!TryMergeTwoCellQueries(leaf1.LeftCellWrapper.RightCellQuery, leaf2.LeftCellWrapper.RightCellQuery, opType, m_viewgenContext.MemberMaps.RightDomainMap, out mergedRightCellQuery)) { return false; } if (!TryMergeTwoCellQueries(leaf1.LeftCellWrapper.LeftCellQuery, leaf2.LeftCellWrapper.LeftCellQuery, opType, m_viewgenContext.MemberMaps.LeftDomainMap, out mergedLeftCellQuery)) { return false; } // Create a temporary node and add the two children // so that we can get the merged selectiondomains and attributes // Note that temp.SelectionDomain below determines the domain // based on the opType, e.g., for IJ, it intersects the // multiconstants of all the children OpCellTreeNode temp = new OpCellTreeNode(m_viewgenContext, opType); temp.Add(node1); temp.Add(node2); // Note: We are losing the original cell number information here and the line number information // But we will not raise any // We do not create CellExpressions with LOJ, FOJ - canBooleansOverlap is true for validation CellTreeOpType inputOpType = opType; if (opType == CellTreeOpType.FOJ || opType == CellTreeOpType.LOJ) { inputOpType = CellTreeOpType.IJ; } LeftCellWrapper wrapper = new LeftCellWrapper(m_viewgenContext.ViewTarget, temp.Attributes, temp.LeftFragmentQuery, mergedLeftCellQuery, mergedRightCellQuery, m_viewgenContext.MemberMaps, leaf1.LeftCellWrapper.Cells.Concat(leaf2.LeftCellWrapper.Cells)); node1 = new LeafCellTreeNode(m_viewgenContext, wrapper, temp.RightFragmentQuery); return true; }
private CellTreeNode ConvertUnionsToNormalizedLOJs(CellTreeNode rootNode) { // Recursively, transform the subtrees rooted at rootNode's children. for (var i = 0; i < rootNode.Children.Count; i++) { // Method modifies input as well. rootNode.Children[i] = ConvertUnionsToNormalizedLOJs(rootNode.Children[i]); } // We rewrite only LOJs. if (rootNode.OpType != CellTreeOpType.LOJ || rootNode.Children.Count < 2) { return(rootNode); } // Create the resulting LOJ node. var result = new OpCellTreeNode(m_viewgenContext, rootNode.OpType); // Create working collection for the LOJ children. var children = new List <CellTreeNode>(); // If rootNode looks something like ((V0 IJ V1) LOJ V2 LOJ V3), // and it turns out that there are FK associations from V2 or V3 pointing, let's say at V0, // then we want to rewrite the result as (V1 IJ (V0 LOJ V2 LOJ V3)). // If we don't do this, then plan compiler won't have a chance to eliminate LOJ V2 LOJ V3. // Hence, flatten the first child or rootNode if it's IJ, but remember that its parts are driving nodes for the LOJ, // so that we don't accidentally nest them. OpCellTreeNode resultIJDriver = null; HashSet <CellTreeNode> resultIJDriverChildren = null; if (rootNode.Children[0].OpType == CellTreeOpType.IJ) { // Create empty resultIJDriver node and add it as the first child (driving) into the LOJ result. resultIJDriver = new OpCellTreeNode(m_viewgenContext, rootNode.Children[0].OpType); result.Add(resultIJDriver); children.AddRange(rootNode.Children[0].Children); resultIJDriverChildren = new HashSet <CellTreeNode>(rootNode.Children[0].Children); } else { result.Add(rootNode.Children[0]); } // Flatten unions in non-driving nodes: (V0 LOJ (V1 Union V2 Union V3)) -> (V0 LOJ V1 LOJ V2 LOJ V3) foreach (var child in rootNode.Children.Skip(1)) { var opNode = child as OpCellTreeNode; if (opNode != null && opNode.OpType == CellTreeOpType.Union) { children.AddRange(opNode.Children); } else { children.Add(child); } } // A dictionary that maps an extent to the nodes that are from that extent. // We want a ref comparer here. var extentMap = new KeyToListMap <EntitySet, LeafCellTreeNode>(EqualityComparer <EntitySet> .Default); // Note that we skip non-leaf nodes (non-leaf nodes don't have FKs) and attach them directly to the result. foreach (var child in children) { var leaf = child as LeafCellTreeNode; if (leaf != null) { EntitySetBase extent = GetLeafNodeTable(leaf); if (extent != null) { extentMap.Add((EntitySet)extent, leaf); } } else { if (resultIJDriverChildren != null && resultIJDriverChildren.Contains(child)) { resultIJDriver.Add(child); } else { result.Add(child); } } } // We only deal with simple cases - one node per extent, remove the rest from children and attach directly to result. var nonTrivial = extentMap.KeyValuePairs.Where(m => m.Value.Count > 1).ToArray(); foreach (var m in nonTrivial) { extentMap.RemoveKey(m.Key); foreach (var n in m.Value) { if (resultIJDriverChildren != null && resultIJDriverChildren.Contains(n)) { resultIJDriver.Add(n); } else { result.Add(n); } } } Debug.Assert(extentMap.KeyValuePairs.All(m => m.Value.Count == 1), "extentMap must map to single nodes only."); // Walk the extents in extentMap and for each extent build PK -> FK1(PK1), FK2(PK2), ... map // where PK is the primary key of the left extent, and FKn(PKn) is an FK of a right extent that // points to the PK of the left extent and is based on the PK columns of the right extent. // Example: // table tBaseType(Id int, c1 int), PK = (tBaseType.Id) // table tDerivedType1(Id int, c2 int), PK1 = (tDerivedType1.Id), FK1 = (tDerivedType1.Id -> tBaseType.Id) // table tDerivedType2(Id int, c3 int), PK2 = (tDerivedType2.Id), FK2 = (tDerivedType2.Id -> tBaseType.Id) // Will produce: // (tBaseType) -> (tDerivedType1, tDerivedType2) var pkFkMap = new KeyToListMap <EntitySet, EntitySet>(EqualityComparer <EntitySet> .Default); // Also for each extent in extentMap, build another map (extent) -> (LOJ node). // It will be used to construct the nesting in the next step. var extentLOJs = new Dictionary <EntitySet, OpCellTreeNode>(EqualityComparer <EntitySet> .Default); foreach (var extentInfo in extentMap.KeyValuePairs) { var principalExtent = extentInfo.Key; foreach (var fkExtent in GetFKOverPKDependents(principalExtent)) { // Only track fkExtents that are in extentMap. ReadOnlyCollection <LeafCellTreeNode> nodes; if (extentMap.TryGetListForKey(fkExtent, out nodes)) { // Make sure that we are not adding resultIJDriverChildren as FK dependents - we do not want them to get nested. if (resultIJDriverChildren == null || !resultIJDriverChildren.Contains(nodes.Single())) { pkFkMap.Add(principalExtent, fkExtent); } } } var extentLojNode = new OpCellTreeNode(m_viewgenContext, CellTreeOpType.LOJ); extentLojNode.Add(extentInfo.Value.Single()); extentLOJs.Add(principalExtent, extentLojNode); } // Construct LOJ nesting inside extentLOJs based on the information in pkFkMap. // Also, track nested extents using nestedExtents. // Example: // We start with nestedExtents empty extentLOJs as such: // tBaseType -> LOJ(BaseTypeNode) // tDerivedType1 -> LOJ(DerivedType1Node)* // tDerivedType2 -> LOJ(DerivedType2Node)** // Note that * and ** represent object references. So each time something is nested, // we don't clone, but nest the original LOJ. When we get to processing the extent of that LOJ, // we might add other children to that nested LOJ. // As we walk pkFkMap, we end up with this: // tBaseType -> LOJ(BaseTypeNode, LOJ(DerivedType1Node)*, LOJ(DerivedType2Node)**) // tDerivedType1 -> LOJ(DerivedType1Node)* // tDerivedType2 -> LOJ(DerivedType2Node)** // nestedExtens = (tDerivedType1, tDerivedType2) var nestedExtents = new Dictionary <EntitySet, EntitySet>(EqualityComparer <EntitySet> .Default); foreach (var m in pkFkMap.KeyValuePairs) { var principalExtent = m.Key; foreach (var fkExtent in m.Value) { OpCellTreeNode fkExtentLOJ; if (extentLOJs.TryGetValue(fkExtent, out fkExtentLOJ) && // make sure we don't nest twice and we don't create a cycle. !nestedExtents.ContainsKey(fkExtent) && !CheckLOJCycle(fkExtent, principalExtent, nestedExtents)) { extentLOJs[m.Key].Add(fkExtentLOJ); nestedExtents.Add(fkExtent, principalExtent); } } } // Now we need to grab the LOJs that have not been nested and add them to the result. // All LOJs that have been nested must be somewhere inside the LOJs that have not been nested, // so they as well end up in the result as part of the unnested ones. foreach (var m in extentLOJs) { if (!nestedExtents.ContainsKey(m.Key)) { // extentLOJ represents (Vx LOJ Vy LOJ(Vm LOJ Vn)) where Vx is the original node from rootNode.Children or resultIJDriverChildren. var extentLOJ = m.Value; if (resultIJDriverChildren != null && resultIJDriverChildren.Contains(extentLOJ.Children[0])) { resultIJDriver.Add(extentLOJ); } else { result.Add(extentLOJ); } } } return(result.Flatten()); }
// requires: opTypeToIsolate must be LOJ, IJ, or Union // effects: Given a tree rooted at rootNode, determines if there // are any FOJs that can be replaced by opTypeToIsolate. If so, // does that and a returns a new tree with the replaced operators // Note: Method may modify rootNode's contents and children internal CellTreeNode IsolateByOperator(CellTreeNode rootNode, CellTreeOpType opTypeToIsolate) { Debug.Assert( opTypeToIsolate == CellTreeOpType.IJ || opTypeToIsolate == CellTreeOpType.LOJ || opTypeToIsolate == CellTreeOpType.Union, "IsolateJoins can only be called for IJs, LOJs, and Unions"); var children = rootNode.Children; if (children.Count <= 1) { // No child or one child - do nothing return(rootNode); } // Replace the FOJs with IJs/LOJs/Unions in the children's subtrees first for (var i = 0; i < children.Count; i++) { // Method modifies input as well children[i] = IsolateByOperator(children[i], opTypeToIsolate); } // Only FOJs and LOJs can be coverted (to IJs, Unions, LOJs) -- // so if the node is not that, we can ignore it (or if the node is already of // the same type that we want) if (rootNode.OpType != CellTreeOpType.FOJ && rootNode.OpType != CellTreeOpType.LOJ || rootNode.OpType == opTypeToIsolate) { return(rootNode); } // Create a new node with the same type as the input cell node type var newRootNode = new OpCellTreeNode(m_viewgenContext, rootNode.OpType); // We start a new "group" with one of the children X - we create // a newChildNode with type "opTypeToIsolate". Then we // determine if any of the remaining children should be in the // same group as X. // childrenSet keeps track of the children that need to be procesed/partitioned var childrenSet = new ModifiableIteratorCollection <CellTreeNode>(children); // Find groups with same or subsumed constants and create a join // or union node for them. We do this so that some of the FOJs // can be replaced by union and join nodes // while (false == childrenSet.IsEmpty) { // Start a new "group" with some child node (for the opTypeToIsolate node type) var groupNode = new OpCellTreeNode(m_viewgenContext, opTypeToIsolate); var someChild = childrenSet.RemoveOneElement(); groupNode.Add(someChild); // Go through the remaining children and determine if their // constants are subsets/equal/disjoint w.r.t the joinNode // constants. foreach (var child in childrenSet.Elements()) { // Check if we can add the child as part of this // groupNode (with opTypeToIsolate being LOJ, IJ, or Union) if (TryAddChildToGroup(opTypeToIsolate, child, groupNode)) { childrenSet.RemoveCurrentOfIterator(); // For LOJ, suppose that child A did not subsume B or // vice-versa. But child C subsumes both. To ensure // that we can get A, B, C in the same group, we // reset the iterator so that when C is added in B's // loop, we can reconsider A. // // For IJ, adding a child to groupNode does not change the range of it, // so there is no need to reconsider previously skipped children. // // For Union, adding a child to groupNode increases the range of the groupNode, // hence previously skipped (because they weren't disjoint with groupNode) children will continue // being ignored because they would still have an overlap with one of the nodes inside groupNode. if (opTypeToIsolate == CellTreeOpType.LOJ) { childrenSet.ResetIterator(); } } } // The new Union/LOJ/IJ node needs to be connected to the root newRootNode.Add(groupNode); } return(newRootNode.Flatten()); }
// requires: cellTreeNode has a tree such that all its intermediate nodes // are FOJ nodes only // effects: Converts the tree rooted at rootNode (recursively) in // following way and returns a new rootNode -- it partitions // rootNode's children such that no two different partitions have // any overlapping constants. These partitions are connected by Union // nodes (since there is no overlapping). // Note: Method may modify rootNode's contents and children private CellTreeNode IsolateUnions(CellTreeNode rootNode) { if (rootNode.Children.Count <= 1) { // No partitioning of children needs to be done return rootNode; } Debug.Assert(rootNode.OpType == CellTreeOpType.FOJ, "So far, we have FOJs only"); // Recursively, transform the subtrees rooted at cellTreeNode's children for (var i = 0; i < rootNode.Children.Count; i++) { // Method modifies input as well rootNode.Children[i] = IsolateUnions(rootNode.Children[i]); } // Different children groups are connected by a Union // node -- the secltion domain of one group is disjoint from // another group's selection domain, i.e., group A1 contributes // tuples to the extent which are disjoint from the tuples by // A2. So we can connect these groups by union alls. // Inside each group, we continue to connect children of the same // group using FOJ var unionNode = new OpCellTreeNode(m_viewgenContext, CellTreeOpType.Union); // childrenSet keeps track of the children that need to be procesed/partitioned var childrenSet = new ModifiableIteratorCollection<CellTreeNode>(rootNode.Children); while (false == childrenSet.IsEmpty) { // Start a new group // Make an FOJ node to connect children of the same group var fojNode = new OpCellTreeNode(m_viewgenContext, CellTreeOpType.FOJ); // Add one of the root's children as a child to the foj node var someChild = childrenSet.RemoveOneElement(); fojNode.Add(someChild); // We now want a transitive closure of the overlap between the // the children node. We keep checking each child with the // fojNode and add it as a child of fojNode if there is an // overlap. Note that when a node is added to the fojNode, // its constants are propagated to the fojNode -- so we do // get transitive closure in terms of intersection foreach (var child in childrenSet.Elements()) { if (!IsDisjoint(fojNode, child)) { fojNode.Add(child); childrenSet.RemoveCurrentOfIterator(); // To ensure that we get all overlapping node, we // need to restart checking all the children childrenSet.ResetIterator(); } } // Now we have a group of children nodes rooted at // fojNode. Add this fojNode to the union unionNode.Add(fojNode); } // The union node as the root of the view var result = unionNode.Flatten(); return result; }
private CellTreeNode ConvertUnionsToNormalizedLOJs(CellTreeNode rootNode) { for (int index = 0; index < rootNode.Children.Count; ++index) { rootNode.Children[index] = this.ConvertUnionsToNormalizedLOJs(rootNode.Children[index]); } if (rootNode.OpType != CellTreeOpType.LOJ || rootNode.Children.Count < 2) { return(rootNode); } OpCellTreeNode opCellTreeNode1 = new OpCellTreeNode(this.m_viewgenContext, rootNode.OpType); List <CellTreeNode> cellTreeNodeList = new List <CellTreeNode>(); OpCellTreeNode opCellTreeNode2 = (OpCellTreeNode)null; HashSet <CellTreeNode> cellTreeNodeSet = (HashSet <CellTreeNode>)null; if (rootNode.Children[0].OpType == CellTreeOpType.IJ) { opCellTreeNode2 = new OpCellTreeNode(this.m_viewgenContext, rootNode.Children[0].OpType); opCellTreeNode1.Add((CellTreeNode)opCellTreeNode2); cellTreeNodeList.AddRange((IEnumerable <CellTreeNode>)rootNode.Children[0].Children); cellTreeNodeSet = new HashSet <CellTreeNode>((IEnumerable <CellTreeNode>)rootNode.Children[0].Children); } else { opCellTreeNode1.Add(rootNode.Children[0]); } foreach (CellTreeNode cellTreeNode in rootNode.Children.Skip <CellTreeNode>(1)) { OpCellTreeNode opCellTreeNode3 = cellTreeNode as OpCellTreeNode; if (opCellTreeNode3 != null && opCellTreeNode3.OpType == CellTreeOpType.Union) { cellTreeNodeList.AddRange((IEnumerable <CellTreeNode>)opCellTreeNode3.Children); } else { cellTreeNodeList.Add(cellTreeNode); } } KeyToListMap <EntitySet, LeafCellTreeNode> keyToListMap1 = new KeyToListMap <EntitySet, LeafCellTreeNode>((IEqualityComparer <EntitySet>)EqualityComparer <EntitySet> .Default); foreach (CellTreeNode child in cellTreeNodeList) { LeafCellTreeNode leaf = child as LeafCellTreeNode; if (leaf != null) { EntitySetBase leafNodeTable = (EntitySetBase)BasicViewGenerator.GetLeafNodeTable(leaf); if (leafNodeTable != null) { keyToListMap1.Add((EntitySet)leafNodeTable, leaf); } } else if (cellTreeNodeSet != null && cellTreeNodeSet.Contains(child)) { opCellTreeNode2.Add(child); } else { opCellTreeNode1.Add(child); } } foreach (KeyValuePair <EntitySet, List <LeafCellTreeNode> > keyValuePair in keyToListMap1.KeyValuePairs.Where <KeyValuePair <EntitySet, List <LeafCellTreeNode> > >((Func <KeyValuePair <EntitySet, List <LeafCellTreeNode> >, bool>)(m => m.Value.Count > 1)).ToArray <KeyValuePair <EntitySet, List <LeafCellTreeNode> > >()) { keyToListMap1.RemoveKey(keyValuePair.Key); foreach (LeafCellTreeNode leafCellTreeNode in keyValuePair.Value) { if (cellTreeNodeSet != null && cellTreeNodeSet.Contains((CellTreeNode)leafCellTreeNode)) { opCellTreeNode2.Add((CellTreeNode)leafCellTreeNode); } else { opCellTreeNode1.Add((CellTreeNode)leafCellTreeNode); } } } KeyToListMap <EntitySet, EntitySet> keyToListMap2 = new KeyToListMap <EntitySet, EntitySet>((IEqualityComparer <EntitySet>)EqualityComparer <EntitySet> .Default); Dictionary <EntitySet, OpCellTreeNode> dictionary = new Dictionary <EntitySet, OpCellTreeNode>((IEqualityComparer <EntitySet>)EqualityComparer <EntitySet> .Default); foreach (KeyValuePair <EntitySet, List <LeafCellTreeNode> > keyValuePair in keyToListMap1.KeyValuePairs) { EntitySet key = keyValuePair.Key; foreach (EntitySet fkOverPkDependent in BasicViewGenerator.GetFKOverPKDependents(key)) { ReadOnlyCollection <LeafCellTreeNode> valueCollection; if (keyToListMap1.TryGetListForKey(fkOverPkDependent, out valueCollection) && (cellTreeNodeSet == null || !cellTreeNodeSet.Contains((CellTreeNode)valueCollection.Single <LeafCellTreeNode>()))) { keyToListMap2.Add(key, fkOverPkDependent); } } OpCellTreeNode opCellTreeNode3 = new OpCellTreeNode(this.m_viewgenContext, CellTreeOpType.LOJ); opCellTreeNode3.Add((CellTreeNode)keyValuePair.Value.Single <LeafCellTreeNode>()); dictionary.Add(key, opCellTreeNode3); } Dictionary <EntitySet, EntitySet> nestedExtents = new Dictionary <EntitySet, EntitySet>((IEqualityComparer <EntitySet>)EqualityComparer <EntitySet> .Default); foreach (KeyValuePair <EntitySet, List <EntitySet> > keyValuePair in keyToListMap2.KeyValuePairs) { EntitySet key = keyValuePair.Key; foreach (EntitySet entitySet in keyValuePair.Value) { OpCellTreeNode opCellTreeNode3; if (dictionary.TryGetValue(entitySet, out opCellTreeNode3) && !nestedExtents.ContainsKey(entitySet) && !BasicViewGenerator.CheckLOJCycle(entitySet, key, nestedExtents)) { dictionary[keyValuePair.Key].Add((CellTreeNode)opCellTreeNode3); nestedExtents.Add(entitySet, key); } } } foreach (KeyValuePair <EntitySet, OpCellTreeNode> keyValuePair in dictionary) { if (!nestedExtents.ContainsKey(keyValuePair.Key)) { OpCellTreeNode opCellTreeNode3 = keyValuePair.Value; if (cellTreeNodeSet != null && cellTreeNodeSet.Contains(opCellTreeNode3.Children[0])) { opCellTreeNode2.Add((CellTreeNode)opCellTreeNode3); } else { opCellTreeNode1.Add((CellTreeNode)opCellTreeNode3); } } } return(opCellTreeNode1.Flatten()); }
private CellTreeNode ConvertUnionsToNormalizedLOJs(CellTreeNode rootNode) { // Recursively, transform the subtrees rooted at rootNode's children. for (var i = 0; i < rootNode.Children.Count; i++) { // Method modifies input as well. rootNode.Children[i] = ConvertUnionsToNormalizedLOJs(rootNode.Children[i]); } // We rewrite only LOJs. if (rootNode.OpType != CellTreeOpType.LOJ || rootNode.Children.Count < 2) { return rootNode; } // Create the resulting LOJ node. var result = new OpCellTreeNode(m_viewgenContext, rootNode.OpType); // Create working collection for the LOJ children. var children = new List<CellTreeNode>(); // If rootNode looks something like ((V0 IJ V1) LOJ V2 LOJ V3), // and it turns out that there are FK associations from V2 or V3 pointing, let's say at V0, // then we want to rewrite the result as (V1 IJ (V0 LOJ V2 LOJ V3)). // If we don't do this, then plan compiler won't have a chance to eliminate LOJ V2 LOJ V3. // Hence, flatten the first child or rootNode if it's IJ, but remember that its parts are driving nodes for the LOJ, // so that we don't accidentally nest them. OpCellTreeNode resultIJDriver = null; HashSet<CellTreeNode> resultIJDriverChildren = null; if (rootNode.Children[0].OpType == CellTreeOpType.IJ) { // Create empty resultIJDriver node and add it as the first child (driving) into the LOJ result. resultIJDriver = new OpCellTreeNode(m_viewgenContext, rootNode.Children[0].OpType); result.Add(resultIJDriver); children.AddRange(rootNode.Children[0].Children); resultIJDriverChildren = new HashSet<CellTreeNode>(rootNode.Children[0].Children); } else { result.Add(rootNode.Children[0]); } // Flatten unions in non-driving nodes: (V0 LOJ (V1 Union V2 Union V3)) -> (V0 LOJ V1 LOJ V2 LOJ V3) foreach (var child in rootNode.Children.Skip(1)) { var opNode = child as OpCellTreeNode; if (opNode != null && opNode.OpType == CellTreeOpType.Union) { children.AddRange(opNode.Children); } else { children.Add(child); } } // A dictionary that maps an extent to the nodes that are from that extent. // We want a ref comparer here. var extentMap = new KeyToListMap<EntitySet, LeafCellTreeNode>(EqualityComparer<EntitySet>.Default); // Note that we skip non-leaf nodes (non-leaf nodes don't have FKs) and attach them directly to the result. foreach (var child in children) { var leaf = child as LeafCellTreeNode; if (leaf != null) { EntitySetBase extent = GetLeafNodeTable(leaf); if (extent != null) { extentMap.Add((EntitySet)extent, leaf); } } else { if (resultIJDriverChildren != null && resultIJDriverChildren.Contains(child)) { resultIJDriver.Add(child); } else { result.Add(child); } } } // We only deal with simple cases - one node per extent, remove the rest from children and attach directly to result. var nonTrivial = extentMap.KeyValuePairs.Where(m => m.Value.Count > 1).ToArray(); foreach (var m in nonTrivial) { extentMap.RemoveKey(m.Key); foreach (var n in m.Value) { if (resultIJDriverChildren != null && resultIJDriverChildren.Contains(n)) { resultIJDriver.Add(n); } else { result.Add(n); } } } Debug.Assert(extentMap.KeyValuePairs.All(m => m.Value.Count == 1), "extentMap must map to single nodes only."); // Walk the extents in extentMap and for each extent build PK -> FK1(PK1), FK2(PK2), ... map // where PK is the primary key of the left extent, and FKn(PKn) is an FK of a right extent that // points to the PK of the left extent and is based on the PK columns of the right extent. // Example: // table tBaseType(Id int, c1 int), PK = (tBaseType.Id) // table tDerivedType1(Id int, c2 int), PK1 = (tDerivedType1.Id), FK1 = (tDerivedType1.Id -> tBaseType.Id) // table tDerivedType2(Id int, c3 int), PK2 = (tDerivedType2.Id), FK2 = (tDerivedType2.Id -> tBaseType.Id) // Will produce: // (tBaseType) -> (tDerivedType1, tDerivedType2) var pkFkMap = new KeyToListMap<EntitySet, EntitySet>(EqualityComparer<EntitySet>.Default); // Also for each extent in extentMap, build another map (extent) -> (LOJ node). // It will be used to construct the nesting in the next step. var extentLOJs = new Dictionary<EntitySet, OpCellTreeNode>(EqualityComparer<EntitySet>.Default); foreach (var extentInfo in extentMap.KeyValuePairs) { var principalExtent = extentInfo.Key; foreach (var fkExtent in GetFKOverPKDependents(principalExtent)) { // Only track fkExtents that are in extentMap. ReadOnlyCollection<LeafCellTreeNode> nodes; if (extentMap.TryGetListForKey(fkExtent, out nodes)) { // Make sure that we are not adding resultIJDriverChildren as FK dependents - we do not want them to get nested. if (resultIJDriverChildren == null || !resultIJDriverChildren.Contains(nodes.Single())) { pkFkMap.Add(principalExtent, fkExtent); } } } var extentLojNode = new OpCellTreeNode(m_viewgenContext, CellTreeOpType.LOJ); extentLojNode.Add(extentInfo.Value.Single()); extentLOJs.Add(principalExtent, extentLojNode); } // Construct LOJ nesting inside extentLOJs based on the information in pkFkMap. // Also, track nested extents using nestedExtents. // Example: // We start with nestedExtents empty extentLOJs as such: // tBaseType -> LOJ(BaseTypeNode) // tDerivedType1 -> LOJ(DerivedType1Node)* // tDerivedType2 -> LOJ(DerivedType2Node)** // Note that * and ** represent object references. So each time something is nested, // we don't clone, but nest the original LOJ. When we get to processing the extent of that LOJ, // we might add other children to that nested LOJ. // As we walk pkFkMap, we end up with this: // tBaseType -> LOJ(BaseTypeNode, LOJ(DerivedType1Node)*, LOJ(DerivedType2Node)**) // tDerivedType1 -> LOJ(DerivedType1Node)* // tDerivedType2 -> LOJ(DerivedType2Node)** // nestedExtens = (tDerivedType1, tDerivedType2) var nestedExtents = new Dictionary<EntitySet, EntitySet>(EqualityComparer<EntitySet>.Default); foreach (var m in pkFkMap.KeyValuePairs) { var principalExtent = m.Key; foreach (var fkExtent in m.Value) { OpCellTreeNode fkExtentLOJ; if (extentLOJs.TryGetValue(fkExtent, out fkExtentLOJ) && // make sure we don't nest twice and we don't create a cycle. !nestedExtents.ContainsKey(fkExtent) && !CheckLOJCycle(fkExtent, principalExtent, nestedExtents)) { extentLOJs[m.Key].Add(fkExtentLOJ); nestedExtents.Add(fkExtent, principalExtent); } } } // Now we need to grab the LOJs that have not been nested and add them to the result. // All LOJs that have been nested must be somewhere inside the LOJs that have not been nested, // so they as well end up in the result as part of the unnested ones. foreach (var m in extentLOJs) { if (!nestedExtents.ContainsKey(m.Key)) { // extentLOJ represents (Vx LOJ Vy LOJ(Vm LOJ Vn)) where Vx is the original node from rootNode.Children or resultIJDriverChildren. var extentLOJ = m.Value; if (resultIJDriverChildren != null && resultIJDriverChildren.Contains(extentLOJ.Children[0])) { resultIJDriver.Add(extentLOJ); } else { result.Add(extentLOJ); } } } return result.Flatten(); }