//--------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // // // @owner [....] // @backupOwner [....] //--------------------------------------------------------------------- using System; using System.Collections; using System.Collections.Generic; using System.Data.Common; using System.Data.Common.Utils; using md = System.Data.Metadata.Edm; using System.Data.Query.InternalTrees; using System.Data.Query.PlanCompiler; //using System.Diagnostics; // Please use PlanCompiler.Assert instead of Debug.Assert in this class... using System.Globalization; namespace System.Data.Query.PlanCompiler { internal class ColumnMapProcessor { #region "public" methods internal ColumnMap ExpandColumnMap() { // special handling for the case when the top-level var is a collection. // The element type of the collection may have changed, and consequently a new // var will have been created. We simply create a columnmap with that var. if (m_varInfo.Kind == VarInfoKind.CollectionVarInfo) { return new VarRefColumnMap(m_columnMap.Var.Type, m_columnMap.Name, ((CollectionVarInfo)m_varInfo).NewVar); } else if(m_varInfo.Kind == VarInfoKind.PrimitiveTypeVarInfo) { return new VarRefColumnMap(m_columnMap.Var.Type, m_columnMap.Name, ((PrimitiveTypeVarInfo)m_varInfo).NewVar); } else { return this.CreateColumnMap(m_columnMap.Var.Type, m_columnMap.Name); } } #endregion #region Constructors internal ColumnMapProcessor(VarRefColumnMap columnMap, VarInfo varInfo, StructuredTypeInfo typeInfo) { m_columnMap = columnMap; m_varInfo = varInfo; PlanCompiler.Assert(varInfo.NewVars != null && varInfo.NewVars.Count > 0, "No new Vars specified"); m_varList = varInfo.NewVars.GetEnumerator(); m_typeInfo = typeInfo; } #endregion #region private state private IEnumerator m_varList; private VarInfo m_varInfo; private VarRefColumnMap m_columnMap; private StructuredTypeInfo m_typeInfo; private const string c_TypeIdColumnName = "__TypeId"; // name of the typeid column private const string c_EntitySetIdColumnName = "__EntitySetId"; // name of the entityset column private const string c_NullSentinelColumnName = "__NullSentinel"; // name of the nullability column #endregion #region private methods private Var GetNextVar() { if (m_varList.MoveNext()) { return m_varList.Current; } PlanCompiler.Assert(false, "Could not GetNextVar"); return null; } /// /// Creates a column map for a column /// /// column datatype /// column name /// private ColumnMap CreateColumnMap(md.TypeUsage type, string name) { // For simple types, create a simple column map // Temporarily, handle collections exactly the same way if (!TypeUtils.IsStructuredType(type)) { return CreateSimpleColumnMap(type, name); } // At this point, we must be dealing with either a record type, a // complex type, or an entity type return CreateStructuralColumnMap(type, name); } /// /// Create a column map for a complextype column /// /// Type information for the type /// column name /// Supertype info if any /// Dictionary of typeidvalue->column map /// List of all maps /// private ComplexTypeColumnMap CreateComplexTypeColumnMap(TypeInfo typeInfo, string name, ComplexTypeColumnMap superTypeColumnMap, Dictionary discriminatorMap, List allMaps) { List propertyColumnMapList = new List(); IEnumerable myProperties = null; SimpleColumnMap nullSentinelColumnMap = null; if (typeInfo.HasNullSentinelProperty) { nullSentinelColumnMap = CreateSimpleColumnMap(md.Helper.GetModelTypeUsage(typeInfo.NullSentinelProperty), c_NullSentinelColumnName); } // Copy over information from my supertype if it already exists if (superTypeColumnMap != null) { foreach (ColumnMap c in superTypeColumnMap.Properties) { propertyColumnMapList.Add(c); } myProperties = TypeHelpers.GetDeclaredStructuralMembers(typeInfo.Type); } else { // need to get all members otherwise myProperties = TypeHelpers.GetAllStructuralMembers(typeInfo.Type); } // Now add on all of my "specific" properties foreach (md.EdmMember property in myProperties) { ColumnMap propertyColumnMap = CreateColumnMap(md.Helper.GetModelTypeUsage(property), property.Name); propertyColumnMapList.Add(propertyColumnMap); } // Create a map for myself ComplexTypeColumnMap columnMap = new ComplexTypeColumnMap(typeInfo.Type, name, propertyColumnMapList.ToArray(), nullSentinelColumnMap); // if a dictionary is supplied, add myself to the dictionary if (discriminatorMap != null) { discriminatorMap[typeInfo.TypeId] = columnMap; } if (allMaps != null) { allMaps.Add(columnMap); } // Finally walk through my subtypes - use the same column name foreach (TypeInfo subTypeInfo in typeInfo.ImmediateSubTypes) { CreateComplexTypeColumnMap(subTypeInfo, name, columnMap, discriminatorMap, allMaps); } return columnMap; } /// /// Create a column map for an entitytype column. /// Currently, the key columns are not duplicated (ie) they point into the /// same locations as in the properties list. /// Note: we also don't handle keys that are properties of nested fields /// /// Type information for the type /// column name /// supertype information if any /// Dictionary of typeid->column map information /// List of all column maps (including those without typeid) /// should we handle rel-properties? /// private EntityColumnMap CreateEntityColumnMap(TypeInfo typeInfo, string name, EntityColumnMap superTypeColumnMap, Dictionary discriminatorMap, List allMaps, bool handleRelProperties) { EntityColumnMap columnMap = null; List propertyColumnMapList = new List(); // Copy over information from my supertype if it already exists if (superTypeColumnMap != null) { // get supertype properties foreach (ColumnMap c in superTypeColumnMap.Properties) { propertyColumnMapList.Add(c); } // Now add on all of my "specific" properties foreach (md.EdmMember property in TypeHelpers.GetDeclaredStructuralMembers(typeInfo.Type)) { ColumnMap propertyColumnMap = CreateColumnMap(md.Helper.GetModelTypeUsage(property), property.Name); propertyColumnMapList.Add(propertyColumnMap); } // create the entity column map w/ information from my supertype columnMap = new EntityColumnMap(typeInfo.Type, name, propertyColumnMapList.ToArray(), superTypeColumnMap.EntityIdentity); } else { SimpleColumnMap entitySetIdColumnMap = null; if (typeInfo.HasEntitySetIdProperty) { entitySetIdColumnMap = CreateEntitySetIdColumnMap(typeInfo.EntitySetIdProperty); } // build up a list of key columns List keyColumnMapList = new List(); // Create a dictionary to look up the key properties Dictionary keyPropertyMap = new Dictionary(); foreach (md.EdmMember property in TypeHelpers.GetDeclaredStructuralMembers(typeInfo.Type)) { ColumnMap propertyColumnMap = CreateColumnMap(md.Helper.GetModelTypeUsage(property), property.Name); propertyColumnMapList.Add(propertyColumnMap); // add property to keymap, if this property is part of the key if (md.TypeSemantics.IsPartOfKey(property)) { md.EdmProperty edmProperty = property as md.EdmProperty; PlanCompiler.Assert(edmProperty != null, "EntityType key member is not property?"); keyPropertyMap[edmProperty] = propertyColumnMap; } } // Build up the key list if required foreach (md.EdmMember keyProperty in TypeHelpers.GetEdmType(typeInfo.Type).KeyMembers) { md.EdmProperty edmKeyProperty = keyProperty as md.EdmProperty; PlanCompiler.Assert(edmKeyProperty != null, "EntityType key member is not property?"); SimpleColumnMap keyColumnMap = keyPropertyMap[edmKeyProperty] as SimpleColumnMap; PlanCompiler.Assert(keyColumnMap != null, "keyColumnMap is null"); keyColumnMapList.Add(keyColumnMap); } // // Create the entity identity. // EntityIdentity identity = CreateEntityIdentity((md.EntityType)typeInfo.Type.EdmType, entitySetIdColumnMap, keyColumnMapList.ToArray()); // finally create the entity column map columnMap = new EntityColumnMap(typeInfo.Type, name, propertyColumnMapList.ToArray(), identity); } // if a dictionary is supplied, add myself to the dictionary (abstract types need not be added) if (discriminatorMap != null) { // where DiscriminatedNewInstanceOp is used, there will not be an explicit type id for an abstract type // or types that do not appear in the QueryView // (the mapping will not include such information) if (null != typeInfo.TypeId) { discriminatorMap[typeInfo.TypeId] = columnMap; } } if (allMaps != null) { allMaps.Add(columnMap); } // Finally walk through my subtypes foreach (TypeInfo subTypeInfo in typeInfo.ImmediateSubTypes) { CreateEntityColumnMap(subTypeInfo, name, columnMap, discriminatorMap, allMaps, false); } // // Build up the list of rel property column maps // if (handleRelProperties) { BuildRelPropertyColumnMaps(typeInfo, true); } return columnMap; } /// /// Build up the list of columnmaps for the relproperties. /// Assumption: rel-properties follow after ALL the regular properties of the /// types in the type hierarchy. /// For now, we're simply going to ignore the rel-property columnmaps - we're /// just going to use this function to "drain" the corresponding vars /// /// typeinfo for the entity type /// should we get rel-properties from our supertype instances private void BuildRelPropertyColumnMaps(TypeInfo typeInfo, bool includeSupertypeRelProperties) { // // Get the appropriate set of rel-properties // IEnumerable relProperties = null; if (includeSupertypeRelProperties) { relProperties = m_typeInfo.RelPropertyHelper.GetRelProperties(typeInfo.Type.EdmType as md.EntityTypeBase); } else { relProperties = m_typeInfo.RelPropertyHelper.GetDeclaredOnlyRelProperties(typeInfo.Type.EdmType as md.EntityTypeBase); } // // Create a column-map for each rel-properties // foreach (RelProperty property in relProperties) { ColumnMap propertyColumnMap = CreateColumnMap(property.ToEnd.TypeUsage, property.ToString()); } // // Add all subtypes // foreach (TypeInfo subTypeInfo in typeInfo.ImmediateSubTypes) { BuildRelPropertyColumnMaps(subTypeInfo, false); } } /// /// Create a column map for the entitysetid column /// /// /// private SimpleColumnMap CreateEntitySetIdColumnMap(md.EdmProperty prop) { return CreateSimpleColumnMap(md.Helper.GetModelTypeUsage(prop), c_EntitySetIdColumnName); } /// /// Creates a column map for a polymorphic type. This method first /// creates column maps for each type that is a subtype of the input type, /// and then creates a dictionary of typeid value -> column /// Finally, a PolymorphicColumnMap is created with these pieces of information /// /// Info about the type /// column name /// private SimplePolymorphicColumnMap CreatePolymorphicColumnMap(TypeInfo typeInfo, string name) { // if the typeInfo has a DiscriminatorMap, use TrailingSpaceComparer to ensure that lookups // against discriminator values that SQL Server has right-padded (e.g. nchar and char) are properly // interpreted Dictionary discriminatorMap = new Dictionary( typeInfo.RootType.DiscriminatorMap == null ? null : TrailingSpaceComparer.Instance); // abstract types may not have discriminator values, but may nonetheless be interesting List allMaps = new List(); // SQLBUDT #433011 -- Polymorphic types must construct column maps // that map to the entire type hierarchy, so we // need to use the RootType, not the current type. TypeInfo rootTypeInfo = typeInfo.RootType; // Get the type discriminant column first SimpleColumnMap typeIdColumnMap = CreateTypeIdColumnMap(rootTypeInfo.TypeIdProperty); // Prepare a place for the constructors to put the columns on the base // type, as they identify them. TypedColumnMap rootTypeColumnMap = null; // process complex/entity types appropriately // use the same name for the column if (md.TypeSemantics.IsComplexType(typeInfo.Type)) { rootTypeColumnMap = CreateComplexTypeColumnMap(rootTypeInfo, name, null, discriminatorMap, allMaps); } else { rootTypeColumnMap = CreateEntityColumnMap(rootTypeInfo, name, null, discriminatorMap, allMaps, true); } // Naturally, nothing is simple; we need to walk the rootTypeColumnMap hierarchy // and find the column map for the type that we are supposed to have as the base // type of this hierarchy. TypedColumnMap baseTypeColumnMap = null; foreach (TypedColumnMap value in allMaps) { if (md.TypeSemantics.IsStructurallyEqual(value.Type, typeInfo.Type)) { baseTypeColumnMap = value; break; } } PlanCompiler.Assert(null != baseTypeColumnMap, "Didn't find requested type in polymorphic type hierarchy?"); // Create a polymorphic column map SimplePolymorphicColumnMap result = new SimplePolymorphicColumnMap(typeInfo.Type, name, baseTypeColumnMap.Properties, typeIdColumnMap, discriminatorMap); return result; } /// /// Create a column map for a record type. Simply iterates through the /// list of fields, and produces a column map for each field /// /// Type information for the record type /// column name /// private RecordColumnMap CreateRecordColumnMap(TypeInfo typeInfo, string name) { PlanCompiler.Assert(typeInfo.Type.EdmType is md.RowType, "not RowType"); SimpleColumnMap nullSentinelColumnMap = null; if (typeInfo.HasNullSentinelProperty) { nullSentinelColumnMap = CreateSimpleColumnMap(md.Helper.GetModelTypeUsage(typeInfo.NullSentinelProperty), c_NullSentinelColumnName); } md.ReadOnlyMetadataCollection properties = TypeHelpers.GetProperties(typeInfo.Type); ColumnMap[] propertyColumnMapList = new ColumnMap[properties.Count]; for (int i = 0; i < propertyColumnMapList.Length; ++i) { md.EdmMember property = properties[i]; propertyColumnMapList[i] = CreateColumnMap(md.Helper.GetModelTypeUsage(property), property.Name); } RecordColumnMap result = new RecordColumnMap(typeInfo.Type, name, propertyColumnMapList, nullSentinelColumnMap); return result; } /// /// Create a column map for a ref type /// /// Type information for the ref type /// Name of the column /// Column map for the ref type private RefColumnMap CreateRefColumnMap(TypeInfo typeInfo, string name) { SimpleColumnMap entitySetIdColumnMap = null; if (typeInfo.HasEntitySetIdProperty) { entitySetIdColumnMap = CreateSimpleColumnMap(md.Helper.GetModelTypeUsage(typeInfo.EntitySetIdProperty), c_EntitySetIdColumnName); } // get the target entity type, md.EntityType entityType = (md.EntityType)(TypeHelpers.GetEdmType(typeInfo.Type).ElementType); // Iterate through the list of "key" properties SimpleColumnMap[] keyColList = new SimpleColumnMap[entityType.KeyMembers.Count]; for (int i = 0; i < keyColList.Length; ++i) { md.EdmMember property = entityType.KeyMembers[i]; keyColList[i] = CreateSimpleColumnMap(md.Helper.GetModelTypeUsage(property), property.Name); } // Create the entity identity EntityIdentity identity = CreateEntityIdentity(entityType, entitySetIdColumnMap, keyColList); RefColumnMap result = new RefColumnMap(typeInfo.Type, name, identity); return result; } /// /// Create a simple columnmap - applies only to scalar properties /// (Temporarily, also for collections) /// Simply picks up the next available column in the reader /// /// Column type /// column name /// Column map for this column private SimpleColumnMap CreateSimpleColumnMap(md.TypeUsage type, string name) { Var newVar = GetNextVar(); SimpleColumnMap result = new VarRefColumnMap(type, name, newVar); return result; } /// /// Create a column map for the typeid column /// /// /// private SimpleColumnMap CreateTypeIdColumnMap(md.EdmProperty prop) { return CreateSimpleColumnMap(md.Helper.GetModelTypeUsage(prop), c_TypeIdColumnName); } /// /// Create a column map for a structural column - ref/complextype/entity/record /// /// Type info for the type /// column name /// private ColumnMap CreateStructuralColumnMap(md.TypeUsage type, string name) { // Get our augmented type information for this type TypeInfo typeInfo = m_typeInfo.GetTypeInfo(type); // records? if (md.TypeSemantics.IsRowType(type)) { return CreateRecordColumnMap(typeInfo, name); } // ref? if (md.TypeSemantics.IsReferenceType(type)) { return CreateRefColumnMap(typeInfo, name); } // polymorphic type? if (typeInfo.HasTypeIdProperty) { return CreatePolymorphicColumnMap(typeInfo, name); } // process complex/entity types appropriately if (md.TypeSemantics.IsComplexType(type)) { return CreateComplexTypeColumnMap(typeInfo, name, null, null, null); } if (md.TypeSemantics.IsEntityType(type)) { return CreateEntityColumnMap(typeInfo, name, null, null, null, true); } // Anything else is not supported (this currently includes relationship types) throw EntityUtil.NotSupported(type.Identity); } /// /// Build out an EntityIdentity structure - for use by EntityColumnMap and RefColumnMap /// /// the entity type in question /// column map for the entitysetid column /// column maps for the keys /// private EntityIdentity CreateEntityIdentity(md.EntityType entityType, SimpleColumnMap entitySetIdColumnMap, SimpleColumnMap[] keyColumnMaps) { // // If we have an entitysetid (and therefore, a column map for the entitysetid), // then use a discriminated entity identity; otherwise, we use a simpleentityidentity // instead // if (entitySetIdColumnMap != null) { return new DiscriminatedEntityIdentity(entitySetIdColumnMap, m_typeInfo.EntitySetIdToEntitySetMap, keyColumnMaps); } else { md.EntitySet entitySet = m_typeInfo.GetEntitySet(entityType); PlanCompiler.Assert(entitySet != null, "Expected non-null entityset when no entitysetid is required. Entity type = " + entityType); return new SimpleEntityIdentity(entitySet, keyColumnMaps); } } #endregion } }