1 //------------------------------------------------------------------------------ 2 // <copyright file="SqlEnums.cs" company="Microsoft"> 3 // Copyright (c) Microsoft Corporation. All rights reserved. 4 // </copyright> 5 // <owner current="true" primary="true">Microsoft</owner> 6 // <owner current="true" primary="false">Microsoft</owner> 7 //------------------------------------------------------------------------------ 8 9 namespace System.Data.SqlClient { 10 11 12 using System; 13 using System.Collections.Generic; 14 using System.ComponentModel; 15 using System.Data.Common; 16 using System.Data.OleDb; 17 using System.Data.SqlTypes; 18 using System.Diagnostics; 19 using System.Globalization; 20 using System.Xml; 21 using System.IO; 22 using System.Data.Sql; 23 24 using MSS=Microsoft.SqlServer.Server; 25 26 internal sealed class MetaType { 27 internal readonly Type ClassType; // com+ type 28 internal readonly Type SqlType; 29 30 internal readonly int FixedLength; // fixed length size in bytes (-1 for variable) 31 internal readonly bool IsFixed; // true if fixed length, note that sqlchar and sqlbinary are not considered fixed length 32 internal readonly bool IsLong; // true if long 33 internal readonly bool IsPlp; // Column is Partially Length Prefixed (MAX) 34 internal readonly byte Precision; // maxium precision for numeric types // $ 35 internal readonly byte Scale; 36 internal readonly byte TDSType; 37 internal readonly byte NullableType; 38 39 internal readonly string TypeName; // string name of this type 40 internal readonly SqlDbType SqlDbType; 41 internal readonly DbType DbType; 42 43 // holds count of property bytes expected for a SQLVariant structure 44 internal readonly byte PropBytes; 45 46 47 // pre-computed fields 48 internal readonly bool IsAnsiType; 49 internal readonly bool IsBinType; 50 internal readonly bool IsCharType; 51 internal readonly bool IsNCharType; 52 internal readonly bool IsSizeInCharacters; 53 internal readonly bool IsNewKatmaiType; 54 internal readonly bool IsVarTime; 55 56 internal readonly bool Is70Supported; 57 internal readonly bool Is80Supported; 58 internal readonly bool Is90Supported; 59 internal readonly bool Is100Supported; 60 MetaType(byte precision, byte scale, int fixedLength, bool isFixed, bool isLong, bool isPlp, byte tdsType, byte nullableTdsType, string typeName, Type classType, Type sqlType, SqlDbType sqldbType, DbType dbType, byte propBytes)61 public MetaType(byte precision, byte scale, int fixedLength, bool isFixed, bool isLong, bool isPlp, byte tdsType, byte nullableTdsType, string typeName, Type classType, Type sqlType, SqlDbType sqldbType, DbType dbType, byte propBytes) { 62 this.Precision = precision; 63 this.Scale = scale; 64 this.FixedLength = fixedLength; 65 this.IsFixed = isFixed; 66 this.IsLong = isLong; 67 this.IsPlp = isPlp; 68 // can we get rid of this (?just have a mapping?) 69 this.TDSType = tdsType; 70 this.NullableType = nullableTdsType; 71 this.TypeName = typeName; 72 this.SqlDbType = sqldbType; 73 this.DbType = dbType; 74 75 this.ClassType = classType; 76 this.SqlType = sqlType; 77 this.PropBytes = propBytes; 78 79 IsAnsiType = _IsAnsiType(sqldbType); 80 IsBinType = _IsBinType(sqldbType); 81 IsCharType = _IsCharType(sqldbType); 82 IsNCharType = _IsNCharType(sqldbType); 83 IsSizeInCharacters = _IsSizeInCharacters(sqldbType); 84 IsNewKatmaiType = _IsNewKatmaiType(sqldbType); 85 IsVarTime = _IsVarTime(sqldbType); 86 87 Is70Supported = _Is70Supported(SqlDbType); 88 Is80Supported = _Is80Supported(SqlDbType); 89 Is90Supported = _Is90Supported(SqlDbType); 90 Is100Supported = _Is100Supported(SqlDbType); 91 } 92 93 // properties should be inlined so there should be no perf penalty for using these accessor functions 94 public int TypeId { // partial length prefixed (xml, nvarchar(max),...) 95 get { return 0;} 96 } 97 _IsAnsiType(SqlDbType type)98 private static bool _IsAnsiType(SqlDbType type) { 99 return(type == SqlDbType.Char || 100 type == SqlDbType.VarChar || 101 type == SqlDbType.Text); 102 } 103 104 // is this type size expressed as count of characters or bytes? _IsSizeInCharacters(SqlDbType type)105 private static bool _IsSizeInCharacters(SqlDbType type) { 106 return(type == SqlDbType.NChar || 107 type == SqlDbType.NVarChar || 108 type == SqlDbType.Xml || 109 type == SqlDbType.NText); 110 } 111 _IsCharType(SqlDbType type)112 private static bool _IsCharType(SqlDbType type) { 113 return(type == SqlDbType.NChar || 114 type == SqlDbType.NVarChar || 115 type == SqlDbType.NText || 116 type == SqlDbType.Char || 117 type == SqlDbType.VarChar || 118 type == SqlDbType.Text || 119 type == SqlDbType.Xml); 120 } 121 _IsNCharType(SqlDbType type)122 private static bool _IsNCharType(SqlDbType type) { 123 return(type == SqlDbType.NChar || 124 type == SqlDbType.NVarChar || 125 type == SqlDbType.NText || 126 type == SqlDbType.Xml); 127 } 128 _IsBinType(SqlDbType type)129 private static bool _IsBinType(SqlDbType type) { 130 return(type == SqlDbType.Image || 131 type == SqlDbType.Binary || 132 type == SqlDbType.VarBinary || 133 type == SqlDbType.Timestamp || 134 type == SqlDbType.Udt || 135 (int) type == 24 /*SqlSmallVarBinary*/); 136 } 137 _Is70Supported(SqlDbType type)138 private static bool _Is70Supported(SqlDbType type) { 139 return((type != SqlDbType.BigInt) && ((int)type > 0) && 140 ((int)type <= (int) SqlDbType.VarChar)); 141 } 142 _Is80Supported(SqlDbType type)143 private static bool _Is80Supported(SqlDbType type) { 144 return((int)type >= 0 && 145 ((int)type <= (int) SqlDbType.Variant)); 146 } 147 _Is90Supported(SqlDbType type)148 private static bool _Is90Supported(SqlDbType type) { 149 return _Is80Supported(type) || 150 SqlDbType.Xml == type || 151 SqlDbType.Udt == type; 152 } 153 _Is100Supported(SqlDbType type)154 private static bool _Is100Supported(SqlDbType type) { 155 return _Is90Supported(type) || 156 SqlDbType.Date == type || 157 SqlDbType.Time == type || 158 SqlDbType.DateTime2 == type || 159 SqlDbType.DateTimeOffset == type; 160 } 161 _IsNewKatmaiType(SqlDbType type)162 private static bool _IsNewKatmaiType(SqlDbType type) { 163 return SqlDbType.Structured == type; 164 } 165 _IsVarTime(SqlDbType type)166 internal static bool _IsVarTime(SqlDbType type) { 167 return (type == SqlDbType.Time || type == SqlDbType.DateTime2 || type == SqlDbType.DateTimeOffset); 168 } 169 170 // 171 // map SqlDbType to MetaType class 172 // GetMetaTypeFromSqlDbType(SqlDbType target, bool isMultiValued)173 internal static MetaType GetMetaTypeFromSqlDbType(SqlDbType target, bool isMultiValued) { // WebData 113289 174 switch(target) { 175 case SqlDbType.BigInt: return MetaBigInt; 176 case SqlDbType.Binary: return MetaBinary; 177 case SqlDbType.Bit: return MetaBit; 178 case SqlDbType.Char: return MetaChar; 179 case SqlDbType.DateTime: return MetaDateTime; 180 case SqlDbType.Decimal: return MetaDecimal; 181 case SqlDbType.Float: return MetaFloat; 182 case SqlDbType.Image: return MetaImage; 183 case SqlDbType.Int: return MetaInt; 184 case SqlDbType.Money: return MetaMoney; 185 case SqlDbType.NChar: return MetaNChar; 186 case SqlDbType.NText: return MetaNText; 187 case SqlDbType.NVarChar: return MetaNVarChar; 188 case SqlDbType.Real: return MetaReal; 189 case SqlDbType.UniqueIdentifier: return MetaUniqueId; 190 case SqlDbType.SmallDateTime: return MetaSmallDateTime; 191 case SqlDbType.SmallInt: return MetaSmallInt; 192 case SqlDbType.SmallMoney: return MetaSmallMoney; 193 case SqlDbType.Text: return MetaText; 194 case SqlDbType.Timestamp: return MetaTimestamp; 195 case SqlDbType.TinyInt: return MetaTinyInt; 196 case SqlDbType.VarBinary: return MetaVarBinary; 197 case SqlDbType.VarChar: return MetaVarChar; 198 case SqlDbType.Variant: return MetaVariant; 199 case (SqlDbType)TdsEnums.SmallVarBinary: return MetaSmallVarBinary; 200 case SqlDbType.Xml: return MetaXml; 201 case SqlDbType.Udt: return MetaUdt; 202 case SqlDbType.Structured: 203 if (isMultiValued) { 204 return MetaTable; 205 } 206 else { 207 return MetaSUDT; 208 } 209 case SqlDbType.Date: return MetaDate; 210 case SqlDbType.Time: return MetaTime; 211 case SqlDbType.DateTime2: return MetaDateTime2; 212 case SqlDbType.DateTimeOffset: return MetaDateTimeOffset; 213 default: throw SQL.InvalidSqlDbType(target); 214 } 215 } 216 217 // 218 // map DbType to MetaType class 219 // GetMetaTypeFromDbType(DbType target)220 internal static MetaType GetMetaTypeFromDbType(DbType target) { 221 // if we can't map it, we need to throw 222 switch (target) { 223 case DbType.AnsiString: return MetaVarChar; 224 case DbType.AnsiStringFixedLength: return MetaChar; 225 case DbType.Binary: return MetaVarBinary; 226 case DbType.Byte: return MetaTinyInt; 227 case DbType.Boolean: return MetaBit; 228 case DbType.Currency: return MetaMoney; 229 case DbType.Date: 230 case DbType.DateTime: return MetaDateTime; 231 case DbType.Decimal: return MetaDecimal; 232 case DbType.Double: return MetaFloat; 233 case DbType.Guid: return MetaUniqueId; 234 case DbType.Int16: return MetaSmallInt; 235 case DbType.Int32: return MetaInt; 236 case DbType.Int64: return MetaBigInt; 237 case DbType.Object: return MetaVariant; 238 case DbType.Single: return MetaReal; 239 case DbType.String: return MetaNVarChar; 240 case DbType.StringFixedLength: return MetaNChar; 241 case DbType.Time: return MetaDateTime; 242 case DbType.Xml: return MetaXml; 243 case DbType.DateTime2: return MetaDateTime2; 244 case DbType.DateTimeOffset: return MetaDateTimeOffset; 245 case DbType.SByte: // unsupported 246 case DbType.UInt16: 247 case DbType.UInt32: 248 case DbType.UInt64: 249 case DbType.VarNumeric: 250 default: throw ADP.DbTypeNotSupported(target, typeof(SqlDbType)); // no direct mapping, error out 251 } 252 } 253 GetMaxMetaTypeFromMetaType(MetaType mt)254 internal static MetaType GetMaxMetaTypeFromMetaType(MetaType mt) { 255 // if we can't map it, we need to throw 256 switch (mt.SqlDbType) { 257 case SqlDbType.VarBinary: 258 case SqlDbType.Binary: 259 return MetaMaxVarBinary; 260 case SqlDbType.VarChar: 261 case SqlDbType.Char: 262 return MetaMaxVarChar; 263 case SqlDbType.NVarChar: 264 case SqlDbType.NChar: 265 return MetaMaxNVarChar; 266 case SqlDbType.Udt: 267 // 268 return MetaMaxUdt; 269 default: 270 return mt; 271 } 272 } 273 274 // 275 // map COM+ Type to MetaType class 276 // GetMetaTypeFromType(Type dataType)277 static internal MetaType GetMetaTypeFromType(Type dataType) { 278 return GetMetaTypeFromValue(dataType, null, false, true); 279 } GetMetaTypeFromValue(object value, bool streamAllowed=true)280 static internal MetaType GetMetaTypeFromValue(object value, bool streamAllowed=true) { 281 return GetMetaTypeFromValue(value.GetType(), value, true, streamAllowed); 282 } 283 GetMetaTypeFromValue(Type dataType, object value, bool inferLen, bool streamAllowed)284 static private MetaType GetMetaTypeFromValue(Type dataType, object value, bool inferLen, bool streamAllowed) { 285 switch (Type.GetTypeCode(dataType)) { 286 case TypeCode.Empty: throw ADP.InvalidDataType(TypeCode.Empty); 287 case TypeCode.Object: 288 if (dataType == typeof(System.Byte[])) { 289 // mdac 90455 must not default to image if inferLen is false ... 290 // 291 if (!inferLen || ((byte[]) value).Length <= TdsEnums.TYPE_SIZE_LIMIT) { 292 return MetaVarBinary; 293 } 294 else { 295 return MetaImage; 296 } 297 } 298 else if (dataType == typeof(System.Guid)) { 299 return MetaUniqueId; 300 } 301 else if (dataType == typeof(System.Object)) { 302 return MetaVariant; 303 } // check sql types now 304 else if (dataType == typeof(SqlBinary)) 305 return MetaVarBinary; 306 else if (dataType == typeof(SqlBoolean)) 307 return MetaBit; 308 else if (dataType == typeof(SqlByte)) 309 return MetaTinyInt; 310 else if (dataType == typeof(SqlBytes)) 311 return MetaVarBinary; 312 else if (dataType == typeof(SqlChars)) 313 return MetaNVarChar; // MDAC 87587 314 else if (dataType == typeof(SqlDateTime)) 315 return MetaDateTime; 316 else if (dataType == typeof(SqlDouble)) 317 return MetaFloat; 318 else if (dataType == typeof(SqlGuid)) 319 return MetaUniqueId; 320 else if (dataType == typeof(SqlInt16)) 321 return MetaSmallInt; 322 else if (dataType == typeof(SqlInt32)) 323 return MetaInt; 324 else if (dataType == typeof(SqlInt64)) 325 return MetaBigInt; 326 else if (dataType == typeof(SqlMoney)) 327 return MetaMoney; 328 else if (dataType == typeof(SqlDecimal)) 329 return MetaDecimal; 330 else if (dataType == typeof(SqlSingle)) 331 return MetaReal; 332 else if (dataType == typeof(SqlXml)) 333 return MetaXml; 334 else if (dataType == typeof(SqlString)) { 335 return ((inferLen && !((SqlString)value).IsNull) ? PromoteStringType(((SqlString)value).Value) : MetaNVarChar); // MDAC 87587 336 } 337 else if (dataType == typeof(IEnumerable<DbDataRecord>) || dataType == typeof(DataTable)) { 338 return MetaTable; 339 } else if (dataType == typeof(TimeSpan)) { 340 return MetaTime; 341 } 342 else if (dataType == typeof(DateTimeOffset)) { 343 return MetaDateTimeOffset; 344 } 345 else { 346 // UDT ? 347 SqlUdtInfo attribs = SqlUdtInfo.TryGetFromType(dataType); 348 if (attribs != null) { 349 return MetaUdt; 350 } 351 if (streamAllowed) { 352 // Derived from Stream ? 353 if (typeof(Stream).IsAssignableFrom(dataType)) { 354 return MetaVarBinary; 355 } 356 // Derived from TextReader ? 357 if (typeof(TextReader).IsAssignableFrom(dataType)) { 358 return MetaNVarChar; 359 } 360 // Derived from XmlReader ? 361 if (typeof(System.Xml.XmlReader).IsAssignableFrom(dataType)) { 362 return MetaXml; 363 } 364 } 365 } 366 throw ADP.UnknownDataType(dataType); 367 368 case TypeCode.DBNull: throw ADP.InvalidDataType(TypeCode.DBNull); 369 case TypeCode.Boolean: return MetaBit; 370 case TypeCode.Char: throw ADP.InvalidDataType(TypeCode.Char); 371 case TypeCode.SByte: throw ADP.InvalidDataType(TypeCode.SByte); 372 case TypeCode.Byte: return MetaTinyInt; 373 case TypeCode.Int16: return MetaSmallInt; 374 case TypeCode.UInt16: throw ADP.InvalidDataType(TypeCode.UInt16); 375 case TypeCode.Int32: return MetaInt; 376 case TypeCode.UInt32: throw ADP.InvalidDataType(TypeCode.UInt32); 377 case TypeCode.Int64: return MetaBigInt; 378 case TypeCode.UInt64: throw ADP.InvalidDataType(TypeCode.UInt64); 379 case TypeCode.Single: return MetaReal; 380 case TypeCode.Double: return MetaFloat; 381 case TypeCode.Decimal: return MetaDecimal; 382 case TypeCode.DateTime: return MetaDateTime; 383 case TypeCode.String: return (inferLen ? PromoteStringType((string)value) : MetaNVarChar); 384 default: throw ADP.UnknownDataTypeCode(dataType, Type.GetTypeCode(dataType)); 385 } 386 } 387 GetNullSqlValue(Type sqlType)388 internal static object GetNullSqlValue(Type sqlType) { 389 if (sqlType == typeof(SqlSingle)) return SqlSingle.Null; 390 else if (sqlType == typeof(SqlString)) return SqlString.Null; 391 else if (sqlType == typeof(SqlDouble)) return SqlDouble.Null; 392 else if (sqlType == typeof(SqlBinary)) return SqlBinary.Null; 393 else if (sqlType == typeof(SqlGuid)) return SqlGuid.Null; 394 else if (sqlType == typeof(SqlBoolean)) return SqlBoolean.Null; 395 else if (sqlType == typeof(SqlByte)) return SqlByte.Null; 396 else if (sqlType == typeof(SqlInt16)) return SqlInt16.Null; 397 else if (sqlType == typeof(SqlInt32)) return SqlInt32.Null; 398 else if (sqlType == typeof(SqlInt64)) return SqlInt64.Null; 399 else if (sqlType == typeof(SqlDecimal)) return SqlDecimal.Null; 400 else if (sqlType == typeof(SqlDateTime)) return SqlDateTime.Null; 401 else if (sqlType == typeof(SqlMoney)) return SqlMoney.Null; 402 else if (sqlType == typeof(SqlXml)) return SqlXml.Null; 403 else if (sqlType == typeof(object)) return DBNull.Value; 404 else if (sqlType == typeof(IEnumerable<DbDataRecord>)) return DBNull.Value; 405 else if (sqlType == typeof(DataTable)) return DBNull.Value; 406 else if (sqlType == typeof(DateTime)) return DBNull.Value; 407 else if (sqlType == typeof(TimeSpan)) return DBNull.Value; 408 else if (sqlType == typeof(DateTimeOffset)) return DBNull.Value; 409 else { 410 Debug.Assert(false, "Unknown SqlType!"); 411 return DBNull.Value; 412 } 413 } 414 PromoteStringType(string s)415 internal static MetaType PromoteStringType(string s) { 416 int len = s.Length; 417 418 if ((len << 1) > TdsEnums.TYPE_SIZE_LIMIT) { 419 return MetaVarChar; // try as var char since we can send a 8K characters 420 } 421 return MetaNVarChar; // send 4k chars, but send as unicode 422 } 423 GetComValueFromSqlVariant(object sqlVal)424 internal static object GetComValueFromSqlVariant(object sqlVal) { 425 object comVal = null; 426 427 if (ADP.IsNull(sqlVal)) 428 return comVal; 429 430 if (sqlVal is SqlSingle) 431 comVal = ((SqlSingle)sqlVal).Value; 432 else if (sqlVal is SqlString) 433 comVal = ((SqlString)sqlVal).Value; 434 else if (sqlVal is SqlDouble) 435 comVal = ((SqlDouble)sqlVal).Value; 436 else if (sqlVal is SqlBinary) 437 comVal = ((SqlBinary)sqlVal).Value; 438 else if (sqlVal is SqlGuid) 439 comVal = ((SqlGuid)sqlVal).Value; 440 else if (sqlVal is SqlBoolean) 441 comVal = ((SqlBoolean)sqlVal).Value; 442 else if (sqlVal is SqlByte) 443 comVal = ((SqlByte)sqlVal).Value; 444 else if (sqlVal is SqlInt16) 445 comVal = ((SqlInt16)sqlVal).Value; 446 else if (sqlVal is SqlInt32) 447 comVal = ((SqlInt32)sqlVal).Value; 448 else if (sqlVal is SqlInt64) 449 comVal = ((SqlInt64)sqlVal).Value; 450 else if (sqlVal is SqlDecimal) 451 comVal = ((SqlDecimal)sqlVal).Value; 452 else if (sqlVal is SqlDateTime) 453 comVal = ((SqlDateTime)sqlVal).Value; 454 else if (sqlVal is SqlMoney) 455 comVal = ((SqlMoney)sqlVal).Value; 456 else if (sqlVal is SqlXml) 457 comVal = ((SqlXml)sqlVal).Value; 458 else 459 { 460 AssertIsUserDefinedTypeInstance(sqlVal, "unknown SqlType class stored in sqlVal"); 461 } 462 463 464 return comVal; 465 } 466 467 /// <summary> 468 /// Assert that the supplied object is an instance of a SQL User-Defined Type (UDT). 469 /// </summary> 470 /// <param name="sqlValue">Object instance to be tested.</param> 471 /// <remarks> 472 /// This method is only compiled with debug builds, and it a helper method for the GetComValueFromSqlVariant method defined in this class. 473 /// 474 /// The presence of the SqlUserDefinedTypeAttribute on the object's type 475 /// is used to determine if the object is a UDT instance (if present it is a UDT, else it is not). 476 /// </remarks> 477 /// <exception cref="NullReferenceException"> 478 /// If sqlValue is null. Callers must ensure the object is non-null. 479 /// </exception> 480 [Conditional("DEBUG")] AssertIsUserDefinedTypeInstance(object sqlValue, string failedAssertMessage)481 private static void AssertIsUserDefinedTypeInstance(object sqlValue, string failedAssertMessage) 482 { 483 Type type = sqlValue.GetType(); 484 Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute[] attributes = (Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute[])type.GetCustomAttributes(typeof(Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute), true); 485 486 Debug.Assert(attributes.Length > 0, failedAssertMessage); 487 } 488 489 // devnote: This method should not be used with SqlDbType.Date and SqlDbType.DateTime2. 490 // With these types the values should be used directly as CLR types instead of being converted to a SqlValue GetSqlValueFromComVariant(object comVal)491 internal static object GetSqlValueFromComVariant(object comVal) { 492 object sqlVal = null; 493 if ((null != comVal) && (DBNull.Value != comVal)) { 494 if (comVal is float) 495 sqlVal = new SqlSingle((float)comVal); 496 else if (comVal is string) 497 sqlVal = new SqlString((string)comVal); 498 else if (comVal is double) 499 sqlVal = new SqlDouble((double)comVal); 500 else if (comVal is System.Byte[]) 501 sqlVal = new SqlBinary((byte[])comVal); 502 else if (comVal is System.Char) 503 sqlVal = new SqlString(((char)comVal).ToString()); 504 else if (comVal is System.Char[]) 505 sqlVal = new SqlChars((System.Char[])comVal); 506 else if (comVal is System.Guid) 507 sqlVal = new SqlGuid((Guid)comVal); 508 else if (comVal is bool) 509 sqlVal = new SqlBoolean((bool)comVal); 510 else if (comVal is byte) 511 sqlVal = new SqlByte((byte)comVal); 512 else if (comVal is Int16) 513 sqlVal = new SqlInt16((Int16)comVal); 514 else if (comVal is Int32) 515 sqlVal = new SqlInt32((Int32)comVal); 516 else if (comVal is Int64) 517 sqlVal = new SqlInt64((Int64)comVal); 518 else if (comVal is Decimal) 519 sqlVal = new SqlDecimal((Decimal)comVal); 520 else if (comVal is DateTime) { 521 // devnote: Do not use with SqlDbType.Date and SqlDbType.DateTime2. See comment at top of method. 522 sqlVal = new SqlDateTime((DateTime)comVal); 523 } else if (comVal is XmlReader) 524 sqlVal = new SqlXml((XmlReader)comVal); 525 else if (comVal is TimeSpan || comVal is DateTimeOffset) 526 sqlVal = comVal; 527 #if DEBUG 528 else 529 Debug.Assert(false, "unknown SqlType class stored in sqlVal"); 530 #endif 531 } 532 return sqlVal; 533 } 534 GetSqlDbTypeFromOleDbType(short dbType, string typeName)535 internal static SqlDbType GetSqlDbTypeFromOleDbType(short dbType, string typeName) { 536 SqlDbType sqlType = SqlDbType.Variant; 537 switch ((OleDbType)dbType) { 538 case OleDbType.BigInt: 539 sqlType = SqlDbType.BigInt; 540 break; 541 case OleDbType.Boolean: 542 sqlType = SqlDbType.Bit; 543 break; 544 case OleDbType.Char: 545 case OleDbType.VarChar: 546 // these guys are ambiguous - server sends over DBTYPE_STR in both cases 547 sqlType = (typeName == MetaTypeName.CHAR) ? SqlDbType.Char : SqlDbType.VarChar; 548 break; 549 case OleDbType.Currency: 550 sqlType = (typeName == MetaTypeName.SMALLMONEY) ? SqlDbType.SmallMoney : SqlDbType.Money; 551 break; 552 case OleDbType.Date: 553 case OleDbType.DBTimeStamp: 554 case OleDbType.Filetime: 555 switch (typeName) { 556 case MetaTypeName.SMALLDATETIME: 557 sqlType = SqlDbType.SmallDateTime; 558 break; 559 case MetaTypeName.DATETIME2: 560 sqlType = SqlDbType.DateTime2; 561 break; 562 default: 563 sqlType = SqlDbType.DateTime; 564 break; 565 } 566 break; 567 case OleDbType.Decimal: 568 case OleDbType.Numeric: 569 sqlType = SqlDbType.Decimal; 570 break; 571 case OleDbType.Double: 572 sqlType = SqlDbType.Float; 573 break; 574 case OleDbType.Guid: 575 sqlType = SqlDbType.UniqueIdentifier; 576 break; 577 case OleDbType.Integer: 578 sqlType = SqlDbType.Int; 579 break; 580 case OleDbType.LongVarBinary: 581 sqlType = SqlDbType.Image; 582 break; 583 case OleDbType.LongVarChar: 584 sqlType = SqlDbType.Text; 585 break; 586 case OleDbType.LongVarWChar: 587 sqlType = SqlDbType.NText; 588 break; 589 case OleDbType.Single: 590 sqlType = SqlDbType.Real; 591 break; 592 case OleDbType.SmallInt: 593 case OleDbType.UnsignedSmallInt: 594 sqlType = SqlDbType.SmallInt; 595 break; 596 case OleDbType.TinyInt: 597 case OleDbType.UnsignedTinyInt: 598 sqlType = SqlDbType.TinyInt; 599 break; 600 case OleDbType.VarBinary: 601 case OleDbType.Binary: 602 sqlType = (typeName == MetaTypeName.BINARY) ? SqlDbType.Binary : SqlDbType.VarBinary; 603 break; 604 case OleDbType.Variant: 605 sqlType = SqlDbType.Variant; 606 break; 607 case OleDbType.VarWChar: 608 case OleDbType.WChar: 609 case OleDbType.BSTR: 610 // these guys are ambiguous - server sends over DBTYPE_WSTR in both cases 611 // BSTR is always assumed to be NVARCHAR 612 sqlType = (typeName == MetaTypeName.NCHAR) ? SqlDbType.NChar : SqlDbType.NVarChar; 613 break; 614 case OleDbType.DBDate: // Date 615 sqlType = SqlDbType.Date; 616 break; 617 case (OleDbType)132: // Udt 618 sqlType = SqlDbType.Udt; 619 break; 620 case (OleDbType)141: // Xml 621 sqlType = SqlDbType.Xml; 622 break; 623 case (OleDbType)145: // Time 624 sqlType = SqlDbType.Time; 625 break; 626 case (OleDbType)146: // DateTimeOffset 627 sqlType = SqlDbType.DateTimeOffset; 628 break; 629 // 630 default: 631 break; // no direct mapping, just use SqlDbType.Variant; 632 } 633 634 return sqlType; 635 } 636 GetSqlDataType(int tdsType, UInt32 userType, int length)637 internal static MetaType GetSqlDataType(int tdsType, UInt32 userType, int length) { 638 switch (tdsType) { 639 case TdsEnums.SQLMONEYN: return ((4 == length) ? MetaSmallMoney : MetaMoney); 640 case TdsEnums.SQLDATETIMN: return ((4 == length) ? MetaSmallDateTime : MetaDateTime); 641 case TdsEnums.SQLINTN: return ((4 <= length) ? ((4 == length) ? MetaInt : MetaBigInt) : ((2 == length) ? MetaSmallInt : MetaTinyInt)); 642 case TdsEnums.SQLFLTN: return ((4 == length) ? MetaReal : MetaFloat); 643 case TdsEnums.SQLTEXT: return MetaText; 644 case TdsEnums.SQLVARBINARY: return MetaSmallVarBinary; 645 case TdsEnums.SQLBIGVARBINARY: return MetaVarBinary; 646 647 case TdsEnums.SQLVARCHAR: //goto TdsEnums.SQLBIGVARCHAR; 648 case TdsEnums.SQLBIGVARCHAR: return MetaVarChar; 649 650 case TdsEnums.SQLBINARY: //goto TdsEnums.SQLBIGBINARY; 651 case TdsEnums.SQLBIGBINARY: return ((TdsEnums.SQLTIMESTAMP == userType) ? MetaTimestamp : MetaBinary); 652 653 case TdsEnums.SQLIMAGE: return MetaImage; 654 655 case TdsEnums.SQLCHAR: //goto TdsEnums.SQLBIGCHAR; 656 case TdsEnums.SQLBIGCHAR: return MetaChar; 657 658 case TdsEnums.SQLINT1: return MetaTinyInt; 659 660 case TdsEnums.SQLBIT: //goto TdsEnums.SQLBITN; 661 case TdsEnums.SQLBITN: return MetaBit; 662 663 case TdsEnums.SQLINT2: return MetaSmallInt; 664 case TdsEnums.SQLINT4: return MetaInt; 665 case TdsEnums.SQLINT8: return MetaBigInt; 666 case TdsEnums.SQLMONEY: return MetaMoney; 667 case TdsEnums.SQLDATETIME: return MetaDateTime; 668 case TdsEnums.SQLFLT8: return MetaFloat; 669 case TdsEnums.SQLFLT4: return MetaReal; 670 case TdsEnums.SQLMONEY4: return MetaSmallMoney; 671 case TdsEnums.SQLDATETIM4: return MetaSmallDateTime; 672 673 case TdsEnums.SQLDECIMALN: //goto TdsEnums.SQLNUMERICN; 674 case TdsEnums.SQLNUMERICN: return MetaDecimal; 675 676 case TdsEnums.SQLUNIQUEID: return MetaUniqueId ; 677 case TdsEnums.SQLNCHAR: return MetaNChar; 678 case TdsEnums.SQLNVARCHAR: return MetaNVarChar; 679 case TdsEnums.SQLNTEXT: return MetaNText; 680 case TdsEnums.SQLVARIANT: return MetaVariant; 681 case TdsEnums.SQLUDT: return MetaUdt; 682 case TdsEnums.SQLXMLTYPE: return MetaXml; 683 case TdsEnums.SQLTABLE: return MetaTable; 684 case TdsEnums.SQLDATE: return MetaDate; 685 case TdsEnums.SQLTIME: return MetaTime; 686 case TdsEnums.SQLDATETIME2: return MetaDateTime2; 687 case TdsEnums.SQLDATETIMEOFFSET: return MetaDateTimeOffset; 688 689 case TdsEnums.SQLVOID: 690 default: 691 Debug.Assert(false, "Unknown type " + tdsType.ToString(CultureInfo.InvariantCulture)); 692 throw SQL.InvalidSqlDbType((SqlDbType)tdsType); 693 }// case 694 } 695 GetDefaultMetaType()696 internal static MetaType GetDefaultMetaType() { 697 return MetaNVarChar; 698 } 699 700 // Converts an XmlReader into String GetStringFromXml(XmlReader xmlreader)701 internal static String GetStringFromXml(XmlReader xmlreader) { 702 SqlXml sxml = new SqlXml(xmlreader); 703 return sxml.Value; 704 } 705 706 private static readonly MetaType MetaBigInt = new MetaType 707 (19, 255, 8, true, false, false, TdsEnums.SQLINT8, TdsEnums.SQLINTN, MetaTypeName.BIGINT, typeof(System.Int64), typeof(SqlInt64), SqlDbType.BigInt, DbType.Int64, 0); 708 709 private static readonly MetaType MetaFloat = new MetaType 710 (15, 255, 8, true, false, false, TdsEnums.SQLFLT8, TdsEnums.SQLFLTN, MetaTypeName.FLOAT, typeof(System.Double), typeof(SqlDouble), SqlDbType.Float, DbType.Double, 0); 711 712 private static readonly MetaType MetaReal = new MetaType 713 (7, 255, 4, true, false, false, TdsEnums.SQLFLT4, TdsEnums.SQLFLTN, MetaTypeName.REAL, typeof(System.Single), typeof(SqlSingle), SqlDbType.Real, DbType.Single, 0); 714 715 // MetaBinary has two bytes of properties for binary and varbinary 716 // 2 byte maxlen 717 private static readonly MetaType MetaBinary = new MetaType 718 (255, 255, -1, false, false, false, TdsEnums.SQLBIGBINARY, TdsEnums.SQLBIGBINARY, MetaTypeName.BINARY, typeof(System.Byte[]), typeof(SqlBinary), SqlDbType.Binary, DbType.Binary, 2); 719 720 // syntatic sugar for the user...timestamps are 8-byte fixed length binary columns 721 private static readonly MetaType MetaTimestamp = new MetaType 722 (255, 255, -1, false, false, false, TdsEnums.SQLBIGBINARY, TdsEnums.SQLBIGBINARY, MetaTypeName.TIMESTAMP, typeof(System.Byte[]), typeof(SqlBinary), SqlDbType.Timestamp, DbType.Binary, 2); 723 724 internal static readonly MetaType MetaVarBinary = new MetaType 725 (255, 255, -1, false, false, false, TdsEnums.SQLBIGVARBINARY, TdsEnums.SQLBIGVARBINARY, MetaTypeName.VARBINARY, typeof(System.Byte[]), typeof(SqlBinary), SqlDbType.VarBinary, DbType.Binary, 2); 726 727 internal static readonly MetaType MetaMaxVarBinary = new MetaType 728 (255, 255, -1, false, true, true, TdsEnums.SQLBIGVARBINARY, TdsEnums.SQLBIGVARBINARY, MetaTypeName.VARBINARY, typeof(System.Byte[]), typeof(SqlBinary), SqlDbType.VarBinary, DbType.Binary, 2); 729 730 // HACK!!! We have an internal type for smallvarbinarys stored on TdsEnums. We 731 // store on TdsEnums instead of SqlDbType because we do not want to expose 732 // this type to the user! 733 private static readonly MetaType MetaSmallVarBinary = new MetaType 734 (255, 255, -1, false, false, false, TdsEnums.SQLVARBINARY, TdsEnums.SQLBIGBINARY, ADP.StrEmpty, typeof(System.Byte[]), typeof(SqlBinary), TdsEnums.SmallVarBinary, DbType.Binary, 2); 735 736 internal static readonly MetaType MetaImage = new MetaType 737 (255, 255, -1, false, true, false, TdsEnums.SQLIMAGE, TdsEnums.SQLIMAGE, MetaTypeName.IMAGE, typeof(System.Byte[]), typeof(SqlBinary), SqlDbType.Image, DbType.Binary, 0); 738 739 private static readonly MetaType MetaBit = new MetaType 740 (255, 255, 1, true, false, false, TdsEnums.SQLBIT, TdsEnums.SQLBITN, MetaTypeName.BIT, typeof(System.Boolean), typeof(SqlBoolean), SqlDbType.Bit, DbType.Boolean, 0); 741 742 private static readonly MetaType MetaTinyInt = new MetaType 743 (3, 255, 1, true, false, false, TdsEnums.SQLINT1, TdsEnums.SQLINTN, MetaTypeName.TINYINT, typeof(System.Byte), typeof(SqlByte), SqlDbType.TinyInt, DbType.Byte, 0); 744 745 private static readonly MetaType MetaSmallInt = new MetaType 746 (5, 255, 2, true, false, false, TdsEnums.SQLINT2, TdsEnums.SQLINTN, MetaTypeName.SMALLINT, typeof(System.Int16), typeof(SqlInt16), SqlDbType.SmallInt, DbType.Int16, 0); 747 748 private static readonly MetaType MetaInt = new MetaType 749 (10, 255, 4, true, false, false, TdsEnums.SQLINT4, TdsEnums.SQLINTN, MetaTypeName.INT, typeof(System.Int32), typeof(SqlInt32), SqlDbType.Int, DbType.Int32, 0); 750 751 // MetaVariant has seven bytes of properties for MetaChar and MetaVarChar 752 // 5 byte tds collation 753 // 2 byte maxlen 754 private static readonly MetaType MetaChar = new MetaType 755 (255, 255, -1, false, false, false, TdsEnums.SQLBIGCHAR, TdsEnums.SQLBIGCHAR, MetaTypeName.CHAR, typeof(System.String), typeof(SqlString), SqlDbType.Char, DbType.AnsiStringFixedLength, 7); 756 757 private static readonly MetaType MetaVarChar = new MetaType 758 (255, 255, -1, false, false, false, TdsEnums.SQLBIGVARCHAR, TdsEnums.SQLBIGVARCHAR, MetaTypeName.VARCHAR, typeof(System.String), typeof(SqlString), SqlDbType.VarChar, DbType.AnsiString, 7); 759 760 internal static readonly MetaType MetaMaxVarChar = new MetaType 761 (255, 255, -1, false, true, true, TdsEnums.SQLBIGVARCHAR, TdsEnums.SQLBIGVARCHAR, MetaTypeName.VARCHAR, typeof(System.String), typeof(SqlString), SqlDbType.VarChar, DbType.AnsiString, 7); 762 763 internal static readonly MetaType MetaText = new MetaType 764 (255, 255, -1, false, true, false, TdsEnums.SQLTEXT, TdsEnums.SQLTEXT, MetaTypeName.TEXT, typeof(System.String), typeof(SqlString), SqlDbType.Text, DbType.AnsiString, 0); 765 766 // MetaVariant has seven bytes of properties for MetaNChar and MetaNVarChar 767 // 5 byte tds collation 768 // 2 byte maxlen 769 private static readonly MetaType MetaNChar = new MetaType 770 (255, 255, -1, false, false, false, TdsEnums.SQLNCHAR, TdsEnums.SQLNCHAR, MetaTypeName.NCHAR, typeof(System.String), typeof(SqlString), SqlDbType.NChar, DbType.StringFixedLength, 7); 771 772 internal static readonly MetaType MetaNVarChar = new MetaType 773 (255, 255, -1, false, false, false, TdsEnums.SQLNVARCHAR, TdsEnums.SQLNVARCHAR, MetaTypeName.NVARCHAR, typeof(System.String), typeof(SqlString), SqlDbType.NVarChar, DbType.String, 7); 774 775 internal static readonly MetaType MetaMaxNVarChar = new MetaType 776 (255, 255, -1, false, true, true, TdsEnums.SQLNVARCHAR, TdsEnums.SQLNVARCHAR, MetaTypeName.NVARCHAR, typeof(System.String), typeof(SqlString), SqlDbType.NVarChar, DbType.String, 7); 777 778 internal static readonly MetaType MetaNText = new MetaType 779 (255, 255, -1, false, true, false, TdsEnums.SQLNTEXT, TdsEnums.SQLNTEXT, MetaTypeName.NTEXT, typeof(System.String), typeof(SqlString), SqlDbType.NText, DbType.String, 7); 780 781 // MetaVariant has two bytes of properties for numeric/decimal types 782 // 1 byte precision 783 // 1 byte scale 784 internal static readonly MetaType MetaDecimal = new MetaType 785 (38, 4, 17, true, false, false, TdsEnums.SQLNUMERICN, TdsEnums.SQLNUMERICN, MetaTypeName.DECIMAL, typeof(System.Decimal), typeof(SqlDecimal), SqlDbType.Decimal, DbType.Decimal, 2); 786 787 internal static readonly MetaType MetaXml = new MetaType 788 (255, 255, -1, false, true, true, TdsEnums.SQLXMLTYPE, TdsEnums.SQLXMLTYPE, MetaTypeName.XML, typeof(System.String), typeof(SqlXml), SqlDbType.Xml, DbType.Xml, 0); 789 790 private static readonly MetaType MetaDateTime = new MetaType 791 (23, 3, 8, true, false, false, TdsEnums.SQLDATETIME, TdsEnums.SQLDATETIMN, MetaTypeName.DATETIME, typeof(System.DateTime), typeof(SqlDateTime), SqlDbType.DateTime, DbType.DateTime, 0); 792 793 private static readonly MetaType MetaSmallDateTime = new MetaType 794 (16, 0, 4, true, false, false, TdsEnums.SQLDATETIM4, TdsEnums.SQLDATETIMN, MetaTypeName.SMALLDATETIME, typeof(System.DateTime), typeof(SqlDateTime), SqlDbType.SmallDateTime, DbType.DateTime, 0); 795 796 private static readonly MetaType MetaMoney = new MetaType 797 (19, 255, 8, true, false, false, TdsEnums.SQLMONEY, TdsEnums.SQLMONEYN, MetaTypeName.MONEY, typeof(System.Decimal), typeof(SqlMoney), SqlDbType.Money, DbType.Currency, 0); 798 799 private static readonly MetaType MetaSmallMoney = new MetaType 800 (10, 255, 4, true, false, false, TdsEnums.SQLMONEY4, TdsEnums.SQLMONEYN, MetaTypeName.SMALLMONEY, typeof(System.Decimal), typeof(SqlMoney), SqlDbType.SmallMoney, DbType.Currency, 0); 801 802 private static readonly MetaType MetaUniqueId = new MetaType 803 (255, 255, 16, true, false, false, TdsEnums.SQLUNIQUEID, TdsEnums.SQLUNIQUEID, MetaTypeName.ROWGUID, typeof(System.Guid), typeof(SqlGuid), SqlDbType.UniqueIdentifier, DbType.Guid, 0); 804 805 private static readonly MetaType MetaVariant = new MetaType 806 (255, 255, -1, true, false, false, TdsEnums.SQLVARIANT, TdsEnums.SQLVARIANT, MetaTypeName.VARIANT, typeof(System.Object), typeof(System.Object), SqlDbType.Variant, DbType.Object, 0); 807 808 internal static readonly MetaType MetaUdt = new MetaType 809 (255, 255, -1, false, false, true, TdsEnums.SQLUDT, TdsEnums.SQLUDT, MetaTypeName.UDT, typeof(System.Object), typeof(System.Object), SqlDbType.Udt, DbType.Object, 0); 810 811 private static readonly MetaType MetaMaxUdt = new MetaType 812 (255, 255, -1, false, true, true, TdsEnums.SQLUDT, TdsEnums.SQLUDT, MetaTypeName.UDT, typeof(System.Object), typeof(System.Object), SqlDbType.Udt, DbType.Object, 0); 813 814 private static readonly MetaType MetaTable = new MetaType 815 (255, 255, -1, false, false, false, TdsEnums.SQLTABLE, TdsEnums.SQLTABLE, MetaTypeName.TABLE, typeof(IEnumerable<DbDataRecord>), typeof(IEnumerable<DbDataRecord>), SqlDbType.Structured, DbType.Object, 0); 816 817 // 818 819 private static readonly MetaType MetaSUDT = new MetaType 820 (255, 255, -1, false, false, false, TdsEnums.SQLVOID, TdsEnums.SQLVOID, "", typeof(MSS.SqlDataRecord), typeof(MSS.SqlDataRecord), SqlDbType.Structured, DbType.Object, 0); 821 822 private static readonly MetaType MetaDate = new MetaType 823 (255, 255, 3, true, false, false, TdsEnums.SQLDATE, TdsEnums.SQLDATE, MetaTypeName.DATE, typeof(System.DateTime), typeof(System.DateTime), SqlDbType.Date, DbType.Date, 0); 824 825 internal static readonly MetaType MetaTime = new MetaType 826 (255, 7, -1, false, false, false, TdsEnums.SQLTIME, TdsEnums.SQLTIME, MetaTypeName.TIME, typeof(System.TimeSpan), typeof(System.TimeSpan), SqlDbType.Time, DbType.Time, 1); 827 828 private static readonly MetaType MetaDateTime2 = new MetaType 829 (255, 7, -1, false, false, false, TdsEnums.SQLDATETIME2, TdsEnums.SQLDATETIME2, MetaTypeName.DATETIME2, typeof(System.DateTime), typeof(System.DateTime), SqlDbType.DateTime2, DbType.DateTime2, 1); 830 831 internal static readonly MetaType MetaDateTimeOffset = new MetaType 832 (255, 7, -1, false, false, false, TdsEnums.SQLDATETIMEOFFSET, TdsEnums.SQLDATETIMEOFFSET, MetaTypeName.DATETIMEOFFSET, typeof(System.DateTimeOffset), typeof(System.DateTimeOffset), SqlDbType.DateTimeOffset, DbType.DateTimeOffset, 1); 833 FromDateTime(DateTime dateTime, byte cb)834 public static TdsDateTime FromDateTime(DateTime dateTime, byte cb) { 835 SqlDateTime sqlDateTime; 836 TdsDateTime tdsDateTime = new TdsDateTime(); 837 838 Debug.Assert(cb == 8 || cb == 4, "Invalid date time size!"); 839 840 if (cb == 8) { 841 sqlDateTime = new SqlDateTime(dateTime); 842 tdsDateTime.time = sqlDateTime.TimeTicks; 843 } 844 else { 845 // note that smalldatetime is days&minutes. 846 // Adding 30 seconds ensures proper roundup if the seconds are >= 30 847 // The AddSeconds function handles eventual carryover 848 sqlDateTime = new SqlDateTime(dateTime.AddSeconds(30)); 849 tdsDateTime.time = sqlDateTime.TimeTicks / SqlDateTime.SQLTicksPerMinute; 850 } 851 tdsDateTime.days = sqlDateTime.DayTicks; 852 return tdsDateTime; 853 } 854 855 ToDateTime(int sqlDays, int sqlTime, int length)856 public static DateTime ToDateTime(int sqlDays, int sqlTime, int length) { 857 if (length == 4) { 858 return new SqlDateTime(sqlDays, sqlTime * SqlDateTime.SQLTicksPerMinute).Value; 859 } 860 else { 861 Debug.Assert(length == 8, "invalid length for DateTime"); 862 return new SqlDateTime(sqlDays, sqlTime).Value; 863 } 864 } 865 GetTimeSizeFromScale(byte scale)866 internal static int GetTimeSizeFromScale(byte scale) 867 { 868 // Disable the assert here since we do not properly handle wrong Scale value on the parameter, 869 // see VSTFDEVDIV 795578 for more details. 870 // But, this assert is still valid when we receive Time/DateTime2/DateTimeOffset scale from server over TDS, 871 // so it is moved to TdsParser.CommonProcessMetaData. 872 // For new scenarios, assert and/or validate the scale value before this call! 873 // Debug.Assert(0 <= scale && scale <= 7); 874 875 if (scale <= 2) 876 return 3; 877 878 if (scale <= 4) 879 return 4; 880 881 return 5; 882 } 883 884 // 885 // please leave string sorted alphabetically 886 // note that these names should only be used in the context of parameters. We always send over BIG* and nullable types for SQL Server 887 // 888 private static class MetaTypeName { 889 public const string BIGINT = "bigint"; 890 public const string BINARY = "binary"; 891 public const string BIT = "bit"; 892 public const string CHAR = "char"; 893 public const string DATETIME = "datetime"; 894 public const string DECIMAL = "decimal"; 895 public const string FLOAT = "float"; 896 public const string IMAGE = "image"; 897 public const string INT = "int"; 898 public const string MONEY = "money"; 899 public const string NCHAR = "nchar"; 900 public const string NTEXT = "ntext"; 901 public const string NVARCHAR = "nvarchar"; 902 public const string REAL = "real"; 903 public const string ROWGUID = "uniqueidentifier"; 904 public const string SMALLDATETIME = "smalldatetime"; 905 public const string SMALLINT = "smallint"; 906 public const string SMALLMONEY = "smallmoney"; 907 public const string TEXT = "text"; 908 public const string TIMESTAMP = "timestamp"; 909 public const string TINYINT = "tinyint"; 910 public const string UDT = "udt"; 911 public const string VARBINARY = "varbinary"; 912 public const string VARCHAR = "varchar"; 913 public const string VARIANT = "sql_variant"; 914 public const string XML = "xml"; 915 public const string TABLE = "table"; 916 public const string DATE = "date"; 917 public const string TIME = "time"; 918 public const string DATETIME2 = "datetime2"; 919 public const string DATETIMEOFFSET = "datetimeoffset"; 920 } 921 } 922 923 // 924 // note: it is the client's responsibility to know what size date time he is working with 925 // 926 internal struct TdsDateTime { 927 public int days; // offset in days from 1/1/1900 928 // private UInt32 time; // if smalldatetime, this is # of minutes since midnight 929 // otherwise: # of 1/300th of a second since midnight 930 public int time; // 931 } 932 933 } 934 935