/// <summary>
        /// Convert a 
        ///    SingleRowOp(X) => X
        /// if X produces at most one row
        /// </summary>
        /// <param name="context">Rule Processing context</param>
        /// <param name="singleRowNode">Current subtree</param>
        /// <param name="newNode">transformed subtree</param>
        /// <returns>Transformation status</returns>
        private static bool ProcessSingleRowOpOverAnything(RuleProcessingContext context, Node singleRowNode, out Node newNode)
        {
            newNode = singleRowNode;
            var trc = (TransformationRulesContext)context;
            var childNodeInfo = context.Command.GetExtendedNodeInfo(singleRowNode.Child0);

            // If the input to this Op can produce at most one row, then we don't need the
            // singleRowOp - simply return the input
            if (childNodeInfo.MaxRows
                <= RowCount.One)
            {
                newNode = singleRowNode.Child0;
                return true;
            }

            //
            // if the current node is a FilterOp, then try and determine if the FilterOp
            // produces one row at most
            //
            if (singleRowNode.Child0.Op.OpType
                == OpType.Filter)
            {
                var predicate = new Predicate(context.Command, singleRowNode.Child0.Child1);
                if (predicate.SatisfiesKey(childNodeInfo.Keys.KeyVars, childNodeInfo.Definitions))
                {
                    childNodeInfo.MaxRows = RowCount.One;
                    newNode = singleRowNode.Child0;
                    return true;
                }
            }

            // we couldn't do anything
            return false;
        }
        /// <summary>
        /// Split up a predicate into 2 parts - the pushdown and the non-pushdown predicate. 
        /// 
        /// If the filter node has no external references *and* the "columns" parameter is null,
        /// then the entire predicate can be pushed down
        /// 
        /// We then compute the set of valid column references - if the "columns" parameter
        /// is non-null, this set is used. Otherwise, we get the definitions of the 
        /// input relop node of the filterOp, and use that.
        /// 
        /// We use this list of valid column references to identify which parts of the filter
        /// predicate can be pushed down - only those parts of the predicate that do not 
        /// reference anything beyond these columns are considered for pushdown. The rest are
        /// stuffed into the nonPushdownPredicate output parameter
        /// 
        /// </summary>
        /// <param name="command">Command object</param>
        /// <param name="filterNode">the FilterOp subtree</param>
        /// <param name="columns">(Optional) List of columns to consider for "pushdown"</param>
        /// <param name="nonPushdownPredicateNode">(output) Part of the predicate that cannot be pushed down</param>
        /// <returns>part of the predicate that can be pushed down</returns>
        private static Node GetPushdownPredicate(Command command, Node filterNode, VarVec columns, out Node nonPushdownPredicateNode)
        {
            var pushdownPredicateNode = filterNode.Child1;
            nonPushdownPredicateNode = null;
            var filterNodeInfo = command.GetExtendedNodeInfo(filterNode);
            if (columns == null
                && filterNodeInfo.ExternalReferences.IsEmpty)
            {
                return pushdownPredicateNode;
            }

            if (columns == null)
            {
                var inputNodeInfo = command.GetExtendedNodeInfo(filterNode.Child0);
                columns = inputNodeInfo.Definitions;
            }

            var predicate = new Predicate(command, pushdownPredicateNode);
            Predicate nonPushdownPredicate;
            predicate = predicate.GetSingleTablePredicates(columns, out nonPushdownPredicate);
            pushdownPredicateNode = predicate.BuildAndTree();
            nonPushdownPredicateNode = nonPushdownPredicate.BuildAndTree();
            return pushdownPredicateNode;
        }
        private void GetSingleTablePredicates(List<VarVec> tableDefinitions,
            out List<Predicate> singleTablePredicates, out Predicate otherPredicates)
        {
            singleTablePredicates = new List<Predicate>();
            foreach (VarVec vec in tableDefinitions)
            {
                singleTablePredicates.Add(new Predicate(m_command));
            }
            otherPredicates = new Predicate(m_command);
            VarVec externalRefs = m_command.CreateVarVec();

            foreach (Node part in m_parts)
            {
                NodeInfo nodeInfo = m_command.GetNodeInfo(part);

                bool singleTablePart = false;
                for (int i = 0; i < tableDefinitions.Count; i++)
                {
                    VarVec tableColumns = tableDefinitions[i];
                    if (tableColumns != null)
                    {
                        externalRefs.InitFrom(nodeInfo.ExternalReferences);
                        externalRefs.Minus(tableColumns);
                        if (externalRefs.IsEmpty)
                        {
                            singleTablePart = true;
                            singleTablePredicates[i].AddPart(part);
                            break;
                        }
                    }
                }
                if (!singleTablePart)
                {
                    otherPredicates.AddPart(part);
                }
            }
        }
        internal Predicate GetJoinPredicates(VarVec leftTableDefinitions, VarVec rightTableDefinitions,
            out Predicate otherPredicates)
        {
            Predicate joinPredicate = new Predicate(m_command);
            otherPredicates = new Predicate(m_command);

            foreach (Node part in m_parts)
            {
                Var leftTableVar;
                Var rightTableVar;

                if (Predicate.IsEquiJoinPredicate(part, leftTableDefinitions, rightTableDefinitions, out leftTableVar, out rightTableVar))
                {
                    joinPredicate.AddPart(part);
                }
                else
                {
                    otherPredicates.AddPart(part);
                }
            }
            return joinPredicate;
        }
        /// <summary>
        /// Get the set of equi-join columns from this predicate
        /// </summary>
        /// <param name="leftTableDefinitions"></param>
        /// <param name="rightTableDefinitions"></param>
        /// <param name="leftTableEquiJoinColumns"></param>
        /// <param name="rightTableEquiJoinColumns"></param>
        /// <param name="otherPredicates"></param>
        internal void GetEquiJoinPredicates(VarVec leftTableDefinitions, VarVec rightTableDefinitions,
            out List<Var> leftTableEquiJoinColumns, out List<Var> rightTableEquiJoinColumns,
            out Predicate otherPredicates)
        {
            otherPredicates = new Predicate(m_command);
            leftTableEquiJoinColumns = new List<Var>();
            rightTableEquiJoinColumns = new List<Var>();
            foreach (Node part in m_parts)
            {
                Var leftTableVar;
                Var rightTableVar;

                if (IsEquiJoinPredicate(part, leftTableDefinitions, rightTableDefinitions, out leftTableVar, out rightTableVar))
                {
                    leftTableEquiJoinColumns.Add(leftTableVar);
                    rightTableEquiJoinColumns.Add(rightTableVar);
                }
                else
                {
                    otherPredicates.AddPart(part);
                }
            }
        }
 /// <summary>
 /// Partition the current predicate into predicates that only apply 
 /// to the specified table (single-table-predicates), and others
 /// </summary>
 /// <param name="tableDefinitions">current columns defined by the table</param>
 /// <param name="otherPredicates">non-single-table predicates</param>
 /// <returns>single-table-predicates</returns>
 internal Predicate GetSingleTablePredicates(VarVec tableDefinitions, 
     out Predicate otherPredicates)
 {
     List<VarVec> tableDefinitionList = new List<VarVec>();
     tableDefinitionList.Add(tableDefinitions);
     List<Predicate> singleTablePredicateList;
     GetSingleTablePredicates(tableDefinitionList, out singleTablePredicateList, out otherPredicates);
     return singleTablePredicateList[0];
 }
        /// <summary>
        /// Convert Filter(OuterApply(X,Y), p) into 
        ///    Filter(CrossApply(X,Y), p)
        /// if "p" is not null-preserving for Y (ie) "p" does not preserve null values from Y
        /// </summary>
        /// <param name="context">Rule processing context</param>
        /// <param name="filterNode">Filter node</param>
        /// <param name="newNode">modified subtree</param>
        /// <returns>transformation status</returns>
        private static bool ProcessFilterOverOuterApply(RuleProcessingContext context, Node filterNode, out Node newNode)
        {
            newNode = filterNode;
            var applyNode = filterNode.Child0;
            var applyOp = applyNode.Op;
            var applyRightInputNode = applyNode.Child1;
            var trc = (TransformationRulesContext)context;
            var command = trc.Command;

            //
            // Check to see if the current predicate preserves nulls for the right table. 
            // If it doesn't then we can convert the outer apply into a cross-apply,
            // 
            var rightTableNodeInfo = command.GetExtendedNodeInfo(applyRightInputNode);
            var predicate = new Predicate(command, filterNode.Child1);
            if (!predicate.PreservesNulls(rightTableNodeInfo.Definitions, true))
            {
                var newApplyNode = command.CreateNode(command.CreateCrossApplyOp(), applyNode.Child0, applyRightInputNode);
                var newFilterNode = command.CreateNode(command.CreateFilterOp(), newApplyNode, filterNode.Child1);
                newNode = newFilterNode;
                return true;
            }

            return false;
        }
        private static bool ProcessFilterOverJoin(RuleProcessingContext context, Node filterNode, out Node newNode)
        {
            newNode = filterNode;
            var trc = (TransformationRulesContext)context;

            //
            // Have we shut off filter pushdown for this node? Return
            //
            if (trc.IsFilterPushdownSuppressed(filterNode))
            {
                return false;
            }

            var joinNode = filterNode.Child0;
            var joinOp = joinNode.Op;
            var leftInputNode = joinNode.Child0;
            var rightInputNode = joinNode.Child1;
            var command = trc.Command;
            var needsTransformation = false;

            //
            // If we're dealing with an outer-join, first check to see if the current 
            // predicate preserves nulls for the right table. 
            // If it doesn't then we can convert the outer join into an inner join,
            // and then continue with the rest of our processing here
            // 
            var rightTableNodeInfo = command.GetExtendedNodeInfo(rightInputNode);
            var predicate = new Predicate(command, filterNode.Child1);
            if (joinOp.OpType
                == OpType.LeftOuterJoin)
            {
                if (!predicate.PreservesNulls(rightTableNodeInfo.Definitions, true))
                {
                    joinOp = command.CreateInnerJoinOp();
                    needsTransformation = true;
                }
            }
            var leftTableInfo = command.GetExtendedNodeInfo(leftInputNode);

            //
            // Check to see if the predicate contains any "single-table-filters". In those
            // cases, we could simply push that filter down to the child. 
            // We can do this for inner joins and cross joins - for both inputs.
            // For left-outer joins, however, we can only do this for the left-side input
            // Further note that we only want to do the pushdown if it will help us - if 
            // the join input is a ScanTable (or some other cases), then it doesn't help us.
            // 
            Node leftSingleTablePredicateNode = null;
            if (leftInputNode.Op.OpType
                != OpType.ScanTable)
            {
                var leftSingleTablePredicates = predicate.GetSingleTablePredicates(leftTableInfo.Definitions, out predicate);
                leftSingleTablePredicateNode = leftSingleTablePredicates.BuildAndTree();
            }

            Node rightSingleTablePredicateNode = null;
            if ((rightInputNode.Op.OpType != OpType.ScanTable)
                &&
                (joinOp.OpType != OpType.LeftOuterJoin))
            {
                var rightSingleTablePredicates = predicate.GetSingleTablePredicates(rightTableNodeInfo.Definitions, out predicate);
                rightSingleTablePredicateNode = rightSingleTablePredicates.BuildAndTree();
            }

            //
            // Now check to see if the predicate contains some "join predicates". We can
            // add these to the existing join predicate (if any). 
            // We can only do this for inner joins and cross joins - not for LOJs
            //
            Node newJoinPredicateNode = null;
            if (joinOp.OpType == OpType.CrossJoin
                || joinOp.OpType == OpType.InnerJoin)
            {
                var joinPredicate = predicate.GetJoinPredicates(leftTableInfo.Definitions, rightTableNodeInfo.Definitions, out predicate);
                newJoinPredicateNode = joinPredicate.BuildAndTree();
            }

            //
            // Now for the dirty work. We've identified some predicates that could be pushed
            // into the left table, some predicates that could be pushed into the right table
            // and some that could become join predicates. 
            // 
            if (leftSingleTablePredicateNode != null)
            {
                leftInputNode = command.CreateNode(command.CreateFilterOp(), leftInputNode, leftSingleTablePredicateNode);
                needsTransformation = true;
            }
            if (rightSingleTablePredicateNode != null)
            {
                rightInputNode = command.CreateNode(command.CreateFilterOp(), rightInputNode, rightSingleTablePredicateNode);
                needsTransformation = true;
            }

            // Identify the new join predicate
            if (newJoinPredicateNode != null)
            {
                needsTransformation = true;
                if (joinOp.OpType
                    == OpType.CrossJoin)
                {
                    joinOp = command.CreateInnerJoinOp();
                }
                else
                {
                    PlanCompiler.Assert(joinOp.OpType == OpType.InnerJoin, "unexpected non-InnerJoin?");
                    newJoinPredicateNode = PlanCompilerUtil.CombinePredicates(joinNode.Child2, newJoinPredicateNode, command);
                }
            }
            else
            {
                newJoinPredicateNode = (joinOp.OpType == OpType.CrossJoin) ? null : joinNode.Child2;
            }

            // 
            // If nothing has changed, then just return the current node. Otherwise, 
            // we will loop forever
            //
            if (!needsTransformation)
            {
                return false;
            }

            Node newJoinNode;
            // 
            // Finally build up a new join node
            // 
            if (joinOp.OpType
                == OpType.CrossJoin)
            {
                newJoinNode = command.CreateNode(joinOp, leftInputNode, rightInputNode);
            }
            else
            {
                newJoinNode = command.CreateNode(joinOp, leftInputNode, rightInputNode, newJoinPredicateNode);
            }

            //
            // Build up a new filterNode above this join node. But only if we have a filter left
            // 
            var newFilterPredicateNode = predicate.BuildAndTree();
            if (newFilterPredicateNode == null)
            {
                newNode = newJoinNode;
            }
            else
            {
                newNode = command.CreateNode(command.CreateFilterOp(), newJoinNode, newFilterPredicateNode);
            }
            return true;
        }