File: System\Data\Query\PlanCompiler\ProjectionPruner.cs
Project: ndp\fx\src\DataEntity\System.Data.Entity.csproj (System.Data.Entity)
//---------------------------------------------------------------------
// <copyright file="ProjectionPruner.cs" company="Microsoft">
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//
// @owner  Microsoft
// @backupOwner Microsoft
//---------------------------------------------------------------------
 
using System;
using System.Collections.Generic;
//using System.Diagnostics; // Please use PlanCompiler.Assert instead of Debug.Assert in this class...
 
// It is fine to use Debug.Assert in cases where you assert an obvious thing that is supposed
// to prevent from simple mistakes during development (e.g. method argument validation 
// in cases where it was you who created the variables or the variables had already been validated or 
// in "else" clauses where due to code changes (e.g. adding a new value to an enum type) the default 
// "else" block is chosen why the new condition should be treated separately). This kind of asserts are 
// (can be) helpful when developing new code to avoid simple mistakes but have no or little value in 
// the shipped product. 
// PlanCompiler.Assert *MUST* be used to verify conditions in the trees. These would be assumptions 
// about how the tree was built etc. - in these cases we probably want to throw an exception (this is
// what PlanCompiler.Assert does when the condition is not met) if either the assumption is not correct 
// or the tree was built/rewritten not the way we thought it was.
// Use your judgment - if you rather remove an assert than ship it use Debug.Assert otherwise use
// PlanCompiler.Assert.
 
using System.Globalization;
using System.Text;
using System.Linq;
 
using md = System.Data.Metadata.Edm;
using cqt = System.Data.Common.CommandTrees;
using System.Data.Query.InternalTrees;
 
namespace System.Data.Query.PlanCompiler
{
 
    /// <summary>
    /// The ProjectionPruner module is responsible for eliminating unnecessary column
    /// references (and other expressions) from the query.
    ///
    /// Projection pruning logically operates in two passes - the first pass is a top-down
    /// pass where information about all referenced columns and expressions is collected
    /// (pushed down from a node to its children).
    /// 
    /// The second phase is a bottom-up phase, where each node (in response to the 
    /// information collected above) attempts to rid itself of unwanted columns and 
    /// expressions.
    /// 
    /// The two phases can be combined into a single tree walk, where for each node, the 
    /// processing is on the lines of:
    /// 
    /// - compute and push information to children (top-down)
    /// - process children
    /// - eliminate unnecessary references from myself (bottom-up)
    /// 
    /// </summary>
    internal class ProjectionPruner : BasicOpVisitorOfNode
    {
        #region Nested Classes
        /// <summary>
        /// This class tracks down the vars that are referenced in the column map
        /// </summary>
        private class ColumnMapVarTracker : ColumnMapVisitor<VarVec>
        {
            #region public methods
            /// <summary>
            /// Find all vars that were referenced in the column map. Looks for VarRefColumnMap
            /// in the ColumnMap tree, and tracks those vars
            /// 
            /// NOTE: The "vec" parameter must be supplied by the caller. The caller is responsible for
            /// clearing out this parameter (if necessary) before calling into this function
            /// </summary>
            /// <param name="columnMap">the column map to traverse</param>
            /// <param name="vec">the set of referenced columns</param>
            internal static void FindVars(ColumnMap columnMap, VarVec vec)
            {
                ColumnMapVarTracker tracker = new ColumnMapVarTracker();
                columnMap.Accept<VarVec>(tracker, vec);
                return;
            }
            #endregion
 
            #region constructors
            /// <summary>
            /// Trivial constructor
            /// </summary>
            private ColumnMapVarTracker() : base() { }
            #endregion
 
            #region overrides
            /// <summary>
            /// Handler for VarRefColumnMap. Simply adds the "var" to the set of referenced vars
            /// </summary>
            /// <param name="columnMap">the current varRefColumnMap</param>
            /// <param name="arg">the set of referenced vars so far</param>
            internal override void Visit(VarRefColumnMap columnMap, VarVec arg)
            {
                arg.Set(columnMap.Var);
                base.Visit(columnMap, arg);
            }
            #endregion
        }
        #endregion
 
        #region private state
 
        private PlanCompiler m_compilerState;
        private Command m_command { get { return m_compilerState.Command; } }
        private VarVec m_referencedVars; // the list of referenced vars in the query
 
        #endregion
 
        #region constructor
 
        /// <summary>
        /// Trivial private constructor
        /// </summary>
        /// <param name="compilerState">current compiler state</param>
        private ProjectionPruner(PlanCompiler compilerState)
        {
            m_compilerState = compilerState;
            m_referencedVars = compilerState.Command.CreateVarVec();
        }
 
        #endregion
 
        #region Process Driver
 
        /// <summary>
        /// Runs through the root node of the tree, and eliminates all
        /// unreferenced expressions
        /// </summary>
        /// <param name="compilerState">current compiler state</param>
        internal static void Process(PlanCompiler compilerState)
        {
            compilerState.Command.Root = Process(compilerState, compilerState.Command.Root);
        }
 
        /// <summary>
        /// Runs through the given subtree, and eliminates all
        /// unreferenced expressions
        /// </summary>
        /// <param name="compilerState">current compiler state</param>
        /// <param name="node">The node to be processed</param>
        /// <returns>The processed, i.e. transformed node</returns>
        internal static Node Process(PlanCompiler compilerState, Node node)
        {
            ProjectionPruner pruner = new ProjectionPruner(compilerState);
            return pruner.Process(node);
        }
 
        /// <summary>
        /// The real driver of the pruning process. Simply invokes the visitor over the input node
        /// </summary>
        /// <param name="node">The node to be processed</param>
        /// <returns>The processed node</returns>
        private Node Process(Node node)
        {
            return VisitNode(node);
        }
 
        #endregion
 
        #region misc helpers
 
        /// <summary>
        /// Adds a reference to this Var
        /// </summary>
        /// <param name="v"></param>
        private void AddReference(Var v)
        {
            m_referencedVars.Set(v);
        }
 
        /// <summary>
        /// Adds a reference to each var in a set of Vars
        /// </summary>
        /// <param name="varSet"></param>
        private void AddReference(IEnumerable<Var> varSet)
        {
            foreach (Var v in varSet)
            {
                AddReference(v);
            }
        }
 
        /// <summary>
        /// Is this Var referenced?
        /// </summary>
        /// <param name="v"></param>
        /// <returns></returns>
        private bool IsReferenced(Var v)
        {
            return m_referencedVars.IsSet(v);
        }
        /// <summary>
        /// Is this var unreferenced? Duh
        /// </summary>
        /// <param name="v"></param>
        /// <returns></returns>
        private bool IsUnreferenced(Var v)
        {
            return !IsReferenced(v);
        }
 
        /// <summary>
        /// Prunes a VarMap - gets rid of unreferenced vars from the VarMap inplace
        /// Additionally, propagates var references to the inner vars
        /// </summary>
        /// <param name="varMap"></param>
        private void PruneVarMap(VarMap varMap)
        {
            List<Var> unreferencedVars = new List<Var>();
            // build up a list of unreferenced vars
            foreach (Var v in varMap.Keys)
            {
                if (!IsReferenced(v))
                {
                    unreferencedVars.Add(v);
                }
                else
                {
                    AddReference(varMap[v]);
                }
            }
            // remove each of the corresponding entries from the varmap
            foreach (Var v in unreferencedVars)
            {
                varMap.Remove(v);
            }
        }
 
        /// <summary>
        /// Prunes a varset - gets rid of unreferenced vars from the Varset in place
        /// </summary>
        /// <param name="varSet">the varset to prune</param>
        private void PruneVarSet(VarVec varSet)
        {
            varSet.And(m_referencedVars);
        }
 
        #endregion
 
        #region Visitor Helpers
 
        /// <summary>
        /// Visits the children and recomputes the node info
        /// </summary>
        /// <param name="n">The current node</param>
        protected override void VisitChildren(Node n)
        {
            base.VisitChildren(n);
            m_command.RecomputeNodeInfo(n);
        }
 
        /// <summary>
        /// Visits the children in reverse order and recomputes the node info
        /// </summary>
        /// <param name="n">The current node</param>
        protected override void VisitChildrenReverse(Node n)
        {
            base.VisitChildrenReverse(n);
            m_command.RecomputeNodeInfo(n);
        }
        #endregion
 
        #region Visitor methods
 
        #region AncillaryOp Visitors
 
        /// <summary>
        /// VarDefListOp
        /// 
        /// Walks the children (VarDefOp), and looks for those whose Vars 
        /// have been referenced. Only those VarDefOps are visited - the 
        /// others are ignored.
        /// 
        /// At the end, a new list of children is created - with only those 
        /// VarDefOps that have been referenced
        /// </summary>
        /// <param name="op">the varDefListOp</param>
        /// <param name="n">corresponding node</param>
        /// <returns>modified node</returns>
        public override Node Visit(VarDefListOp op, Node n)
        {
 
            // NOTE: It would be nice to optimize this to only create a new node 
            //       and new list, if we needed to eliminate some arguments, but
            //       I'm not sure that the effort to eliminate the allocations 
            //       wouldn't be more expensive than the allocations themselves.
            //       It's something that we can consider if it shows up on the
            //       perf radar.
 
            // Get rid of all the children that we don't care about (ie)
            // those VarDefOp's that haven't been referenced
            List<Node> newChildren = new List<Node>();
            foreach (Node chi in n.Children)
            {
                VarDefOp varDefOp = chi.Op as VarDefOp;
                if (IsReferenced(varDefOp.Var))
                {
                    newChildren.Add(VisitNode(chi));
                }
            }
            return m_command.CreateNode(op, newChildren);
        }
 
        #endregion
 
        #region PhysicalOps
 
        /// <summary>
        /// PhysicalProjectOp
        /// 
        /// Insist that all Vars in this are required
        /// </summary>
        /// <param name="op"></param>
        /// <param name="n"></param>
        /// <returns></returns>
        public override Node Visit(PhysicalProjectOp op, Node n)
        {
            if (n == m_command.Root)
            {
                //
                // Walk the column map to find all the referenced vars
                //
                ColumnMapVarTracker.FindVars(op.ColumnMap, m_referencedVars);
                op.Outputs.RemoveAll(IsUnreferenced);
            }
            else
            {
                AddReference(op.Outputs);
            }
            // then visit the children
            VisitChildren(n);
 
            return n;
        }
 
        /// <summary>
        /// NestOps
        /// 
        /// Common handling for all NestOps. 
        /// </summary>
        /// <param name="op"></param>
        /// <param name="n"></param>
        /// <returns></returns>
        protected override Node VisitNestOp(NestBaseOp op, Node n)
        {
            // Mark all vars as needed
            AddReference(op.Outputs);
 
            // visit children. Need to do some more actually - to indicate that all 
            // vars from the children are really required. 
            VisitChildren(n);
            return n;
        }
 
        /// <summary>
        /// SingleStreamNestOp 
        /// 
        /// Insist (for now) that all Vars are required
        /// </summary>
        /// <param name="op"></param>
        /// <param name="n"></param>
        /// <returns></returns>
        public override Node Visit(SingleStreamNestOp op, Node n)
        {
            AddReference(op.Discriminator);
            return VisitNestOp(op, n);
        }
 
        /// <summary>
        /// MultiStreamNestOp
        /// 
        /// Insist (for now) that all Vars are required
        /// </summary>
        /// <param name="op"></param>
        /// <param name="n"></param>
        /// <returns></returns>
        public override Node Visit(MultiStreamNestOp op, Node n)
        {
            return VisitNestOp(op, n);
        }
 
        #endregion
 
        #region RelOp Visitors
 
        /// <summary>
        /// ApplyOps
        /// 
        /// Common handling for all ApplyOps. Visit the right child first to capture
        /// any references to the left, and then visit the left child.
        /// </summary>
        /// <param name="op"></param>
        /// <param name="n">the apply op</param>
        /// <returns>modified subtree</returns>
        protected override Node VisitApplyOp(ApplyBaseOp op, Node n)
        {
            // visit the right child first, then the left
            VisitChildrenReverse(n);
            return n;
        }
 
        /// <summary>
        /// DistinctOp
        /// 
        /// We remove all null and constant keys that are not referenced as long as 
        /// there is one key left. We add all remaining keys to the referenced list
        /// and proceed to the inputs
        /// </summary>
        /// <param name="op">the DistinctOp</param>
        /// <param name="n">Current subtree</param>
        /// <returns></returns>
        public override Node Visit(DistinctOp op, Node n)
        {
            if (op.Keys.Count > 1 && n.Child0.Op.OpType == OpType.Project)
            {
                RemoveRedundantConstantKeys(op.Keys, ((ProjectOp)n.Child0.Op).Outputs, n.Child0.Child1);
            }
            AddReference(op.Keys); // mark all keys as referenced - nothing more to do
            VisitChildren(n); // visit the children
            return n;
        }
 
        /// <summary>
        /// ElementOp
        /// 
        /// An ElementOp that is still present when Projection Prunning is invoked can only get introduced 
        /// in the TransformationRules phase by transforming an apply operation into a scalar subquery. 
        /// Such ElementOp serves as root of a defining expression of a VarDefinitionOp node and 
        /// thus what it produces is useful.
        /// </summary>
        /// <param name="op">the ElementOp</param>
        /// <param name="n">Current subtree</param>
        /// <returns></returns>
        public override Node Visit(ElementOp op, Node n)
        {
            ExtendedNodeInfo nodeInfo = m_command.GetExtendedNodeInfo(n.Child0);
            AddReference(nodeInfo.Definitions);
 
            n.Child0 = VisitNode(n.Child0); // visit the child
            m_command.RecomputeNodeInfo(n);
            return n;
        }
 
        /// <summary>
        /// FilterOp
        /// 
        /// First visit the predicate (because that may contain references to 
        /// the relop input), and then visit the relop input. No additional 
        /// processing is required
        /// </summary>
        /// <param name="op">the filterOp</param>
        /// <param name="n">current node</param>
        /// <returns></returns>
        public override Node Visit(FilterOp op, Node n)
        {
            // visit the predicate first, and then teh relop input
            VisitChildrenReverse(n);
            return n;
        }
 
        /// <summary>
        /// GroupByBase
        /// 
        /// First, we visit the vardeflist for aggregates and potentially group aggregates
        /// as they may reference keys (including constant keys). 
        /// Then we remove all null and constant keys that are not referenced as long as 
        /// there is one key left. We add all remaining key columns to the referenced list.
        /// Then we walk through the vardeflist for the keys; and finally process the relop input
        /// Once we're done, we update the "Outputs" varset - to account for any 
        /// pruned vars. The "Keys" varset will not change
        /// </summary>
        /// <param name="op">the groupbyOp</param>
        /// <param name="n">current subtree</param>
        /// <returns>modified subtree</returns>
        protected override Node VisitGroupByOp(GroupByBaseOp op, Node n)
        {
            // DevDiv#322980: Visit the vardeflist for aggregates and potentially group aggregates before removing 
            // redundant constant keys. This is because they may depend on (reference) the keys
            for (int i = n.Children.Count - 1; i >= 2; i--)
            {
                n.Children[i] = VisitNode(n.Children[i]);
            }
 
            //All constant and null keys that are not referenced can be removed
            //as long as there is at least one key left.           
            if (op.Keys.Count > 1)
            {
                RemoveRedundantConstantKeys(op.Keys, op.Outputs, n.Child1);
            }
 
            AddReference(op.Keys); // all keys are referenced
 
            //Visit the keys
            n.Children[1] = VisitNode(n.Children[1]);
 
            //Visit the input
            n.Children[0] = VisitNode(n.Children[0]);
 
            PruneVarSet(op.Outputs); // remove unnecessary vars from the outputs
 
            //SQLBUDT #543064: If there are no keys to start with
            // and none of the aggregates is referenced, the GroupBy
            // is equivalent to a SingleRowTableOp
            if (op.Keys.Count == 0 && op.Outputs.Count == 0)
            {
                return m_command.CreateNode(m_command.CreateSingleRowTableOp());
            }
 
            m_command.RecomputeNodeInfo(n);
            return n;
        }
 
        /// <summary>
        /// Helper method for removing redundant constant keys from GroupByOp and DistictOp.
        /// It only examines the keys defined in the given varDefListNode.
        /// It removes all constant and null keys that are not referenced elsewhere, 
        /// but ensuring that at least one key is left. 
        /// It should not be called with empty keyVec. 
        /// </summary>
        /// <param name="keyVec">The keys</param>
        /// <param name="outputVec">The var vec that needs to be updated along with the keys</param>
        /// <param name="varDefListNode">Var def list node for the keys </param>
        private void RemoveRedundantConstantKeys(VarVec keyVec, VarVec outputVec, Node varDefListNode)
        {
            //Find all the keys that are nulls and constants
            List<Node> constantKeys = varDefListNode.Children.Where(d => d.Op.OpType == OpType.VarDef 
                && PlanCompilerUtil.IsConstantBaseOp(d.Child0.Op.OpType)).ToList();
 
            VarVec constantKeyVars = this.m_command.CreateVarVec(constantKeys.Select(d => ((VarDefOp)d.Op).Var));
 
            //Get the list of unreferenced  constant keys
            constantKeyVars.Minus(m_referencedVars);
 
            //Remove the unreferenced constant keys 
            keyVec.Minus(constantKeyVars);
            outputVec.Minus(constantKeyVars);
 
            varDefListNode.Children.RemoveAll(c => constantKeys.Contains(c) && constantKeyVars.IsSet(((VarDefOp)c.Op).Var));
 
            //If no keys are left add one. 
            if (keyVec.Count == 0)
            {
                Node keyNode = constantKeys.First();
                Var keyVar = ((VarDefOp)keyNode.Op).Var;
                keyVec.Set(keyVar);
                outputVec.Set(keyVar);
                varDefListNode.Children.Add(keyNode);
            }
        }
 
        /// <summary>
        /// First defer to default handling for groupby nodes
        /// If all group aggregate vars are prunned out turn it into a GroupBy.
        /// </summary>
        /// <param name="op"></param>
        /// <param name="n"></param>
        /// <returns></returns>
        public override Node Visit(GroupByIntoOp op, Node n)
        {
            Node result = VisitGroupByOp(op, n);
 
            //Transform the GroupByInto into a GroupBy if all group aggregate vars were prunned out
            if (result.Op.OpType == OpType.GroupByInto && n.Child3.Children.Count == 0)
            {
                GroupByIntoOp newOp = (GroupByIntoOp)result.Op;
 
                result = m_command.CreateNode(m_command.CreateGroupByOp(newOp.Keys, newOp.Outputs),
                    result.Child0, result.Child1, result.Child2);
            }
            return result;
        }
 
        /// <summary>
        /// JoinOps
        /// 
        /// Common handling for all join ops. For all joins (other than crossjoin),
        /// we must first visit the predicate (to capture any references from it), and
        /// then visit the relop inputs. The relop inputs can be visited in any order
        /// because there can be no correlations between them
        /// For crossjoins, we simply use the default processing - visit all children
        /// ; there can be no correlations between the nodes anyway
        /// </summary>
        /// <param name="op"></param>
        /// <param name="n">Node for the join subtree</param>
        /// <returns>modified subtree</returns>
        protected override Node VisitJoinOp(JoinBaseOp op, Node n)
        {
            // Simply visit all children for a CrossJoin
            if (n.Op.OpType == OpType.CrossJoin)
            {
                VisitChildren(n);
                return n;
            }
 
            // For other joins, we first need to visit the predicate, and then the
            // other inputs
            // first visit the predicate
            n.Child2 = VisitNode(n.Child2);
            // then visit the 2 join inputs
            n.Child0 = VisitNode(n.Child0);
            n.Child1 = VisitNode(n.Child1);
            m_command.RecomputeNodeInfo(n);
 
            return n;
        }
 
        /// <summary>
        /// ProjectOp
        /// 
        /// We visit the projections first (the VarDefListOp child), and then 
        /// the input (the RelOp child) - this reverse order is necessary, since
        /// the projections need to be visited to determine if anything from
        /// the input is really needed.
        /// 
        /// The VarDefListOp child will handle the removal of unnecessary VarDefOps.
        /// On the way out, we then update our "Vars" property to reflect the Vars
        /// that have been eliminated
        /// </summary>
        /// <param name="op">the ProjectOp</param>
        /// <param name="n">the current node</param>
        /// <returns>modified subtree</returns>
        public override Node Visit(ProjectOp op, Node n)
        {
 
            // Update my Vars - to remove "unreferenced" vars. Do this before visiting 
            // the children - the outputs of the ProjectOp are only consumed by upstream
            // consumers, and if a Var has not yet been referenced, its not needed upstream
            PruneVarSet(op.Outputs);
 
            // first visit the computed expressions, then visit the input relop
            VisitChildrenReverse(n);
 
            // If there are no Vars left, then simply return my child - otherwise, 
            // return the current node
            return op.Outputs.IsEmpty ? n.Child0 : n;
        }
 
        /// <summary>
        /// ScanTableOp 
        /// 
        /// Update the list of referenced columns
        /// </summary>
        /// <param name="op"></param>
        /// <param name="n"></param>
        /// <returns></returns>
        public override Node Visit(ScanTableOp op, Node n)
        {
            PlanCompiler.Assert(!n.HasChild0, "scanTable with an input?"); // no more views
            // update the list of referenced columns in the table
            op.Table.ReferencedColumns.And(m_referencedVars);
            m_command.RecomputeNodeInfo(n);
            return n;
        }
 
        /// <summary>
        /// SetOps
        /// 
        /// Common handling for all SetOps. We first identify the "output" vars 
        /// that are referenced, and mark the corresponding "input" vars as referenced
        /// We then remove all unreferenced output Vars from the "Outputs" varset
        /// as well as from the Varmaps.
        /// Finally, we visit the children
        /// </summary>
        /// <param name="op"></param>
        /// <param name="n">current node</param>
        /// <returns></returns>
        protected override Node VisitSetOp(SetOp op, Node n)
        {
            // Prune the outputs varset, except for Intersect and Except, which require 
            // all their outputs to compare, so don't bother pruning them.
            if (OpType.Intersect == op.OpType || OpType.Except == op.OpType)
            {
                AddReference(op.Outputs);
            }
 
            PruneVarSet(op.Outputs);
 
            // Prune the varmaps. Identify which of the setOp vars have been
            // referenced, and eliminate those entries that don't show up. Additionally
            // mark all the other Vars as referenced
            foreach (VarMap varMap in op.VarMap)
            {
                PruneVarMap(varMap);
            }
 
            // Now visit the children
            VisitChildren(n);
            return n;
        }
 
        /// <summary>
        /// SortOp 
        /// 
        /// First visit the sort keys - no sort key can be eliminated. 
        /// Then process the vardeflist child (if there is one) that contains computed
        /// vars, and finally process the relop input. As before, the computedvars 
        /// and sortkeys need to be processed before the relop input
        /// </summary>
        /// <param name="op">the sortop</param>
        /// <param name="n">the current subtree</param>
        /// <returns>modified subtree</returns>
        protected override Node VisitSortOp(SortBaseOp op, Node n)
        {
            // first visit the sort keys
            foreach (InternalTrees.SortKey sk in op.Keys)
            {
                AddReference(sk.Var);
            }
            // next walk through all the computed expressions
            if (n.HasChild1)
            {
                n.Child1 = VisitNode(n.Child1);
            }
            // finally process the input
            n.Child0 = VisitNode(n.Child0);
 
            m_command.RecomputeNodeInfo(n);
            return n;
        }
 
        /// <summary>
        /// UnnestOp
        /// 
        /// Marks the unnestVar as referenced, and if there
        /// is a child, visits the child.
        /// </summary>
        /// <param name="op">the unnestOp</param>
        /// <param name="n">current subtree</param>
        /// <returns>modified subtree</returns>
        public override Node Visit(UnnestOp op, Node n)
        {
            AddReference(op.Var);
            VisitChildren(n); // visit my vardefop - defining the unnest var(if any)
            return n;
        }
 
        #endregion
 
        #region ScalarOps Visitors
 
        //
        // The only ScalarOps that need special processing are 
        //  * VarRefOp: we mark the corresponding Var as referenced
        //  * ExistsOp: We mark the (only) Var of the child ProjectOp as referenced
        //
 
        #region ScalarOps with special treatment
 
        /// <summary>
        /// VarRefOp
        /// 
        /// Mark the corresponding Var as "referenced"
        /// </summary>
        /// <param name="op">the VarRefOp</param>
        /// <param name="n">current node</param>
        /// <returns></returns>
        public override Node Visit(VarRefOp op, Node n)
        {
            AddReference(op.Var);
            return n;
        }
 
        /// <summary>
        /// ExistsOp
        /// 
        /// The child must be a ProjectOp - with exactly 1 var. Mark it as referenced
        /// </summary>
        /// <param name="op">the ExistsOp</param>
        /// <param name="n">the input node</param>
        /// <returns></returns>
        public override Node Visit(ExistsOp op, Node n)
        {
            // Ensure that the child is a projectOp, and has exactly one var. Mark 
            // that var as referenced always
            ProjectOp projectOp = (ProjectOp)n.Child0.Op;
 
            //It is enougth to reference the first output, this usually is a simple constant
            AddReference(projectOp.Outputs.First);
 
            VisitChildren(n);
            return n;
        }
        #endregion
 
        #endregion
 
        #endregion
    }
}