File: System\Data\Query\PlanCompiler\StructuredTypeInfo.cs
Project: ndp\fx\src\DataEntity\System.Data.Entity.csproj (System.Data.Entity)
//---------------------------------------------------------------------
// <copyright file="StructuredTypeInfo.cs" company="Microsoft">
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//
// @owner  Microsoft
// @backupOwner Microsoft
//---------------------------------------------------------------------
 
using System;
using System.Collections;
using System.Collections.Generic;
//using System.Diagnostics; // Please use PlanCompiler.Assert instead of Debug.Assert in this class...
 
// It is fine to use Debug.Assert in cases where you assert an obvious thing that is supposed
// to prevent from simple mistakes during development (e.g. method argument validation 
// in cases where it was you who created the variables or the variables had already been validated or 
// in "else" clauses where due to code changes (e.g. adding a new value to an enum type) the default 
// "else" block is chosen why the new condition should be treated separately). This kind of asserts are 
// (can be) helpful when developing new code to avoid simple mistakes but have no or little value in 
// the shipped product. 
// PlanCompiler.Assert *MUST* be used to verify conditions in the trees. These would be assumptions 
// about how the tree was built etc. - in these cases we probably want to throw an exception (this is
// what PlanCompiler.Assert does when the condition is not met) if either the assumption is not correct 
// or the tree was built/rewritten not the way we thought it was.
// Use your judgment - if you rather remove an assert than ship it use Debug.Assert otherwise use
// PlanCompiler.Assert.
 
using System.Globalization;
using System.Linq;
using System.Data.Common;
using md = System.Data.Metadata.Edm;
using System.Data.Query.InternalTrees;
 
namespace System.Data.Query.PlanCompiler
{
 
    /// <summary>
    /// The type flattener module is part of the structured type elimination phase,
    /// and is largely responsible for "flattening" record and nominal types into 
    /// flat record types. Additionally, for nominal types, this module produces typeid
    /// values that can be used later to interpret the input data stream.
    ///
    /// The goal of this module is to load up information about type and entityset metadata
    /// used in the ITree. This module is part of the "StructuredTypeElimination" phase, 
    /// and provides information to help in this process. 
    /// 
    /// This module itself is broken down into multiple parts.
    /// 
    /// (*) Loading type information: We walk the query tree to identify all references
    ///     to structured types and entity sets
    /// 
    /// (*) Processing entitysets: We walk the list of entitysets, and assign ids to each
    ///     entityset. We also create a map of id->entityset metadata in this phase.
    /// 
    /// (*) Processing types: We then walk the list of types, and process each type. This, 
    ///     in turn, is also broken into multiple parts:
    /// 
    ///     * Populating the Type Map: we walk the list of reference types and add each of
    ///       them to our typeMap, along with their base types.
    /// 
    ///     * TypeId assignment: We assign typeids to each nominal (complextype/entitytype).
    ///       This typeid is based on a dewey encoding. The typeid of a type is typically
    ///       the typeid of its supertype suffixed by the subtype number of this type within
    ///       its supertype. This encoding is intended to support easy type matching 
    ///       later on in the query - both for exact (IS OF ONLY) and inexact (IS OF) matches.
    /// 
    ///     * Type flattening: We then "explode"/"flatten" each structured type - refs, 
    ///       entity types, complex types and record types. The result is a flattened type
    ///       where every single property of the resulting type is a primitive/scalar type
    ///       (Note: UDTs are considered to be scalar types). Additional information may also
    ///       be encoded as a type property. For example, a typeid property is added (if
    ///       necessary) to complex/entity types to help discriminate polymorphic instances.
    ///       An EntitySetId property is added to ref and entity type attributes to help
    ///       determine the entity set that a given entity instance comes from.
    ///       As part of type flattening, we keep track of additional information that allows
    ///       us to map easily from the original property to the properties in the new type
    ///
    /// The final result of this processing is an object that contains:
    /// 
    ///  * a TypeInfo (extra type information) for each structured type in the query
    ///  * a map from typeid value to type. To be used later by result assembly
    ///  * a map between entitysetid value and entityset. To be used later by result assembly 
    ///
    /// NOTE: StructuredTypeInfo is probably not the best name for this class, since
    ///       it doesn't derive from TypeInfo but rather manages a collection of them.
    ///       I don't have a better name, but if you come up with one change this.
    /// 
    /// </summary>
    internal class StructuredTypeInfo
    {
 
        #region private state
 
        private md.TypeUsage m_stringType;
        private md.TypeUsage m_intType;
        private Dictionary<md.TypeUsage, TypeInfo> m_typeInfoMap;
        private bool m_typeInfoMapPopulated;
        private md.EntitySet[] m_entitySetIdToEntitySetMap; //used as a Dictionary with the index as key
        private Dictionary<md.EntitySet, int> m_entitySetToEntitySetIdMap;
        // A mapping from entity types to the "single" entityset (in the query) that can
        // produce instances of that entity. If there are multiple entitysets of the
        // same type, or "free-floating" entity constructors in the query, then 
        // the corresponding entry is null
        private Dictionary<md.EntityTypeBase, md.EntitySet> m_entityTypeToEntitySetMap;
        private Dictionary<md.EntitySetBase, ExplicitDiscriminatorMap> m_discriminatorMaps;
        private RelPropertyHelper m_relPropertyHelper;
        private HashSet<string> m_typesNeedingNullSentinel;
        #endregion
 
        #region constructor
 
        private StructuredTypeInfo(HashSet<string> typesNeedingNullSentinel)
        {
 
            // Bug 428351: Make the type->typeInfo dictionary use ref equality for
            // types. The problem is that records (and other transient types) can 
            // compare equal, even if they are not reference-equal, and this causes
            // us trouble down the road when we try to compare properties.
            // Type unification is a good thing, but it needs to happen earlier somewhere
            m_typeInfoMap = new Dictionary<md.TypeUsage, TypeInfo>(TypeUsageEqualityComparer.Instance);
            m_typeInfoMapPopulated = false;
            m_typesNeedingNullSentinel = typesNeedingNullSentinel;
        }
 
        #endregion
 
        #region Process driver
 
        /// <summary>
        /// Process Driver
        /// </summary>
        /// <param name="itree"></param>
        /// <param name="referencedTypes">structured types referenced in the query</param>
        /// <param name="referencedEntitySets">entitysets referenced in the query</param>
        /// <param name="freeFloatingEntityConstructorTypes">entity types that have "free-floating" entity constructors</param>
        /// <param name="discriminatorMaps">information on optimized discriminator patterns for entity sets</param>
        /// <param name="relPropertyHelper">helper for rel properties</param>
        /// <param name="typesNeedingNullSentinel">which types need a null sentinel</param>
        /// <param name="structuredTypeInfo"></param>
        internal static void Process(Command itree,
            HashSet<md.TypeUsage> referencedTypes,
            HashSet<md.EntitySet> referencedEntitySets,
            HashSet<md.EntityType> freeFloatingEntityConstructorTypes,
            Dictionary<md.EntitySetBase, DiscriminatorMapInfo> discriminatorMaps,
            RelPropertyHelper relPropertyHelper,
            HashSet<string> typesNeedingNullSentinel,
            out StructuredTypeInfo structuredTypeInfo)
        {
            structuredTypeInfo = new StructuredTypeInfo(typesNeedingNullSentinel);
            structuredTypeInfo.Process(itree, referencedTypes, referencedEntitySets, freeFloatingEntityConstructorTypes, discriminatorMaps, relPropertyHelper);
        }
 
        /// <summary>
        /// Fills the StructuredTypeInfo instance from the itree provided.
        /// </summary>
        /// <param name="itree"></param>
        /// <param name="referencedTypes">referenced structured types</param>
        /// <param name="referencedEntitySets">referenced entitysets</param>
        /// <param name="freeFloatingEntityConstructorTypes">free-floating entityConstructor types</param>
        /// <param name="discriminatorMaps">discriminator information for entity sets mapped using TPH pattern</param>
        /// <param name="relPropertyHelper">helper for rel properties</param>
        private void Process(Command itree,
            HashSet<md.TypeUsage> referencedTypes,
            HashSet<md.EntitySet> referencedEntitySets,
            HashSet<md.EntityType> freeFloatingEntityConstructorTypes,
            Dictionary<md.EntitySetBase, DiscriminatorMapInfo> discriminatorMaps,
            RelPropertyHelper relPropertyHelper)
        {
            PlanCompiler.Assert(null != itree, "null itree?");
 
            m_stringType = itree.StringType;
            m_intType = itree.IntegerType;
            m_relPropertyHelper = relPropertyHelper;
 
            ProcessEntitySets(referencedEntitySets, freeFloatingEntityConstructorTypes);
            ProcessDiscriminatorMaps(discriminatorMaps);
            ProcessTypes(referencedTypes);
        }
 
        #endregion
 
        #region "public" properties
 
        /// <summary>
        /// Mapping from entitysetid-s to entitysets
        /// </summary>
        internal md.EntitySet[] EntitySetIdToEntitySetMap
        {
            get
            {
                return m_entitySetIdToEntitySetMap;
            }
        }
 
        #endregion
 
        #region "public" methods
        /// <summary>
        /// Get a helper for rel properties
        /// </summary>
        internal RelPropertyHelper RelPropertyHelper
        {
            get { return m_relPropertyHelper; }
        }
        /// <summary>
        /// Gets the "single" entityset that stores instances of this type
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        internal md.EntitySet GetEntitySet(md.EntityTypeBase type)
        {
            md.EntitySet set;
            md.EntityTypeBase rootType = GetRootType(type);
            if (!m_entityTypeToEntitySetMap.TryGetValue(rootType, out set))
            {
                return null;
            }
            return set;
        }
 
        /// <summary>
        /// Get the entitysetid value for a given entityset
        /// </summary>
        /// <param name="e">the entityset</param>
        /// <returns>entitysetid value</returns>
        internal int GetEntitySetId(md.EntitySet e)
        {
            int result = 0;
 
            if (!m_entitySetToEntitySetIdMap.TryGetValue(e, out result))
            {
                PlanCompiler.Assert(false, "no such entity set?");
            }
            return result;
        }
 
        /// <summary>
        /// Gets entity sets referenced by the query.
        /// </summary>
        /// <returns>entity sets</returns>
        internal Common.Utils.Set<md.EntitySet> GetEntitySets()
        {
            return new Common.Utils.Set<md.EntitySet>(m_entitySetIdToEntitySetMap).MakeReadOnly();
        }
 
        /// <summary>
        /// Find the TypeInfo entry for a type. For non-structured types, we always
        /// return null. For structured types, we return the entry in the typeInfoMap. 
        /// If we don't find one, and the typeInfoMap has already been populated, then we
        /// assert
        /// </summary>
        /// <param name="type">the type to look up</param>
        /// <returns>the typeinfo for the type (null if we couldn't find one)</returns>
        internal TypeInfo GetTypeInfo(md.TypeUsage type)
        {
            if (!TypeUtils.IsStructuredType(type))
            {
                return null;
            }
            TypeInfo typeInfo = null;
            if (!m_typeInfoMap.TryGetValue(type, out typeInfo))
            {
                PlanCompiler.Assert(!TypeUtils.IsStructuredType(type) || !m_typeInfoMapPopulated,
                    "cannot find typeInfo for type " + type);
            }
            return typeInfo;
        }
 
        #endregion
 
        #region private methods
 
        #region EntitySet processing methods
 
        /// <summary>
        /// Add a new entry to the entityTypeToSet map
        /// </summary>
        /// <param name="entityType">entity type</param>
        /// <param name="entitySet">entityset producing this type</param>
        private void AddEntityTypeToSetEntry(md.EntityType entityType, md.EntitySet entitySet)
        {
            md.EntitySet other;
            md.EntityTypeBase rootType = GetRootType(entityType);
            bool hasSingleEntitySet = true;
 
            if (entitySet == null)
            {
                hasSingleEntitySet = false;
            }
            else if (m_entityTypeToEntitySetMap.TryGetValue(rootType, out other))
            {
                if (other != entitySet)
                {
                    hasSingleEntitySet = false;
                }
            }
 
            if (hasSingleEntitySet)
            {
                m_entityTypeToEntitySetMap[rootType] = entitySet;
            }
            else
            {
                m_entityTypeToEntitySetMap[rootType] = null;
            }
        }
 
        /// <summary>
        /// Handle any relevant processing for entity sets
        /// <param name="referencedEntitySets">referenced entitysets</param>
        /// <param name="freeFloatingEntityConstructorTypes">free-floating entity constructor types</param>
        /// </summary>
        private void ProcessEntitySets(HashSet<md.EntitySet> referencedEntitySets, HashSet<md.EntityType> freeFloatingEntityConstructorTypes)
        {
            AssignEntitySetIds(referencedEntitySets);
 
            //
            // set up the entity-type to set map
            //
            m_entityTypeToEntitySetMap = new Dictionary<md.EntityTypeBase, md.EntitySet>();
            foreach (md.EntitySet e in referencedEntitySets)
            {
                AddEntityTypeToSetEntry(e.ElementType, e);
            }
            foreach (md.EntityType t in freeFloatingEntityConstructorTypes)
            {
                AddEntityTypeToSetEntry(t, null);
            }
        }
 
        /// <summary>
        /// Handle discriminator maps (determine which can safely be used in the query)
        /// </summary>
        private void ProcessDiscriminatorMaps(Dictionary<md.EntitySetBase, DiscriminatorMapInfo> discriminatorMaps)
        {
            // Only use custom type discrimination where a type has a single entity set. Where
            // there are multiple sets, discriminator properties and flattened representations
            // may be incompatible.
            Dictionary<md.EntitySetBase, ExplicitDiscriminatorMap> filteredMaps = null;
            if (null != discriminatorMaps)
            {
                filteredMaps = new Dictionary<md.EntitySetBase, ExplicitDiscriminatorMap>(discriminatorMaps.Count, discriminatorMaps.Comparer);
                foreach (KeyValuePair<md.EntitySetBase, DiscriminatorMapInfo> setMapPair in discriminatorMaps)
                {
                    md.EntitySetBase set = setMapPair.Key;
                    ExplicitDiscriminatorMap map = setMapPair.Value.DiscriminatorMap;
                    if (null != map)
                    {
                        md.EntityTypeBase rootType = GetRootType(set.ElementType);
                        bool hasOneSet = GetEntitySet(rootType) != null;
                        if (hasOneSet)
                        {
                            filteredMaps.Add(set, map);
                        }
                    }
                }
                if (filteredMaps.Count == 0)
                {
                    // don't bother keeping the dictionary if it's empty
                    filteredMaps = null;
                }
            }
            m_discriminatorMaps = filteredMaps;
        }
 
        /// <summary>
        /// Assign ids to each entityset in the query
        /// <param name="referencedEntitySets">referenced entitysets</param>
        /// </summary>
        private void AssignEntitySetIds(HashSet<md.EntitySet> referencedEntitySets)
        {
            m_entitySetIdToEntitySetMap = new md.EntitySet[referencedEntitySets.Count];
            m_entitySetToEntitySetIdMap = new Dictionary<md.EntitySet, int>();
 
            int id = 0;
            foreach (md.EntitySet e in referencedEntitySets)
            {
                if (m_entitySetToEntitySetIdMap.ContainsKey(e))
                {
                    continue;
                }
                m_entitySetIdToEntitySetMap[id] = e;
                m_entitySetToEntitySetIdMap[e] = id;
                id++;
            }
        }
 
        #endregion
 
        #region Type processing methods
 
        /// <summary>
        /// Process all types in the query
        /// </summary>
        /// <param name="referencedTypes">referenced types</param>
        private void ProcessTypes(HashSet<md.TypeUsage> referencedTypes)
        {
            // Build up auxilliary information for each type
            PopulateTypeInfoMap(referencedTypes);
            // Assign typeids to all nominal types
            AssignTypeIds();
            // Then "explode" all types
            ExplodeTypes();
        }
 
        #region Populating TypeInfo Map
 
        /// <summary>
        /// Build up auxilliary information for each referenced type in the query
        /// </summary>
        /// <param name="referencedTypes"></param>
        private void PopulateTypeInfoMap(HashSet<md.TypeUsage> referencedTypes)
        {
            foreach (md.TypeUsage t in referencedTypes)
            {
                CreateTypeInfoForType(t);
            }
            m_typeInfoMapPopulated = true;
        }
 
        /// <summary>
        /// Tries to lookup custom discriminator map for the given type (applies to EntitySets with
        /// TPH discrimination pattern)
        /// </summary>
        private bool TryGetDiscriminatorMap(md.EdmType type, out ExplicitDiscriminatorMap discriminatorMap)
        {
            discriminatorMap = null;
 
            // check that there are actually discriminator maps available
            if (null == m_discriminatorMaps)
            {
                return false;
            }
 
            // must be an entity type...
            if (type.BuiltInTypeKind != md.BuiltInTypeKind.EntityType)
            {
                return false;
            }
 
            // get root entity type (discriminator maps are mapped from the root)
            md.EntityTypeBase rootEntityType = GetRootType((md.EntityType)type);
 
            // find entity set
            md.EntitySet entitySet;
            if (!m_entityTypeToEntitySetMap.TryGetValue(rootEntityType, out entitySet))
            {
                return false;
            }
 
            // free floating entity constructors are stored with a null EntitySet
            if (entitySet == null)
            {
                return false;
            }
 
            // look for discriminator map
            return m_discriminatorMaps.TryGetValue(entitySet, out discriminatorMap);
        }
 
        /// <summary>
        /// Create a TypeInfo (if necessary) for the type, and add it to the TypeInfo map
        /// </summary>
        /// <param name="type">the type to process</param>
        private void CreateTypeInfoForType(md.TypeUsage type)
        {
            //
            // peel off all collection wrappers
            //
            while (TypeUtils.IsCollectionType(type))
            {
                type = TypeHelpers.GetEdmType<md.CollectionType>(type).TypeUsage;
            }
 
            // Only add "structured" types
            if (TypeUtils.IsStructuredType(type))
            {
                // check for discriminator map...
                ExplicitDiscriminatorMap discriminatorMap;
                TryGetDiscriminatorMap(type.EdmType, out discriminatorMap);
 
                CreateTypeInfoForStructuredType(type, discriminatorMap);
            }
        }
 
        /// <summary>
        /// Add a new entry to the map. If an entry already exists, then this function
        /// simply returns the existing entry. Otherwise a new entry is created. If 
        /// the type has a supertype, then we ensure that the supertype also exists in
        /// the map, and we add our info to the supertype's list of subtypes
        /// </summary>
        /// <param name="type">New type to add</param>
        /// <param name="discriminatorMap">type discriminator map</param>
        /// <returns>The TypeInfo for this type</returns>
        private TypeInfo CreateTypeInfoForStructuredType(md.TypeUsage type, ExplicitDiscriminatorMap discriminatorMap)
        {
            TypeInfo typeInfo;
 
            PlanCompiler.Assert(TypeUtils.IsStructuredType(type), "expected structured type. Found " + type);
 
            // Return existing entry, if one is available
            typeInfo = GetTypeInfo(type);
            if (typeInfo != null)
            {
                return typeInfo;
            }
 
            // Ensure that my supertype has been added to the map. 
            TypeInfo superTypeInfo = null;
            md.RefType refType;
            if (type.EdmType.BaseType != null)
            {
 
                superTypeInfo = CreateTypeInfoForStructuredType(md.TypeUsage.Create(type.EdmType.BaseType), discriminatorMap);
            }
            // 
            // Handle Ref types also in a similar fashion
            //
            else if (TypeHelpers.TryGetEdmType<md.RefType>(type, out refType))
            {
                md.EntityType entityType = refType.ElementType as md.EntityType;
                if (entityType != null && entityType.BaseType != null)
                {
                    md.TypeUsage baseRefType = TypeHelpers.CreateReferenceTypeUsage(entityType.BaseType as md.EntityType);
                    superTypeInfo = CreateTypeInfoForStructuredType(baseRefType, discriminatorMap);
                }
            }
 
            //
            // Add the types of my properties to the TypeInfo map
            // 
            foreach (md.EdmMember m in TypeHelpers.GetDeclaredStructuralMembers(type))
            {
                CreateTypeInfoForType(m.TypeUsage);
            }
 
            // 
            // Get the types of the rel properties also
            //
            {
                md.EntityTypeBase entityType;
                if (TypeHelpers.TryGetEdmType<md.EntityTypeBase>(type, out entityType))
                {
                    foreach (RelProperty p in m_relPropertyHelper.GetDeclaredOnlyRelProperties(entityType))
                    {
                        CreateTypeInfoForType(p.ToEnd.TypeUsage);
                    }
                }
            }
 
 
            // Now add myself to the map
            typeInfo = TypeInfo.Create(type, superTypeInfo, discriminatorMap);
            m_typeInfoMap.Add(type, typeInfo);
 
            return typeInfo;
        }
 
        #endregion
 
        #region Assigning TypeIds
 
        /// <summary>
        /// Assigns typeids to each type in the map. 
        /// We walk the map looking only for "root" types, and call the function
        /// above to process root types. All other types will be handled in that
        /// function
        /// </summary>
        private void AssignTypeIds()
        {
            int typeNum = 0;
 
            foreach (KeyValuePair<md.TypeUsage, TypeInfo> kv in m_typeInfoMap)
            {
                // See if there is a declared discriminator value for this column
                if (kv.Value.RootType.DiscriminatorMap != null)
                {
                    // find discriminator value for type
                    var entityType = (md.EntityType)kv.Key.EdmType;
                    kv.Value.TypeId = kv.Value.RootType.DiscriminatorMap.GetTypeId(entityType);
                }
 
                // Only handle root types. The call below will ensure that all the 
                // subtypes are appropriately tagged
                else if (kv.Value.IsRootType && (md.TypeSemantics.IsEntityType(kv.Key) || md.TypeSemantics.IsComplexType(kv.Key)))
                {
                    AssignRootTypeId(kv.Value, String.Format(CultureInfo.InvariantCulture, "{0}X", typeNum));
                    typeNum++;
                }
            }
        }
 
        /// <summary>
        /// Assign a typeid to a root type
        /// </summary>
        /// <param name="typeInfo"></param>
        /// <param name="typeId"></param>
        private void AssignRootTypeId(TypeInfo typeInfo, string typeId)
        {
            typeInfo.TypeId = typeId;
            AssignTypeIdsToSubTypes(typeInfo);
        }
 
        /// <summary>
        /// Assigns typeids to each subtype of the current type. 
        /// Assertion: the current type has already had a typeid assigned to it.
        /// </summary>
        /// <param name="typeInfo">The current type</param>
        private void AssignTypeIdsToSubTypes(TypeInfo typeInfo)
        {
            // Now walk through all my subtypes, and assign their typeids
            int mySubTypeNum = 0;
            foreach (TypeInfo subtype in typeInfo.ImmediateSubTypes)
            {
                AssignTypeId(subtype, mySubTypeNum);
                mySubTypeNum++;
            }
        }
 
        /// <summary>
        /// Assign a typeid to a non-root type.
        /// Assigns typeids to a non-root type based on a dewey encoding scheme. 
        /// The typeid will be the typeId of the supertype suffixed by a 
        /// local identifier for the type.  
        /// </summary>
        /// <param name="typeInfo">the non-root type</param>
        /// <param name="subtypeNum">position in the subtype list</param>
        private void AssignTypeId(TypeInfo typeInfo, int subtypeNum)
        {
            typeInfo.TypeId = String.Format(CultureInfo.InvariantCulture, "{0}{1}X", typeInfo.SuperType.TypeId, subtypeNum);
            AssignTypeIdsToSubTypes(typeInfo);
        }
 
        #endregion
 
        #region Flattening/Exploding types
        /// <summary>
        /// A type needs a type-id property if it is an entity type or a complex tpe that
        /// has subtypes.
        /// Coming soon: relax the "need subtype" requirement (ie) any entity/complex type will
        /// have a typeid
        /// </summary>
        /// <param name="typeInfo"></param>
        /// <returns></returns>
        private bool NeedsTypeIdProperty(TypeInfo typeInfo)
        {
            return typeInfo.ImmediateSubTypes.Count > 0 && !md.TypeSemantics.IsReferenceType(typeInfo.Type);
        }
 
        /// <summary>
        /// A type needs a null-sentinel property if it is an row type that was projected
        /// at the top level of the query; we capture that information in the preprocessor
        /// and pass it in here.
        /// </summary>
        /// <param name="typeInfo"></param>
        /// <returns></returns>
        private bool NeedsNullSentinelProperty(TypeInfo typeInfo)
        {
            return m_typesNeedingNullSentinel.Contains(typeInfo.Type.EdmType.Identity);
        }
 
        /// <summary>
        /// The type needs an entitysetidproperty, if it is either an entity type
        /// or a reference type, AND we cannot determine that there is only entityset
        /// in the query that could be producing instances of this entity
        /// </summary>
        /// <param name="typeInfo"></param>
        /// <returns></returns>
        private bool NeedsEntitySetIdProperty(TypeInfo typeInfo)
        {
            md.EntityType entityType;
            md.RefType refType = typeInfo.Type.EdmType as md.RefType;
            if (refType != null)
            {
                entityType = refType.ElementType as md.EntityType;
            }
            else
            {
                entityType = typeInfo.Type.EdmType as md.EntityType;
            }
            bool result = ((entityType != null) && (GetEntitySet(entityType) == null));
            return result;
        }
 
        /// <summary>
        /// "Explode" each type in the dictionary. (ie) for each type, get a flattened
        /// list of all its members (including special cases for the typeid)
        /// </summary>
        private void ExplodeTypes()
        {
            // Walk through the list of types, and only process the supertypes, since
            // The ExplodeType method will ensure that all the subtypes are appropriately
            // tagged
            foreach (KeyValuePair<md.TypeUsage, TypeInfo> kv in m_typeInfoMap)
            {
                if (kv.Value.IsRootType)
                {
                    ExplodeType(kv.Value);
                }
            }
        }
 
        /// <summary>
        /// "Explode" a type.  (ie) produce a flat record type with one property for each
        /// scalar property (top-level or nested) of the original type.
        /// Really deals with structured types, but also 
        /// peels off collection wrappers
        /// </summary>
        /// <param name="type">the type to explode</param>
        /// <returns>the typeinfo for this type (with the explosion)</returns>
        private TypeInfo ExplodeType(md.TypeUsage type)
        {
            if (TypeUtils.IsStructuredType(type))
            {
                TypeInfo typeInfo = GetTypeInfo(type);
                ExplodeType(typeInfo);
                return typeInfo;
            }
 
            if (TypeUtils.IsCollectionType(type))
            {
                md.TypeUsage elementType = TypeHelpers.GetEdmType<md.CollectionType>(type).TypeUsage;
                ExplodeType(elementType);
                return null;
            }
            return null;
        }
 
        /// <summary>
        /// Type Explosion - simply delegates to the root type
        /// </summary>
        /// <param name="typeInfo">type info</param>
        private void ExplodeType(TypeInfo typeInfo)
        {
            ExplodeRootStructuredType(typeInfo.RootType);
        }
 
        /// <summary>
        /// "Explode" a root type. (ie) add each member of the type to a flat list of 
        /// members for the supertype. 
        /// 
        /// Type explosion works in a DFS style model. We first walk through the
        /// list of properties for the current type, and "flatten" out the properties
        /// that are themselves "structured". We then target each subtype (recursively)
        /// and perform the same kind of processing. 
        /// 
        /// Consider a very simple case:
        /// 
        ///   Q = (z1 int, z2 date)
        ///   Q2: Q = (z3 string)  -- Q2 is a subtype of Q
        ///   T = (a int, b Q, c date)
        ///   S: T = (d int)  -- read as S is a subtype of T
        /// 
        /// The result of flattening T (and S) will be
        /// 
        ///   (a int, b.z1 int, b.z2 date, b.z3 string, c date, d int)
        /// </summary>
        /// <param name="rootType">the root type to explode</param>        
        private void ExplodeRootStructuredType(RootTypeInfo rootType)
        {
            // Already done??
            if (rootType.FlattenedType != null)
            {
                return;
            }
 
            //
            // Special handling for root types. Add any special
            // properties that are needed - TypeId, EntitySetId, etc
            //
            if (NeedsTypeIdProperty(rootType))
            {
                rootType.AddPropertyRef(TypeIdPropertyRef.Instance);
                // check for discriminator map; if one exists, use custom discriminator member; otherwise, use default
                if (null != rootType.DiscriminatorMap)
                {
                    rootType.TypeIdKind = TypeIdKind.UserSpecified;
                    rootType.TypeIdType = md.Helper.GetModelTypeUsage(rootType.DiscriminatorMap.DiscriminatorProperty);
                }
                else
                {
                    rootType.TypeIdKind = TypeIdKind.Generated;
                    rootType.TypeIdType = m_stringType;
                }
            }
            if (NeedsEntitySetIdProperty(rootType))
            {
                rootType.AddPropertyRef(EntitySetIdPropertyRef.Instance);
            }
            if (NeedsNullSentinelProperty(rootType))
            {
                rootType.AddPropertyRef(NullSentinelPropertyRef.Instance);
            }
 
            //
            // Then add members from each type in the hierarchy (including 
            // the root type)
            //
            ExplodeRootStructuredTypeHelper(rootType);
 
            //
            // For entity types, add all the rel-properties now. Note that rel-properties
            // are added after the regular properties of all subtypes
            //
            if (md.TypeSemantics.IsEntityType(rootType.Type))
            {
                AddRelProperties(rootType);
            }
 
            //
            // We've now gotten all the relevant properties
            // Now let's create a new record type
            //
            CreateFlattenedRecordType(rootType);
        }
 
        /// <summary>
        /// Helper for ExplodeType. 
        /// Walks through each member introduced by the current type, and
        /// adds it onto the "flat" record type being constructed. 
        /// We then walk through all subtypes of this type, and process those as
        /// well.
        /// Special handling for Refs: we only add the keys; there is no
        /// need to handle subtypes (since they won't be introducing anything
        /// different)
        /// </summary>
        /// <param name="typeInfo">type in the type hierarchy</param>
        private void ExplodeRootStructuredTypeHelper(TypeInfo typeInfo)
        {
            RootTypeInfo rootType = typeInfo.RootType;
 
            // Identify the members of this type. For Refs, use the key properties
            // of the target entity type. For all other types, simply use the type
            // members
            IEnumerable typeMembers = null;
            md.RefType refType;
            if (TypeHelpers.TryGetEdmType<md.RefType>(typeInfo.Type, out refType))
            {
                //
                // If this is not the root type, then don't bother adding the keys.
                // the root type has already done this
                //
                if (!typeInfo.IsRootType)
                {
                    return;
                }
                typeMembers = refType.ElementType.KeyMembers;
            }
            else
            {
                typeMembers = TypeHelpers.GetDeclaredStructuralMembers(typeInfo.Type);
            }
 
            // Walk through all the members of the type
            foreach (md.EdmMember p in typeMembers)
            {
                TypeInfo propertyType = ExplodeType(p.TypeUsage);
 
                //
                // If we can't find a TypeInfo for this property's type, then it must
                // be a scalar type or a collection type. In either case, we'll 
                // build up a SimplePropertyRef
                //
                if (propertyType == null)
                {
                    rootType.AddPropertyRef(new SimplePropertyRef(p));
                }
                else
                {
                    //
                    // We're dealing with a structured type again. Create NestedPropertyRef
                    // for each property of the nested type
                    //
                    foreach (PropertyRef nestedPropInfo in propertyType.PropertyRefList)
                    {
                        rootType.AddPropertyRef(nestedPropInfo.CreateNestedPropertyRef(p));
                    }
                }
            }
 
            // 
            // Process all subtypes now
            //
            foreach (TypeInfo subTypeInfo in typeInfo.ImmediateSubTypes)
            {
                ExplodeRootStructuredTypeHelper(subTypeInfo);
            }
        }
 
        /// <summary>
        /// Add the list of rel-properties for this type
        /// </summary>
        /// <param name="typeInfo">the type to process</param>
        private void AddRelProperties(TypeInfo typeInfo)
        {
 
            md.EntityTypeBase entityType = (md.EntityTypeBase)typeInfo.Type.EdmType;
 
            //
            // Walk through each rel-property defined for this specific type, 
            // and add a corresponding property-ref
            //
            foreach (RelProperty p in m_relPropertyHelper.GetDeclaredOnlyRelProperties(entityType))
            {
                md.EdmType refType = p.ToEnd.TypeUsage.EdmType;
                TypeInfo refTypeInfo = GetTypeInfo(p.ToEnd.TypeUsage);
 
                //
                // We're dealing with a structured type again - flatten this out
                // as well
                //
                ExplodeType(refTypeInfo);
 
                foreach (PropertyRef nestedPropInfo in refTypeInfo.PropertyRefList)
                {
                    typeInfo.RootType.AddPropertyRef(nestedPropInfo.CreateNestedPropertyRef(p));
                }
            }
 
            // 
            // Process all subtypes now
            //
            foreach (TypeInfo subTypeInfo in typeInfo.ImmediateSubTypes)
            {
                AddRelProperties(subTypeInfo);
            }
        }
 
        /// <summary>
        /// Create the flattened record type for the type.
        /// Walk through the list of property refs, and creates a new field
        /// (which we name as "F1", "F2" etc.) with the required property type.
        /// 
        /// We then produce a mapping from the original property (propertyRef really)
        /// to the new property for use in later modules.
        /// 
        /// Finally, we identify the TypeId and EntitySetId property if they exist
        /// </summary>
        /// <param name="type"></param>
        private void CreateFlattenedRecordType(RootTypeInfo type)
        {
            //
            // If this type corresponds to an entity type, and that entity type
            // has no subtypes, and that that entity type has no complex properties
            // then simply use the name from that property
            //
            bool usePropertyNamesFromUnderlyingType;
            if (md.TypeSemantics.IsEntityType(type.Type) &&
                type.ImmediateSubTypes.Count == 0)
            {
                usePropertyNamesFromUnderlyingType = true;
            }
            else
            {
                usePropertyNamesFromUnderlyingType = false;
            }
 
 
            // Build the record type
            List<KeyValuePair<string, md.TypeUsage>> fieldList = new List<KeyValuePair<string, md.TypeUsage>>();
            HashSet<string> fieldNames = new HashSet<string>();
            int nextFieldId = 0;
            foreach (PropertyRef p in type.PropertyRefList)
            {
                string fieldName = null;
                if (usePropertyNamesFromUnderlyingType)
                {
                    SimplePropertyRef simpleP = p as SimplePropertyRef;
                    if (simpleP != null)
                    {
                        fieldName = simpleP.Property.Name;
                    }
                }
               
                if (fieldName == null)
                {
                    fieldName = "F" + nextFieldId.ToString(CultureInfo.InvariantCulture);
                    nextFieldId++;
                }
                
                // Deal with collisions
                while (fieldNames.Contains(fieldName))
                {
                    fieldName = "F" + nextFieldId.ToString(CultureInfo.InvariantCulture);
                    nextFieldId++;
                }
 
                md.TypeUsage propertyType = GetPropertyType(type, p);
                fieldList.Add(new KeyValuePair<string, md.TypeUsage>(fieldName, propertyType));
                fieldNames.Add(fieldName);
            }
 
            type.FlattenedType = TypeHelpers.CreateRowType(fieldList);
 
            // Now build up the property map
            IEnumerator<PropertyRef> origProps = type.PropertyRefList.GetEnumerator();
            foreach (md.EdmProperty p in type.FlattenedType.Properties)
            {
                if (!origProps.MoveNext())
                {
                    PlanCompiler.Assert(false, "property refs count and flattened type member count mismatch?");
                }
                type.AddPropertyMapping(origProps.Current, p);
            }
        }
 
        /// <summary>
        /// Get the "new" type corresponding to the input type. For structured types,
        /// we return the flattened record type.
        /// For collections of structured type, we return a new collection type of the corresponding flattened
        /// type.
        /// For enum types we return the underlying type of the enum type.
        /// For strong spatial types we return the union type that includes the strong spatial type.
        /// For everything else, we return the input type
        /// </summary>
        /// <param name="type">the original type</param>
        /// <returns>the new type (if any)</returns>
        private md.TypeUsage GetNewType(md.TypeUsage type)
        {
            if (TypeUtils.IsStructuredType(type))
            {
                TypeInfo typeInfo = GetTypeInfo(type);
                return typeInfo.FlattenedTypeUsage;
            }
            md.TypeUsage elementType;
            if (TypeHelpers.TryGetCollectionElementType(type, out elementType))
            {
                md.TypeUsage newElementType = GetNewType(elementType);
                if (newElementType.EdmEquals(elementType))
                {
                    return type;
                }
                else
                {
                    return TypeHelpers.CreateCollectionTypeUsage(newElementType);
                }
            }
 
            if (TypeUtils.IsEnumerationType(type))
            {
                return TypeHelpers.CreateEnumUnderlyingTypeUsage(type);
            }
            
            if (md.TypeSemantics.IsStrongSpatialType(type))
            {
                return TypeHelpers.CreateSpatialUnionTypeUsage(type);
            }
 
            // simple scalar
            return type;
        }
 
        /// <summary>
        /// Get the datatype for a propertyRef. The only concrete classes that we 
        /// handle are TypeIdPropertyRef, and BasicPropertyRef. 
        /// AllPropertyRef is illegal here.
        /// For BasicPropertyRef, we simply pick up the type from the corresponding
        /// property. For TypeIdPropertyRef, we use "string" as the default type
        /// or the discriminator property type where one is available.
        /// </summary>
        /// <param name="typeInfo">typeinfo of the current type</param>
        /// <param name="p">current property ref</param>
        /// <returns>the datatype of the property</returns>
        private md.TypeUsage GetPropertyType(RootTypeInfo typeInfo, PropertyRef p)
        {
            md.TypeUsage result = null;
 
            PropertyRef innerProperty = null;
            // Get the "leaf" property first
            while (p is NestedPropertyRef)
            {
                NestedPropertyRef npr = (NestedPropertyRef)p;
                p = npr.OuterProperty;
                innerProperty = npr.InnerProperty;
            }
 
            if (p is TypeIdPropertyRef)
            {
                //
                // Get to the innermost type that specifies this typeid (the entity type),
                // get the datatype for the typeid column from that type
                //
                if (innerProperty != null && innerProperty is SimplePropertyRef)
                {
                    md.TypeUsage innerType = ((SimplePropertyRef)innerProperty).Property.TypeUsage;
                    TypeInfo innerTypeInfo = GetTypeInfo(innerType);
                    result = innerTypeInfo.RootType.TypeIdType;
                }
                else
                {
                    result = typeInfo.TypeIdType;
                }
            }
            else if (p is EntitySetIdPropertyRef || p is NullSentinelPropertyRef)
            {
                result = m_intType;
            }
            else if (p is RelPropertyRef)
            {
                result = (p as RelPropertyRef).Property.ToEnd.TypeUsage;
            }
            else
            {
                SimplePropertyRef simpleP = p as SimplePropertyRef;
                if (simpleP != null)
                {
                    result = md.Helper.GetModelTypeUsage(simpleP.Property);
                }
            }
 
            result = GetNewType(result);
            PlanCompiler.Assert(null != result, "unrecognized property type?");
            return result;
        }
 
        #endregion
 
        #endregion
 
        #region utils
        /// <summary>
        /// Get the root entity type for a type
        /// </summary>
        /// <param name="type">entity type</param>
        /// <returns></returns>
        private static md.EntityTypeBase GetRootType(md.EntityTypeBase type)
        {
            while (type.BaseType != null)
            {
                type = (md.EntityTypeBase)type.BaseType;
            }
            return type;
        }
        #endregion
        #endregion
    }
}