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); }
// effects: Creates a node with operation opType and no children internal OpCellTreeNode(ViewgenContext context, CellTreeOpType opType) : base(context) { m_opType = opType; m_attrs = new AttributeSet(MemberPath.EqualityComparer); m_children = new List <CellTreeNode>(); }
private static FragmentQuery GenerateFragmentQuery( IEnumerable <CellTreeNode> children, bool isLeft, ViewgenContext context, CellTreeOpType OpType) { FragmentQuery fragmentQuery1 = isLeft ? children.First <CellTreeNode>().LeftFragmentQuery : children.First <CellTreeNode>().RightFragmentQuery; FragmentQueryProcessor fragmentQueryProcessor = isLeft ? context.LeftFragmentQP : context.RightFragmentQP; foreach (CellTreeNode cellTreeNode in children.Skip <CellTreeNode>(1)) { FragmentQuery fragmentQuery2 = isLeft ? cellTreeNode.LeftFragmentQuery : cellTreeNode.RightFragmentQuery; switch (OpType) { case CellTreeOpType.LOJ: continue; case CellTreeOpType.IJ: fragmentQuery1 = fragmentQueryProcessor.Intersect(fragmentQuery1, fragmentQuery2); continue; case CellTreeOpType.LASJ: fragmentQuery1 = fragmentQueryProcessor.Difference(fragmentQuery1, fragmentQuery2); continue; default: fragmentQuery1 = fragmentQueryProcessor.Union(fragmentQuery1, fragmentQuery2); continue; } } return(fragmentQuery1); }
internal OpCellTreeNode( ViewgenContext context, CellTreeOpType opType, params CellTreeNode[] children) : this(context, opType, (IEnumerable <CellTreeNode>)children) { }
internal OpCellTreeNode(ViewgenContext context, CellTreeOpType opType) : base(context) { this.m_opType = opType; this.m_attrs = new Set <MemberPath>(MemberPath.EqualityComparer); this.m_children = new List <CellTreeNode>(); }
// 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 static bool IsAssociativeOp(CellTreeOpType opType) { if (opType != CellTreeOpType.IJ && opType != CellTreeOpType.Union) { return(opType == CellTreeOpType.FOJ); } return(true); }
// effects: Given a set of nodes, determines if all nodes are the exact same associative opType AND // there are leaf children that are common across the children "nodes". If there are any, // returns them. Else return null private static Set <LeafCellTreeNode> GetCommonGrandChildren(List <CellTreeNode> nodes) { Set <LeafCellTreeNode> commonLeaves = null; // We could make this general and apply recursively but we don't for now // Look for a tree of the form: (common op2 gc2) op1 (common op2 gc3) op1 (common op2 gc4) // e.g., (A IJ B IJ X IJ Y) UNION (A IJ B IJ Y IJ Z) UNION (A IJ B IJ R IJ S) // Where op1 and op2 are associative and common, gc2 etc are leaf nodes CellTreeOpType commonChildOpType = CellTreeOpType.Leaf; foreach (CellTreeNode node in nodes) { OpCellTreeNode opNode = node as OpCellTreeNode; if (opNode == null) { return(null); } Debug.Assert(opNode.OpType != CellTreeOpType.Leaf, "Leaf type for op cell node?"); // Now check for whether the op is associative and the same as the previous one if (commonChildOpType == CellTreeOpType.Leaf) { commonChildOpType = opNode.OpType; } else if (CellTreeNode.IsAssociativeOp(opNode.OpType) == false || commonChildOpType != opNode.OpType) { return(null); } // Make sure all the children are leaf children Set <LeafCellTreeNode> nodeChildrenSet = new Set <LeafCellTreeNode>(LeafCellTreeNode.EqualityComparer); foreach (CellTreeNode grandChild in opNode.Children) { LeafCellTreeNode leafGrandChild = grandChild as LeafCellTreeNode; if (leafGrandChild == null) { return(null); } nodeChildrenSet.Add(leafGrandChild); } if (commonLeaves == null) { commonLeaves = nodeChildrenSet; } else { commonLeaves.Intersect(nodeChildrenSet); } } if (commonLeaves.Count == 0) { // No restructuring possible return(null); } return(commonLeaves); }
// effects: Given a sequence of children node and the opType, creates // an OpCellTreeNode and returns it internal OpCellTreeNode(ViewgenContext context, CellTreeOpType opType, IEnumerable <CellTreeNode> children) : this(context, opType) { // Add the children one by one so that we can get the attrs etc fixed foreach (CellTreeNode child in children) { Add(child); } }
private SlotInfo GetJoinSlotInfo( CellTreeOpType opType, bool isRequiredSlot, List <CqlBlock> children, int slotNum, CqlIdentifiers identifiers) { if (!isRequiredSlot) { return(new SlotInfo(false, false, (ProjectedSlot)null, this.GetMemberPath(slotNum))); } int index1 = -1; CaseStatement caseStatement = (CaseStatement)null; for (int index2 = 0; index2 < children.Count; ++index2) { CqlBlock child = children[index2]; if (child.IsProjected(slotNum)) { if (this.IsKeySlot(slotNum)) { index1 = index2; break; } if (opType == CellTreeOpType.IJ) { index1 = OpCellTreeNode.GetInnerJoinChildForSlot(children, slotNum); break; } if (index1 != -1) { if (caseStatement == null) { caseStatement = new CaseStatement(this.GetMemberPath(slotNum)); this.AddCaseForOuterJoins(caseStatement, children[index1], slotNum, identifiers); } this.AddCaseForOuterJoins(caseStatement, child, slotNum, identifiers); } index1 = index2; } } MemberPath memberPath = this.GetMemberPath(slotNum); ProjectedSlot slotValue; if (caseStatement != null && (caseStatement.Clauses.Count > 0 || caseStatement.ElseValue != null)) { caseStatement.Simplify(); slotValue = (ProjectedSlot) new CaseStatementProjectedSlot(caseStatement, (IEnumerable <WithRelationship>)null); } else { slotValue = index1 < 0 ? (!this.IsBoolSlot(slotNum) ? (ProjectedSlot) new ConstantProjectedSlot(Domain.GetDefaultValueForMemberPath(memberPath, (IEnumerable <LeftCellWrapper>) this.GetLeaves(), this.ViewgenContext.Config)) : (ProjectedSlot) new BooleanProjectedSlot(BoolExpression.False, identifiers, this.SlotToBoolIndex(slotNum))) : (ProjectedSlot)children[index1].QualifySlotWithBlockAlias(slotNum); } bool enforceNotNull = this.IsBoolSlot(slotNum) && (opType == CellTreeOpType.LOJ && index1 > 0 || opType == CellTreeOpType.FOJ); return(new SlotInfo(true, true, slotValue, memberPath, enforceNotNull)); }
/// <summary> /// Creates a join block (type given by <paramref name="opType"/>) with SELECT (<paramref name="slotInfos"/>), FROM (<paramref name="children"/>), /// ON (<paramref name="onClauses"/> - one for each child except 0th), WHERE (true), AS (<paramref name="blockAliasNum"/>). /// </summary> internal JoinCqlBlock(CellTreeOpType opType, SlotInfo[] slotInfos, List<CqlBlock> children, List<OnClause> onClauses, CqlIdentifiers identifiers, int blockAliasNum) : base(slotInfos, children, BoolExpression.True, identifiers, blockAliasNum) { m_opType = opType; m_onClauses = onClauses; }
/// <summary> /// Creates a join block (type given by <paramref name="opType"/>) with SELECT (<paramref name="slotInfos"/>), FROM (<paramref name="children"/>), /// ON (<paramref name="onClauses"/> - one for each child except 0th), WHERE (true), AS (<paramref name="blockAliasNum"/>). /// </summary> internal JoinCqlBlock(CellTreeOpType opType, SlotInfo[] slotInfos, List <CqlBlock> children, List <OnClause> onClauses, CqlIdentifiers identifiers, int blockAliasNum) : base(slotInfos, children, BoolExpression.True, identifiers, blockAliasNum) { m_opType = opType; m_onClauses = onClauses; }
internal OpCellTreeNode( ViewgenContext context, CellTreeOpType opType, IEnumerable <CellTreeNode> children) : this(context, opType) { foreach (CellTreeNode child in children) { this.Add(child); } }
private static List <BoolExpression> MergeBoolExpressions( CellQuery query1, CellQuery query2, BoolExpression conjunct1, BoolExpression conjunct2, CellTreeOpType opType) { List <BoolExpression> bools1 = query1.BoolVars; List <BoolExpression> bools2 = query2.BoolVars; if (!conjunct1.IsTrue) { bools1 = BoolExpression.AddConjunctionToBools(bools1, conjunct1); } if (!conjunct2.IsTrue) { bools2 = BoolExpression.AddConjunctionToBools(bools2, conjunct2); } List <BoolExpression> boolExpressionList = new List <BoolExpression>(); for (int index = 0; index < bools1.Count; ++index) { BoolExpression boolExpression = (BoolExpression)null; if (bools1[index] == null) { boolExpression = bools2[index]; } else if (bools2[index] == null) { boolExpression = bools1[index]; } else { switch (opType) { case CellTreeOpType.Union: boolExpression = BoolExpression.CreateOr(bools1[index], bools2[index]); break; case CellTreeOpType.IJ: boolExpression = BoolExpression.CreateAnd(bools1[index], bools2[index]); break; case CellTreeOpType.LASJ: boolExpression = BoolExpression.CreateAnd(bools1[index], BoolExpression.CreateNot(bools2[index])); break; } } boolExpression?.ExpensiveSimplify(); boolExpressionList.Add(boolExpression); } return(boolExpressionList); }
// 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); }
/// <summary> /// Given the <paramref name="opType"/>, returns eSQL string corresponding to the op. /// </summary> internal static string OpToEsql(CellTreeOpType opType) { switch (opType) { case CellTreeOpType.FOJ: return("FULL OUTER JOIN"); case CellTreeOpType.IJ: return("INNER JOIN"); case CellTreeOpType.LOJ: return("LEFT OUTER JOIN"); case CellTreeOpType.Union: return("UNION ALL"); default: Debug.Fail("Unknown operator"); return(null); } }
private static Set <LeafCellTreeNode> GetCommonGrandChildren( List <CellTreeNode> nodes) { Set <LeafCellTreeNode> set = (Set <LeafCellTreeNode>)null; CellTreeOpType cellTreeOpType = CellTreeOpType.Leaf; foreach (CellTreeNode node in nodes) { OpCellTreeNode opCellTreeNode = node as OpCellTreeNode; if (opCellTreeNode == null) { return((Set <LeafCellTreeNode>)null); } if (cellTreeOpType == CellTreeOpType.Leaf) { cellTreeOpType = opCellTreeNode.OpType; } else if (!CellTreeNode.IsAssociativeOp(opCellTreeNode.OpType) || cellTreeOpType != opCellTreeNode.OpType) { return((Set <LeafCellTreeNode>)null); } Set <LeafCellTreeNode> other = new Set <LeafCellTreeNode>(LeafCellTreeNode.EqualityComparer); foreach (CellTreeNode child in opCellTreeNode.Children) { LeafCellTreeNode element = child as LeafCellTreeNode; if (element == null) { return((Set <LeafCellTreeNode>)null); } other.Add(element); } if (set == null) { set = other; } else { set.Intersect(other); } } if (set.Count == 0) { return((Set <LeafCellTreeNode>)null); } return(set); }
internal static string OpToEsql(CellTreeOpType opType) { switch (opType) { case CellTreeOpType.Union: return("UNION ALL"); case CellTreeOpType.FOJ: return("FULL OUTER JOIN"); case CellTreeOpType.LOJ: return("LEFT OUTER JOIN"); case CellTreeOpType.IJ: return("INNER JOIN"); default: return((string)null); } }
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()); }
private CellTreeNode RestructureTreeForMerges(CellTreeNode rootNode) { List <CellTreeNode> children = rootNode.Children; if (!CellTreeNode.IsAssociativeOp(rootNode.OpType) || children.Count <= 1) { return(rootNode); } Set <LeafCellTreeNode> commonGrandChildren = CellTreeSimplifier.GetCommonGrandChildren(children); if (commonGrandChildren == null) { return(rootNode); } CellTreeOpType opType = children[0].OpType; List <OpCellTreeNode> opCellTreeNodeList = new List <OpCellTreeNode>(children.Count); foreach (OpCellTreeNode opCellTreeNode1 in children) { List <LeafCellTreeNode> leafCellTreeNodeList = new List <LeafCellTreeNode>(opCellTreeNode1.Children.Count); foreach (LeafCellTreeNode child in opCellTreeNode1.Children) { if (!commonGrandChildren.Contains(child)) { leafCellTreeNodeList.Add(child); } } OpCellTreeNode opCellTreeNode2 = new OpCellTreeNode(this.m_viewgenContext, opCellTreeNode1.OpType, Helpers.AsSuperTypeList <LeafCellTreeNode, CellTreeNode>((IEnumerable <LeafCellTreeNode>)leafCellTreeNodeList)); opCellTreeNodeList.Add(opCellTreeNode2); } CellTreeNode cellTreeNode1 = (CellTreeNode) new OpCellTreeNode(this.m_viewgenContext, rootNode.OpType, Helpers.AsSuperTypeList <OpCellTreeNode, CellTreeNode>((IEnumerable <OpCellTreeNode>)opCellTreeNodeList)); CellTreeNode cellTreeNode2 = (CellTreeNode) new OpCellTreeNode(this.m_viewgenContext, opType, Helpers.AsSuperTypeList <LeafCellTreeNode, CellTreeNode>((IEnumerable <LeafCellTreeNode>)commonGrandChildren)); return(new OpCellTreeNode(this.m_viewgenContext, opType, new CellTreeNode[2] { cellTreeNode2, cellTreeNode1 }).AssociativeFlatten()); }
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); }
MergeBoolExpressions( CellQuery query1, CellQuery query2, BoolExpression conjunct1, BoolExpression conjunct2, CellTreeOpType opType) { var bools1 = query1.BoolVars; var bools2 = query2.BoolVars; // Add conjuncts to both sets if needed if (false == conjunct1.IsTrue) { bools1 = BoolExpression.AddConjunctionToBools(bools1, conjunct1); } if (false == conjunct2.IsTrue) { bools2 = BoolExpression.AddConjunctionToBools(bools2, conjunct2); } // Perform merge Debug.Assert(bools1.Count == bools2.Count); var bools = new List <BoolExpression>(); // Both bools1[i] and bools2[i] be null for some of the i's. When // we merge two (leaf) cells (say), only one boolean each is set // in it; the rest are all nulls. If the SP/TM rules have been // applied, more than one boolean may be non-null in a cell query for (var i = 0; i < bools1.Count; i++) { BoolExpression merged = null; if (bools1[i] == null) { merged = bools2[i]; } else if (bools2[i] == null) { merged = bools1[i]; } else { if (opType == CellTreeOpType.IJ) { merged = BoolExpression.CreateAnd(bools1[i], bools2[i]); } else if (opType == CellTreeOpType.Union) { merged = BoolExpression.CreateOr(bools1[i], bools2[i]); } else if (opType == CellTreeOpType.LASJ) { merged = BoolExpression.CreateAnd( bools1[i], BoolExpression.CreateNot(bools2[i])); } else { Debug.Fail("No other operation expected for boolean merge"); } } if (merged != null) { merged.ExpensiveSimplify(); } bools.Add(merged); } return(bools); }
// effects: Merges query2 with this according to the TM/SP rules for opType and // returns the merged result. canBooleansOverlap indicates whether the bools in this and query2 can overlap, i.e. // the same cells may have contributed to query2 and this earlier in the merge process internal static bool TryMergeTwoCellQueries( CellQuery query1, CellQuery query2, CellTreeOpType opType, out CellQuery mergedQuery) { mergedQuery = null; // Initialize g1 and g2 according to the TM/SP rules for IJ, LOJ, Union, FOJ cases BoolExpression g1 = null; BoolExpression g2 = null; switch (opType) { case CellTreeOpType.IJ: break; case CellTreeOpType.LOJ: case CellTreeOpType.LASJ: g2 = BoolExpression.True; break; case CellTreeOpType.FOJ: case CellTreeOpType.Union: g1 = BoolExpression.True; g2 = BoolExpression.True; break; default: Debug.Fail("Unsupported operator"); break; } var remap = new Dictionary <MemberPath, MemberPath>(MemberPath.EqualityComparer); //Continue merging only if both queries are over the same source MemberPath newRoot; if (!query1.Extent.Equals(query2.Extent)) { // could not merge return(false); } else { newRoot = query1.SourceExtentMemberPath; } // Conjuncts for ANDing with the previous whereClauses var conjunct1 = BoolExpression.True; var conjunct2 = BoolExpression.True; BoolExpression whereClause = null; switch (opType) { case CellTreeOpType.IJ: // Project[D1, D2, A, B, C] Select[cond1 and cond2] (T) // We simply merge the two lists of booleans -- no conjuct is added // conjunct1 and conjunct2 don't change // query1.WhereCaluse AND query2.WhereCaluse Debug.Assert(g1 == null && g2 == null, "IJ does not affect g1 and g2"); whereClause = BoolExpression.CreateAnd(query1.WhereClause, query2.WhereClause); break; case CellTreeOpType.LOJ: // conjunct1 does not change since D1 remains as is // Project[D1, (expr2 and cond2 and G2) as D2, A, B, C] Select[cond1] (T) // D1 does not change. New d2 is the list of booleans expressions // for query2 ANDed with g2 AND query2.WhereClause Debug.Assert(g1 == null, "LOJ does not affect g1"); conjunct2 = BoolExpression.CreateAnd(query2.WhereClause, g2); // Just query1's whereclause whereClause = query1.WhereClause; break; case CellTreeOpType.FOJ: case CellTreeOpType.Union: // Project[(expr1 and cond1 and G1) as D1, (expr2 and cond2 and G2) as D2, A, B, C] Select[cond1] (T) // New D1 is a list -- newD1 = D1 AND query1.WhereClause AND g1 // New D1 is a list -- newD2 = D2 AND query2.WhereClause AND g2 conjunct1 = BoolExpression.CreateAnd(query1.WhereClause, g1); conjunct2 = BoolExpression.CreateAnd(query2.WhereClause, g2); // The new whereClause -- g1 AND query1.WhereCaluse OR g2 AND query2.WhereClause whereClause = BoolExpression.CreateOr( BoolExpression.CreateAnd(query1.WhereClause, g1), BoolExpression.CreateAnd(query2.WhereClause, g2)); break; case CellTreeOpType.LASJ: // conjunct1 does not change since D1 remains as is // Project[D1, (expr2 and cond2 and G2) as D2, A, B, C] Select[cond1] (T) // D1 does not change. New d2 is the list of booleans expressions // for query2 ANDed with g2 AND NOT query2.WhereClause Debug.Assert(g1 == null, "LASJ does not affect g1"); conjunct2 = BoolExpression.CreateAnd(query2.WhereClause, g2); whereClause = BoolExpression.CreateAnd(query1.WhereClause, BoolExpression.CreateNot(conjunct2)); break; default: Debug.Fail("Unsupported operator"); break; } // Create the various remapped parts for the cell query -- // boolean expressions, merged slots, whereclause, duplicate // elimination, join tree var boolExprs = MergeBoolExpressions(query1, query2, conjunct1, conjunct2, opType); //BoolExpression.RemapBools(boolExprs, remap); ProjectedSlot[] mergedSlots; if (false == ProjectedSlot.TryMergeRemapSlots(query1.ProjectedSlots, query2.ProjectedSlots, out mergedSlots)) { // merging failed because two different right slots go to same left slot return(false); } whereClause = whereClause.RemapBool(remap); var elimDupl = MergeDupl(query1.SelectDistinctFlag, query2.SelectDistinctFlag); whereClause.ExpensiveSimplify(); mergedQuery = new CellQuery( mergedSlots, whereClause, boolExprs, elimDupl, newRoot); return(true); }
private static FragmentQuery GenerateFragmentQuery(IEnumerable <CellTreeNode> children, bool isLeft, ViewgenContext context, CellTreeOpType OpType) { Debug.Assert(children.Any()); FragmentQuery fragmentQuery = isLeft ? children.First().LeftFragmentQuery : children.First().RightFragmentQuery; FragmentQueryProcessor qp = isLeft ? context.LeftFragmentQP : context.RightFragmentQP; foreach (var child in children.Skip(1)) { FragmentQuery nextQuery = isLeft ? child.LeftFragmentQuery : child.RightFragmentQuery; switch (OpType) { case CellTreeOpType.IJ: fragmentQuery = qp.Intersect(fragmentQuery, nextQuery); 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 fragmentQuery = qp.Difference(fragmentQuery, nextQuery); break; default: // All other operators (Union, FOJ) require union of the domains fragmentQuery = qp.Union(fragmentQuery, nextQuery); break; } } return(fragmentQuery); }
// effects: Merges query2 with this according to the TM/SP rules for opType and // returns the merged result. canBooleansOverlap indicates whether the bools in this and query2 can overlap, i.e. // the same cells may have contributed to query2 and this earlier in the merge process internal bool TryMergeTwoCellQueries(CellQuery query1, CellQuery query2, CellTreeOpType opType, MemberDomainMap memberDomainMap, out CellQuery mergedQuery) { mergedQuery = null; // Initialize g1 and g2 according to the TM/SP rules for IJ, LOJ, Union, FOJ cases BoolExpression g1 = null; BoolExpression g2 = null; switch (opType) { case CellTreeOpType.IJ: break; case CellTreeOpType.LOJ: case CellTreeOpType.LASJ: g2 = BoolExpression.True; break; case CellTreeOpType.FOJ: case CellTreeOpType.Union: g1 = BoolExpression.True; g2 = BoolExpression.True; break; default: Debug.Fail("Unsupported operator"); break; } Dictionary<MemberPath, MemberPath> remap = new Dictionary<MemberPath, MemberPath>(MemberPath.EqualityComparer); //Continue merging only if both queries are over the same source MemberPath newRoot; if (!query1.Extent.Equals(query2.Extent)) { // could not merge return false; } else { newRoot = query1.SourceExtentMemberPath; } // Conjuncts for ANDing with the previous whereClauses BoolExpression conjunct1 = BoolExpression.True; BoolExpression conjunct2 = BoolExpression.True; BoolExpression whereClause = null; switch (opType) { case CellTreeOpType.IJ: // Project[D1, D2, A, B, C] Select[cond1 and cond2] (T) // We simply merge the two lists of booleans -- no conjuct is added // conjunct1 and conjunct2 don't change // query1.WhereCaluse AND query2.WhereCaluse Debug.Assert(g1 == null && g2 == null, "IJ does not affect g1 and g2"); whereClause = BoolExpression.CreateAnd(query1.WhereClause, query2.WhereClause); break; case CellTreeOpType.LOJ: // conjunct1 does not change since D1 remains as is // Project[D1, (expr2 and cond2 and G2) as D2, A, B, C] Select[cond1] (T) // D1 does not change. New d2 is the list of booleans expressions // for query2 ANDed with g2 AND query2.WhereClause Debug.Assert(g1 == null, "LOJ does not affect g1"); conjunct2 = BoolExpression.CreateAnd(query2.WhereClause, g2); // Just query1's whereclause whereClause = query1.WhereClause; break; case CellTreeOpType.FOJ: case CellTreeOpType.Union: // Project[(expr1 and cond1 and G1) as D1, (expr2 and cond2 and G2) as D2, A, B, C] Select[cond1] (T) // New D1 is a list -- newD1 = D1 AND query1.WhereClause AND g1 // New D1 is a list -- newD2 = D2 AND query2.WhereClause AND g2 conjunct1 = BoolExpression.CreateAnd(query1.WhereClause, g1); conjunct2 = BoolExpression.CreateAnd(query2.WhereClause, g2); // The new whereClause -- g1 AND query1.WhereCaluse OR g2 AND query2.WhereClause whereClause = BoolExpression.CreateOr(BoolExpression.CreateAnd(query1.WhereClause, g1), BoolExpression.CreateAnd(query2.WhereClause, g2)); break; case CellTreeOpType.LASJ: // conjunct1 does not change since D1 remains as is // Project[D1, (expr2 and cond2 and G2) as D2, A, B, C] Select[cond1] (T) // D1 does not change. New d2 is the list of booleans expressions // for query2 ANDed with g2 AND NOT query2.WhereClause Debug.Assert(g1 == null, "LASJ does not affect g1"); conjunct2 = BoolExpression.CreateAnd(query2.WhereClause, g2); whereClause = BoolExpression.CreateAnd(query1.WhereClause, BoolExpression.CreateNot(conjunct2)); break; default: Debug.Fail("Unsupported operator"); break; } // Create the various remapped parts for the cell query -- // boolean expressions, merged slots, whereclause, duplicate // elimination, join tree List<BoolExpression> boolExprs = MergeBoolExpressions(query1, query2, conjunct1, conjunct2, opType); //BoolExpression.RemapBools(boolExprs, remap); ProjectedSlot[] mergedSlots; if (false == ProjectedSlot.TryMergeRemapSlots(query1.ProjectedSlots, query2.ProjectedSlots, out mergedSlots)) { // merging failed because two different right slots go to same left slot return false; } whereClause = whereClause.RemapBool(remap); CellQuery.SelectDistinct elimDupl = MergeDupl(query1.SelectDistinctFlag, query2.SelectDistinctFlag); whereClause.ExpensiveSimplify(); mergedQuery = new CellQuery(mergedSlots, whereClause, boolExprs, elimDupl, newRoot); return true; }
// 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()); }
// effects: Restructure tree so that it is better positioned for merges private CellTreeNode RestructureTreeForMerges(CellTreeNode rootNode) { List <CellTreeNode> children = rootNode.Children; if (CellTreeNode.IsAssociativeOp(rootNode.OpType) == false || children.Count <= 1) { return(rootNode); } // If this node's operator is associative and each child's // operator is also associative, check if there is a common set // of leaf nodes across all grandchildren Set <LeafCellTreeNode> commonGrandChildren = GetCommonGrandChildren(children); if (commonGrandChildren == null) { return(rootNode); } CellTreeOpType commonChildOpType = children[0].OpType; // We do have the structure that we are looking for // (common op2 gc2) op1 (common op2 gc3) op1 (common op2 gc4) becomes // common op2 (gc2 op1 gc3 op1 gc4) // e.g., (A IJ B IJ X IJ Y) UNION (A IJ B IJ Y IJ Z) UNION (A IJ B IJ R IJ S) // becomes A IJ B IJ ((X IJ Y) UNION (Y IJ Z) UNION (R IJ S)) // From each child in children, get the nodes other than commonGrandChildren - these are gc2, gc3, ... // Each gc2 must be connected by op2 as before, i.e., ABC + ACD = A(BC + CD) // All children must be OpCellTreeNodes! List <OpCellTreeNode> newChildren = new List <OpCellTreeNode>(children.Count); foreach (OpCellTreeNode child in children) { // Remove all children in child that belong to commonGrandChildren // All grandChildren must be leaf nodes at this point List <LeafCellTreeNode> newGrandChildren = new List <LeafCellTreeNode>(child.Children.Count); foreach (LeafCellTreeNode grandChild in child.Children) { if (commonGrandChildren.Contains(grandChild) == false) { newGrandChildren.Add(grandChild); } } // In the above example, child.OpType is IJ Debug.Assert(child.OpType == commonChildOpType); OpCellTreeNode newChild = new OpCellTreeNode(m_viewgenContext, child.OpType, Helpers.AsSuperTypeList <LeafCellTreeNode, CellTreeNode>(newGrandChildren)); newChildren.Add(newChild); } // Connect gc2 op1 gc3 op1 gc4 - op1 is UNION in this // ((X IJ Y) UNION (Y IJ Z) UNION (R IJ S)) // rootNode.Type is UNION CellTreeNode remainingNodes = new OpCellTreeNode(m_viewgenContext, rootNode.OpType, Helpers.AsSuperTypeList <OpCellTreeNode, CellTreeNode>(newChildren)); // Take the common grandchildren and connect via commonChildType // i.e., A IJ B CellTreeNode commonNodes = new OpCellTreeNode(m_viewgenContext, commonChildOpType, Helpers.AsSuperTypeList <LeafCellTreeNode, CellTreeNode>(commonGrandChildren)); // Connect both by commonChildType CellTreeNode result = new OpCellTreeNode(m_viewgenContext, commonChildOpType, new CellTreeNode[] { commonNodes, remainingNodes }); result = result.AssociativeFlatten(); return(result); }
internal static bool TryMergeTwoCellQueries( CellQuery query1, CellQuery query2, CellTreeOpType opType, out CellQuery mergedQuery) { mergedQuery = (CellQuery)null; BoolExpression boolExpression1 = (BoolExpression)null; BoolExpression boolExpression2 = (BoolExpression)null; switch (opType) { case CellTreeOpType.Union: case CellTreeOpType.FOJ: boolExpression1 = BoolExpression.True; boolExpression2 = BoolExpression.True; break; case CellTreeOpType.LOJ: case CellTreeOpType.LASJ: boolExpression2 = BoolExpression.True; break; } Dictionary <MemberPath, MemberPath> remap = new Dictionary <MemberPath, MemberPath>(MemberPath.EqualityComparer); if (!query1.Extent.Equals((object)query2.Extent)) { return(false); } MemberPath extentMemberPath = query1.SourceExtentMemberPath; BoolExpression and1 = BoolExpression.True; BoolExpression and2 = BoolExpression.True; BoolExpression boolExpression3 = (BoolExpression)null; switch (opType) { case CellTreeOpType.Union: case CellTreeOpType.FOJ: and1 = BoolExpression.CreateAnd(query1.WhereClause, boolExpression1); and2 = BoolExpression.CreateAnd(query2.WhereClause, boolExpression2); boolExpression3 = BoolExpression.CreateOr(BoolExpression.CreateAnd(query1.WhereClause, boolExpression1), BoolExpression.CreateAnd(query2.WhereClause, boolExpression2)); break; case CellTreeOpType.LOJ: and2 = BoolExpression.CreateAnd(query2.WhereClause, boolExpression2); boolExpression3 = query1.WhereClause; break; case CellTreeOpType.IJ: boolExpression3 = BoolExpression.CreateAnd(query1.WhereClause, query2.WhereClause); break; case CellTreeOpType.LASJ: and2 = BoolExpression.CreateAnd(query2.WhereClause, boolExpression2); boolExpression3 = BoolExpression.CreateAnd(query1.WhereClause, BoolExpression.CreateNot(and2)); break; } List <BoolExpression> boolExprs = CellTreeSimplifier.MergeBoolExpressions(query1, query2, and1, and2, opType); ProjectedSlot[] result; if (!ProjectedSlot.TryMergeRemapSlots(query1.ProjectedSlots, query2.ProjectedSlots, out result)) { return(false); } BoolExpression whereClause = boolExpression3.RemapBool(remap); CellQuery.SelectDistinct elimDupl = CellTreeSimplifier.MergeDupl(query1.SelectDistinctFlag, query2.SelectDistinctFlag); whereClause.ExpensiveSimplify(); mergedQuery = new CellQuery(result, whereClause, boolExprs, elimDupl, extentMemberPath); return(true); }
// effects: Returns true iff the Op (e.g., IJ) is associative, i.e., // A OP (B OP C) is the same as (A OP B) OP C or A OP B OP C internal static bool IsAssociativeOp(CellTreeOpType opType) { // This is not true for LOJ and LASJ return(opType == CellTreeOpType.IJ || opType == CellTreeOpType.Union || opType == CellTreeOpType.FOJ); }
// 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(); }
// 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; }
// effects: Generates a SlotInfo object for a slot of a join node. It // uses the type of the join operation (opType), whether the slot is // required by the parent or not (isRequiredSlot), the children of // this node (children) and the number of the slotNum private SlotInfo GetJoinSlotInfo(CellTreeOpType opType, bool isRequiredSlot, List <CqlBlock> children, int slotNum, CqlIdentifiers identifiers) { if (false == isRequiredSlot) { // The slot will not be used. So we can set the projected slot to be null 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 a qualified slot or null or false. // If case statement slot has nothing, treat it as null/empty. if (caseForOuterJoins != null && (caseForOuterJoins.Clauses.Count > 0 || caseForOuterJoins.ElseValue != null)) { caseForOuterJoins.Simplify(); slot = new CaseStatementProjectedSlot(caseForOuterJoins, null); } else if (childDefiningSlot >= 0) { slot = children[childDefiningSlot].QualifySlotWithBlockAlias(slotNum); } else { // need to produce output slot, but don't have a value // output NULL for fields or False for bools if (IsBoolSlot(slotNum)) { slot = new BooleanProjectedSlot(BoolExpression.False, identifiers, SlotToBoolIndex(slotNum)); } else { slot = new ConstantProjectedSlot(Domain.GetDefaultValueForMemberPath(memberPath, GetLeaves(), ViewgenContext.Config), memberPath); } } // 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); }
MergeBoolExpressions(CellQuery query1, CellQuery query2, BoolExpression conjunct1, BoolExpression conjunct2, CellTreeOpType opType) { List<BoolExpression> bools1 = query1.BoolVars; List<BoolExpression> bools2 = query2.BoolVars; // Add conjuncts to both sets if needed if (false == conjunct1.IsTrue) { bools1 = BoolExpression.AddConjunctionToBools(bools1, conjunct1); } if (false == conjunct2.IsTrue) { bools2 = BoolExpression.AddConjunctionToBools(bools2, conjunct2); } // Perform merge Debug.Assert(bools1.Count == bools2.Count); List<BoolExpression> bools = new List<BoolExpression>(); // Both bools1[i] and bools2[i] be null for some of the i's. When // we merge two (leaf) cells (say), only one boolean each is set // in it; the rest are all nulls. If the SP/TM rules have been // applied, more than one boolean may be non-null in a cell query for (int i = 0; i < bools1.Count; i++) { BoolExpression merged = null; if (bools1[i] == null) { merged = bools2[i]; } else if (bools2[i] == null) { merged = bools1[i]; } else { if (opType == CellTreeOpType.IJ) { merged = BoolExpression.CreateAnd(bools1[i], bools2[i]); } else if (opType == CellTreeOpType.Union) { merged = BoolExpression.CreateOr(bools1[i], bools2[i]); } else if (opType == CellTreeOpType.LASJ) { merged = BoolExpression.CreateAnd(bools1[i], BoolExpression.CreateNot(bools2[i])); } else { Debug.Fail("No other operation expected for boolean merge"); } } if (merged != null) { merged.ExpensiveSimplify(); } bools.Add(merged); } return bools; }
// 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; }