NominalTypeEliminator.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / DataEntity / System / Data / Query / PlanCompiler / NominalTypeEliminator.cs / 1305376 / NominalTypeEliminator.cs

                            //---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// 
// @owner  [....]
// @backupOwner [....] 
//--------------------------------------------------------------------- 

using System; 
using System.Collections;
using System.Collections.Generic;
//using System.Diagnostics; // Please use PlanCompiler.Assert instead of Debug.Assert in this class...
using System.Globalization; 
using System.Linq;
using System.Data.Common; 
using md = System.Data.Metadata.Edm; 
using System.Data.Query.InternalTrees;
using System.Data.Query.PlanCompiler; 


namespace System.Data.Query.PlanCompiler
{ 
    /// 
    /// The goal of this module is to eliminate all references to nominal types 
    /// in the tree. Additionally, all structured types are replaced by "flat" 
    /// record types - where every field of the structured type is a scalar type.
    /// Note that UDTs are not considered to be structured types. 
    ///
    /// At the end of this phase,
    /// * there are no more nominal types in the tree
    /// * there are no more nested record types in the tree 
    /// * No Var in the tree is of an structured type
    /// * Additionally (and these follow from the statements above) 
    ///   * There are no NewInstanceOp constructors in the tree 
    ///   * There are no PropertyOp operators where the result is a structured type
    /// 
    /// This module uses information from the PropertyPushdown phase to "optimize"
    /// structured type elimination. Essentially, if we can avoid producing pieces
    /// of information that will be discarded later, then lets do that.
    /// 
    /// The general mechanism of type elimination is as follows. We walk up the tree
    /// in a bottom up fashion, and try to convert all structured types into flattened 
    /// record types - type constructors are first converted into flat record constructors 
    /// and then dismantled etc. The barrier points - Vars - are all converted into
    /// scalar types, and all intermediate stages will be eliminated in transition. 
    ///
    /// The output from this phase includes a ColumnMap - which is used later by
    /// the execution model to produce results in the right form from an otherwise
    /// flat query 
    ///
    /// Notes: This phase could be combined later with the PropertyPushdown phase 
    /// 
    /// 
    internal class NominalTypeEliminator : BasicOpVisitorOfNode 
    {

        #region Nested Classes
        ///  
        /// Describes an operation kind - for various property extractions
        ///  
        internal enum OperationKind 
        {
            ///  
            /// Comparing two instances for equality
            /// 
            Equality,
 
            /// 
            /// Checking to see if an instance is null 
            ///  
            IsNull,
 
            /// 
            /// Getting the "identity" of an entity
            /// 
            GetIdentity, 

            ///  
            /// Getting the keys of an entity 
            /// 
            GetKeys, 

            /// 
            /// All properties of an entity
            ///  
            All
        } 
        #endregion 

        #region private state 

        private Dictionary m_varPropertyMap;
        private Dictionary m_nodePropertyMap;
        private VarInfoMap m_varInfoMap; 
        private PlanCompiler m_compilerState;
        private Command m_command { get { return m_compilerState.Command; } } 
        private StructuredTypeInfo m_typeInfo; 
        private Dictionary m_typeToNewTypeMap;
        private const string PrefixMatchCharacter = "%"; // This is ANSI-SQL defined, but it should probably be configurable. 

        #endregion

        #region constructors 

        private NominalTypeEliminator(PlanCompiler compilerState, 
            StructuredTypeInfo typeInfo, 
            Dictionary varPropertyMap,
            Dictionary nodePropertyMap) 
        {
            m_compilerState = compilerState;
            m_typeInfo = typeInfo;
            m_varPropertyMap = varPropertyMap; 
            m_nodePropertyMap = nodePropertyMap;
            m_varInfoMap = new VarInfoMap(); 
            m_typeToNewTypeMap = new Dictionary(TypeUsageEqualityComparer.Instance); 
        }
 
        #endregion

        #region Process Driver
 
        /// 
        /// Eliminates all structural types from the query 
        ///  
        /// current compiler state
        /// list of all referenced types 
        /// list of referenced entitysets
        internal static void Process(PlanCompiler compilerState,
            StructuredTypeInfo structuredTypeInfo)
        { 
#if DEBUG
            //string phase0 = Dump.ToXml(compilerState.Command); 
            Validator.Validate(compilerState); 
#endif
 
            // Phase 1: Top-down property pushdown
            Dictionary varPropertyMap;
            Dictionary nodePropertyMap;
            PropertyPushdownHelper.Process(compilerState.Command, structuredTypeInfo, out varPropertyMap, out nodePropertyMap); 

#if DEBUG 
            //string phase1 = Dump.ToXml(compilerState.Command); 
            Validator.Validate(compilerState);
#endif 

            // Phase 2: actually eliminate nominal types
            NominalTypeEliminator nte = new NominalTypeEliminator(compilerState, structuredTypeInfo,
                varPropertyMap, nodePropertyMap); 
            nte.Process();
 
#if DEBUG 
            //string phase2 = Dump.ToXml(compilerState.Command);
            Validator.Validate(compilerState); 
#endif

#if DEBUG
            //To avoid garbage collection 
            //int size = phase0.Length;
            //size = phase1.Length; 
            //size = phase2.Length; 
#endif
        } 


        /// 
        /// The real driver. Invokes the visitor to traverse the tree bottom-up, 
        /// and modifies the tree along the way.
        ///  
        private void Process() 
        {
            Node rootNode = m_command.Root; 
            PlanCompiler.Assert(rootNode.Op.OpType == OpType.PhysicalProject, "root node is not PhysicalProjectOp?");
            // invoke the visitor on the root node
            rootNode.Op.Accept(this, rootNode);
        } 

        #endregion 
 
        #region type utilities
 
        /// 
        /// The datatype of the typeid property
        /// 
        private md.TypeUsage DefaultTypeIdType 
        {
            get { return m_command.StringType; } 
        } 

        ///  
        /// Get the "new" type corresponding to the input type.
        /// For structured types, we simply look up the typeInfoMap
        /// For collection types, we create a new collection type based on the
        ///   "new" element type. 
        /// For all other types, we simply return the input type
        ///  
        ///  
        /// 
        private md.TypeUsage GetNewType(md.TypeUsage type) 
        {
            md.TypeUsage newType;

            if (m_typeToNewTypeMap.TryGetValue(type, out newType)) 
            {
                return newType; 
            } 

            md.CollectionType collectionType; 
            if (TypeHelpers.TryGetEdmType(type, out collectionType))
            {
                // If this is a collection type, then clone a new collection type
                md.TypeUsage newElementType = GetNewType(collectionType.TypeUsage); 
                newType = TypeUtils.CreateCollectionType(newElementType);
            } 
            else if (TypeUtils.IsStructuredType(type)) 
            {
                // structured type => we've already calculated the input 
                newType = m_typeInfo.GetTypeInfo(type).FlattenedTypeUsage;
            }
            else
            { 
                // "simple" type => return the input type
                newType = type; 
            } 

            // Add this information to the map 
            m_typeToNewTypeMap[type] = newType;
            return newType;
        }
 
        #endregion
 
        #region misc utilities 

        ///  
        /// This function builds a "property accessor" over the input expression.  It
        /// can produce one of three results:
        ///
        ///   - It can return "null", if it is convinced that the input has no 
        ///     such expression
        ///   - It can return a subnode of the input, if that subnode represents 
        ///     the property 
        ///   - Or, it can build a PropertyOp explicitly
        /// 
        /// Assertion: the property is not a structured type
        /// 
        /// The input expression
        /// The desired property 
        /// 
        private Node BuildAccessor(Node input, md.EdmProperty property) 
        { 
            Op inputOp = input.Op;
 
            // Special handling if the input is a NewRecordOp
            NewRecordOp newRecordOp = inputOp as NewRecordOp;
            if (null != newRecordOp)
            { 
                int fieldPos;
                // Identify the specific property we're interested in. 
                if (newRecordOp.GetFieldPosition(property, out fieldPos)) 
                {
                    return Copy(input.Children[fieldPos]); 
                }
                else
                {
                    return null; 
                }
            } 
 
            // special handling if the input is a null
            if (inputOp.OpType == OpType.Null) 
            {
                return null;
            }
 
            // The default case: Simply return a new PropertyOp
            PropertyOp newPropertyOp = m_command.CreatePropertyOp(property); 
            return m_command.CreateNode(newPropertyOp, this.Copy(input)); 
        }
 
        /// 
        /// A BuildAccessor variant. If the appropriate property was not found, then
        /// build up a null constant instead
        ///  
        /// 
        ///  
        ///  
        private Node BuildAccessorWithNulls(Node input, md.EdmProperty property)
        { 
            Node newNode = this.BuildAccessor(input, property);
            if (newNode == null)
            {
                newNode = CreateNullConstantNode(md.Helper.GetModelTypeUsage(property)); 
            }
            return newNode; 
        } 

        ///  
        /// Builds up an accessor to the typeid property. If the type has no typeid
        /// property, then we simply create a constantOp with the corresponding
        /// typeid value for the type
        ///  
        /// the input expression
        /// the original type of the input expression 
        ///  
        private Node BuildTypeIdAccessor(Node input, TypeInfo typeInfo)
        { 
            Node result;

            if (typeInfo.HasTypeIdProperty)
            { 
                result = BuildAccessorWithNulls(input, typeInfo.TypeIdProperty);
            } 
            else 
            {
                result = CreateTypeIdConstant(typeInfo); 
            }

            return result;
        } 

        ///  
        /// Builds a SoftCast operator over the input - if one is necessary. 
        /// 
        /// the input expression to "cast" 
        /// the target type
        /// the "cast"ed expression
        private Node BuildSoftCast(Node node, md.TypeUsage targetType)
        { 
            PlanCompiler.Assert(node.Op.IsScalarOp, "Attempting SoftCast around non-ScalarOp?");
            if (Command.EqualTypes(node.Op.Type, targetType)) 
            { 
                return node;
            } 
            // Skip any castOps we may have created already
            while (node.Op.OpType == OpType.SoftCast)
            {
                node = node.Child0; 
            }
            Node newNode = m_command.CreateNode(m_command.CreateSoftCastOp(targetType), node); 
            return newNode; 
        }
 
        /// 
        /// Clones a subtree.
        /// This is used by the "BuildAccessor" routines to build a property-accessor
        /// over some input. If we're reusing the input, the input must be cloned. 
        /// 
        /// The subtree to copy 
        ///  
        private Node Copy(Node n)
        { 
            return OpCopier.Copy(m_command, n);
        }

        ///  
        /// Returns a node for a null constant of the desired type
        ///  
        ///  
        /// 
        private Node CreateNullConstantNode(md.TypeUsage type) 
        {
            return m_command.CreateNode(m_command.CreateNullOp(type));
        }
 
        /// 
        /// Create a node to represent nullability. 
        ///  
        /// Node for the typeid constant
        private Node CreateNullSentinelConstant() 
        {
            NullSentinelOp op = m_command.CreateNullSentinelOp();
            return m_command.CreateNode(op);
        } 

        ///  
        /// Create a node to represent the exact value of the typeid constant 
        /// 
        /// The current type 
        /// Node for the typeid constant
        private Node CreateTypeIdConstant(TypeInfo typeInfo)
        {
            object value = typeInfo.TypeId; 
            md.TypeUsage typeIdType;
            if (typeInfo.RootType.DiscriminatorMap != null) 
            { 
                typeIdType = md.Helper.GetModelTypeUsage(typeInfo.RootType.DiscriminatorMap.DiscriminatorProperty);
            } 
            else
            {
                typeIdType = DefaultTypeIdType;
            } 
            InternalConstantOp op = m_command.CreateInternalConstantOp(typeIdType, value);
            return m_command.CreateNode(op); 
        } 

        ///  
        /// Create a node to represent a typeid constant for a prefix match.
        /// If the typeid value were "123X", then we would generate a constant
        /// like "123X%"
        ///  
        /// the current type
        /// Node for the typeid constant 
        private Node CreateTypeIdConstantForPrefixMatch(TypeInfo typeInfo) 
        {
            string value = typeInfo.TypeId + PrefixMatchCharacter; 
            InternalConstantOp op = m_command.CreateInternalConstantOp(DefaultTypeIdType, value);
            return m_command.CreateNode(op);
        }
 
        /// 
        /// Identify the list of property refs for comparison and isnull semantics 
        ///  
        /// 
        ///  
        /// 
        private IEnumerable GetPropertyRefsForComparisonAndIsNull(TypeInfo typeInfo, OperationKind opKind)
        {
            PlanCompiler.Assert(opKind == OperationKind.IsNull || opKind == OperationKind.Equality, 
                "Unexpected opKind: " + opKind + "; Can only handle IsNull and Equality");
 
            md.TypeUsage currentType = typeInfo.Type; 

            md.RowType recordType = null; 
            if (TypeHelpers.TryGetEdmType(currentType, out recordType))
            {
                if (opKind == OperationKind.IsNull && typeInfo.HasNullSentinelProperty)
                { 
                    yield return NullSentinelPropertyRef.Instance;
                } 
                else 
                    foreach (md.EdmProperty m in recordType.Properties)
                    { 
                        if (!TypeUtils.IsStructuredType(md.Helper.GetModelTypeUsage(m)))
                        {
                            yield return new SimplePropertyRef(m);
                        } 
                        else
                        { 
                            TypeInfo nestedTypeInfo = m_typeInfo.GetTypeInfo(md.Helper.GetModelTypeUsage(m)); 
                            foreach (PropertyRef p in GetPropertyRefs(nestedTypeInfo, opKind))
                            { 
                                PropertyRef nestedPropertyRef = p.CreateNestedPropertyRef(m);
                                yield return nestedPropertyRef;
                            }
                        } 
                    }
                yield break; 
            } 

            md.EntityType entityType = null; 
            if (TypeHelpers.TryGetEdmType(currentType, out entityType))
            {
                if (opKind == OperationKind.Equality ||
                    (opKind == OperationKind.IsNull && !typeInfo.HasTypeIdProperty)) 
                {
                    foreach (PropertyRef p in typeInfo.GetIdentityPropertyRefs()) 
                    { 
                        yield return p;
                    } 
                }
                else
                {
                    yield return TypeIdPropertyRef.Instance; 
                }
                yield break; 
            } 

            md.ComplexType complexType = null; 
            if (TypeHelpers.TryGetEdmType(currentType, out complexType))
            {
                PlanCompiler.Assert(opKind == OperationKind.IsNull, "complex types not equality-comparable");
                PlanCompiler.Assert(typeInfo.HasNullSentinelProperty, "complex type with no null sentinel property: can't handle isNull"); 
                yield return NullSentinelPropertyRef.Instance;
                yield break; 
            } 

            md.RefType refType = null; 
            if (TypeHelpers.TryGetEdmType(currentType, out refType))
            {
                foreach (PropertyRef p in typeInfo.GetAllPropertyRefs())
                { 
                    yield return p;
                } 
                yield break; 
            }
 
            PlanCompiler.Assert(false, "Unknown type");
        }

        ///  
        /// Get the list of "desired" propertyrefs for the specified type and operation
        ///  
        ///  
        /// 
        ///  
        private IEnumerable GetPropertyRefs(TypeInfo typeInfo, OperationKind opKind)
        {
            PlanCompiler.Assert(opKind != OperationKind.All, "unexpected attempt to GetPropertyRefs(...,OperationKind.All)");
            if (opKind == OperationKind.GetKeys) 
            {
                return typeInfo.GetKeyPropertyRefs(); 
            } 
            else if (opKind == OperationKind.GetIdentity)
            { 
                return typeInfo.GetIdentityPropertyRefs();
            }
            else
            { 
                return GetPropertyRefsForComparisonAndIsNull(typeInfo, opKind);
            } 
        } 

        ///  
        /// Get a list of "desired" properties for each operationKind (specified by the opKind
        /// parameter). The OpKinds we support are
        ///
        ///  * GetKeys 
        ///    Applies only to entity and ref types - gets the key properties (more specifically
        ///      the flattened equivalents) 
        ///  * GetIdentity 
        ///    Applies only to entity and ref types - gets the entityset id property first, and then the
        ///      the Key properties 
        ///  * All
        ///    Gets all properties of the flattened type
        ///
        ///  * Equality 
        ///    Scalar types - the entire instance
        ///    Entity - the identity properties 
        ///    Ref - all properties (= identity properties) 
        ///    Complex/Collection - Not supported
        ///    Record - recurse over each property 
        ///
        ///  * IsNull
        ///    Scalar types - entire instance
        ///    Entity - typeid property, if it exists; otherwise, the key properties 
        ///    ComplexType - typeid property
        ///    Ref - all properties 
        ///    Collection - not supported 
        ///    Record - recurse over each property
        ///  
        /// Type information for the current op
        /// Current operation kind
        /// List of desired properties
        private IEnumerable GetProperties(TypeInfo typeInfo, OperationKind opKind) 
        {
            if (opKind == OperationKind.All) 
            { 
                foreach (md.EdmProperty p in typeInfo.GetAllProperties())
                { 
                    yield return p;
                }
            }
            else 
            {
                foreach (PropertyRef p in GetPropertyRefs(typeInfo, opKind)) 
                { 
                    yield return typeInfo.GetNewProperty(p);
                } 
            }
        }

        ///  
        /// Get a list of properties and value (expressions) for each desired property of the
        /// input. The list of desired properties is based on the opKind parameter. 
        /// The ignoreMissingProperties indicates if we should create a null constant, in case 
        /// the input cannot produce the specified property
        ///  
        /// typeinfo for the input
        /// Current operation kind
        /// The input expression tree
        /// Should we ignore missing properties 
        /// Output: list of properties
        /// Output: correspondng list of values 
        private void GetPropertyValues(TypeInfo typeInfo, OperationKind opKind, Node input, bool ignoreMissingProperties, 
            out List properties, out List values)
        { 

            values = new List();
            properties = new List();
            foreach (md.EdmProperty prop in GetProperties(typeInfo, opKind)) 
            {
                KeyValuePair kv = GetPropertyValue(input, prop, ignoreMissingProperties); 
                if (kv.Value != null) 
                {
                    properties.Add(kv.Key); 
                    values.Add(kv.Value);
                }
            }
        } 

        ///  
        /// Build up a key-value pair of (property, expression) to represent 
        /// the extraction of the appropriate property from the input expression
        ///  
        /// The input (structured type) expression
        /// The property in question
        /// should we ignore missing properties
        ///  
        private KeyValuePair GetPropertyValue(Node input, md.EdmProperty property, bool ignoreMissingProperties)
        { 
            Node n = null; 

            if (!ignoreMissingProperties) 
            {
                n = BuildAccessorWithNulls(input, property);
            }
            else 
            {
                n = BuildAccessor(input, property); 
            } 
            return new KeyValuePair(property, n);
        } 

        /// 
        /// Walk the SortKeys, and expand out
        /// any Structured type Var references 
        /// If any of the sort keys is expanded to include a var representing a null sentinel,
        /// set PlanCompiler.HasSortingOnNullSentinels to true. 
        ///  
        /// The list of input keys
        /// An expanded list of keys. If there is nothing to expand it returns the original list. 
        private List HandleSortKeys(List keys)
        {
            List newSortKeys = new List();
            bool modified = false; 
            foreach (InternalTrees.SortKey k in keys)
            { 
                VarInfo varInfo; 
                if (!m_varInfoMap.TryGetVarInfo(k.Var, out varInfo))
                { 
                    newSortKeys.Add(k);
                }
                else
                { 
                    StructuredVarInfo structuredVarInfo = varInfo as StructuredVarInfo;
                    if (structuredVarInfo != null && structuredVarInfo.NewVarsIncludeNullSentinelVar) 
                    { 
                        m_compilerState.HasSortingOnNullSentinels = true;
                    } 

                    foreach (Var v in varInfo.NewVars)
                    {
                        InternalTrees.SortKey newKey = Command.CreateSortKey(v, k.AscendingSort, k.Collation); 
                        newSortKeys.Add(newKey);
                    } 
                    modified = true; 
                }
            } 

            List result = modified ? newSortKeys : keys;
            return result;
        } 
        #endregion
 
        #region Visitor methods 

        #region AncillaryOp Visitors 

        /// 
        /// VarDefListOp
        /// 
        /// Walks each VarDefOp child, and "expands" it out if the Var is a
        /// structured type. 
        /// 
        /// For each Var that is expanded, a new expression is created to compute
        /// its value (from the original computed expression) 
        /// A new VarDefListOp is created to hold all the "expanded" Varlist
        /// 
        /// 
        ///  
        /// 
        public override Node Visit(VarDefListOp op, Node n) 
        { 
            VisitChildren(n);
 
            List newChildren = new List();

            foreach (Node chi in n.Children)
            { 
                VarDefOp varDefOp = chi.Op as VarDefOp;
 
                if (TypeUtils.IsStructuredType(varDefOp.Var.Type) 
                 || TypeUtils.IsCollectionType(varDefOp.Var.Type))
                { 
                    List newChiList;
                    md.TypeUsage x;

                    FlattenComputedVar((ComputedVar)varDefOp.Var, chi, out newChiList, out x); 

                    foreach (Node newChi in newChiList) 
                    { 
                        newChildren.Add(newChi);
                    } 
                }
                else
                {
                    newChildren.Add(chi); 
                }
            } 
            Node newVarDefListNode = m_command.CreateNode(n.Op, newChildren); 
            return newVarDefListNode;
        } 

        /// 
        /// Helps flatten out a computedVar expression
        ///  
        /// The Var
        /// Subtree rooted at the VarDefOp expression 
        /// list of new nodes produced 
        /// 
        /// VarInfo for this var 
        private void FlattenComputedVar(ComputedVar v, Node node, out List newNodes, out md.TypeUsage newType)
        {
            newNodes = new List();
            Node definingExprNode = node.Child0; // defining expression for the VarDefOp 
            newType = null;
 
            if (TypeUtils.IsCollectionType(v.Type)) 
            {
                newType = GetNewType(v.Type); 
                Var newVar;
                Node newVarDefNode = m_command.CreateVarDefNode(definingExprNode, out newVar);
                newNodes.Add(newVarDefNode);
                m_varInfoMap.CreateCollectionVarInfo(v, newVar); 
                return;
            } 
 
            // Get the "new" type for the Var
            TypeInfo typeInfo = m_typeInfo.GetTypeInfo(v.Type); 
            // Get a list of properties that we think are necessary
            PropertyRefList desiredProperties = m_varPropertyMap[v];
            List newVars = new List();
            List newProps = new List(); 
            newNodes = new List();
            var hasNullSentinelVar = false; 
            foreach (PropertyRef p in typeInfo.PropertyRefList) 
            {
                // do I care for this property? 
                if (!desiredProperties.Contains(p))
                {
                    continue;
                } 

                md.EdmProperty newProperty = typeInfo.GetNewProperty(p); 
 
                //
                // #479467 - Make sure that we build Vars for all properties - if 
                // we are asked to produce all properties. This is extremely important
                // for the top-level Vars
                //
                Node propAccessor = null; 
                if (desiredProperties.AllProperties)
                { 
                    propAccessor = BuildAccessorWithNulls(definingExprNode, newProperty); 
                }
                else 
                {
                    propAccessor = BuildAccessor(definingExprNode, newProperty);
                    if (propAccessor == null)
                    { 
                        continue;
                    } 
                } 

                // Add the new property 
                newProps.Add(newProperty);

                // Create a new VarDefOp.
                Var newVar; 
                Node newVarDefNode = m_command.CreateVarDefNode(propAccessor, out newVar);
                newNodes.Add(newVarDefNode); 
                newVars.Add(newVar); 

                // Check if it is a null sentinel var 
                if (!hasNullSentinelVar && IsNullSentinelPropertyRef(p))
                {
                    hasNullSentinelVar = true;
                } 
            }
            m_varInfoMap.CreateStructuredVarInfo(v, typeInfo.FlattenedType, newVars, newProps, hasNullSentinelVar); 
            return; 
        }
 
        /// 
        /// Is the given propertyRef representing a null sentinel
        /// It is if:
        ///  - it is a NullSentinelPropertyRef 
        ///  - it is a NestedPropertyRef with the outer property being a NullSentinelPropertyRef
        ///  
        ///  
        /// 
        private bool IsNullSentinelPropertyRef(PropertyRef propertyRef) 
        {
            if (propertyRef is NullSentinelPropertyRef)
            {
                return true; 
            }
            NestedPropertyRef nestedPropertyRef = propertyRef as NestedPropertyRef; 
            if (nestedPropertyRef == null) 
            {
                return false; 
            }
            return nestedPropertyRef.OuterProperty is NullSentinelPropertyRef;
        }
 
        #endregion
 
        #region PhysicalOp Visitors 

        ///  
        /// PhysicalProjectOp
        /// 
        /// 
        ///  
        /// 
        public override Node Visit(PhysicalProjectOp op, Node n) 
        { 
            // visit my children
            VisitChildren(n); 

            // flatten out the varset
            VarList newVarList = FlattenVarList(op.Outputs);
            // reflect changes into my column map 
            SimpleCollectionColumnMap newColumnMap = ExpandColumnMap(op.ColumnMap);
            PhysicalProjectOp newOp = m_command.CreatePhysicalProjectOp(newVarList, newColumnMap); 
            n.Op = newOp; 

            return n; 
        }

        private SimpleCollectionColumnMap ExpandColumnMap(SimpleCollectionColumnMap columnMap)
        { 
            VarRefColumnMap varRefColumnMap = columnMap.Element as VarRefColumnMap;
            PlanCompiler.Assert(varRefColumnMap != null, "Encountered a SimpleCollectionColumnMap element that is not VarRefColumnMap when expanding a column map in NominalTypeEliminator."); 
 
            // see if this var has changed in some fashion
            VarInfo varInfo; 
            if (!m_varInfoMap.TryGetVarInfo(varRefColumnMap.Var, out varInfo))
            {
                return columnMap; // no changes
            } 

            // 
            // Ensure that we get the right number of Vars - we need one Var for 
            // each scalar property
            // 
            if (TypeUtils.IsStructuredType(varRefColumnMap.Var.Type))
            {
                TypeInfo typeInfo = m_typeInfo.GetTypeInfo(varRefColumnMap.Var.Type);
                PlanCompiler.Assert(typeInfo.RootType.FlattenedType.Properties.Count == varInfo.NewVars.Count, 
                    "Var count mismatch; Expected " + typeInfo.RootType.FlattenedType.Properties.Count + "; got " + varInfo.NewVars.Count + " instead.");
            } 
 
            // "Process" this columnMap
            ColumnMapProcessor processor = new ColumnMapProcessor(varRefColumnMap, varInfo, m_typeInfo); 
            ColumnMap newColumnMap = processor.ExpandColumnMap();

            //Wrap it with a collection
            SimpleCollectionColumnMap resultColumnMap = new SimpleCollectionColumnMap(TypeUtils.CreateCollectionType(newColumnMap.Type), newColumnMap.Name, newColumnMap, columnMap.Keys, columnMap.ForeignKeys); 

            return resultColumnMap; 
        } 

        #endregion 

        #region RelOp Visitors

        ///  
        /// Walk the input var sequence, flatten each var, and return the new sequence of
        /// Vars 
        ///  
        /// input Var sequence
        /// flattened output var sequence 
        private IEnumerable FlattenVars(IEnumerable vars)
        {
            foreach (Var v in vars)
            { 
                VarInfo varInfo;
 
                if (!m_varInfoMap.TryGetVarInfo(v, out varInfo)) 
                {
                    yield return v; 
                }
                else
                {
                    foreach (Var newVar in varInfo.NewVars) 
                    {
                        yield return newVar; 
                    } 
                }
            } 
        }

        /// 
        /// Probe the current VarSet for "structured" Vars - replace these with the 
        /// corresponding sets of flattened Vars
        ///  
        /// current set of vars 
        /// an "expanded" varset
        private VarVec FlattenVarSet(VarVec varSet) 
        {
            VarVec newVarSet = m_command.CreateVarVec(FlattenVars(varSet));
            return newVarSet;
        } 

        ///  
        /// Build up a new varlist, where each structured var has been replaced by its 
        /// corresponding flattened vars
        ///  
        /// the varlist to flatten
        /// the new flattened varlist
        private VarList FlattenVarList(VarList varList)
        { 
            VarList newVarList = Command.CreateVarList(FlattenVars(varList));
            return newVarList; 
        } 

        ///  
        /// Simply flatten out every var in the keys, and return a new DistinctOp
        /// 
        /// DistinctOp
        /// Current subtree 
        /// 
        public override Node Visit(DistinctOp op, Node n) 
        { 
            VisitChildren(n);
 
            // Simply flatten out all the Vars
            VarVec newKeys = FlattenVarSet(op.Keys);
            n.Op = m_command.CreateDistinctOp(newKeys);
            return n; 
        }
 
        ///  
        /// GroupBy
        /// 
        /// Again, VisitChildren - for the Keys and Properties VarDefList nodes - does
        /// the real work.
        ///
        /// The "Keys" and the "OutputVars" varsets are updated to flatten out 
        /// references to any structured Vars.
        ///  
        ///  
        /// 
        ///  
        public override Node Visit(GroupByOp op, Node n)
        {
            VisitChildren(n);
 
            // update the output Vars and the key vars with the right sets
            VarVec newKeys = FlattenVarSet(op.Keys); 
            VarVec newOutputs = FlattenVarSet(op.Outputs); 

            if (newKeys != op.Keys || newOutputs != op.Outputs) 
            {
                n.Op = m_command.CreateGroupByOp(newKeys, newOutputs);
            }
 
            return n;
        } 
 
        /// 
        /// GroupByInto 
        ///
        /// Again, VisitChildren - for the Keys and Properties VarDefList nodes - does
        /// the real work.
        /// 
        /// The "Keys", "InputVars" and "OutputVars" varsets are updated to flatten out
        /// references to any structured Vars. 
        ///  
        /// 
        ///  
        /// 
        public override Node Visit(GroupByIntoOp op, Node n)
        {
            VisitChildren(n); 

            // update the output Vars and the key vars with the right sets 
            VarVec newKeys = FlattenVarSet(op.Keys); 
            VarVec newInputs = FlattenVarSet(op.Inputs);
            VarVec newOutputs = FlattenVarSet(op.Outputs); 

            if (newKeys != op.Keys || newInputs != op.Inputs || newOutputs != op.Outputs)
            {
                n.Op = m_command.CreateGroupByIntoOp(newKeys, newInputs, newOutputs); 
            }
 
            return n; 
        }
 
        /// 
        /// ProjectOp
        ///
        /// The computedVars (the VarDefList) are processed via the VisitChildren() call 
        /// We then try to update the "Vars" property to flatten out any structured
        /// type Vars - if a new VarSet is produced, then the ProjectOp is cloned 
        ///  
        /// 
        ///  
        /// new subtree
        public override Node Visit(ProjectOp op, Node n)
        {
            VisitChildren(n); 

            // update the output Vars with the right set of information 
            VarVec newVars = FlattenVarSet(op.Outputs); 

            if (op.Outputs != newVars) 
            {
                // If the set of vars is empty, that means we didn;t need any of the Vars
                if (newVars.IsEmpty)
                { 
                    return n.Child0;
                } 
                n.Op = m_command.CreateProjectOp(newVars); 
            }
            return n; 
        }

        /// 
        /// ScanTableOp 
        ///
        /// Visit a scanTable Op. Flatten out the table's record into one column 
        /// for each field. Additionally, set up the VarInfo map appropriately 
        /// 
        ///  
        /// 
        /// new subtree
        public override Node Visit(ScanTableOp op, Node n)
        { 

            Var columnVar = op.Table.Columns[0]; 
            TypeInfo typeInfo = m_typeInfo.GetTypeInfo(columnVar.Type); 
            md.RowType newRowType = typeInfo.FlattenedType;
 
            List properties = new List();
            List keyProperties = new List();
            HashSet declaredProps = new HashSet();
            foreach (md.EdmProperty p in TypeHelpers.GetAllStructuralMembers(columnVar.Type.EdmType)) 
            {
                declaredProps.Add(p.Name); 
            } 
            foreach (md.EdmProperty p in newRowType.Properties)
            { 
                if (declaredProps.Contains(p.Name))
                {
                    properties.Add(p);
                } 
            }
            foreach (PropertyRef pref in typeInfo.GetKeyPropertyRefs()) 
            { 
                md.EdmProperty p = typeInfo.GetNewProperty(pref);
                keyProperties.Add(p); 
            }

            //
            // Create a flattened table definition, and a table with that definiton; 
            //
            TableMD newTableMD = m_command.CreateFlatTableDefinition(properties, keyProperties, op.Table.TableMetadata.Extent); 
            Table newTable = m_command.CreateTableInstance(newTableMD); 

            VarInfo varInfo = m_varInfoMap.CreateStructuredVarInfo(columnVar, newRowType, newTable.Columns, properties); 

            n.Op = m_command.CreateScanTableOp(newTable);
            return n;
        } 

        ///  
        /// Get the *single" var produced by the subtree rooted at this node. 
        /// Returns null, if the node produces more than one var, or less than one
        ///  
        /// the node
        /// the single var produced by the node
        internal static Var GetSingletonVar(Node n)
        { 
            switch (n.Op.OpType)
            { 
                case OpType.Project: 
                    {
                        ProjectOp projectOp = (ProjectOp)n.Op; 
                        return (projectOp.Outputs.Count == 1) ? projectOp.Outputs.First : null;
                    }
                case OpType.ScanTable:
                    { 
                        ScanTableOp tableOp = (ScanTableOp)n.Op;
                        return (tableOp.Table.Columns.Count == 1) ? tableOp.Table.Columns[0] : null; 
                    } 

                case OpType.Filter: 
                case OpType.SingleRow:
                case OpType.Sort:
                case OpType.ConstrainedSort:
                    return GetSingletonVar(n.Child0); 

                case OpType.UnionAll: 
                case OpType.Intersect: 
                case OpType.Except:
                    { 
                        SetOp setOp = (SetOp)n.Op;
                        return (setOp.Outputs.Count == 1) ? setOp.Outputs.First : null;
                    }
 
                case OpType.Unnest:
                    { 
                        UnnestOp unnestOp = (UnnestOp)n.Op; 
                        return unnestOp.Table.Columns.Count == 1 ? unnestOp.Table.Columns[0] : null;
                    } 

                case OpType.Distinct:
                    {
                        DistinctOp distinctOp = (DistinctOp)n.Op; 
                        return (distinctOp.Keys.Count == 1) ? distinctOp.Keys.First : null;
                    } 
 
                default:
                    return null; 
            }
        }

        ///  
        /// ScanViewOp
        /// 
        /// Flatten out the view definition, and return that after 
        /// the appropriate remapping
        ///  
        /// the ScanViewOp
        /// current subtree
        /// the flattened view definition
        public override Node Visit(ScanViewOp op, Node n) 
        {
            // 
            // Get the "single" var produced by the input 
            //
            Var inputVar = GetSingletonVar(n.Child0); 
            PlanCompiler.Assert(inputVar != null, "cannot identify Var for the input node to the ScanViewOp");
            // and the table should have exactly one column
            PlanCompiler.Assert(op.Table.Columns.Count == 1, "table for scanViewOp has more than on column?");
            Var columnVar = op.Table.Columns[0]; 

            Node definingNode = VisitNode(n.Child0); 
 
            VarInfo varInfo;
            if (!m_varInfoMap.TryGetVarInfo(inputVar, out varInfo)) 
            {
                PlanCompiler.Assert(false, "didn't find inputVar for scanViewOp?");
            }
            // we must be dealing with a structured column here 
            StructuredVarInfo svarInfo = (StructuredVarInfo)varInfo;
 
            TypeInfo typeInfo = m_typeInfo.GetTypeInfo(columnVar.Type); 

            // if this view does not represent an entityset, then we're pretty much 
            // done. We simply add a mapping from the columnVar to the list of flattened
            // vars produced by the underlying projectOp
            m_varInfoMap.CreateStructuredVarInfo(columnVar, svarInfo.NewType, svarInfo.NewVars, svarInfo.Fields);
            return definingNode; 
        }
 
        ///  
        /// Convert a SortOp. Specifically, walk the SortKeys, and expand out
        /// any Structured type Var references 
        /// 
        /// the sortOp
        /// the current node
        /// new subtree 
        public override Node Visit(SortOp op, Node n)
        { 
            VisitChildren(n); 

            List newSortKeys = HandleSortKeys(op.Keys); 

            if (newSortKeys != op.Keys)
            {
                n.Op = m_command.CreateSortOp(newSortKeys); 
            }
            return n; 
        } 

        ///  
        /// UnnestOp
        ///
        /// Converts an UnnestOp to the right shape.
        /// Flattens out the Table instance, and updates 
        /// 
        ///  
        ///  
        /// new subtree
        public override Node Visit(UnnestOp op, Node n) 
        {
            // Visit the children first
            VisitChildren(n);
 
            md.TypeUsage newType;
 
            if (n.HasChild0) 
            {
                Node chi = n.Child0; 
                VarDefOp varDefOp = chi.Op as VarDefOp;

                if (null != varDefOp)
                { 
                    if (TypeUtils.IsStructuredType(varDefOp.Var.Type)
                     || TypeUtils.IsCollectionType(varDefOp.Var.Type)) 
                    { 
                        List newChildren = new List();
 
                        FlattenComputedVar((ComputedVar)varDefOp.Var, chi, out newChildren, out newType);
                        PlanCompiler.Assert(newChildren.Count == 1, "Flattening unnest var produced more than one Var");
                        n.Child0 = newChildren[0];
                    } 
                }
            } 
 
            // Create a new unnestVar
            VarInfo unnestVarInfo; 
            Var newUnnestVar;
            if (!m_varInfoMap.TryGetVarInfo(op.Var, out unnestVarInfo))
            {
                throw EntityUtil.InternalError(EntityUtil.InternalErrorCode.WrongVarType); 
            }
            else if (!unnestVarInfo.IsCollectionType) 
            { 
                throw EntityUtil.InternalError(EntityUtil.InternalErrorCode.WrongVarType);
            } 
            else
            {
                newUnnestVar = ((CollectionVarInfo)unnestVarInfo).NewVar;
            } 

            // 
            // Flatten out the table 
            // Create a flattened table definition, and a table with that definiton;
            // 
            Table newTable = op.Table;
            Var columnVar = op.Table.Columns[0];
            if (TypeUtils.IsStructuredType(columnVar.Type))
            { 
                TypeInfo typeInfo = m_typeInfo.GetTypeInfo(columnVar.Type);
                md.RowType newRowType = typeInfo.FlattenedType; 
                TableMD newTableMD = m_command.CreateFlatTableDefinition(newRowType); 

                newTable = m_command.CreateTableInstance(newTableMD); 
                VarInfo outputVarInfo = m_varInfoMap.CreateStructuredVarInfo(columnVar, newRowType, newTable.Columns, newRowType.Properties.ToList());
            }

            // Create a new UnnestOp 
            n.Op = m_command.CreateUnnestOp(newUnnestVar, newTable);
            return n; 
        } 

        #region SetOps 

        /// 
        /// SetOp
        /// 
        /// Converts all SetOps - union/intersect/except.
        /// Calls VisitChildren() to do the bulk of the work. After that, the VarMaps 
        /// need to be updated to reflect the removal of any structured Vars 
        /// 
        ///  
        /// 
        /// new subtree
        protected override Node VisitSetOp(SetOp op, Node n)
        { 
            VisitChildren(n);
 
            // Now walk through the first VarMap, and identify the Vars that are needed 
            for (int i = 0; i < op.VarMap.Length; i++)
            { 
                List newComputedVars;
                op.VarMap[i] = FlattenVarMap(op.VarMap[i], out newComputedVars);
                if (newComputedVars != null)
                { 
                    n.Children[i] = FixupSetOpChild(n.Children[i], op.VarMap[i], newComputedVars);
                } 
            } 

            // now get the set of Vars that we will actually need 
            op.Outputs.Clear();
            foreach (Var v in op.VarMap[0].Keys)
            {
                op.Outputs.Set(v); 
            }
            return n; 
        } 

        ///  
        /// Fixes up a SetOp child.
        /// As part of Var flattening, it may so happen that the outer var in the VarMap
        /// may require a property that has no corresponding analog in the inner Var
        /// This logically implies that the corresponding inner property is null. H 
        /// What we do here is to throw an additional projectOp over the setOp child to
        /// add computed Vars (whose defining expressions are null constants) for each 
        /// of those missing properties 
        /// 
        /// one child of the setop 
        /// the varmap for this child
        /// list of new Vars produced
        /// new node for the setOpchild (if any)
        private Node FixupSetOpChild(Node setOpChild, VarMap varMap, List newComputedVars) 
        {
            PlanCompiler.Assert(null != setOpChild, "null setOpChild?"); 
            PlanCompiler.Assert(null != varMap, "null varMap?"); 
            PlanCompiler.Assert(null != newComputedVars, "null newComputedVars?");
 
            // Walk through the list of Vars that have no inner analog, and create
            // a computed Var for each of them
            VarVec newVarSet = m_command.CreateVarVec();
            foreach (KeyValuePair kv in varMap) 
            {
                newVarSet.Set(kv.Value); 
            } 

            List varDefOpNodes = new List(); 
            foreach (Var v in newComputedVars)
            {
                VarDefOp varDefOp = m_command.CreateVarDefOp(v);
                Node varDefOpNode = m_command.CreateNode(varDefOp, CreateNullConstantNode(v.Type)); 
                varDefOpNodes.Add(varDefOpNode);
            } 
            Node varDefListNode = m_command.CreateNode(m_command.CreateVarDefListOp(), varDefOpNodes); 
            ProjectOp projectOp = m_command.CreateProjectOp(newVarSet);
            Node projectNode = m_command.CreateNode(projectOp, setOpChild, varDefListNode); 
            return projectNode;
        }

        ///  
        /// Flattens out a VarMap.
        /// 
        /// Any structured type Vars are expanded out; and collection type Vars 
        /// are replaced by new Vars that reflect the new collection types.
        /// 
        /// There is one special case when dealing with Structured type Vars -
        /// the output and input vars may no longer be 1-1; specifically, there
        /// may be no input Var corresponding to an output var. In such cases, we
        /// build up a new ComputedVar (with an expected value of null), and use that 
        /// in place of the inner var. A subsequent stage will inspect the list of
        /// new ComputedVars, and perform the appropriate fixups 
        ///  
        /// The VarMap to fixup
        /// list of any new computedVars that are created 
        /// a new VarMap
        private VarMap FlattenVarMap(VarMap varMap, out List newComputedVars)
        {
            newComputedVars = null; 

            VarMap newVarMap = new VarMap(); 
            foreach (KeyValuePair kv in varMap) 
            {
                VarInfo innerVarInfo; 
                VarInfo outerVarInfo;
                // Does the inner var have a Varinfo - if not, simply add it
                // to the VarMap, and continue.
                // Otherwise, the Outer var must have a VarInfo too 
                if (!m_varInfoMap.TryGetVarInfo(kv.Value, out innerVarInfo))
                { 
                    newVarMap.Add(kv.Key, kv.Value); 
                }
                else 
                {
                    // get my own var info
                    if (!m_varInfoMap.TryGetVarInfo(kv.Key, out outerVarInfo))
                    { 
                        outerVarInfo = FlattenSetOpVar((SetOpVar)kv.Key);
                    } 
 
                    // If this Var represents a collection type, then simply
                    // replace the singleton Var 
                    if (outerVarInfo.IsCollectionType)
                    {
                        newVarMap.Add(((CollectionVarInfo)outerVarInfo).NewVar, ((CollectionVarInfo)innerVarInfo).NewVar);
                    } 
                    else
                    { // structured type 
                        StructuredVarInfo outerSvarInfo = (StructuredVarInfo)outerVarInfo; 
                        StructuredVarInfo innerSvarInfo = (StructuredVarInfo)innerVarInfo;
 
                        // walk through each property, and find the innerVar corresponding
                        // to that property
                        foreach (md.EdmProperty prop in outerSvarInfo.Fields)
                        { 
                            Var outerVar;
                            Var innerVar; 
                            bool ret = outerSvarInfo.TryGetVar(prop, out outerVar); 
                            PlanCompiler.Assert(ret, "Could not find VarInfo for prop " + prop.Name);
 
                            if (!innerSvarInfo.TryGetVar(prop, out innerVar))
                            {
                                // we didn't find a corresponding innerVar.
                                innerVar = m_command.CreateComputedVar(outerVar.Type); 
                                if (newComputedVars == null)
                                { 
                                    newComputedVars = new List(); 
                                }
                                newComputedVars.Add((ComputedVar)innerVar); 
                            }
                            newVarMap.Add(outerVar, innerVar);
                        }
                    } 
                }
            } 
            return newVarMap; 
        }
 
        /// 
        /// Flattens a SetOpVar (used in SetOps). Simply produces a list of
        /// properties corresponding to each desired property
        ///  
        /// 
        ///  
        private VarInfo FlattenSetOpVar(SetOpVar v) 
        {
            if (TypeUtils.IsCollectionType(v.Type)) 
            {
                md.TypeUsage newType = GetNewType(v.Type);
                Var newVar = m_command.CreateSetOpVar(newType);
                return m_varInfoMap.CreateCollectionVarInfo(v, newVar); 
            }
 
            // Get the "new" type for the Var 
            TypeInfo typeInfo = m_typeInfo.GetTypeInfo(v.Type);
            // Get a list of properties that we think are necessary 
            PropertyRefList desiredProperties = m_varPropertyMap[v];
            List newVars = new List();
            List newProps = new List();
            bool hasNullSentinelVar = false; 
            foreach (PropertyRef p in typeInfo.PropertyRefList)
            { 
                if (!desiredProperties.Contains(p)) 
                {
                    continue; 
                }
                md.EdmProperty newProperty = typeInfo.GetNewProperty(p);
                newProps.Add(newProperty);
                SetOpVar newVar = m_command.CreateSetOpVar(md.Helper.GetModelTypeUsage(newProperty)); 
                newVars.Add(newVar);
 
                // Check if it is a null sentinel var 
                if (!hasNullSentinelVar && IsNullSentinelPropertyRef(p))
                { 
                    hasNullSentinelVar = true;
                }
            }
            VarInfo varInfo = m_varInfoMap.CreateStructuredVarInfo(v, typeInfo.FlattenedType, newVars, newProps, hasNullSentinelVar); 
            return varInfo;
        } 
 
        #endregion
 
        #region DML RelOps

        //
        // DML RelOps are technically very simple - we should simply visit the 
        // children. However, I will defer this to when we actually support DML
        // so for now, the default implementation in the basicVisitor is to throw 
        // unimplemented and that's good enough. 
        //
 
        #endregion

        #endregion
 
        #region ScalarOp Visitors
 
        ///  
        /// SoftCastOp
        /// 
        /// Visit the children first.
        ///
        /// If this is an entity type, complextype or ref type, simply return the
        ///   visited child. (Rationale: These must be in the same type hierarchy; or 
        ///   the earlier stages of query would have barfed. And, we end up
        ///   using the same "flat" type for every type in the hierarchy) 
        /// 
        /// If this is a scalar type, then simply return the current node
        /// 
        /// If this is a collection type, then create a new softcastOp over the input
        ///  (the collection type may have changed)
        ///
        /// Otherwise, we're dealing with a record type. Since our earlier 
        /// definitions of equivalence required that equivalent record types must
        /// have the same number of fields, with "promotable" types, and in the same 
        /// order; *and* since we asked for all properties (see PropertyPushdownHelper), 
        /// the input must be a NewRecordOp, whose fields line up 1-1 with our fields.
        /// Build up a new NewRecordOp based on the arguments to the input NewRecordOp, 
        /// and build up SoftCastOps for any field whose type does not match
        /// 
        /// 
        ///  
        /// 
        public override Node Visit(SoftCastOp op, Node n) 
        { 
            md.TypeUsage inputTypeUsage = n.Child0.Op.Type;
            md.TypeUsage oldType = op.Type; 

            // Always think of your children first
            VisitChildren(n);
 
            md.TypeUsage newType = GetNewType(oldType);
 
            if (md.TypeSemantics.IsRowType(oldType)) 
            {
                PlanCompiler.Assert(n.Child0.Op.OpType == OpType.NewRecord, "Expected a record constructor here. Found " + n.Child0.Op.OpType + " instead"); 

                TypeInfo inputTypeInfo = m_typeInfo.GetTypeInfo(inputTypeUsage);
                TypeInfo outputTypeInfo = m_typeInfo.GetTypeInfo(op.Type);
 
                NewRecordOp newOp = m_command.CreateNewRecordOp(newType);
 
                List newArgs = new List(); 

                // We have to adjust for when we're supposed to add/remove null sentinels; 
                // it is entirely possible that we may need to add multiple null sentinel
                // columns (See SQLBUDT #549068 for an example).
                IEnumerator outputs = newOp.Properties.GetEnumerator();
                int outputPropertyCount = newOp.Properties.Count; 
                outputs.MoveNext();
 
                IEnumerator inputs = n.Child0.Children.GetEnumerator(); 
                int inputPropertyCount = n.Child0.Children.Count;
                inputs.MoveNext(); 

                // We know that all Null Sentinels are added on the left side, so we'll
                // just keep adding them until we have the same number of properties on
                // both the input and the output... 
                while (inputPropertyCount < outputPropertyCount)
                { 
                    PlanCompiler.Assert(outputTypeInfo.HasNullSentinelProperty && !inputTypeInfo.HasNullSentinelProperty, "NullSentinelProperty mismatch on input?"); 

                    // make up a null sentinel; the output requires it. 
                    newArgs.Add(CreateNullSentinelConstant());
                    outputs.MoveNext();
                    outputPropertyCount--;
                } 

                // Likewise, we'll just drop any null sentinel columns from the input until 
                // we have the same number of columns... 
                while (inputPropertyCount > outputPropertyCount)
                { 
                    PlanCompiler.Assert(!outputTypeInfo.HasNullSentinelProperty && inputTypeInfo.HasNullSentinelProperty, "NullSentinelProperty mismatch on output?");

                    // remove the null sentinel; the output doesn't require it.
                    inputs.MoveNext(); 
                    inputPropertyCount--;
                } 
 
                do
                { 
                    md.EdmProperty p = outputs.Current;
                    Node arg = BuildSoftCast(inputs.Current, md.Helper.GetModelTypeUsage(p));
                    newArgs.Add(arg);
                    outputs.MoveNext(); 
                }
                while (inputs.MoveNext()); 
 
                Node newNode = m_command.CreateNode(newOp, newArgs);
                return newNode; 
            }
            else if (md.TypeSemantics.IsCollectionType(oldType))
            {
                // 
                // Our collection type may have changed - 'coz the
                // element type of the collection may have changed. 
                // Simply build up a new castOp (if necessary) 
                //
                return BuildSoftCast(n.Child0, newType); 
            }
            else if (md.TypeSemantics.IsPrimitiveType(oldType))
            {
                // How primitive! Well, the Prime Directive prohibits me 
                // from doing much with these.
                return n; 
            } 
            else
            { 
                PlanCompiler.Assert(md.TypeSemantics.IsNominalType(oldType) ||
                    md.TypeSemantics.IsReferenceType(oldType),
                    "Gasp! Not a nominal type or even a reference type");
                // I'm dealing with a nominal type (entity, complex type) or 
                // a reference type here. Every type in the same hierarchy
                // must have been rationalized into the same type, and so, we 
                // won't need to do anything special 
                PlanCompiler.Assert(Command.EqualTypes(newType, n.Child0.Op.Type),
                    "Types are not equal"); 
                return n.Child0;
            }
        }
 
        /// 
        /// 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. 
        /// 
        /// the CaseOp 
        /// corresponding node
        /// new subtree
        public override Node Visit(CaseOp op, Node n)
        { 
            // Before visiting the children, check whether the case statment can be optimized
            bool thenClauseIsNull; 
            bool 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.
            if (TypeUtils.IsCollectionType(op.Type))
            { 
                md.TypeUsage 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. 
                PropertyRefList desiredProperties = m_nodePropertyMap[n];
                Node newNode = FlattenCaseOp(op, n, m_typeInfo.GetTypeInfo(op.Type), desiredProperties); 
                return newNode; 
            }
            else 
            {
                return n;
            }
        } 

        ///  
        /// Given a case statement 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 rewrittes 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. 
        ///
        /// The rewrite only happens if: 
        ///     (1) Y has null sentinel, and 
        ///     (2) Y is a NewRecordOp.
        ///  
        /// 
        /// 
        /// 
        /// Whether a rewrite was done 
        private bool TryRewriteCaseOp(Node n, bool thenClauseIsNull, out Node rewrittenNode)
        { 
            rewrittenNode = n; 

            //If the type of the case op does not have a null sentinel, we can't do the rewrite. 
            if (!m_typeInfo.GetTypeInfo(n.Op.Type).HasNullSentinelProperty)
            {
                return false;
            } 

            Node resultNode = thenClauseIsNull ? n.Child2 : n.Child1; 
            if (resultNode.Op.OpType != OpType.NewRecord) 
            {
                return false; 
            }

            //Rewrite the null sentinel, which is the first child of the resultNode
            Node currentNullSentinel = resultNode.Child0; 
            md.TypeUsage integerType = this.m_command.IntegerType;
            PlanCompiler.Assert(currentNullSentinel.Op.Type.EdmEquals(integerType), "Column that is expected to be a null sentinel is not of Integer type."); 
 
            CaseOp newCaseOp = m_command.CreateCaseOp(integerType);
            List children = new List(3); 

            //The the 'when' from the case statement
            children.Add(n.Child0);
 
            Node nullSentinelNullNode = m_command.CreateNode(m_command.CreateNullOp(integerType));
            Node nullSentinelThenNode = thenClauseIsNull ? nullSentinelNullNode : currentNullSentinel; 
            Node nullSentinelElseNode = thenClauseIsNull ? currentNullSentinel : nullSentinelNullNode; 
            children.Add(nullSentinelThenNode);
            children.Add(nullSentinelElseNode); 

            //Use the case op as a new null sentinel
            resultNode.Child0 = m_command.CreateNode(newCaseOp, children);
 
            rewrittenNode = resultNode;
            return true; 
        } 

        ///  
        /// 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. 
        ///  
        /// the CaseOp
        /// Node corresponding to the CaseOp 
        /// Information about the type
        /// Set of properties desired
        /// 
        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 fieldTypes = new List();
            List fieldValues = new List(); 

            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 caseChildren = new List();
                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); 
        } 

        ///  
        /// CollectOp
        ///
        /// Nothing much to do - simply update the result type
        ///  
        /// the NestOp
        /// corresponding node 
        /// new subtree 
        public override Node Visit(CollectOp op, Node n)
        { 
            VisitChildren(n);
            // simply update the desired type
            n.Op = m_command.CreateCollectOp(GetNewType(op.Type));
            return n; 
        }
 
        ///  
        /// ComparisonOp
        /// 
        /// If the inputs to the comparisonOp are Refs/records/entitytypes, then
        /// we need to flatten these out. Of course, the only reasonable comparisons
        /// should be EQ and NE
        ///  
        /// 
        ///  
        ///  
        public override Node Visit(ComparisonOp op, Node n)
        { 
            md.TypeUsage child0Type = ((ScalarOp)n.Child0.Op).Type;
            md.TypeUsage child1Type = ((ScalarOp)n.Child1.Op).Type;

            if (!TypeUtils.IsStructuredType(child0Type)) 
            {
                return VisitScalarOpDefault(op, n); 
            } 

            VisitChildren(n); // visit the children first 

            // We're now dealing with a structured type
            PlanCompiler.Assert(!(md.TypeSemantics.IsComplexType(child0Type) || md.TypeSemantics.IsComplexType(child1Type)), "complex type?"); // cannot be a complex type
            PlanCompiler.Assert(op.OpType == OpType.EQ || op.OpType == OpType.NE, "non-equality comparison of structured types?"); 

            // 
            // Strictly speaking, we should be able to use the typeinfo of either of the arguments. 
            // However, as things stand today, we do have scenarios where the types on the
            // two sides (records mainly) are equivalent, but not identical. This non-identicality 
            // may involve the field types being different, the field names being different etc. - but
            // we may be assured that the order of the field types is fixed.
            //
            TypeInfo child0TypeInfo = m_typeInfo.GetTypeInfo(child0Type); 
            TypeInfo child1TypeInfo = m_typeInfo.GetTypeInfo(child1Type);
            List properties1; 
            List properties2; 
            List values1;
            List values2; 

            // get a list of the relevant properties and values from each of the children

            GetPropertyValues(child0TypeInfo, OperationKind.Equality, n.Child0, false, out properties1, out values1); 
            GetPropertyValues(child1TypeInfo, OperationKind.Equality, n.Child1, false, out properties2, out values2);
 
            PlanCompiler.Assert((properties1.Count == properties2.Count) && (values1.Count == values2.Count), "different shaped structured types?"); 

            // Build up an and-chain of comparison ops on the property values 
            Node andNode = null;
            for (int i = 0; i < values1.Count; i++)
            {
                ComparisonOp newCompOp = m_command.CreateComparisonOp(op.OpType); 
                Node newCompNode = m_command.CreateNode(newCompOp, values1[i], values2[i]);
                if (null == andNode) 
                    andNode = newCompNode; 
                else
                    andNode = m_command.CreateNode(m_command.CreateConditionalOp(OpType.And), andNode, newCompNode); 
            }
            return andNode;
        }
 
        /// 
        /// ConditionalOp 
        /// 
        /// IsNull requires special handling.
        ///  
        /// 
        /// 
        /// 
        public override Node Visit(ConditionalOp op, Node n) 
        {
            if (op.OpType != OpType.IsNull) 
            { 
                return VisitScalarOpDefault(op, n);
            } 

            //
            // Special handling for IS NULL ops on structured types
            // 
            // For structured types, we simply convert this into an AND chain of
            // IS NULL predicates, one for each property. There are a couple of 
            // optimizations that we perform. 
            //
            // For entity types, we simply perfom the IS NULL operations on the 
            // key attributes alone.
            //
            // Complex types must have a typeid property - the isnull is pushed to the
            // typeid property 
            //
            // We do NOT support IsNull for Collections 
            // 

            md.TypeUsage childOpType = ((ScalarOp)n.Child0.Op).Type; 

            // Special cases are for structured types only
            if (!TypeUtils.IsStructuredType(childOpType))
            { 
                return VisitScalarOpDefault(op, n);
            } 
 
            // visit the children first
            VisitChildren(n); 

            TypeInfo typeInfo = m_typeInfo.GetTypeInfo(childOpType);

            // Otherwise, build up an and-chain of is null checks for each appropriate 
            // property - which should consist only of key properties for Entity types.
            List properties = null; 
            List values = null; 
            GetPropertyValues(typeInfo, OperationKind.IsNull, n.Child0, false, out properties, out values);
 
            PlanCompiler.Assert(properties.Count == values.Count && properties.Count > 0, "No properties returned from GetPropertyValues(IsNull)?");

            Node andNode = null;
            foreach (Node propertyValue in values) 
            {
                Node isNullNode = m_command.CreateNode(m_command.CreateConditionalOp(OpType.IsNull), propertyValue); 
                if (andNode == null) 
                    andNode = isNullNode;
                else 
                    andNode = m_command.CreateNode(m_command.CreateConditionalOp(OpType.And), andNode, isNullNode);
            }
            return andNode;
        } 

        ///  
        /// Convert a ConstrainedSortOp. Specifically, walk the SortKeys, and expand out 
        /// any Structured type Var references
        ///  
        /// the constrainedSortOp
        /// the current node
        /// new subtree
        public override Node Visit(ConstrainedSortOp op, Node n) 
        {
            VisitChildren(n); 
 
            List newSortKeys = HandleSortKeys(op.Keys);
 
            if (newSortKeys != op.Keys)
            {
                n.Op = m_command.CreateConstrainedSortOp(newSortKeys, op.WithTies);
            } 
            return n;
        } 
 
        /// 
        /// GetEntityKeyOp 
        /// 
        /// 
        /// 
        ///  
        public override Node Visit(GetEntityRefOp op, Node n)
        { 
            return FlattenGetKeyOp(op, n); 
        }
 
        /// 
        /// GetRefKeyOp
        /// 
        ///  
        /// 
        ///  
        public override Node Visit(GetRefKeyOp op, Node n) 
        {
            return FlattenGetKeyOp(op, n); 
        }

        /// 
        /// GetEntityKeyOp/GetRefKeyOp common handling 
        ///
        /// In either case, get the "key" properties from the input entity/ref, and 
        /// build up a record constructor from these values 
        /// 
        /// the GetRefKey/GetEntityKey op 
        /// current subtree
        /// new expression subtree
        private Node FlattenGetKeyOp(ScalarOp op, Node n)
        { 
            PlanCompiler.Assert(op.OpType == OpType.GetEntityRef || op.OpType == OpType.GetRefKey, "Expecting GetEntityRef or GetRefKey ops");
 
            TypeInfo inputTypeInfo = m_typeInfo.GetTypeInfo(((ScalarOp)n.Child0.Op).Type); 
            TypeInfo outputTypeInfo = m_typeInfo.GetTypeInfo(op.Type);
 
            // Visit the child - will flatten out the input ref/entity
            VisitChildren(n);

            // Get "key" properties (and the corresponding values) from the input 
            List inputFieldTypes;
            List inputFieldValues; 
 
            // Get the key properties for GetRefKey; get the Identity properties
            // for GetEntityRef 
            if (op.OpType == OpType.GetRefKey)
            {
                GetPropertyValues(inputTypeInfo, OperationKind.GetKeys, n.Child0, false /* ignore missing props */, out inputFieldTypes, out inputFieldValues);
            } 
            else
            { 
                PlanCompiler.Assert(op.OpType == OpType.GetEntityRef, 
                    "Expected OpType.GetEntityRef: Found " + op.OpType);
                GetPropertyValues(inputTypeInfo, OperationKind.GetIdentity, n.Child0, false, out inputFieldTypes, out inputFieldValues); 
            }

            if (outputTypeInfo.HasNullSentinelProperty && !inputTypeInfo.HasNullSentinelProperty)
            { 
                // Add a null sentinel column, the input doesn't have one but the output requires it.
                inputFieldValues.Insert(0, CreateNullSentinelConstant()); 
            } 

            // create an appropriate record constructor 
            List outputFieldTypes = new List(outputTypeInfo.FlattenedType.Properties);
            PlanCompiler.Assert(inputFieldValues.Count == outputFieldTypes.Count, "fieldTypes.Count mismatch?");

            NewRecordOp rec = m_command.CreateNewRecordOp(outputTypeInfo.FlattenedTypeUsage, outputFieldTypes); 
            Node newNode = m_command.CreateNode(rec, inputFieldValues);
            return newNode; 
        } 

        ///  
        /// Common handler for PropertyOp and RelPropertyOp
        /// 
        /// 
        ///  
        /// 
        ///  
        private Node VisitPropertyOp(Op op, Node n, PropertyRef propertyRef) 
        {
            PlanCompiler.Assert(op.OpType == OpType.Property || op.OpType == OpType.RelProperty, 
                "Unexpected optype: " + op.OpType);

            md.TypeUsage inputType = n.Child0.Op.Type;
            md.TypeUsage outputType = op.Type; 

            // First visit all my children 
            VisitChildren(n); 

            // If the instance is not a structured type (ie) it is a udt, then there 
            // is little for us to do. Simply return
            if (TypeUtils.IsUdt(inputType))
            {
                return n; 
            }
 
            Node newNode = null; 
            TypeInfo inputTypeInfo = m_typeInfo.GetTypeInfo(inputType);
 
            if (TypeUtils.IsStructuredType(outputType))
            {
                TypeInfo outputTypeInfo = m_typeInfo.GetTypeInfo(outputType);
                List fieldTypes = new List(); 
                List fieldValues = new List();
                PropertyRefList expectedProperties = m_nodePropertyMap[n]; 
 
                foreach (PropertyRef npr in outputTypeInfo.PropertyRefList)
                { 
                    // Is this a property that's desired by my consumers?
                    if (expectedProperties.Contains(npr))
                    {
                        PropertyRef newPropRef = npr.CreateNestedPropertyRef(propertyRef); 
                        md.EdmProperty outputNestedProp = outputTypeInfo.GetNewProperty(npr);
                        md.EdmProperty newNestedProp = inputTypeInfo.GetNewProperty(newPropRef); 
 
                        Node field = BuildAccessor(n.Child0, newNestedProp);
                        if (null != field) 
                        {
                            fieldTypes.Add(outputNestedProp);
                            fieldValues.Add(field);
                        } 
                    }
                } 
                Op newRecordOp = m_command.CreateNewRecordOp(outputTypeInfo.FlattenedTypeUsage, fieldTypes); 
                newNode = m_command.CreateNode(newRecordOp, fieldValues);
            } 
            else
            {
                md.EdmProperty newProp = inputTypeInfo.GetNewProperty(propertyRef);
                // Build an accessor over the new property 
                newNode = this.BuildAccessorWithNulls(n.Child0, newProp);
            } 
            return newNode; 
        }
 
        /// 
        /// PropertyOp
        ///
        /// If this is a scalar/collection property, then simply get the appropriate 
        /// field out.
        /// 
        /// Otherwise, build up a record constructor corresponding to the result 
        /// type - optimize this by only getting those properties that are needed
        /// 
        /// If the instance is not a structured type (ie) it is a UDT, then simply return
        ///
        /// 
        /// the PropertyOp 
        /// the corresponding node
        /// new subtree 
        public override Node Visit(PropertyOp op, Node n) 
        {
            return VisitPropertyOp(op, n, new SimplePropertyRef(op.PropertyInfo)); 
        }

        /// 
        /// RelPropertyOp. Pick out the appropriate property from the child 
        /// 
        ///  
        ///  
        /// 
        public override Node Visit(RelPropertyOp op, Node n) 
        {
            return VisitPropertyOp(op, n, new RelPropertyRef(op.PropertyInfo));
        }
 
        /// 
        /// RefOp 
        /// 
        /// Simply convert this into the corresponding record type - with one
        /// field for each key, and one for the entitysetid 
        /// 
        /// 
        /// 
        ///  
        public override Node Visit(RefOp op, Node n)
        { 
            TypeInfo inputTypeInfo = m_typeInfo.GetTypeInfo(((ScalarOp)n.Child0.Op).Type); 
            TypeInfo outputTypeInfo = m_typeInfo.GetTypeInfo(op.Type);
 
            // visit children now
            VisitChildren(n);

            // Get the list of fields and properties from the input (key) op 
            List inputFields;
            List inputFieldValues; 
            GetPropertyValues(inputTypeInfo, OperationKind.All, n.Child0, false, out inputFields, out inputFieldValues); 

            // Get my property list 
            List outputFields = new List(outputTypeInfo.FlattenedType.Properties);

            if (outputTypeInfo.HasEntitySetIdProperty)
            { 
                PlanCompiler.Assert(outputFields[0] == outputTypeInfo.EntitySetIdProperty, "OutputField0 must be the entitySetId property");
 
                if (inputTypeInfo.HasNullSentinelProperty && !outputTypeInfo.HasNullSentinelProperty) 
                {  // realistically, REFs can't have null sentinels, but I'm being pedantic...
                    PlanCompiler.Assert(outputFields.Count == inputFields.Count, "Mismatched field count: Expected " + inputFields.Count + "; Got " + outputFields.Count); 
                    RemoveNullSentinel(inputTypeInfo, inputFields, inputFieldValues, outputFields);
                }
                else
                { 
                    PlanCompiler.Assert(outputFields.Count == inputFields.Count + 1, "Mismatched field count: Expected " + (inputFields.Count + 1) + "; Got " + outputFields.Count);
                } 
 
                // Now prepend a value for the entitysetid property and a value for this property
                int entitySetId = m_typeInfo.GetEntitySetId(op.EntitySet); 
                inputFieldValues.Insert(0, m_command.CreateNode(m_command.CreateInternalConstantOp(md.Helper.GetModelTypeUsage(outputTypeInfo.EntitySetIdProperty), entitySetId)));
            }
            else
            { 
                if (inputTypeInfo.HasNullSentinelProperty && !outputTypeInfo.HasNullSentinelProperty)
                { // realistically, REFs can't have null sentinels, but I'm being pedantic... 
                    RemoveNullSentinel(inputTypeInfo, inputFields, inputFieldValues, outputFields); 
                }
 
                PlanCompiler.Assert(outputFields.Count == inputFields.Count, "Mismatched field count: Expected " + inputFields.Count + "; Got " + outputFields.Count);
            }

            // now build up a NewRecordConstructor with the appropriate info 
            NewRecordOp recOp = m_command.CreateNewRecordOp(outputTypeInfo.FlattenedTypeUsage, outputFields);
            Node newNode = m_command.CreateNode(recOp, inputFieldValues); 
 
            return newNode;
        } 

        // We have to adjust for when we're supposed to remove null sentinels;
        // columns (See SQLBUDT #553534 for an example).  Note that we shouldn't
        // have to add null sentinels here, since reference types won't be expecting 
        // them (the fact that the key is null is good enough...)
        private static void RemoveNullSentinel(TypeInfo inputTypeInfo, List inputFields, List inputFieldValues, List outputFields) 
        { 
            PlanCompiler.Assert(inputFields[0] == inputTypeInfo.NullSentinelProperty, "InputField0 must be the null sentinel property");
            inputFields.RemoveAt(0); 
            inputFieldValues.RemoveAt(0);
        }

        ///  
        /// VarRefOp
        /// 
        /// Replace a VarRef with a copy of the corresponding "Record" constructor 
        /// 
        /// the VarRefOp 
        /// the node
        /// new subtree
        public override Node Visit(VarRefOp op, Node n)
        { 
            // Lookup my VarInfo
            VarInfo varInfo; 
            if (!m_varInfoMap.TryGetVarInfo(op.Var, out varInfo)) 
            {
                PlanCompiler.Assert(!TypeUtils.IsStructuredType(op.Type), 
                    "No varInfo for a structured type var: Id = " + op.Var.Id + " Type = " + op.Type);
                return n;
            }
            if (varInfo.IsCollectionType) 
            {
                n.Op = m_command.CreateVarRefOp(((CollectionVarInfo)varInfo).NewVar); 
                return n; 
            }
            else 
            {
                // A very specialized record constructor mechanism for structured type Vars.
                // We look up the VarInfo corresponding to the Var - which has a set of fields
                // and the corresponding properties that we need to produce 

                StructuredVarInfo structuredVarInfo = (StructuredVarInfo)varInfo; 
 
                NewRecordOp newOp = m_command.CreateNewRecordOp(structuredVarInfo.NewTypeUsage, structuredVarInfo.Fields);
                List newNodeChildren = new List(); 
                foreach (Var v in varInfo.NewVars)
                {
                    VarRefOp newVarRefOp = m_command.CreateVarRefOp(v);
                    newNodeChildren.Add(m_command.CreateNode(newVarRefOp)); 
                }
                Node newNode = m_command.CreateNode(newOp, newNodeChildren); 
                return newNode; 
            }
        } 

        #region record construction ops

        ///  
        /// Handler for NewEntity
        ///  
        ///  
        /// 
        ///  
        public override Node Visit(NewEntityOp op, Node n)
        {
            return FlattenConstructor(op, n);
        } 

        ///  
        /// NewInstanceOp 
        /// 
        /// the NewInstanceOp 
        /// corresponding node
        /// new subtree
        public override Node Visit(NewInstanceOp op, Node n)
        { 
            return FlattenConstructor(op, n);
        } 
 
        /// 
        /// DiscriminatedNewInstanceOp 
        /// 
        /// the DiscriminatedNewInstanceOp
        /// corresponding node
        /// new subtree 
        public override Node Visit(DiscriminatedNewEntityOp op, Node n)
        { 
            return FlattenConstructor(op, n); 
        }
 
        /// 
        /// Given an explicit discriminator value, map to normalized values. Essentially, this allows
        /// a discriminated new instance to coexist with free-floating entities, MEST, etc. which use
        /// general purpose ordpath type ids (e.g. '0X0X') 
        ///
        /// An example of the normalization is given: 
        /// 
        /// CASE
        ///     WHEN discriminator = 'Base' THEN '0X' 
        ///     WHEN discriminator = 'Derived1' THEN '0X0X'
        ///     WHEN discriminator = 'Derived2' THEN '0X1X'
        ///     ELSE '0X2X' -- default case for 'Derived3'
        ///  
        private Node NormalizeTypeDiscriminatorValues(DiscriminatedNewEntityOp op, Node discriminator)
        { 
            TypeInfo typeInfo = m_typeInfo.GetTypeInfo(op.Type); 

            CaseOp normalizer = m_command.CreateCaseOp(typeInfo.RootType.TypeIdProperty.TypeUsage); 
            List children = new List(op.DiscriminatorMap.TypeMap.Count * 2 - 1);
            for (int i = 0; i < op.DiscriminatorMap.TypeMap.Count; i++)
            {
                object discriminatorValue = op.DiscriminatorMap.TypeMap[i].Key; 
                md.EntityType type = op.DiscriminatorMap.TypeMap[i].Value;
                TypeInfo currentTypeInfo = m_typeInfo.GetTypeInfo(md.TypeUsage.Create(type)); 
 
                Node normalizedDiscriminatorConstant = CreateTypeIdConstant(currentTypeInfo);
                // for the last type, return the 'then' value 
                if (i == op.DiscriminatorMap.TypeMap.Count - 1)
                {
                    // ELSE normalizedDiscriminatorValue
                    children.Add(normalizedDiscriminatorConstant); 
                }
                else 
                { 
                    // WHEN discriminator = discriminatorValue THEN normalizedDiscriminatorValue
                    ConstantBaseOp discriminatorValueOp = m_command.CreateConstantOp(md.Helper.GetModelTypeUsage(op.DiscriminatorMap.DiscriminatorProperty.TypeUsage), 
                                                                                     discriminatorValue);
                    Node discriminatorConstant = m_command.CreateNode(discriminatorValueOp);
                    ComparisonOp discriminatorPredicateOp = m_command.CreateComparisonOp(OpType.EQ);
                    Node discriminatorPredicate = m_command.CreateNode(discriminatorPredicateOp, discriminator, discriminatorConstant); 
                    children.Add(discriminatorPredicate);
                    children.Add(normalizedDiscriminatorConstant); 
                } 
            }
 
            // swap discriminator with case op normalizing the discriminator
            discriminator = m_command.CreateNode(normalizer, children);
            return discriminator;
        } 

        ///  
        /// NewRecordOp 
        /// 
        /// the newRecordOp 
        /// corresponding node
        /// new subtree
        public override Node Visit(NewRecordOp op, Node n)
        { 
            return FlattenConstructor(op, n);
        } 
 
        /// 
        /// Build out an expression corresponding to the entitysetid 
        /// 
        /// the property corresponding to the entitysetid
        /// the *NewEntity op
        ///  
        private Node GetEntitySetIdExpr(md.EdmProperty entitySetIdProperty, NewEntityBaseOp op)
        { 
            Node entitySetIdNode; 
            md.EntitySet entitySet = op.EntitySet as md.EntitySet;
            if (entitySet != null) 
            {
                int entitySetId = m_typeInfo.GetEntitySetId(entitySet);
                InternalConstantOp entitySetIdOp = m_command.CreateInternalConstantOp(md.Helper.GetModelTypeUsage(entitySetIdProperty), entitySetId);
                entitySetIdNode = m_command.CreateNode(entitySetIdOp); 
            }
            else 
            { 
                //
                // Not in a view context; simply assume a null entityset 
                //
                entitySetIdNode = CreateNullConstantNode(md.Helper.GetModelTypeUsage(entitySetIdProperty));
            }
 
            return entitySetIdNode;
        } 
 
        /// 
        /// Flattens out a constructor into a "flat" record constructor. 
        /// The "flat" record type is looked up for the current constructor's type,
        /// and each property is filled out from the current constructor's fields
        /// 
        /// The NewRecordOp/NewInstanceOp 
        /// The current subtree
        /// the new subtree 
        private Node FlattenConstructor(ScalarOp op, Node n) 
        {
            PlanCompiler.Assert(op.OpType == OpType.NewInstance || op.OpType == OpType.NewRecord || op.OpType == OpType.DiscriminatedNewEntity || op.OpType == OpType.NewEntity, 
                "unexpected op: " + op.OpType + "?");

            // First visit all my children
            VisitChildren(n); 

            // Find the new type corresponding to the type 
            TypeInfo typeInfo = m_typeInfo.GetTypeInfo(op.Type); 
            md.RowType flatType = typeInfo.FlattenedType;
            NewEntityBaseOp newEntityOp = op as NewEntityBaseOp; 

            // Identify the fields
            IEnumerable opFields = null;
            DiscriminatedNewEntityOp discriminatedNewInstanceOp = null; 
            if (op.OpType == OpType.NewRecord)
            { 
                // Get only those fields that I have values for 
                opFields = ((NewRecordOp)op).Properties;
            } 
            else if (op.OpType == OpType.DiscriminatedNewEntity)
            {
                // Get all properties projected by the discriminated new instance op
                discriminatedNewInstanceOp = (DiscriminatedNewEntityOp)op; 
                opFields = discriminatedNewInstanceOp.DiscriminatorMap.Properties;
            } 
            else 
            {
                // Children align with structural members of type for a standard NewInstanceOp 
                opFields = TypeHelpers.GetAllStructuralMembers(op.Type);
            }

            // Next, walk through each of my field, and flatten out any field 
            // that is structured.
            List newFields = new List(); 
            List newFieldValues = new List(); 

            // 
            // NOTE: we expect the type id property and the entityset id properties
            //       to be at the start of the properties collection.
            //
            // Add a typeid property if we need one 
            //
            if (typeInfo.HasTypeIdProperty) 
            { 
                newFields.Add(typeInfo.TypeIdProperty);
                if (null == discriminatedNewInstanceOp) 
                {
                    newFieldValues.Add(CreateTypeIdConstant(typeInfo));
                }
                else 
                {
                    // first child in DiscriminatedNewInstanceOp is discriminator/typeid 
                    Node discriminator = n.Children[0]; 

                    if (null == typeInfo.RootType.DiscriminatorMap) 
                    {
                        // if there are multiple sets (or free-floating constructors) for this type
                        // hierarchy, normalize the discriminator value to expose the standard
                        // '0X' style values 
                        discriminator = NormalizeTypeDiscriminatorValues(discriminatedNewInstanceOp, discriminator);
                    } 
 
                    newFieldValues.Add(discriminator);
                } 
            }

            //
            // Add an entitysetid property if we need one 
            //
            if (typeInfo.HasEntitySetIdProperty) 
            { 
                newFields.Add(typeInfo.EntitySetIdProperty);
 
                PlanCompiler.Assert(newEntityOp != null, "unexpected optype:" + op.OpType);
                Node entitySetIdNode = GetEntitySetIdExpr(typeInfo.EntitySetIdProperty, newEntityOp);

                // Get the entity-set-id of the "current" entityset 
                newFieldValues.Add(entitySetIdNode);
            } 
 
            // Add a nullability property if we need one
            if (typeInfo.HasNullSentinelProperty) 
            {
                newFields.Add(typeInfo.NullSentinelProperty);
                newFieldValues.Add(CreateNullSentinelConstant());
            } 

            // 
            // first child of discriminatedNewInstanceOp is the typeId; otherwise, the first child is the first property 
            //
            int childrenIndex = null == discriminatedNewInstanceOp ? 0 : 1; 

            foreach (md.EdmMember opField in opFields)
            {
                Node fieldValue = n.Children[childrenIndex]; 
                if (TypeUtils.IsStructuredType(md.Helper.GetModelTypeUsage(opField)))
                { 
                    // Flatten out nested type 
                    md.RowType nestedFlatType = m_typeInfo.GetTypeInfo(md.Helper.GetModelTypeUsage(opField)).FlattenedType;
 
                    // Find offset of opField in top-level flat type
                    int nestedPropertyOffset = typeInfo.RootType.GetNestedStructureOffset(new SimplePropertyRef(opField));

                    foreach (md.EdmProperty nestedProperty in nestedFlatType.Properties) 
                    {
                        // Try to build up an accessor for this property from the input 
                        Node nestedPropertyValue = BuildAccessor(fieldValue, nestedProperty); 

                        if (null != nestedPropertyValue) 
                        {
                            newFields.Add(flatType.Properties[nestedPropertyOffset]);
                            newFieldValues.Add(nestedPropertyValue);
                        } 

                        nestedPropertyOffset++; 
                    } 
                }
                else 
                {
                    PropertyRef propRef = new SimplePropertyRef(opField);
                    md.EdmProperty outputTypeProp = typeInfo.GetNewProperty(propRef);
 
                    newFields.Add(outputTypeProp);
 
                    newFieldValues.Add(fieldValue); 
                }
 
                childrenIndex++;
            }

            // 
            // We've now handled all the regular properties. Now, walk through all the rel properties -
            // obviously, this only applies for the *NewEntityOps 
            // 
            if (newEntityOp != null)
            { 
                foreach (RelProperty relProp in newEntityOp.RelationshipProperties)
                {
                    Node fieldValue = n.Children[childrenIndex];
                    md.RowType nestedFlatType = m_typeInfo.GetTypeInfo(relProp.ToEnd.TypeUsage).FlattenedType; 

                    // Find offset of opField in top-level flat type 
                    int nestedPropertyOffset = typeInfo.RootType.GetNestedStructureOffset(new RelPropertyRef(relProp)); 

                    foreach (md.EdmProperty nestedProperty in nestedFlatType.Properties) 
                    {
                        // Try to build up an accessor for this property from the input
                        Node nestedPropertyValue = BuildAccessor(fieldValue, nestedProperty);
 
                        if (null != nestedPropertyValue)
                        { 
                            newFields.Add(flatType.Properties[nestedPropertyOffset]); 
                            newFieldValues.Add(nestedPropertyValue);
                        } 

                        nestedPropertyOffset++;
                    }
                    childrenIndex++; 
                }
            } 
 
            //
            // So, now we have the list of all fields that should make up the 
            // flat type.  Create a new node with them.
            //
            NewRecordOp newOp = m_command.CreateNewRecordOp(typeInfo.FlattenedTypeUsage, newFields);
            Node newNode = m_command.CreateNode(newOp, newFieldValues); 

            return newNode; 
        } 

        ///  
        /// NullOp
        ///
        /// If the node represents a null of an entity type it 'flattens' it into a new record,
        /// with at most one non-null value: for the typeIdProperty, if one is needed. 
        /// If the node represents an null of a non-entity type, no special work is done.
        ///  
        /// The NullOp 
        /// The current subtree
        /// the new subtree 
        public override Node Visit(NullOp op, Node n)
        {
            if (!TypeUtils.IsStructuredType(op.Type))
            { 
                return n;
            } 
 
            // Find the new type corresponding to the type
            TypeInfo typeInfo = m_typeInfo.GetTypeInfo(op.Type); 

            List newFields = new List();
            List newFieldValues = new List();
 
            // Add a typeid property if we need one
            if (typeInfo.HasTypeIdProperty) 
            { 
                newFields.Add(typeInfo.TypeIdProperty);
                var typeIdType = md.Helper.GetModelTypeUsage(typeInfo.TypeIdProperty); 
                newFieldValues.Add(CreateNullConstantNode(typeIdType));
            }

            NewRecordOp newRecordOp = new NewRecordOp(typeInfo.FlattenedTypeUsage, newFields); 
            return m_command.CreateNode(newRecordOp, newFieldValues);
        } 
 
        #endregion
 
        #region type comparison ops

        /// 
        /// IsOf 
        ///
        /// Convert an IsOf operator into a typeid comparison: 
        /// 
        ///     IsOfOnly(e, T) => e.TypeId == TypeIdValue(T)
        ///     IsOf(e, T)     => e.TypeId like TypeIdValue(T)% escape null 
        ///
        /// 
        /// The IsOfOp to handle
        /// current isof subtree 
        /// new subtree
        public override Node Visit(IsOfOp op, Node n) 
        { 
            // First visit all my children
            VisitChildren(n); 

            if (!TypeUtils.IsStructuredType(op.IsOfType))
            {
                return n; 
            }
            TypeInfo typeInfo = m_typeInfo.GetTypeInfo(op.IsOfType); 
            Node newNode = CreateTypeComparisonOp(n.Child0, typeInfo, op.IsOfOnly); 
            return newNode;
        } 

        /// 
        /// TreatOp
        /// 
        ///     TreatOp(e, T) => case when e.TypeId like TypeIdValue(T) then T else null end
        ///  
        /// the TreatOp 
        /// the node
        /// new subtree 
        public override Node Visit(TreatOp op, Node n)
        {
            // First visit all my children
            VisitChildren(n); 

            // 
            // filter out useless treat operations 
            // Treat(subtype-instance as superType)
            // 
            ScalarOp arg = (ScalarOp)n.Child0.Op;
            if (op.IsFakeTreat ||
                md.TypeSemantics.IsStructurallyEqual(arg.Type, op.Type) ||
                md.TypeSemantics.IsSubTypeOf(arg.Type, op.Type)) 
            {
                return n.Child0; 
            } 

            // When we support UDTs 
            if (!TypeUtils.IsStructuredType(op.Type))
            {
                return n;
            } 

            // 
            // First, convert this into a CaseOp: 
            //   case when e.TypeId like TypeIdValue then e else null end
            // 
            TypeInfo typeInfo = m_typeInfo.GetTypeInfo(op.Type);
            Node likeNode = CreateTypeComparisonOp(n.Child0, typeInfo, false);
            CaseOp caseOp = m_command.CreateCaseOp(typeInfo.FlattenedTypeUsage);
            Node caseNode = m_command.CreateNode(caseOp, likeNode, n.Child0, CreateNullConstantNode(caseOp.Type)); 

            // 
            // Now "flatten" out this Op into a constructor. But only get the 
            // desired properties
            // 
            PropertyRefList desiredProperties = m_nodePropertyMap[n];
            Node flattenedCaseNode = FlattenCaseOp(caseOp, caseNode, typeInfo, desiredProperties);
            return flattenedCaseNode;
        } 

        ///  
        /// Create a typeid-comparison operator - more specifically, create an 
        /// operator that compares a typeid value with the typeid property of an
        /// input structured type. 
        /// The comparison may be "exact" - in which case we're looking for the exact
        /// type; otherwise, we're looking for any possible subtypes.
        /// The "exact" variant is used by the IsOfOp (only); the other variant is
        /// used by IsOfOp and TreatOp 
        /// 
        /// The input structured type expression 
        /// Augmented type information for the type 
        /// Exact comparison?
        /// New comparison expression 
        private Node CreateTypeComparisonOp(Node input, TypeInfo typeInfo, bool isExact)
        {
            Node typeIdProperty = BuildTypeIdAccessor(input, typeInfo);
            Node newNode = null; 

            if (isExact) 
            { 
                newNode = CreateTypeEqualsOp(typeInfo, typeIdProperty);
            } 
            else
            {
                if (typeInfo.RootType.DiscriminatorMap != null)
                { 
                    // where there are explicit discriminator values, LIKE '0X%' pattern does not work...
                    newNode = CreateDisjunctiveTypeComparisonOp(typeInfo, typeIdProperty); 
                } 
                else
                { 
                    Node typeIdConstantNode = CreateTypeIdConstantForPrefixMatch(typeInfo);
                    LikeOp likeOp = m_command.CreateLikeOp();
                    newNode = m_command.CreateNode(likeOp, typeIdProperty, typeIdConstantNode, CreateNullConstantNode(DefaultTypeIdType));
                } 
            }
            return newNode; 
        } 

        ///  
        /// Create a filter matching all types in the given hierarchy (typeIdProperty IN typeInfo.Hierarchy) e.g.:
        ///
        ///     typeIdProperty = 'Base' OR typeIdProperty = 'Derived1' ...
        /// 
        /// This is called only for types using DiscriminatorMap (explicit discriminator values)
        ///  
        ///  
        /// 
        /// type hierarchy check 
        private Node CreateDisjunctiveTypeComparisonOp(TypeInfo typeInfo, Node typeIdProperty)
        {
            PlanCompiler.Assert(typeInfo.RootType.DiscriminatorMap != null, "should be used only for DiscriminatorMap type checks");
            // collect all non-abstract types in the given hierarchy 
            IEnumerable types = typeInfo.GetTypeHierarchy().Where(t => !t.Type.EdmType.Abstract);
 
            // generate a disjunction 
            Node current = null;
            foreach (TypeInfo type in types) 
            {
                Node typeComparisonNode = CreateTypeEqualsOp(type, typeIdProperty);
                if (null == current)
                { 
                    current = typeComparisonNode;
                } 
                else 
                {
                    current = m_command.CreateNode(m_command.CreateConditionalOp(OpType.Or), current, typeComparisonNode); 
                }
            }
            if (null == current)
            { 
                // only abstract types in this hierarchy... no values possible
                current = m_command.CreateNode(m_command.CreateFalseOp()); 
            } 
            return current;
        } 

        /// 
        /// Generates a node of the form typeIdProperty = typeInfo.TypeId
        ///  
        /// 
        ///  
        /// type equality check 
        private Node CreateTypeEqualsOp(TypeInfo typeInfo, Node typeIdProperty)
        { 
            Node typeIdConstantNode = CreateTypeIdConstant(typeInfo);
            ComparisonOp eqCompOp = m_command.CreateComparisonOp(OpType.EQ);
            Node result = m_command.CreateNode(eqCompOp, typeIdProperty, typeIdConstantNode);
            return result; 
        }
 
        #endregion 

        #endregion 

        #endregion

    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// 
// @owner  [....]
// @backupOwner [....] 
//--------------------------------------------------------------------- 

using System; 
using System.Collections;
using System.Collections.Generic;
//using System.Diagnostics; // Please use PlanCompiler.Assert instead of Debug.Assert in this class...
using System.Globalization; 
using System.Linq;
using System.Data.Common; 
using md = System.Data.Metadata.Edm; 
using System.Data.Query.InternalTrees;
using System.Data.Query.PlanCompiler; 


namespace System.Data.Query.PlanCompiler
{ 
    /// 
    /// The goal of this module is to eliminate all references to nominal types 
    /// in the tree. Additionally, all structured types are replaced by "flat" 
    /// record types - where every field of the structured type is a scalar type.
    /// Note that UDTs are not considered to be structured types. 
    ///
    /// At the end of this phase,
    /// * there are no more nominal types in the tree
    /// * there are no more nested record types in the tree 
    /// * No Var in the tree is of an structured type
    /// * Additionally (and these follow from the statements above) 
    ///   * There are no NewInstanceOp constructors in the tree 
    ///   * There are no PropertyOp operators where the result is a structured type
    /// 
    /// This module uses information from the PropertyPushdown phase to "optimize"
    /// structured type elimination. Essentially, if we can avoid producing pieces
    /// of information that will be discarded later, then lets do that.
    /// 
    /// The general mechanism of type elimination is as follows. We walk up the tree
    /// in a bottom up fashion, and try to convert all structured types into flattened 
    /// record types - type constructors are first converted into flat record constructors 
    /// and then dismantled etc. The barrier points - Vars - are all converted into
    /// scalar types, and all intermediate stages will be eliminated in transition. 
    ///
    /// The output from this phase includes a ColumnMap - which is used later by
    /// the execution model to produce results in the right form from an otherwise
    /// flat query 
    ///
    /// Notes: This phase could be combined later with the PropertyPushdown phase 
    /// 
    /// 
    internal class NominalTypeEliminator : BasicOpVisitorOfNode 
    {

        #region Nested Classes
        ///  
        /// Describes an operation kind - for various property extractions
        ///  
        internal enum OperationKind 
        {
            ///  
            /// Comparing two instances for equality
            /// 
            Equality,
 
            /// 
            /// Checking to see if an instance is null 
            ///  
            IsNull,
 
            /// 
            /// Getting the "identity" of an entity
            /// 
            GetIdentity, 

            ///  
            /// Getting the keys of an entity 
            /// 
            GetKeys, 

            /// 
            /// All properties of an entity
            ///  
            All
        } 
        #endregion 

        #region private state 

        private Dictionary m_varPropertyMap;
        private Dictionary m_nodePropertyMap;
        private VarInfoMap m_varInfoMap; 
        private PlanCompiler m_compilerState;
        private Command m_command { get { return m_compilerState.Command; } } 
        private StructuredTypeInfo m_typeInfo; 
        private Dictionary m_typeToNewTypeMap;
        private const string PrefixMatchCharacter = "%"; // This is ANSI-SQL defined, but it should probably be configurable. 

        #endregion

        #region constructors 

        private NominalTypeEliminator(PlanCompiler compilerState, 
            StructuredTypeInfo typeInfo, 
            Dictionary varPropertyMap,
            Dictionary nodePropertyMap) 
        {
            m_compilerState = compilerState;
            m_typeInfo = typeInfo;
            m_varPropertyMap = varPropertyMap; 
            m_nodePropertyMap = nodePropertyMap;
            m_varInfoMap = new VarInfoMap(); 
            m_typeToNewTypeMap = new Dictionary(TypeUsageEqualityComparer.Instance); 
        }
 
        #endregion

        #region Process Driver
 
        /// 
        /// Eliminates all structural types from the query 
        ///  
        /// current compiler state
        /// list of all referenced types 
        /// list of referenced entitysets
        internal static void Process(PlanCompiler compilerState,
            StructuredTypeInfo structuredTypeInfo)
        { 
#if DEBUG
            //string phase0 = Dump.ToXml(compilerState.Command); 
            Validator.Validate(compilerState); 
#endif
 
            // Phase 1: Top-down property pushdown
            Dictionary varPropertyMap;
            Dictionary nodePropertyMap;
            PropertyPushdownHelper.Process(compilerState.Command, structuredTypeInfo, out varPropertyMap, out nodePropertyMap); 

#if DEBUG 
            //string phase1 = Dump.ToXml(compilerState.Command); 
            Validator.Validate(compilerState);
#endif 

            // Phase 2: actually eliminate nominal types
            NominalTypeEliminator nte = new NominalTypeEliminator(compilerState, structuredTypeInfo,
                varPropertyMap, nodePropertyMap); 
            nte.Process();
 
#if DEBUG 
            //string phase2 = Dump.ToXml(compilerState.Command);
            Validator.Validate(compilerState); 
#endif

#if DEBUG
            //To avoid garbage collection 
            //int size = phase0.Length;
            //size = phase1.Length; 
            //size = phase2.Length; 
#endif
        } 


        /// 
        /// The real driver. Invokes the visitor to traverse the tree bottom-up, 
        /// and modifies the tree along the way.
        ///  
        private void Process() 
        {
            Node rootNode = m_command.Root; 
            PlanCompiler.Assert(rootNode.Op.OpType == OpType.PhysicalProject, "root node is not PhysicalProjectOp?");
            // invoke the visitor on the root node
            rootNode.Op.Accept(this, rootNode);
        } 

        #endregion 
 
        #region type utilities
 
        /// 
        /// The datatype of the typeid property
        /// 
        private md.TypeUsage DefaultTypeIdType 
        {
            get { return m_command.StringType; } 
        } 

        ///  
        /// Get the "new" type corresponding to the input type.
        /// For structured types, we simply look up the typeInfoMap
        /// For collection types, we create a new collection type based on the
        ///   "new" element type. 
        /// For all other types, we simply return the input type
        ///  
        ///  
        /// 
        private md.TypeUsage GetNewType(md.TypeUsage type) 
        {
            md.TypeUsage newType;

            if (m_typeToNewTypeMap.TryGetValue(type, out newType)) 
            {
                return newType; 
            } 

            md.CollectionType collectionType; 
            if (TypeHelpers.TryGetEdmType(type, out collectionType))
            {
                // If this is a collection type, then clone a new collection type
                md.TypeUsage newElementType = GetNewType(collectionType.TypeUsage); 
                newType = TypeUtils.CreateCollectionType(newElementType);
            } 
            else if (TypeUtils.IsStructuredType(type)) 
            {
                // structured type => we've already calculated the input 
                newType = m_typeInfo.GetTypeInfo(type).FlattenedTypeUsage;
            }
            else
            { 
                // "simple" type => return the input type
                newType = type; 
            } 

            // Add this information to the map 
            m_typeToNewTypeMap[type] = newType;
            return newType;
        }
 
        #endregion
 
        #region misc utilities 

        ///  
        /// This function builds a "property accessor" over the input expression.  It
        /// can produce one of three results:
        ///
        ///   - It can return "null", if it is convinced that the input has no 
        ///     such expression
        ///   - It can return a subnode of the input, if that subnode represents 
        ///     the property 
        ///   - Or, it can build a PropertyOp explicitly
        /// 
        /// Assertion: the property is not a structured type
        /// 
        /// The input expression
        /// The desired property 
        /// 
        private Node BuildAccessor(Node input, md.EdmProperty property) 
        { 
            Op inputOp = input.Op;
 
            // Special handling if the input is a NewRecordOp
            NewRecordOp newRecordOp = inputOp as NewRecordOp;
            if (null != newRecordOp)
            { 
                int fieldPos;
                // Identify the specific property we're interested in. 
                if (newRecordOp.GetFieldPosition(property, out fieldPos)) 
                {
                    return Copy(input.Children[fieldPos]); 
                }
                else
                {
                    return null; 
                }
            } 
 
            // special handling if the input is a null
            if (inputOp.OpType == OpType.Null) 
            {
                return null;
            }
 
            // The default case: Simply return a new PropertyOp
            PropertyOp newPropertyOp = m_command.CreatePropertyOp(property); 
            return m_command.CreateNode(newPropertyOp, this.Copy(input)); 
        }
 
        /// 
        /// A BuildAccessor variant. If the appropriate property was not found, then
        /// build up a null constant instead
        ///  
        /// 
        ///  
        ///  
        private Node BuildAccessorWithNulls(Node input, md.EdmProperty property)
        { 
            Node newNode = this.BuildAccessor(input, property);
            if (newNode == null)
            {
                newNode = CreateNullConstantNode(md.Helper.GetModelTypeUsage(property)); 
            }
            return newNode; 
        } 

        ///  
        /// Builds up an accessor to the typeid property. If the type has no typeid
        /// property, then we simply create a constantOp with the corresponding
        /// typeid value for the type
        ///  
        /// the input expression
        /// the original type of the input expression 
        ///  
        private Node BuildTypeIdAccessor(Node input, TypeInfo typeInfo)
        { 
            Node result;

            if (typeInfo.HasTypeIdProperty)
            { 
                result = BuildAccessorWithNulls(input, typeInfo.TypeIdProperty);
            } 
            else 
            {
                result = CreateTypeIdConstant(typeInfo); 
            }

            return result;
        } 

        ///  
        /// Builds a SoftCast operator over the input - if one is necessary. 
        /// 
        /// the input expression to "cast" 
        /// the target type
        /// the "cast"ed expression
        private Node BuildSoftCast(Node node, md.TypeUsage targetType)
        { 
            PlanCompiler.Assert(node.Op.IsScalarOp, "Attempting SoftCast around non-ScalarOp?");
            if (Command.EqualTypes(node.Op.Type, targetType)) 
            { 
                return node;
            } 
            // Skip any castOps we may have created already
            while (node.Op.OpType == OpType.SoftCast)
            {
                node = node.Child0; 
            }
            Node newNode = m_command.CreateNode(m_command.CreateSoftCastOp(targetType), node); 
            return newNode; 
        }
 
        /// 
        /// Clones a subtree.
        /// This is used by the "BuildAccessor" routines to build a property-accessor
        /// over some input. If we're reusing the input, the input must be cloned. 
        /// 
        /// The subtree to copy 
        ///  
        private Node Copy(Node n)
        { 
            return OpCopier.Copy(m_command, n);
        }

        ///  
        /// Returns a node for a null constant of the desired type
        ///  
        ///  
        /// 
        private Node CreateNullConstantNode(md.TypeUsage type) 
        {
            return m_command.CreateNode(m_command.CreateNullOp(type));
        }
 
        /// 
        /// Create a node to represent nullability. 
        ///  
        /// Node for the typeid constant
        private Node CreateNullSentinelConstant() 
        {
            NullSentinelOp op = m_command.CreateNullSentinelOp();
            return m_command.CreateNode(op);
        } 

        ///  
        /// Create a node to represent the exact value of the typeid constant 
        /// 
        /// The current type 
        /// Node for the typeid constant
        private Node CreateTypeIdConstant(TypeInfo typeInfo)
        {
            object value = typeInfo.TypeId; 
            md.TypeUsage typeIdType;
            if (typeInfo.RootType.DiscriminatorMap != null) 
            { 
                typeIdType = md.Helper.GetModelTypeUsage(typeInfo.RootType.DiscriminatorMap.DiscriminatorProperty);
            } 
            else
            {
                typeIdType = DefaultTypeIdType;
            } 
            InternalConstantOp op = m_command.CreateInternalConstantOp(typeIdType, value);
            return m_command.CreateNode(op); 
        } 

        ///  
        /// Create a node to represent a typeid constant for a prefix match.
        /// If the typeid value were "123X", then we would generate a constant
        /// like "123X%"
        ///  
        /// the current type
        /// Node for the typeid constant 
        private Node CreateTypeIdConstantForPrefixMatch(TypeInfo typeInfo) 
        {
            string value = typeInfo.TypeId + PrefixMatchCharacter; 
            InternalConstantOp op = m_command.CreateInternalConstantOp(DefaultTypeIdType, value);
            return m_command.CreateNode(op);
        }
 
        /// 
        /// Identify the list of property refs for comparison and isnull semantics 
        ///  
        /// 
        ///  
        /// 
        private IEnumerable GetPropertyRefsForComparisonAndIsNull(TypeInfo typeInfo, OperationKind opKind)
        {
            PlanCompiler.Assert(opKind == OperationKind.IsNull || opKind == OperationKind.Equality, 
                "Unexpected opKind: " + opKind + "; Can only handle IsNull and Equality");
 
            md.TypeUsage currentType = typeInfo.Type; 

            md.RowType recordType = null; 
            if (TypeHelpers.TryGetEdmType(currentType, out recordType))
            {
                if (opKind == OperationKind.IsNull && typeInfo.HasNullSentinelProperty)
                { 
                    yield return NullSentinelPropertyRef.Instance;
                } 
                else 
                    foreach (md.EdmProperty m in recordType.Properties)
                    { 
                        if (!TypeUtils.IsStructuredType(md.Helper.GetModelTypeUsage(m)))
                        {
                            yield return new SimplePropertyRef(m);
                        } 
                        else
                        { 
                            TypeInfo nestedTypeInfo = m_typeInfo.GetTypeInfo(md.Helper.GetModelTypeUsage(m)); 
                            foreach (PropertyRef p in GetPropertyRefs(nestedTypeInfo, opKind))
                            { 
                                PropertyRef nestedPropertyRef = p.CreateNestedPropertyRef(m);
                                yield return nestedPropertyRef;
                            }
                        } 
                    }
                yield break; 
            } 

            md.EntityType entityType = null; 
            if (TypeHelpers.TryGetEdmType(currentType, out entityType))
            {
                if (opKind == OperationKind.Equality ||
                    (opKind == OperationKind.IsNull && !typeInfo.HasTypeIdProperty)) 
                {
                    foreach (PropertyRef p in typeInfo.GetIdentityPropertyRefs()) 
                    { 
                        yield return p;
                    } 
                }
                else
                {
                    yield return TypeIdPropertyRef.Instance; 
                }
                yield break; 
            } 

            md.ComplexType complexType = null; 
            if (TypeHelpers.TryGetEdmType(currentType, out complexType))
            {
                PlanCompiler.Assert(opKind == OperationKind.IsNull, "complex types not equality-comparable");
                PlanCompiler.Assert(typeInfo.HasNullSentinelProperty, "complex type with no null sentinel property: can't handle isNull"); 
                yield return NullSentinelPropertyRef.Instance;
                yield break; 
            } 

            md.RefType refType = null; 
            if (TypeHelpers.TryGetEdmType(currentType, out refType))
            {
                foreach (PropertyRef p in typeInfo.GetAllPropertyRefs())
                { 
                    yield return p;
                } 
                yield break; 
            }
 
            PlanCompiler.Assert(false, "Unknown type");
        }

        ///  
        /// Get the list of "desired" propertyrefs for the specified type and operation
        ///  
        ///  
        /// 
        ///  
        private IEnumerable GetPropertyRefs(TypeInfo typeInfo, OperationKind opKind)
        {
            PlanCompiler.Assert(opKind != OperationKind.All, "unexpected attempt to GetPropertyRefs(...,OperationKind.All)");
            if (opKind == OperationKind.GetKeys) 
            {
                return typeInfo.GetKeyPropertyRefs(); 
            } 
            else if (opKind == OperationKind.GetIdentity)
            { 
                return typeInfo.GetIdentityPropertyRefs();
            }
            else
            { 
                return GetPropertyRefsForComparisonAndIsNull(typeInfo, opKind);
            } 
        } 

        ///  
        /// Get a list of "desired" properties for each operationKind (specified by the opKind
        /// parameter). The OpKinds we support are
        ///
        ///  * GetKeys 
        ///    Applies only to entity and ref types - gets the key properties (more specifically
        ///      the flattened equivalents) 
        ///  * GetIdentity 
        ///    Applies only to entity and ref types - gets the entityset id property first, and then the
        ///      the Key properties 
        ///  * All
        ///    Gets all properties of the flattened type
        ///
        ///  * Equality 
        ///    Scalar types - the entire instance
        ///    Entity - the identity properties 
        ///    Ref - all properties (= identity properties) 
        ///    Complex/Collection - Not supported
        ///    Record - recurse over each property 
        ///
        ///  * IsNull
        ///    Scalar types - entire instance
        ///    Entity - typeid property, if it exists; otherwise, the key properties 
        ///    ComplexType - typeid property
        ///    Ref - all properties 
        ///    Collection - not supported 
        ///    Record - recurse over each property
        ///  
        /// Type information for the current op
        /// Current operation kind
        /// List of desired properties
        private IEnumerable GetProperties(TypeInfo typeInfo, OperationKind opKind) 
        {
            if (opKind == OperationKind.All) 
            { 
                foreach (md.EdmProperty p in typeInfo.GetAllProperties())
                { 
                    yield return p;
                }
            }
            else 
            {
                foreach (PropertyRef p in GetPropertyRefs(typeInfo, opKind)) 
                { 
                    yield return typeInfo.GetNewProperty(p);
                } 
            }
        }

        ///  
        /// Get a list of properties and value (expressions) for each desired property of the
        /// input. The list of desired properties is based on the opKind parameter. 
        /// The ignoreMissingProperties indicates if we should create a null constant, in case 
        /// the input cannot produce the specified property
        ///  
        /// typeinfo for the input
        /// Current operation kind
        /// The input expression tree
        /// Should we ignore missing properties 
        /// Output: list of properties
        /// Output: correspondng list of values 
        private void GetPropertyValues(TypeInfo typeInfo, OperationKind opKind, Node input, bool ignoreMissingProperties, 
            out List properties, out List values)
        { 

            values = new List();
            properties = new List();
            foreach (md.EdmProperty prop in GetProperties(typeInfo, opKind)) 
            {
                KeyValuePair kv = GetPropertyValue(input, prop, ignoreMissingProperties); 
                if (kv.Value != null) 
                {
                    properties.Add(kv.Key); 
                    values.Add(kv.Value);
                }
            }
        } 

        ///  
        /// Build up a key-value pair of (property, expression) to represent 
        /// the extraction of the appropriate property from the input expression
        ///  
        /// The input (structured type) expression
        /// The property in question
        /// should we ignore missing properties
        ///  
        private KeyValuePair GetPropertyValue(Node input, md.EdmProperty property, bool ignoreMissingProperties)
        { 
            Node n = null; 

            if (!ignoreMissingProperties) 
            {
                n = BuildAccessorWithNulls(input, property);
            }
            else 
            {
                n = BuildAccessor(input, property); 
            } 
            return new KeyValuePair(property, n);
        } 

        /// 
        /// Walk the SortKeys, and expand out
        /// any Structured type Var references 
        /// If any of the sort keys is expanded to include a var representing a null sentinel,
        /// set PlanCompiler.HasSortingOnNullSentinels to true. 
        ///  
        /// The list of input keys
        /// An expanded list of keys. If there is nothing to expand it returns the original list. 
        private List HandleSortKeys(List keys)
        {
            List newSortKeys = new List();
            bool modified = false; 
            foreach (InternalTrees.SortKey k in keys)
            { 
                VarInfo varInfo; 
                if (!m_varInfoMap.TryGetVarInfo(k.Var, out varInfo))
                { 
                    newSortKeys.Add(k);
                }
                else
                { 
                    StructuredVarInfo structuredVarInfo = varInfo as StructuredVarInfo;
                    if (structuredVarInfo != null && structuredVarInfo.NewVarsIncludeNullSentinelVar) 
                    { 
                        m_compilerState.HasSortingOnNullSentinels = true;
                    } 

                    foreach (Var v in varInfo.NewVars)
                    {
                        InternalTrees.SortKey newKey = Command.CreateSortKey(v, k.AscendingSort, k.Collation); 
                        newSortKeys.Add(newKey);
                    } 
                    modified = true; 
                }
            } 

            List result = modified ? newSortKeys : keys;
            return result;
        } 
        #endregion
 
        #region Visitor methods 

        #region AncillaryOp Visitors 

        /// 
        /// VarDefListOp
        /// 
        /// Walks each VarDefOp child, and "expands" it out if the Var is a
        /// structured type. 
        /// 
        /// For each Var that is expanded, a new expression is created to compute
        /// its value (from the original computed expression) 
        /// A new VarDefListOp is created to hold all the "expanded" Varlist
        /// 
        /// 
        ///  
        /// 
        public override Node Visit(VarDefListOp op, Node n) 
        { 
            VisitChildren(n);
 
            List newChildren = new List();

            foreach (Node chi in n.Children)
            { 
                VarDefOp varDefOp = chi.Op as VarDefOp;
 
                if (TypeUtils.IsStructuredType(varDefOp.Var.Type) 
                 || TypeUtils.IsCollectionType(varDefOp.Var.Type))
                { 
                    List newChiList;
                    md.TypeUsage x;

                    FlattenComputedVar((ComputedVar)varDefOp.Var, chi, out newChiList, out x); 

                    foreach (Node newChi in newChiList) 
                    { 
                        newChildren.Add(newChi);
                    } 
                }
                else
                {
                    newChildren.Add(chi); 
                }
            } 
            Node newVarDefListNode = m_command.CreateNode(n.Op, newChildren); 
            return newVarDefListNode;
        } 

        /// 
        /// Helps flatten out a computedVar expression
        ///  
        /// The Var
        /// Subtree rooted at the VarDefOp expression 
        /// list of new nodes produced 
        /// 
        /// VarInfo for this var 
        private void FlattenComputedVar(ComputedVar v, Node node, out List newNodes, out md.TypeUsage newType)
        {
            newNodes = new List();
            Node definingExprNode = node.Child0; // defining expression for the VarDefOp 
            newType = null;
 
            if (TypeUtils.IsCollectionType(v.Type)) 
            {
                newType = GetNewType(v.Type); 
                Var newVar;
                Node newVarDefNode = m_command.CreateVarDefNode(definingExprNode, out newVar);
                newNodes.Add(newVarDefNode);
                m_varInfoMap.CreateCollectionVarInfo(v, newVar); 
                return;
            } 
 
            // Get the "new" type for the Var
            TypeInfo typeInfo = m_typeInfo.GetTypeInfo(v.Type); 
            // Get a list of properties that we think are necessary
            PropertyRefList desiredProperties = m_varPropertyMap[v];
            List newVars = new List();
            List newProps = new List(); 
            newNodes = new List();
            var hasNullSentinelVar = false; 
            foreach (PropertyRef p in typeInfo.PropertyRefList) 
            {
                // do I care for this property? 
                if (!desiredProperties.Contains(p))
                {
                    continue;
                } 

                md.EdmProperty newProperty = typeInfo.GetNewProperty(p); 
 
                //
                // #479467 - Make sure that we build Vars for all properties - if 
                // we are asked to produce all properties. This is extremely important
                // for the top-level Vars
                //
                Node propAccessor = null; 
                if (desiredProperties.AllProperties)
                { 
                    propAccessor = BuildAccessorWithNulls(definingExprNode, newProperty); 
                }
                else 
                {
                    propAccessor = BuildAccessor(definingExprNode, newProperty);
                    if (propAccessor == null)
                    { 
                        continue;
                    } 
                } 

                // Add the new property 
                newProps.Add(newProperty);

                // Create a new VarDefOp.
                Var newVar; 
                Node newVarDefNode = m_command.CreateVarDefNode(propAccessor, out newVar);
                newNodes.Add(newVarDefNode); 
                newVars.Add(newVar); 

                // Check if it is a null sentinel var 
                if (!hasNullSentinelVar && IsNullSentinelPropertyRef(p))
                {
                    hasNullSentinelVar = true;
                } 
            }
            m_varInfoMap.CreateStructuredVarInfo(v, typeInfo.FlattenedType, newVars, newProps, hasNullSentinelVar); 
            return; 
        }
 
        /// 
        /// Is the given propertyRef representing a null sentinel
        /// It is if:
        ///  - it is a NullSentinelPropertyRef 
        ///  - it is a NestedPropertyRef with the outer property being a NullSentinelPropertyRef
        ///  
        ///  
        /// 
        private bool IsNullSentinelPropertyRef(PropertyRef propertyRef) 
        {
            if (propertyRef is NullSentinelPropertyRef)
            {
                return true; 
            }
            NestedPropertyRef nestedPropertyRef = propertyRef as NestedPropertyRef; 
            if (nestedPropertyRef == null) 
            {
                return false; 
            }
            return nestedPropertyRef.OuterProperty is NullSentinelPropertyRef;
        }
 
        #endregion
 
        #region PhysicalOp Visitors 

        ///  
        /// PhysicalProjectOp
        /// 
        /// 
        ///  
        /// 
        public override Node Visit(PhysicalProjectOp op, Node n) 
        { 
            // visit my children
            VisitChildren(n); 

            // flatten out the varset
            VarList newVarList = FlattenVarList(op.Outputs);
            // reflect changes into my column map 
            SimpleCollectionColumnMap newColumnMap = ExpandColumnMap(op.ColumnMap);
            PhysicalProjectOp newOp = m_command.CreatePhysicalProjectOp(newVarList, newColumnMap); 
            n.Op = newOp; 

            return n; 
        }

        private SimpleCollectionColumnMap ExpandColumnMap(SimpleCollectionColumnMap columnMap)
        { 
            VarRefColumnMap varRefColumnMap = columnMap.Element as VarRefColumnMap;
            PlanCompiler.Assert(varRefColumnMap != null, "Encountered a SimpleCollectionColumnMap element that is not VarRefColumnMap when expanding a column map in NominalTypeEliminator."); 
 
            // see if this var has changed in some fashion
            VarInfo varInfo; 
            if (!m_varInfoMap.TryGetVarInfo(varRefColumnMap.Var, out varInfo))
            {
                return columnMap; // no changes
            } 

            // 
            // Ensure that we get the right number of Vars - we need one Var for 
            // each scalar property
            // 
            if (TypeUtils.IsStructuredType(varRefColumnMap.Var.Type))
            {
                TypeInfo typeInfo = m_typeInfo.GetTypeInfo(varRefColumnMap.Var.Type);
                PlanCompiler.Assert(typeInfo.RootType.FlattenedType.Properties.Count == varInfo.NewVars.Count, 
                    "Var count mismatch; Expected " + typeInfo.RootType.FlattenedType.Properties.Count + "; got " + varInfo.NewVars.Count + " instead.");
            } 
 
            // "Process" this columnMap
            ColumnMapProcessor processor = new ColumnMapProcessor(varRefColumnMap, varInfo, m_typeInfo); 
            ColumnMap newColumnMap = processor.ExpandColumnMap();

            //Wrap it with a collection
            SimpleCollectionColumnMap resultColumnMap = new SimpleCollectionColumnMap(TypeUtils.CreateCollectionType(newColumnMap.Type), newColumnMap.Name, newColumnMap, columnMap.Keys, columnMap.ForeignKeys); 

            return resultColumnMap; 
        } 

        #endregion 

        #region RelOp Visitors

        ///  
        /// Walk the input var sequence, flatten each var, and return the new sequence of
        /// Vars 
        ///  
        /// input Var sequence
        /// flattened output var sequence 
        private IEnumerable FlattenVars(IEnumerable vars)
        {
            foreach (Var v in vars)
            { 
                VarInfo varInfo;
 
                if (!m_varInfoMap.TryGetVarInfo(v, out varInfo)) 
                {
                    yield return v; 
                }
                else
                {
                    foreach (Var newVar in varInfo.NewVars) 
                    {
                        yield return newVar; 
                    } 
                }
            } 
        }

        /// 
        /// Probe the current VarSet for "structured" Vars - replace these with the 
        /// corresponding sets of flattened Vars
        ///  
        /// current set of vars 
        /// an "expanded" varset
        private VarVec FlattenVarSet(VarVec varSet) 
        {
            VarVec newVarSet = m_command.CreateVarVec(FlattenVars(varSet));
            return newVarSet;
        } 

        ///  
        /// Build up a new varlist, where each structured var has been replaced by its 
        /// corresponding flattened vars
        ///  
        /// the varlist to flatten
        /// the new flattened varlist
        private VarList FlattenVarList(VarList varList)
        { 
            VarList newVarList = Command.CreateVarList(FlattenVars(varList));
            return newVarList; 
        } 

        ///  
        /// Simply flatten out every var in the keys, and return a new DistinctOp
        /// 
        /// DistinctOp
        /// Current subtree 
        /// 
        public override Node Visit(DistinctOp op, Node n) 
        { 
            VisitChildren(n);
 
            // Simply flatten out all the Vars
            VarVec newKeys = FlattenVarSet(op.Keys);
            n.Op = m_command.CreateDistinctOp(newKeys);
            return n; 
        }
 
        ///  
        /// GroupBy
        /// 
        /// Again, VisitChildren - for the Keys and Properties VarDefList nodes - does
        /// the real work.
        ///
        /// The "Keys" and the "OutputVars" varsets are updated to flatten out 
        /// references to any structured Vars.
        ///  
        ///  
        /// 
        ///  
        public override Node Visit(GroupByOp op, Node n)
        {
            VisitChildren(n);
 
            // update the output Vars and the key vars with the right sets
            VarVec newKeys = FlattenVarSet(op.Keys); 
            VarVec newOutputs = FlattenVarSet(op.Outputs); 

            if (newKeys != op.Keys || newOutputs != op.Outputs) 
            {
                n.Op = m_command.CreateGroupByOp(newKeys, newOutputs);
            }
 
            return n;
        } 
 
        /// 
        /// GroupByInto 
        ///
        /// Again, VisitChildren - for the Keys and Properties VarDefList nodes - does
        /// the real work.
        /// 
        /// The "Keys", "InputVars" and "OutputVars" varsets are updated to flatten out
        /// references to any structured Vars. 
        ///  
        /// 
        ///  
        /// 
        public override Node Visit(GroupByIntoOp op, Node n)
        {
            VisitChildren(n); 

            // update the output Vars and the key vars with the right sets 
            VarVec newKeys = FlattenVarSet(op.Keys); 
            VarVec newInputs = FlattenVarSet(op.Inputs);
            VarVec newOutputs = FlattenVarSet(op.Outputs); 

            if (newKeys != op.Keys || newInputs != op.Inputs || newOutputs != op.Outputs)
            {
                n.Op = m_command.CreateGroupByIntoOp(newKeys, newInputs, newOutputs); 
            }
 
            return n; 
        }
 
        /// 
        /// ProjectOp
        ///
        /// The computedVars (the VarDefList) are processed via the VisitChildren() call 
        /// We then try to update the "Vars" property to flatten out any structured
        /// type Vars - if a new VarSet is produced, then the ProjectOp is cloned 
        ///  
        /// 
        ///  
        /// new subtree
        public override Node Visit(ProjectOp op, Node n)
        {
            VisitChildren(n); 

            // update the output Vars with the right set of information 
            VarVec newVars = FlattenVarSet(op.Outputs); 

            if (op.Outputs != newVars) 
            {
                // If the set of vars is empty, that means we didn;t need any of the Vars
                if (newVars.IsEmpty)
                { 
                    return n.Child0;
                } 
                n.Op = m_command.CreateProjectOp(newVars); 
            }
            return n; 
        }

        /// 
        /// ScanTableOp 
        ///
        /// Visit a scanTable Op. Flatten out the table's record into one column 
        /// for each field. Additionally, set up the VarInfo map appropriately 
        /// 
        ///  
        /// 
        /// new subtree
        public override Node Visit(ScanTableOp op, Node n)
        { 

            Var columnVar = op.Table.Columns[0]; 
            TypeInfo typeInfo = m_typeInfo.GetTypeInfo(columnVar.Type); 
            md.RowType newRowType = typeInfo.FlattenedType;
 
            List properties = new List();
            List keyProperties = new List();
            HashSet declaredProps = new HashSet();
            foreach (md.EdmProperty p in TypeHelpers.GetAllStructuralMembers(columnVar.Type.EdmType)) 
            {
                declaredProps.Add(p.Name); 
            } 
            foreach (md.EdmProperty p in newRowType.Properties)
            { 
                if (declaredProps.Contains(p.Name))
                {
                    properties.Add(p);
                } 
            }
            foreach (PropertyRef pref in typeInfo.GetKeyPropertyRefs()) 
            { 
                md.EdmProperty p = typeInfo.GetNewProperty(pref);
                keyProperties.Add(p); 
            }

            //
            // Create a flattened table definition, and a table with that definiton; 
            //
            TableMD newTableMD = m_command.CreateFlatTableDefinition(properties, keyProperties, op.Table.TableMetadata.Extent); 
            Table newTable = m_command.CreateTableInstance(newTableMD); 

            VarInfo varInfo = m_varInfoMap.CreateStructuredVarInfo(columnVar, newRowType, newTable.Columns, properties); 

            n.Op = m_command.CreateScanTableOp(newTable);
            return n;
        } 

        ///  
        /// Get the *single" var produced by the subtree rooted at this node. 
        /// Returns null, if the node produces more than one var, or less than one
        ///  
        /// the node
        /// the single var produced by the node
        internal static Var GetSingletonVar(Node n)
        { 
            switch (n.Op.OpType)
            { 
                case OpType.Project: 
                    {
                        ProjectOp projectOp = (ProjectOp)n.Op; 
                        return (projectOp.Outputs.Count == 1) ? projectOp.Outputs.First : null;
                    }
                case OpType.ScanTable:
                    { 
                        ScanTableOp tableOp = (ScanTableOp)n.Op;
                        return (tableOp.Table.Columns.Count == 1) ? tableOp.Table.Columns[0] : null; 
                    } 

                case OpType.Filter: 
                case OpType.SingleRow:
                case OpType.Sort:
                case OpType.ConstrainedSort:
                    return GetSingletonVar(n.Child0); 

                case OpType.UnionAll: 
                case OpType.Intersect: 
                case OpType.Except:
                    { 
                        SetOp setOp = (SetOp)n.Op;
                        return (setOp.Outputs.Count == 1) ? setOp.Outputs.First : null;
                    }
 
                case OpType.Unnest:
                    { 
                        UnnestOp unnestOp = (UnnestOp)n.Op; 
                        return unnestOp.Table.Columns.Count == 1 ? unnestOp.Table.Columns[0] : null;
                    } 

                case OpType.Distinct:
                    {
                        DistinctOp distinctOp = (DistinctOp)n.Op; 
                        return (distinctOp.Keys.Count == 1) ? distinctOp.Keys.First : null;
                    } 
 
                default:
                    return null; 
            }
        }

        ///  
        /// ScanViewOp
        /// 
        /// Flatten out the view definition, and return that after 
        /// the appropriate remapping
        ///  
        /// the ScanViewOp
        /// current subtree
        /// the flattened view definition
        public override Node Visit(ScanViewOp op, Node n) 
        {
            // 
            // Get the "single" var produced by the input 
            //
            Var inputVar = GetSingletonVar(n.Child0); 
            PlanCompiler.Assert(inputVar != null, "cannot identify Var for the input node to the ScanViewOp");
            // and the table should have exactly one column
            PlanCompiler.Assert(op.Table.Columns.Count == 1, "table for scanViewOp has more than on column?");
            Var columnVar = op.Table.Columns[0]; 

            Node definingNode = VisitNode(n.Child0); 
 
            VarInfo varInfo;
            if (!m_varInfoMap.TryGetVarInfo(inputVar, out varInfo)) 
            {
                PlanCompiler.Assert(false, "didn't find inputVar for scanViewOp?");
            }
            // we must be dealing with a structured column here 
            StructuredVarInfo svarInfo = (StructuredVarInfo)varInfo;
 
            TypeInfo typeInfo = m_typeInfo.GetTypeInfo(columnVar.Type); 

            // if this view does not represent an entityset, then we're pretty much 
            // done. We simply add a mapping from the columnVar to the list of flattened
            // vars produced by the underlying projectOp
            m_varInfoMap.CreateStructuredVarInfo(columnVar, svarInfo.NewType, svarInfo.NewVars, svarInfo.Fields);
            return definingNode; 
        }
 
        ///  
        /// Convert a SortOp. Specifically, walk the SortKeys, and expand out
        /// any Structured type Var references 
        /// 
        /// the sortOp
        /// the current node
        /// new subtree 
        public override Node Visit(SortOp op, Node n)
        { 
            VisitChildren(n); 

            List newSortKeys = HandleSortKeys(op.Keys); 

            if (newSortKeys != op.Keys)
            {
                n.Op = m_command.CreateSortOp(newSortKeys); 
            }
            return n; 
        } 

        ///  
        /// UnnestOp
        ///
        /// Converts an UnnestOp to the right shape.
        /// Flattens out the Table instance, and updates 
        /// 
        ///  
        ///  
        /// new subtree
        public override Node Visit(UnnestOp op, Node n) 
        {
            // Visit the children first
            VisitChildren(n);
 
            md.TypeUsage newType;
 
            if (n.HasChild0) 
            {
                Node chi = n.Child0; 
                VarDefOp varDefOp = chi.Op as VarDefOp;

                if (null != varDefOp)
                { 
                    if (TypeUtils.IsStructuredType(varDefOp.Var.Type)
                     || TypeUtils.IsCollectionType(varDefOp.Var.Type)) 
                    { 
                        List newChildren = new List();
 
                        FlattenComputedVar((ComputedVar)varDefOp.Var, chi, out newChildren, out newType);
                        PlanCompiler.Assert(newChildren.Count == 1, "Flattening unnest var produced more than one Var");
                        n.Child0 = newChildren[0];
                    } 
                }
            } 
 
            // Create a new unnestVar
            VarInfo unnestVarInfo; 
            Var newUnnestVar;
            if (!m_varInfoMap.TryGetVarInfo(op.Var, out unnestVarInfo))
            {
                throw EntityUtil.InternalError(EntityUtil.InternalErrorCode.WrongVarType); 
            }
            else if (!unnestVarInfo.IsCollectionType) 
            { 
                throw EntityUtil.InternalError(EntityUtil.InternalErrorCode.WrongVarType);
            } 
            else
            {
                newUnnestVar = ((CollectionVarInfo)unnestVarInfo).NewVar;
            } 

            // 
            // Flatten out the table 
            // Create a flattened table definition, and a table with that definiton;
            // 
            Table newTable = op.Table;
            Var columnVar = op.Table.Columns[0];
            if (TypeUtils.IsStructuredType(columnVar.Type))
            { 
                TypeInfo typeInfo = m_typeInfo.GetTypeInfo(columnVar.Type);
                md.RowType newRowType = typeInfo.FlattenedType; 
                TableMD newTableMD = m_command.CreateFlatTableDefinition(newRowType); 

                newTable = m_command.CreateTableInstance(newTableMD); 
                VarInfo outputVarInfo = m_varInfoMap.CreateStructuredVarInfo(columnVar, newRowType, newTable.Columns, newRowType.Properties.ToList());
            }

            // Create a new UnnestOp 
            n.Op = m_command.CreateUnnestOp(newUnnestVar, newTable);
            return n; 
        } 

        #region SetOps 

        /// 
        /// SetOp
        /// 
        /// Converts all SetOps - union/intersect/except.
        /// Calls VisitChildren() to do the bulk of the work. After that, the VarMaps 
        /// need to be updated to reflect the removal of any structured Vars 
        /// 
        ///  
        /// 
        /// new subtree
        protected override Node VisitSetOp(SetOp op, Node n)
        { 
            VisitChildren(n);
 
            // Now walk through the first VarMap, and identify the Vars that are needed 
            for (int i = 0; i < op.VarMap.Length; i++)
            { 
                List newComputedVars;
                op.VarMap[i] = FlattenVarMap(op.VarMap[i], out newComputedVars);
                if (newComputedVars != null)
                { 
                    n.Children[i] = FixupSetOpChild(n.Children[i], op.VarMap[i], newComputedVars);
                } 
            } 

            // now get the set of Vars that we will actually need 
            op.Outputs.Clear();
            foreach (Var v in op.VarMap[0].Keys)
            {
                op.Outputs.Set(v); 
            }
            return n; 
        } 

        ///  
        /// Fixes up a SetOp child.
        /// As part of Var flattening, it may so happen that the outer var in the VarMap
        /// may require a property that has no corresponding analog in the inner Var
        /// This logically implies that the corresponding inner property is null. H 
        /// What we do here is to throw an additional projectOp over the setOp child to
        /// add computed Vars (whose defining expressions are null constants) for each 
        /// of those missing properties 
        /// 
        /// one child of the setop 
        /// the varmap for this child
        /// list of new Vars produced
        /// new node for the setOpchild (if any)
        private Node FixupSetOpChild(Node setOpChild, VarMap varMap, List newComputedVars) 
        {
            PlanCompiler.Assert(null != setOpChild, "null setOpChild?"); 
            PlanCompiler.Assert(null != varMap, "null varMap?"); 
            PlanCompiler.Assert(null != newComputedVars, "null newComputedVars?");
 
            // Walk through the list of Vars that have no inner analog, and create
            // a computed Var for each of them
            VarVec newVarSet = m_command.CreateVarVec();
            foreach (KeyValuePair kv in varMap) 
            {
                newVarSet.Set(kv.Value); 
            } 

            List varDefOpNodes = new List(); 
            foreach (Var v in newComputedVars)
            {
                VarDefOp varDefOp = m_command.CreateVarDefOp(v);
                Node varDefOpNode = m_command.CreateNode(varDefOp, CreateNullConstantNode(v.Type)); 
                varDefOpNodes.Add(varDefOpNode);
            } 
            Node varDefListNode = m_command.CreateNode(m_command.CreateVarDefListOp(), varDefOpNodes); 
            ProjectOp projectOp = m_command.CreateProjectOp(newVarSet);
            Node projectNode = m_command.CreateNode(projectOp, setOpChild, varDefListNode); 
            return projectNode;
        }

        ///  
        /// Flattens out a VarMap.
        /// 
        /// Any structured type Vars are expanded out; and collection type Vars 
        /// are replaced by new Vars that reflect the new collection types.
        /// 
        /// There is one special case when dealing with Structured type Vars -
        /// the output and input vars may no longer be 1-1; specifically, there
        /// may be no input Var corresponding to an output var. In such cases, we
        /// build up a new ComputedVar (with an expected value of null), and use that 
        /// in place of the inner var. A subsequent stage will inspect the list of
        /// new ComputedVars, and perform the appropriate fixups 
        ///  
        /// The VarMap to fixup
        /// list of any new computedVars that are created 
        /// a new VarMap
        private VarMap FlattenVarMap(VarMap varMap, out List newComputedVars)
        {
            newComputedVars = null; 

            VarMap newVarMap = new VarMap(); 
            foreach (KeyValuePair kv in varMap) 
            {
                VarInfo innerVarInfo; 
                VarInfo outerVarInfo;
                // Does the inner var have a Varinfo - if not, simply add it
                // to the VarMap, and continue.
                // Otherwise, the Outer var must have a VarInfo too 
                if (!m_varInfoMap.TryGetVarInfo(kv.Value, out innerVarInfo))
                { 
                    newVarMap.Add(kv.Key, kv.Value); 
                }
                else 
                {
                    // get my own var info
                    if (!m_varInfoMap.TryGetVarInfo(kv.Key, out outerVarInfo))
                    { 
                        outerVarInfo = FlattenSetOpVar((SetOpVar)kv.Key);
                    } 
 
                    // If this Var represents a collection type, then simply
                    // replace the singleton Var 
                    if (outerVarInfo.IsCollectionType)
                    {
                        newVarMap.Add(((CollectionVarInfo)outerVarInfo).NewVar, ((CollectionVarInfo)innerVarInfo).NewVar);
                    } 
                    else
                    { // structured type 
                        StructuredVarInfo outerSvarInfo = (StructuredVarInfo)outerVarInfo; 
                        StructuredVarInfo innerSvarInfo = (StructuredVarInfo)innerVarInfo;
 
                        // walk through each property, and find the innerVar corresponding
                        // to that property
                        foreach (md.EdmProperty prop in outerSvarInfo.Fields)
                        { 
                            Var outerVar;
                            Var innerVar; 
                            bool ret = outerSvarInfo.TryGetVar(prop, out outerVar); 
                            PlanCompiler.Assert(ret, "Could not find VarInfo for prop " + prop.Name);
 
                            if (!innerSvarInfo.TryGetVar(prop, out innerVar))
                            {
                                // we didn't find a corresponding innerVar.
                                innerVar = m_command.CreateComputedVar(outerVar.Type); 
                                if (newComputedVars == null)
                                { 
                                    newComputedVars = new List(); 
                                }
                                newComputedVars.Add((ComputedVar)innerVar); 
                            }
                            newVarMap.Add(outerVar, innerVar);
                        }
                    } 
                }
            } 
            return newVarMap; 
        }
 
        /// 
        /// Flattens a SetOpVar (used in SetOps). Simply produces a list of
        /// properties corresponding to each desired property
        ///  
        /// 
        ///  
        private VarInfo FlattenSetOpVar(SetOpVar v) 
        {
            if (TypeUtils.IsCollectionType(v.Type)) 
            {
                md.TypeUsage newType = GetNewType(v.Type);
                Var newVar = m_command.CreateSetOpVar(newType);
                return m_varInfoMap.CreateCollectionVarInfo(v, newVar); 
            }
 
            // Get the "new" type for the Var 
            TypeInfo typeInfo = m_typeInfo.GetTypeInfo(v.Type);
            // Get a list of properties that we think are necessary 
            PropertyRefList desiredProperties = m_varPropertyMap[v];
            List newVars = new List();
            List newProps = new List();
            bool hasNullSentinelVar = false; 
            foreach (PropertyRef p in typeInfo.PropertyRefList)
            { 
                if (!desiredProperties.Contains(p)) 
                {
                    continue; 
                }
                md.EdmProperty newProperty = typeInfo.GetNewProperty(p);
                newProps.Add(newProperty);
                SetOpVar newVar = m_command.CreateSetOpVar(md.Helper.GetModelTypeUsage(newProperty)); 
                newVars.Add(newVar);
 
                // Check if it is a null sentinel var 
                if (!hasNullSentinelVar && IsNullSentinelPropertyRef(p))
                { 
                    hasNullSentinelVar = true;
                }
            }
            VarInfo varInfo = m_varInfoMap.CreateStructuredVarInfo(v, typeInfo.FlattenedType, newVars, newProps, hasNullSentinelVar); 
            return varInfo;
        } 
 
        #endregion
 
        #region DML RelOps

        //
        // DML RelOps are technically very simple - we should simply visit the 
        // children. However, I will defer this to when we actually support DML
        // so for now, the default implementation in the basicVisitor is to throw 
        // unimplemented and that's good enough. 
        //
 
        #endregion

        #endregion
 
        #region ScalarOp Visitors
 
        ///  
        /// SoftCastOp
        /// 
        /// Visit the children first.
        ///
        /// If this is an entity type, complextype or ref type, simply return the
        ///   visited child. (Rationale: These must be in the same type hierarchy; or 
        ///   the earlier stages of query would have barfed. And, we end up
        ///   using the same "flat" type for every type in the hierarchy) 
        /// 
        /// If this is a scalar type, then simply return the current node
        /// 
        /// If this is a collection type, then create a new softcastOp over the input
        ///  (the collection type may have changed)
        ///
        /// Otherwise, we're dealing with a record type. Since our earlier 
        /// definitions of equivalence required that equivalent record types must
        /// have the same number of fields, with "promotable" types, and in the same 
        /// order; *and* since we asked for all properties (see PropertyPushdownHelper), 
        /// the input must be a NewRecordOp, whose fields line up 1-1 with our fields.
        /// Build up a new NewRecordOp based on the arguments to the input NewRecordOp, 
        /// and build up SoftCastOps for any field whose type does not match
        /// 
        /// 
        ///  
        /// 
        public override Node Visit(SoftCastOp op, Node n) 
        { 
            md.TypeUsage inputTypeUsage = n.Child0.Op.Type;
            md.TypeUsage oldType = op.Type; 

            // Always think of your children first
            VisitChildren(n);
 
            md.TypeUsage newType = GetNewType(oldType);
 
            if (md.TypeSemantics.IsRowType(oldType)) 
            {
                PlanCompiler.Assert(n.Child0.Op.OpType == OpType.NewRecord, "Expected a record constructor here. Found " + n.Child0.Op.OpType + " instead"); 

                TypeInfo inputTypeInfo = m_typeInfo.GetTypeInfo(inputTypeUsage);
                TypeInfo outputTypeInfo = m_typeInfo.GetTypeInfo(op.Type);
 
                NewRecordOp newOp = m_command.CreateNewRecordOp(newType);
 
                List newArgs = new List(); 

                // We have to adjust for when we're supposed to add/remove null sentinels; 
                // it is entirely possible that we may need to add multiple null sentinel
                // columns (See SQLBUDT #549068 for an example).
                IEnumerator outputs = newOp.Properties.GetEnumerator();
                int outputPropertyCount = newOp.Properties.Count; 
                outputs.MoveNext();
 
                IEnumerator inputs = n.Child0.Children.GetEnumerator(); 
                int inputPropertyCount = n.Child0.Children.Count;
                inputs.MoveNext(); 

                // We know that all Null Sentinels are added on the left side, so we'll
                // just keep adding them until we have the same number of properties on
                // both the input and the output... 
                while (inputPropertyCount < outputPropertyCount)
                { 
                    PlanCompiler.Assert(outputTypeInfo.HasNullSentinelProperty && !inputTypeInfo.HasNullSentinelProperty, "NullSentinelProperty mismatch on input?"); 

                    // make up a null sentinel; the output requires it. 
                    newArgs.Add(CreateNullSentinelConstant());
                    outputs.MoveNext();
                    outputPropertyCount--;
                } 

                // Likewise, we'll just drop any null sentinel columns from the input until 
                // we have the same number of columns... 
                while (inputPropertyCount > outputPropertyCount)
                { 
                    PlanCompiler.Assert(!outputTypeInfo.HasNullSentinelProperty && inputTypeInfo.HasNullSentinelProperty, "NullSentinelProperty mismatch on output?");

                    // remove the null sentinel; the output doesn't require it.
                    inputs.MoveNext(); 
                    inputPropertyCount--;
                } 
 
                do
                { 
                    md.EdmProperty p = outputs.Current;
                    Node arg = BuildSoftCast(inputs.Current, md.Helper.GetModelTypeUsage(p));
                    newArgs.Add(arg);
                    outputs.MoveNext(); 
                }
                while (inputs.MoveNext()); 
 
                Node newNode = m_command.CreateNode(newOp, newArgs);
                return newNode; 
            }
            else if (md.TypeSemantics.IsCollectionType(oldType))
            {
                // 
                // Our collection type may have changed - 'coz the
                // element type of the collection may have changed. 
                // Simply build up a new castOp (if necessary) 
                //
                return BuildSoftCast(n.Child0, newType); 
            }
            else if (md.TypeSemantics.IsPrimitiveType(oldType))
            {
                // How primitive! Well, the Prime Directive prohibits me 
                // from doing much with these.
                return n; 
            } 
            else
            { 
                PlanCompiler.Assert(md.TypeSemantics.IsNominalType(oldType) ||
                    md.TypeSemantics.IsReferenceType(oldType),
                    "Gasp! Not a nominal type or even a reference type");
                // I'm dealing with a nominal type (entity, complex type) or 
                // a reference type here. Every type in the same hierarchy
                // must have been rationalized into the same type, and so, we 
                // won't need to do anything special 
                PlanCompiler.Assert(Command.EqualTypes(newType, n.Child0.Op.Type),
                    "Types are not equal"); 
                return n.Child0;
            }
        }
 
        /// 
        /// 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. 
        /// 
        /// the CaseOp 
        /// corresponding node
        /// new subtree
        public override Node Visit(CaseOp op, Node n)
        { 
            // Before visiting the children, check whether the case statment can be optimized
            bool thenClauseIsNull; 
            bool 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.
            if (TypeUtils.IsCollectionType(op.Type))
            { 
                md.TypeUsage 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. 
                PropertyRefList desiredProperties = m_nodePropertyMap[n];
                Node newNode = FlattenCaseOp(op, n, m_typeInfo.GetTypeInfo(op.Type), desiredProperties); 
                return newNode; 
            }
            else 
            {
                return n;
            }
        } 

        ///  
        /// Given a case statement 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 rewrittes 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. 
        ///
        /// The rewrite only happens if: 
        ///     (1) Y has null sentinel, and 
        ///     (2) Y is a NewRecordOp.
        ///  
        /// 
        /// 
        /// 
        /// Whether a rewrite was done 
        private bool TryRewriteCaseOp(Node n, bool thenClauseIsNull, out Node rewrittenNode)
        { 
            rewrittenNode = n; 

            //If the type of the case op does not have a null sentinel, we can't do the rewrite. 
            if (!m_typeInfo.GetTypeInfo(n.Op.Type).HasNullSentinelProperty)
            {
                return false;
            } 

            Node resultNode = thenClauseIsNull ? n.Child2 : n.Child1; 
            if (resultNode.Op.OpType != OpType.NewRecord) 
            {
                return false; 
            }

            //Rewrite the null sentinel, which is the first child of the resultNode
            Node currentNullSentinel = resultNode.Child0; 
            md.TypeUsage integerType = this.m_command.IntegerType;
            PlanCompiler.Assert(currentNullSentinel.Op.Type.EdmEquals(integerType), "Column that is expected to be a null sentinel is not of Integer type."); 
 
            CaseOp newCaseOp = m_command.CreateCaseOp(integerType);
            List children = new List(3); 

            //The the 'when' from the case statement
            children.Add(n.Child0);
 
            Node nullSentinelNullNode = m_command.CreateNode(m_command.CreateNullOp(integerType));
            Node nullSentinelThenNode = thenClauseIsNull ? nullSentinelNullNode : currentNullSentinel; 
            Node nullSentinelElseNode = thenClauseIsNull ? currentNullSentinel : nullSentinelNullNode; 
            children.Add(nullSentinelThenNode);
            children.Add(nullSentinelElseNode); 

            //Use the case op as a new null sentinel
            resultNode.Child0 = m_command.CreateNode(newCaseOp, children);
 
            rewrittenNode = resultNode;
            return true; 
        } 

        ///  
        /// 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. 
        ///  
        /// the CaseOp
        /// Node corresponding to the CaseOp 
        /// Information about the type
        /// Set of properties desired
        /// 
        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 fieldTypes = new List();
            List fieldValues = new List(); 

            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 caseChildren = new List();
                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); 
        } 

        ///  
        /// CollectOp
        ///
        /// Nothing much to do - simply update the result type
        ///  
        /// the NestOp
        /// corresponding node 
        /// new subtree 
        public override Node Visit(CollectOp op, Node n)
        { 
            VisitChildren(n);
            // simply update the desired type
            n.Op = m_command.CreateCollectOp(GetNewType(op.Type));
            return n; 
        }
 
        ///  
        /// ComparisonOp
        /// 
        /// If the inputs to the comparisonOp are Refs/records/entitytypes, then
        /// we need to flatten these out. Of course, the only reasonable comparisons
        /// should be EQ and NE
        ///  
        /// 
        ///  
        ///  
        public override Node Visit(ComparisonOp op, Node n)
        { 
            md.TypeUsage child0Type = ((ScalarOp)n.Child0.Op).Type;
            md.TypeUsage child1Type = ((ScalarOp)n.Child1.Op).Type;

            if (!TypeUtils.IsStructuredType(child0Type)) 
            {
                return VisitScalarOpDefault(op, n); 
            } 

            VisitChildren(n); // visit the children first 

            // We're now dealing with a structured type
            PlanCompiler.Assert(!(md.TypeSemantics.IsComplexType(child0Type) || md.TypeSemantics.IsComplexType(child1Type)), "complex type?"); // cannot be a complex type
            PlanCompiler.Assert(op.OpType == OpType.EQ || op.OpType == OpType.NE, "non-equality comparison of structured types?"); 

            // 
            // Strictly speaking, we should be able to use the typeinfo of either of the arguments. 
            // However, as things stand today, we do have scenarios where the types on the
            // two sides (records mainly) are equivalent, but not identical. This non-identicality 
            // may involve the field types being different, the field names being different etc. - but
            // we may be assured that the order of the field types is fixed.
            //
            TypeInfo child0TypeInfo = m_typeInfo.GetTypeInfo(child0Type); 
            TypeInfo child1TypeInfo = m_typeInfo.GetTypeInfo(child1Type);
            List properties1; 
            List properties2; 
            List values1;
            List values2; 

            // get a list of the relevant properties and values from each of the children

            GetPropertyValues(child0TypeInfo, OperationKind.Equality, n.Child0, false, out properties1, out values1); 
            GetPropertyValues(child1TypeInfo, OperationKind.Equality, n.Child1, false, out properties2, out values2);
 
            PlanCompiler.Assert((properties1.Count == properties2.Count) && (values1.Count == values2.Count), "different shaped structured types?"); 

            // Build up an and-chain of comparison ops on the property values 
            Node andNode = null;
            for (int i = 0; i < values1.Count; i++)
            {
                ComparisonOp newCompOp = m_command.CreateComparisonOp(op.OpType); 
                Node newCompNode = m_command.CreateNode(newCompOp, values1[i], values2[i]);
                if (null == andNode) 
                    andNode = newCompNode; 
                else
                    andNode = m_command.CreateNode(m_command.CreateConditionalOp(OpType.And), andNode, newCompNode); 
            }
            return andNode;
        }
 
        /// 
        /// ConditionalOp 
        /// 
        /// IsNull requires special handling.
        ///  
        /// 
        /// 
        /// 
        public override Node Visit(ConditionalOp op, Node n) 
        {
            if (op.OpType != OpType.IsNull) 
            { 
                return VisitScalarOpDefault(op, n);
            } 

            //
            // Special handling for IS NULL ops on structured types
            // 
            // For structured types, we simply convert this into an AND chain of
            // IS NULL predicates, one for each property. There are a couple of 
            // optimizations that we perform. 
            //
            // For entity types, we simply perfom the IS NULL operations on the 
            // key attributes alone.
            //
            // Complex types must have a typeid property - the isnull is pushed to the
            // typeid property 
            //
            // We do NOT support IsNull for Collections 
            // 

            md.TypeUsage childOpType = ((ScalarOp)n.Child0.Op).Type; 

            // Special cases are for structured types only
            if (!TypeUtils.IsStructuredType(childOpType))
            { 
                return VisitScalarOpDefault(op, n);
            } 
 
            // visit the children first
            VisitChildren(n); 

            TypeInfo typeInfo = m_typeInfo.GetTypeInfo(childOpType);

            // Otherwise, build up an and-chain of is null checks for each appropriate 
            // property - which should consist only of key properties for Entity types.
            List properties = null; 
            List values = null; 
            GetPropertyValues(typeInfo, OperationKind.IsNull, n.Child0, false, out properties, out values);
 
            PlanCompiler.Assert(properties.Count == values.Count && properties.Count > 0, "No properties returned from GetPropertyValues(IsNull)?");

            Node andNode = null;
            foreach (Node propertyValue in values) 
            {
                Node isNullNode = m_command.CreateNode(m_command.CreateConditionalOp(OpType.IsNull), propertyValue); 
                if (andNode == null) 
                    andNode = isNullNode;
                else 
                    andNode = m_command.CreateNode(m_command.CreateConditionalOp(OpType.And), andNode, isNullNode);
            }
            return andNode;
        } 

        ///  
        /// Convert a ConstrainedSortOp. Specifically, walk the SortKeys, and expand out 
        /// any Structured type Var references
        ///  
        /// the constrainedSortOp
        /// the current node
        /// new subtree
        public override Node Visit(ConstrainedSortOp op, Node n) 
        {
            VisitChildren(n); 
 
            List newSortKeys = HandleSortKeys(op.Keys);
 
            if (newSortKeys != op.Keys)
            {
                n.Op = m_command.CreateConstrainedSortOp(newSortKeys, op.WithTies);
            } 
            return n;
        } 
 
        /// 
        /// GetEntityKeyOp 
        /// 
        /// 
        /// 
        ///  
        public override Node Visit(GetEntityRefOp op, Node n)
        { 
            return FlattenGetKeyOp(op, n); 
        }
 
        /// 
        /// GetRefKeyOp
        /// 
        ///  
        /// 
        ///  
        public override Node Visit(GetRefKeyOp op, Node n) 
        {
            return FlattenGetKeyOp(op, n); 
        }

        /// 
        /// GetEntityKeyOp/GetRefKeyOp common handling 
        ///
        /// In either case, get the "key" properties from the input entity/ref, and 
        /// build up a record constructor from these values 
        /// 
        /// the GetRefKey/GetEntityKey op 
        /// current subtree
        /// new expression subtree
        private Node FlattenGetKeyOp(ScalarOp op, Node n)
        { 
            PlanCompiler.Assert(op.OpType == OpType.GetEntityRef || op.OpType == OpType.GetRefKey, "Expecting GetEntityRef or GetRefKey ops");
 
            TypeInfo inputTypeInfo = m_typeInfo.GetTypeInfo(((ScalarOp)n.Child0.Op).Type); 
            TypeInfo outputTypeInfo = m_typeInfo.GetTypeInfo(op.Type);
 
            // Visit the child - will flatten out the input ref/entity
            VisitChildren(n);

            // Get "key" properties (and the corresponding values) from the input 
            List inputFieldTypes;
            List inputFieldValues; 
 
            // Get the key properties for GetRefKey; get the Identity properties
            // for GetEntityRef 
            if (op.OpType == OpType.GetRefKey)
            {
                GetPropertyValues(inputTypeInfo, OperationKind.GetKeys, n.Child0, false /* ignore missing props */, out inputFieldTypes, out inputFieldValues);
            } 
            else
            { 
                PlanCompiler.Assert(op.OpType == OpType.GetEntityRef, 
                    "Expected OpType.GetEntityRef: Found " + op.OpType);
                GetPropertyValues(inputTypeInfo, OperationKind.GetIdentity, n.Child0, false, out inputFieldTypes, out inputFieldValues); 
            }

            if (outputTypeInfo.HasNullSentinelProperty && !inputTypeInfo.HasNullSentinelProperty)
            { 
                // Add a null sentinel column, the input doesn't have one but the output requires it.
                inputFieldValues.Insert(0, CreateNullSentinelConstant()); 
            } 

            // create an appropriate record constructor 
            List outputFieldTypes = new List(outputTypeInfo.FlattenedType.Properties);
            PlanCompiler.Assert(inputFieldValues.Count == outputFieldTypes.Count, "fieldTypes.Count mismatch?");

            NewRecordOp rec = m_command.CreateNewRecordOp(outputTypeInfo.FlattenedTypeUsage, outputFieldTypes); 
            Node newNode = m_command.CreateNode(rec, inputFieldValues);
            return newNode; 
        } 

        ///  
        /// Common handler for PropertyOp and RelPropertyOp
        /// 
        /// 
        ///  
        /// 
        ///  
        private Node VisitPropertyOp(Op op, Node n, PropertyRef propertyRef) 
        {
            PlanCompiler.Assert(op.OpType == OpType.Property || op.OpType == OpType.RelProperty, 
                "Unexpected optype: " + op.OpType);

            md.TypeUsage inputType = n.Child0.Op.Type;
            md.TypeUsage outputType = op.Type; 

            // First visit all my children 
            VisitChildren(n); 

            // If the instance is not a structured type (ie) it is a udt, then there 
            // is little for us to do. Simply return
            if (TypeUtils.IsUdt(inputType))
            {
                return n; 
            }
 
            Node newNode = null; 
            TypeInfo inputTypeInfo = m_typeInfo.GetTypeInfo(inputType);
 
            if (TypeUtils.IsStructuredType(outputType))
            {
                TypeInfo outputTypeInfo = m_typeInfo.GetTypeInfo(outputType);
                List fieldTypes = new List(); 
                List fieldValues = new List();
                PropertyRefList expectedProperties = m_nodePropertyMap[n]; 
 
                foreach (PropertyRef npr in outputTypeInfo.PropertyRefList)
                { 
                    // Is this a property that's desired by my consumers?
                    if (expectedProperties.Contains(npr))
                    {
                        PropertyRef newPropRef = npr.CreateNestedPropertyRef(propertyRef); 
                        md.EdmProperty outputNestedProp = outputTypeInfo.GetNewProperty(npr);
                        md.EdmProperty newNestedProp = inputTypeInfo.GetNewProperty(newPropRef); 
 
                        Node field = BuildAccessor(n.Child0, newNestedProp);
                        if (null != field) 
                        {
                            fieldTypes.Add(outputNestedProp);
                            fieldValues.Add(field);
                        } 
                    }
                } 
                Op newRecordOp = m_command.CreateNewRecordOp(outputTypeInfo.FlattenedTypeUsage, fieldTypes); 
                newNode = m_command.CreateNode(newRecordOp, fieldValues);
            } 
            else
            {
                md.EdmProperty newProp = inputTypeInfo.GetNewProperty(propertyRef);
                // Build an accessor over the new property 
                newNode = this.BuildAccessorWithNulls(n.Child0, newProp);
            } 
            return newNode; 
        }
 
        /// 
        /// PropertyOp
        ///
        /// If this is a scalar/collection property, then simply get the appropriate 
        /// field out.
        /// 
        /// Otherwise, build up a record constructor corresponding to the result 
        /// type - optimize this by only getting those properties that are needed
        /// 
        /// If the instance is not a structured type (ie) it is a UDT, then simply return
        ///
        /// 
        /// the PropertyOp 
        /// the corresponding node
        /// new subtree 
        public override Node Visit(PropertyOp op, Node n) 
        {
            return VisitPropertyOp(op, n, new SimplePropertyRef(op.PropertyInfo)); 
        }

        /// 
        /// RelPropertyOp. Pick out the appropriate property from the child 
        /// 
        ///  
        ///  
        /// 
        public override Node Visit(RelPropertyOp op, Node n) 
        {
            return VisitPropertyOp(op, n, new RelPropertyRef(op.PropertyInfo));
        }
 
        /// 
        /// RefOp 
        /// 
        /// Simply convert this into the corresponding record type - with one
        /// field for each key, and one for the entitysetid 
        /// 
        /// 
        /// 
        ///  
        public override Node Visit(RefOp op, Node n)
        { 
            TypeInfo inputTypeInfo = m_typeInfo.GetTypeInfo(((ScalarOp)n.Child0.Op).Type); 
            TypeInfo outputTypeInfo = m_typeInfo.GetTypeInfo(op.Type);
 
            // visit children now
            VisitChildren(n);

            // Get the list of fields and properties from the input (key) op 
            List inputFields;
            List inputFieldValues; 
            GetPropertyValues(inputTypeInfo, OperationKind.All, n.Child0, false, out inputFields, out inputFieldValues); 

            // Get my property list 
            List outputFields = new List(outputTypeInfo.FlattenedType.Properties);

            if (outputTypeInfo.HasEntitySetIdProperty)
            { 
                PlanCompiler.Assert(outputFields[0] == outputTypeInfo.EntitySetIdProperty, "OutputField0 must be the entitySetId property");
 
                if (inputTypeInfo.HasNullSentinelProperty && !outputTypeInfo.HasNullSentinelProperty) 
                {  // realistically, REFs can't have null sentinels, but I'm being pedantic...
                    PlanCompiler.Assert(outputFields.Count == inputFields.Count, "Mismatched field count: Expected " + inputFields.Count + "; Got " + outputFields.Count); 
                    RemoveNullSentinel(inputTypeInfo, inputFields, inputFieldValues, outputFields);
                }
                else
                { 
                    PlanCompiler.Assert(outputFields.Count == inputFields.Count + 1, "Mismatched field count: Expected " + (inputFields.Count + 1) + "; Got " + outputFields.Count);
                } 
 
                // Now prepend a value for the entitysetid property and a value for this property
                int entitySetId = m_typeInfo.GetEntitySetId(op.EntitySet); 
                inputFieldValues.Insert(0, m_command.CreateNode(m_command.CreateInternalConstantOp(md.Helper.GetModelTypeUsage(outputTypeInfo.EntitySetIdProperty), entitySetId)));
            }
            else
            { 
                if (inputTypeInfo.HasNullSentinelProperty && !outputTypeInfo.HasNullSentinelProperty)
                { // realistically, REFs can't have null sentinels, but I'm being pedantic... 
                    RemoveNullSentinel(inputTypeInfo, inputFields, inputFieldValues, outputFields); 
                }
 
                PlanCompiler.Assert(outputFields.Count == inputFields.Count, "Mismatched field count: Expected " + inputFields.Count + "; Got " + outputFields.Count);
            }

            // now build up a NewRecordConstructor with the appropriate info 
            NewRecordOp recOp = m_command.CreateNewRecordOp(outputTypeInfo.FlattenedTypeUsage, outputFields);
            Node newNode = m_command.CreateNode(recOp, inputFieldValues); 
 
            return newNode;
        } 

        // We have to adjust for when we're supposed to remove null sentinels;
        // columns (See SQLBUDT #553534 for an example).  Note that we shouldn't
        // have to add null sentinels here, since reference types won't be expecting 
        // them (the fact that the key is null is good enough...)
        private static void RemoveNullSentinel(TypeInfo inputTypeInfo, List inputFields, List inputFieldValues, List outputFields) 
        { 
            PlanCompiler.Assert(inputFields[0] == inputTypeInfo.NullSentinelProperty, "InputField0 must be the null sentinel property");
            inputFields.RemoveAt(0); 
            inputFieldValues.RemoveAt(0);
        }

        ///  
        /// VarRefOp
        /// 
        /// Replace a VarRef with a copy of the corresponding "Record" constructor 
        /// 
        /// the VarRefOp 
        /// the node
        /// new subtree
        public override Node Visit(VarRefOp op, Node n)
        { 
            // Lookup my VarInfo
            VarInfo varInfo; 
            if (!m_varInfoMap.TryGetVarInfo(op.Var, out varInfo)) 
            {
                PlanCompiler.Assert(!TypeUtils.IsStructuredType(op.Type), 
                    "No varInfo for a structured type var: Id = " + op.Var.Id + " Type = " + op.Type);
                return n;
            }
            if (varInfo.IsCollectionType) 
            {
                n.Op = m_command.CreateVarRefOp(((CollectionVarInfo)varInfo).NewVar); 
                return n; 
            }
            else 
            {
                // A very specialized record constructor mechanism for structured type Vars.
                // We look up the VarInfo corresponding to the Var - which has a set of fields
                // and the corresponding properties that we need to produce 

                StructuredVarInfo structuredVarInfo = (StructuredVarInfo)varInfo; 
 
                NewRecordOp newOp = m_command.CreateNewRecordOp(structuredVarInfo.NewTypeUsage, structuredVarInfo.Fields);
                List newNodeChildren = new List(); 
                foreach (Var v in varInfo.NewVars)
                {
                    VarRefOp newVarRefOp = m_command.CreateVarRefOp(v);
                    newNodeChildren.Add(m_command.CreateNode(newVarRefOp)); 
                }
                Node newNode = m_command.CreateNode(newOp, newNodeChildren); 
                return newNode; 
            }
        } 

        #region record construction ops

        ///  
        /// Handler for NewEntity
        ///  
        ///  
        /// 
        ///  
        public override Node Visit(NewEntityOp op, Node n)
        {
            return FlattenConstructor(op, n);
        } 

        ///  
        /// NewInstanceOp 
        /// 
        /// the NewInstanceOp 
        /// corresponding node
        /// new subtree
        public override Node Visit(NewInstanceOp op, Node n)
        { 
            return FlattenConstructor(op, n);
        } 
 
        /// 
        /// DiscriminatedNewInstanceOp 
        /// 
        /// the DiscriminatedNewInstanceOp
        /// corresponding node
        /// new subtree 
        public override Node Visit(DiscriminatedNewEntityOp op, Node n)
        { 
            return FlattenConstructor(op, n); 
        }
 
        /// 
        /// Given an explicit discriminator value, map to normalized values. Essentially, this allows
        /// a discriminated new instance to coexist with free-floating entities, MEST, etc. which use
        /// general purpose ordpath type ids (e.g. '0X0X') 
        ///
        /// An example of the normalization is given: 
        /// 
        /// CASE
        ///     WHEN discriminator = 'Base' THEN '0X' 
        ///     WHEN discriminator = 'Derived1' THEN '0X0X'
        ///     WHEN discriminator = 'Derived2' THEN '0X1X'
        ///     ELSE '0X2X' -- default case for 'Derived3'
        ///  
        private Node NormalizeTypeDiscriminatorValues(DiscriminatedNewEntityOp op, Node discriminator)
        { 
            TypeInfo typeInfo = m_typeInfo.GetTypeInfo(op.Type); 

            CaseOp normalizer = m_command.CreateCaseOp(typeInfo.RootType.TypeIdProperty.TypeUsage); 
            List children = new List(op.DiscriminatorMap.TypeMap.Count * 2 - 1);
            for (int i = 0; i < op.DiscriminatorMap.TypeMap.Count; i++)
            {
                object discriminatorValue = op.DiscriminatorMap.TypeMap[i].Key; 
                md.EntityType type = op.DiscriminatorMap.TypeMap[i].Value;
                TypeInfo currentTypeInfo = m_typeInfo.GetTypeInfo(md.TypeUsage.Create(type)); 
 
                Node normalizedDiscriminatorConstant = CreateTypeIdConstant(currentTypeInfo);
                // for the last type, return the 'then' value 
                if (i == op.DiscriminatorMap.TypeMap.Count - 1)
                {
                    // ELSE normalizedDiscriminatorValue
                    children.Add(normalizedDiscriminatorConstant); 
                }
                else 
                { 
                    // WHEN discriminator = discriminatorValue THEN normalizedDiscriminatorValue
                    ConstantBaseOp discriminatorValueOp = m_command.CreateConstantOp(md.Helper.GetModelTypeUsage(op.DiscriminatorMap.DiscriminatorProperty.TypeUsage), 
                                                                                     discriminatorValue);
                    Node discriminatorConstant = m_command.CreateNode(discriminatorValueOp);
                    ComparisonOp discriminatorPredicateOp = m_command.CreateComparisonOp(OpType.EQ);
                    Node discriminatorPredicate = m_command.CreateNode(discriminatorPredicateOp, discriminator, discriminatorConstant); 
                    children.Add(discriminatorPredicate);
                    children.Add(normalizedDiscriminatorConstant); 
                } 
            }
 
            // swap discriminator with case op normalizing the discriminator
            discriminator = m_command.CreateNode(normalizer, children);
            return discriminator;
        } 

        ///  
        /// NewRecordOp 
        /// 
        /// the newRecordOp 
        /// corresponding node
        /// new subtree
        public override Node Visit(NewRecordOp op, Node n)
        { 
            return FlattenConstructor(op, n);
        } 
 
        /// 
        /// Build out an expression corresponding to the entitysetid 
        /// 
        /// the property corresponding to the entitysetid
        /// the *NewEntity op
        ///  
        private Node GetEntitySetIdExpr(md.EdmProperty entitySetIdProperty, NewEntityBaseOp op)
        { 
            Node entitySetIdNode; 
            md.EntitySet entitySet = op.EntitySet as md.EntitySet;
            if (entitySet != null) 
            {
                int entitySetId = m_typeInfo.GetEntitySetId(entitySet);
                InternalConstantOp entitySetIdOp = m_command.CreateInternalConstantOp(md.Helper.GetModelTypeUsage(entitySetIdProperty), entitySetId);
                entitySetIdNode = m_command.CreateNode(entitySetIdOp); 
            }
            else 
            { 
                //
                // Not in a view context; simply assume a null entityset 
                //
                entitySetIdNode = CreateNullConstantNode(md.Helper.GetModelTypeUsage(entitySetIdProperty));
            }
 
            return entitySetIdNode;
        } 
 
        /// 
        /// Flattens out a constructor into a "flat" record constructor. 
        /// The "flat" record type is looked up for the current constructor's type,
        /// and each property is filled out from the current constructor's fields
        /// 
        /// The NewRecordOp/NewInstanceOp 
        /// The current subtree
        /// the new subtree 
        private Node FlattenConstructor(ScalarOp op, Node n) 
        {
            PlanCompiler.Assert(op.OpType == OpType.NewInstance || op.OpType == OpType.NewRecord || op.OpType == OpType.DiscriminatedNewEntity || op.OpType == OpType.NewEntity, 
                "unexpected op: " + op.OpType + "?");

            // First visit all my children
            VisitChildren(n); 

            // Find the new type corresponding to the type 
            TypeInfo typeInfo = m_typeInfo.GetTypeInfo(op.Type); 
            md.RowType flatType = typeInfo.FlattenedType;
            NewEntityBaseOp newEntityOp = op as NewEntityBaseOp; 

            // Identify the fields
            IEnumerable opFields = null;
            DiscriminatedNewEntityOp discriminatedNewInstanceOp = null; 
            if (op.OpType == OpType.NewRecord)
            { 
                // Get only those fields that I have values for 
                opFields = ((NewRecordOp)op).Properties;
            } 
            else if (op.OpType == OpType.DiscriminatedNewEntity)
            {
                // Get all properties projected by the discriminated new instance op
                discriminatedNewInstanceOp = (DiscriminatedNewEntityOp)op; 
                opFields = discriminatedNewInstanceOp.DiscriminatorMap.Properties;
            } 
            else 
            {
                // Children align with structural members of type for a standard NewInstanceOp 
                opFields = TypeHelpers.GetAllStructuralMembers(op.Type);
            }

            // Next, walk through each of my field, and flatten out any field 
            // that is structured.
            List newFields = new List(); 
            List newFieldValues = new List(); 

            // 
            // NOTE: we expect the type id property and the entityset id properties
            //       to be at the start of the properties collection.
            //
            // Add a typeid property if we need one 
            //
            if (typeInfo.HasTypeIdProperty) 
            { 
                newFields.Add(typeInfo.TypeIdProperty);
                if (null == discriminatedNewInstanceOp) 
                {
                    newFieldValues.Add(CreateTypeIdConstant(typeInfo));
                }
                else 
                {
                    // first child in DiscriminatedNewInstanceOp is discriminator/typeid 
                    Node discriminator = n.Children[0]; 

                    if (null == typeInfo.RootType.DiscriminatorMap) 
                    {
                        // if there are multiple sets (or free-floating constructors) for this type
                        // hierarchy, normalize the discriminator value to expose the standard
                        // '0X' style values 
                        discriminator = NormalizeTypeDiscriminatorValues(discriminatedNewInstanceOp, discriminator);
                    } 
 
                    newFieldValues.Add(discriminator);
                } 
            }

            //
            // Add an entitysetid property if we need one 
            //
            if (typeInfo.HasEntitySetIdProperty) 
            { 
                newFields.Add(typeInfo.EntitySetIdProperty);
 
                PlanCompiler.Assert(newEntityOp != null, "unexpected optype:" + op.OpType);
                Node entitySetIdNode = GetEntitySetIdExpr(typeInfo.EntitySetIdProperty, newEntityOp);

                // Get the entity-set-id of the "current" entityset 
                newFieldValues.Add(entitySetIdNode);
            } 
 
            // Add a nullability property if we need one
            if (typeInfo.HasNullSentinelProperty) 
            {
                newFields.Add(typeInfo.NullSentinelProperty);
                newFieldValues.Add(CreateNullSentinelConstant());
            } 

            // 
            // first child of discriminatedNewInstanceOp is the typeId; otherwise, the first child is the first property 
            //
            int childrenIndex = null == discriminatedNewInstanceOp ? 0 : 1; 

            foreach (md.EdmMember opField in opFields)
            {
                Node fieldValue = n.Children[childrenIndex]; 
                if (TypeUtils.IsStructuredType(md.Helper.GetModelTypeUsage(opField)))
                { 
                    // Flatten out nested type 
                    md.RowType nestedFlatType = m_typeInfo.GetTypeInfo(md.Helper.GetModelTypeUsage(opField)).FlattenedType;
 
                    // Find offset of opField in top-level flat type
                    int nestedPropertyOffset = typeInfo.RootType.GetNestedStructureOffset(new SimplePropertyRef(opField));

                    foreach (md.EdmProperty nestedProperty in nestedFlatType.Properties) 
                    {
                        // Try to build up an accessor for this property from the input 
                        Node nestedPropertyValue = BuildAccessor(fieldValue, nestedProperty); 

                        if (null != nestedPropertyValue) 
                        {
                            newFields.Add(flatType.Properties[nestedPropertyOffset]);
                            newFieldValues.Add(nestedPropertyValue);
                        } 

                        nestedPropertyOffset++; 
                    } 
                }
                else 
                {
                    PropertyRef propRef = new SimplePropertyRef(opField);
                    md.EdmProperty outputTypeProp = typeInfo.GetNewProperty(propRef);
 
                    newFields.Add(outputTypeProp);
 
                    newFieldValues.Add(fieldValue); 
                }
 
                childrenIndex++;
            }

            // 
            // We've now handled all the regular properties. Now, walk through all the rel properties -
            // obviously, this only applies for the *NewEntityOps 
            // 
            if (newEntityOp != null)
            { 
                foreach (RelProperty relProp in newEntityOp.RelationshipProperties)
                {
                    Node fieldValue = n.Children[childrenIndex];
                    md.RowType nestedFlatType = m_typeInfo.GetTypeInfo(relProp.ToEnd.TypeUsage).FlattenedType; 

                    // Find offset of opField in top-level flat type 
                    int nestedPropertyOffset = typeInfo.RootType.GetNestedStructureOffset(new RelPropertyRef(relProp)); 

                    foreach (md.EdmProperty nestedProperty in nestedFlatType.Properties) 
                    {
                        // Try to build up an accessor for this property from the input
                        Node nestedPropertyValue = BuildAccessor(fieldValue, nestedProperty);
 
                        if (null != nestedPropertyValue)
                        { 
                            newFields.Add(flatType.Properties[nestedPropertyOffset]); 
                            newFieldValues.Add(nestedPropertyValue);
                        } 

                        nestedPropertyOffset++;
                    }
                    childrenIndex++; 
                }
            } 
 
            //
            // So, now we have the list of all fields that should make up the 
            // flat type.  Create a new node with them.
            //
            NewRecordOp newOp = m_command.CreateNewRecordOp(typeInfo.FlattenedTypeUsage, newFields);
            Node newNode = m_command.CreateNode(newOp, newFieldValues); 

            return newNode; 
        } 

        ///  
        /// NullOp
        ///
        /// If the node represents a null of an entity type it 'flattens' it into a new record,
        /// with at most one non-null value: for the typeIdProperty, if one is needed. 
        /// If the node represents an null of a non-entity type, no special work is done.
        ///  
        /// The NullOp 
        /// The current subtree
        /// the new subtree 
        public override Node Visit(NullOp op, Node n)
        {
            if (!TypeUtils.IsStructuredType(op.Type))
            { 
                return n;
            } 
 
            // Find the new type corresponding to the type
            TypeInfo typeInfo = m_typeInfo.GetTypeInfo(op.Type); 

            List newFields = new List();
            List newFieldValues = new List();
 
            // Add a typeid property if we need one
            if (typeInfo.HasTypeIdProperty) 
            { 
                newFields.Add(typeInfo.TypeIdProperty);
                var typeIdType = md.Helper.GetModelTypeUsage(typeInfo.TypeIdProperty); 
                newFieldValues.Add(CreateNullConstantNode(typeIdType));
            }

            NewRecordOp newRecordOp = new NewRecordOp(typeInfo.FlattenedTypeUsage, newFields); 
            return m_command.CreateNode(newRecordOp, newFieldValues);
        } 
 
        #endregion
 
        #region type comparison ops

        /// 
        /// IsOf 
        ///
        /// Convert an IsOf operator into a typeid comparison: 
        /// 
        ///     IsOfOnly(e, T) => e.TypeId == TypeIdValue(T)
        ///     IsOf(e, T)     => e.TypeId like TypeIdValue(T)% escape null 
        ///
        /// 
        /// The IsOfOp to handle
        /// current isof subtree 
        /// new subtree
        public override Node Visit(IsOfOp op, Node n) 
        { 
            // First visit all my children
            VisitChildren(n); 

            if (!TypeUtils.IsStructuredType(op.IsOfType))
            {
                return n; 
            }
            TypeInfo typeInfo = m_typeInfo.GetTypeInfo(op.IsOfType); 
            Node newNode = CreateTypeComparisonOp(n.Child0, typeInfo, op.IsOfOnly); 
            return newNode;
        } 

        /// 
        /// TreatOp
        /// 
        ///     TreatOp(e, T) => case when e.TypeId like TypeIdValue(T) then T else null end
        ///  
        /// the TreatOp 
        /// the node
        /// new subtree 
        public override Node Visit(TreatOp op, Node n)
        {
            // First visit all my children
            VisitChildren(n); 

            // 
            // filter out useless treat operations 
            // Treat(subtype-instance as superType)
            // 
            ScalarOp arg = (ScalarOp)n.Child0.Op;
            if (op.IsFakeTreat ||
                md.TypeSemantics.IsStructurallyEqual(arg.Type, op.Type) ||
                md.TypeSemantics.IsSubTypeOf(arg.Type, op.Type)) 
            {
                return n.Child0; 
            } 

            // When we support UDTs 
            if (!TypeUtils.IsStructuredType(op.Type))
            {
                return n;
            } 

            // 
            // First, convert this into a CaseOp: 
            //   case when e.TypeId like TypeIdValue then e else null end
            // 
            TypeInfo typeInfo = m_typeInfo.GetTypeInfo(op.Type);
            Node likeNode = CreateTypeComparisonOp(n.Child0, typeInfo, false);
            CaseOp caseOp = m_command.CreateCaseOp(typeInfo.FlattenedTypeUsage);
            Node caseNode = m_command.CreateNode(caseOp, likeNode, n.Child0, CreateNullConstantNode(caseOp.Type)); 

            // 
            // Now "flatten" out this Op into a constructor. But only get the 
            // desired properties
            // 
            PropertyRefList desiredProperties = m_nodePropertyMap[n];
            Node flattenedCaseNode = FlattenCaseOp(caseOp, caseNode, typeInfo, desiredProperties);
            return flattenedCaseNode;
        } 

        ///  
        /// Create a typeid-comparison operator - more specifically, create an 
        /// operator that compares a typeid value with the typeid property of an
        /// input structured type. 
        /// The comparison may be "exact" - in which case we're looking for the exact
        /// type; otherwise, we're looking for any possible subtypes.
        /// The "exact" variant is used by the IsOfOp (only); the other variant is
        /// used by IsOfOp and TreatOp 
        /// 
        /// The input structured type expression 
        /// Augmented type information for the type 
        /// Exact comparison?
        /// New comparison expression 
        private Node CreateTypeComparisonOp(Node input, TypeInfo typeInfo, bool isExact)
        {
            Node typeIdProperty = BuildTypeIdAccessor(input, typeInfo);
            Node newNode = null; 

            if (isExact) 
            { 
                newNode = CreateTypeEqualsOp(typeInfo, typeIdProperty);
            } 
            else
            {
                if (typeInfo.RootType.DiscriminatorMap != null)
                { 
                    // where there are explicit discriminator values, LIKE '0X%' pattern does not work...
                    newNode = CreateDisjunctiveTypeComparisonOp(typeInfo, typeIdProperty); 
                } 
                else
                { 
                    Node typeIdConstantNode = CreateTypeIdConstantForPrefixMatch(typeInfo);
                    LikeOp likeOp = m_command.CreateLikeOp();
                    newNode = m_command.CreateNode(likeOp, typeIdProperty, typeIdConstantNode, CreateNullConstantNode(DefaultTypeIdType));
                } 
            }
            return newNode; 
        } 

        ///  
        /// Create a filter matching all types in the given hierarchy (typeIdProperty IN typeInfo.Hierarchy) e.g.:
        ///
        ///     typeIdProperty = 'Base' OR typeIdProperty = 'Derived1' ...
        /// 
        /// This is called only for types using DiscriminatorMap (explicit discriminator values)
        ///  
        ///  
        /// 
        /// type hierarchy check 
        private Node CreateDisjunctiveTypeComparisonOp(TypeInfo typeInfo, Node typeIdProperty)
        {
            PlanCompiler.Assert(typeInfo.RootType.DiscriminatorMap != null, "should be used only for DiscriminatorMap type checks");
            // collect all non-abstract types in the given hierarchy 
            IEnumerable types = typeInfo.GetTypeHierarchy().Where(t => !t.Type.EdmType.Abstract);
 
            // generate a disjunction 
            Node current = null;
            foreach (TypeInfo type in types) 
            {
                Node typeComparisonNode = CreateTypeEqualsOp(type, typeIdProperty);
                if (null == current)
                { 
                    current = typeComparisonNode;
                } 
                else 
                {
                    current = m_command.CreateNode(m_command.CreateConditionalOp(OpType.Or), current, typeComparisonNode); 
                }
            }
            if (null == current)
            { 
                // only abstract types in this hierarchy... no values possible
                current = m_command.CreateNode(m_command.CreateFalseOp()); 
            } 
            return current;
        } 

        /// 
        /// Generates a node of the form typeIdProperty = typeInfo.TypeId
        ///  
        /// 
        ///  
        /// type equality check 
        private Node CreateTypeEqualsOp(TypeInfo typeInfo, Node typeIdProperty)
        { 
            Node typeIdConstantNode = CreateTypeIdConstant(typeInfo);
            ComparisonOp eqCompOp = m_command.CreateComparisonOp(OpType.EQ);
            Node result = m_command.CreateNode(eqCompOp, typeIdProperty, typeIdConstantNode);
            return result; 
        }
 
        #endregion 

        #endregion 

        #endregion

    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK