1 //------------------------------------------------------------------------------
2 // <copyright file="SmiMetaData.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 // <owner current="true" primary="true">[....]</owner>
6 // <owner current="true" primary="false">[....]</owner>
7 //------------------------------------------------------------------------------
9 namespace Microsoft.SqlServer.Server {
12 using System.Collections.Generic;
13 using System.Diagnostics;
15 using System.Data.Sql;
16 using System.Data.SqlTypes;
17 using System.Globalization;
22 // The following classes are a tight inheritance heirarchy, and are not designed for
23 // being inherited outside of this file. Instances are guaranteed to be immutable, and
24 // outside classes rely on this fact.
26 // The various levels may not all be used outside of this file, but for clarity of purpose
27 // they are all usefull distinctions to make.
29 // In general, moving lower in the type heirarchy exposes less portable values. Thus,
30 // the root metadata can be readily shared across different (MSSQL) servers and clients,
31 // while QueryMetaData has attributes tied to a specific query, running against specific
32 // data storage on a specific server.
34 // The SmiMetaData heirarchy does not do data validation on retail builds! It will assert
35 // that the values passed to it have been validated externally, however.
41 // Root of the heirarchy.
42 // Represents the minimal amount of metadata required to represent any Sql Server datum
43 // without any references to any particular server or schema (thus, no server-specific multi-part names).
44 // It could be used to communicate solely between two disconnected clients, for instance.
46 // NOTE: It currently does not contain sufficient information to describe typed XML, since we
47 // don't have a good server-independent mechanism for such.
49 // This class is also used as implementation for the public SqlMetaData class.
50 internal class SmiMetaData {
52 private SqlDbType _databaseType; // Main enum that determines what is valid for other attributes.
53 private long _maxLength; // Varies for variable-length types, others are fixed value per type
54 private byte _precision; // Varies for SqlDbType.Decimal, others are fixed value per type
55 private byte _scale; // Varies for SqlDbType.Decimal, others are fixed value per type
56 private long _localeId; // Valid only for character types, others are 0
57 private SqlCompareOptions _compareOptions; // Valid only for character types, others are SqlCompareOptions.Default
58 private Type _clrType; // Varies for SqlDbType.Udt, others are fixed value per type.
59 private string _udtAssemblyQualifiedName; // Valid only for UDT types when _clrType is not available
60 private bool _isMultiValued; // Multiple instances per value? (I.e. tables, arrays)
61 private IList<SmiExtendedMetaData> _fieldMetaData; // Metadata of fields for structured types
62 private SmiMetaDataPropertyCollection _extendedProperties; // Extended properties, Key columns, sort order, etc.
64 // DevNote: For now, since the list of extended property types is small, we can handle them in a simple list.
65 // In the future, we may need to create a more performant storage & lookup mechanism, such as a hash table
66 // of lists indexed by type of property or an array of lists with a well-known index for each type.
70 // Limits for attributes (SmiMetaData will assert that these limits as applicable in constructor)
71 internal const long UnlimitedMaxLengthIndicator = -1; // unlimited (except by implementation) max-length.
72 internal const long MaxUnicodeCharacters = 4000; // Maximum for limited type
73 internal const long MaxANSICharacters = 8000; // Maximum for limited type
74 internal const long MaxBinaryLength = 8000; // Maximum for limited type
75 internal const int MinPrecision = 1; // SqlDecimal defines max precision
76 internal const int MinScale = 0; // SqlDecimal defines max scale
77 internal const int MaxTimeScale = 7; // Max scale for time, datetime2, and datetimeoffset
78 internal static readonly DateTime MaxSmallDateTime = new DateTime(2079, 06, 06, 23, 59, 29, 998);
79 internal static readonly DateTime MinSmallDateTime = new DateTime(1899, 12, 31, 23, 59, 29, 999);
80 internal static readonly SqlMoney MaxSmallMoney = new SqlMoney( ( (Decimal)Int32.MaxValue ) / 10000 );
81 internal static readonly SqlMoney MinSmallMoney = new SqlMoney( ( (Decimal)Int32.MinValue ) / 10000 );
82 internal const SqlCompareOptions DefaultStringCompareOptions = SqlCompareOptions.IgnoreCase
83 | SqlCompareOptions.IgnoreKanaType | SqlCompareOptions.IgnoreWidth;
85 internal const long MaxNameLength = 128; // maximum length in the server is 128.
86 private static readonly IList<SmiExtendedMetaData> __emptyFieldList = new List<SmiExtendedMetaData>().AsReadOnly();
89 // Precision to max length lookup table
90 private static byte[] __maxLenFromPrecision = new byte[] {5,5,5,5,5,5,5,5,5,9,9,9,9,9,
91 9,9,9,9,9,13,13,13,13,13,13,13,13,13,17,17,17,17,17,17,17,17,17,17};
93 // Scale offset to max length lookup table
94 private static byte[] __maxVarTimeLenOffsetFromScale = new byte[] { 2, 2, 2, 1, 1, 0, 0, 0 };
97 // SmiMetaData(SqlDbType, MaxLen, Prec, Scale, CompareOptions)
98 internal static readonly SmiMetaData DefaultBigInt = new SmiMetaData(SqlDbType.BigInt, 8, 19, 0, SqlCompareOptions.None); // SqlDbType.BigInt
99 internal static readonly SmiMetaData DefaultBinary = new SmiMetaData(SqlDbType.Binary, 1, 0, 0, SqlCompareOptions.None); // SqlDbType.Binary
100 internal static readonly SmiMetaData DefaultBit = new SmiMetaData(SqlDbType.Bit, 1, 1, 0, SqlCompareOptions.None); // SqlDbType.Bit
101 internal static readonly SmiMetaData DefaultChar_NoCollation = new SmiMetaData(SqlDbType.Char, 1, 0, 0, DefaultStringCompareOptions);// SqlDbType.Char
102 internal static readonly SmiMetaData DefaultDateTime = new SmiMetaData(SqlDbType.DateTime, 8, 23, 3, SqlCompareOptions.None); // SqlDbType.DateTime
103 internal static readonly SmiMetaData DefaultDecimal = new SmiMetaData(SqlDbType.Decimal, 9, 18, 0, SqlCompareOptions.None); // SqlDbType.Decimal
104 internal static readonly SmiMetaData DefaultFloat = new SmiMetaData(SqlDbType.Float, 8, 53, 0, SqlCompareOptions.None); // SqlDbType.Float
105 internal static readonly SmiMetaData DefaultImage = new SmiMetaData(SqlDbType.Image, UnlimitedMaxLengthIndicator,0, 0, SqlCompareOptions.None); // SqlDbType.Image
106 internal static readonly SmiMetaData DefaultInt = new SmiMetaData(SqlDbType.Int, 4, 10, 0, SqlCompareOptions.None); // SqlDbType.Int
107 internal static readonly SmiMetaData DefaultMoney = new SmiMetaData(SqlDbType.Money, 8, 19, 4, SqlCompareOptions.None); // SqlDbType.Money
108 internal static readonly SmiMetaData DefaultNChar_NoCollation = new SmiMetaData(SqlDbType.NChar, 1, 0, 0, DefaultStringCompareOptions);// SqlDbType.NChar
109 internal static readonly SmiMetaData DefaultNText_NoCollation = new SmiMetaData(SqlDbType.NText, UnlimitedMaxLengthIndicator,0, 0, DefaultStringCompareOptions);// SqlDbType.NText
110 internal static readonly SmiMetaData DefaultNVarChar_NoCollation = new SmiMetaData(SqlDbType.NVarChar, MaxUnicodeCharacters, 0, 0, DefaultStringCompareOptions);// SqlDbType.NVarChar
111 internal static readonly SmiMetaData DefaultReal = new SmiMetaData(SqlDbType.Real, 4, 24, 0, SqlCompareOptions.None); // SqlDbType.Real
112 internal static readonly SmiMetaData DefaultUniqueIdentifier = new SmiMetaData(SqlDbType.UniqueIdentifier, 16, 0, 0, SqlCompareOptions.None); // SqlDbType.UniqueIdentifier
113 internal static readonly SmiMetaData DefaultSmallDateTime = new SmiMetaData(SqlDbType.SmallDateTime, 4, 16, 0, SqlCompareOptions.None); // SqlDbType.SmallDateTime
114 internal static readonly SmiMetaData DefaultSmallInt = new SmiMetaData(SqlDbType.SmallInt, 2, 5, 0, SqlCompareOptions.None); // SqlDbType.SmallInt
115 internal static readonly SmiMetaData DefaultSmallMoney = new SmiMetaData(SqlDbType.SmallMoney, 4, 10, 4, SqlCompareOptions.None); // SqlDbType.SmallMoney
116 internal static readonly SmiMetaData DefaultText_NoCollation = new SmiMetaData(SqlDbType.Text, UnlimitedMaxLengthIndicator,0, 0, DefaultStringCompareOptions);// SqlDbType.Text
117 internal static readonly SmiMetaData DefaultTimestamp = new SmiMetaData(SqlDbType.Timestamp, 8, 0, 0, SqlCompareOptions.None); // SqlDbType.Timestamp
118 internal static readonly SmiMetaData DefaultTinyInt = new SmiMetaData(SqlDbType.TinyInt, 1, 3, 0, SqlCompareOptions.None); // SqlDbType.TinyInt
119 internal static readonly SmiMetaData DefaultVarBinary = new SmiMetaData(SqlDbType.VarBinary, MaxBinaryLength, 0, 0, SqlCompareOptions.None); // SqlDbType.VarBinary
120 internal static readonly SmiMetaData DefaultVarChar_NoCollation = new SmiMetaData(SqlDbType.VarChar, MaxANSICharacters, 0, 0, DefaultStringCompareOptions);// SqlDbType.VarChar
121 internal static readonly SmiMetaData DefaultVariant = new SmiMetaData(SqlDbType.Variant, 8016, 0, 0, SqlCompareOptions.None); // SqlDbType.Variant
122 internal static readonly SmiMetaData DefaultXml = new SmiMetaData(SqlDbType.Xml, UnlimitedMaxLengthIndicator,0, 0, DefaultStringCompareOptions);// SqlDbType.Xml
123 internal static readonly SmiMetaData DefaultUdt_NoType = new SmiMetaData(SqlDbType.Udt, 0, 0, 0, SqlCompareOptions.None); // SqlDbType.Udt
124 internal static readonly SmiMetaData DefaultStructured = new SmiMetaData(SqlDbType.Structured, 0, 0, 0, SqlCompareOptions.None); // SqlDbType.Structured
125 internal static readonly SmiMetaData DefaultDate = new SmiMetaData(SqlDbType.Date, 3, 10, 0, SqlCompareOptions.None); // SqlDbType.Date
126 internal static readonly SmiMetaData DefaultTime = new SmiMetaData(SqlDbType.Time, 5, 0, 7, SqlCompareOptions.None); // SqlDbType.Time
127 internal static readonly SmiMetaData DefaultDateTime2 = new SmiMetaData(SqlDbType.DateTime2, 8, 0, 7, SqlCompareOptions.None); // SqlDbType.DateTime2
128 internal static readonly SmiMetaData DefaultDateTimeOffset = new SmiMetaData(SqlDbType.DateTimeOffset, 10, 0, 7, SqlCompareOptions.None); // SqlDbType.DateTimeOffset
129 // No default for generic UDT
131 // character defaults hook thread-local culture to get collation
132 internal static SmiMetaData DefaultChar {
134 return new SmiMetaData(
135 DefaultChar_NoCollation.SqlDbType,
136 DefaultChar_NoCollation.MaxLength,
137 DefaultChar_NoCollation.Precision,
138 DefaultChar_NoCollation.Scale,
139 System.Globalization.CultureInfo.CurrentCulture.LCID,
140 SqlCompareOptions.IgnoreCase | SqlCompareOptions.IgnoreKanaType | SqlCompareOptions.IgnoreWidth,
146 internal static SmiMetaData DefaultNChar {
148 return new SmiMetaData(
149 DefaultNChar_NoCollation.SqlDbType,
150 DefaultNChar_NoCollation.MaxLength,
151 DefaultNChar_NoCollation.Precision,
152 DefaultNChar_NoCollation.Scale,
153 System.Globalization.CultureInfo.CurrentCulture.LCID,
154 SqlCompareOptions.IgnoreCase | SqlCompareOptions.IgnoreKanaType | SqlCompareOptions.IgnoreWidth,
160 internal static SmiMetaData DefaultNText {
162 return new SmiMetaData(
163 DefaultNText_NoCollation.SqlDbType,
164 DefaultNText_NoCollation.MaxLength,
165 DefaultNText_NoCollation.Precision,
166 DefaultNText_NoCollation.Scale,
167 System.Globalization.CultureInfo.CurrentCulture.LCID,
168 SqlCompareOptions.IgnoreCase | SqlCompareOptions.IgnoreKanaType | SqlCompareOptions.IgnoreWidth,
174 internal static SmiMetaData DefaultNVarChar {
176 return new SmiMetaData(
177 DefaultNVarChar_NoCollation.SqlDbType,
178 DefaultNVarChar_NoCollation.MaxLength,
179 DefaultNVarChar_NoCollation.Precision,
180 DefaultNVarChar_NoCollation.Scale,
181 System.Globalization.CultureInfo.CurrentCulture.LCID,
182 SqlCompareOptions.IgnoreCase | SqlCompareOptions.IgnoreKanaType | SqlCompareOptions.IgnoreWidth,
188 internal static SmiMetaData DefaultText {
190 return new SmiMetaData(
191 DefaultText_NoCollation.SqlDbType,
192 DefaultText_NoCollation.MaxLength,
193 DefaultText_NoCollation.Precision,
194 DefaultText_NoCollation.Scale,
195 System.Globalization.CultureInfo.CurrentCulture.LCID,
196 SqlCompareOptions.IgnoreCase | SqlCompareOptions.IgnoreKanaType | SqlCompareOptions.IgnoreWidth,
202 internal static SmiMetaData DefaultVarChar {
204 return new SmiMetaData(
205 DefaultVarChar_NoCollation.SqlDbType,
206 DefaultVarChar_NoCollation.MaxLength,
207 DefaultVarChar_NoCollation.Precision,
208 DefaultVarChar_NoCollation.Scale,
209 System.Globalization.CultureInfo.CurrentCulture.LCID,
210 SqlCompareOptions.IgnoreCase | SqlCompareOptions.IgnoreKanaType | SqlCompareOptions.IgnoreWidth,
216 // The one and only constructor for use by outside code.
218 // Parameters that matter for given values of dbType (other parameters are ignored in favor of internal defaults).
219 // Thus, if dbType parameter value is SqlDbType.Decimal, the values of precision and scale passed in are used, but
220 // maxLength, localeId, compareOptions, etc are set to defaults for the Decimal type:
221 // SqlDbType.BigInt: dbType
222 // SqlDbType.Binary: dbType, maxLength
223 // SqlDbType.Bit: dbType
224 // SqlDbType.Char: dbType, maxLength, localeId, compareOptions
225 // SqlDbType.DateTime: dbType
226 // SqlDbType.Decimal: dbType, precision, scale
227 // SqlDbType.Float: dbType
228 // SqlDbType.Image: dbType
229 // SqlDbType.Int: dbType
230 // SqlDbType.Money: dbType
231 // SqlDbType.NChar: dbType, maxLength, localeId, compareOptions
232 // SqlDbType.NText: dbType, localeId, compareOptions
233 // SqlDbType.NVarChar: dbType, maxLength, localeId, compareOptions
234 // SqlDbType.Real: dbType
235 // SqlDbType.UniqueIdentifier: dbType
236 // SqlDbType.SmallDateTime: dbType
237 // SqlDbType.SmallInt: dbType
238 // SqlDbType.SmallMoney: dbType
239 // SqlDbType.Text: dbType, localeId, compareOptions
240 // SqlDbType.Timestamp: dbType
241 // SqlDbType.TinyInt: dbType
242 // SqlDbType.VarBinary: dbType, maxLength
243 // SqlDbType.VarChar: dbType, maxLength, localeId, compareOptions
244 // SqlDbType.Variant: dbType
245 // PlaceHolder for value 24
246 // SqlDbType.Xml: dbType
247 // Placeholder for value 26
248 // Placeholder for value 27
249 // Placeholder for value 28
250 // SqlDbType.Udt: dbType, userDefinedType
253 [ObsoleteAttribute( "Not supported as of SMI v2. Will be removed when v1 support dropped. Use ctor without columns param." )]
254 internal SmiMetaData(
260 SqlCompareOptions compareOptions,
261 Type userDefinedType,
262 SmiMetaData[] columns) :
263 // Implement as calling the new ctor
272 Debug.Assert( null == columns, "Row types not supported" );
275 // SMI V100 (aka V3) constructor. Superceded in V200.
276 internal SmiMetaData(
282 SqlCompareOptions compareOptions,
283 Type userDefinedType) :
297 internal SmiMetaData(
303 SqlCompareOptions compareOptions,
304 Type userDefinedType,
306 IList<SmiExtendedMetaData> fieldTypes,
307 SmiMetaDataPropertyCollection extendedProperties)
319 extendedProperties) {
323 internal SmiMetaData(
329 SqlCompareOptions compareOptions,
330 Type userDefinedType,
331 string udtAssemblyQualifiedName,
333 IList<SmiExtendedMetaData> fieldTypes,
334 SmiMetaDataPropertyCollection extendedProperties) {
337 Debug.Assert( IsSupportedDbType(dbType), "Invalid SqlDbType: " + dbType );
339 SetDefaultsForType( dbType );
345 case SqlDbType.BigInt:
347 case SqlDbType.DateTime:
348 case SqlDbType.Float:
349 case SqlDbType.Image:
351 case SqlDbType.Money:
353 case SqlDbType.SmallDateTime:
354 case SqlDbType.SmallInt:
355 case SqlDbType.SmallMoney:
356 case SqlDbType.Timestamp:
357 case SqlDbType.TinyInt:
358 case SqlDbType.UniqueIdentifier:
359 case SqlDbType.Variant:
363 case SqlDbType.Binary:
364 case SqlDbType.VarBinary:
365 _maxLength = maxLength;
368 case SqlDbType.NChar:
369 case SqlDbType.NVarChar:
370 case SqlDbType.VarChar:
371 // locale and compare options are not validated until they get to the server
372 _maxLength = maxLength;
373 _localeId = localeId;
374 _compareOptions = compareOptions;
376 case SqlDbType.NText:
378 _localeId = localeId;
379 _compareOptions = compareOptions;
381 case SqlDbType.Decimal:
382 Debug.Assert( MinPrecision <= precision && SqlDecimal.MaxPrecision >= precision, "Invalid precision: " + precision );
383 Debug.Assert( MinScale <= scale && SqlDecimal.MaxScale >= scale, "Invalid scale: " + scale );
384 Debug.Assert( scale <= precision, "Precision: " + precision + " greater than scale: " + scale );
385 _precision = precision;
387 _maxLength = __maxLenFromPrecision[precision - 1];
390 // Assert modified for VSFTDEVDIV479492 - for SqlParameter both userDefinedType and udtAssemblyQualifiedName
391 // can be NULL, we are checking only maxLength if it will be used (i.e. userDefinedType is NULL)
392 Debug.Assert((null != userDefinedType) || (0 <= maxLength || UnlimitedMaxLengthIndicator == maxLength),
393 String.Format((IFormatProvider)null, "SmiMetaData.ctor: Udt name={0}, maxLength={1}", udtAssemblyQualifiedName, maxLength));
394 // Type not validated until matched to a server. Could be null if extended metadata supplies three-part name!
395 _clrType = userDefinedType;
396 if (null != userDefinedType) {
397 _maxLength = SerializationHelperSql9.GetUdtMaxLength(userDefinedType);
400 _maxLength = maxLength;
402 _udtAssemblyQualifiedName = udtAssemblyQualifiedName;
404 case SqlDbType.Structured:
405 if (null != fieldTypes) {
406 _fieldMetaData = (new List<SmiExtendedMetaData>(fieldTypes)).AsReadOnly();
408 _isMultiValued = isMultiValued;
409 _maxLength = _fieldMetaData.Count;
412 Debug.Assert(MinScale <= scale && scale <= MaxTimeScale, "Invalid time scale: " + scale);
414 _maxLength = 5 - __maxVarTimeLenOffsetFromScale[scale];
416 case SqlDbType.DateTime2:
417 Debug.Assert(MinScale <= scale && scale <= MaxTimeScale, "Invalid time scale: " + scale);
419 _maxLength = 8 - __maxVarTimeLenOffsetFromScale[scale];
421 case SqlDbType.DateTimeOffset:
422 Debug.Assert(MinScale <= scale && scale <= MaxTimeScale, "Invalid time scale: " + scale);
424 _maxLength = 10 - __maxVarTimeLenOffsetFromScale[scale];
427 Debug.Assert( false, "How in the world did we get here? :" + dbType );
431 if (null != extendedProperties) {
432 extendedProperties.SetReadOnly();
433 _extendedProperties = extendedProperties;
436 // properties and fields must meet the following conditions at this point:
439 // 3) same number of columns in each list (0 count acceptable for properties that are "unused")
440 Debug.Assert(null != _extendedProperties && _extendedProperties.IsReadOnly, "SmiMetaData.ctor: _extendedProperties is " + (null!=_extendedProperties?"writeable":"null"));
441 Debug.Assert(null != _fieldMetaData && _fieldMetaData.IsReadOnly, "SmiMetaData.ctor: _fieldMetaData is " + (null!=_fieldMetaData?"writeable":"null"));
443 ((SmiDefaultFieldsProperty)_extendedProperties[SmiPropertySelector.DefaultFields]).CheckCount(_fieldMetaData.Count);
444 ((SmiOrderProperty)_extendedProperties[SmiPropertySelector.SortOrder]).CheckCount(_fieldMetaData.Count);
445 ((SmiUniqueKeyProperty)_extendedProperties[SmiPropertySelector.UniqueKey]).CheckCount(_fieldMetaData.Count);
450 internal bool IsValidMaxLengthForCtorGivenType( SqlDbType dbType, long maxLength ) {
453 case SqlDbType.BigInt:
455 case SqlDbType.DateTime:
456 case SqlDbType.Float:
457 case SqlDbType.Image:
459 case SqlDbType.Money:
461 case SqlDbType.SmallDateTime:
462 case SqlDbType.SmallInt:
463 case SqlDbType.SmallMoney:
464 case SqlDbType.Timestamp:
465 case SqlDbType.TinyInt:
466 case SqlDbType.UniqueIdentifier:
467 case SqlDbType.Variant:
469 case SqlDbType.NText:
471 case SqlDbType.Decimal:
473 case SqlDbType.Structured: //
476 case SqlDbType.DateTime2:
477 case SqlDbType.DateTimeOffset:
479 case SqlDbType.Binary:
480 result = 0 < maxLength && MaxBinaryLength >= maxLength;
482 case SqlDbType.VarBinary:
483 result = UnlimitedMaxLengthIndicator == maxLength || ( 0 < maxLength && MaxBinaryLength >= maxLength );
486 result = 0 < maxLength && MaxANSICharacters >= maxLength;
488 case SqlDbType.NChar:
489 result = 0 < maxLength && MaxUnicodeCharacters >= maxLength;
491 case SqlDbType.NVarChar:
492 result = UnlimitedMaxLengthIndicator == maxLength || ( 0 < maxLength && MaxUnicodeCharacters >= maxLength );
494 case SqlDbType.VarChar:
495 result = UnlimitedMaxLengthIndicator == maxLength || ( 0 < maxLength && MaxANSICharacters >= maxLength );
498 Debug.Assert( false, "How in the world did we get here? :" + dbType );
505 // Sql-style compare options for character types.
506 internal SqlCompareOptions CompareOptions {
508 return _compareOptions;
512 // LCID for type. 0 for non-character types.
513 internal long LocaleId {
519 // Units of length depend on type.
520 // NVarChar, NChar, NText: # of unicode characters
521 // Everything else: # of bytes
522 internal long MaxLength {
528 internal byte Precision {
534 internal byte Scale {
540 internal SqlDbType SqlDbType {
542 return _databaseType;
546 // Clr Type instance for user-defined types
549 // Fault-in UDT clr types on access if have assembly-qualified name
550 if (null == _clrType && SqlDbType.Udt == _databaseType && _udtAssemblyQualifiedName != null) {
551 _clrType = Type.GetType(_udtAssemblyQualifiedName, true);
557 // Clr Type instance for user-defined types in cases where we don't want to throw if the assembly isn't available
558 internal Type TypeWithoutThrowing {
560 // Fault-in UDT clr types on access if have assembly-qualified name
561 if (null == _clrType && SqlDbType.Udt == _databaseType && _udtAssemblyQualifiedName != null) {
562 _clrType = Type.GetType(_udtAssemblyQualifiedName, false);
568 internal string TypeName {
570 string result = null;
571 if (SqlDbType.Udt == _databaseType) {
572 Debug.Assert(String.Empty == __typeNameByDatabaseType[(int)_databaseType], "unexpected udt?");
573 result = Type.FullName;
576 result = __typeNameByDatabaseType[(int)_databaseType];
577 Debug.Assert(null != result, "unknown type name?");
583 internal string AssemblyQualifiedName {
585 string result = null;
586 if (SqlDbType.Udt == _databaseType) {
587 // Fault-in assembly-qualified name if type is available
588 if (_udtAssemblyQualifiedName == null && _clrType != null) {
589 _udtAssemblyQualifiedName = _clrType.AssemblyQualifiedName;
591 result = _udtAssemblyQualifiedName;
597 internal bool IsMultiValued {
599 return _isMultiValued;
603 // Returns read-only list of field metadata
604 internal IList<SmiExtendedMetaData> FieldMetaData {
606 return _fieldMetaData;
610 // Returns read-only list of extended properties
611 internal SmiMetaDataPropertyCollection ExtendedProperties {
613 return _extendedProperties;
617 internal static bool IsSupportedDbType(SqlDbType dbType) {
618 // Hole in SqlDbTypes between Xml and Udt for non-WinFS scenarios.
619 return (SqlDbType.BigInt <= dbType && SqlDbType.Xml >= dbType) ||
620 (SqlDbType.Udt <= dbType && SqlDbType.DateTimeOffset >= dbType);
623 // Only correct access point for defaults per SqlDbType.
624 internal static SmiMetaData GetDefaultForType( SqlDbType dbType ) {
625 Debug.Assert( IsSupportedDbType(dbType), "Unsupported SqlDbtype: " + dbType);
627 return __defaultValues[(int)dbType];
630 // Private constructor used only to initialize default instance array elements.
631 // DO NOT EXPOSE OUTSIDE THIS CLASS!
632 private SmiMetaData (
637 SqlCompareOptions compareOptions) {
638 _databaseType = sqlDbType;
639 _maxLength = maxLength;
640 _precision = precision;
642 _compareOptions = compareOptions;
644 // defaults are the same for all types for the following attributes.
647 _isMultiValued = false;
648 _fieldMetaData = __emptyFieldList;
649 _extendedProperties = SmiMetaDataPropertyCollection.EmptyInstance;
652 // static array of default-valued metadata ordered by corresponding SqlDbType.
653 // NOTE: INDEXED BY SqlDbType ENUM! MUST UPDATE THIS ARRAY WHEN UPDATING SqlDbType!
654 // ONLY ACCESS THIS GLOBAL FROM GetDefaultForType!
655 private static SmiMetaData[] __defaultValues =
657 DefaultBigInt, // SqlDbType.BigInt
658 DefaultBinary, // SqlDbType.Binary
659 DefaultBit, // SqlDbType.Bit
660 DefaultChar_NoCollation, // SqlDbType.Char
661 DefaultDateTime, // SqlDbType.DateTime
662 DefaultDecimal, // SqlDbType.Decimal
663 DefaultFloat, // SqlDbType.Float
664 DefaultImage, // SqlDbType.Image
665 DefaultInt, // SqlDbType.Int
666 DefaultMoney, // SqlDbType.Money
667 DefaultNChar_NoCollation, // SqlDbType.NChar
668 DefaultNText_NoCollation, // SqlDbType.NText
669 DefaultNVarChar_NoCollation, // SqlDbType.NVarChar
670 DefaultReal, // SqlDbType.Real
671 DefaultUniqueIdentifier, // SqlDbType.UniqueIdentifier
672 DefaultSmallDateTime, // SqlDbType.SmallDateTime
673 DefaultSmallInt, // SqlDbType.SmallInt
674 DefaultSmallMoney, // SqlDbType.SmallMoney
675 DefaultText_NoCollation, // SqlDbType.Text
676 DefaultTimestamp, // SqlDbType.Timestamp
677 DefaultTinyInt, // SqlDbType.TinyInt
678 DefaultVarBinary, // SqlDbType.VarBinary
679 DefaultVarChar_NoCollation, // SqlDbType.VarChar
680 DefaultVariant, // SqlDbType.Variant
681 DefaultNVarChar_NoCollation, // Placeholder for value 24
682 DefaultXml, // SqlDbType.Xml
683 DefaultNVarChar_NoCollation, // Placeholder for value 26
684 DefaultNVarChar_NoCollation, // Placeholder for value 27
685 DefaultNVarChar_NoCollation, // Placeholder for value 28
686 DefaultUdt_NoType, // Generic Udt
687 DefaultStructured, // Generic structured type
688 DefaultDate, // SqlDbType.Date
689 DefaultTime, // SqlDbType.Time
690 DefaultDateTime2, // SqlDbType.DateTime2
691 DefaultDateTimeOffset, // SqlDbType.DateTimeOffset
694 // static array of type names ordered by corresponding SqlDbType.
695 // NOTE: INDEXED BY SqlDbType ENUM! MUST UPDATE THIS ARRAY WHEN UPDATING SqlDbType!
696 // ONLY ACCESS THIS GLOBAL FROM get_TypeName!
697 private static string[] __typeNameByDatabaseType =
699 "bigint", // SqlDbType.BigInt
700 "binary", // SqlDbType.Binary
701 "bit", // SqlDbType.Bit
702 "char", // SqlDbType.Char
703 "datetime", // SqlDbType.DateTime
704 "decimal", // SqlDbType.Decimal
705 "float", // SqlDbType.Float
706 "image", // SqlDbType.Image
707 "int", // SqlDbType.Int
708 "money", // SqlDbType.Money
709 "nchar", // SqlDbType.NChar
710 "ntext", // SqlDbType.NText
711 "nvarchar", // SqlDbType.NVarChar
712 "real", // SqlDbType.Real
713 "uniqueidentifier", // SqlDbType.UniqueIdentifier
714 "smalldatetime", // SqlDbType.SmallDateTime
715 "smallint", // SqlDbType.SmallInt
716 "smallmoney", // SqlDbType.SmallMoney
717 "text", // SqlDbType.Text
718 "timestamp", // SqlDbType.Timestamp
719 "tinyint", // SqlDbType.TinyInt
720 "varbinary", // SqlDbType.VarBinary
721 "varchar", // SqlDbType.VarChar
722 "sql_variant", // SqlDbType.Variant
723 null, // placeholder for 24
724 "xml", // SqlDbType.Xml
725 null, // placeholder for 26
726 null, // placeholder for 27
727 null, // placeholder for 28
728 String.Empty, // SqlDbType.Udt -- get type name from Type.FullName instead.
729 String.Empty, // Structured types have user-defined type names.
730 "date", // SqlDbType.Date
731 "time", // SqlDbType.Time
732 "datetime2", // SqlDbType.DateTime2
733 "datetimeoffset", // SqlDbType.DateTimeOffset
736 // Internal setter to be used by constructors only! Modifies state!
737 private void SetDefaultsForType( SqlDbType dbType )
739 SmiMetaData smdDflt = GetDefaultForType( dbType );
740 _databaseType = dbType;
741 _maxLength = smdDflt.MaxLength;
742 _precision = smdDflt.Precision;
743 _scale = smdDflt.Scale;
744 _localeId = smdDflt.LocaleId;
745 _compareOptions = smdDflt.CompareOptions;
747 _isMultiValued = smdDflt._isMultiValued;
748 _fieldMetaData = smdDflt._fieldMetaData; // This is ok due to immutability
749 _extendedProperties = smdDflt._extendedProperties; // This is ok due to immutability
752 internal string TraceString() {
753 return TraceString(0);
755 virtual internal string TraceString(int indent) {
756 string indentStr = new String(' ', indent);
757 string fields = String.Empty;
758 if (null != _fieldMetaData) {
759 foreach(SmiMetaData fieldMd in _fieldMetaData) {
760 fields = String.Format(CultureInfo.InvariantCulture,
761 "{0}{1}\n\t", fields, fieldMd.TraceString(indent+5));
765 string properties = String.Empty;
766 if (null != _extendedProperties) {
767 foreach(SmiMetaDataProperty property in _extendedProperties.Values) {
768 properties = String.Format(CultureInfo.InvariantCulture,
769 "{0}{1} {2}\n\t", properties, indentStr, property.TraceString());
773 return String.Format(CultureInfo.InvariantCulture, "\n\t"
774 +"{0} SqlDbType={1:g}\n\t"
775 +"{0} MaxLength={2:d}\n\t"
776 +"{0} Precision={3:d}\n\t"
777 +"{0} Scale={4:d}\n\t"
778 +"{0} LocaleId={5:x}\n\t"
779 +"{0} CompareOptions={6:g}\n\t"
781 +"{0} MultiValued={8}\n\t"
782 +"{0} fields=\n\t{9}"
783 +"{0} properties=\n\t{10}",
791 (null!=Type) ? Type.ToString():"<null>",
800 // SmiExtendedMetaData
802 // Adds server-specific type extension information to base metadata, but still portable across a specific server.
804 internal class SmiExtendedMetaData : SmiMetaData {
806 private string _name; // context-dependant identifier, ie. parameter name for parameters, column name for columns, etc.
808 // three-part name for typed xml schema and for udt names
809 private string _typeSpecificNamePart1;
810 private string _typeSpecificNamePart2;
811 private string _typeSpecificNamePart3;
813 [ObsoleteAttribute( "Not supported as of SMI v2. Will be removed when v1 support dropped. Use ctor without columns param." )]
814 internal SmiExtendedMetaData(
820 SqlCompareOptions compareOptions,
821 Type userDefinedType,
822 SmiMetaData[] columns,
824 string typeSpecificNamePart1,
825 string typeSpecificNamePart2,
826 string typeSpecificNamePart3) :
827 // Implement as calling the new ctor
837 typeSpecificNamePart1,
838 typeSpecificNamePart2,
839 typeSpecificNamePart3 ) {
840 Debug.Assert( null == columns, "Row types not supported" );
843 internal SmiExtendedMetaData(
849 SqlCompareOptions compareOptions,
850 Type userDefinedType,
852 string typeSpecificNamePart1,
853 string typeSpecificNamePart2,
854 string typeSpecificNamePart3) :
867 typeSpecificNamePart1,
868 typeSpecificNamePart2,
869 typeSpecificNamePart3) {
873 internal SmiExtendedMetaData(
879 SqlCompareOptions compareOptions,
880 Type userDefinedType,
882 IList<SmiExtendedMetaData> fieldMetaData,
883 SmiMetaDataPropertyCollection extendedProperties,
885 string typeSpecificNamePart1,
886 string typeSpecificNamePart2,
887 string typeSpecificNamePart3) :
900 typeSpecificNamePart1,
901 typeSpecificNamePart2,
902 typeSpecificNamePart3) {
906 internal SmiExtendedMetaData(
912 SqlCompareOptions compareOptions,
913 Type userDefinedType,
914 string udtAssemblyQualifiedName,
916 IList<SmiExtendedMetaData> fieldMetaData,
917 SmiMetaDataPropertyCollection extendedProperties,
919 string typeSpecificNamePart1,
920 string typeSpecificNamePart2,
921 string typeSpecificNamePart3 ):
929 udtAssemblyQualifiedName,
932 extendedProperties) {
933 Debug.Assert(null == name || MaxNameLength >= name.Length, "Name is too long");
936 _typeSpecificNamePart1 = typeSpecificNamePart1;
937 _typeSpecificNamePart2 = typeSpecificNamePart2;
938 _typeSpecificNamePart3 = typeSpecificNamePart3;
941 internal string Name {
947 internal string TypeSpecificNamePart1 {
949 return _typeSpecificNamePart1;
953 internal string TypeSpecificNamePart2 {
955 return _typeSpecificNamePart2;
959 internal string TypeSpecificNamePart3 {
961 return _typeSpecificNamePart3;
965 internal override string TraceString(int indent) {
966 return String.Format(CultureInfo.InvariantCulture,
969 +"{2}TypeSpecificNamePart1='{3}'\n\t"
970 +"{2}TypeSpecificNamePart2='{4}'\n\t"
971 +"{2}TypeSpecificNamePart3='{5}'\n\t",
972 (null!=_name) ? _name : "<null>",
973 base.TraceString(indent),
974 new String(' ', indent),
975 (null!=TypeSpecificNamePart1) ? TypeSpecificNamePart1:"<null>",
976 (null!=TypeSpecificNamePart2) ? TypeSpecificNamePart2:"<null>",
977 (null!=TypeSpecificNamePart3) ? TypeSpecificNamePart3:"<null>");
982 // SmiParameterMetaData
984 // MetaData class to send parameter definitions to server.
985 // Sealed because we don't need to derive from it yet.
986 // IMPORTANT DEVNOTE: This class is being used for parameter encryption functionality, to get the type_info TDS object from SqlParameter.
987 // Please consider impact to that when changing this class. Refer to the callers of SqlParameter.GetMetadataForTypeInfo().
988 internal sealed class SmiParameterMetaData : SmiExtendedMetaData {
990 private ParameterDirection _direction;
992 [ObsoleteAttribute( "Not supported as of SMI v2. Will be removed when v1 support dropped. Use ctor without columns param." )]
993 internal SmiParameterMetaData(
999 SqlCompareOptions compareOptions,
1000 Type userDefinedType,
1001 SmiMetaData[] columns,
1003 string typeSpecificNamePart1,
1004 string typeSpecificNamePart2,
1005 string typeSpecificNamePart3,
1006 ParameterDirection direction) :
1007 // Implement as calling the new ctor
1017 typeSpecificNamePart1,
1018 typeSpecificNamePart2,
1019 typeSpecificNamePart3,
1021 Debug.Assert( null == columns, "Row types not supported" );
1024 // SMI V100 (aka V3) ctor
1025 internal SmiParameterMetaData(
1031 SqlCompareOptions compareOptions,
1032 Type userDefinedType,
1034 string typeSpecificNamePart1,
1035 string typeSpecificNamePart2,
1036 string typeSpecificNamePart3,
1037 ParameterDirection direction) :
1050 typeSpecificNamePart1,
1051 typeSpecificNamePart2,
1052 typeSpecificNamePart3,
1057 internal SmiParameterMetaData(
1063 SqlCompareOptions compareOptions,
1064 Type userDefinedType,
1066 IList<SmiExtendedMetaData> fieldMetaData,
1067 SmiMetaDataPropertyCollection extendedProperties,
1069 string typeSpecificNamePart1,
1070 string typeSpecificNamePart2,
1071 string typeSpecificNamePart3,
1072 ParameterDirection direction) :
1085 typeSpecificNamePart1,
1086 typeSpecificNamePart2,
1087 typeSpecificNamePart3,
1092 internal SmiParameterMetaData(
1098 SqlCompareOptions compareOptions,
1099 Type userDefinedType,
1100 string udtAssemblyQualifiedName,
1102 IList<SmiExtendedMetaData> fieldMetaData,
1103 SmiMetaDataPropertyCollection extendedProperties,
1105 string typeSpecificNamePart1,
1106 string typeSpecificNamePart2,
1107 string typeSpecificNamePart3,
1108 ParameterDirection direction) :
1116 udtAssemblyQualifiedName,
1121 typeSpecificNamePart1,
1122 typeSpecificNamePart2,
1123 typeSpecificNamePart3) {
1124 Debug.Assert( ParameterDirection.Input == direction
1125 || ParameterDirection.Output == direction
1126 || ParameterDirection.InputOutput == direction
1127 || ParameterDirection.ReturnValue == direction, "Invalid direction: " + direction );
1128 _direction = direction;
1131 internal ParameterDirection Direction {
1137 internal override string TraceString(int indent) {
1138 return String.Format(CultureInfo.InvariantCulture, "{0}"
1139 +"{1} Direction={2:g}\n\t",
1140 base.TraceString(indent),
1141 new String(' ', indent),
1147 // SmiStorageMetaData
1149 // This class represents the addition of storage-level attributes to the heirarchy (i.e. attributes from
1150 // underlying table, source variables, or whatever).
1152 // Most values use Null (either IsNullable == true or CLR null) to indicate "Not specified" state. Selection
1153 // of which values allow "not specified" determined by backward compatibility.
1155 // Maps approximately to TDS' COLMETADATA token with TABNAME and part of COLINFO thrown in.
1156 internal class SmiStorageMetaData : SmiExtendedMetaData {
1158 // AllowsDBNull is the only value required to be specified.
1159 private bool _allowsDBNull; // could the column return nulls? equivalent to TDS's IsNullable bit
1160 private string _serverName; // underlying column's server
1161 private string _catalogName; // underlying column's database
1162 private string _schemaName; // underlying column's schema
1163 private string _tableName; // underlying column's table
1164 private string _columnName; // underlying column's name
1165 private SqlBoolean _isKey; // Is this one of a set of key columns that uniquely identify an underlying table?
1166 private bool _isIdentity; // Is this from an identity column
1167 private bool _isColumnSet; // Is this column the XML representation of a columnset?
1169 [ObsoleteAttribute( "Not supported as of SMI v2. Will be removed when v1 support dropped. Use ctor without columns param." )]
1170 internal SmiStorageMetaData(
1176 SqlCompareOptions compareOptions,
1177 Type userDefinedType,
1178 SmiMetaData[] columns,
1180 string typeSpecificNamePart1,
1181 string typeSpecificNamePart2,
1182 string typeSpecificNamePart3,
1191 // Implement as calling the new ctor
1201 typeSpecificNamePart1,
1202 typeSpecificNamePart2,
1203 typeSpecificNamePart3,
1212 Debug.Assert( null == columns, "Row types not supported" );
1215 internal SmiStorageMetaData(
1221 SqlCompareOptions compareOptions,
1222 Type userDefinedType,
1224 string typeSpecificNamePart1,
1225 string typeSpecificNamePart2,
1226 string typeSpecificNamePart3,
1246 typeSpecificNamePart1,
1247 typeSpecificNamePart2,
1248 typeSpecificNamePart3,
1260 internal SmiStorageMetaData(
1266 SqlCompareOptions compareOptions,
1267 Type userDefinedType,
1269 IList<SmiExtendedMetaData> fieldMetaData,
1270 SmiMetaDataPropertyCollection extendedProperties,
1272 string typeSpecificNamePart1,
1273 string typeSpecificNamePart2,
1274 string typeSpecificNamePart3,
1295 typeSpecificNamePart1,
1296 typeSpecificNamePart2,
1297 typeSpecificNamePart3,
1310 internal SmiStorageMetaData(
1316 SqlCompareOptions compareOptions,
1317 Type userDefinedType,
1318 string udtAssemblyQualifiedName,
1320 IList<SmiExtendedMetaData> fieldMetaData,
1321 SmiMetaDataPropertyCollection extendedProperties,
1323 string typeSpecificNamePart1,
1324 string typeSpecificNamePart2,
1325 string typeSpecificNamePart3,
1342 udtAssemblyQualifiedName,
1347 typeSpecificNamePart1,
1348 typeSpecificNamePart2,
1349 typeSpecificNamePart3) {
1350 _allowsDBNull = allowsDBNull;
1351 _serverName = serverName;
1352 _catalogName = catalogName;
1353 _schemaName = schemaName;
1354 _tableName = tableName;
1355 _columnName = columnName;
1357 _isIdentity = isIdentity;
1358 _isColumnSet = isColumnSet;
1361 internal bool AllowsDBNull {
1363 return _allowsDBNull;
1367 internal string ServerName {
1373 internal string CatalogName {
1375 return _catalogName;
1379 internal string SchemaName {
1385 internal string TableName {
1391 internal string ColumnName {
1397 internal SqlBoolean IsKey {
1403 internal bool IsIdentity {
1409 internal bool IsColumnSet {
1411 return _isColumnSet;
1415 internal override string TraceString(int indent) {
1416 return String.Format(CultureInfo.InvariantCulture, "{0}"
1417 +"{1} AllowsDBNull={2}\n\t"
1418 +"{1} ServerName='{3}'\n\t"
1419 +"{1} CatalogName='{4}'\n\t"
1420 +"{1} SchemaName='{5}'\n\t"
1421 +"{1} TableName='{6}'\n\t"
1422 +"{1} ColumnName='{7}'\n\t"
1423 +"{1} IsKey={8}\n\t"
1424 +"{1} IsIdentity={9}\n\t",
1425 base.TraceString(indent),
1426 new String(' ', indent),
1428 (null!=ServerName) ? ServerName:"<null>",
1429 (null!=CatalogName) ? CatalogName:"<null>",
1430 (null!=SchemaName) ? SchemaName:"<null>",
1431 (null!=TableName) ? TableName:"<null>",
1432 (null!=ColumnName) ? ColumnName:"<null>",
1441 // Adds Query-specific attributes.
1442 // Sealed since we don't need to extend it for now.
1443 // Maps to full COLMETADATA + COLINFO + TABNAME tokens on TDS.
1444 internal class SmiQueryMetaData : SmiStorageMetaData {
1446 private bool _isReadOnly;
1447 private SqlBoolean _isExpression;
1448 private SqlBoolean _isAliased;
1449 private SqlBoolean _isHidden;
1451 [ObsoleteAttribute( "Not supported as of SMI v2. Will be removed when v1 support dropped. Use ctor without columns param." )]
1452 internal SmiQueryMetaData(
1458 SqlCompareOptions compareOptions,
1459 Type userDefinedType,
1460 SmiMetaData[] columns,
1462 string typeSpecificNamePart1,
1463 string typeSpecificNamePart2,
1464 string typeSpecificNamePart3,
1474 SqlBoolean isExpression,
1475 SqlBoolean isAliased,
1476 SqlBoolean isHidden ) :
1477 // Implement as calling the new ctor
1487 typeSpecificNamePart1,
1488 typeSpecificNamePart2,
1489 typeSpecificNamePart3,
1502 Debug.Assert( null == columns, "Row types not supported" );
1505 internal SmiQueryMetaData( SqlDbType dbType,
1510 SqlCompareOptions compareOptions,
1511 Type userDefinedType,
1513 string typeSpecificNamePart1,
1514 string typeSpecificNamePart2,
1515 string typeSpecificNamePart3,
1525 SqlBoolean isExpression,
1526 SqlBoolean isAliased,
1527 SqlBoolean isHidden ) :
1539 typeSpecificNamePart1,
1540 typeSpecificNamePart2,
1541 typeSpecificNamePart3,
1557 internal SmiQueryMetaData( SqlDbType dbType,
1562 SqlCompareOptions compareOptions,
1563 Type userDefinedType,
1565 IList<SmiExtendedMetaData> fieldMetaData,
1566 SmiMetaDataPropertyCollection extendedProperties,
1568 string typeSpecificNamePart1,
1569 string typeSpecificNamePart2,
1570 string typeSpecificNamePart3,
1580 SqlBoolean isExpression,
1581 SqlBoolean isAliased,
1582 SqlBoolean isHidden) :
1595 typeSpecificNamePart1,
1596 typeSpecificNamePart2,
1597 typeSpecificNamePart3,
1613 internal SmiQueryMetaData( SqlDbType dbType,
1618 SqlCompareOptions compareOptions,
1619 Type userDefinedType,
1620 string udtAssemblyQualifiedName,
1622 IList<SmiExtendedMetaData> fieldMetaData,
1623 SmiMetaDataPropertyCollection extendedProperties,
1625 string typeSpecificNamePart1,
1626 string typeSpecificNamePart2,
1627 string typeSpecificNamePart3,
1638 SqlBoolean isExpression,
1639 SqlBoolean isAliased,
1640 SqlBoolean isHidden ) :
1648 udtAssemblyQualifiedName,
1653 typeSpecificNamePart1,
1654 typeSpecificNamePart2,
1655 typeSpecificNamePart3,
1665 _isReadOnly = isReadOnly;
1666 _isExpression = isExpression;
1667 _isAliased = isAliased;
1668 _isHidden = isHidden;
1671 internal bool IsReadOnly {
1677 internal SqlBoolean IsExpression {
1679 return _isExpression;
1683 internal SqlBoolean IsAliased {
1689 internal SqlBoolean IsHidden {
1696 internal override string TraceString(int indent) {
1697 return String.Format(CultureInfo.InvariantCulture, "{0}"
1698 +"{1} IsReadOnly={2}\n\t"
1699 +"{1} IsExpression={3}\n\t"
1700 +"{1} IsAliased={4}\n\t"
1701 +"{1} IsHidden={5}",
1702 base.TraceString(indent),
1703 new String(' ', indent),