//--------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // // // @owner Microsoft // @backupOwner Microsoft //--------------------------------------------------------------------- using System; using System.Text; using System.Collections.Generic; using md = System.Data.Metadata.Edm; using mp = System.Data.Mapping; using System.Globalization; using System.Diagnostics; using System.Data.Common.Utils; // A ColumnMap is a data structure that maps columns from the C space to // the corresponding columns from one or more underlying readers. // // ColumnMaps are used by the ResultAssembly phase to assemble results in the // desired shape (as requested by the caller) from a set of underlying // (usually) flat readers. ColumnMaps are produced as part of the PlanCompiler // module of the bridge, and are consumed by the Execution phase of the bridge. // // * Simple (scalar) columns (and UDTs) are represented by a SimpleColumnMap // * Record type columns are represented by a RecordColumnMap // * A nominal type instance (that supports inheritance) is usually represented // by a PolymorphicColumnMap - this polymorphicColumnMap contains information // about the type discriminator (assumed to be a simple column), and a mapping // from type-discriminator value to the column map for the specific type // * The specific type for nominal types is represented by ComplexTypeColumnMap // for complextype columns, and EntityColumnMap for entity type columns. // EntityColumnMaps additionally have an EntityIdentity that describes // the entity identity. The entity identity is logically just a set of keys // (and the column maps), plus a column map that helps to identify the // the appropriate entity set for the entity instance // * Refs are represented by a RefColumnMap. The RefColumnMap simply contains an // EntityIdentity // * Collections are represented by either a SimpleCollectionColumnMap or a // DiscriminatedCollectionColumnMap. Both of these contain a column map for the // element type, and an optional list of simple columns (the keys) that help // demarcate the elements of a specific collection instance. // The DiscriminatedCollectionColumnMap is used in scenarios when the containing // row has multiple collections, and the different collection properties must be // differentiated. This differentiation is achieved via a Discriminator column // (a simple column), and a Discriminator value. The value of the Discriminator // column is read and compared with the DiscriminatorValue stored in this map // to determine if we're dealing with the current collection. // // NOTE: // * Key columns are assumed to be SimpleColumns. There may be more than one key // column (applies to EntityColumnMap and *CollectionColumnMap) // * TypeDiscriminator and Discriminator columns are also considered to be // SimpleColumns. There are singleton columns. // // It is the responsibility of the PlanCompiler phase to produce the right column // maps. // // The result of a query is always assumed to be a collection. The ColumnMap that we // return as part of plan compilation refers to the element type of this collection // - the element type is usually a structured type, but may also be a simple type // or another collection type. How does the DbRecord framework handle these cases? // // namespace System.Data.Query.InternalTrees { /// /// Represents a column /// internal abstract class ColumnMap { private md.TypeUsage m_type; // column datatype private string m_name; // name of the column /// /// Default Column Name; should not be set until CodeGen once we're done /// with all our transformations that might give us a good name, but put /// here for ease of finding it. /// internal const string DefaultColumnName = "Value"; /// /// Simple constructor - just needs the name and type of the column /// /// column type /// column name internal ColumnMap(md.TypeUsage type, string name) { Debug.Assert(type != null, "Unspecified type"); m_type = type; m_name = name; } /// /// Get the column's datatype /// internal md.TypeUsage Type { get { return m_type; } } /// /// Get the column name /// internal string Name { get { return m_name; } set { Debug.Assert(!String.IsNullOrEmpty(value), "invalid name?"); m_name = value; } } /// /// Returns whether the column already has a name; /// internal bool IsNamed { get { return m_name != null; } } /// /// Visitor Design Pattern /// /// /// /// /// [DebuggerNonUserCode] internal abstract void Accept(ColumnMapVisitor visitor, TArgType arg); /// /// Visitor Design Pattern /// /// /// /// /// /// [DebuggerNonUserCode] internal abstract TResultType Accept(ColumnMapVisitorWithResults visitor, TArgType arg); } /// /// Base class for simple column maps; can be either a VarRefColumnMap or /// ScalarColumnMap; the former is used pretty much throughout the PlanCompiler, /// while the latter will only be used once we generate the final Plan. /// internal abstract class SimpleColumnMap : ColumnMap { /// /// Basic constructor /// /// datatype for this column /// column name internal SimpleColumnMap(md.TypeUsage type, string name) : base(type, name) { } } /// /// Column map for a scalar column - maps 1-1 with a column from a /// row of the underlying reader /// internal class ScalarColumnMap : SimpleColumnMap { private int m_commandId; private int m_columnPos; /// /// Basic constructor /// /// datatype for this column /// column name /// Underlying command to locate this column /// Position in underlying reader internal ScalarColumnMap(md.TypeUsage type, string name, int commandId, int columnPos) : base(type, name) { Debug.Assert(commandId >= 0, "invalid command id"); Debug.Assert(columnPos >= 0, "invalid column position"); m_commandId = commandId; m_columnPos = columnPos; } /// /// The command (reader, really) to get this column value from /// internal int CommandId { get { return m_commandId; } } /// /// Column position within the reader of the command /// internal int ColumnPos { get { return m_columnPos; } } /// /// Visitor Design Pattern /// /// /// /// [DebuggerNonUserCode] internal override void Accept(ColumnMapVisitor visitor, TArgType arg) { visitor.Visit(this, arg); } /// /// Visitor Design Pattern /// /// /// /// /// [DebuggerNonUserCode] internal override TResultType Accept(ColumnMapVisitorWithResults visitor, TArgType arg) { return visitor.Visit(this, arg); } /// /// Debugging support /// /// public override string ToString() { return String.Format(CultureInfo.InvariantCulture, "S({0},{1})", this.CommandId, this.ColumnPos); } } #region Structured Columns /// /// Represents a column map for a structured column /// internal abstract class StructuredColumnMap : ColumnMap { private readonly ColumnMap[] m_properties; /// /// Structured columnmap constructor /// /// datatype for this column /// column name /// list of properties internal StructuredColumnMap(md.TypeUsage type, string name, ColumnMap[] properties) : base(type, name) { Debug.Assert(properties != null, "No properties (gasp!) for a structured type"); m_properties = properties; } /// /// Get the null sentinel column, if any. Virtual so only derived column map /// types that can have NullSentinel have to provide storage, etc. /// virtual internal SimpleColumnMap NullSentinel { get { return null; } } /// /// Get the list of properties that constitute this structured type /// internal ColumnMap[] Properties { get { return m_properties; } } /// /// Debugging support /// /// public override string ToString() { StringBuilder sb = new StringBuilder(); string separator = String.Empty; sb.Append("{"); foreach (ColumnMap c in this.Properties) { sb.AppendFormat(CultureInfo.InvariantCulture, "{0}{1}", separator, c); separator = ","; } sb.Append("}"); return sb.ToString(); } } /// /// Represents a record (an untyped structured column) /// internal class RecordColumnMap : StructuredColumnMap { private SimpleColumnMap m_nullSentinel; /// /// Constructor for a record column map /// /// Datatype of this column /// column name /// List of ColumnMaps - one for each property internal RecordColumnMap(md.TypeUsage type, string name, ColumnMap[] properties, SimpleColumnMap nullSentinel) : base(type, name, properties) { m_nullSentinel = nullSentinel; } /// /// Get the type Nullability column /// internal override SimpleColumnMap NullSentinel { get { return m_nullSentinel; } } /// /// Visitor Design Pattern /// /// /// /// [DebuggerNonUserCode] internal override void Accept(ColumnMapVisitor visitor, TArgType arg) { visitor.Visit(this, arg); } /// /// Visitor Design Pattern /// /// /// /// /// [DebuggerNonUserCode] internal override TResultType Accept(ColumnMapVisitorWithResults visitor, TArgType arg) { return visitor.Visit(this, arg); } } /// /// Column map for a "typed" column /// - either an entity type or a complex type /// internal abstract class TypedColumnMap : StructuredColumnMap { /// /// Typed columnMap constructor /// /// Datatype of column /// column name /// List of column maps - one for each property internal TypedColumnMap(md.TypeUsage type, string name, ColumnMap[] properties) : base(type, name, properties) { } } /// /// Represents a polymorphic typed column - either an entity or /// a complex type. /// internal class SimplePolymorphicColumnMap : TypedColumnMap { private SimpleColumnMap m_typeDiscriminator; private Dictionary m_typedColumnMap; /// /// Internal constructor /// /// datatype of the column /// column name /// column map for type discriminator column /// base list of fields common to all types /// map from type discriminator value->columnMap internal SimplePolymorphicColumnMap(md.TypeUsage type, string name, ColumnMap[] baseTypeColumns, SimpleColumnMap typeDiscriminator, Dictionary typeChoices) : base(type, name, baseTypeColumns) { Debug.Assert(typeDiscriminator != null, "Must specify a type discriminator column"); Debug.Assert(typeChoices != null, "No type choices for polymorphic column"); m_typedColumnMap = typeChoices; m_typeDiscriminator = typeDiscriminator; } /// /// Get the type discriminator column /// internal SimpleColumnMap TypeDiscriminator { get { return m_typeDiscriminator; } } /// /// Get the type mapping /// internal Dictionary TypeChoices { get { return m_typedColumnMap; } } /// /// Visitor Design Pattern /// /// /// /// [DebuggerNonUserCode] internal override void Accept(ColumnMapVisitor visitor, TArgType arg) { visitor.Visit(this, arg); } /// /// Visitor Design Pattern /// /// /// /// /// [DebuggerNonUserCode] internal override TResultType Accept(ColumnMapVisitorWithResults visitor, TArgType arg) { return visitor.Visit(this, arg); } /// /// Debugging support /// /// public override string ToString() { StringBuilder sb = new StringBuilder(); string separator = String.Empty; sb.AppendFormat(CultureInfo.InvariantCulture, "P{{TypeId={0}, ", this.TypeDiscriminator); foreach (KeyValuePair kv in this.TypeChoices) { sb.AppendFormat(CultureInfo.InvariantCulture, "{0}({1},{2})", separator, kv.Key, kv.Value); separator = ","; } sb.Append("}"); return sb.ToString(); } } /// /// Represents a function import column map. /// internal class MultipleDiscriminatorPolymorphicColumnMap : TypedColumnMap { private readonly SimpleColumnMap[] m_typeDiscriminators; private readonly Dictionary m_typeChoices; private readonly Func m_discriminate; /// /// Internal constructor /// internal MultipleDiscriminatorPolymorphicColumnMap(md.TypeUsage type, string name, ColumnMap[] baseTypeColumns, SimpleColumnMap[] typeDiscriminators, Dictionary typeChoices, Func discriminate) : base(type, name, baseTypeColumns) { Debug.Assert(typeDiscriminators != null, "Must specify type discriminator columns"); Debug.Assert(typeChoices != null, "No type choices for polymorphic column"); Debug.Assert(discriminate != null, "Must specify discriminate"); m_typeDiscriminators = typeDiscriminators; m_typeChoices = typeChoices; m_discriminate = discriminate; } /// /// Get the type discriminator column /// internal SimpleColumnMap[] TypeDiscriminators { get { return m_typeDiscriminators; } } /// /// Get the type mapping /// internal Dictionary TypeChoices { get { return m_typeChoices; } } /// /// Gets discriminator delegate /// internal Func Discriminate { get { return m_discriminate; } } /// /// Visitor Design Pattern /// [DebuggerNonUserCode] internal override void Accept(ColumnMapVisitor visitor, TArgType arg) { visitor.Visit(this, arg); } /// /// Visitor Design Pattern /// [DebuggerNonUserCode] internal override TResultType Accept(ColumnMapVisitorWithResults visitor, TArgType arg) { return visitor.Visit(this, arg); } /// /// Debugging support /// public override string ToString() { StringBuilder sb = new StringBuilder(); string separator = String.Empty; sb.AppendFormat(CultureInfo.InvariantCulture, "P{{TypeId=<{0}>, ", StringUtil.ToCommaSeparatedString(this.TypeDiscriminators)); foreach (var kv in this.TypeChoices) { sb.AppendFormat(CultureInfo.InvariantCulture, "{0}(<{1}>,{2})", separator, kv.Key, kv.Value); separator = ","; } sb.Append("}"); return sb.ToString(); } } /// /// Represents a column map for a specific complextype /// internal class ComplexTypeColumnMap : TypedColumnMap { private SimpleColumnMap m_nullSentinel; /// /// Constructor /// /// column Datatype /// column name /// list of properties internal ComplexTypeColumnMap(md.TypeUsage type, string name, ColumnMap[] properties, SimpleColumnMap nullSentinel) : base(type, name, properties) { m_nullSentinel = nullSentinel; } /// /// Get the type Nullability column /// internal override SimpleColumnMap NullSentinel { get { return m_nullSentinel; } } /// /// Visitor Design Pattern /// /// /// /// [DebuggerNonUserCode] internal override void Accept(ColumnMapVisitor visitor, TArgType arg) { visitor.Visit(this, arg); } /// /// Visitor Design Pattern /// /// /// /// /// [DebuggerNonUserCode] internal override TResultType Accept(ColumnMapVisitorWithResults visitor, TArgType arg) { return visitor.Visit(this, arg); } /// /// Debugging support /// /// public override string ToString() { string str = String.Format(CultureInfo.InvariantCulture, "C{0}", base.ToString()); return str; } } /// /// Represents a column map for a specific entity type /// internal class EntityColumnMap : TypedColumnMap { private EntityIdentity m_entityIdentity; /// /// constructor /// /// column datatype /// column name /// entity identity information /// list of properties internal EntityColumnMap(md.TypeUsage type, string name, ColumnMap[] properties, EntityIdentity entityIdentity) : base(type, name, properties) { Debug.Assert(entityIdentity != null, "Must specify an entity identity"); m_entityIdentity = entityIdentity; } /// /// Get the entity identity information /// internal EntityIdentity EntityIdentity { get { return m_entityIdentity; } } /// /// Visitor Design Pattern /// /// /// /// [DebuggerNonUserCode] internal override void Accept(ColumnMapVisitor visitor, TArgType arg) { visitor.Visit(this, arg); } /// /// Visitor Design Pattern /// /// /// /// /// [DebuggerNonUserCode] internal override TResultType Accept(ColumnMapVisitorWithResults visitor, TArgType arg) { return visitor.Visit(this, arg); } /// /// Debugging support /// /// public override string ToString() { string str = String.Format(CultureInfo.InvariantCulture, "E{0}", base.ToString()); return str; } } /// /// A column map that represents a ref column. /// internal class RefColumnMap: ColumnMap { private EntityIdentity m_entityIdentity; /// /// Constructor for a ref column /// /// column datatype /// column name /// identity information for this entity internal RefColumnMap(md.TypeUsage type, string name, EntityIdentity entityIdentity) : base(type, name) { Debug.Assert(entityIdentity != null, "Must specify entity identity information"); m_entityIdentity = entityIdentity; } /// /// Get the entity identity information for this ref /// internal EntityIdentity EntityIdentity { get { return m_entityIdentity; } } /// /// Visitor Design Pattern /// /// /// /// [DebuggerNonUserCode] internal override void Accept(ColumnMapVisitor visitor, TArgType arg) { visitor.Visit(this, arg); } /// /// Visitor Design Pattern /// /// /// /// /// [DebuggerNonUserCode] internal override TResultType Accept(ColumnMapVisitorWithResults visitor, TArgType arg) { return visitor.Visit(this, arg); } } #endregion #region Collections /// /// Represents a column map for a collection column. /// The "element" represents the element of the collection - usually a Structured /// type, but occasionally a collection/simple type as well. /// The "ForeignKeys" property is optional (but usually necessary) to determine the /// elements of the collection. /// internal abstract class CollectionColumnMap : ColumnMap { private readonly ColumnMap m_element; private readonly SimpleColumnMap[] m_foreignKeys; private readonly SimpleColumnMap[] m_keys; /// /// Constructor /// /// datatype of column /// column name /// column map for collection element /// List of keys /// List of foreign keys internal CollectionColumnMap(md.TypeUsage type, string name, ColumnMap elementMap, SimpleColumnMap[] keys, SimpleColumnMap[] foreignKeys) : base(type, name) { Debug.Assert(elementMap != null, "Must specify column map for element"); m_element = elementMap; m_keys = keys ?? new SimpleColumnMap[0]; m_foreignKeys = foreignKeys ?? new SimpleColumnMap[0]; } /// /// Get the list of columns that may comprise the foreign key /// internal SimpleColumnMap[] ForeignKeys { get { return m_foreignKeys; } } /// /// Get the list of columns that may comprise the key /// internal SimpleColumnMap[] Keys { get { return m_keys; } } /// /// Get the column map describing the collection element /// internal ColumnMap Element { get { return m_element; } } } /// /// Represents a "simple" collection map. /// internal class SimpleCollectionColumnMap : CollectionColumnMap { /// /// Basic constructor /// /// Column datatype /// column name /// column map for the element of the collection /// list of key columns /// list of foreign key columns internal SimpleCollectionColumnMap(md.TypeUsage type, string name, ColumnMap elementMap, SimpleColumnMap[] keys, SimpleColumnMap[] foreignKeys) : base(type, name, elementMap, keys, foreignKeys) { } /// /// Visitor Design Pattern /// /// /// /// [DebuggerNonUserCode] internal override void Accept(ColumnMapVisitor visitor, TArgType arg) { visitor.Visit(this, arg); } /// /// Visitor Design Pattern /// /// /// /// /// [DebuggerNonUserCode] internal override TResultType Accept(ColumnMapVisitorWithResults visitor, TArgType arg) { return visitor.Visit(this, arg); } } /// /// Represents a "discriminated" collection column. /// This represents a scenario when multiple collections are represented /// at the same level of the container row, and there is a need to distinguish /// between these collections /// internal class DiscriminatedCollectionColumnMap : CollectionColumnMap { private SimpleColumnMap m_discriminator; private object m_discriminatorValue; /// /// Internal constructor /// /// Column datatype /// column name /// column map for collection element /// Keys for the collection /// Foreign keys for the collection /// Discriminator column map /// Discriminator value internal DiscriminatedCollectionColumnMap(md.TypeUsage type, string name, ColumnMap elementMap, SimpleColumnMap[] keys, SimpleColumnMap[] foreignKeys, SimpleColumnMap discriminator, object discriminatorValue) : base(type, name, elementMap, keys, foreignKeys) { Debug.Assert(discriminator != null, "Must specify a column map for the collection discriminator"); Debug.Assert(discriminatorValue != null, "Must specify a discriminator value"); m_discriminator = discriminator; m_discriminatorValue = discriminatorValue; } /// /// Get the column that describes the discriminator /// internal SimpleColumnMap Discriminator { get { return m_discriminator; } } /// /// Get the discriminator value /// internal object DiscriminatorValue { get { return m_discriminatorValue; } } /// /// Visitor Design Pattern /// /// /// /// [DebuggerNonUserCode] internal override void Accept(ColumnMapVisitor visitor, TArgType arg) { visitor.Visit(this, arg); } /// /// Visitor Design Pattern /// /// /// /// /// [DebuggerNonUserCode] internal override TResultType Accept(ColumnMapVisitorWithResults visitor, TArgType arg) { return visitor.Visit(this, arg); } /// /// Debugging support /// /// public override string ToString() { string str = String.Format(CultureInfo.InvariantCulture, "M{{{0}}}", this.Element.ToString()); return str; } } #endregion #region EntityIdentity /// /// Abstract base class representing entity identity. Used by both /// EntityColumnMap and RefColumnMap. /// An EntityIdentity captures two pieces of information - the list of keys /// that uniquely identify an entity within an entityset, and the the entityset /// itself. /// internal abstract class EntityIdentity { private readonly SimpleColumnMap[] m_keys; // list of keys /// /// Simple constructor - gets a list of key columns /// /// internal EntityIdentity(SimpleColumnMap[] keyColumns) { Debug.Assert(keyColumns != null, "Must specify column maps for key columns"); m_keys = keyColumns; } /// /// Get the key columns /// internal SimpleColumnMap[] Keys { get { return m_keys; } } } /// /// This class is a "simple" representation of the entity identity, where the /// entityset containing the entity is known a priori. This may be because /// there is exactly one entityset for the entity; or because it is inferrable /// from the query that only one entityset is relevant here /// internal class SimpleEntityIdentity : EntityIdentity { private md.EntitySet m_entitySet; // the entity set /// /// Basic constructor. /// Note: the entitySet may be null - in which case, we are referring to /// a transient entity /// /// The entityset /// key columns of the entity internal SimpleEntityIdentity(md.EntitySet entitySet, SimpleColumnMap[] keyColumns) : base(keyColumns) { // the entityset may be null m_entitySet = entitySet; } /// /// The entityset containing the entity /// internal md.EntitySet EntitySet { get { return m_entitySet; } } /// /// Debugging support /// /// public override string ToString() { StringBuilder sb = new StringBuilder(); string separator = String.Empty; sb.AppendFormat(CultureInfo.InvariantCulture, "[(ES={0}) (Keys={", this.EntitySet.Name); foreach (SimpleColumnMap c in this.Keys) { sb.AppendFormat(CultureInfo.InvariantCulture, "{0}{1}", separator, c); separator = ","; } sb.AppendFormat(CultureInfo.InvariantCulture, "})]"); return sb.ToString(); } } /// /// This class also represents entity identity. However, this class addresses /// those scenarios where the entityset for the entity is not uniquely known /// a priori. Instead, the query is annotated with information, and based on /// the resulting information, the appropriate entityset is identified. /// Specifically, the specific entityset is represented as a SimpleColumnMap /// in the query. The value of that column is used to look up a dictionary, /// and then identify the appropriate entity set. /// It is entirely possible that no entityset may be located for the entity /// instance - this represents a transient entity instance /// internal class DiscriminatedEntityIdentity : EntityIdentity { private SimpleColumnMap m_entitySetColumn; // (optional) column map representing the entity set private md.EntitySet[] m_entitySetMap; // optional dictionary that maps values to entitysets /// /// Simple constructor /// /// column map representing the entityset /// Map from value -> the appropriate entityset /// list of key columns internal DiscriminatedEntityIdentity(SimpleColumnMap entitySetColumn, md.EntitySet[] entitySetMap, SimpleColumnMap[] keyColumns) : base(keyColumns) { Debug.Assert(entitySetColumn != null, "Must specify a column map to identify the entity set"); Debug.Assert(entitySetMap != null, "Must specify a dictionary to look up entitysets"); m_entitySetColumn = entitySetColumn; m_entitySetMap = entitySetMap; } /// /// Get the column map representing the entityset /// internal SimpleColumnMap EntitySetColumnMap { get { return m_entitySetColumn; } } /// /// Return the entityset map /// internal md.EntitySet[] EntitySetMap { get { return m_entitySetMap; } } /// /// Debugging support /// /// public override string ToString() { StringBuilder sb = new StringBuilder(); string separator = String.Empty; sb.AppendFormat(CultureInfo.InvariantCulture, "[(Keys={"); foreach (SimpleColumnMap c in this.Keys) { sb.AppendFormat(CultureInfo.InvariantCulture, "{0}{1}", separator, c); separator = ","; } sb.AppendFormat(CultureInfo.InvariantCulture, "})]"); return sb.ToString(); } } #endregion #region internal classes /// /// A VarRefColumnMap is our intermediate representation of a ColumnMap. /// Eventually, this gets translated into a regular ColumnMap - during the CodeGen phase /// internal class VarRefColumnMap : SimpleColumnMap { #region Public Methods /// /// Get the Var that produces this column's value /// internal InternalTrees.Var Var { get { return m_var; } } #endregion #region Constructors /// /// Simple constructor /// /// datatype of this Var /// the name of the column /// the var producing the value for this column internal VarRefColumnMap(md.TypeUsage type, string name, InternalTrees.Var v) : base(type, name) { m_var = v; } internal VarRefColumnMap(InternalTrees.Var v) : this(v.Type, null, v) { } /// /// Visitor Design Pattern /// /// /// /// [DebuggerNonUserCode] internal override void Accept(ColumnMapVisitor visitor, TArgType arg) { visitor.Visit(this, arg); } /// /// Visitor Design Pattern /// /// /// /// /// [DebuggerNonUserCode] internal override TResultType Accept(ColumnMapVisitorWithResults visitor, TArgType arg) { return visitor.Visit(this, arg); } /// /// Debugging support /// /// public override string ToString() { return this.IsNamed ? this.Name : String.Format(CultureInfo.InvariantCulture, "{0}", m_var.Id); } #endregion #region private state private InternalTrees.Var m_var; #endregion } #endregion }