MetadataUtilsSmi.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / whidbey / NetFXspW7 / ndp / fx / src / Data / Microsoft / SqlServer / Server / MetadataUtilsSmi.cs / 1 / MetadataUtilsSmi.cs

                            //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// [....] 
// [....]
//----------------------------------------------------------------------------- 
 
namespace Microsoft.SqlServer.Server {
 
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Data; 
    using System.Data.Sql;
    using System.Data.Common; 
    using System.Data.SqlClient; 
    using System.Data.SqlTypes;
    using System.Diagnostics; 


    // Utilities for manipulating smi-related metadata.
    // 
    //  THIS CLASS IS BUILT ON TOP OF THE SMI INTERFACE -- SMI SHOULD NOT DEPEND ON IT!
    // 
    //  These are all based off of knowing the clr type of the value 
    //  as an ExtendedClrTypeCode enum for rapid access (lookup in static array is best, if possible).
    internal class MetaDataUtilsSmi { 

        internal const SqlDbType    InvalidSqlDbType = (SqlDbType) (-1);
        internal const long         InvalidMaxLength = -2;
 
        // Standard type inference map to get SqlDbType when all you know is the value's type (typecode)
        //  This map's index is off by one (add one to typecode locate correct entry) in order 
        //  support ExtendedSqlDbType.Invalid 
        // ONLY ACCESS THIS ARRAY FROM InferSqlDbTypeFromTypeCode!!!
        static readonly SqlDbType[] __extendedTypeCodeToSqlDbTypeMap = { 
            InvalidSqlDbType,               // Invalid extended type code
            SqlDbType.Bit,                  // System.Boolean
            SqlDbType.TinyInt,              // System.Byte
            SqlDbType.NVarChar,             // System.Char 
            SqlDbType.DateTime,             // System.DateTime
            InvalidSqlDbType,               // System.DBNull doesn't have an inferable SqlDbType 
            SqlDbType.Decimal,              // System.Decimal 
            SqlDbType.Float,                // System.Double
            InvalidSqlDbType,               // null reference doesn't have an inferable SqlDbType 
            SqlDbType.SmallInt,             // System.Int16
            SqlDbType.Int,                  // System.Int32
            SqlDbType.BigInt,               // System.Int64
            InvalidSqlDbType,               // System.SByte doesn't have an inferable SqlDbType 
            SqlDbType.Real,                 // System.Single
            SqlDbType.NVarChar,             // System.String 
            InvalidSqlDbType,               // System.UInt16 doesn't have an inferable SqlDbType 
            InvalidSqlDbType,               // System.UInt32 doesn't have an inferable SqlDbType
            InvalidSqlDbType,               // System.UInt64 doesn't have an inferable SqlDbType 
            InvalidSqlDbType,               // System.Object doesn't have an inferable SqlDbType
            SqlDbType.VarBinary,            // System.ByteArray
            SqlDbType.NVarChar,             // System.CharArray
            SqlDbType.UniqueIdentifier,     // System.Guid 
            SqlDbType.VarBinary,            // System.Data.SqlTypes.SqlBinary
            SqlDbType.Bit,                  // System.Data.SqlTypes.SqlBoolean 
            SqlDbType.TinyInt,              // System.Data.SqlTypes.SqlByte 
            SqlDbType.DateTime,             // System.Data.SqlTypes.SqlDateTime
            SqlDbType.Float,                // System.Data.SqlTypes.SqlDouble 
            SqlDbType.UniqueIdentifier,     // System.Data.SqlTypes.SqlGuid
            SqlDbType.SmallInt,             // System.Data.SqlTypes.SqlInt16
            SqlDbType.Int,                  // System.Data.SqlTypes.SqlInt32
            SqlDbType.BigInt,               // System.Data.SqlTypes.SqlInt64 
            SqlDbType.Money,                // System.Data.SqlTypes.SqlMoney
            SqlDbType.Decimal,              // System.Data.SqlTypes.SqlDecimal 
            SqlDbType.Real,                 // System.Data.SqlTypes.SqlSingle 
            SqlDbType.NVarChar,             // System.Data.SqlTypes.SqlString
            SqlDbType.NVarChar,             // System.Data.SqlTypes.SqlChars 
            SqlDbType.VarBinary,            // System.Data.SqlTypes.SqlBytes
            SqlDbType.Xml,                  // System.Data.SqlTypes.SqlXml
            SqlDbType.Structured,           // System.Data.DataTable
            SqlDbType.Structured,           // System.Collections.IEnumerable, used for TVPs it must return IDataRecord 
            SqlDbType.Structured,           // System.Collections.Generic.IEnumerable
            SqlDbType.Time,                 // System.TimeSpan 
            SqlDbType.DateTimeOffset,       // System.DateTimeOffset 
        };
 
        // Hash table to map from clr type object to ExtendedClrTypeCodeMap enum
        //  ONLY ACCESS THIS HASH TABLE FROM DetermineExtendedTypeCode METHOD!!!  (and class ctor for setup)
        static readonly Hashtable __typeToExtendedTypeCodeMap;
 

        // class ctor 
        static MetaDataUtilsSmi() { 
            // set up type mapping hash table
            //  keep this initialization list in the same order as ExtendedClrTypeCode for ease in validating! 
            Hashtable ht = new Hashtable(42);
            ht.Add( typeof( System.Boolean ),       ExtendedClrTypeCode.Boolean );
            ht.Add( typeof( System.Byte ),          ExtendedClrTypeCode.Byte );
            ht.Add( typeof( System.Char ),          ExtendedClrTypeCode.Char ); 
            ht.Add( typeof( System.DateTime ),      ExtendedClrTypeCode.DateTime );
            ht.Add( typeof( System.DBNull ),        ExtendedClrTypeCode.DBNull ); 
            ht.Add( typeof( System.Decimal ),       ExtendedClrTypeCode.Decimal ); 
            ht.Add( typeof( System.Double ),        ExtendedClrTypeCode.Double );
            // lookup code will have to special-case null-ref anyway, so don't bother adding ExtendedTypeCode.Empty to the table 
            ht.Add( typeof( System.Int16 ),         ExtendedClrTypeCode.Int16 );
            ht.Add( typeof( System.Int32 ),         ExtendedClrTypeCode.Int32 );
            ht.Add( typeof( System.Int64 ),         ExtendedClrTypeCode.Int64 );
            ht.Add( typeof( System.SByte ),         ExtendedClrTypeCode.SByte ); 
            ht.Add( typeof( System.Single ),        ExtendedClrTypeCode.Single );
            ht.Add( typeof( System.String ),        ExtendedClrTypeCode.String ); 
            ht.Add( typeof( System.UInt16 ),        ExtendedClrTypeCode.UInt16 ); 
            ht.Add( typeof( System.UInt32 ),        ExtendedClrTypeCode.UInt32 );
            ht.Add( typeof( System.UInt64 ),        ExtendedClrTypeCode.UInt64 ); 
            ht.Add( typeof( System.Object ),        ExtendedClrTypeCode.Object );
            ht.Add( typeof( System.Byte[] ),        ExtendedClrTypeCode.ByteArray );
            ht.Add( typeof( System.Char[] ),        ExtendedClrTypeCode.CharArray );
            ht.Add( typeof( System.Guid ),          ExtendedClrTypeCode.Guid ); 
            ht.Add( typeof( SqlBinary ),            ExtendedClrTypeCode.SqlBinary );
            ht.Add( typeof( SqlBoolean ),           ExtendedClrTypeCode.SqlBoolean ); 
            ht.Add( typeof( SqlByte ),              ExtendedClrTypeCode.SqlByte ); 
            ht.Add( typeof( SqlDateTime ),          ExtendedClrTypeCode.SqlDateTime );
            ht.Add( typeof( SqlDouble ),            ExtendedClrTypeCode.SqlDouble ); 
            ht.Add( typeof( SqlGuid ),              ExtendedClrTypeCode.SqlGuid );
            ht.Add( typeof( SqlInt16 ),             ExtendedClrTypeCode.SqlInt16 );
            ht.Add( typeof( SqlInt32 ),             ExtendedClrTypeCode.SqlInt32 );
            ht.Add( typeof( SqlInt64 ),             ExtendedClrTypeCode.SqlInt64 ); 
            ht.Add( typeof( SqlMoney ),             ExtendedClrTypeCode.SqlMoney );
            ht.Add( typeof( SqlDecimal ),           ExtendedClrTypeCode.SqlDecimal ); 
            ht.Add( typeof( SqlSingle ),            ExtendedClrTypeCode.SqlSingle ); 
            ht.Add( typeof( SqlString ),            ExtendedClrTypeCode.SqlString );
            ht.Add( typeof( SqlChars ),             ExtendedClrTypeCode.SqlChars ); 
            ht.Add( typeof( SqlBytes ),             ExtendedClrTypeCode.SqlBytes );
            ht.Add( typeof( SqlXml ),               ExtendedClrTypeCode.SqlXml );
            ht.Add( typeof( DataTable ),            ExtendedClrTypeCode.DataTable );
            ht.Add( typeof( DbDataReader ),         ExtendedClrTypeCode.DbDataReader ); 
            ht.Add( typeof( IEnumerable ),          ExtendedClrTypeCode.IEnumerableOfSqlDataRecord );
            ht.Add( typeof( System.TimeSpan ),      ExtendedClrTypeCode.TimeSpan ); 
            ht.Add( typeof( System.DateTimeOffset ),               ExtendedClrTypeCode.DateTimeOffset ); 
            __typeToExtendedTypeCodeMap = ht;
        } 


        internal static bool IsCharOrXmlType(SqlDbType type) {
            return  IsUnicodeType(type) || 
                    IsAnsiType(type) ||
                    type == SqlDbType.Xml; 
        } 

        internal static bool IsUnicodeType(SqlDbType type) { 
            return  type == SqlDbType.NChar ||
                    type == SqlDbType.NVarChar ||
                    type == SqlDbType.NText;
        } 

        internal static bool IsAnsiType(SqlDbType type) { 
            return  type == SqlDbType.Char || 
                    type == SqlDbType.VarChar ||
                    type == SqlDbType.Text; 
        }

        internal static bool IsBinaryType(SqlDbType type) {
            return  type == SqlDbType.Binary || 
                    type == SqlDbType.VarBinary ||
                    type == SqlDbType.Image; 
        } 

        // Does this type use PLP format values? 
        internal static bool IsPlpFormat(SmiMetaData metaData) {
            return  metaData.MaxLength == SmiMetaData.UnlimitedMaxLengthIndicator ||
                    metaData.SqlDbType == SqlDbType.Image ||
                    metaData.SqlDbType == SqlDbType.NText || 
                    metaData.SqlDbType == SqlDbType.Text ||
                    metaData.SqlDbType == SqlDbType.Udt; 
        } 

 

        // If we know we're only going to use this object to assign to a specific SqlDbType back end object,
        //  we can save some processing time by only checking for the few valid types that can be assigned to the dbType.
        //  This assumes a switch statement over SqlDbType is faster than getting the ClrTypeCode and iterating over a 
        //  series of if statements, or using a hash table.
        // NOTE: the form of these checks is taking advantage of a feature of the JIT compiler that is supposed to 
        //      optimize checks of the form '(xxx.GetType() == typeof( YYY ))'.  The JIT team claimed at one point that 
        //      this doesn't even instantiate a Type instance, thus was the fastest method for individual comparisions.
        //      Given that there's a known SqlDbType, thus a minimal number of comparisions, it's likely this is faster 
        //      than the other approaches considered (both GetType().GetTypeCode() switch and hash table using Type keys
        //      must instantiate a Type object.  The typecode switch also degenerates into a large if-then-else for
        //      all but the primitive clr types.
        internal static ExtendedClrTypeCode DetermineExtendedTypeCodeForUseWithSqlDbType( 
                SqlDbType   dbType,
                bool        isMultiValued, 
                object      value, 
                Type        udtType,
                ulong       smiVersion) { 
            ExtendedClrTypeCode extendedCode = ExtendedClrTypeCode.Invalid;

            // fast-track null, which is valid for all types
            if ( null == value ) { 
                extendedCode = ExtendedClrTypeCode.Empty;
            } 
            else if ( DBNull.Value == value ) { 
                extendedCode = ExtendedClrTypeCode.DBNull;
            } 
            else {
                switch(dbType)
                    {
                    case SqlDbType.BigInt: 
                        if (value.GetType() == typeof(Int64))
                            extendedCode = ExtendedClrTypeCode.Int64; 
                        else if (value.GetType() == typeof(SqlInt64)) 
                            extendedCode = ExtendedClrTypeCode.SqlInt64;
                        else if (Type.GetTypeCode(value.GetType()) == TypeCode.Int64) 
                            extendedCode = ExtendedClrTypeCode.Int64;
                        break;
                    case SqlDbType.Binary:
                    case SqlDbType.VarBinary: 
                    case SqlDbType.Image:
                    case SqlDbType.Timestamp: 
                        if (value.GetType() == typeof( byte[] )) 
                            extendedCode = ExtendedClrTypeCode.ByteArray;
                        else if (value.GetType() == typeof( SqlBinary )) 
                            extendedCode = ExtendedClrTypeCode.SqlBinary;
                        else if (value.GetType() == typeof( SqlBytes ))
                            extendedCode = ExtendedClrTypeCode.SqlBytes;
                        break; 
                    case SqlDbType.Bit:
                        if (value.GetType() == typeof( bool )) 
                            extendedCode = ExtendedClrTypeCode.Boolean; 
                        else if (value.GetType() == typeof( SqlBoolean ))
                            extendedCode = ExtendedClrTypeCode.SqlBoolean; 
                        else if (Type.GetTypeCode(value.GetType()) == TypeCode.Boolean)
                            extendedCode = ExtendedClrTypeCode.Boolean;
                        break;
                    case SqlDbType.Char: 
                    case SqlDbType.NChar:
                    case SqlDbType.NText: 
                    case SqlDbType.NVarChar: 
                    case SqlDbType.Text:
                    case SqlDbType.VarChar: 
                        if (value.GetType() == typeof( string ))
                            extendedCode = ExtendedClrTypeCode.String;
                        else if (value.GetType() == typeof( SqlString ))
                            extendedCode = ExtendedClrTypeCode.SqlString; 
                        else if (value.GetType() == typeof( char[] ))
                            extendedCode = ExtendedClrTypeCode.CharArray; 
                        else if (value.GetType() == typeof( SqlChars )) 
                            extendedCode = ExtendedClrTypeCode.SqlChars;
                        else if (value.GetType() == typeof( char )) 
                            extendedCode = ExtendedClrTypeCode.Char;
                        else if (Type.GetTypeCode(value.GetType()) == TypeCode.Char)
                            extendedCode = ExtendedClrTypeCode.Char;
                        else if (Type.GetTypeCode(value.GetType()) == TypeCode.String) 
                            extendedCode = ExtendedClrTypeCode.String;
                        break; 
                    case SqlDbType.Date: 
                    case SqlDbType.DateTime2:
                        if (smiVersion >= SmiContextFactory.KatmaiVersion) { 
                            goto case SqlDbType.DateTime;
                        }
                        break;
                    case SqlDbType.DateTime: 
                    case SqlDbType.SmallDateTime:
                        if (value.GetType() == typeof( DateTime )) 
                            extendedCode = ExtendedClrTypeCode.DateTime; 
                        else if (value.GetType() == typeof( SqlDateTime ))
                            extendedCode = ExtendedClrTypeCode.SqlDateTime; 
                        else if (Type.GetTypeCode(value.GetType()) == TypeCode.DateTime)
                            extendedCode = ExtendedClrTypeCode.DateTime;
                        break;
                    case SqlDbType.Decimal: 
                        if (value.GetType() == typeof( Decimal ))
                            extendedCode = ExtendedClrTypeCode.Decimal; 
                        else if (value.GetType() == typeof( SqlDecimal )) 
                            extendedCode = ExtendedClrTypeCode.SqlDecimal;
                        else if (Type.GetTypeCode(value.GetType()) == TypeCode.Decimal) 
                            extendedCode = ExtendedClrTypeCode.Decimal;
                        break;
                    case SqlDbType.Real:
                        if (value.GetType() == typeof( Single )) 
                            extendedCode = ExtendedClrTypeCode.Single;
                        else if (value.GetType() == typeof( SqlSingle )) 
                            extendedCode = ExtendedClrTypeCode.SqlSingle; 
                        else if (Type.GetTypeCode(value.GetType()) == TypeCode.Single)
                            extendedCode = ExtendedClrTypeCode.Single; 
                        break;
                    case SqlDbType.Int:
                        if (value.GetType() == typeof( Int32 ))
                            extendedCode = ExtendedClrTypeCode.Int32; 
                        else if (value.GetType() == typeof( SqlInt32 ))
                            extendedCode = ExtendedClrTypeCode.SqlInt32; 
                        else if (Type.GetTypeCode(value.GetType()) == TypeCode.Int32) 
                            extendedCode = ExtendedClrTypeCode.Int32;
                        break; 
                    case SqlDbType.Money:
                    case SqlDbType.SmallMoney:
                        if (value.GetType() == typeof( SqlMoney ))
                            extendedCode = ExtendedClrTypeCode.SqlMoney; 
                        else if (value.GetType() == typeof( Decimal ))
                            extendedCode = ExtendedClrTypeCode.Decimal; 
                        else if (Type.GetTypeCode(value.GetType()) == TypeCode.Decimal) 
                            extendedCode = ExtendedClrTypeCode.Decimal;
                        break; 
                    case SqlDbType.Float:
                        if (value.GetType() == typeof( SqlDouble ))
                            extendedCode = ExtendedClrTypeCode.SqlDouble;
                        else if (value.GetType() == typeof( Double )) 
                            extendedCode = ExtendedClrTypeCode.Double;
                        else if (Type.GetTypeCode(value.GetType()) == TypeCode.Double) 
                            extendedCode = ExtendedClrTypeCode.Double; 
                        break;
                    case SqlDbType.UniqueIdentifier: 
                        if (value.GetType() == typeof( SqlGuid ))
                            extendedCode = ExtendedClrTypeCode.SqlGuid;
                        else if (value.GetType() == typeof( Guid ))
                            extendedCode = ExtendedClrTypeCode.Guid; 
                        break;
                    case SqlDbType.SmallInt: 
                        if (value.GetType() == typeof( Int16 )) 
                            extendedCode = ExtendedClrTypeCode.Int16;
                        else if (value.GetType() == typeof( SqlInt16 )) 
                            extendedCode = ExtendedClrTypeCode.SqlInt16;
                        else if (Type.GetTypeCode(value.GetType()) == TypeCode.Int16)
                            extendedCode = ExtendedClrTypeCode.Int16;
                        break; 
                    case SqlDbType.TinyInt:
                        if (value.GetType() == typeof( Byte )) 
                            extendedCode = ExtendedClrTypeCode.Byte; 
                        else if (value.GetType() == typeof( SqlByte ))
                            extendedCode = ExtendedClrTypeCode.SqlByte; 
                        else if (Type.GetTypeCode(value.GetType()) == TypeCode.Byte)
                            extendedCode = ExtendedClrTypeCode.Byte;
                        break;
                    case SqlDbType.Variant: 
                        // SqlDbType doesn't help us here, call general-purpose function
                        extendedCode = DetermineExtendedTypeCode( value ); 
 
                        // Some types aren't allowed for Variants but are for the general-purpos function.
                        //  Match behavior of other types and return invalid in these cases. 
                        if ( ExtendedClrTypeCode.SqlXml == extendedCode ) {
                            extendedCode = ExtendedClrTypeCode.Invalid;
                        }
                        break; 
                    case SqlDbType.Udt:
                        // Validate UDT type if caller gave us a type to validate against 
                        if ( null == udtType || 
#if WINFSFunctionality
                                // WINFS needs inheritance to work 
                                value is udtType
#else
                                value.GetType() == udtType
#endif 
                            ) {
                            extendedCode = ExtendedClrTypeCode.Object; 
                        } 
                        else {
                            extendedCode = ExtendedClrTypeCode.Invalid; 
                        }
                        break;
                    case SqlDbType.Time:
                        if (value.GetType() == typeof(TimeSpan) && smiVersion >= SmiContextFactory.KatmaiVersion) 
                            extendedCode = ExtendedClrTypeCode.TimeSpan;
                        break; 
                    case SqlDbType.DateTimeOffset: 
                        if (value.GetType() == typeof(DateTimeOffset) && smiVersion >= SmiContextFactory.KatmaiVersion)
                            extendedCode = ExtendedClrTypeCode.DateTimeOffset; 
                        break;
#if WINFS_UTC
                    case SqlDbType.Date :
                    case SqlDbType.UtcDateTime: 
                    case SqlDbType.Time:
                        // 
                        extendedCode = ExtendedClrTypeCode.Object; 
                        break;
#endif 
                    case SqlDbType.Xml:
                        if (value.GetType() == typeof( SqlXml ))
                            extendedCode = ExtendedClrTypeCode.SqlXml;
                        else if (value.GetType() == typeof( System.String )) 
                            extendedCode = ExtendedClrTypeCode.String;
                        break; 
                    case SqlDbType.Structured: 
                        if (isMultiValued) {
                            if (value is DataTable) { 
                                extendedCode = ExtendedClrTypeCode.DataTable;
                            }
                            // Order is important, since some of these types are base types of the others.
                            //  Evaluate from most derived to parent types 
                            else if (value is IEnumerable) {
                                extendedCode = ExtendedClrTypeCode.IEnumerableOfSqlDataRecord; 
                            } 
                            else if (value is DbDataReader) {
                                extendedCode = ExtendedClrTypeCode.DbDataReader; 
                            }
                        }
                        break;
                    default: 
                        // Leave as invalid
                        break; 
                    } 
            }
 
            return extendedCode;

        }
 
        // Method to map from Type to ExtendedTypeCode
        static internal ExtendedClrTypeCode DetermineExtendedTypeCodeFromType(Type clrType) { 
            object result = __typeToExtendedTypeCodeMap[clrType]; 

            ExtendedClrTypeCode resultCode; 
            if ( null == result ) {
                resultCode = ExtendedClrTypeCode.Invalid;
            }
            else { 
                resultCode = (ExtendedClrTypeCode) result;
            } 
 
            return resultCode;
        } 

         // Returns the ExtendedClrTypeCode that describes the given value
        //
 

 
 

 



 

        static internal ExtendedClrTypeCode DetermineExtendedTypeCode( object value ) { 
            ExtendedClrTypeCode resultCode; 
            if ( null == value ) {
                resultCode = ExtendedClrTypeCode.Empty; 
            }
            else {
                resultCode = DetermineExtendedTypeCodeFromType(value.GetType());
            } 

            return resultCode; 
        } 

        // returns a sqldbtype for the given type code 
        static internal SqlDbType InferSqlDbTypeFromTypeCode( ExtendedClrTypeCode typeCode ) {
            Debug.Assert( typeCode >= ExtendedClrTypeCode.Invalid && typeCode <= ExtendedClrTypeCode.Last, "Someone added a typecode without adding support here!" );

            return __extendedTypeCodeToSqlDbTypeMap[ (int) typeCode+1 ]; 
        }
 
        // Infer SqlDbType from Type in the general case.  Katmai-only (or later) features that need to 
        //  infer types should use InferSqlDbTypeFromType_Katmai.
        static internal SqlDbType InferSqlDbTypeFromType(Type type) { 
            ExtendedClrTypeCode typeCode = DetermineExtendedTypeCodeFromType(type);
            SqlDbType returnType;
            if (ExtendedClrTypeCode.Invalid == typeCode) {
                returnType = InvalidSqlDbType;  // Return invalid type so caller can generate specific error 
            }
            else { 
                returnType = InferSqlDbTypeFromTypeCode(typeCode); 
            }
 
            return returnType;
        }

        // Inference rules changed for Katmai-or-later-only cases.  Only features that are guaranteed to be 
        //  running against Katmai and don't have backward compat issues should call this code path.
        //      example: TVP's are a new Katmai feature (no back compat issues) so can infer DATETIME2 
        //          when mapping System.DateTime from DateTable or DbDataReader.  DATETIME2 is better because 
        //          of greater range that can handle all DateTime values.
        static internal SqlDbType InferSqlDbTypeFromType_Katmai(Type type) { 
            SqlDbType returnType = InferSqlDbTypeFromType(type);
            if (SqlDbType.DateTime == returnType) {
                returnType = SqlDbType.DateTime2;
            } 
            return returnType;
        } 
 
        static internal bool IsValidForSmiVersion(SmiExtendedMetaData md, ulong smiVersion) {
            if (SmiContextFactory.LatestVersion == smiVersion) { 
                return true;
            }
            else {
                // Yukon doesn't support Structured nor the new time types 
                Debug.Assert(SmiContextFactory.YukonVersion == smiVersion, "Other versions should have been eliminated during link stage");
                return md.SqlDbType != SqlDbType.Structured && 
                        md.SqlDbType != SqlDbType.Date && 
                        md.SqlDbType != SqlDbType.DateTime2 &&
                        md.SqlDbType != SqlDbType.DateTimeOffset && 
                        md.SqlDbType != SqlDbType.Time;
            }
        }
 
        static internal SqlMetaData SmiExtendedMetaDataToSqlMetaData(SmiExtendedMetaData source) {
            if (SqlDbType.Xml == source.SqlDbType) { 
                return new SqlMetaData(source.Name, 
                    source.SqlDbType,
                    source.MaxLength, 
                    source.Precision,
                    source.Scale,
                    source.LocaleId,
                    source.CompareOptions, 
                    source.TypeSpecificNamePart1,
                    source.TypeSpecificNamePart2, 
                    source.TypeSpecificNamePart3, 
                    true,
                    source.Type); 
            }

            return new SqlMetaData(source.Name,
                source.SqlDbType, 
                source.MaxLength,
                source.Precision, 
                source.Scale, 
                source.LocaleId,
                source.CompareOptions, 
                source.Type);
        }

        // Convert SqlMetaData instance to an SmiExtendedMetaData instance. 

        internal static SmiExtendedMetaData SqlMetaDataToSmiExtendedMetaData( SqlMetaData source ) { 
            // now map everything across to the extended metadata object 
            string typeSpecificNamePart1 = null;
            string typeSpecificNamePart2 = null; 
            string typeSpecificNamePart3 = null;

            if (SqlDbType.Xml == source.SqlDbType) {
                typeSpecificNamePart1 = source.XmlSchemaCollectionDatabase; 
                typeSpecificNamePart2 = source.XmlSchemaCollectionOwningSchema;
                typeSpecificNamePart3 = source.XmlSchemaCollectionName; 
            } 
            else if (SqlDbType.Udt == source.SqlDbType) {
                // Split the input name. UdtTypeName is specified as single 3 part name. 
                // NOTE: ParseUdtTypeName throws if format is incorrect
                string typeName = source.ServerTypeName;
                if (null != typeName) {
                    String[] names = SqlParameter.ParseTypeName(typeName, true /* is for UdtTypeName */); 

                    if (1 == names.Length) { 
                        typeSpecificNamePart3 = names[0]; 
                    }
                    else if (2 == names.Length) { 
                        typeSpecificNamePart2 = names[0];
                        typeSpecificNamePart3 = names[1];
                    }
                    else if (3 == names.Length) { 
                        typeSpecificNamePart1 = names[0];
                        typeSpecificNamePart2 = names[1]; 
                        typeSpecificNamePart3 = names[2]; 
                    }
                    else { 
                        throw ADP.ArgumentOutOfRange("typeName");
                    }

                    if ((!ADP.IsEmpty(typeSpecificNamePart1) && TdsEnums.MAX_SERVERNAME < typeSpecificNamePart1.Length) 
                        || (!ADP.IsEmpty(typeSpecificNamePart2) && TdsEnums.MAX_SERVERNAME < typeSpecificNamePart2.Length)
                        || (!ADP.IsEmpty(typeSpecificNamePart3) && TdsEnums.MAX_SERVERNAME < typeSpecificNamePart3.Length)) { 
                        throw ADP.ArgumentOutOfRange("typeName"); 
                    }
                } 
            }

            return new SmiExtendedMetaData( source.SqlDbType,
                                            source.MaxLength, 
                                            source.Precision,
                                            source.Scale, 
                                            source.LocaleId, 
                                            source.CompareOptions,
                                            source.Type, 
                                            source.Name,
                                            typeSpecificNamePart1,
                                            typeSpecificNamePart2,
                                            typeSpecificNamePart3 ); 

 
        } 

 
        // compare SmiMetaData to SqlMetaData and determine if they are compatible.
        static internal bool IsCompatible(SmiMetaData firstMd, SqlMetaData secondMd) {
            return firstMd.SqlDbType == secondMd.SqlDbType &&
                    firstMd.MaxLength == secondMd.MaxLength && 
                    firstMd.Precision == secondMd.Precision &&
                    firstMd.Scale == secondMd.Scale && 
                    firstMd.CompareOptions == secondMd.CompareOptions && 
                    firstMd.LocaleId == secondMd.LocaleId &&
                    firstMd.Type == secondMd.Type && 
                    firstMd.SqlDbType != SqlDbType.Structured &&  // SqlMetaData doesn't support Structured types
                    !firstMd.IsMultiValued;  // SqlMetaData doesn't have a "multivalued" option
        }
 
        static internal long AdjustMaxLength(SqlDbType dbType, long maxLength) {
            if (SmiMetaData.UnlimitedMaxLengthIndicator != maxLength) { 
                if (maxLength < 0) { 
                    maxLength = InvalidMaxLength;
                } 

                switch(dbType) {
                    case SqlDbType.Binary:
                        if (maxLength > SmiMetaData.MaxBinaryLength) { 
                            maxLength = InvalidMaxLength;
                        } 
                        break; 
                    case SqlDbType.Char:
                        if (maxLength > SmiMetaData.MaxANSICharacters) { 
                            maxLength = InvalidMaxLength;
                        }
                        break;
                    case SqlDbType.NChar: 
                        if (maxLength > SmiMetaData.MaxUnicodeCharacters) {
                            maxLength = InvalidMaxLength; 
                        } 
                        break;
                    case SqlDbType.NVarChar: 
                        // Promote to MAX type if it won't fit in a normal type
                        if (SmiMetaData.MaxUnicodeCharacters < maxLength) {
                            maxLength = SmiMetaData.UnlimitedMaxLengthIndicator;
                        } 
                        break;
                    case SqlDbType.VarBinary: 
                        // Promote to MAX type if it won't fit in a normal type 
                        if (SmiMetaData.MaxBinaryLength < maxLength) {
                            maxLength = SmiMetaData.UnlimitedMaxLengthIndicator; 
                        }
                        break;
                    case SqlDbType.VarChar:
                        // Promote to MAX type if it won't fit in a normal type 
                        if (SmiMetaData.MaxANSICharacters < maxLength) {
                            maxLength = SmiMetaData.UnlimitedMaxLengthIndicator; 
                        } 
                        break;
                    default: 
                        break;
                }
            }
 
            return maxLength;
        } 
 
        // Extract metadata for a single DataColumn
        static internal SmiExtendedMetaData SmiMetaDataFromDataColumn(DataColumn column, DataTable parent) { 
            SqlDbType dbType = InferSqlDbTypeFromType_Katmai(column.DataType);
            if (InvalidSqlDbType == dbType) {
                throw SQL.UnsupportedColumnTypeForSqlProvider(column.ColumnName, column.DataType.Name);
            } 

            long maxLength = AdjustMaxLength(dbType, column.MaxLength); 
            if (InvalidMaxLength == maxLength) { 
                throw SQL.InvalidColumnMaxLength(column.ColumnName, maxLength);
            } 

            byte precision;
            byte scale;
            if (column.DataType == typeof(SqlDecimal)) { 

                // Must scan all values in column to determine best-fit precision & scale 
                Debug.Assert(null != parent); 
                scale = 0;
                byte nonFractionalPrecision = 0; // finds largest non-Fractional portion of precision 
                foreach (DataRow row in parent.Rows) {
                    object obj = row[column];
                    if (!(obj is DBNull)) {
                        SqlDecimal value = (SqlDecimal) obj; 
                        if (!value.IsNull) {
                            byte tempNonFractPrec = checked((byte) (value.Precision - value.Scale)); 
                            if (tempNonFractPrec > nonFractionalPrecision) { 
                                nonFractionalPrecision = tempNonFractPrec;
                            } 

                            if (value.Scale > scale) {
                                scale = value.Scale;
                            } 
                        }
                    } 
                } 

                precision = checked((byte)(nonFractionalPrecision + scale)); 

                if (SqlDecimal.MaxPrecision < precision) {
                    throw SQL.InvalidTableDerivedPrecisionForTvp(column.ColumnName, precision);
                } 
                else if (0 == precision) {
                    precision = 1; 
                } 
            }
            else if (dbType == SqlDbType.DateTime2 || dbType == SqlDbType.DateTimeOffset || dbType == SqlDbType.Time) { 
                // Time types care about scale, too.  But have to infer maximums for these.
                precision = 0;
                scale = SmiMetaData.DefaultTime.Scale;
            } 
            else if (dbType == SqlDbType.Decimal) {
                // Must scan all values in column to determine best-fit precision & scale 
                Debug.Assert(null != parent); 
                scale = 0;
                byte nonFractionalPrecision = 0; // finds largest non-Fractional portion of precision 
                foreach (DataRow row in parent.Rows) {
                    object obj = row[column];
                    if (!(obj is DBNull)) {
                        SqlDecimal value = (SqlDecimal)(Decimal)obj; 
                        byte tempNonFractPrec = checked((byte)(value.Precision - value.Scale));
                        if (tempNonFractPrec > nonFractionalPrecision) { 
                            nonFractionalPrecision = tempNonFractPrec; 
                        }
 
                        if (value.Scale > scale) {
                            scale = value.Scale;
                        }
                    } 
                }
 
                precision = checked((byte)(nonFractionalPrecision + scale)); 

                if (SqlDecimal.MaxPrecision < precision) { 
                    throw SQL.InvalidTableDerivedPrecisionForTvp(column.ColumnName, precision);
                }
                else if (0 == precision) {
                    precision = 1; 
                }
            } 
            else { 
                precision = 0;
                scale = 0; 
            }

            return new SmiExtendedMetaData(
                                        dbType, 
                                        maxLength,
                                        precision, 
                                        scale, 
                                        column.Locale.LCID,
                                        SmiMetaData.DefaultNVarChar.CompareOptions, 
                                        column.DataType,
                                        false,  // no support for multi-valued columns in a TVP yet
                                        null,   // no support for structured columns yet
                                        null,   // no support for structured columns yet 
                                        column.ColumnName,
                                        null, 
                                        null, 
                                        null);
        } 

        // Map SmiMetaData from a schema table.
        //  DEVNOTE: since we're using SchemaTable, we can assume that we aren't directly using a SqlDataReader
        //      so we don't support the Sql-specific stuff, like collation 
        static internal SmiExtendedMetaData SmiMetaDataFromSchemaTableRow(DataRow schemaRow) {
            // One way or another, we'll need column name, so put it in a local now to shorten code later. 
            string colName = ""; 
            object temp = schemaRow[SchemaTableColumn.ColumnName];
            if (DBNull.Value != temp) { 
                colName = (string)temp;
            }

            // Determine correct SqlDbType. 
            temp = schemaRow[SchemaTableColumn.DataType];
            if (DBNull.Value == temp) { 
                throw SQL.NullSchemaTableDataTypeNotSupported(colName); 
            }
            Type colType = (Type)temp; 
            SqlDbType colDbType = InferSqlDbTypeFromType_Katmai(colType);
            if (InvalidSqlDbType == colDbType) {
                // Unknown through standard mapping, use VarBinary for columns that are Object typed, otherwise error
                if (typeof(object) == colType) { 
                    colDbType = SqlDbType.VarBinary;
                } 
                else { 
                    throw SQL.UnsupportedColumnTypeForSqlProvider(colName, colType.ToString());
                } 
            }

            // Determine metadata modifier values per type (maxlength, precision, scale, etc)
            long maxLength = 0; 
            byte precision = 0;
            byte scale = 0; 
            switch (colDbType) { 
                case SqlDbType.BigInt:
                case SqlDbType.Bit: 
                case SqlDbType.DateTime:
                case SqlDbType.Float:
                case SqlDbType.Image:
                case SqlDbType.Int: 
                case SqlDbType.Money:
                case SqlDbType.NText: 
                case SqlDbType.Real: 
                case SqlDbType.UniqueIdentifier:
                case SqlDbType.SmallDateTime: 
                case SqlDbType.SmallInt:
                case SqlDbType.SmallMoney:
                case SqlDbType.Text:
                case SqlDbType.Timestamp: 
                case SqlDbType.TinyInt:
                case SqlDbType.Variant: 
                case SqlDbType.Xml: 
                case SqlDbType.Date:
                    // These types require no  metadata modifies 
                    break;
                case SqlDbType.Binary:
                case SqlDbType.VarBinary:
                    // These types need a binary max length 
                    temp = schemaRow[SchemaTableColumn.ColumnSize];
                    if (DBNull.Value == temp) { 
                        // source isn't specifying a size, so assume the worst 
                        if (SqlDbType.Binary == colDbType) {
                            maxLength = SmiMetaData.MaxBinaryLength; 
                        }
                        else {
                            maxLength = SmiMetaData.UnlimitedMaxLengthIndicator;
                        } 
                    }
                    else { 
                        // We (should) have a valid maxlength, so use it. 
                        maxLength = Convert.ToInt64(temp, null);
 
                        // Max length must be 0 to MaxBinaryLength or it can be UnlimitedMAX if type is varbinary
                        //   If it's greater than MaxBinaryLength, just promote it to UnlimitedMAX, if possible
                        if (maxLength > SmiMetaData.MaxBinaryLength) {
                            maxLength = SmiMetaData.UnlimitedMaxLengthIndicator; 
                        }
 
                        if ((maxLength < 0 && 
                                (maxLength != SmiMetaData.UnlimitedMaxLengthIndicator ||
                                 SqlDbType.Binary == colDbType))) { 
                            throw SQL.InvalidColumnMaxLength(colName, maxLength);
                        }
                    }
                    break; 
                case SqlDbType.Char:
                case SqlDbType.VarChar: 
                    // These types need an ANSI max length 
                    temp = schemaRow[SchemaTableColumn.ColumnSize];
                    if (DBNull.Value == temp) { 
                        // source isn't specifying a size, so assume the worst
                        if (SqlDbType.Char == colDbType) {
                            maxLength = SmiMetaData.MaxANSICharacters;
                        } 
                        else {
                            maxLength = SmiMetaData.UnlimitedMaxLengthIndicator; 
                        } 
                    }
                    else { 
                        // We (should) have a valid maxlength, so use it.
                        maxLength = Convert.ToInt64(temp, null);

                        // Max length must be 0 to MaxANSICharacters or it can be UnlimitedMAX if type is varbinary 
                        //   If it's greater than MaxANSICharacters, just promote it to UnlimitedMAX, if possible
                        if (maxLength > SmiMetaData.MaxANSICharacters) { 
                            maxLength = SmiMetaData.UnlimitedMaxLengthIndicator; 
                        }
 
                        if ((maxLength < 0 &&
                                (maxLength != SmiMetaData.UnlimitedMaxLengthIndicator ||
                                 SqlDbType.Char == colDbType))) {
                            throw SQL.InvalidColumnMaxLength(colName, maxLength); 
                        }
                    } 
                    break; 
                case SqlDbType.NChar:
                case SqlDbType.NVarChar: 
                    // These types need a unicode max length
                    temp = schemaRow[SchemaTableColumn.ColumnSize];
                    if (DBNull.Value == temp) {
                        // source isn't specifying a size, so assume the worst 
                        if (SqlDbType.NChar == colDbType) {
                            maxLength = SmiMetaData.MaxUnicodeCharacters; 
                        } 
                        else {
                            maxLength = SmiMetaData.UnlimitedMaxLengthIndicator; 
                        }
                    }
                    else {
                        // We (should) have a valid maxlength, so use it. 
                        maxLength = Convert.ToInt64(temp, null);
 
                        // Max length must be 0 to MaxUnicodeCharacters or it can be UnlimitedMAX if type is varbinary 
                        //   If it's greater than MaxUnicodeCharacters, just promote it to UnlimitedMAX, if possible
                        if (maxLength > SmiMetaData.MaxUnicodeCharacters) { 
                            maxLength = SmiMetaData.UnlimitedMaxLengthIndicator;
                        }

                        if ((maxLength < 0 && 
                                (maxLength != SmiMetaData.UnlimitedMaxLengthIndicator ||
                                 SqlDbType.NChar == colDbType))) { 
                            throw SQL.InvalidColumnMaxLength(colName, maxLength); 
                        }
                    } 
                    break;
                case SqlDbType.Decimal:
                    // Decimal requires precision and scale
                    temp = schemaRow[SchemaTableColumn.NumericPrecision]; 
                    if (DBNull.Value == temp) {
                        precision = SmiMetaData.DefaultDecimal.Precision; 
                    } 
                    else {
                        precision = Convert.ToByte(temp, null); 
                    }

                    temp = schemaRow[SchemaTableColumn.NumericScale];
                    if (DBNull.Value == temp) { 
                        scale = SmiMetaData.DefaultDecimal.Scale;
                    } 
                    else { 
                        scale = Convert.ToByte(temp, null);
                    } 

                    if (precision < SmiMetaData.MinPrecision ||
                            precision > SqlDecimal.MaxPrecision ||
                            scale < SmiMetaData.MinScale || 
                            scale > SqlDecimal.MaxScale ||
                            scale > precision) { 
                        throw SQL.InvalidColumnPrecScale(); 
                    }
                    break; 
                case SqlDbType.Time:
                case SqlDbType.DateTime2:
                case SqlDbType.DateTimeOffset:
                    // requires scale 
                    temp = schemaRow[SchemaTableColumn.NumericScale];
                    if (DBNull.Value == temp) { 
                        scale = SmiMetaData.DefaultTime.Scale; 
                    }
                    else { 
                        scale = Convert.ToByte(temp, null);
                    }

                    if (scale > SmiMetaData.MaxTimeScale) { 
                        throw SQL.InvalidColumnPrecScale();
                    } 
                    else if (scale < 0) { 
                        scale = SmiMetaData.DefaultTime.Scale;
                    } 
                    break;
                case SqlDbType.Udt:
                case SqlDbType.Structured:
                default: 
                    // These types are not supported from SchemaTable
                    throw SQL.UnsupportedColumnTypeForSqlProvider(colName, colType.ToString()); 
            } 

            return new SmiExtendedMetaData( 
                                        colDbType,
                                        maxLength,
                                        precision,
                                        scale, 
                                        System.Globalization.CultureInfo.CurrentCulture.LCID,
                                        SmiMetaData.GetDefaultForType(colDbType).CompareOptions, 
                                        null,   // no support for UDTs from SchemaTable 
                                        false,  // no support for multi-valued columns in a TVP yet
                                        null,   // no support for structured columns yet 
                                        null,   // no support for structured columns yet
                                        colName,
                                        null,
                                        null, 
                                        null);
        } 
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// [....] 
// [....]
//----------------------------------------------------------------------------- 
 
namespace Microsoft.SqlServer.Server {
 
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Data; 
    using System.Data.Sql;
    using System.Data.Common; 
    using System.Data.SqlClient; 
    using System.Data.SqlTypes;
    using System.Diagnostics; 


    // Utilities for manipulating smi-related metadata.
    // 
    //  THIS CLASS IS BUILT ON TOP OF THE SMI INTERFACE -- SMI SHOULD NOT DEPEND ON IT!
    // 
    //  These are all based off of knowing the clr type of the value 
    //  as an ExtendedClrTypeCode enum for rapid access (lookup in static array is best, if possible).
    internal class MetaDataUtilsSmi { 

        internal const SqlDbType    InvalidSqlDbType = (SqlDbType) (-1);
        internal const long         InvalidMaxLength = -2;
 
        // Standard type inference map to get SqlDbType when all you know is the value's type (typecode)
        //  This map's index is off by one (add one to typecode locate correct entry) in order 
        //  support ExtendedSqlDbType.Invalid 
        // ONLY ACCESS THIS ARRAY FROM InferSqlDbTypeFromTypeCode!!!
        static readonly SqlDbType[] __extendedTypeCodeToSqlDbTypeMap = { 
            InvalidSqlDbType,               // Invalid extended type code
            SqlDbType.Bit,                  // System.Boolean
            SqlDbType.TinyInt,              // System.Byte
            SqlDbType.NVarChar,             // System.Char 
            SqlDbType.DateTime,             // System.DateTime
            InvalidSqlDbType,               // System.DBNull doesn't have an inferable SqlDbType 
            SqlDbType.Decimal,              // System.Decimal 
            SqlDbType.Float,                // System.Double
            InvalidSqlDbType,               // null reference doesn't have an inferable SqlDbType 
            SqlDbType.SmallInt,             // System.Int16
            SqlDbType.Int,                  // System.Int32
            SqlDbType.BigInt,               // System.Int64
            InvalidSqlDbType,               // System.SByte doesn't have an inferable SqlDbType 
            SqlDbType.Real,                 // System.Single
            SqlDbType.NVarChar,             // System.String 
            InvalidSqlDbType,               // System.UInt16 doesn't have an inferable SqlDbType 
            InvalidSqlDbType,               // System.UInt32 doesn't have an inferable SqlDbType
            InvalidSqlDbType,               // System.UInt64 doesn't have an inferable SqlDbType 
            InvalidSqlDbType,               // System.Object doesn't have an inferable SqlDbType
            SqlDbType.VarBinary,            // System.ByteArray
            SqlDbType.NVarChar,             // System.CharArray
            SqlDbType.UniqueIdentifier,     // System.Guid 
            SqlDbType.VarBinary,            // System.Data.SqlTypes.SqlBinary
            SqlDbType.Bit,                  // System.Data.SqlTypes.SqlBoolean 
            SqlDbType.TinyInt,              // System.Data.SqlTypes.SqlByte 
            SqlDbType.DateTime,             // System.Data.SqlTypes.SqlDateTime
            SqlDbType.Float,                // System.Data.SqlTypes.SqlDouble 
            SqlDbType.UniqueIdentifier,     // System.Data.SqlTypes.SqlGuid
            SqlDbType.SmallInt,             // System.Data.SqlTypes.SqlInt16
            SqlDbType.Int,                  // System.Data.SqlTypes.SqlInt32
            SqlDbType.BigInt,               // System.Data.SqlTypes.SqlInt64 
            SqlDbType.Money,                // System.Data.SqlTypes.SqlMoney
            SqlDbType.Decimal,              // System.Data.SqlTypes.SqlDecimal 
            SqlDbType.Real,                 // System.Data.SqlTypes.SqlSingle 
            SqlDbType.NVarChar,             // System.Data.SqlTypes.SqlString
            SqlDbType.NVarChar,             // System.Data.SqlTypes.SqlChars 
            SqlDbType.VarBinary,            // System.Data.SqlTypes.SqlBytes
            SqlDbType.Xml,                  // System.Data.SqlTypes.SqlXml
            SqlDbType.Structured,           // System.Data.DataTable
            SqlDbType.Structured,           // System.Collections.IEnumerable, used for TVPs it must return IDataRecord 
            SqlDbType.Structured,           // System.Collections.Generic.IEnumerable
            SqlDbType.Time,                 // System.TimeSpan 
            SqlDbType.DateTimeOffset,       // System.DateTimeOffset 
        };
 
        // Hash table to map from clr type object to ExtendedClrTypeCodeMap enum
        //  ONLY ACCESS THIS HASH TABLE FROM DetermineExtendedTypeCode METHOD!!!  (and class ctor for setup)
        static readonly Hashtable __typeToExtendedTypeCodeMap;
 

        // class ctor 
        static MetaDataUtilsSmi() { 
            // set up type mapping hash table
            //  keep this initialization list in the same order as ExtendedClrTypeCode for ease in validating! 
            Hashtable ht = new Hashtable(42);
            ht.Add( typeof( System.Boolean ),       ExtendedClrTypeCode.Boolean );
            ht.Add( typeof( System.Byte ),          ExtendedClrTypeCode.Byte );
            ht.Add( typeof( System.Char ),          ExtendedClrTypeCode.Char ); 
            ht.Add( typeof( System.DateTime ),      ExtendedClrTypeCode.DateTime );
            ht.Add( typeof( System.DBNull ),        ExtendedClrTypeCode.DBNull ); 
            ht.Add( typeof( System.Decimal ),       ExtendedClrTypeCode.Decimal ); 
            ht.Add( typeof( System.Double ),        ExtendedClrTypeCode.Double );
            // lookup code will have to special-case null-ref anyway, so don't bother adding ExtendedTypeCode.Empty to the table 
            ht.Add( typeof( System.Int16 ),         ExtendedClrTypeCode.Int16 );
            ht.Add( typeof( System.Int32 ),         ExtendedClrTypeCode.Int32 );
            ht.Add( typeof( System.Int64 ),         ExtendedClrTypeCode.Int64 );
            ht.Add( typeof( System.SByte ),         ExtendedClrTypeCode.SByte ); 
            ht.Add( typeof( System.Single ),        ExtendedClrTypeCode.Single );
            ht.Add( typeof( System.String ),        ExtendedClrTypeCode.String ); 
            ht.Add( typeof( System.UInt16 ),        ExtendedClrTypeCode.UInt16 ); 
            ht.Add( typeof( System.UInt32 ),        ExtendedClrTypeCode.UInt32 );
            ht.Add( typeof( System.UInt64 ),        ExtendedClrTypeCode.UInt64 ); 
            ht.Add( typeof( System.Object ),        ExtendedClrTypeCode.Object );
            ht.Add( typeof( System.Byte[] ),        ExtendedClrTypeCode.ByteArray );
            ht.Add( typeof( System.Char[] ),        ExtendedClrTypeCode.CharArray );
            ht.Add( typeof( System.Guid ),          ExtendedClrTypeCode.Guid ); 
            ht.Add( typeof( SqlBinary ),            ExtendedClrTypeCode.SqlBinary );
            ht.Add( typeof( SqlBoolean ),           ExtendedClrTypeCode.SqlBoolean ); 
            ht.Add( typeof( SqlByte ),              ExtendedClrTypeCode.SqlByte ); 
            ht.Add( typeof( SqlDateTime ),          ExtendedClrTypeCode.SqlDateTime );
            ht.Add( typeof( SqlDouble ),            ExtendedClrTypeCode.SqlDouble ); 
            ht.Add( typeof( SqlGuid ),              ExtendedClrTypeCode.SqlGuid );
            ht.Add( typeof( SqlInt16 ),             ExtendedClrTypeCode.SqlInt16 );
            ht.Add( typeof( SqlInt32 ),             ExtendedClrTypeCode.SqlInt32 );
            ht.Add( typeof( SqlInt64 ),             ExtendedClrTypeCode.SqlInt64 ); 
            ht.Add( typeof( SqlMoney ),             ExtendedClrTypeCode.SqlMoney );
            ht.Add( typeof( SqlDecimal ),           ExtendedClrTypeCode.SqlDecimal ); 
            ht.Add( typeof( SqlSingle ),            ExtendedClrTypeCode.SqlSingle ); 
            ht.Add( typeof( SqlString ),            ExtendedClrTypeCode.SqlString );
            ht.Add( typeof( SqlChars ),             ExtendedClrTypeCode.SqlChars ); 
            ht.Add( typeof( SqlBytes ),             ExtendedClrTypeCode.SqlBytes );
            ht.Add( typeof( SqlXml ),               ExtendedClrTypeCode.SqlXml );
            ht.Add( typeof( DataTable ),            ExtendedClrTypeCode.DataTable );
            ht.Add( typeof( DbDataReader ),         ExtendedClrTypeCode.DbDataReader ); 
            ht.Add( typeof( IEnumerable ),          ExtendedClrTypeCode.IEnumerableOfSqlDataRecord );
            ht.Add( typeof( System.TimeSpan ),      ExtendedClrTypeCode.TimeSpan ); 
            ht.Add( typeof( System.DateTimeOffset ),               ExtendedClrTypeCode.DateTimeOffset ); 
            __typeToExtendedTypeCodeMap = ht;
        } 


        internal static bool IsCharOrXmlType(SqlDbType type) {
            return  IsUnicodeType(type) || 
                    IsAnsiType(type) ||
                    type == SqlDbType.Xml; 
        } 

        internal static bool IsUnicodeType(SqlDbType type) { 
            return  type == SqlDbType.NChar ||
                    type == SqlDbType.NVarChar ||
                    type == SqlDbType.NText;
        } 

        internal static bool IsAnsiType(SqlDbType type) { 
            return  type == SqlDbType.Char || 
                    type == SqlDbType.VarChar ||
                    type == SqlDbType.Text; 
        }

        internal static bool IsBinaryType(SqlDbType type) {
            return  type == SqlDbType.Binary || 
                    type == SqlDbType.VarBinary ||
                    type == SqlDbType.Image; 
        } 

        // Does this type use PLP format values? 
        internal static bool IsPlpFormat(SmiMetaData metaData) {
            return  metaData.MaxLength == SmiMetaData.UnlimitedMaxLengthIndicator ||
                    metaData.SqlDbType == SqlDbType.Image ||
                    metaData.SqlDbType == SqlDbType.NText || 
                    metaData.SqlDbType == SqlDbType.Text ||
                    metaData.SqlDbType == SqlDbType.Udt; 
        } 

 

        // If we know we're only going to use this object to assign to a specific SqlDbType back end object,
        //  we can save some processing time by only checking for the few valid types that can be assigned to the dbType.
        //  This assumes a switch statement over SqlDbType is faster than getting the ClrTypeCode and iterating over a 
        //  series of if statements, or using a hash table.
        // NOTE: the form of these checks is taking advantage of a feature of the JIT compiler that is supposed to 
        //      optimize checks of the form '(xxx.GetType() == typeof( YYY ))'.  The JIT team claimed at one point that 
        //      this doesn't even instantiate a Type instance, thus was the fastest method for individual comparisions.
        //      Given that there's a known SqlDbType, thus a minimal number of comparisions, it's likely this is faster 
        //      than the other approaches considered (both GetType().GetTypeCode() switch and hash table using Type keys
        //      must instantiate a Type object.  The typecode switch also degenerates into a large if-then-else for
        //      all but the primitive clr types.
        internal static ExtendedClrTypeCode DetermineExtendedTypeCodeForUseWithSqlDbType( 
                SqlDbType   dbType,
                bool        isMultiValued, 
                object      value, 
                Type        udtType,
                ulong       smiVersion) { 
            ExtendedClrTypeCode extendedCode = ExtendedClrTypeCode.Invalid;

            // fast-track null, which is valid for all types
            if ( null == value ) { 
                extendedCode = ExtendedClrTypeCode.Empty;
            } 
            else if ( DBNull.Value == value ) { 
                extendedCode = ExtendedClrTypeCode.DBNull;
            } 
            else {
                switch(dbType)
                    {
                    case SqlDbType.BigInt: 
                        if (value.GetType() == typeof(Int64))
                            extendedCode = ExtendedClrTypeCode.Int64; 
                        else if (value.GetType() == typeof(SqlInt64)) 
                            extendedCode = ExtendedClrTypeCode.SqlInt64;
                        else if (Type.GetTypeCode(value.GetType()) == TypeCode.Int64) 
                            extendedCode = ExtendedClrTypeCode.Int64;
                        break;
                    case SqlDbType.Binary:
                    case SqlDbType.VarBinary: 
                    case SqlDbType.Image:
                    case SqlDbType.Timestamp: 
                        if (value.GetType() == typeof( byte[] )) 
                            extendedCode = ExtendedClrTypeCode.ByteArray;
                        else if (value.GetType() == typeof( SqlBinary )) 
                            extendedCode = ExtendedClrTypeCode.SqlBinary;
                        else if (value.GetType() == typeof( SqlBytes ))
                            extendedCode = ExtendedClrTypeCode.SqlBytes;
                        break; 
                    case SqlDbType.Bit:
                        if (value.GetType() == typeof( bool )) 
                            extendedCode = ExtendedClrTypeCode.Boolean; 
                        else if (value.GetType() == typeof( SqlBoolean ))
                            extendedCode = ExtendedClrTypeCode.SqlBoolean; 
                        else if (Type.GetTypeCode(value.GetType()) == TypeCode.Boolean)
                            extendedCode = ExtendedClrTypeCode.Boolean;
                        break;
                    case SqlDbType.Char: 
                    case SqlDbType.NChar:
                    case SqlDbType.NText: 
                    case SqlDbType.NVarChar: 
                    case SqlDbType.Text:
                    case SqlDbType.VarChar: 
                        if (value.GetType() == typeof( string ))
                            extendedCode = ExtendedClrTypeCode.String;
                        else if (value.GetType() == typeof( SqlString ))
                            extendedCode = ExtendedClrTypeCode.SqlString; 
                        else if (value.GetType() == typeof( char[] ))
                            extendedCode = ExtendedClrTypeCode.CharArray; 
                        else if (value.GetType() == typeof( SqlChars )) 
                            extendedCode = ExtendedClrTypeCode.SqlChars;
                        else if (value.GetType() == typeof( char )) 
                            extendedCode = ExtendedClrTypeCode.Char;
                        else if (Type.GetTypeCode(value.GetType()) == TypeCode.Char)
                            extendedCode = ExtendedClrTypeCode.Char;
                        else if (Type.GetTypeCode(value.GetType()) == TypeCode.String) 
                            extendedCode = ExtendedClrTypeCode.String;
                        break; 
                    case SqlDbType.Date: 
                    case SqlDbType.DateTime2:
                        if (smiVersion >= SmiContextFactory.KatmaiVersion) { 
                            goto case SqlDbType.DateTime;
                        }
                        break;
                    case SqlDbType.DateTime: 
                    case SqlDbType.SmallDateTime:
                        if (value.GetType() == typeof( DateTime )) 
                            extendedCode = ExtendedClrTypeCode.DateTime; 
                        else if (value.GetType() == typeof( SqlDateTime ))
                            extendedCode = ExtendedClrTypeCode.SqlDateTime; 
                        else if (Type.GetTypeCode(value.GetType()) == TypeCode.DateTime)
                            extendedCode = ExtendedClrTypeCode.DateTime;
                        break;
                    case SqlDbType.Decimal: 
                        if (value.GetType() == typeof( Decimal ))
                            extendedCode = ExtendedClrTypeCode.Decimal; 
                        else if (value.GetType() == typeof( SqlDecimal )) 
                            extendedCode = ExtendedClrTypeCode.SqlDecimal;
                        else if (Type.GetTypeCode(value.GetType()) == TypeCode.Decimal) 
                            extendedCode = ExtendedClrTypeCode.Decimal;
                        break;
                    case SqlDbType.Real:
                        if (value.GetType() == typeof( Single )) 
                            extendedCode = ExtendedClrTypeCode.Single;
                        else if (value.GetType() == typeof( SqlSingle )) 
                            extendedCode = ExtendedClrTypeCode.SqlSingle; 
                        else if (Type.GetTypeCode(value.GetType()) == TypeCode.Single)
                            extendedCode = ExtendedClrTypeCode.Single; 
                        break;
                    case SqlDbType.Int:
                        if (value.GetType() == typeof( Int32 ))
                            extendedCode = ExtendedClrTypeCode.Int32; 
                        else if (value.GetType() == typeof( SqlInt32 ))
                            extendedCode = ExtendedClrTypeCode.SqlInt32; 
                        else if (Type.GetTypeCode(value.GetType()) == TypeCode.Int32) 
                            extendedCode = ExtendedClrTypeCode.Int32;
                        break; 
                    case SqlDbType.Money:
                    case SqlDbType.SmallMoney:
                        if (value.GetType() == typeof( SqlMoney ))
                            extendedCode = ExtendedClrTypeCode.SqlMoney; 
                        else if (value.GetType() == typeof( Decimal ))
                            extendedCode = ExtendedClrTypeCode.Decimal; 
                        else if (Type.GetTypeCode(value.GetType()) == TypeCode.Decimal) 
                            extendedCode = ExtendedClrTypeCode.Decimal;
                        break; 
                    case SqlDbType.Float:
                        if (value.GetType() == typeof( SqlDouble ))
                            extendedCode = ExtendedClrTypeCode.SqlDouble;
                        else if (value.GetType() == typeof( Double )) 
                            extendedCode = ExtendedClrTypeCode.Double;
                        else if (Type.GetTypeCode(value.GetType()) == TypeCode.Double) 
                            extendedCode = ExtendedClrTypeCode.Double; 
                        break;
                    case SqlDbType.UniqueIdentifier: 
                        if (value.GetType() == typeof( SqlGuid ))
                            extendedCode = ExtendedClrTypeCode.SqlGuid;
                        else if (value.GetType() == typeof( Guid ))
                            extendedCode = ExtendedClrTypeCode.Guid; 
                        break;
                    case SqlDbType.SmallInt: 
                        if (value.GetType() == typeof( Int16 )) 
                            extendedCode = ExtendedClrTypeCode.Int16;
                        else if (value.GetType() == typeof( SqlInt16 )) 
                            extendedCode = ExtendedClrTypeCode.SqlInt16;
                        else if (Type.GetTypeCode(value.GetType()) == TypeCode.Int16)
                            extendedCode = ExtendedClrTypeCode.Int16;
                        break; 
                    case SqlDbType.TinyInt:
                        if (value.GetType() == typeof( Byte )) 
                            extendedCode = ExtendedClrTypeCode.Byte; 
                        else if (value.GetType() == typeof( SqlByte ))
                            extendedCode = ExtendedClrTypeCode.SqlByte; 
                        else if (Type.GetTypeCode(value.GetType()) == TypeCode.Byte)
                            extendedCode = ExtendedClrTypeCode.Byte;
                        break;
                    case SqlDbType.Variant: 
                        // SqlDbType doesn't help us here, call general-purpose function
                        extendedCode = DetermineExtendedTypeCode( value ); 
 
                        // Some types aren't allowed for Variants but are for the general-purpos function.
                        //  Match behavior of other types and return invalid in these cases. 
                        if ( ExtendedClrTypeCode.SqlXml == extendedCode ) {
                            extendedCode = ExtendedClrTypeCode.Invalid;
                        }
                        break; 
                    case SqlDbType.Udt:
                        // Validate UDT type if caller gave us a type to validate against 
                        if ( null == udtType || 
#if WINFSFunctionality
                                // WINFS needs inheritance to work 
                                value is udtType
#else
                                value.GetType() == udtType
#endif 
                            ) {
                            extendedCode = ExtendedClrTypeCode.Object; 
                        } 
                        else {
                            extendedCode = ExtendedClrTypeCode.Invalid; 
                        }
                        break;
                    case SqlDbType.Time:
                        if (value.GetType() == typeof(TimeSpan) && smiVersion >= SmiContextFactory.KatmaiVersion) 
                            extendedCode = ExtendedClrTypeCode.TimeSpan;
                        break; 
                    case SqlDbType.DateTimeOffset: 
                        if (value.GetType() == typeof(DateTimeOffset) && smiVersion >= SmiContextFactory.KatmaiVersion)
                            extendedCode = ExtendedClrTypeCode.DateTimeOffset; 
                        break;
#if WINFS_UTC
                    case SqlDbType.Date :
                    case SqlDbType.UtcDateTime: 
                    case SqlDbType.Time:
                        // 
                        extendedCode = ExtendedClrTypeCode.Object; 
                        break;
#endif 
                    case SqlDbType.Xml:
                        if (value.GetType() == typeof( SqlXml ))
                            extendedCode = ExtendedClrTypeCode.SqlXml;
                        else if (value.GetType() == typeof( System.String )) 
                            extendedCode = ExtendedClrTypeCode.String;
                        break; 
                    case SqlDbType.Structured: 
                        if (isMultiValued) {
                            if (value is DataTable) { 
                                extendedCode = ExtendedClrTypeCode.DataTable;
                            }
                            // Order is important, since some of these types are base types of the others.
                            //  Evaluate from most derived to parent types 
                            else if (value is IEnumerable) {
                                extendedCode = ExtendedClrTypeCode.IEnumerableOfSqlDataRecord; 
                            } 
                            else if (value is DbDataReader) {
                                extendedCode = ExtendedClrTypeCode.DbDataReader; 
                            }
                        }
                        break;
                    default: 
                        // Leave as invalid
                        break; 
                    } 
            }
 
            return extendedCode;

        }
 
        // Method to map from Type to ExtendedTypeCode
        static internal ExtendedClrTypeCode DetermineExtendedTypeCodeFromType(Type clrType) { 
            object result = __typeToExtendedTypeCodeMap[clrType]; 

            ExtendedClrTypeCode resultCode; 
            if ( null == result ) {
                resultCode = ExtendedClrTypeCode.Invalid;
            }
            else { 
                resultCode = (ExtendedClrTypeCode) result;
            } 
 
            return resultCode;
        } 

         // Returns the ExtendedClrTypeCode that describes the given value
        //
 

 
 

 



 

        static internal ExtendedClrTypeCode DetermineExtendedTypeCode( object value ) { 
            ExtendedClrTypeCode resultCode; 
            if ( null == value ) {
                resultCode = ExtendedClrTypeCode.Empty; 
            }
            else {
                resultCode = DetermineExtendedTypeCodeFromType(value.GetType());
            } 

            return resultCode; 
        } 

        // returns a sqldbtype for the given type code 
        static internal SqlDbType InferSqlDbTypeFromTypeCode( ExtendedClrTypeCode typeCode ) {
            Debug.Assert( typeCode >= ExtendedClrTypeCode.Invalid && typeCode <= ExtendedClrTypeCode.Last, "Someone added a typecode without adding support here!" );

            return __extendedTypeCodeToSqlDbTypeMap[ (int) typeCode+1 ]; 
        }
 
        // Infer SqlDbType from Type in the general case.  Katmai-only (or later) features that need to 
        //  infer types should use InferSqlDbTypeFromType_Katmai.
        static internal SqlDbType InferSqlDbTypeFromType(Type type) { 
            ExtendedClrTypeCode typeCode = DetermineExtendedTypeCodeFromType(type);
            SqlDbType returnType;
            if (ExtendedClrTypeCode.Invalid == typeCode) {
                returnType = InvalidSqlDbType;  // Return invalid type so caller can generate specific error 
            }
            else { 
                returnType = InferSqlDbTypeFromTypeCode(typeCode); 
            }
 
            return returnType;
        }

        // Inference rules changed for Katmai-or-later-only cases.  Only features that are guaranteed to be 
        //  running against Katmai and don't have backward compat issues should call this code path.
        //      example: TVP's are a new Katmai feature (no back compat issues) so can infer DATETIME2 
        //          when mapping System.DateTime from DateTable or DbDataReader.  DATETIME2 is better because 
        //          of greater range that can handle all DateTime values.
        static internal SqlDbType InferSqlDbTypeFromType_Katmai(Type type) { 
            SqlDbType returnType = InferSqlDbTypeFromType(type);
            if (SqlDbType.DateTime == returnType) {
                returnType = SqlDbType.DateTime2;
            } 
            return returnType;
        } 
 
        static internal bool IsValidForSmiVersion(SmiExtendedMetaData md, ulong smiVersion) {
            if (SmiContextFactory.LatestVersion == smiVersion) { 
                return true;
            }
            else {
                // Yukon doesn't support Structured nor the new time types 
                Debug.Assert(SmiContextFactory.YukonVersion == smiVersion, "Other versions should have been eliminated during link stage");
                return md.SqlDbType != SqlDbType.Structured && 
                        md.SqlDbType != SqlDbType.Date && 
                        md.SqlDbType != SqlDbType.DateTime2 &&
                        md.SqlDbType != SqlDbType.DateTimeOffset && 
                        md.SqlDbType != SqlDbType.Time;
            }
        }
 
        static internal SqlMetaData SmiExtendedMetaDataToSqlMetaData(SmiExtendedMetaData source) {
            if (SqlDbType.Xml == source.SqlDbType) { 
                return new SqlMetaData(source.Name, 
                    source.SqlDbType,
                    source.MaxLength, 
                    source.Precision,
                    source.Scale,
                    source.LocaleId,
                    source.CompareOptions, 
                    source.TypeSpecificNamePart1,
                    source.TypeSpecificNamePart2, 
                    source.TypeSpecificNamePart3, 
                    true,
                    source.Type); 
            }

            return new SqlMetaData(source.Name,
                source.SqlDbType, 
                source.MaxLength,
                source.Precision, 
                source.Scale, 
                source.LocaleId,
                source.CompareOptions, 
                source.Type);
        }

        // Convert SqlMetaData instance to an SmiExtendedMetaData instance. 

        internal static SmiExtendedMetaData SqlMetaDataToSmiExtendedMetaData( SqlMetaData source ) { 
            // now map everything across to the extended metadata object 
            string typeSpecificNamePart1 = null;
            string typeSpecificNamePart2 = null; 
            string typeSpecificNamePart3 = null;

            if (SqlDbType.Xml == source.SqlDbType) {
                typeSpecificNamePart1 = source.XmlSchemaCollectionDatabase; 
                typeSpecificNamePart2 = source.XmlSchemaCollectionOwningSchema;
                typeSpecificNamePart3 = source.XmlSchemaCollectionName; 
            } 
            else if (SqlDbType.Udt == source.SqlDbType) {
                // Split the input name. UdtTypeName is specified as single 3 part name. 
                // NOTE: ParseUdtTypeName throws if format is incorrect
                string typeName = source.ServerTypeName;
                if (null != typeName) {
                    String[] names = SqlParameter.ParseTypeName(typeName, true /* is for UdtTypeName */); 

                    if (1 == names.Length) { 
                        typeSpecificNamePart3 = names[0]; 
                    }
                    else if (2 == names.Length) { 
                        typeSpecificNamePart2 = names[0];
                        typeSpecificNamePart3 = names[1];
                    }
                    else if (3 == names.Length) { 
                        typeSpecificNamePart1 = names[0];
                        typeSpecificNamePart2 = names[1]; 
                        typeSpecificNamePart3 = names[2]; 
                    }
                    else { 
                        throw ADP.ArgumentOutOfRange("typeName");
                    }

                    if ((!ADP.IsEmpty(typeSpecificNamePart1) && TdsEnums.MAX_SERVERNAME < typeSpecificNamePart1.Length) 
                        || (!ADP.IsEmpty(typeSpecificNamePart2) && TdsEnums.MAX_SERVERNAME < typeSpecificNamePart2.Length)
                        || (!ADP.IsEmpty(typeSpecificNamePart3) && TdsEnums.MAX_SERVERNAME < typeSpecificNamePart3.Length)) { 
                        throw ADP.ArgumentOutOfRange("typeName"); 
                    }
                } 
            }

            return new SmiExtendedMetaData( source.SqlDbType,
                                            source.MaxLength, 
                                            source.Precision,
                                            source.Scale, 
                                            source.LocaleId, 
                                            source.CompareOptions,
                                            source.Type, 
                                            source.Name,
                                            typeSpecificNamePart1,
                                            typeSpecificNamePart2,
                                            typeSpecificNamePart3 ); 

 
        } 

 
        // compare SmiMetaData to SqlMetaData and determine if they are compatible.
        static internal bool IsCompatible(SmiMetaData firstMd, SqlMetaData secondMd) {
            return firstMd.SqlDbType == secondMd.SqlDbType &&
                    firstMd.MaxLength == secondMd.MaxLength && 
                    firstMd.Precision == secondMd.Precision &&
                    firstMd.Scale == secondMd.Scale && 
                    firstMd.CompareOptions == secondMd.CompareOptions && 
                    firstMd.LocaleId == secondMd.LocaleId &&
                    firstMd.Type == secondMd.Type && 
                    firstMd.SqlDbType != SqlDbType.Structured &&  // SqlMetaData doesn't support Structured types
                    !firstMd.IsMultiValued;  // SqlMetaData doesn't have a "multivalued" option
        }
 
        static internal long AdjustMaxLength(SqlDbType dbType, long maxLength) {
            if (SmiMetaData.UnlimitedMaxLengthIndicator != maxLength) { 
                if (maxLength < 0) { 
                    maxLength = InvalidMaxLength;
                } 

                switch(dbType) {
                    case SqlDbType.Binary:
                        if (maxLength > SmiMetaData.MaxBinaryLength) { 
                            maxLength = InvalidMaxLength;
                        } 
                        break; 
                    case SqlDbType.Char:
                        if (maxLength > SmiMetaData.MaxANSICharacters) { 
                            maxLength = InvalidMaxLength;
                        }
                        break;
                    case SqlDbType.NChar: 
                        if (maxLength > SmiMetaData.MaxUnicodeCharacters) {
                            maxLength = InvalidMaxLength; 
                        } 
                        break;
                    case SqlDbType.NVarChar: 
                        // Promote to MAX type if it won't fit in a normal type
                        if (SmiMetaData.MaxUnicodeCharacters < maxLength) {
                            maxLength = SmiMetaData.UnlimitedMaxLengthIndicator;
                        } 
                        break;
                    case SqlDbType.VarBinary: 
                        // Promote to MAX type if it won't fit in a normal type 
                        if (SmiMetaData.MaxBinaryLength < maxLength) {
                            maxLength = SmiMetaData.UnlimitedMaxLengthIndicator; 
                        }
                        break;
                    case SqlDbType.VarChar:
                        // Promote to MAX type if it won't fit in a normal type 
                        if (SmiMetaData.MaxANSICharacters < maxLength) {
                            maxLength = SmiMetaData.UnlimitedMaxLengthIndicator; 
                        } 
                        break;
                    default: 
                        break;
                }
            }
 
            return maxLength;
        } 
 
        // Extract metadata for a single DataColumn
        static internal SmiExtendedMetaData SmiMetaDataFromDataColumn(DataColumn column, DataTable parent) { 
            SqlDbType dbType = InferSqlDbTypeFromType_Katmai(column.DataType);
            if (InvalidSqlDbType == dbType) {
                throw SQL.UnsupportedColumnTypeForSqlProvider(column.ColumnName, column.DataType.Name);
            } 

            long maxLength = AdjustMaxLength(dbType, column.MaxLength); 
            if (InvalidMaxLength == maxLength) { 
                throw SQL.InvalidColumnMaxLength(column.ColumnName, maxLength);
            } 

            byte precision;
            byte scale;
            if (column.DataType == typeof(SqlDecimal)) { 

                // Must scan all values in column to determine best-fit precision & scale 
                Debug.Assert(null != parent); 
                scale = 0;
                byte nonFractionalPrecision = 0; // finds largest non-Fractional portion of precision 
                foreach (DataRow row in parent.Rows) {
                    object obj = row[column];
                    if (!(obj is DBNull)) {
                        SqlDecimal value = (SqlDecimal) obj; 
                        if (!value.IsNull) {
                            byte tempNonFractPrec = checked((byte) (value.Precision - value.Scale)); 
                            if (tempNonFractPrec > nonFractionalPrecision) { 
                                nonFractionalPrecision = tempNonFractPrec;
                            } 

                            if (value.Scale > scale) {
                                scale = value.Scale;
                            } 
                        }
                    } 
                } 

                precision = checked((byte)(nonFractionalPrecision + scale)); 

                if (SqlDecimal.MaxPrecision < precision) {
                    throw SQL.InvalidTableDerivedPrecisionForTvp(column.ColumnName, precision);
                } 
                else if (0 == precision) {
                    precision = 1; 
                } 
            }
            else if (dbType == SqlDbType.DateTime2 || dbType == SqlDbType.DateTimeOffset || dbType == SqlDbType.Time) { 
                // Time types care about scale, too.  But have to infer maximums for these.
                precision = 0;
                scale = SmiMetaData.DefaultTime.Scale;
            } 
            else if (dbType == SqlDbType.Decimal) {
                // Must scan all values in column to determine best-fit precision & scale 
                Debug.Assert(null != parent); 
                scale = 0;
                byte nonFractionalPrecision = 0; // finds largest non-Fractional portion of precision 
                foreach (DataRow row in parent.Rows) {
                    object obj = row[column];
                    if (!(obj is DBNull)) {
                        SqlDecimal value = (SqlDecimal)(Decimal)obj; 
                        byte tempNonFractPrec = checked((byte)(value.Precision - value.Scale));
                        if (tempNonFractPrec > nonFractionalPrecision) { 
                            nonFractionalPrecision = tempNonFractPrec; 
                        }
 
                        if (value.Scale > scale) {
                            scale = value.Scale;
                        }
                    } 
                }
 
                precision = checked((byte)(nonFractionalPrecision + scale)); 

                if (SqlDecimal.MaxPrecision < precision) { 
                    throw SQL.InvalidTableDerivedPrecisionForTvp(column.ColumnName, precision);
                }
                else if (0 == precision) {
                    precision = 1; 
                }
            } 
            else { 
                precision = 0;
                scale = 0; 
            }

            return new SmiExtendedMetaData(
                                        dbType, 
                                        maxLength,
                                        precision, 
                                        scale, 
                                        column.Locale.LCID,
                                        SmiMetaData.DefaultNVarChar.CompareOptions, 
                                        column.DataType,
                                        false,  // no support for multi-valued columns in a TVP yet
                                        null,   // no support for structured columns yet
                                        null,   // no support for structured columns yet 
                                        column.ColumnName,
                                        null, 
                                        null, 
                                        null);
        } 

        // Map SmiMetaData from a schema table.
        //  DEVNOTE: since we're using SchemaTable, we can assume that we aren't directly using a SqlDataReader
        //      so we don't support the Sql-specific stuff, like collation 
        static internal SmiExtendedMetaData SmiMetaDataFromSchemaTableRow(DataRow schemaRow) {
            // One way or another, we'll need column name, so put it in a local now to shorten code later. 
            string colName = ""; 
            object temp = schemaRow[SchemaTableColumn.ColumnName];
            if (DBNull.Value != temp) { 
                colName = (string)temp;
            }

            // Determine correct SqlDbType. 
            temp = schemaRow[SchemaTableColumn.DataType];
            if (DBNull.Value == temp) { 
                throw SQL.NullSchemaTableDataTypeNotSupported(colName); 
            }
            Type colType = (Type)temp; 
            SqlDbType colDbType = InferSqlDbTypeFromType_Katmai(colType);
            if (InvalidSqlDbType == colDbType) {
                // Unknown through standard mapping, use VarBinary for columns that are Object typed, otherwise error
                if (typeof(object) == colType) { 
                    colDbType = SqlDbType.VarBinary;
                } 
                else { 
                    throw SQL.UnsupportedColumnTypeForSqlProvider(colName, colType.ToString());
                } 
            }

            // Determine metadata modifier values per type (maxlength, precision, scale, etc)
            long maxLength = 0; 
            byte precision = 0;
            byte scale = 0; 
            switch (colDbType) { 
                case SqlDbType.BigInt:
                case SqlDbType.Bit: 
                case SqlDbType.DateTime:
                case SqlDbType.Float:
                case SqlDbType.Image:
                case SqlDbType.Int: 
                case SqlDbType.Money:
                case SqlDbType.NText: 
                case SqlDbType.Real: 
                case SqlDbType.UniqueIdentifier:
                case SqlDbType.SmallDateTime: 
                case SqlDbType.SmallInt:
                case SqlDbType.SmallMoney:
                case SqlDbType.Text:
                case SqlDbType.Timestamp: 
                case SqlDbType.TinyInt:
                case SqlDbType.Variant: 
                case SqlDbType.Xml: 
                case SqlDbType.Date:
                    // These types require no  metadata modifies 
                    break;
                case SqlDbType.Binary:
                case SqlDbType.VarBinary:
                    // These types need a binary max length 
                    temp = schemaRow[SchemaTableColumn.ColumnSize];
                    if (DBNull.Value == temp) { 
                        // source isn't specifying a size, so assume the worst 
                        if (SqlDbType.Binary == colDbType) {
                            maxLength = SmiMetaData.MaxBinaryLength; 
                        }
                        else {
                            maxLength = SmiMetaData.UnlimitedMaxLengthIndicator;
                        } 
                    }
                    else { 
                        // We (should) have a valid maxlength, so use it. 
                        maxLength = Convert.ToInt64(temp, null);
 
                        // Max length must be 0 to MaxBinaryLength or it can be UnlimitedMAX if type is varbinary
                        //   If it's greater than MaxBinaryLength, just promote it to UnlimitedMAX, if possible
                        if (maxLength > SmiMetaData.MaxBinaryLength) {
                            maxLength = SmiMetaData.UnlimitedMaxLengthIndicator; 
                        }
 
                        if ((maxLength < 0 && 
                                (maxLength != SmiMetaData.UnlimitedMaxLengthIndicator ||
                                 SqlDbType.Binary == colDbType))) { 
                            throw SQL.InvalidColumnMaxLength(colName, maxLength);
                        }
                    }
                    break; 
                case SqlDbType.Char:
                case SqlDbType.VarChar: 
                    // These types need an ANSI max length 
                    temp = schemaRow[SchemaTableColumn.ColumnSize];
                    if (DBNull.Value == temp) { 
                        // source isn't specifying a size, so assume the worst
                        if (SqlDbType.Char == colDbType) {
                            maxLength = SmiMetaData.MaxANSICharacters;
                        } 
                        else {
                            maxLength = SmiMetaData.UnlimitedMaxLengthIndicator; 
                        } 
                    }
                    else { 
                        // We (should) have a valid maxlength, so use it.
                        maxLength = Convert.ToInt64(temp, null);

                        // Max length must be 0 to MaxANSICharacters or it can be UnlimitedMAX if type is varbinary 
                        //   If it's greater than MaxANSICharacters, just promote it to UnlimitedMAX, if possible
                        if (maxLength > SmiMetaData.MaxANSICharacters) { 
                            maxLength = SmiMetaData.UnlimitedMaxLengthIndicator; 
                        }
 
                        if ((maxLength < 0 &&
                                (maxLength != SmiMetaData.UnlimitedMaxLengthIndicator ||
                                 SqlDbType.Char == colDbType))) {
                            throw SQL.InvalidColumnMaxLength(colName, maxLength); 
                        }
                    } 
                    break; 
                case SqlDbType.NChar:
                case SqlDbType.NVarChar: 
                    // These types need a unicode max length
                    temp = schemaRow[SchemaTableColumn.ColumnSize];
                    if (DBNull.Value == temp) {
                        // source isn't specifying a size, so assume the worst 
                        if (SqlDbType.NChar == colDbType) {
                            maxLength = SmiMetaData.MaxUnicodeCharacters; 
                        } 
                        else {
                            maxLength = SmiMetaData.UnlimitedMaxLengthIndicator; 
                        }
                    }
                    else {
                        // We (should) have a valid maxlength, so use it. 
                        maxLength = Convert.ToInt64(temp, null);
 
                        // Max length must be 0 to MaxUnicodeCharacters or it can be UnlimitedMAX if type is varbinary 
                        //   If it's greater than MaxUnicodeCharacters, just promote it to UnlimitedMAX, if possible
                        if (maxLength > SmiMetaData.MaxUnicodeCharacters) { 
                            maxLength = SmiMetaData.UnlimitedMaxLengthIndicator;
                        }

                        if ((maxLength < 0 && 
                                (maxLength != SmiMetaData.UnlimitedMaxLengthIndicator ||
                                 SqlDbType.NChar == colDbType))) { 
                            throw SQL.InvalidColumnMaxLength(colName, maxLength); 
                        }
                    } 
                    break;
                case SqlDbType.Decimal:
                    // Decimal requires precision and scale
                    temp = schemaRow[SchemaTableColumn.NumericPrecision]; 
                    if (DBNull.Value == temp) {
                        precision = SmiMetaData.DefaultDecimal.Precision; 
                    } 
                    else {
                        precision = Convert.ToByte(temp, null); 
                    }

                    temp = schemaRow[SchemaTableColumn.NumericScale];
                    if (DBNull.Value == temp) { 
                        scale = SmiMetaData.DefaultDecimal.Scale;
                    } 
                    else { 
                        scale = Convert.ToByte(temp, null);
                    } 

                    if (precision < SmiMetaData.MinPrecision ||
                            precision > SqlDecimal.MaxPrecision ||
                            scale < SmiMetaData.MinScale || 
                            scale > SqlDecimal.MaxScale ||
                            scale > precision) { 
                        throw SQL.InvalidColumnPrecScale(); 
                    }
                    break; 
                case SqlDbType.Time:
                case SqlDbType.DateTime2:
                case SqlDbType.DateTimeOffset:
                    // requires scale 
                    temp = schemaRow[SchemaTableColumn.NumericScale];
                    if (DBNull.Value == temp) { 
                        scale = SmiMetaData.DefaultTime.Scale; 
                    }
                    else { 
                        scale = Convert.ToByte(temp, null);
                    }

                    if (scale > SmiMetaData.MaxTimeScale) { 
                        throw SQL.InvalidColumnPrecScale();
                    } 
                    else if (scale < 0) { 
                        scale = SmiMetaData.DefaultTime.Scale;
                    } 
                    break;
                case SqlDbType.Udt:
                case SqlDbType.Structured:
                default: 
                    // These types are not supported from SchemaTable
                    throw SQL.UnsupportedColumnTypeForSqlProvider(colName, colType.ToString()); 
            } 

            return new SmiExtendedMetaData( 
                                        colDbType,
                                        maxLength,
                                        precision,
                                        scale, 
                                        System.Globalization.CultureInfo.CurrentCulture.LCID,
                                        SmiMetaData.GetDefaultForType(colDbType).CompareOptions, 
                                        null,   // no support for UDTs from SchemaTable 
                                        false,  // no support for multi-valued columns in a TVP yet
                                        null,   // no support for structured columns yet 
                                        null,   // no support for structured columns yet
                                        colName,
                                        null,
                                        null, 
                                        null);
        } 
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK