/// <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; }
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> /// 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; }
/// <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> /// 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; }
internal SubTreeId(RuleProcessingContext context, Node node, Node parent, int childIndex) { m_subTreeRoot = node; m_parent = parent; m_childIndex = childIndex; m_hashCode = context.GetHashCode(node); m_parentHashCode = parent == null ? 0 : context.GetHashCode(parent); }
/// <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> /// 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> /// Convert /// SingleRowOp(Project) => Project(SingleRowOp) /// </summary> /// <param name="context">Rule Processing context</param> /// <param name="singleRowNode">current subtree</param> /// <param name="newNode">transformeed subtree</param> /// <returns>transformation status</returns> private static bool ProcessSingleRowOpOverProject(RuleProcessingContext context, Node singleRowNode, out Node newNode) { newNode = singleRowNode; var projectNode = singleRowNode.Child0; var projectNodeInput = projectNode.Child0; // Simply push the SingleRowOp below the ProjectOp singleRowNode.Child0 = projectNodeInput; context.Command.RecomputeNodeInfo(singleRowNode); projectNode.Child0 = singleRowNode; newNode = projectNode; return true; // subtree modified internally }
/// <summary> /// If the ConstrainedSortOp's input is guaranteed to produce no rows, remove the ConstrainedSortOp completly: /// CSort(EmptySet) => EmptySet /// </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 ProcessConstrainedSortOpOverEmptySet(RuleProcessingContext context, Node n, out Node newNode) { var nodeInfo = (context).Command.GetExtendedNodeInfo(n.Child0); //If the input has no rows, remove the ConstraintSortOp node completly if (nodeInfo.MaxRows == RowCount.Zero) { newNode = n.Child0; return true; } newNode = n; return false; }
/// <summary> /// If the SortOp's input is guaranteed to produce at most 1 row, remove the node with the SortOp: /// Sort(X) => X, if X is guaranteed to produce no more than 1 row /// </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 ProcessSortOpOverAtMostOneRow(RuleProcessingContext context, Node n, out Node newNode) { var nodeInfo = (context).Command.GetExtendedNodeInfo(n.Child0); //If the input has at most one row, omit the SortOp if (nodeInfo.MaxRows == RowCount.Zero || nodeInfo.MaxRows == RowCount.One) { newNode = n.Child0; return true; } //Otherwise return the node as is newNode = n; return false; }
/// <summary> /// eliminates nested null casts into a single cast of the outermost cast type. /// basically the transformation applied is: cast(null[x] as T) => null[t] /// </summary> /// <param name="context"></param> /// <param name="castNullOp"></param> /// <param name="newNode">modified subtree</param> /// <returns></returns> private static bool ProcessNullCast(RuleProcessingContext context, Node castNullOp, out Node newNode) { newNode = context.Command.CreateNode(context.Command.CreateNullOp(castNullOp.Op.Type)); return true; }
/// <summary> /// Eliminate a ProjectOp that has no local definitions at all and /// no external references, (ie) if Child1 /// of the ProjectOp (the VarDefListOp child) has no children, then the ProjectOp /// is serving no useful purpose. Get rid of the ProjectOp, and replace it with its /// child /// </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 ProcessProjectWithNoLocalDefinitions(RuleProcessingContext context, Node n, out Node newNode) { newNode = n; var nodeInfo = context.Command.GetNodeInfo(n); // We cannot eliminate this node because it can break other rules, // e.g. ProcessApplyOverAnything which relies on existance of external refs to substitute // CrossApply(x, y) => CrossJoin(x, y). See SQLBU #481719. if (!nodeInfo.ExternalReferences.IsEmpty) { return false; } newNode = n.Child0; return true; }
/// <summary> /// If the ProjectOp defines some computedVars, but those computedVars are simply /// redefinitions of other Vars, then eliminate the computedVars. /// /// Project(X, VarDefList(VarDef(cv1, VarRef(v1)), ...)) /// can be transformed into /// Project(X, VarDefList(...)) /// where cv1 has now been replaced by v1 /// </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 ProcessProjectWithSimpleVarRedefinitions(RuleProcessingContext context, Node n, out Node newNode) { newNode = n; var projectOp = (ProjectOp)n.Op; if (n.Child1.Children.Count == 0) { return false; } var trc = (TransformationRulesContext)context; var command = trc.Command; var nodeInfo = command.GetExtendedNodeInfo(n); // // Check to see if any of the computed Vars defined by this ProjectOp // are simple redefinitions of other VarRefOps. Consider only those // VarRefOps that are not "external" references var canEliminateSomeVars = false; foreach (var varDefNode in n.Child1.Children) { var definingExprNode = varDefNode.Child0; if (definingExprNode.Op.OpType == OpType.VarRef) { var varRefOp = (VarRefOp)definingExprNode.Op; if (!nodeInfo.ExternalReferences.IsSet(varRefOp.Var)) { // this is a Var that we should remove canEliminateSomeVars = true; break; } } } // Did we have any redefinitions if (!canEliminateSomeVars) { return false; } // // OK. We've now identified a set of vars that are simple redefinitions. // Try and replace the computed Vars with the Vars that they're redefining // // Lets now build up a new VarDefListNode var newVarDefNodes = new List<Node>(); foreach (var varDefNode in n.Child1.Children) { var varDefOp = (VarDefOp)varDefNode.Op; var varRefOp = varDefNode.Child0.Op as VarRefOp; if (varRefOp != null && !nodeInfo.ExternalReferences.IsSet(varRefOp.Var)) { projectOp.Outputs.Clear(varDefOp.Var); projectOp.Outputs.Set(varRefOp.Var); trc.AddVarMapping(varDefOp.Var, varRefOp.Var); } else { newVarDefNodes.Add(varDefNode); } } // Note: Even if we don't have any local var definitions left, we should not remove // this project yet because: // (1) this project node may be prunning out some outputs; // (2) the rule Rule_ProjectWithNoLocalDefs, would do that later anyway. // Create a new vardeflist node, and set that as Child1 for the projectOp var newVarDefListNode = command.CreateNode(command.CreateVarDefListOp(), newVarDefNodes); n.Child1 = newVarDefListNode; return true; // some part of the subtree was modified }
/// <summary> /// We need to invoke the specified callback on the subtree in question - but only /// if the match succeeds /// </summary> /// <param name="ruleProcessingContext">Current rule processing context</param> /// <param name="node">The node (subtree) to process</param> /// <param name="newNode">the (possibly) modified subtree</param> /// <returns>true, if the subtree was modified</returns> internal bool Apply(RuleProcessingContext ruleProcessingContext, Node node, out Node newNode) { // invoke the real callback return m_nodeDelegate(ruleProcessingContext, node, out newNode); }
/// <summary> /// If the GroupByOp has no aggregates: /// /// (1) and if it includes all all the keys of the input, than it is unnecessary /// GroupBy (X, keys) -> Project(X, keys) where keys includes all keys of X. /// /// (2) else it can be turned into a Distinct: /// GroupBy (X, keys) -> Distinct(X, keys) /// /// </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 ProcessGroupByOpWithNoAggregates(RuleProcessingContext context, Node n, out Node newNode) { var command = context.Command; var op = (GroupByOp)n.Op; var nodeInfo = command.GetExtendedNodeInfo(n.Child0); var newOp = command.CreateProjectOp(op.Keys); var varDefListOp = command.CreateVarDefListOp(); var varDefListNode = command.CreateNode(varDefListOp); newNode = command.CreateNode(newOp, n.Child0, n.Child1); //If we know the keys of the input and the list of keys includes them all, // this is the result, otherwise add distinct if (nodeInfo.Keys.NoKeys || !op.Keys.Subsumes(nodeInfo.Keys.KeyVars)) { newNode = command.CreateNode(command.CreateDistinctOp(command.CreateVarVec(op.Keys)), newNode); } return true; }
/// <summary> /// Apply rules to the current subtree in a bottom-up fashion. /// </summary> /// <param name="context">Current rule processing context</param> /// <param name="rules">The look-up table with the rules to be applied</param> /// <param name="subTreeRoot">Current subtree</param> /// <param name="parent">Parent node</param> /// <param name="childIndexInParent">Index of this child within the parent</param> /// <returns>the result of the transformation</returns> private Node ApplyRulesToSubtree(RuleProcessingContext context, ReadOnlyCollection<ReadOnlyCollection<InternalTrees.Rule>> rules, Node subTreeRoot, Node parent, int childIndexInParent) { int loopCount = 0; Dictionary<SubTreeId, SubTreeId> localProcessedMap = new Dictionary<SubTreeId, SubTreeId>(); SubTreeId subTreeId; while (true) { // Am I looping forever Debug.Assert(loopCount < 12, "endless loops?"); loopCount++; // // We may need to update state regardless of whether this subTree has // changed after it has been processed last. For example, it may be // affected by transformation in its siblings due to external references. // context.PreProcessSubTree(subTreeRoot); subTreeId = new SubTreeId(context, subTreeRoot, parent, childIndexInParent); // Have I seen this subtree already? Just return, if so if (m_processedNodeMap.ContainsKey(subTreeId)) { break; } // Avoid endless loops here - avoid cycles of 2 or more if (localProcessedMap.ContainsKey(subTreeId)) { // mark this subtree as processed m_processedNodeMap[subTreeId] = subTreeId; break; } // Keep track of this one localProcessedMap[subTreeId] = subTreeId; // Walk my children for (int i = 0; i < subTreeRoot.Children.Count; i++) { subTreeRoot.Children[i] = ApplyRulesToSubtree(context, rules, subTreeRoot.Children[i], subTreeRoot, i); } // Apply rules to myself. If no transformations were performed, // then mark this subtree as processed, and break out Node newSubTreeRoot; if (!ApplyRulesToNode(context, rules, subTreeRoot, out newSubTreeRoot)) { Debug.Assert(subTreeRoot == newSubTreeRoot); // mark this subtree as processed m_processedNodeMap[subTreeId] = subTreeId; break; } context.PostProcessSubTree(subTreeRoot); subTreeRoot = newSubTreeRoot; } context.PostProcessSubTree(subTreeRoot); return subTreeRoot; }
private static bool ProcessSimplifyCase_EliminateWhenClauses( RuleProcessingContext context, CaseOp caseOp, Node caseOpNode, out Node newNode) { List<Node> newNodeArgs = null; newNode = caseOpNode; for (var i = 0; i < caseOpNode.Children.Count;) { // Special handling for the else clause if (i == caseOpNode.Children.Count - 1) { // If the else clause is a SoftCast then we do not attempt to simplify // the case operation, since this may change the result type. // This really belongs in more general SoftCastOp logic in the CTreeGenerator // that converts SoftCasts that could affect the result type of the query into // a real cast or a trivial case statement, to preserve the result type. // This is tracked by SQL PT Work Item #300003327. if (OpType.SoftCast == caseOpNode.Children[i].Op.OpType) { return false; } if (newNodeArgs != null) { newNodeArgs.Add(caseOpNode.Children[i]); } break; } // If the current then clause is a SoftCast then we do not attempt to simplify // the case operation, since this may change the result type. // Again, this really belongs in the CTreeGenerator as per SQL PT Work Item #300003327. if (OpType.SoftCast == caseOpNode.Children[i + 1].Op.OpType) { return false; } // Check to see if the when clause is a ConstantPredicate if (caseOpNode.Children[i].Op.OpType != OpType.ConstantPredicate) { if (newNodeArgs != null) { newNodeArgs.Add(caseOpNode.Children[i]); newNodeArgs.Add(caseOpNode.Children[i + 1]); } i += 2; continue; } // Found a when-clause which is a constant predicate var constPred = (ConstantPredicateOp)caseOpNode.Children[i].Op; // Create the newArgs list, if we haven't done so already if (newNodeArgs == null) { newNodeArgs = new List<Node>(); for (var j = 0; j < i; j++) { newNodeArgs.Add(caseOpNode.Children[j]); } } // If the when-clause is the "true" predicate, then we simply ignore all // the succeeding arguments. We make the "then" clause of this when-clause // as the "else-clause" of the resulting caseOp if (constPred.IsTrue) { newNodeArgs.Add(caseOpNode.Children[i + 1]); break; } else { // Otherwise, we simply skip the when-then pair PlanCompiler.Assert(constPred.IsFalse, "constant predicate must be either true or false"); i += 2; continue; } } // Did we see any changes? Simply return if (newNodeArgs == null) { return false; } // Otherwise, we did do some processing PlanCompiler.Assert(newNodeArgs.Count > 0, "new args list must not be empty"); // Is there only one expression in the args list - simply return that expression if (newNodeArgs.Count == 1) { newNode = newNodeArgs[0]; } else { newNode = context.Command.CreateNode(caseOp, newNodeArgs); } return true; }
private static bool ProcessComparisonsOverConstant(RuleProcessingContext context, Node node, out Node newNode) { newNode = node; PlanCompiler.Assert(node.Op.OpType == OpType.EQ || node.Op.OpType == OpType.NE, "unexpected comparison op type?"); bool? comparisonStatus = node.Child0.Op.IsEquivalent(node.Child1.Op); // Don't mess with nulls or with non-internal constants if (comparisonStatus == null) { return false; } var result = (node.Op.OpType == OpType.EQ) ? (bool)comparisonStatus : !((bool)comparisonStatus); var newOp = context.Command.CreateConstantPredicateOp(result); newNode = context.Command.CreateNode(newOp); return true; }
private static bool ProcessNotOverConstantPredicate(RuleProcessingContext context, Node node, out Node newNode) { return ProcessLogOpOverConstant(context, node, node.Child0, null, out newNode); }
private static bool ProcessLikeOverConstant(RuleProcessingContext context, Node n, out Node newNode) { newNode = n; var patternOp = (InternalConstantOp)n.Child1.Op; var strOp = (InternalConstantOp)n.Child0.Op; var str = (string)strOp.Value; var pattern = (string)patternOp.Value; var match = MatchesPattern((string)strOp.Value, (string)patternOp.Value); if (match == null) { return false; } var constOp = context.Command.CreateConstantPredicateOp((bool)match); newNode = context.Command.CreateNode(constOp); return true; }
/// <summary> /// Apply a set of rules to the subtree /// </summary> /// <param name="context">Rule processing context</param> /// <param name="subTreeRoot">current subtree</param> /// <returns>transformed subtree</returns> internal Node ApplyRulesToSubtree(RuleProcessingContext context, ReadOnlyCollection <ReadOnlyCollection <InternalTrees.Rule> > rules, Node subTreeRoot) { return(ApplyRulesToSubtree(context, rules, subTreeRoot, null, 0)); }
/// <summary> /// We need to invoke the specified callback on the subtree in question - but only /// if the match succeeds /// </summary> /// <param name="ruleProcessingContext">Current rule processing context</param> /// <param name="node">The node (subtree) to process</param> /// <param name="newNode">the (possibly) modified subtree</param> /// <returns>true, if the subtree was modified</returns> internal bool Apply(RuleProcessingContext ruleProcessingContext, Node node, out Node newNode) { // invoke the real callback return(m_nodeDelegate(ruleProcessingContext, node, out newNode)); }
/// <summary> /// Converts a GroupBy(Project(X, c1,..ck), agg1, agg2, .. aggm) => /// GroupBy(X, agg1', agg2', .. aggm') /// where agg1', agg2', .. aggm' are the "mapped" versions /// of agg1, agg2, .. aggm, such that the references to c1, ... ck are /// replaced by their definitions. /// /// We only do this if each c1, ..ck is refereneced (in aggregates) at most once or it is a constant. /// </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 ProcessGroupByOverProject(RuleProcessingContext context, Node n, out Node newNode) { newNode = n; var op = (GroupByOp)n.Op; var command = (context).Command; var projectNode = n.Child0; var projectNodeVarDefList = projectNode.Child1; var keys = n.Child1; var aggregates = n.Child2; // If there are any keys, we should not remove the inner project if (keys.Children.Count > 0) { return false; } //Get a list of all defining vars var projectDefinitions = command.GetExtendedNodeInfo(projectNode).LocalDefinitions; //If any of the defined vars is output, than we need the extra project anyway. if (op.Outputs.Overlaps(projectDefinitions)) { return false; } var createdNewProjectDefinitions = false; //If there are any constants remove them from the list that needs to be tested, //These can safely be replaced for (var i = 0; i < projectNodeVarDefList.Children.Count; i++) { var varDefNode = projectNodeVarDefList.Children[i]; if (varDefNode.Child0.Op.OpType == OpType.Constant || varDefNode.Child0.Op.OpType == OpType.InternalConstant || varDefNode.Child0.Op.OpType == OpType.NullSentinel) { //We shouldn't modify the original project definitions, thus we copy it // the first time we encounter a constant if (!createdNewProjectDefinitions) { projectDefinitions = command.CreateVarVec(projectDefinitions); createdNewProjectDefinitions = true; } projectDefinitions.Clear(((VarDefOp)varDefNode.Op).Var); } } if (VarRefUsageFinder.AnyVarUsedMoreThanOnce(projectDefinitions, aggregates, command)) { return false; } //If we got here it means that all vars were either constants, or used at most once // Create a dictionary to be used for remapping the keys and the aggregates var varToDefiningNode = new Dictionary<Var, Node>(projectNodeVarDefList.Children.Count); for (var j = 0; j < projectNodeVarDefList.Children.Count; j++) { var varDefNode = projectNodeVarDefList.Children[j]; var var = ((VarDefOp)varDefNode.Op).Var; varToDefiningNode.Add(var, varDefNode.Child0); } newNode.Child2 = VarRefReplacer.Replace(varToDefiningNode, aggregates, command); newNode.Child0 = projectNode.Child0; return true; }
/// <summary> /// If the else clause of the CaseOp is another CaseOp, when two can be collapsed into one. /// In particular, /// /// CASE /// WHEN W1 THEN T1 /// WHEN W2 THEN T2 ... /// ELSE (CASE /// WHEN WN1 THEN TN1, � /// ELSE E) /// /// Is transformed into /// /// CASE /// WHEN W1 THEN T1 /// WHEN W2 THEN T2 ... /// WHEN WN1 THEN TN1 ... /// ELSE E /// </summary> /// <param name="caseOp">the current caseOp</param> /// <param name="caseOpNode">current subtree</param> /// <param name="newNode">new subtree</param> /// <returns>true, if we performed a transformation</returns> private static bool ProcessFlattenCase(RuleProcessingContext context, Node caseOpNode, out Node newNode) { newNode = caseOpNode; var elseChild = caseOpNode.Children[caseOpNode.Children.Count - 1]; if (elseChild.Op.OpType != OpType.Case) { return false; } // // Flatten the case statements. // The else child is removed from the outer CaseOp op // and the else child's children are reparented to the outer CaseOp // Node info recomputation does not need to happen, the outer CaseOp // node still has the same descendants. // caseOpNode.Children.RemoveAt(caseOpNode.Children.Count - 1); caseOpNode.Children.AddRange(elseChild.Children); return true; }
/// <summary> /// Tries to remove null sentinel definitions by replacing them to vars that are guaranteed /// to be non-nullable and of integer type, or with reference to other constants defined in the /// same project. In particular, /// /// - If based on the ancestors, the value of the null sentinel can be changed and the /// input of the project has a var that is guaranteed to be non-nullable and /// is of integer type, then the definitions of the vars defined as NullSentinels in the ProjectOp /// are replaced with a reference to that var. I.eg: /// /// Project(X, VarDefList(VarDef(ns_var, NullSentinel), ...)) /// can be transformed into /// Project(X, VarDefList(VarDef(ns_var, VarRef(v))...)) /// where v is known to be non-nullable /// /// - Else, if based on the ancestors, the value of the null sentinel can be changed and /// the project already has definitions of other int constants, the definitions of the null sentinels /// are removed and the respective vars are remapped to the var representing the constant. /// /// - Else, the definitions of the all null sentinels except for one are removed, and the /// the respective vars are remapped to the remaining null sentinel. /// </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 ProcessProjectOpWithNullSentinel(RuleProcessingContext context, Node n, out Node newNode) { newNode = n; var projectOp = (ProjectOp)n.Op; var varDefListNode = n.Child1; if (varDefListNode.Children.Where(c => c.Child0.Op.OpType == OpType.NullSentinel).Count() == 0) { return false; } var trc = (TransformationRulesContext)context; var command = trc.Command; var relOpInputNodeInfo = command.GetExtendedNodeInfo(n.Child0); Var inputSentinel; var reusingConstantFromSameProjectAsSentinel = false; var canChangeNullSentinelValue = trc.CanChangeNullSentinelValue; if (!canChangeNullSentinelValue || !TransformationRulesContext.TryGetInt32Var(relOpInputNodeInfo.NonNullableDefinitions, out inputSentinel)) { reusingConstantFromSameProjectAsSentinel = true; if (!canChangeNullSentinelValue || !TransformationRulesContext.TryGetInt32Var( n.Child1.Children.Where( child => child.Child0.Op.OpType == OpType.Constant || child.Child0.Op.OpType == OpType.InternalConstant).Select( child => ((VarDefOp)(child.Op)).Var), out inputSentinel)) { inputSentinel = n.Child1.Children.Where(child => child.Child0.Op.OpType == OpType.NullSentinel).Select( child => ((VarDefOp)(child.Op)).Var).FirstOrDefault(); if (inputSentinel == null) { return false; } } } var modified = false; for (var i = n.Child1.Children.Count - 1; i >= 0; i--) { var varDefNode = n.Child1.Children[i]; var definingExprNode = varDefNode.Child0; if (definingExprNode.Op.OpType == OpType.NullSentinel) { if (!reusingConstantFromSameProjectAsSentinel) { var varRefOp = command.CreateVarRefOp(inputSentinel); varDefNode.Child0 = command.CreateNode(varRefOp); command.RecomputeNodeInfo(varDefNode); modified = true; } else if (!inputSentinel.Equals(((VarDefOp)varDefNode.Op).Var)) { projectOp.Outputs.Clear(((VarDefOp)varDefNode.Op).Var); n.Child1.Children.RemoveAt(i); trc.AddVarMapping(((VarDefOp)varDefNode.Op).Var, inputSentinel); modified = true; } } } if (modified) { command.RecomputeNodeInfo(n.Child1); } return modified; }
/// <summary> /// Apply rules to the current subtree in a bottom-up fashion. /// </summary> /// <param name="context">Current rule processing context</param> /// <param name="rules">The look-up table with the rules to be applied</param> /// <param name="subTreeRoot">Current subtree</param> /// <param name="parent">Parent node</param> /// <param name="childIndexInParent">Index of this child within the parent</param> /// <returns>the result of the transformation</returns> private Node ApplyRulesToSubtree(RuleProcessingContext context, ReadOnlyCollection <ReadOnlyCollection <InternalTrees.Rule> > rules, Node subTreeRoot, Node parent, int childIndexInParent) { int loopCount = 0; Dictionary <SubTreeId, SubTreeId> localProcessedMap = new Dictionary <SubTreeId, SubTreeId>(); SubTreeId subTreeId; while (true) { // Am I looping forever Debug.Assert(loopCount < 12, "endless loops?"); loopCount++; // // We may need to update state regardless of whether this subTree has // changed after it has been processed last. For example, it may be // affected by transformation in its siblings due to external references. // context.PreProcessSubTree(subTreeRoot); subTreeId = new SubTreeId(context, subTreeRoot, parent, childIndexInParent); // Have I seen this subtree already? Just return, if so if (m_processedNodeMap.ContainsKey(subTreeId)) { break; } // Avoid endless loops here - avoid cycles of 2 or more if (localProcessedMap.ContainsKey(subTreeId)) { // mark this subtree as processed m_processedNodeMap[subTreeId] = subTreeId; break; } // Keep track of this one localProcessedMap[subTreeId] = subTreeId; // Walk my children for (int i = 0; i < subTreeRoot.Children.Count; i++) { subTreeRoot.Children[i] = ApplyRulesToSubtree(context, rules, subTreeRoot.Children[i], subTreeRoot, i); } // Apply rules to myself. If no transformations were performed, // then mark this subtree as processed, and break out Node newSubTreeRoot; if (!ApplyRulesToNode(context, rules, subTreeRoot, out newSubTreeRoot)) { Debug.Assert(subTreeRoot == newSubTreeRoot); // mark this subtree as processed m_processedNodeMap[subTreeId] = subTreeId; break; } context.PostProcessSubTree(subTreeRoot); subTreeRoot = newSubTreeRoot; } context.PostProcessSubTree(subTreeRoot); return(subTreeRoot); }
/// <summary> /// If the GroupByOp defines some computedVars as part of its keys, but those computedVars are simply /// redefinitions of other Vars, then eliminate the computedVars. /// /// GroupBy(X, VarDefList(VarDef(cv1, VarRef(v1)), ...), VarDefList(...)) /// can be transformed into /// GroupBy(X, VarDefList(...)) /// where cv1 has now been replaced by v1 /// </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 ProcessGroupByWithSimpleVarRedefinitions(RuleProcessingContext context, Node n, out Node newNode) { newNode = n; var groupByOp = (GroupByOp)n.Op; // no local keys? nothing to do if (n.Child1.Children.Count == 0) { return false; } var trc = (TransformationRulesContext)context; var command = trc.Command; var nodeInfo = command.GetExtendedNodeInfo(n); // // Check to see if any of the computed Vars defined by this GroupByOp // are simple redefinitions of other VarRefOps. Consider only those // VarRefOps that are not "external" references // var canEliminateSomeVars = false; foreach (var varDefNode in n.Child1.Children) { var definingExprNode = varDefNode.Child0; if (definingExprNode.Op.OpType == OpType.VarRef) { var varRefOp = (VarRefOp)definingExprNode.Op; if (!nodeInfo.ExternalReferences.IsSet(varRefOp.Var)) { // this is a Var that we should remove canEliminateSomeVars = true; } } } // Did we have any redefinitions if (!canEliminateSomeVars) { return false; } // // OK. We've now identified a set of vars that are simple redefinitions. // Try and replace the computed Vars with the Vars that they're redefining // // Lets now build up a new VarDefListNode var newVarDefNodes = new List<Node>(); foreach (var varDefNode in n.Child1.Children) { var varDefOp = (VarDefOp)varDefNode.Op; var varRefOp = varDefNode.Child0.Op as VarRefOp; if (varRefOp != null && !nodeInfo.ExternalReferences.IsSet(varRefOp.Var)) { groupByOp.Outputs.Clear(varDefOp.Var); groupByOp.Outputs.Set(varRefOp.Var); groupByOp.Keys.Clear(varDefOp.Var); groupByOp.Keys.Set(varRefOp.Var); trc.AddVarMapping(varDefOp.Var, varRefOp.Var); } else { newVarDefNodes.Add(varDefNode); } } // Create a new vardeflist node, and set that as Child1 for the group by op var newVarDefListNode = command.CreateNode(command.CreateVarDefListOp(), newVarDefNodes); n.Child1 = newVarDefListNode; return true; // subtree modified }
private static bool ProcessLogOpOverConstant( RuleProcessingContext context, Node node, Node constantPredicateNode, Node otherNode, out Node newNode) { PlanCompiler.Assert(constantPredicateNode != null, "null constantPredicateOp?"); var pred = (ConstantPredicateOp)constantPredicateNode.Op; switch (node.Op.OpType) { case OpType.And: newNode = pred.IsTrue ? otherNode : constantPredicateNode; break; case OpType.Or: newNode = pred.IsTrue ? constantPredicateNode : otherNode; break; case OpType.Not: PlanCompiler.Assert(otherNode == null, "Not Op with more than 1 child. Gasp!"); newNode = context.Command.CreateNode(context.Command.CreateConstantPredicateOp(!pred.Value)); break; default: PlanCompiler.Assert(false, "Unexpected OpType - " + node.Op.OpType); newNode = null; break; } return true; }
/// <summary> /// Apply a set of rules to the subtree /// </summary> /// <param name="context">Rule processing context</param> /// <param name="subTreeRoot">current subtree</param> /// <returns>transformed subtree</returns> internal Node ApplyRulesToSubtree(RuleProcessingContext context, ReadOnlyCollection<ReadOnlyCollection<InternalTrees.Rule>> rules, Node subTreeRoot) { return ApplyRulesToSubtree(context, rules, subTreeRoot, null, 0); }
/// <summary> /// Convert an IsNull(null) to just the 'true' predicate /// </summary> /// <param name="context"></param> /// <param name="isNullNode"></param> /// <param name="newNode">new subtree</param> /// <returns></returns> private static bool ProcessIsNullOverNull(RuleProcessingContext context, Node isNullNode, out Node newNode) { newNode = context.Command.CreateNode(context.Command.CreateTrueOp()); return true; }
/// <summary> /// Convert a /// IsNull(VarRef(v)) /// to just the /// False predicate /// /// if v is guaranteed to be non nullable. /// </summary> /// <param name="context"></param> /// <param name="isNullNode"></param> /// <param name="newNode">new subtree</param> /// <returns></returns> private static bool ProcessIsNullOverVarRef(RuleProcessingContext context, Node isNullNode, out Node newNode) { var command = context.Command; var trc = (TransformationRulesContext)context; var v = ((VarRefOp)isNullNode.Child0.Op).Var; if (trc.IsNonNullable(v)) { newNode = command.CreateNode(context.Command.CreateFalseOp()); return true; } else { newNode = isNullNode; return false; } }
private static bool ProcessFilterWithConstantPredicate(RuleProcessingContext context, Node n, out Node newNode) { newNode = n; var predOp = (ConstantPredicateOp)n.Child1.Op; // If we're dealing with a "true" predicate, then simply return the RelOp // input to the filter if (predOp.IsTrue) { newNode = n.Child0; return true; } PlanCompiler.Assert(predOp.IsFalse, "unexpected non-false predicate?"); // We're dealing with a "false" predicate, then we can get rid of the // input, and replace it with a dummy project // // If the input is already a singlerowtableOp, then there's nothing // further to do // if (n.Child0.Op.OpType == OpType.SingleRowTable || (n.Child0.Op.OpType == OpType.Project && n.Child0.Child0.Op.OpType == OpType.SingleRowTable)) { return false; } var trc = (TransformationRulesContext)context; var childNodeInfo = trc.Command.GetExtendedNodeInfo(n.Child0); var varDefNodeList = new List<Node>(); var newVars = trc.Command.CreateVarVec(); foreach (var v in childNodeInfo.Definitions) { var nullConst = trc.Command.CreateNullOp(v.Type); var constNode = trc.Command.CreateNode(nullConst); Var computedVar; var varDefNode = trc.Command.CreateVarDefNode(constNode, out computedVar); trc.AddVarMapping(v, computedVar); newVars.Set(computedVar); varDefNodeList.Add(varDefNode); } // If no vars have been selected out, add a dummy var if (newVars.IsEmpty) { var nullConst = trc.Command.CreateNullOp(trc.Command.BooleanType); var constNode = trc.Command.CreateNode(nullConst); Var computedVar; var varDefNode = trc.Command.CreateVarDefNode(constNode, out computedVar); newVars.Set(computedVar); varDefNodeList.Add(varDefNode); } var singleRowTableNode = trc.Command.CreateNode(trc.Command.CreateSingleRowTableOp()); n.Child0 = singleRowTableNode; var varDefListNode = trc.Command.CreateNode(trc.Command.CreateVarDefListOp(), varDefNodeList); var projectOp = trc.Command.CreateProjectOp(newVars); var projectNode = trc.Command.CreateNode(projectOp, n, varDefListNode); projectNode.Child0 = n; newNode = projectNode; return true; }