File: fx\src\data\Microsoft\SqlServer\Server\SmiMetaData.cs
Project: ndp\System.Data.csproj (System.Data)
//------------------------------------------------------------------------------
// <copyright file="SmiMetaData.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
// <owner current="true" primary="true">Microsoft</owner>
// <owner current="true" primary="false">Microsoft</owner>
//------------------------------------------------------------------------------
 
namespace Microsoft.SqlServer.Server {
 
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Data;
    using System.Data.Sql;
    using System.Data.SqlTypes;
    using System.Globalization;
 
 
    // DESIGN NOTES
    //
    //  The following classes are a tight inheritance heirarchy, and are not designed for
    //  being inherited outside of this file.  Instances are guaranteed to be immutable, and
    //  outside classes rely on this fact.
    //
    //  The various levels may not all be used outside of this file, but for clarity of purpose
    //  they are all usefull distinctions to make.
    //
    //  In general, moving lower in the type heirarchy exposes less portable values.  Thus,
    //  the root metadata can be readily shared across different (MSSQL) servers and clients,
    //  while QueryMetaData has attributes tied to a specific query, running against specific
    //  data storage on a specific server.
    //
    //  The SmiMetaData heirarchy does not do data validation on retail builds!  It will assert
    //  that the values passed to it have been validated externally, however.
    //
 
 
    // SmiMetaData
    //
    //  Root of the heirarchy.
    //  Represents the minimal amount of metadata required to represent any Sql Server datum
    //  without any references to any particular server or schema (thus, no server-specific multi-part names).
    //  It could be used to communicate solely between two disconnected clients, for instance.
    //
    //  NOTE: It currently does not contain sufficient information to describe typed XML, since we
    //      don't have a good server-independent mechanism for such.
    //
    //  This class is also used as implementation for the public SqlMetaData class.
    internal class SmiMetaData {
 
        private SqlDbType           _databaseType;          // Main enum that determines what is valid for other attributes.
        private long                _maxLength;             // Varies for variable-length types, others are fixed value per type
        private byte                _precision;             // Varies for SqlDbType.Decimal, others are fixed value per type
        private byte                _scale;                 // Varies for SqlDbType.Decimal, others are fixed value per type
        private long                _localeId;              // Valid only for character types, others are 0
        private SqlCompareOptions   _compareOptions;        // Valid only for character types, others are SqlCompareOptions.Default
        private Type                _clrType;               // Varies for SqlDbType.Udt, others are fixed value per type.
        private string _udtAssemblyQualifiedName;           // Valid only for UDT types when _clrType is not available
        private bool                _isMultiValued;         // Multiple instances per value? (I.e. tables, arrays)
        private IList<SmiExtendedMetaData>  _fieldMetaData;         // Metadata of fields for structured types
        private SmiMetaDataPropertyCollection _extendedProperties;  // Extended properties, Key columns, sort order, etc.
 
        // DevNote: For now, since the list of extended property types is small, we can handle them in a simple list.
        //  In the future, we may need to create a more performant storage & lookup mechanism, such as a hash table
        //  of lists indexed by type of property or an array of lists with a well-known index for each type.
 
 
 
        // Limits for attributes (SmiMetaData will assert that these limits as applicable in constructor)
        internal const long UnlimitedMaxLengthIndicator = -1;  // unlimited (except by implementation) max-length.
        internal const long MaxUnicodeCharacters = 4000;        // Maximum for limited type
        internal const long MaxANSICharacters = 8000;           // Maximum for limited type
        internal const long MaxBinaryLength = 8000;             // Maximum for limited type
        internal const int  MinPrecision = 1;       // SqlDecimal defines max precision
        internal const int  MinScale = 0;            // SqlDecimal defines max scale
        internal const int  MaxTimeScale = 7;        // Max scale for time, datetime2, and datetimeoffset
        internal static readonly DateTime MaxSmallDateTime = new DateTime(2079, 06, 06, 23, 59, 29, 998);
        internal static readonly DateTime MinSmallDateTime = new DateTime(1899, 12, 31, 23, 59, 29, 999);
        internal static readonly SqlMoney MaxSmallMoney = new SqlMoney( ( (Decimal)Int32.MaxValue ) / 10000 );
        internal static readonly SqlMoney MinSmallMoney = new SqlMoney( ( (Decimal)Int32.MinValue ) / 10000 );
        internal const SqlCompareOptions DefaultStringCompareOptions = SqlCompareOptions.IgnoreCase 
                                        | SqlCompareOptions.IgnoreKanaType | SqlCompareOptions.IgnoreWidth;
        
        internal const long MaxNameLength = 128;        // maximum length in the server is 128.
        private static readonly IList<SmiExtendedMetaData> __emptyFieldList = new List<SmiExtendedMetaData>().AsReadOnly();
 
 
        // Precision to max length lookup table
        private static byte[] __maxLenFromPrecision = new byte[] {5,5,5,5,5,5,5,5,5,9,9,9,9,9,
            9,9,9,9,9,13,13,13,13,13,13,13,13,13,17,17,17,17,17,17,17,17,17,17};
 
        // Scale offset to max length lookup table
        private static byte[] __maxVarTimeLenOffsetFromScale = new byte[] { 2, 2, 2, 1, 1, 0, 0, 0 };
 
        // Defaults
                                                                         //    SmiMetaData(SqlDbType,                  MaxLen,                     Prec, Scale,  CompareOptions)
        internal static readonly SmiMetaData DefaultBigInt               = new SmiMetaData(SqlDbType.BigInt,           8,                          19,   0,      SqlCompareOptions.None);     // SqlDbType.BigInt
        internal static readonly SmiMetaData DefaultBinary               = new SmiMetaData(SqlDbType.Binary,           1,                          0,    0,      SqlCompareOptions.None);     // SqlDbType.Binary
        internal static readonly SmiMetaData DefaultBit                  = new SmiMetaData(SqlDbType.Bit,              1,                          1,    0,      SqlCompareOptions.None);     // SqlDbType.Bit
        internal static readonly SmiMetaData DefaultChar_NoCollation     = new SmiMetaData(SqlDbType.Char,             1,                          0,    0,      DefaultStringCompareOptions);// SqlDbType.Char
        internal static readonly SmiMetaData DefaultDateTime             = new SmiMetaData(SqlDbType.DateTime,         8,                          23,   3,      SqlCompareOptions.None);     // SqlDbType.DateTime
        internal static readonly SmiMetaData DefaultDecimal              = new SmiMetaData(SqlDbType.Decimal,          9,                          18,   0,      SqlCompareOptions.None);     // SqlDbType.Decimal
        internal static readonly SmiMetaData DefaultFloat                = new SmiMetaData(SqlDbType.Float,            8,                          53,   0,      SqlCompareOptions.None);     // SqlDbType.Float
        internal static readonly SmiMetaData DefaultImage                = new SmiMetaData(SqlDbType.Image,            UnlimitedMaxLengthIndicator,0,    0,      SqlCompareOptions.None);     // SqlDbType.Image
        internal static readonly SmiMetaData DefaultInt                  = new SmiMetaData(SqlDbType.Int,              4,                          10,   0,      SqlCompareOptions.None);     // SqlDbType.Int
        internal static readonly SmiMetaData DefaultMoney                = new SmiMetaData(SqlDbType.Money,            8,                          19,   4,      SqlCompareOptions.None);     // SqlDbType.Money
        internal static readonly SmiMetaData DefaultNChar_NoCollation    = new SmiMetaData(SqlDbType.NChar,            1,                          0,    0,      DefaultStringCompareOptions);// SqlDbType.NChar
        internal static readonly SmiMetaData DefaultNText_NoCollation    = new SmiMetaData(SqlDbType.NText,            UnlimitedMaxLengthIndicator,0,    0,      DefaultStringCompareOptions);// SqlDbType.NText
        internal static readonly SmiMetaData DefaultNVarChar_NoCollation = new SmiMetaData(SqlDbType.NVarChar,         MaxUnicodeCharacters,       0,    0,      DefaultStringCompareOptions);// SqlDbType.NVarChar
        internal static readonly SmiMetaData DefaultReal                 = new SmiMetaData(SqlDbType.Real,             4,                          24,   0,      SqlCompareOptions.None);     // SqlDbType.Real
        internal static readonly SmiMetaData DefaultUniqueIdentifier     = new SmiMetaData(SqlDbType.UniqueIdentifier, 16,                         0,    0,      SqlCompareOptions.None);     // SqlDbType.UniqueIdentifier
        internal static readonly SmiMetaData DefaultSmallDateTime        = new SmiMetaData(SqlDbType.SmallDateTime,    4,                          16,   0,      SqlCompareOptions.None);     // SqlDbType.SmallDateTime
        internal static readonly SmiMetaData DefaultSmallInt             = new SmiMetaData(SqlDbType.SmallInt,         2,                          5,    0,      SqlCompareOptions.None);     // SqlDbType.SmallInt
        internal static readonly SmiMetaData DefaultSmallMoney           = new SmiMetaData(SqlDbType.SmallMoney,       4,                          10,   4,      SqlCompareOptions.None);     // SqlDbType.SmallMoney
        internal static readonly SmiMetaData DefaultText_NoCollation     = new SmiMetaData(SqlDbType.Text,             UnlimitedMaxLengthIndicator,0,    0,      DefaultStringCompareOptions);// SqlDbType.Text
        internal static readonly SmiMetaData DefaultTimestamp            = new SmiMetaData(SqlDbType.Timestamp,        8,                          0,    0,      SqlCompareOptions.None);     // SqlDbType.Timestamp
        internal static readonly SmiMetaData DefaultTinyInt              = new SmiMetaData(SqlDbType.TinyInt,          1,                          3,    0,      SqlCompareOptions.None);     // SqlDbType.TinyInt
        internal static readonly SmiMetaData DefaultVarBinary            = new SmiMetaData(SqlDbType.VarBinary,        MaxBinaryLength,            0,    0,      SqlCompareOptions.None);     // SqlDbType.VarBinary
        internal static readonly SmiMetaData DefaultVarChar_NoCollation  = new SmiMetaData(SqlDbType.VarChar,          MaxANSICharacters,          0,    0,      DefaultStringCompareOptions);// SqlDbType.VarChar
        internal static readonly SmiMetaData DefaultVariant              = new SmiMetaData(SqlDbType.Variant,          8016,                       0,    0,      SqlCompareOptions.None);     // SqlDbType.Variant
        internal static readonly SmiMetaData DefaultXml                  = new SmiMetaData(SqlDbType.Xml,              UnlimitedMaxLengthIndicator,0,    0,      DefaultStringCompareOptions);// SqlDbType.Xml
        internal static readonly SmiMetaData DefaultUdt_NoType           = new SmiMetaData(SqlDbType.Udt,              0,                          0,    0,      SqlCompareOptions.None);     // SqlDbType.Udt
        internal static readonly SmiMetaData DefaultStructured           = new SmiMetaData(SqlDbType.Structured,       0,                          0,    0,      SqlCompareOptions.None);     // SqlDbType.Structured
        internal static readonly SmiMetaData DefaultDate                 = new SmiMetaData(SqlDbType.Date,             3,                          10,   0,      SqlCompareOptions.None);     // SqlDbType.Date
        internal static readonly SmiMetaData DefaultTime                 = new SmiMetaData(SqlDbType.Time,             5,                          0,    7,      SqlCompareOptions.None);     // SqlDbType.Time
        internal static readonly SmiMetaData DefaultDateTime2            = new SmiMetaData(SqlDbType.DateTime2,        8,                          0,    7,      SqlCompareOptions.None);     // SqlDbType.DateTime2
        internal static readonly SmiMetaData DefaultDateTimeOffset       = new SmiMetaData(SqlDbType.DateTimeOffset,   10,                         0,    7,      SqlCompareOptions.None);     // SqlDbType.DateTimeOffset
        // No default for generic UDT
 
        // character defaults hook thread-local culture to get collation
        internal static SmiMetaData DefaultChar {
            get {
                return new SmiMetaData(
                        DefaultChar_NoCollation.SqlDbType,
                        DefaultChar_NoCollation.MaxLength,
                        DefaultChar_NoCollation.Precision,
                        DefaultChar_NoCollation.Scale,
                        System.Globalization.CultureInfo.CurrentCulture.LCID,
                        SqlCompareOptions.IgnoreCase | SqlCompareOptions.IgnoreKanaType | SqlCompareOptions.IgnoreWidth,
                        null
                        );
            }
        }
 
        internal static SmiMetaData DefaultNChar {
            get {
                return new SmiMetaData(
                        DefaultNChar_NoCollation.SqlDbType,
                        DefaultNChar_NoCollation.MaxLength,
                        DefaultNChar_NoCollation.Precision,
                        DefaultNChar_NoCollation.Scale,
                        System.Globalization.CultureInfo.CurrentCulture.LCID,
                        SqlCompareOptions.IgnoreCase | SqlCompareOptions.IgnoreKanaType | SqlCompareOptions.IgnoreWidth,
                        null
                        );
            }
        }
 
        internal static SmiMetaData DefaultNText {
            get {
                return new SmiMetaData(
                        DefaultNText_NoCollation.SqlDbType,
                        DefaultNText_NoCollation.MaxLength,
                        DefaultNText_NoCollation.Precision,
                        DefaultNText_NoCollation.Scale,
                        System.Globalization.CultureInfo.CurrentCulture.LCID,
                        SqlCompareOptions.IgnoreCase | SqlCompareOptions.IgnoreKanaType | SqlCompareOptions.IgnoreWidth,
                        null
                        );
            }
        }
 
        internal static SmiMetaData DefaultNVarChar {
            get {
                return new SmiMetaData(
                        DefaultNVarChar_NoCollation.SqlDbType,
                        DefaultNVarChar_NoCollation.MaxLength,
                        DefaultNVarChar_NoCollation.Precision,
                        DefaultNVarChar_NoCollation.Scale,
                        System.Globalization.CultureInfo.CurrentCulture.LCID,
                        SqlCompareOptions.IgnoreCase | SqlCompareOptions.IgnoreKanaType | SqlCompareOptions.IgnoreWidth,
                        null
                        );
            }
        }
 
        internal static SmiMetaData DefaultText {
            get {
                return new SmiMetaData(
                        DefaultText_NoCollation.SqlDbType,
                        DefaultText_NoCollation.MaxLength,
                        DefaultText_NoCollation.Precision,
                        DefaultText_NoCollation.Scale,
                        System.Globalization.CultureInfo.CurrentCulture.LCID,
                        SqlCompareOptions.IgnoreCase | SqlCompareOptions.IgnoreKanaType | SqlCompareOptions.IgnoreWidth,
                        null
                        );
            }
        }
 
        internal static SmiMetaData DefaultVarChar {
            get {
                return new SmiMetaData(
                        DefaultVarChar_NoCollation.SqlDbType,
                        DefaultVarChar_NoCollation.MaxLength,
                        DefaultVarChar_NoCollation.Precision,
                        DefaultVarChar_NoCollation.Scale,
                        System.Globalization.CultureInfo.CurrentCulture.LCID,
                        SqlCompareOptions.IgnoreCase | SqlCompareOptions.IgnoreKanaType | SqlCompareOptions.IgnoreWidth,
                        null
                        );
            }
        }
 
        // The one and only constructor for use by outside code.
        //
        //  Parameters that matter for given values of dbType (other parameters are ignored in favor of internal defaults).
        //  Thus, if dbType parameter value is SqlDbType.Decimal, the values of precision and scale passed in are used, but
        //  maxLength, localeId, compareOptions, etc are set to defaults for the Decimal type:
        //      SqlDbType.BigInt:               dbType
        //      SqlDbType.Binary:               dbType, maxLength
        //      SqlDbType.Bit:                  dbType
        //      SqlDbType.Char:                 dbType, maxLength, localeId, compareOptions
        //      SqlDbType.DateTime:             dbType
        //      SqlDbType.Decimal:              dbType, precision, scale
        //      SqlDbType.Float:                dbType
        //      SqlDbType.Image:                dbType
        //      SqlDbType.Int:                  dbType
        //      SqlDbType.Money:                dbType
        //      SqlDbType.NChar:                dbType, maxLength, localeId, compareOptions
        //      SqlDbType.NText:                dbType, localeId, compareOptions
        //      SqlDbType.NVarChar:             dbType, maxLength, localeId, compareOptions
        //      SqlDbType.Real:                 dbType
        //      SqlDbType.UniqueIdentifier:     dbType
        //      SqlDbType.SmallDateTime:        dbType
        //      SqlDbType.SmallInt:             dbType
        //      SqlDbType.SmallMoney:           dbType
        //      SqlDbType.Text:                 dbType, localeId, compareOptions
        //      SqlDbType.Timestamp:            dbType
        //      SqlDbType.TinyInt:              dbType
        //      SqlDbType.VarBinary:            dbType, maxLength
        //      SqlDbType.VarChar:              dbType, maxLength, localeId, compareOptions
        //      SqlDbType.Variant:              dbType
        //      PlaceHolder for value 24
        //      SqlDbType.Xml:                  dbType
        //      Placeholder for value 26
        //      Placeholder for value 27
        //      Placeholder for value 28
        //      SqlDbType.Udt:                  dbType, userDefinedType
        //
 
        [ObsoleteAttribute( "Not supported as of SMI v2.  Will be removed when v1 support dropped. Use ctor without columns param." )]
        internal SmiMetaData(
                                SqlDbType dbType, 
                                long maxLength,
                                byte precision, 
                                byte scale, 
                                long localeId, 
                                SqlCompareOptions compareOptions, 
                                Type userDefinedType, 
                                SmiMetaData[] columns) :
                          // Implement as calling the new ctor
                          this(
                                dbType,
                                maxLength,
                                precision,
                                scale, 
                                localeId, 
                                compareOptions, 
                                userDefinedType ) {
            Debug.Assert( null == columns, "Row types not supported" );
        }
 
        // SMI V100 (aka V3) constructor.  Superceded in V200.
        internal SmiMetaData(
                                SqlDbType dbType, 
                                long maxLength,
                                byte precision, 
                                byte scale, 
                                long localeId, 
                                SqlCompareOptions compareOptions, 
                                Type userDefinedType) :
                        this(   dbType,
                                maxLength,
                                precision,
                                scale,
                                localeId,
                                compareOptions,
                                userDefinedType,
                                false,
                                null, 
                                null ) {
        }
 
        // SMI V200 ctor.
        internal SmiMetaData(
                                SqlDbType dbType,
                                long maxLength,
                                byte precision,
                                byte scale,
                                long localeId,
                                SqlCompareOptions compareOptions,
                                Type userDefinedType,
                                bool isMultiValued,
                                IList<SmiExtendedMetaData> fieldTypes,
                                SmiMetaDataPropertyCollection extendedProperties)
            :
                        this(dbType,
                                maxLength,
                                precision,
                                scale,
                                localeId,
                                compareOptions,
                                userDefinedType,
                                null,
                                isMultiValued,
                                fieldTypes,
                                extendedProperties) {
        }
 
        // SMI V220 ctor.
        internal SmiMetaData(
                                SqlDbType dbType, 
                                long maxLength,
                                byte precision, 
                                byte scale, 
                                long localeId, 
                                SqlCompareOptions compareOptions, 
                                Type userDefinedType,
                                string udtAssemblyQualifiedName,
                                bool isMultiValued,
                                IList<SmiExtendedMetaData> fieldTypes,
                                SmiMetaDataPropertyCollection extendedProperties) {
 
            
            Debug.Assert( IsSupportedDbType(dbType), "Invalid SqlDbType: " + dbType );
 
            SetDefaultsForType( dbType );
            
            // 
 
            
            switch ( dbType ) {
                case SqlDbType.BigInt:
                case SqlDbType.Bit:
                case SqlDbType.DateTime:
                case SqlDbType.Float:
                case SqlDbType.Image:
                case SqlDbType.Int:
                case SqlDbType.Money:
                case SqlDbType.Real:
                case SqlDbType.SmallDateTime:
                case SqlDbType.SmallInt:
                case SqlDbType.SmallMoney:
                case SqlDbType.Timestamp:
                case SqlDbType.TinyInt:
                case SqlDbType.UniqueIdentifier:
                case SqlDbType.Variant:
                case SqlDbType.Xml:
                case SqlDbType.Date:
                    break;
                case SqlDbType.Binary:
                case SqlDbType.VarBinary:
                    _maxLength = maxLength;
                    break;
                case SqlDbType.Char:
                case SqlDbType.NChar:
                case SqlDbType.NVarChar:
                case SqlDbType.VarChar:
                    // locale and compare options are not validated until they get to the server
                    _maxLength = maxLength;
                    _localeId = localeId;
                    _compareOptions = compareOptions;
                    break;
                case SqlDbType.NText:
                case SqlDbType.Text:
                    _localeId = localeId;
                    _compareOptions = compareOptions;
                    break;
                case SqlDbType.Decimal:
                    Debug.Assert( MinPrecision <= precision && SqlDecimal.MaxPrecision >= precision, "Invalid precision: " + precision );
                    Debug.Assert( MinScale <= scale && SqlDecimal.MaxScale >= scale, "Invalid scale: " + scale );
                    Debug.Assert( scale <= precision, "Precision: " + precision + " greater than scale: " + scale );
                    _precision = precision;
                    _scale = scale;
                    _maxLength = __maxLenFromPrecision[precision - 1];
                    break;
                case SqlDbType.Udt:
                    // Assert modified for VSFTDEVDIV479492 - for SqlParameter both userDefinedType and udtAssemblyQualifiedName
                    // can be NULL, we are checking only maxLength if it will be used (i.e. userDefinedType is NULL)
                    Debug.Assert((null != userDefinedType) || (0 <= maxLength || UnlimitedMaxLengthIndicator == maxLength),
                            String.Format((IFormatProvider)null, "SmiMetaData.ctor: Udt name={0}, maxLength={1}", udtAssemblyQualifiedName, maxLength));
                    // Type not validated until matched to a server.  Could be null if extended metadata supplies three-part name!
                    _clrType = userDefinedType;
                    if (null != userDefinedType) {
                        _maxLength = SerializationHelperSql9.GetUdtMaxLength(userDefinedType);
                    }
                    else {
                        _maxLength = maxLength;
                    }
                    _udtAssemblyQualifiedName = udtAssemblyQualifiedName;
                    break;
                case SqlDbType.Structured:
                    if (null != fieldTypes) {
                        _fieldMetaData = (new List<SmiExtendedMetaData>(fieldTypes)).AsReadOnly();
                    }
                    _isMultiValued = isMultiValued;
                    _maxLength = _fieldMetaData.Count;
                    break;
                case SqlDbType.Time:
                    Debug.Assert(MinScale <= scale && scale <= MaxTimeScale, "Invalid time scale: " + scale);
                    _scale = scale;
                    _maxLength = 5 - __maxVarTimeLenOffsetFromScale[scale];
                    break;
                case SqlDbType.DateTime2:
                    Debug.Assert(MinScale <= scale && scale <= MaxTimeScale, "Invalid time scale: " + scale);
                    _scale = scale;
                    _maxLength = 8 - __maxVarTimeLenOffsetFromScale[scale];
                    break;
                case SqlDbType.DateTimeOffset:
                    Debug.Assert(MinScale <= scale && scale <= MaxTimeScale, "Invalid time scale: " + scale);
                    _scale = scale;
                    _maxLength = 10 - __maxVarTimeLenOffsetFromScale[scale];
                    break;
                default:
                    Debug.Assert( false, "How in the world did we get here? :" + dbType );
                    break;
            }
 
            if (null != extendedProperties) {
                extendedProperties.SetReadOnly();
                _extendedProperties = extendedProperties;
            }
 
            // properties and fields must meet the following conditions at this point:
            //  1) not null
            //  2) read only
            //  3) same number of columns in each list (0 count acceptable for properties that are "unused")
            Debug.Assert(null != _extendedProperties && _extendedProperties.IsReadOnly, "SmiMetaData.ctor: _extendedProperties is " + (null!=_extendedProperties?"writeable":"null"));
            Debug.Assert(null != _fieldMetaData && _fieldMetaData.IsReadOnly, "SmiMetaData.ctor: _fieldMetaData is " + (null!=_fieldMetaData?"writeable":"null"));
#if DEBUG
            ((SmiDefaultFieldsProperty)_extendedProperties[SmiPropertySelector.DefaultFields]).CheckCount(_fieldMetaData.Count);
            ((SmiOrderProperty)_extendedProperties[SmiPropertySelector.SortOrder]).CheckCount(_fieldMetaData.Count);
            ((SmiUniqueKeyProperty)_extendedProperties[SmiPropertySelector.UniqueKey]).CheckCount(_fieldMetaData.Count);
#endif
        }
 
 
        internal bool IsValidMaxLengthForCtorGivenType( SqlDbType dbType, long maxLength ) {
            bool result = true;
            switch( dbType ) {
                case SqlDbType.BigInt:
                case SqlDbType.Bit:
                case SqlDbType.DateTime:
                case SqlDbType.Float:
                case SqlDbType.Image:
                case SqlDbType.Int:
                case SqlDbType.Money:
                case SqlDbType.Real:
                case SqlDbType.SmallDateTime:
                case SqlDbType.SmallInt:
                case SqlDbType.SmallMoney:
                case SqlDbType.Timestamp:
                case SqlDbType.TinyInt:
                case SqlDbType.UniqueIdentifier:
                case SqlDbType.Variant:
                case SqlDbType.Xml:
                case SqlDbType.NText:
                case SqlDbType.Text:
                case SqlDbType.Decimal:
                case SqlDbType.Udt:
                case SqlDbType.Structured:  // 
                case SqlDbType.Date:
                case SqlDbType.Time:
                case SqlDbType.DateTime2:
                case SqlDbType.DateTimeOffset:
                    break;
                case SqlDbType.Binary:
                    result = 0 < maxLength && MaxBinaryLength >= maxLength;
                    break;
                case SqlDbType.VarBinary:
                    result = UnlimitedMaxLengthIndicator == maxLength || ( 0 < maxLength && MaxBinaryLength >= maxLength );
                    break;
                case SqlDbType.Char:
                    result = 0 < maxLength && MaxANSICharacters >= maxLength;
                    break;
                case SqlDbType.NChar:
                    result = 0 < maxLength && MaxUnicodeCharacters >= maxLength;
                    break;
                case SqlDbType.NVarChar:
                    result = UnlimitedMaxLengthIndicator == maxLength || ( 0 < maxLength && MaxUnicodeCharacters >= maxLength );
                    break;
                case SqlDbType.VarChar:
                    result = UnlimitedMaxLengthIndicator == maxLength || ( 0 < maxLength && MaxANSICharacters >= maxLength );
                    break;
                default:
                    Debug.Assert( false, "How in the world did we get here? :" + dbType );
                    break;
            }
 
            return result;
        }
 
        // Sql-style compare options for character types.
        internal SqlCompareOptions CompareOptions {
            get {   
                return _compareOptions;
            }
        }
 
        // LCID for type.  0 for non-character types.
        internal long LocaleId {
            get{
                return _localeId;
            }
        }
 
        // Units of length depend on type.
        //  NVarChar, NChar, NText: # of unicode characters
        //  Everything else: # of bytes
        internal long MaxLength {
            get { 
                return _maxLength;
            }
        }
 
        internal byte Precision {
            get {
                return _precision;
            }
        }
 
        internal byte Scale {
            get {
                return _scale;
            }
        }
 
        internal SqlDbType SqlDbType {
            get {
                return _databaseType;
            }
        }
 
        // Clr Type instance for user-defined types
        internal Type Type {
            get {
                // Fault-in UDT clr types on access if have assembly-qualified name
                if (null == _clrType && SqlDbType.Udt == _databaseType && _udtAssemblyQualifiedName != null) {
                    _clrType = Type.GetType(_udtAssemblyQualifiedName, true);
                }
                return _clrType;
            }
        }
 
        // Clr Type instance for user-defined types in cases where we don't want to throw if the assembly isn't available
        internal Type TypeWithoutThrowing {
            get {
                // Fault-in UDT clr types on access if have assembly-qualified name
                if (null == _clrType && SqlDbType.Udt == _databaseType && _udtAssemblyQualifiedName != null) {
                    _clrType = Type.GetType(_udtAssemblyQualifiedName, false);
                }
                return _clrType;
            }
        }
 
        internal string TypeName {
            get { 
                string result = null;
                if (SqlDbType.Udt == _databaseType) {
                    Debug.Assert(String.Empty == __typeNameByDatabaseType[(int)_databaseType], "unexpected udt?");
                    result = Type.FullName;
                }
                else {
                    result = __typeNameByDatabaseType[(int)_databaseType];
                    Debug.Assert(null != result, "unknown type name?");
                }
                return result;
            }
        }
 
        internal string AssemblyQualifiedName {
            get {
                string result = null;
                if (SqlDbType.Udt == _databaseType) {
                    // Fault-in assembly-qualified name if type is available
                    if (_udtAssemblyQualifiedName == null && _clrType != null) {
                        _udtAssemblyQualifiedName = _clrType.AssemblyQualifiedName;
                    }
                    result = _udtAssemblyQualifiedName;
                }
                return result;
            }
        }
 
        internal bool IsMultiValued {
            get {
                return _isMultiValued;
            }
        }
 
        // Returns read-only list of field metadata
        internal IList<SmiExtendedMetaData> FieldMetaData {
            get {
                return _fieldMetaData;
            }
        }
 
        // Returns read-only list of extended properties
        internal SmiMetaDataPropertyCollection ExtendedProperties {
            get {
                return _extendedProperties;
            }
        }
 
        internal static bool IsSupportedDbType(SqlDbType dbType) {
            // Hole in SqlDbTypes between Xml and Udt for non-WinFS scenarios.
            return (SqlDbType.BigInt <= dbType && SqlDbType.Xml >= dbType) || 
                    (SqlDbType.Udt <= dbType && SqlDbType.DateTimeOffset >= dbType);
        }
 
        // Only correct access point for defaults per SqlDbType.
        internal static SmiMetaData GetDefaultForType( SqlDbType dbType ) {
                Debug.Assert( IsSupportedDbType(dbType), "Unsupported SqlDbtype: " + dbType);
 
                return __defaultValues[(int)dbType];
        }
 
        // Private constructor used only to initialize default instance array elements.
        // DO NOT EXPOSE OUTSIDE THIS CLASS!
        private SmiMetaData (
                                SqlDbType         sqlDbType,
                                long              maxLength,
                                byte              precision,
                                byte              scale,
                                SqlCompareOptions compareOptions) {
            _databaseType      = sqlDbType;
            _maxLength         = maxLength;
            _precision         = precision;
            _scale             = scale;
            _compareOptions    = compareOptions;
 
            // defaults are the same for all types for the following attributes.
            _localeId           = 0; 
            _clrType            = null;
            _isMultiValued      = false;
            _fieldMetaData      = __emptyFieldList;
            _extendedProperties = SmiMetaDataPropertyCollection.EmptyInstance;
        }
 
        // static array of default-valued metadata ordered by corresponding SqlDbType.
        // NOTE: INDEXED BY SqlDbType ENUM!  MUST UPDATE THIS ARRAY WHEN UPDATING SqlDbType!
        //   ONLY ACCESS THIS GLOBAL FROM GetDefaultForType!
        private static SmiMetaData[] __defaultValues =
            {
                DefaultBigInt,                 // SqlDbType.BigInt
                DefaultBinary,                 // SqlDbType.Binary
                DefaultBit,                    // SqlDbType.Bit
                DefaultChar_NoCollation,       // SqlDbType.Char
                DefaultDateTime,               // SqlDbType.DateTime
                DefaultDecimal,                // SqlDbType.Decimal
                DefaultFloat,                  // SqlDbType.Float
                DefaultImage,                  // SqlDbType.Image
                DefaultInt,                    // SqlDbType.Int
                DefaultMoney,                  // SqlDbType.Money
                DefaultNChar_NoCollation,      // SqlDbType.NChar
                DefaultNText_NoCollation,      // SqlDbType.NText
                DefaultNVarChar_NoCollation,   // SqlDbType.NVarChar
                DefaultReal,                   // SqlDbType.Real
                DefaultUniqueIdentifier,       // SqlDbType.UniqueIdentifier
                DefaultSmallDateTime,          // SqlDbType.SmallDateTime
                DefaultSmallInt,               // SqlDbType.SmallInt
                DefaultSmallMoney,             // SqlDbType.SmallMoney
                DefaultText_NoCollation,       // SqlDbType.Text
                DefaultTimestamp,              // SqlDbType.Timestamp
                DefaultTinyInt,                // SqlDbType.TinyInt
                DefaultVarBinary,              // SqlDbType.VarBinary
                DefaultVarChar_NoCollation,    // SqlDbType.VarChar
                DefaultVariant,                // SqlDbType.Variant
                DefaultNVarChar_NoCollation,   // Placeholder for value 24
                DefaultXml,                    // SqlDbType.Xml
                DefaultNVarChar_NoCollation,   // Placeholder for value 26
                DefaultNVarChar_NoCollation,   // Placeholder for value 27
                DefaultNVarChar_NoCollation,   // Placeholder for value 28
                DefaultUdt_NoType,             // Generic Udt 
                DefaultStructured,             // Generic structured type
                DefaultDate,                   // SqlDbType.Date
                DefaultTime,                   // SqlDbType.Time
                DefaultDateTime2,              // SqlDbType.DateTime2
                DefaultDateTimeOffset,         // SqlDbType.DateTimeOffset
            };
 
        // static array of type names ordered by corresponding SqlDbType.
        // NOTE: INDEXED BY SqlDbType ENUM!  MUST UPDATE THIS ARRAY WHEN UPDATING SqlDbType!
        //   ONLY ACCESS THIS GLOBAL FROM get_TypeName!
        private static string[] __typeNameByDatabaseType =
            {
                "bigint",               // SqlDbType.BigInt
                "binary",               // SqlDbType.Binary
                "bit",                  // SqlDbType.Bit
                "char",                 // SqlDbType.Char
                "datetime",             // SqlDbType.DateTime
                "decimal",              // SqlDbType.Decimal
                "float",                // SqlDbType.Float
                "image",                // SqlDbType.Image
                "int",                  // SqlDbType.Int
                "money",                // SqlDbType.Money
                "nchar",                // SqlDbType.NChar
                "ntext",                // SqlDbType.NText
                "nvarchar",             // SqlDbType.NVarChar
                "real",                 // SqlDbType.Real
                "uniqueidentifier",     // SqlDbType.UniqueIdentifier
                "smalldatetime",        // SqlDbType.SmallDateTime
                "smallint",             // SqlDbType.SmallInt
                "smallmoney",           // SqlDbType.SmallMoney
                "text",                 // SqlDbType.Text
                "timestamp",            // SqlDbType.Timestamp
                "tinyint",              // SqlDbType.TinyInt
                "varbinary",            // SqlDbType.VarBinary
                "varchar",              // SqlDbType.VarChar
                "sql_variant",          // SqlDbType.Variant
                null,                   // placeholder for 24
                "xml",                  // SqlDbType.Xml
                null,                   // placeholder for 26
                null,                   // placeholder for 27
                null,                   // placeholder for 28
                String.Empty,           // SqlDbType.Udt  -- get type name from Type.FullName instead.
                String.Empty,           // Structured types have user-defined type names.
                "date",                 // SqlDbType.Date
                "time",                 // SqlDbType.Time
                "datetime2",            // SqlDbType.DateTime2
                "datetimeoffset",       // SqlDbType.DateTimeOffset
            };
 
        // Internal setter to be used by constructors only!  Modifies state!
        private void SetDefaultsForType( SqlDbType dbType )
            {
                SmiMetaData smdDflt = GetDefaultForType( dbType );
                _databaseType = dbType;
                _maxLength = smdDflt.MaxLength;
                _precision = smdDflt.Precision;
                _scale = smdDflt.Scale;
                _localeId = smdDflt.LocaleId;
                _compareOptions = smdDflt.CompareOptions;
                _clrType = null;
                _isMultiValued = smdDflt._isMultiValued;
                _fieldMetaData = smdDflt._fieldMetaData;            // This is ok due to immutability
                _extendedProperties = smdDflt._extendedProperties;  // This is ok due to immutability
            }
 
        internal string TraceString() {
            return TraceString(0);
        }
        virtual internal string TraceString(int indent) {
            string indentStr = new String(' ', indent);
            string fields = String.Empty;
            if (null != _fieldMetaData) {
                foreach(SmiMetaData fieldMd in _fieldMetaData) {
                    fields = String.Format(CultureInfo.InvariantCulture, 
                                "{0}{1}\n\t", fields, fieldMd.TraceString(indent+5));
                }
            }
 
            string properties = String.Empty;
            if (null != _extendedProperties) {
                foreach(SmiMetaDataProperty property in _extendedProperties.Values) {
                    properties = String.Format(CultureInfo.InvariantCulture, 
                                "{0}{1}                   {2}\n\t", properties, indentStr, property.TraceString());
                }
            }
 
            return String.Format(CultureInfo.InvariantCulture, "\n\t"
                               +"{0}            SqlDbType={1:g}\n\t"
                               +"{0}            MaxLength={2:d}\n\t"
                               +"{0}            Precision={3:d}\n\t"
                               +"{0}                Scale={4:d}\n\t"
                               +"{0}             LocaleId={5:x}\n\t"
                               +"{0}       CompareOptions={6:g}\n\t"
                               +"{0}                 Type={7}\n\t"
                               +"{0}          MultiValued={8}\n\t"
                               +"{0}               fields=\n\t{9}"
                               +"{0}           properties=\n\t{10}",
                                indentStr,
                                SqlDbType,
                                MaxLength,
                                Precision,
                                Scale,
                                LocaleId,
                                CompareOptions,
                                (null!=Type) ? Type.ToString():"<null>",
                                IsMultiValued,
                                fields,
                                properties);
 
        }
    }
 
 
    // SmiExtendedMetaData
    //
    //  Adds server-specific type extension information to base metadata, but still portable across a specific server.
    //
    internal class SmiExtendedMetaData : SmiMetaData {
 
        private string _name;           // context-dependant identifier, ie. parameter name for parameters, column name for columns, etc.
 
        // three-part name for typed xml schema and for udt names
        private string _typeSpecificNamePart1;
        private string _typeSpecificNamePart2;
        private string _typeSpecificNamePart3;
 
        [ObsoleteAttribute( "Not supported as of SMI v2.  Will be removed when v1 support dropped. Use ctor without columns param." )]
        internal SmiExtendedMetaData(
                                        SqlDbType dbType, 
                                        long maxLength,
                                        byte precision, 
                                        byte scale, 
                                        long localeId, 
                                        SqlCompareOptions compareOptions, 
                                        Type userDefinedType, 
                                        SmiMetaData[] columns, 
                                        string name, 
                                        string typeSpecificNamePart1, 
                                        string typeSpecificNamePart2, 
                                        string typeSpecificNamePart3) :
                                // Implement as calling the new ctor
                                this( 
                                        dbType, 
                                        maxLength, 
                                        precision, 
                                        scale, 
                                        localeId, 
                                        compareOptions, 
                                        userDefinedType, 
                                        name, 
                                        typeSpecificNamePart1, 
                                        typeSpecificNamePart2, 
                                        typeSpecificNamePart3 ) {
            Debug.Assert( null == columns, "Row types not supported" );
        }
 
        internal SmiExtendedMetaData(
                                        SqlDbType dbType, 
                                        long maxLength,
                                        byte precision, 
                                        byte scale, 
                                        long localeId, 
                                        SqlCompareOptions compareOptions, 
                                        Type userDefinedType, 
                                        string name, 
                                        string typeSpecificNamePart1, 
                                        string typeSpecificNamePart2, 
                                        string typeSpecificNamePart3) :
                                    this(
                                        dbType,
                                        maxLength,
                                        precision,
                                        scale,
                                        localeId,
                                        compareOptions,
                                        userDefinedType,
                                        false,
                                        null,
                                        null,
                                        name,
                                        typeSpecificNamePart1,
                                        typeSpecificNamePart2,
                                        typeSpecificNamePart3) {
        }
 
        // SMI V200 ctor.
        internal SmiExtendedMetaData(
                                        SqlDbType dbType,
                                        long maxLength,
                                        byte precision,
                                        byte scale,
                                        long localeId,
                                        SqlCompareOptions compareOptions,
                                        Type userDefinedType,
                                        bool isMultiValued,
                                        IList<SmiExtendedMetaData> fieldMetaData,
                                        SmiMetaDataPropertyCollection extendedProperties,
                                        string name,
                                        string typeSpecificNamePart1,
                                        string typeSpecificNamePart2,
                                        string typeSpecificNamePart3) :
                                this(   dbType,
                                        maxLength,
                                        precision,
                                        scale,
                                        localeId,
                                        compareOptions,
                                        userDefinedType,
                                        null,
                                        isMultiValued,
                                        fieldMetaData,
                                        extendedProperties,
                                        name,
                                        typeSpecificNamePart1,
                                        typeSpecificNamePart2,
                                        typeSpecificNamePart3) {
        }
 
        // SMI V220 ctor.
        internal SmiExtendedMetaData(
                                        SqlDbType dbType, 
                                        long maxLength,
                                        byte precision, 
                                        byte scale, 
                                        long localeId, 
                                        SqlCompareOptions compareOptions, 
                                        Type userDefinedType, 
                                        string udtAssemblyQualifiedName,
                                        bool isMultiValued,
                                        IList<SmiExtendedMetaData> fieldMetaData,
                                        SmiMetaDataPropertyCollection extendedProperties,
                                        string name, 
                                        string typeSpecificNamePart1, 
                                        string typeSpecificNamePart2, 
                                        string typeSpecificNamePart3 ):
                                    base( dbType,
                                        maxLength, 
                                        precision, 
                                        scale, 
                                        localeId, 
                                        compareOptions, 
                                        userDefinedType,
                                        udtAssemblyQualifiedName,
                                        isMultiValued,
                                        fieldMetaData,
                                        extendedProperties) {
            Debug.Assert(null == name || MaxNameLength >= name.Length, "Name is too long");
            
            _name = name;
            _typeSpecificNamePart1 = typeSpecificNamePart1;
            _typeSpecificNamePart2 = typeSpecificNamePart2;
            _typeSpecificNamePart3 = typeSpecificNamePart3;
        }
 
        internal string Name {
            get {
                return _name;
            }
        }
 
        internal string TypeSpecificNamePart1 {
            get {
                return _typeSpecificNamePart1;
            }
        }
 
        internal string TypeSpecificNamePart2 {
            get {
                return _typeSpecificNamePart2;
            }
        }
 
        internal string TypeSpecificNamePart3 {
            get {
                return _typeSpecificNamePart3;
            }
        }
 
        internal override string TraceString(int indent) {
            return String.Format(CultureInfo.InvariantCulture, 
                                 "{2}                 Name={0}"
                                +"{1}"
                                +"{2}TypeSpecificNamePart1='{3}'\n\t"
                                +"{2}TypeSpecificNamePart2='{4}'\n\t"
                                +"{2}TypeSpecificNamePart3='{5}'\n\t",
                                (null!=_name) ? _name : "<null>",
                                base.TraceString(indent),
                                new String(' ', indent),
                                (null!=TypeSpecificNamePart1) ? TypeSpecificNamePart1:"<null>",
                                (null!=TypeSpecificNamePart2) ? TypeSpecificNamePart2:"<null>",
                                (null!=TypeSpecificNamePart3) ? TypeSpecificNamePart3:"<null>");
        }
    }
 
 
    // SmiParameterMetaData
    //
    //  MetaData class to send parameter definitions to server.
    //  Sealed because we don't need to derive from it yet.
    // IMPORTANT DEVNOTE: This class is being used for parameter encryption functionality, to get the type_info TDS object from SqlParameter.
    // Please consider impact to that when changing this class. Refer to the callers of SqlParameter.GetMetadataForTypeInfo().
    internal sealed class SmiParameterMetaData : SmiExtendedMetaData {
 
        private ParameterDirection _direction;
 
        [ObsoleteAttribute( "Not supported as of SMI v2.  Will be removed when v1 support dropped. Use ctor without columns param." )]
        internal SmiParameterMetaData( 
                                        SqlDbType dbType, 
                                        long maxLength, 
                                        byte precision, 
                                        byte scale, 
                                        long  localeId, 
                                        SqlCompareOptions compareOptions, 
                                        Type userDefinedType, 
                                        SmiMetaData[] columns, 
                                        string name, 
                                        string typeSpecificNamePart1, 
                                        string typeSpecificNamePart2, 
                                        string typeSpecificNamePart3,
                                        ParameterDirection direction) :
                                // Implement as calling the new ctor
                                this ( 
                                        dbType, 
                                        maxLength, 
                                        precision, 
                                        scale, 
                                        localeId, 
                                        compareOptions, 
                                        userDefinedType, 
                                        name, 
                                        typeSpecificNamePart1, 
                                        typeSpecificNamePart2, 
                                        typeSpecificNamePart3,
                                        direction ) {
            Debug.Assert( null == columns, "Row types not supported" );
        }
 
        // SMI V100 (aka V3) ctor
        internal SmiParameterMetaData( 
                                        SqlDbType dbType, 
                                        long maxLength, 
                                        byte precision, 
                                        byte scale, 
                                        long  localeId, 
                                        SqlCompareOptions compareOptions, 
                                        Type userDefinedType, 
                                        string name, 
                                        string typeSpecificNamePart1, 
                                        string typeSpecificNamePart2, 
                                        string typeSpecificNamePart3,
                                        ParameterDirection direction) :
                                    this(
                                        dbType,
                                        maxLength, 
                                        precision, 
                                        scale, 
                                        localeId, 
                                        compareOptions, 
                                        userDefinedType,
                                        false,
                                        null,
                                        null,
                                        name, 
                                        typeSpecificNamePart1, 
                                        typeSpecificNamePart2,
                                        typeSpecificNamePart3,
                                        direction) {
        }
                                        
        // SMI V200 ctor.
        internal SmiParameterMetaData( 
                                        SqlDbType dbType, 
                                        long maxLength, 
                                        byte precision, 
                                        byte scale, 
                                        long  localeId, 
                                        SqlCompareOptions compareOptions, 
                                        Type userDefinedType, 
                                        bool isMultiValued,
                                        IList<SmiExtendedMetaData> fieldMetaData,
                                        SmiMetaDataPropertyCollection extendedProperties,
                                        string name, 
                                        string typeSpecificNamePart1, 
                                        string typeSpecificNamePart2, 
                                        string typeSpecificNamePart3,
                                        ParameterDirection direction) :
                                    this( dbType, 
                                        maxLength, 
                                        precision, 
                                        scale, 
                                        localeId, 
                                        compareOptions, 
                                        userDefinedType, 
                                        null,
                                        isMultiValued,
                                        fieldMetaData,
                                        extendedProperties,
                                        name, 
                                        typeSpecificNamePart1, 
                                        typeSpecificNamePart2,
                                        typeSpecificNamePart3,
                                        direction) {
        }
 
        // SMI V220 ctor.
        internal SmiParameterMetaData( 
                                        SqlDbType dbType, 
                                        long maxLength, 
                                        byte precision, 
                                        byte scale, 
                                        long  localeId, 
                                        SqlCompareOptions compareOptions, 
                                        Type userDefinedType,
                                        string udtAssemblyQualifiedName,
                                        bool isMultiValued,
                                        IList<SmiExtendedMetaData> fieldMetaData,
                                        SmiMetaDataPropertyCollection extendedProperties,
                                        string name, 
                                        string typeSpecificNamePart1, 
                                        string typeSpecificNamePart2, 
                                        string typeSpecificNamePart3,
                                        ParameterDirection direction) :
                                    base( dbType, 
                                        maxLength, 
                                        precision, 
                                        scale, 
                                        localeId, 
                                        compareOptions, 
                                        userDefinedType, 
                                        udtAssemblyQualifiedName,
                                        isMultiValued,
                                        fieldMetaData,
                                        extendedProperties,
                                        name, 
                                        typeSpecificNamePart1, 
                                        typeSpecificNamePart2,
                                        typeSpecificNamePart3) {
            Debug.Assert( ParameterDirection.Input == direction
                       || ParameterDirection.Output == direction 
                       || ParameterDirection.InputOutput == direction 
                       || ParameterDirection.ReturnValue == direction, "Invalid direction: " + direction );
            _direction = direction;
        }
 
        internal ParameterDirection Direction {
            get {
                return _direction;
            }
        }
 
        internal override string TraceString(int indent) {
            return String.Format(CultureInfo.InvariantCulture, "{0}"
                                +"{1}            Direction={2:g}\n\t",
                                base.TraceString(indent),
                                new String(' ', indent),
                                Direction);
        }
    }
 
 
    // SmiStorageMetaData
    //
    //  This class represents the addition of storage-level attributes to the heirarchy (i.e. attributes from 
    //  underlying table, source variables, or whatever).
    //
    //  Most values use Null (either IsNullable == true or CLR null) to indicate "Not specified" state.  Selection
    //  of which values allow "not specified" determined by backward compatibility.
    //
    //  Maps approximately to TDS' COLMETADATA token with TABNAME and part of COLINFO thrown in.
    internal class SmiStorageMetaData : SmiExtendedMetaData {
 
        // AllowsDBNull is the only value required to be specified.
        private bool        _allowsDBNull;      // could the column return nulls? equivalent to TDS's IsNullable bit
        private string      _serverName;        // underlying column's server
        private string      _catalogName;       // underlying column's database
        private string      _schemaName;        // underlying column's schema
        private string      _tableName;         // underlying column's table
        private string      _columnName;        // underlying column's name
        private SqlBoolean  _isKey;             // Is this one of a set of key columns that uniquely identify an underlying table?
        private bool        _isIdentity;        // Is this from an identity column
        private bool        _isColumnSet;       // Is this column the XML representation of a columnset?
 
        [ObsoleteAttribute( "Not supported as of SMI v2.  Will be removed when v1 support dropped. Use ctor without columns param." )]
        internal SmiStorageMetaData(
                                        SqlDbType dbType, 
                                        long maxLength, 
                                        byte precision, 
                                        byte scale, 
                                        long localeId, 
                                        SqlCompareOptions compareOptions,
                                        Type userDefinedType, 
                                        SmiMetaData[] columns, 
                                        string name, 
                                        string typeSpecificNamePart1, 
                                        string typeSpecificNamePart2, 
                                        string typeSpecificNamePart3,
                                        bool allowsDBNull, 
                                        string serverName, 
                                        string catalogName, 
                                        string schemaName, 
                                        string tableName, 
                                        string columnName, 
                                        SqlBoolean isKey, 
                                        bool isIdentity) :
                                // Implement as calling the new ctor
                                this ( 
                                        dbType, 
                                        maxLength, 
                                        precision, 
                                        scale, 
                                        localeId, 
                                        compareOptions, 
                                        userDefinedType, 
                                        name, 
                                        typeSpecificNamePart1, 
                                        typeSpecificNamePart2, 
                                        typeSpecificNamePart3,
                                        allowsDBNull,
                                        serverName,
                                        catalogName,
                                        schemaName,
                                        tableName,
                                        columnName,
                                        isKey,
                                        isIdentity) {
            Debug.Assert( null == columns, "Row types not supported" );
        }
 
        internal SmiStorageMetaData(
                                        SqlDbType dbType, 
                                        long maxLength, 
                                        byte precision, 
                                        byte scale, 
                                        long localeId, 
                                        SqlCompareOptions compareOptions,
                                        Type userDefinedType, 
                                        string name, 
                                        string typeSpecificNamePart1, 
                                        string typeSpecificNamePart2, 
                                        string typeSpecificNamePart3,
                                        bool allowsDBNull, 
                                        string serverName, 
                                        string catalogName, 
                                        string schemaName, 
                                        string tableName, 
                                        string columnName, 
                                        SqlBoolean isKey, 
                                        bool isIdentity) :
                                    this(dbType, 
                                        maxLength, 
                                        precision, 
                                        scale, 
                                        localeId, 
                                        compareOptions,
                                        userDefinedType,
                                        false,
                                        null,
                                        null,
                                        name, 
                                        typeSpecificNamePart1, 
                                        typeSpecificNamePart2,
                                        typeSpecificNamePart3,
                                        allowsDBNull,
                                        serverName,
                                        catalogName,
                                        schemaName,
                                        tableName,
                                        columnName,
                                        isKey,
                                        isIdentity) {
        }
                                        
        // SMI V200 ctor.
        internal SmiStorageMetaData(
                                        SqlDbType dbType, 
                                        long maxLength, 
                                        byte precision, 
                                        byte scale, 
                                        long localeId, 
                                        SqlCompareOptions compareOptions,
                                        Type userDefinedType, 
                                        bool isMultiValued,
                                        IList<SmiExtendedMetaData> fieldMetaData,
                                        SmiMetaDataPropertyCollection extendedProperties,
                                        string name, 
                                        string typeSpecificNamePart1, 
                                        string typeSpecificNamePart2, 
                                        string typeSpecificNamePart3,
                                        bool allowsDBNull, 
                                        string serverName, 
                                        string catalogName, 
                                        string schemaName, 
                                        string tableName, 
                                        string columnName, 
                                        SqlBoolean isKey, 
                                        bool isIdentity) :
                                this(   dbType, 
                                        maxLength, 
                                        precision, 
                                        scale, 
                                        localeId, 
                                        compareOptions, 
                                        userDefinedType, 
                                        null,
                                        isMultiValued,
                                        fieldMetaData,
                                        extendedProperties,
                                        name, 
                                        typeSpecificNamePart1, 
                                        typeSpecificNamePart2,
                                        typeSpecificNamePart3,
                                        allowsDBNull,
                                        serverName,
                                        catalogName,
                                        schemaName,
                                        tableName,
                                        columnName,
                                        isKey,
                                        isIdentity,
                                        false) {
        }
 
        // SMI V220 ctor.
        internal SmiStorageMetaData(
                                        SqlDbType dbType, 
                                        long maxLength, 
                                        byte precision, 
                                        byte scale, 
                                        long localeId, 
                                        SqlCompareOptions compareOptions,
                                        Type userDefinedType,
                                        string udtAssemblyQualifiedName,
                                        bool isMultiValued,
                                        IList<SmiExtendedMetaData> fieldMetaData,
                                        SmiMetaDataPropertyCollection extendedProperties,
                                        string name, 
                                        string typeSpecificNamePart1, 
                                        string typeSpecificNamePart2, 
                                        string typeSpecificNamePart3,
                                        bool allowsDBNull, 
                                        string serverName, 
                                        string catalogName, 
                                        string schemaName, 
                                        string tableName, 
                                        string columnName, 
                                        SqlBoolean isKey, 
                                        bool isIdentity,
                                        bool isColumnSet) :
                                    base( dbType, 
                                        maxLength, 
                                        precision, 
                                        scale, 
                                        localeId, 
                                        compareOptions, 
                                        userDefinedType,
                                        udtAssemblyQualifiedName,
                                        isMultiValued,
                                        fieldMetaData,
                                        extendedProperties,
                                        name, 
                                        typeSpecificNamePart1, 
                                        typeSpecificNamePart2,
                                        typeSpecificNamePart3) {
            _allowsDBNull = allowsDBNull;
            _serverName = serverName;
            _catalogName = catalogName;
            _schemaName = schemaName;
            _tableName = tableName;
            _columnName = columnName;
            _isKey = isKey;
            _isIdentity = isIdentity;
            _isColumnSet = isColumnSet;
        }
 
        internal bool AllowsDBNull {
            get {
                return _allowsDBNull;
            }
        }
 
        internal string ServerName {
            get {
                return _serverName;
            }
        }
 
        internal string CatalogName {
            get {
                return _catalogName;
            }
        }
 
        internal string SchemaName {
            get {
                return _schemaName;
            }
        }
 
        internal string TableName {
            get {
                return _tableName;
            }
        }
 
        internal string ColumnName {
            get {
                return _columnName;
            }
        }
 
        internal SqlBoolean IsKey {
            get {
                return _isKey;
            }
        }
 
        internal bool IsIdentity {
            get {
                return _isIdentity;
            }
        }
 
        internal bool IsColumnSet {
            get {
                return _isColumnSet;
            }
        }
 
        internal override string TraceString(int indent) {
            return String.Format(CultureInfo.InvariantCulture, "{0}"
                                +"{1}         AllowsDBNull={2}\n\t"
                                +"{1}           ServerName='{3}'\n\t"
                                +"{1}          CatalogName='{4}'\n\t"
                                +"{1}           SchemaName='{5}'\n\t"
                                +"{1}            TableName='{6}'\n\t"
                                +"{1}           ColumnName='{7}'\n\t"
                                +"{1}                IsKey={8}\n\t"
                                +"{1}           IsIdentity={9}\n\t",
                                base.TraceString(indent),
                                new String(' ', indent),
                                AllowsDBNull,
                                (null!=ServerName) ? ServerName:"<null>",
                                (null!=CatalogName) ? CatalogName:"<null>",
                                (null!=SchemaName) ? SchemaName:"<null>",
                                (null!=TableName) ? TableName:"<null>",
                                (null!=ColumnName) ? ColumnName:"<null>",
                                IsKey,
                                IsIdentity);
        }
 
    }
 
    // SmiQueryMetaData
    //
    //  Adds Query-specific attributes.
    //  Sealed since we don't need to extend it for now.
    //  Maps to full COLMETADATA + COLINFO + TABNAME tokens on TDS.
    internal class SmiQueryMetaData : SmiStorageMetaData {
 
        private bool _isReadOnly;
        private SqlBoolean _isExpression;
        private SqlBoolean _isAliased;
        private SqlBoolean _isHidden;
 
        [ObsoleteAttribute( "Not supported as of SMI v2.  Will be removed when v1 support dropped. Use ctor without columns param." )]
        internal SmiQueryMetaData(
                                        SqlDbType dbType, 
                                        long maxLength, 
                                        byte precision, 
                                        byte scale, 
                                        long localeId, 
                                        SqlCompareOptions compareOptions,
                                        Type userDefinedType, 
                                        SmiMetaData[] columns, 
                                        string name, 
                                        string typeSpecificNamePart1, 
                                        string typeSpecificNamePart2, 
                                        string typeSpecificNamePart3,
                                        bool allowsDBNull, 
                                        string serverName, 
                                        string catalogName, 
                                        string schemaName, 
                                        string tableName, 
                                        string columnName, 
                                        SqlBoolean isKey, 
                                        bool isIdentity, 
                                        bool isReadOnly, 
                                        SqlBoolean isExpression, 
                                        SqlBoolean isAliased, 
                                        SqlBoolean isHidden ) :
                                // Implement as calling the new ctor
                                this ( 
                                        dbType, 
                                        maxLength, 
                                        precision, 
                                        scale, 
                                        localeId, 
                                        compareOptions, 
                                        userDefinedType, 
                                        name, 
                                        typeSpecificNamePart1, 
                                        typeSpecificNamePart2, 
                                        typeSpecificNamePart3,
                                        allowsDBNull,
                                        serverName,
                                        catalogName,
                                        schemaName,
                                        tableName,
                                        columnName,
                                        isKey,
                                        isIdentity,
                                        isReadOnly,
                                        isExpression,
                                        isAliased,
                                        isHidden ) {
            Debug.Assert( null == columns, "Row types not supported" );
        }
 
        internal SmiQueryMetaData( SqlDbType dbType,
                                        long maxLength, 
                                        byte precision, 
                                        byte scale, 
                                        long localeId, 
                                        SqlCompareOptions compareOptions,
                                        Type userDefinedType, 
                                        string name,
                                        string typeSpecificNamePart1, 
                                        string typeSpecificNamePart2, 
                                        string typeSpecificNamePart3,
                                        bool allowsDBNull, 
                                        string serverName, 
                                        string catalogName, 
                                        string schemaName,
                                        string tableName, 
                                        string columnName, 
                                        SqlBoolean isKey,
                                        bool isIdentity, 
                                        bool isReadOnly, 
                                        SqlBoolean isExpression, 
                                        SqlBoolean isAliased, 
                                        SqlBoolean isHidden ) :
                                    this( dbType,
                                        maxLength, 
                                        precision, 
                                        scale, 
                                        localeId, 
                                        compareOptions, 
                                        userDefinedType, 
                                        false,
                                        null,
                                        null,
                                        name,
                                        typeSpecificNamePart1, 
                                        typeSpecificNamePart2,
                                        typeSpecificNamePart3,
                                        allowsDBNull, 
                                        serverName, 
                                        catalogName, 
                                        schemaName, 
                                        tableName, 
                                        columnName, 
                                        isKey, 
                                        isIdentity,
                                        isReadOnly,
                                        isExpression,
                                        isAliased,
                                        isHidden) {
        }
 
        // SMI V200 ctor.
        internal SmiQueryMetaData( SqlDbType dbType,
                                        long maxLength, 
                                        byte precision, 
                                        byte scale, 
                                        long localeId, 
                                        SqlCompareOptions compareOptions,
                                        Type userDefinedType, 
                                        bool isMultiValued,
                                        IList<SmiExtendedMetaData> fieldMetaData,
                                        SmiMetaDataPropertyCollection extendedProperties,
                                        string name,
                                        string typeSpecificNamePart1, 
                                        string typeSpecificNamePart2, 
                                        string typeSpecificNamePart3,
                                        bool allowsDBNull, 
                                        string serverName, 
                                        string catalogName, 
                                        string schemaName,
                                        string tableName, 
                                        string columnName, 
                                        SqlBoolean isKey,
                                        bool isIdentity,
                                        bool isReadOnly, 
                                        SqlBoolean isExpression, 
                                        SqlBoolean isAliased, 
                                        SqlBoolean isHidden) :
                                   this( dbType,
                                        maxLength, 
                                        precision, 
                                        scale, 
                                        localeId, 
                                        compareOptions, 
                                        userDefinedType, 
                                        null,
                                        isMultiValued,
                                        fieldMetaData,
                                        extendedProperties,
                                        name,
                                        typeSpecificNamePart1, 
                                        typeSpecificNamePart2,
                                        typeSpecificNamePart3,
                                        allowsDBNull, 
                                        serverName, 
                                        catalogName, 
                                        schemaName, 
                                        tableName, 
                                        columnName, 
                                        isKey, 
                                        isIdentity,
                                        false,
                                        isReadOnly, 
                                        isExpression, 
                                        isAliased, 
                                        isHidden ) {
        }
        // SMI V220 ctor.
        internal SmiQueryMetaData( SqlDbType dbType,
                                        long maxLength, 
                                        byte precision, 
                                        byte scale, 
                                        long localeId, 
                                        SqlCompareOptions compareOptions,
                                        Type userDefinedType, 
                                        string udtAssemblyQualifiedName,
                                        bool isMultiValued,
                                        IList<SmiExtendedMetaData> fieldMetaData,
                                        SmiMetaDataPropertyCollection extendedProperties,
                                        string name,
                                        string typeSpecificNamePart1, 
                                        string typeSpecificNamePart2, 
                                        string typeSpecificNamePart3,
                                        bool allowsDBNull, 
                                        string serverName, 
                                        string catalogName, 
                                        string schemaName,
                                        string tableName, 
                                        string columnName, 
                                        SqlBoolean isKey,
                                        bool isIdentity, 
                                        bool isColumnSet,
                                        bool isReadOnly, 
                                        SqlBoolean isExpression, 
                                        SqlBoolean isAliased, 
                                        SqlBoolean isHidden ) :
                                    base( dbType,
                                        maxLength, 
                                        precision, 
                                        scale, 
                                        localeId, 
                                        compareOptions, 
                                        userDefinedType, 
                                        udtAssemblyQualifiedName,
                                        isMultiValued,
                                        fieldMetaData,
                                        extendedProperties,
                                        name,
                                        typeSpecificNamePart1, 
                                        typeSpecificNamePart2,
                                        typeSpecificNamePart3,
                                        allowsDBNull, 
                                        serverName, 
                                        catalogName, 
                                        schemaName, 
                                        tableName, 
                                        columnName, 
                                        isKey, 
                                        isIdentity,
                                        isColumnSet ) {
        _isReadOnly = isReadOnly;
        _isExpression = isExpression;
        _isAliased = isAliased;
        _isHidden = isHidden;
        }
 
        internal bool IsReadOnly {
            get {
                return _isReadOnly;
            }
        }
 
        internal SqlBoolean IsExpression {
            get {
                return _isExpression;
            }
        }
 
        internal SqlBoolean IsAliased {
            get {
                return _isAliased;
            }
        }
 
        internal SqlBoolean IsHidden {
            get {
                return _isHidden;
            }
        }
 
 
    internal override string TraceString(int indent) {
        return String.Format(CultureInfo.InvariantCulture, "{0}"
                            +"{1}           IsReadOnly={2}\n\t"
                            +"{1}         IsExpression={3}\n\t"
                            +"{1}            IsAliased={4}\n\t"
                            +"{1}             IsHidden={5}",
                            base.TraceString(indent),
                            new String(' ', indent),
                            AllowsDBNull,
                            IsExpression,
                            IsAliased,
                            IsHidden);
    }
 
    }
}