private bool Match(Node pattern, Node original) { if (pattern.Op.OpType == OpType.Leaf) { return true; } if (pattern.Op.OpType != original.Op.OpType) { return false; } if (pattern.Children.Count != original.Children.Count) { return false; } for (var i = 0; i < pattern.Children.Count; i++) { if (!Match(pattern.Children[i], original.Children[i])) { return false; } } return true; }
/// <summary> /// We perform the following simple transformation for CaseOps. If every single /// then/else expression in the CaseOp is equivalent, then we can simply replace /// the Op with the first then/expression. Specifically, /// case when w1 then t1 when w2 then t2 ... when wn then tn else e end /// => t1 /// assuming that t1 is equivalent to t2 is equivalent to ... to e /// </summary> /// <param name="context">Rule Processing context</param> /// <param name="caseOpNode">The current subtree for the CaseOp</param> /// <param name="newNode">the (possibly) modified subtree</param> /// <returns>true, if we performed any transformations</returns> private static bool ProcessSimplifyCase(RuleProcessingContext context, Node caseOpNode, out Node newNode) { var caseOp = (CaseOp)caseOpNode.Op; newNode = caseOpNode; // // Can I collapse the entire case-expression into a single expression - yes, // if all the then/else clauses are the same expression // if (ProcessSimplifyCase_Collapse(caseOpNode, out newNode)) { return true; } // // Can I remove any unnecessary when-then pairs ? // if (ProcessSimplifyCase_EliminateWhenClauses(context, caseOp, caseOpNode, out newNode)) { return true; } // Nothing else I can think of return false; }
/// <summary> /// Visit the children of this Node. but in reverse order /// </summary> /// <param name="n">The current node</param> protected virtual void VisitChildrenReverse(Node n) { for (var i = n.Children.Count - 1; i >= 0; i--) { VisitNode(n.Children[i]); } }
/// <summary> /// Visit the children of this Node /// </summary> /// <param name="n">The Node that references the Op</param> protected virtual void VisitChildren(Node n) { foreach (var chi in n.Children) { VisitNode(chi); } }
internal static Node Copy(Command cmd, Node n, out VarMap varMap) { OpCopier oc = new OpCopier(cmd); Node newNode = oc.CopyNode(n); varMap = oc.m_varMap; return newNode; }
/// <summary> /// Determines whether the var or a property of the var (if the var is defined as a NewRecord) /// is defined exclusively over a single group aggregate. If so, it registers it as such with the /// group aggregate var info manager. /// </summary> /// <param name="op"></param> /// <param name="n"></param> public override void Visit(VarDefOp op, Node n) { VisitDefault(n); var definingNode = n.Child0; var definingNodeOp = definingNode.Op; GroupAggregateVarInfo referencedVarInfo; Node templateNode; bool isUnnested; if (GroupAggregateVarComputationTranslator.TryTranslateOverGroupAggregateVar( definingNode, true, _command, _groupAggregateVarInfoManager, out referencedVarInfo, out templateNode, out isUnnested)) { _groupAggregateVarInfoManager.Add(op.Var, referencedVarInfo, templateNode, isUnnested); } else if (definingNodeOp.OpType == OpType.NewRecord) { var newRecordOp = (NewRecordOp)definingNodeOp; for (var i = 0; i < definingNode.Children.Count; i++) { var argumentNode = definingNode.Children[i]; if (GroupAggregateVarComputationTranslator.TryTranslateOverGroupAggregateVar( argumentNode, true, _command, _groupAggregateVarInfoManager, out referencedVarInfo, out templateNode, out isUnnested)) { _groupAggregateVarInfoManager.Add(op.Var, referencedVarInfo, templateNode, isUnnested, newRecordOp.Properties[i]); } } } }
/// <summary> /// Basic constructor /// </summary> /// <param name="pattern">The pattern to look for</param> /// <param name="processDelegate">The callback to invoke when such a pattern is identified</param> internal PatternMatchRule(Node pattern, ProcessNodeDelegate processDelegate) : base(pattern.Op.OpType, processDelegate) { Debug.Assert(pattern != null, "null pattern"); Debug.Assert(pattern.Op != null, "null pattern Op"); m_pattern = pattern; }
/// <summary> /// If the DistinctOp includes all all the keys of the input, than it is unnecessary. /// Distinct (X, distinct_keys) -> Project( X, distinct_keys) where distinct_keys includes all keys of X. /// </summary> /// <param name="context">Rule processing context</param> /// <param name="n">current subtree</param> /// <param name="newNode">transformed subtree</param> /// <returns>transformation status</returns> private static bool ProcessDistinctOpOfKeys(RuleProcessingContext context, Node n, out Node newNode) { var command = context.Command; var nodeInfo = command.GetExtendedNodeInfo(n.Child0); var op = (DistinctOp)n.Op; //If we know the keys of the input and the list of distinct keys includes them all, omit the distinct if (!nodeInfo.Keys.NoKeys && op.Keys.Subsumes(nodeInfo.Keys.KeyVars)) { var newOp = command.CreateProjectOp(op.Keys); //Create empty vardef list var varDefListOp = command.CreateVarDefListOp(); var varDefListNode = command.CreateNode(varDefListOp); newNode = command.CreateNode(newOp, n.Child0, varDefListNode); return true; } //Otherwise return the node as is newNode = n; return false; }
private static bool ApplyRulesToNode(RuleProcessingContext context, ReadOnlyCollection<ReadOnlyCollection<InternalTrees.Rule>> rules, Node currentNode, out Node newNode) { newNode = currentNode; // Apply any pre-rule delegates context.PreProcess(currentNode); foreach (Rule r in rules[(int)currentNode.Op.OpType]) { if (!r.Match(currentNode)) { continue; } // Did the rule modify the subtree? if (r.Apply(context, currentNode, out newNode)) { // The node has changed; don't try to apply any more rules context.PostProcess(newNode, r); return true; } else { Debug.Assert(newNode == currentNode, "Liar! This rule should have returned 'true'"); } } context.PostProcess(currentNode, null); return false; }
/// <summary> /// Determines whether any var from a given list of keys is referenced by any of defining node's right relatives, /// with the exception of the relatives brunching at the given targetJoinNode. /// </summary> /// <param name="keys">A list of vars to check for</param> /// <param name="definingNode">The node considered to be the defining node</param> /// <param name="targetJoinNode">The relatives branching at this node are skipped</param> /// <returns>False, only it can determine that not a single var from a given list of keys is referenced by any /// of defining node's right relatives, with the exception of the relatives brunching at the given targetJoinNode. </returns> internal bool HasKeyReferences(VarVec keys, Node definingNode, Node targetJoinNode) { Node currentChild = definingNode; Node parent; bool continueUp = true; while (continueUp & m_nodeToParentMap.TryGetValue(currentChild, out parent)) { if (parent != targetJoinNode) { // Check the parent if (HasVarReferencesShallow(parent, keys, m_nodeToSiblingNumber[currentChild], out continueUp)) { return true; } //Check all the siblings to the right for (int i = m_nodeToSiblingNumber[currentChild] + 1; i < parent.Children.Count; i++) { if (parent.Children[i].GetNodeInfo(m_command).ExternalReferences.Overlaps(keys)) { return true; } } } currentChild = parent; } return false; }
/// <summary> /// Compute the hash value for this node /// </summary> /// <param name="cmd"></param> /// <param name="n"></param> internal override void ComputeHashValue(Command cmd, Node n) { base.ComputeHashValue(cmd, n); m_hashValue = (m_hashValue << 4) ^ GetHashValue(Definitions); m_hashValue = (m_hashValue << 4) ^ GetHashValue(Keys.KeyVars); return; }
internal Node ReMap(Node node, Dictionary<Var, Node> varMap) { PlanCompiler.Assert(node.Op.IsScalarOp, "Expected a scalarOp: Found " + Dump.AutoString.ToString(node.Op.OpType)); // Replace varRefOps by the corresponding expression in the map, if any if (node.Op.OpType == OpType.VarRef) { var varRefOp = node.Op as VarRefOp; Node newNode = null; if (varMap.TryGetValue(varRefOp.Var, out newNode)) { newNode = Copy(newNode); return newNode; } else { return node; } } // Simply process the result of the children. for (var i = 0; i < node.Children.Count; i++) { node.Children[i] = ReMap(node.Children[i], varMap); } // We may have changed something deep down Command.RecomputeNodeInfo(node); return node; }
/// <summary> /// Convert a /// SingleRowOp(X) => X /// if X produces at most one row /// </summary> /// <param name="context">Rule Processing context</param> /// <param name="singleRowNode">Current subtree</param> /// <param name="newNode">transformed subtree</param> /// <returns>Transformation status</returns> private static bool ProcessSingleRowOpOverAnything(RuleProcessingContext context, Node singleRowNode, out Node newNode) { newNode = singleRowNode; var trc = (TransformationRulesContext)context; var childNodeInfo = context.Command.GetExtendedNodeInfo(singleRowNode.Child0); // If the input to this Op can produce at most one row, then we don't need the // singleRowOp - simply return the input if (childNodeInfo.MaxRows <= RowCount.One) { newNode = singleRowNode.Child0; return true; } // // if the current node is a FilterOp, then try and determine if the FilterOp // produces one row at most // if (singleRowNode.Child0.Op.OpType == OpType.Filter) { var predicate = new Predicate(context.Command, singleRowNode.Child0.Child1); if (predicate.SatisfiesKey(childNodeInfo.Keys.KeyVars, childNodeInfo.Definitions)) { childNodeInfo.MaxRows = RowCount.One; newNode = singleRowNode.Child0; return true; } } // we couldn't do anything return false; }
/// <summary> /// Utility method that determines whether a given CaseOp subtree can be optimized. /// Called by both PreProcessor and NominalTypeEliminator. /// /// If the case statement is of the shape: /// case when X then NULL else Y, or /// case when X then Y else NULL, /// where Y is of row type, and the types of the input CaseOp, the NULL and Y are the same, /// return true /// </summary> /// <param name="op"></param> /// <param name="n"></param> /// <returns></returns> internal static bool IsRowTypeCaseOpWithNullability(CaseOp op, Node n, out bool thenClauseIsNull) { thenClauseIsNull = false; //any default value will do if (!TypeSemantics.IsRowType(op.Type)) { return false; } if (n.Children.Count != 3) { return false; } //All three types must be equal if (!n.Child1.Op.Type.EdmEquals(op.Type) || !n.Child2.Op.Type.EdmEquals(op.Type)) { return false; } //At least one of Child1 and Child2 needs to be a null if (n.Child1.Op.OpType == OpType.Null) { thenClauseIsNull = true; return true; } if (n.Child2.Op.OpType == OpType.Null) { // thenClauseIsNull stays false return true; } return false; }
/// <summary> /// Process a SetOp when one of the inputs is an emptyset. /// /// An emptyset is represented by a Filter(X, ConstantPredicate) /// where the ConstantPredicate has a value of "false" /// /// The general rules are /// UnionAll(X, EmptySet) => X /// UnionAll(EmptySet, X) => X /// Intersect(EmptySet, X) => EmptySet /// Intersect(X, EmptySet) => EmptySet /// Except(EmptySet, X) => EmptySet /// Except(X, EmptySet) => X /// /// These rules then translate into /// UnionAll: return the non-empty input /// Intersect: return the empty input /// Except: return the "left" input /// </summary> /// <param name="context">Rule processing context</param> /// <param name="setOpNode">the current setop tree</param> /// <param name="filterNodeIndex">Index of the filter node in the setop</param> /// <param name="newNode">transformed subtree</param> /// <returns>transformation status</returns> private static bool ProcessSetOpOverEmptySet(RuleProcessingContext context, Node setOpNode, out Node newNode) { var leftChildIsEmptySet = context.Command.GetExtendedNodeInfo(setOpNode.Child0).MaxRows == RowCount.Zero; var rightChildIsEmptySet = context.Command.GetExtendedNodeInfo(setOpNode.Child1).MaxRows == RowCount.Zero; if (!leftChildIsEmptySet && !rightChildIsEmptySet) { newNode = setOpNode; return false; } int indexToReturn; var setOp = (SetOp)setOpNode.Op; if (!rightChildIsEmptySet && setOp.OpType == OpType.UnionAll || !leftChildIsEmptySet && setOp.OpType == OpType.Intersect) { indexToReturn = 1; } else { indexToReturn = 0; } newNode = setOpNode.Children[indexToReturn]; var trc = (TransformationRulesContext)context; foreach (var kv in setOp.VarMap[indexToReturn]) { trc.AddVarMapping(kv.Key, kv.Value); } return true; }
/// <summary> /// Update vars in just this node (and not the entire subtree) /// Does *not* recompute the nodeinfo - there are at least some consumers of this /// function that do not want the recomputation - transformation rules, for example /// </summary> /// <param name="node">current node</param> internal virtual void RemapNode(Node node) { if (m_varMap.Count == 0) { return; } VisitNode(node); }
/// <summary> /// Equivalent to OpCopier.Copy, only in addition it keeps track of the defining subtrees /// of collection vars defined in the subtree rooted at the copy of the input node n. /// </summary> /// <param name="cmd"></param> /// <param name="n"></param> /// <param name="varMap"></param> /// <param name="newCollectionVarDefinitions"></param> /// <returns></returns> internal static Node Copy(Command cmd, Node n, out VarMap varMap, out Dictionary<Var, Node> newCollectionVarDefinitions) { var oc = new OpCopierTrackingCollectionVars(cmd); var newNode = oc.CopyNode(n); varMap = oc.m_varMap; newCollectionVarDefinitions = oc.m_newCollectionVarDefinitions; return newNode; }
protected static void AssertArity(Node n) { if (n.Op.Arity != Op.ArityVarying) { AssertArity(n, n.Op.Arity); } }
// List of columns of this table that are nullable (and must have nulls pruned out) #endregion #region constructors /// <summary> /// Basic constructor /// </summary> /// <param name="id">node id</param> /// <param name="node">scan table node</param> internal AugmentedTableNode(int id, Node node) : base(id, node) { var scanTableOp = (ScanTableOp)node.Op; m_table = scanTableOp.Table; LastVisibleId = id; m_replacementTable = this; m_newLocationId = id; }
/// <summary> /// Tracks the information that the given node is a parent of its children (one level only) /// </summary> /// <param name="parent"></param> internal void AddChildren(Node parent) { for(int i=0; i< parent.Children.Count; i++) { //We do not use add on purpose, we may be updating a child's parent after join elimination in a subtree m_nodeToParentMap[parent.Children[i]] = parent; m_nodeToSiblingNumber[parent.Children[i]] = i; } }
/// <summary> /// Converts a Project(Project(X, c1,...), d1,...) => /// Project(X, d1', d2'...) /// where d1', d2' etc. are the "mapped" versions of d1, d2 etc. /// </summary> /// <param name="context">Rule processing context</param> /// <param name="projectNode">Current ProjectOp node</param> /// <param name="newNode">modified subtree</param> /// <returns>Transformation status</returns> private static bool ProcessProjectOverProject(RuleProcessingContext context, Node projectNode, out Node newNode) { newNode = projectNode; var projectOp = (ProjectOp)projectNode.Op; var varDefListNode = projectNode.Child1; var subProjectNode = projectNode.Child0; var subProjectOp = (ProjectOp)subProjectNode.Op; var trc = (TransformationRulesContext)context; // If any of the defining expressions is not a scalar op tree, then simply // quit var varRefMap = new Dictionary<Var, int>(); foreach (var varDefNode in varDefListNode.Children) { if (!trc.IsScalarOpTree(varDefNode.Child0, varRefMap)) { return false; } } var varMap = trc.GetVarMap(subProjectNode.Child1, varRefMap); if (varMap == null) { return false; } // create a new varDefList node... var newVarDefListNode = trc.Command.CreateNode(trc.Command.CreateVarDefListOp()); // Remap any local definitions, I have foreach (var varDefNode in varDefListNode.Children) { // update the defining expression varDefNode.Child0 = trc.ReMap(varDefNode.Child0, varMap); trc.Command.RecomputeNodeInfo(varDefNode); newVarDefListNode.Children.Add(varDefNode); } // Now, pull up any definitions of the subProject that I publish myself var projectNodeInfo = trc.Command.GetExtendedNodeInfo(projectNode); foreach (var chi in subProjectNode.Child1.Children) { var varDefOp = (VarDefOp)chi.Op; if (projectNodeInfo.Definitions.IsSet(varDefOp.Var)) { newVarDefListNode.Children.Add(chi); } } // // now that we have remapped all our computed vars, simply bypass the subproject // node // projectNode.Child0 = subProjectNode.Child0; projectNode.Child1 = newVarDefListNode; return true; }
/// <summary> /// Add a subquery to the "parent" relop node /// </summary> /// <param name="outputVar">the output var to be used - at the current location - in lieu of the subquery</param> /// <param name="subquery">the subquery to move</param> /// <returns>a var ref node for the var returned from the subquery</returns> protected Node AddSubqueryToParentRelOp(Var outputVar, Node subquery) { Node ancestor = FindRelOpAncestor(); PlanCompiler.Assert(ancestor != null, "no ancestors found?"); AddSubqueryToRelOpNode(ancestor, subquery); subquery = m_command.CreateNode(m_command.CreateVarRefOp(outputVar)); return subquery; }
/// <summary> /// Pull up keys (if possible) for the given node /// </summary> /// <param name="node">node to pull up keys for</param> /// <returns>Keys for the node</returns> internal KeyVec GetKeys(Node node) { ExtendedNodeInfo nodeInfo = node.GetExtendedNodeInfo(m_command); if (nodeInfo.Keys.NoKeys) { VisitNode(node); } return nodeInfo.Keys; }
/// <summary> /// Convert Filter(Filter(X, p1), p2) => Filter(X, (p1 and p2)) /// </summary> /// <param name="context">rule processing context</param> /// <param name="filterNode">FilterOp node</param> /// <param name="newNode">modified subtree</param> /// <returns>transformed subtree</returns> private static bool ProcessFilterOverFilter(RuleProcessingContext context, Node filterNode, out Node newNode) { var newAndNode = context.Command.CreateNode( context.Command.CreateConditionalOp(OpType.And), filterNode.Child0.Child1, filterNode.Child1); newNode = context.Command.CreateNode(context.Command.CreateFilterOp(), filterNode.Child0.Child0, newAndNode); return true; }
/// <summary> /// Build Project(select 1 from child). /// </summary> /// <param name="child"></param> /// <returns></returns> private Node BuildDummyProjectForExists(Node child) { Var newVar; var projectNode = m_command.BuildProject( child, m_command.CreateNode(m_command.CreateInternalConstantOp(m_command.IntegerType, 1)), out newVar); return projectNode; }
/// <summary> /// Determines whether the given node is a VarRef over the given var /// </summary> /// <param name="node"></param> /// <param name="var"></param> /// <returns></returns> internal static bool IsVarRefOverGivenVar(Node node, Var var) { if (node.Op.OpType != OpType.VarRef) { return false; } return ((VarRefOp)node.Op).Var == var; }
/// <summary> /// Translate Exists(X) into Exists(select 1 from X) /// </summary> /// <param name="op"></param> /// <param name="n"></param> /// <returns></returns> public override Node Visit(ExistsOp op, Node n) { VisitChildren(n); // Build up a dummy project node over the input n.Child0 = BuildDummyProjectForExists(n.Child0); return n; }
/// <summary> /// Default visitor for children. Simply visit all children, and /// try to get keys for those nodes (relops, physicalOps) that /// don't have keys as yet. /// </summary> /// <param name="n">Current node</param> protected override void VisitChildren(Node n) { foreach (Node chi in n.Children) { if (chi.Op.IsRelOp || chi.Op.IsPhysicalOp) { GetKeys(chi); } } }
/// <summary> /// Creates a ProviderCommandInfo for the given node. /// This method should be called when the keys and the sort keys are not known ahead of time. /// Typically it is used when there is only one command, that is no query factoring is done. /// This method also has the option of pulling up keys and sort information. /// </summary> /// <param name="command">The owning command, used for creating VarVecs, etc</param> /// <param name="node">The root of the sub-command for which a ProviderCommandInfo should be generated</param> /// <returns>The resulting ProviderCommandInfo</returns> internal static ProviderCommandInfo Create( Command command, Node node) { return Create( command, node, new List<ProviderCommandInfo>() //children ); }
/// <summary> /// Tracks the collection vars after calling the base implementation /// </summary> /// <param name="op"></param> /// <param name="n"></param> /// <returns></returns> public override Node Visit(MultiStreamNestOp op, Node n) { var result = base.Visit(op, n); var newOp = (MultiStreamNestOp)result.Op; for (var i = 0; i < newOp.CollectionInfo.Count; i++) { m_newCollectionVarDefinitions.Add(newOp.CollectionInfo[i].CollectionVar, result.Children[i + 1]); } return result; }