/// <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;
        }
        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>
        /// 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;
        }
Beispiel #7
0
 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
        }
Beispiel #16
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>
        /// 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;
        }
Beispiel #23
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 <InternalTrees.Rule> > rules, Node subTreeRoot)
 {
     return(ApplyRulesToSubtree(context, rules, subTreeRoot, null, 0));
 }
Beispiel #24
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;
        }
Beispiel #28
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 <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;
 }
 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>
        /// 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;
        }