private static bool ApplyRulesToNode(
            RuleProcessingContext context, ReadOnlyCollection<ReadOnlyCollection<Rule>> rules, Node currentNode, out Node newNode)
        {
            newNode = currentNode;

            // Apply any pre-rule delegates
            context.PreProcess(currentNode);

            foreach (var 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>
        // 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;
        }
Exemple #3
0
        /// <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;
        }
        /// <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;
        }
Exemple #6
0
        private static bool ApplyRulesToNode(
            RuleProcessingContext context, ReadOnlyCollection <ReadOnlyCollection <Rule> > rules, Node currentNode, out Node newNode)
        {
            newNode = currentNode;

            // Apply any pre-rule delegates
            context.PreProcess(currentNode);

            foreach (var 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);
        }
        private Node ApplyRulesToSubtree(
            RuleProcessingContext context,
            ReadOnlyCollection <ReadOnlyCollection <Rule> > rules,
            Node subTreeRoot,
            Node parent,
            int childIndexInParent)
        {
            int num = 0;
            Dictionary <SubTreeId, SubTreeId> dictionary = new Dictionary <SubTreeId, SubTreeId>();
            SubTreeId key;

            while (true)
            {
                ++num;
                context.PreProcessSubTree(subTreeRoot);
                key = new SubTreeId(context, subTreeRoot, parent, childIndexInParent);
                if (!this.m_processedNodeMap.ContainsKey(key))
                {
                    if (!dictionary.ContainsKey(key))
                    {
                        dictionary[key] = key;
                        for (int childIndexInParent1 = 0; childIndexInParent1 < subTreeRoot.Children.Count; ++childIndexInParent1)
                        {
                            Node child = subTreeRoot.Children[childIndexInParent1];
                            if (RuleProcessor.ShouldApplyRules(child, subTreeRoot))
                            {
                                subTreeRoot.Children[childIndexInParent1] = this.ApplyRulesToSubtree(context, rules, child, subTreeRoot, childIndexInParent1);
                            }
                        }
                        Node newNode;
                        if (RuleProcessor.ApplyRulesToNode(context, rules, subTreeRoot, out newNode))
                        {
                            context.PostProcessSubTree(subTreeRoot);
                            subTreeRoot = newNode;
                        }
                        else
                        {
                            goto label_10;
                        }
                    }
                    else
                    {
                        break;
                    }
                }
                else
                {
                    goto label_12;
                }
            }
            this.m_processedNodeMap[key] = key;
            goto label_12;
label_10:
            this.m_processedNodeMap[key] = key;
label_12:
            context.PostProcessSubTree(subTreeRoot);
            return(subTreeRoot);
        }
        /// <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;
        }
Exemple #9
0
        /// <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;
        }
 private static bool ApplyRulesToNode(
     RuleProcessingContext context,
     ReadOnlyCollection <ReadOnlyCollection <Rule> > rules,
     Node currentNode,
     out Node newNode)
 {
     newNode = currentNode;
     context.PreProcess(currentNode);
     foreach (Rule rule in rules[(int)currentNode.Op.OpType])
     {
         if (rule.Match(currentNode) && rule.Apply(context, currentNode, out newNode))
         {
             context.PostProcess(newNode, rule);
             return(true);
         }
     }
     context.PostProcess(currentNode, (Rule)null);
     return(false);
 }
        private static bool ProcessJoinOverProject(RuleProcessingContext context, Node joinNode, out Node newNode)
        {
            newNode = joinNode;

            var trc = (TransformationRulesContext)context;
            var command = trc.Command;

            var joinConditionNode = joinNode.HasChild2 ? joinNode.Child2 : null;
            var varRefMap = new Dictionary<Var, int>();
            if (joinConditionNode != null
                && !trc.IsScalarOpTree(joinConditionNode, varRefMap))
            {
                return false;
            }

            Node newJoinNode;
            Node newProjectNode;

            // Now locate the ProjectOps
            var newVarSet = command.CreateVarVec();
            var varDefNodes = new List<Node>();

            //
            // Try and handle "project" on both sides only if we're not dealing with 
            // an LOJ. 
            //
            if ((joinNode.Op.OpType != OpType.LeftOuterJoin) &&
                (joinNode.Child0.Op.OpType == OpType.Project)
                &&
                (joinNode.Child1.Op.OpType == OpType.Project))
            {
                var projectOp1 = (ProjectOp)joinNode.Child0.Op;
                var projectOp2 = (ProjectOp)joinNode.Child1.Op;

                var varMap1 = trc.GetVarMap(joinNode.Child0.Child1, varRefMap);
                var varMap2 = trc.GetVarMap(joinNode.Child1.Child1, varRefMap);
                if (varMap1 == null
                    || varMap2 == null)
                {
                    return false;
                }

                if (joinConditionNode != null)
                {
                    joinConditionNode = trc.ReMap(joinConditionNode, varMap1);
                    joinConditionNode = trc.ReMap(joinConditionNode, varMap2);
                    newJoinNode = context.Command.CreateNode(joinNode.Op, joinNode.Child0.Child0, joinNode.Child1.Child0, joinConditionNode);
                }
                else
                {
                    newJoinNode = context.Command.CreateNode(joinNode.Op, joinNode.Child0.Child0, joinNode.Child1.Child0);
                }

                newVarSet.InitFrom(projectOp1.Outputs);
                foreach (var v in projectOp2.Outputs)
                {
                    newVarSet.Set(v);
                }
                var newProjectOp = command.CreateProjectOp(newVarSet);
                varDefNodes.AddRange(joinNode.Child0.Child1.Children);
                varDefNodes.AddRange(joinNode.Child1.Child1.Children);
                var varDefListNode = command.CreateNode(
                    command.CreateVarDefListOp(),
                    varDefNodes);
                newProjectNode = command.CreateNode(
                    newProjectOp,
                    newJoinNode, varDefListNode);
                newNode = newProjectNode;
                return true;
            }

            var projectNodeIdx = -1;
            var otherNodeIdx = -1;
            if (joinNode.Child0.Op.OpType
                == OpType.Project)
            {
                projectNodeIdx = 0;
                otherNodeIdx = 1;
            }
            else
            {
                PlanCompiler.Assert(joinNode.Op.OpType != OpType.LeftOuterJoin, "unexpected non-LeftOuterJoin");
                projectNodeIdx = 1;
                otherNodeIdx = 0;
            }
            var projectNode = joinNode.Children[projectNodeIdx];

            var projectOp = projectNode.Op as ProjectOp;
            var varMap = trc.GetVarMap(projectNode.Child1, varRefMap);
            if (varMap == null)
            {
                return false;
            }
            var otherChildInfo = command.GetExtendedNodeInfo(joinNode.Children[otherNodeIdx]);
            var vec = command.CreateVarVec(projectOp.Outputs);
            vec.Or(otherChildInfo.Definitions);
            projectOp.Outputs.InitFrom(vec);
            if (joinConditionNode != null)
            {
                joinConditionNode = trc.ReMap(joinConditionNode, varMap);
                joinNode.Child2 = joinConditionNode;
            }
            joinNode.Children[projectNodeIdx] = projectNode.Child0; // bypass the projectOp
            context.Command.RecomputeNodeInfo(joinNode);

            newNode = context.Command.CreateNode(projectOp, joinNode, projectNode.Child1);
            return true;
        }
        /// <summary>
        /// Convert a CrossJoin(SingleRowTable, X) or CrossJoin(X, SingleRowTable) or LeftOuterJoin(X, SingleRowTable)
        ///    into just "X"
        /// </summary>
        /// <param name="context">rule processing context</param>
        /// <param name="joinNode">the join node</param>
        /// <param name="newNode">transformed subtree</param>
        /// <returns>transformation status</returns>
        private static bool ProcessJoinOverSingleRowTable(RuleProcessingContext context, Node joinNode, out Node newNode)
        {
            newNode = joinNode;

            if (joinNode.Child0.Op.OpType
                == OpType.SingleRowTable)
            {
                newNode = joinNode.Child1;
            }
            else
            {
                newNode = joinNode.Child0;
            }
            return true;
        }
Exemple #16
0
        /// <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 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;
        }
        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;
        }
Exemple #20
0
        /// <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 <Rule> > rules,
            Node subTreeRoot, Node parent, int childIndexInParent)
        {
            var       loopCount         = 0;
            var       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 (var i = 0; i < subTreeRoot.Children.Count; i++)
                {
                    var childNode = subTreeRoot.Children[i];
                    if (ShouldApplyRules(childNode, subTreeRoot))
                    {
                        subTreeRoot.Children[i] = ApplyRulesToSubtree(context, rules, childNode, 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>
 ///     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;
 }
Exemple #22
0
 /// <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 <Rule> > rules, Node subTreeRoot)
 {
     return(ApplyRulesToSubtree(context, rules, subTreeRoot, null, 0));
 }
Exemple #23
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));
 }
Exemple #24
0
        /// <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;
        }
        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 ProcessIsNullOverAnything(RuleProcessingContext context, Node isNullNode, out Node newNode)
        {
            Debug.Assert(isNullNode.Op.OpType == OpType.IsNull);

            var command = context.Command;

            switch (isNullNode.Child0.Op.OpType)
            {
                case OpType.Cast:
                    newNode = command.CreateNode(
                        command.CreateConditionalOp(OpType.IsNull),
                        isNullNode.Child0.Child0);
                    break;
                case OpType.Function:
                    var function = ((FunctionOp)isNullNode.Child0.Op).Function;
                    newNode = PreservesNulls(function)
                        ? command.CreateNode(
                            command.CreateConditionalOp(OpType.IsNull),
                            isNullNode.Child0.Child0)
                        : isNullNode;
                    break;
                default:
                    newNode = isNullNode;
                    break;
            }

            switch (isNullNode.Child0.Op.OpType)
            {
                case OpType.Constant:
                case OpType.InternalConstant:
                case OpType.NullSentinel:
                    return ProcessIsNullOverConstant(context, newNode, out newNode);
                case OpType.Null:
                    return ProcessIsNullOverNull(context, newNode, out newNode);
                case OpType.VarRef:
                    return ProcessIsNullOverVarRef(context, newNode, out newNode);
                default:
                    return !ReferenceEquals(isNullNode, newNode);
            }
        }
        /// <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>
        /// 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<Rule>> rules,
            Node subTreeRoot, Node parent, int childIndexInParent)
        {
            var loopCount = 0;
            var 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 (var 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;
        }
Exemple #29
0
        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;
        }
        // Simplifies the following two cases:
        // (CASE WHEN condition THEN NULL ELSE constant END) IS NULL => condition
        // (CASE WHEN condition THEN constant ELSE NULL END) IS NULL => NOT condition
        private static bool ProcessIsNullOverCase(RuleProcessingContext context, Node isNullOpNode, out Node newNode)
        {
            var caseOpNode = isNullOpNode.Child0;

            if (caseOpNode.Children.Count != 3)
            {
                newNode = isNullOpNode;
                return false;
            }

            var whenNode = caseOpNode.Child0;
            var thenNode = caseOpNode.Child1;
            var elseNode = caseOpNode.Child2;

            switch (thenNode.Op.OpType)
            {
                case OpType.Null:
                    switch (elseNode.Op.OpType)
                    {
                        case OpType.Constant:
                        case OpType.InternalConstant:
                        case OpType.NullSentinel:
                            newNode = whenNode;
                            return true;
                    }
                    break;
                case OpType.Constant:
                case OpType.InternalConstant:
                case OpType.NullSentinel:
                    if (elseNode.Op.OpType == OpType.Null)
                    {
                        newNode = context.Command.CreateNode(
                            context.Command.CreateConditionalOp(OpType.Not),
                            whenNode);
                        return true;
                    }
                    break;
            }

            newNode = isNullOpNode;
            return false;
        }
        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>
        /// CrossJoin(Filter(A,p), B) => Filter(CrossJoin(A, B), p)
        /// CrossJoin(A, Filter(B,p)) => Filter(CrossJoin(A, B), p)
        /// 
        /// InnerJoin(Filter(A,p), B, c) => Filter(InnerJoin(A, B, c), p)
        /// InnerJoin(A, Filter(B,p), c) => Filter(InnerJoin(A, B, c), p)
        /// 
        /// LeftOuterJoin(Filter(A,p), B, c) => Filter(LeftOuterJoin(A, B, c), p)
        /// 
        /// Note that the predicate on the right table in a left-outer-join cannot be pulled
        /// up above the join.
        /// 
        /// </summary>
        /// <param name="context">Rule processing context</param>
        /// <param name="joinNode">Current JoinOp tree to process</param>
        /// <param name="newNode">transformed subtree</param>
        /// <returns>transformation status</returns>
        private static bool ProcessJoinOverFilter(RuleProcessingContext context, Node joinNode, out Node newNode)
        {
            newNode = joinNode;
            var trc = (TransformationRulesContext)context;
            var command = trc.Command;

            Node predicateNode = null;
            var newLeftInput = joinNode.Child0;
            // get the predicate from the first filter
            if (joinNode.Child0.Op.OpType
                == OpType.Filter)
            {
                predicateNode = joinNode.Child0.Child1;
                newLeftInput = joinNode.Child0.Child0; // bypass the filter
            }

            // get the predicate from the second filter
            var newRightInput = joinNode.Child1;
            if (joinNode.Child1.Op.OpType == OpType.Filter
                && joinNode.Op.OpType != OpType.LeftOuterJoin)
            {
                if (predicateNode == null)
                {
                    predicateNode = joinNode.Child1.Child1;
                }
                else
                {
                    predicateNode = command.CreateNode(
                        command.CreateConditionalOp(OpType.And),
                        predicateNode, joinNode.Child1.Child1);
                }
                newRightInput = joinNode.Child1.Child0; // bypass the filter
            }

            // No optimizations to perform if we can't locate the appropriate predicate
            if (predicateNode == null)
            {
                return false;
            }

            //
            // Create a new join node with the new inputs
            //
            Node newJoinNode;
            if (joinNode.Op.OpType
                == OpType.CrossJoin)
            {
                newJoinNode = command.CreateNode(joinNode.Op, newLeftInput, newRightInput);
            }
            else
            {
                newJoinNode = command.CreateNode(joinNode.Op, newLeftInput, newRightInput, joinNode.Child2);
            }

            //
            // create a new filterOp with the combined predicates, and with the 
            // newjoinNode as the input
            //
            var newFilterOp = command.CreateFilterOp();
            newNode = command.CreateNode(newFilterOp, newJoinNode, predicateNode);

            //
            // Mark this subtree so that we don't try to push filters down again
            // 
            trc.SuppressFilterPushdown(newNode);
            return true;
        }
 /// <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;
 }
Exemple #34
0
        /// <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>
        ///     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;
            }
        }
 /// <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<Rule>> rules, Node subTreeRoot)
 {
     return ApplyRulesToSubtree(context, rules, subTreeRoot, null, 0);
 }