//---------------------------------------------------------------------
//
// 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