/// <summary>
        /// Utility method that determines whether a given CaseOp subtree can be optimized.
        /// Called by both PreProcessor and NominalTypeEliminator.
        /// 
        /// If the case statement is of the shape:
        ///     case when X then NULL else Y, or
        ///     case when X then Y else NULL,
        /// where Y is of row type, and the types of the input CaseOp, the NULL and Y are the same,
        /// return true
        /// </summary>
        /// <param name="op"></param>
        /// <param name="n"></param>
        /// <returns></returns>
        internal static bool IsRowTypeCaseOpWithNullability(CaseOp op, Node n, out bool thenClauseIsNull)
        {
            thenClauseIsNull = false;  //any default value will do

            if (!TypeSemantics.IsRowType(op.Type))
            {
                return false;
            }
            if (n.Children.Count != 3)
            {
                return false;
            }

            //All three types must be equal
            if (!n.Child1.Op.Type.EdmEquals(op.Type) || !n.Child2.Op.Type.EdmEquals(op.Type))
            {
                return false;
            }

            //At least one of Child1 and Child2 needs to be a null
            if (n.Child1.Op.OpType == OpType.Null)
            {
                thenClauseIsNull = true;
                return true;
            }
            if (n.Child2.Op.OpType == OpType.Null)
            {
                // thenClauseIsNull stays false
                return true;
            }

            return false;
        }
Пример #2
0
        /// <summary>
        /// Utility method that determines whether a given CaseOp subtree can be optimized.
        /// Called by both PreProcessor and NominalTypeEliminator.
        ///
        /// If the case statement is of the shape:
        ///     case when X then NULL else Y, or
        ///     case when X then Y else NULL,
        /// where Y is of row type, and the types of the input CaseOp, the NULL and Y are the same,
        /// return true
        /// </summary>
        /// <param name="op"></param>
        /// <param name="n"></param>
        /// <returns></returns>
        internal static bool IsRowTypeCaseOpWithNullability(CaseOp op, Node n, out bool thenClauseIsNull)
        {
            thenClauseIsNull = false;  //any default value will do

            if (!TypeSemantics.IsRowType(op.Type))
            {
                return(false);
            }
            if (n.Children.Count != 3)
            {
                return(false);
            }

            //All three types must be equal
            if (!n.Child1.Op.Type.EdmEquals(op.Type) || !n.Child2.Op.Type.EdmEquals(op.Type))
            {
                return(false);
            }

            //At least one of Child1 and Child2 needs to be a null
            if (n.Child1.Op.OpType == OpType.Null)
            {
                thenClauseIsNull = true;
                return(true);
            }
            if (n.Child2.Op.OpType == OpType.Null)
            {
                // thenClauseIsNull stays false
                return(true);
            }

            return(false);
        }
Пример #3
0
        private static bool ProcessSimplifyCase(
            RuleProcessingContext context,
            System.Data.Entity.Core.Query.InternalTrees.Node caseOpNode,
            out System.Data.Entity.Core.Query.InternalTrees.Node newNode)
        {
            CaseOp op = (CaseOp)caseOpNode.Op;

            newNode = caseOpNode;
            return(ScalarOpRules.ProcessSimplifyCase_Collapse(caseOpNode, out newNode) || ScalarOpRules.ProcessSimplifyCase_EliminateWhenClauses(context, op, caseOpNode, out newNode));
        }
Пример #4
0
        public override void Visit(CaseOp op, System.Data.Entity.Core.Query.InternalTrees.Node n)
        {
            PropertyRefList propertyRefList = this.GetPropertyRefList(n);

            for (int index = 1; index < n.Children.Count - 1; index += 2)
            {
                PropertyRefList propertyRefs = propertyRefList.Clone();
                this.AddPropertyRefs(n.Children[index], propertyRefs);
            }
            this.AddPropertyRefs(n.Children[n.Children.Count - 1], propertyRefList.Clone());
            this.VisitChildren(n);
        }
Пример #5
0
 internal static bool IsRowTypeCaseOpWithNullability(
     CaseOp op,
     System.Data.Entity.Core.Query.InternalTrees.Node n,
     out bool thenClauseIsNull)
 {
     thenClauseIsNull = false;
     if (!TypeSemantics.IsRowType(op.Type) || n.Children.Count != 3 || (!n.Child1.Op.Type.EdmEquals((MetadataItem)op.Type) || !n.Child2.Op.Type.EdmEquals((MetadataItem)op.Type)))
     {
         return(false);
     }
     if (n.Child1.Op.OpType == OpType.Null)
     {
         thenClauseIsNull = true;
         return(true);
     }
     return(n.Child2.Op.OpType == OpType.Null);
 }
Пример #6
0
        /// <summary>
        /// CaseOp handling
        ///
        /// Pushes its desired properties to each of the WHEN/ELSE clauses
        /// </summary>
        /// <param name="op"></param>
        /// <param name="n"></param>
        public override void Visit(CaseOp op, Node n)
        {
            // First find the properties that my parent expects from me
            PropertyRefList pdProps = GetPropertyRefList(n);

            // push down the same properties to my then/else clauses.
            // the "when" clauses are irrelevant
            for (int i = 1; i < n.Children.Count - 1; i += 2)
            {
                PropertyRefList cdProps = pdProps.Clone();
                AddPropertyRefs(n.Children[i], cdProps);
            }
            AddPropertyRefs(n.Children[n.Children.Count - 1], pdProps.Clone());

            // Now visit the children
            VisitChildren(n);
        }
Пример #7
0
        public override void Visit(CaseOp op, Node n)
        {
            VisitScalarOpDefault(op, n);
            Assert((n.Children.Count >= 3 && n.Children.Count % 2 == 1),
                   "CaseOp: Expected odd number of arguments, and at least 3; found {0}", n.Children.Count);

            // Validate that each when statement is of type Boolean
            for (int i = 0; i < n.Children.Count - 1; i += 2)
            {
                Assert(TypeSemantics.IsBooleanType(n.Children[i].Op.Type), "Encountered a when node with a non-boolean return type");
            }

            // Ensure that the then clauses, the else clause and the result type are all the same
            for (int i = 1; i < n.Children.Count - 1; i += 2)
            {
                AssertEqualTypes(n.Op.Type, n.Children[i].Op.Type);
            }
            AssertEqualTypes(n.Op.Type, n.Children[n.Children.Count - 1].Op.Type);
        }
Пример #8
0
 public override void Visit(CaseOp op, Node n)
 {
     using (new AutoXml(this, op)) {
         int i = 0;
         while (i < n.Children.Count)
         {
             if ((i + 1) < n.Children.Count)
             {
                 using (new AutoXml(this, "when")) {
                     VisitNode(n.Children[i++]);
                 }
                 using (new AutoXml(this, "then")) {
                     VisitNode(n.Children[i++]);
                 }
             }
             else
             {
                 using (new AutoXml(this, "else")) {
                     VisitNode(n.Children[i++]);
                 }
             }
         }
     }
 }
Пример #9
0
        // <summary>
        // We don't yet support nest pullups over Case
        // </summary>
        public override Node Visit(CaseOp op, Node n)
        {
            // Make sure we don't have a child that is a nest op.
            foreach (var chi in n.Children)
            {
                if (chi.Op.OpType
                    == OpType.Collect)
                {
                    throw new NotSupportedException(Strings.ADP_NestingNotSupported(op.OpType.ToString(), chi.Op.OpType.ToString()));
                }
                else if (chi.Op.OpType
                         == OpType.VarRef)
                {
                    var refVar = ((VarRefOp)chi.Op).Var;
                    if (m_definingNodeMap.ContainsKey(refVar))
                    {
                        throw new NotSupportedException(Strings.ADP_NestingNotSupported(op.OpType.ToString(), chi.Op.OpType.ToString()));
                    }
                }
            }

            return VisitDefault(n);
        }
        /// <summary>
        ///     CaseOp
        ///     Special handling
        ///     If the case statement is of one of the following two shapes:
        ///     (1) case when X then NULL else Y, or
        ///     (2) case when X then Y else NULL,
        ///     where Y is of row type and the types of the input CaseOp, the NULL and Y are the same,
        ///     it gets rewritten into:  Y', where Y's null sentinel N' is:
        ///     (1) case when X then NULL else N, or
        ///     where N is Y's null sentinel.
        /// </summary>
        /// <param name="op"> the CaseOp </param>
        /// <param name="n"> corresponding node </param>
        /// <returns> new subtree </returns>
        public override Node Visit(CaseOp op, Node n)
        {
            // Before visiting the children, check whether the case statment can be optimized 
            bool thenClauseIsNull;
            var canSimplifyPrecheck = PlanCompilerUtil.IsRowTypeCaseOpWithNullability(op, n, out thenClauseIsNull);

            VisitChildren(n);

            if (canSimplifyPrecheck)
            {
                Node rewrittenNode;
                if (TryRewriteCaseOp(n, thenClauseIsNull, out rewrittenNode))
                {
                    return rewrittenNode;
                }
            }

            //
            // If the CaseOp returns a simple type, then we don't need to do 
            // anything special.
            //
            // Bug 480780: We must perform further processing, if the result
            // type is not a scalar
            //

            // If the CaseOp returns a collection, then we need to create a
            // new CaseOp of the new and improved collection type. Similarly
            // for enums we need to convert the result of the operation from 
            // the enum type to the underlying type of the enum type, and
            // for spatial types we must convert it to the underlying spatial union type.
            if (TypeUtils.IsCollectionType(op.Type)
                || md.TypeSemantics.IsEnumerationType(op.Type)
                || md.TypeSemantics.IsStrongSpatialType(op.Type))
            {
                var newType = GetNewType(op.Type);

                n.Op = m_command.CreateCaseOp(newType);
                return n;
            }
            else if (TypeUtils.IsStructuredType(op.Type))
            {
                // We've got a structured type, so the CaseOp is flattened out into 
                // a NewRecordOp via the FlattenCaseOp method.
                var desiredProperties = m_nodePropertyMap[n];
                var newNode = FlattenCaseOp(n, m_typeInfo.GetTypeInfo(op.Type), desiredProperties);
                return newNode;
            }
            else
            {
                return n;
            }
        }
Пример #11
0
        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;
        }
Пример #12
0
        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);
        }
Пример #13
0
        /// <summary>
        /// Flattens a CaseOp - Specifically, if the CaseOp returns a structuredtype,
        /// then the CaseOp is broken up so that we build up a "flat" record constructor
        /// for that structured type, with each argument to the record constructor being 
        /// a (scalar) CaseOp.  For example:
        /// 
        ///     Case when b1 then e1 else e2 end
        /// 
        /// gets translated into:
        /// 
        ///     RecordOp(case when b1 then e1.a else e2.a end,
        ///              case when b1 then e1.b else e2.b end,
        ///              ...)
        /// 
        /// The property extraction is optimized by producing only those properties 
        /// that have actually been requested.
        /// </summary>
        /// <param name="op">the CaseOp</param>
        /// <param name="n">Node corresponding to the CaseOp</param>
        /// <param name="typeInfo">Information about the type</param>
        /// <param name="desiredProperties">Set of properties desired</param>
        /// <returns></returns>
        private Node FlattenCaseOp(CaseOp op, Node n, TypeInfo typeInfo, PropertyRefList desiredProperties)
        {
            // Build up a type constructor - with only as many fields filled in 
            // as are desired. 
            List<md.EdmProperty> fieldTypes = new List<md.EdmProperty>();
            List<Node> fieldValues = new List<Node>();

            foreach (PropertyRef pref in typeInfo.PropertyRefList)
            {
                // Is this property desired later?
                if (!desiredProperties.Contains(pref))
                {
                    continue;
                }
                md.EdmProperty property = typeInfo.GetNewProperty(pref);

                // Build up an accessor for this property across each when/then clause
                List<Node> caseChildren = new List<Node>();
                for (int i = 0; i < n.Children.Count - 1; )
                {
                    Node whenNode = Copy(n.Children[i]);
                    caseChildren.Add(whenNode);
                    i++;

                    Node propNode = BuildAccessorWithNulls(n.Children[i], property);
                    caseChildren.Add(propNode);
                    i++;
                }
                Node elseNode = BuildAccessorWithNulls(n.Children[n.Children.Count - 1], property);
                caseChildren.Add(elseNode);

                Node caseNode = m_command.CreateNode(m_command.CreateCaseOp(md.Helper.GetModelTypeUsage(property)), caseChildren);

                fieldTypes.Add(property);
                fieldValues.Add(caseNode);
            }

            NewRecordOp newRec = m_command.CreateNewRecordOp(typeInfo.FlattenedTypeUsage, fieldTypes);
            return m_command.CreateNode(newRec, fieldValues);
        }
Пример #14
0
 /// <summary>
 /// Default processing. 
 /// In addition, if the case statement is of the shape 
 ///     case when X then NULL else Y, or
 ///     case when X then Y else NULL,
 /// where Y is of row type and the types of the input CaseOp, the NULL and Y are the same,
 /// marks that type as needing a null sentinel.
 /// This allows in NominalTypeElimination the case op to be pushed inside Y's null sentinel.
 /// </summary>
 /// <param name="op"></param>
 /// <param name="n"></param>
 /// <returns></returns>
 public override Node Visit(CaseOp op, Node n)
 {
     VisitScalarOpDefault(op, n);
     //special handling to enable optimization
     bool thenClauseIsNull;
     if (PlanCompilerUtil.IsRowTypeCaseOpWithNullability(op, n, out thenClauseIsNull))
     {
         //Add a null sentinel for the row type
         m_typesNeedingNullSentinel.Add(op.Type.EdmType.Identity);
     }
     return n;
 }
Пример #15
0
        public override void Visit(CaseOp op, Node n)
        {
            VisitScalarOpDefault(op, n);
            Assert(
                (n.Children.Count >= 3 && n.Children.Count % 2 == 1),
                "CaseOp: Expected odd number of arguments, and at least 3; found {0}", n.Children.Count);

            // Validate that each when statement is of type Boolean
            for (var i = 0; i < n.Children.Count - 1; i += 2)
            {
                Assert(TypeSemantics.IsBooleanType(n.Children[i].Op.Type), "Encountered a when node with a non-boolean return type");
            }

            // Ensure that the then clauses, the else clause and the result type are all the same
            for (var i = 1; i < n.Children.Count - 1; i += 2)
            {
                AssertEqualTypes(n.Op.Type, n.Children[i].Op.Type);
            }
            AssertEqualTypes(n.Op.Type, n.Children[n.Children.Count - 1].Op.Type);
        }
Пример #16
0
 /// <summary>
 /// Copies a CaseOp
 /// </summary>
 /// <param name="op">The Op to Copy</param>
 /// <param name="n">The Node that references the Op</param>
 /// <returns>A copy of the original Node that references a copy of the original Op</returns>
 public override Node Visit(CaseOp op, Node n)
 {
     return(CopyDefault(m_destCmd.CreateCaseOp(op.Type), n));
 }
 /// <summary>
 ///     Visitor pattern method for CaseOp
 /// </summary>
 /// <param name="op"> The CaseOp being visited </param>
 /// <param name="n"> The Node that references the Op </param>
 public virtual void Visit(CaseOp op, Node n)
 {
     VisitScalarOpDefault(op, n);
 }
Пример #18
0
        private static bool ProcessSimplifyCase_EliminateWhenClauses(
            RuleProcessingContext context,
            CaseOp caseOp,
            System.Data.Entity.Core.Query.InternalTrees.Node caseOpNode,
            out System.Data.Entity.Core.Query.InternalTrees.Node newNode)
        {
            List <System.Data.Entity.Core.Query.InternalTrees.Node> args = (List <System.Data.Entity.Core.Query.InternalTrees.Node>)null;

            newNode = caseOpNode;
            int index1 = 0;

            while (index1 < caseOpNode.Children.Count)
            {
                if (index1 == caseOpNode.Children.Count - 1)
                {
                    if (OpType.SoftCast == caseOpNode.Children[index1].Op.OpType)
                    {
                        return(false);
                    }
                    if (args != null)
                    {
                        args.Add(caseOpNode.Children[index1]);
                        break;
                    }
                    break;
                }
                if (OpType.SoftCast == caseOpNode.Children[index1 + 1].Op.OpType)
                {
                    return(false);
                }
                if (caseOpNode.Children[index1].Op.OpType != OpType.ConstantPredicate)
                {
                    if (args != null)
                    {
                        args.Add(caseOpNode.Children[index1]);
                        args.Add(caseOpNode.Children[index1 + 1]);
                    }
                    index1 += 2;
                }
                else
                {
                    ConstantPredicateOp op = (ConstantPredicateOp)caseOpNode.Children[index1].Op;
                    if (args == null)
                    {
                        args = new List <System.Data.Entity.Core.Query.InternalTrees.Node>();
                        for (int index2 = 0; index2 < index1; ++index2)
                        {
                            args.Add(caseOpNode.Children[index2]);
                        }
                    }
                    if (op.IsTrue)
                    {
                        args.Add(caseOpNode.Children[index1 + 1]);
                        break;
                    }
                    System.Data.Entity.Core.Query.PlanCompiler.PlanCompiler.Assert(op.IsFalse, "constant predicate must be either true or false");
                    index1 += 2;
                }
            }
            if (args == null)
            {
                return(false);
            }
            System.Data.Entity.Core.Query.PlanCompiler.PlanCompiler.Assert(args.Count > 0, "new args list must not be empty");
            newNode = args.Count != 1 ? context.Command.CreateNode((Op)caseOp, args) : args[0];
            return(true);
        }
        /// <summary>
        ///     CaseOp handling
        /// 
        ///     Pushes its desired properties to each of the WHEN/ELSE clauses
        /// </summary>
        /// <param name="op"> </param>
        /// <param name="n"> </param>
        public override void Visit(CaseOp op, Node n)
        {
            // First find the properties that my parent expects from me
            var pdProps = GetPropertyRefList(n);

            // push down the same properties to my then/else clauses. 
            // the "when" clauses are irrelevant
            for (var i = 1; i < n.Children.Count - 1; i += 2)
            {
                var cdProps = pdProps.Clone();
                AddPropertyRefs(n.Children[i], cdProps);
            }
            AddPropertyRefs(n.Children[n.Children.Count - 1], pdProps.Clone());

            // Now visit the children
            VisitChildren(n);
        }
Пример #20
0
 // <summary>
 // Copies a CaseOp
 // </summary>
 // <param name="op"> The Op to Copy </param>
 // <param name="n"> The Node that references the Op </param>
 // <returns> A copy of the original Node that references a copy of the original Op </returns>
 public override Node Visit(CaseOp op, Node n)
 {
     return CopyDefault(m_destCmd.CreateCaseOp(op.Type), n);
 }
Пример #21
0
        /// <summary>
        /// We don't yet support nest pullups over Case
        /// </summary>
        /// <param name="op"></param>
        /// <param name="n"></param>
        /// <returns></returns>
        public override Node Visit(CaseOp op, Node n)
        {
            // Make sure we don't have a child that is a nest op.
            foreach (Node chi in n.Children)
            {
                if (chi.Op.OpType == OpType.Collect)
                {
                    throw EntityUtil.NestingNotSupported(op, chi.Op);
                }
                else if (chi.Op.OpType == OpType.VarRef)
                {
                    Var refVar = ((VarRefOp)chi.Op).Var;
                    if (m_definingNodeMap.ContainsKey(refVar))
                    {
                        throw EntityUtil.NestingNotSupported(op, chi.Op);
                    }
                }
            }

            return VisitDefault(n);
        }