diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj index e1483a26a8..d088006903 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -391,9 +391,12 @@ Microsoft\Data\SqlClient\Server\SmiEventSink.cs - + Microsoft\Data\SqlClient\Server\SmiEventSink_Default.Common.cs + + Microsoft\Data\SqlClient\Server\MetadataUtilsSmi.cs + Microsoft\Data\SqlClient\Reliability\SqlRetryingEventArgs.cs @@ -531,7 +534,6 @@ System - diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj index a4ecd51c67..37fdb08a36 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -465,6 +465,9 @@ Microsoft\Data\SqlClient\Server\SqlNormalizer.cs + + Microsoft\Data\SqlClient\Server\MetadataUtilsSmi.cs + Microsoft\Data\SqlClient\Server\SmiRecordBuffer.cs @@ -603,7 +606,6 @@ - diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/Server/MetadataUtilsSmi.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/Server/MetadataUtilsSmi.cs deleted file mode 100644 index 92baf05ff2..0000000000 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/Server/MetadataUtilsSmi.cs +++ /dev/null @@ -1,1067 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Data; -using System.Data.Common; -using System.Data.SqlTypes; -using System.Diagnostics; -using System.Globalization; -using System.Reflection; -using Microsoft.Data.Common; - -namespace Microsoft.Data.SqlClient.Server -{ - - // 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, // Microsoft.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 comparisons. - // Given that there's a known SqlDbType, thus a minimal number of comparisons, 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; - else if (value.GetType() == typeof(StreamDataFeed)) - extendedCode = ExtendedClrTypeCode.Stream; - 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; - if (value.GetType() == typeof(TextDataFeed)) - extendedCode = ExtendedClrTypeCode.TextReader; - 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.Sql2008Version) - { - 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-purpose 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 || - value.GetType() == udtType - ) - { - extendedCode = ExtendedClrTypeCode.Object; - } - else - { - extendedCode = ExtendedClrTypeCode.Invalid; - } - break; - case SqlDbType.Time: - if (value.GetType() == typeof(TimeSpan) && smiVersion >= SmiContextFactory.Sql2008Version) - extendedCode = ExtendedClrTypeCode.TimeSpan; - break; - case SqlDbType.DateTimeOffset: - if (value.GetType() == typeof(DateTimeOffset) && smiVersion >= SmiContextFactory.Sql2008Version) - extendedCode = ExtendedClrTypeCode.DateTimeOffset; - break; - case SqlDbType.Xml: - if (value.GetType() == typeof(SqlXml)) - extendedCode = ExtendedClrTypeCode.SqlXml; - if (value.GetType() == typeof(XmlDataFeed)) - extendedCode = ExtendedClrTypeCode.XmlReader; - 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 - // UNDONE: see if switch on Type.GetTypeCode() + conditionals for object types would be even faster than hash table. - // something like: - // Type type = value.GetType(); - // switch( type.GetTypeCode() ) { - // case Int16: resultCode = ExtendedClrTypeCode.Int16; break; - // ... - // case Object: { - // if ( type == typeof( byte[] ) { - // resultCode = ExtendedClrTypeCode.ByteArray; - // } - // else if ( type == typeof( char[] ) { - // ... - 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. 2008-only (or later) features that need to - // infer types should use InferSqlDbTypeFromType_2008. - 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 2008-or-later-only cases. Only features that are guaranteed to be - // running against 2008 and don't have backward compat issues should call this code path. - // example: TVP's are a new 2008 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_2008(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 - { - // 2005 doesn't support Structured nor the new time types - Debug.Assert(SmiContextFactory.Sql2005Version == 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) - { - SqlMetaData sqlMetaDataInstance = (SqlMetaData)Activator.CreateInstance(typeof(SqlMetaData), System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance, - null, new object[] {source.Name, source.SqlDbType, - source.MaxLength, - source.Precision, - source.Scale, - source.LocaleId, - source.CompareOptions, - source.TypeSpecificNamePart1, - source.TypeSpecificNamePart2, - source.TypeSpecificNamePart3, - true, - source.Type } - , null); - - if (SqlDbType.Xml == source.SqlDbType) - { - return sqlMetaDataInstance; - } - - 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 - PropertyInfo serverTypeNameProperty = source.GetType().GetProperty("ServerTypeName", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); - MethodInfo getter = serverTypeNameProperty.GetGetMethod(nonPublic: true); - String typeName = (string)getter.Invoke(source, null); - 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; - } - - private static CultureInfo GetColumnLocale(DataColumn column) - { - PropertyInfo localeProperty = column.GetType().GetProperty("Locale", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); - MethodInfo getter = localeProperty.GetGetMethod(nonPublic: true); - return (CultureInfo)getter.Invoke(column, null); - } - - // Extract metadata for a single DataColumn - static internal SmiExtendedMetaData SmiMetaDataFromDataColumn(DataColumn column, DataTable parent) - { - SqlDbType dbType = InferSqlDbTypeFromType_2008(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; - } - - CultureInfo locale = GetColumnLocale(column); - return new SmiExtendedMetaData( - dbType, - maxLength, - precision, - scale, - 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_2008(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); - } - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/Server/MetadataUtilsSmi.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/MetadataUtilsSmi.cs similarity index 93% rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/Server/MetadataUtilsSmi.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/MetadataUtilsSmi.cs index 85abc33550..590429222a 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/Server/MetadataUtilsSmi.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/MetadataUtilsSmi.cs @@ -13,21 +13,24 @@ namespace Microsoft.Data.SqlClient.Server { - // Utilities for manipulating smi-related metadata. - // - // Since this class is built on top of SMI, SMI should not have a dependency on this class - // - // These are all based off of knowing the clr type of the value - // as an ExtendedClrTypeCode enum for rapid access. + /// + /// Utilities for manipulating smi-related metadata. + /// Since this class is built on top of SMI, SMI should not have a dependency on this class + /// These are all based off of knowing the clr type of the value + /// as an ExtendedClrTypeCode enum for rapid access. + /// 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 - // to support ExtendedSqlDbType.Invalid - // This array is meant to be accessed from InferSqlDbTypeFromTypeCode. + + /// + /// 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 + /// to support ExtendedSqlDbType.Invalid + /// This array is meant to be accessed from InferSqlDbTypeFromTypeCode. + /// private static readonly SqlDbType[] s_extendedTypeCodeToSqlDbTypeMap = { InvalidSqlDbType, // Invalid extended type code SqlDbType.Bit, // System.Boolean @@ -74,8 +77,11 @@ internal class MetaDataUtilsSmi SqlDbType.DateTimeOffset, // System.DateTimeOffset }; - // Dictionary to map from clr type object to ExtendedClrTypeCodeMap enum. - // This dictionary should only be accessed from DetermineExtendedTypeCode and class ctor for setup. + + /// + /// Dictionary to map from clr type object to ExtendedClrTypeCodeMap enum. + /// This dictionary should only be accessed from DetermineExtendedTypeCode and class ctor for setup. + /// private static readonly Dictionary s_typeToExtendedTypeCodeMap = CreateTypeToExtendedTypeCodeMap(); private static Dictionary CreateTypeToExtendedTypeCodeMap() @@ -130,12 +136,9 @@ private static Dictionary CreateTypeToExtendedTypeCod return dictionary; } - internal static bool IsCharOrXmlType(SqlDbType type) - { - return IsUnicodeType(type) || + internal static bool IsCharOrXmlType(SqlDbType type) => IsUnicodeType(type) || IsAnsiType(type) || type == SqlDbType.Xml; - } internal static bool IsUnicodeType(SqlDbType type) { @@ -144,29 +147,21 @@ internal static bool IsUnicodeType(SqlDbType type) type == SqlDbType.NText; } - internal static bool IsAnsiType(SqlDbType type) - { - return type == SqlDbType.Char || + internal static bool IsAnsiType(SqlDbType type) => type == SqlDbType.Char || type == SqlDbType.VarChar || type == SqlDbType.Text; - } - internal static bool IsBinaryType(SqlDbType type) - { - return type == SqlDbType.Binary || + internal static bool IsBinaryType(SqlDbType type) => 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 || + internal static bool IsPlpFormat(SmiMetaData metaData) => + 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. @@ -183,7 +178,11 @@ internal static ExtendedClrTypeCode DetermineExtendedTypeCodeForUseWithSqlDbType SqlDbType dbType, bool isMultiValued, object value, - Type udtType) + Type udtType +#if NETFRAMEWORK + ,ulong smiVersion +#endif + ) { ExtendedClrTypeCode extendedCode = ExtendedClrTypeCode.Invalid; @@ -246,6 +245,13 @@ internal static ExtendedClrTypeCode DetermineExtendedTypeCodeForUseWithSqlDbType break; case SqlDbType.Date: case SqlDbType.DateTime2: +#if NETFRAMEWORK + if (smiVersion >= SmiContextFactory.Sql2008Version) + { + goto case SqlDbType.DateTime; + } + break; +#endif case SqlDbType.DateTime: case SqlDbType.SmallDateTime: if (value.GetType() == typeof(DateTime)) @@ -325,11 +331,19 @@ internal static ExtendedClrTypeCode DetermineExtendedTypeCodeForUseWithSqlDbType } break; case SqlDbType.Time: - if (value.GetType() == typeof(TimeSpan)) + if (value.GetType() == typeof(TimeSpan) +#if NETFRAMEWORK + && smiVersion >= SmiContextFactory.Sql2008Version +#endif + ) extendedCode = ExtendedClrTypeCode.TimeSpan; break; case SqlDbType.DateTimeOffset: - if (value.GetType() == typeof(DateTimeOffset)) + if (value.GetType() == typeof(DateTimeOffset) +#if NETFRAMEWORK + && smiVersion >= SmiContextFactory.Sql2008Version +#endif + ) extendedCode = ExtendedClrTypeCode.DateTimeOffset; break; case SqlDbType.Xml: @@ -424,35 +438,11 @@ internal static SqlDbType InferSqlDbTypeFromType_2008(Type type) return returnType; } - internal static 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, - null); - } - - // Convert SqlMetaData instance to an SmiExtendedMetaData instance. + /// + /// Convert SqlMetaData instance to an SmiExtendedMetaData instance. + /// + /// + /// internal static SmiExtendedMetaData SqlMetaDataToSmiExtendedMetaData(SqlMetaData source) { // now map everything across to the extended metadata object @@ -510,7 +500,11 @@ internal static SmiExtendedMetaData SqlMetaDataToSmiExtendedMetaData(SqlMetaData source.Scale, source.LocaleId, source.CompareOptions, +#if NETFRAMEWORK + source.Type, +#else null, +#endif source.Name, typeSpecificNamePart1, typeSpecificNamePart2, @@ -526,6 +520,7 @@ internal static bool IsCompatible(SmiMetaData firstMd, SqlMetaData secondMd) 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 } @@ -645,7 +640,11 @@ internal static SmiExtendedMetaData SmiMetaDataFromDataColumn(DataColumn column, scale, columnLocale.LCID, SmiMetaData.DefaultNVarChar.CompareOptions, +#if NETFRAMEWORK + column.DataType, +#else null, +#endif 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 @@ -959,5 +958,27 @@ internal static SmiExtendedMetaData SmiMetaDataFromSchemaTableRow(DataRow schema null, null); } + +#if NETFRAMEWORK + + static internal bool IsValidForSmiVersion(SmiExtendedMetaData md, ulong smiVersion) + { + if (SmiContextFactory.LatestVersion == smiVersion) + { + return true; + } + else + { + // 2005 doesn't support Structured nor the new time types + Debug.Assert(SmiContextFactory.Sql2005Version == 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; + } + } + +#endif } }