1 // Licensed to the .NET Foundation under one or more agreements. 2 // The .NET Foundation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 6 7 //------------------------------------------------------------------------------ 8 9 using System.Collections.Generic; 10 using System.Collections.ObjectModel; 11 using System.Data.Common; 12 using System.Data.SqlTypes; 13 using System.Diagnostics; 14 using System.Text; 15 using Microsoft.SqlServer.Server; 16 17 namespace System.Data.SqlClient 18 { 19 internal enum CallbackType 20 { 21 Read = 0, 22 Write = 1 23 } 24 25 internal enum EncryptionOptions 26 { 27 OFF, 28 ON, 29 NOT_SUP, 30 REQ, 31 LOGIN 32 } 33 34 internal enum PreLoginHandshakeStatus 35 { 36 Successful, 37 InstanceFailure 38 } 39 40 internal enum PreLoginOptions 41 { 42 VERSION, 43 ENCRYPT, 44 INSTANCE, 45 THREADID, 46 MARS, 47 TRACEID, 48 NUMOPT, 49 LASTOPT = 255 50 } 51 52 internal enum RunBehavior 53 { 54 UntilDone = 1, // 0001 binary 55 ReturnImmediately = 2, // 0010 binary 56 Clean = 5, // 0101 binary - Clean AND UntilDone 57 Attention = 13 // 1101 binary - Clean AND UntilDone AND Attention 58 } 59 60 internal enum TdsParserState 61 { 62 Closed, 63 OpenNotLoggedIn, 64 OpenLoggedIn, 65 Broken, 66 } 67 68 sealed internal class SqlCollation 69 { 70 // First 20 bits of info field represent the lcid, bits 21-25 are compare options 71 private const uint IgnoreCase = 1 << 20; // bit 21 - IgnoreCase 72 private const uint IgnoreNonSpace = 1 << 21; // bit 22 - IgnoreNonSpace / IgnoreAccent 73 private const uint IgnoreWidth = 1 << 22; // bit 23 - IgnoreWidth 74 private const uint IgnoreKanaType = 1 << 23; // bit 24 - IgnoreKanaType 75 private const uint BinarySort = 1 << 24; // bit 25 - BinarySort 76 77 internal const uint MaskLcid = 0xfffff; 78 private const int LcidVersionBitOffset = 28; 79 private const uint MaskLcidVersion = unchecked((uint)(0xf << LcidVersionBitOffset)); 80 private const uint MaskCompareOpt = IgnoreCase | IgnoreNonSpace | IgnoreWidth | IgnoreKanaType | BinarySort; 81 82 internal uint info; 83 internal byte sortId; 84 FirstSupportedCollationVersion(int lcid)85 private static int FirstSupportedCollationVersion(int lcid) 86 { 87 // NOTE: switch-case works ~3 times faster in this case than search with Dictionary 88 switch (lcid) 89 { 90 case 1044: return 2; // Norwegian_100_BIN 91 case 1047: return 2; // Romansh_100_BIN 92 case 1056: return 2; // Urdu_100_BIN 93 case 1065: return 2; // Persian_100_BIN 94 case 1068: return 2; // Azeri_Latin_100_BIN 95 case 1070: return 2; // Upper_Sorbian_100_BIN 96 case 1071: return 1; // Macedonian_FYROM_90_BIN 97 case 1081: return 1; // Indic_General_90_BIN 98 case 1082: return 2; // Maltese_100_BIN 99 case 1083: return 2; // Sami_Norway_100_BIN 100 case 1087: return 1; // Kazakh_90_BIN 101 case 1090: return 2; // Turkmen_100_BIN 102 case 1091: return 1; // Uzbek_Latin_90_BIN 103 case 1092: return 1; // Tatar_90_BIN 104 case 1093: return 2; // Bengali_100_BIN 105 case 1101: return 2; // Assamese_100_BIN 106 case 1105: return 2; // Tibetan_100_BIN 107 case 1106: return 2; // Welsh_100_BIN 108 case 1107: return 2; // Khmer_100_BIN 109 case 1108: return 2; // Lao_100_BIN 110 case 1114: return 1; // Syriac_90_BIN 111 case 1121: return 2; // Nepali_100_BIN 112 case 1122: return 2; // Frisian_100_BIN 113 case 1123: return 2; // Pashto_100_BIN 114 case 1125: return 1; // Divehi_90_BIN 115 case 1133: return 2; // Bashkir_100_BIN 116 case 1146: return 2; // Mapudungan_100_BIN 117 case 1148: return 2; // Mohawk_100_BIN 118 case 1150: return 2; // Breton_100_BIN 119 case 1152: return 2; // Uighur_100_BIN 120 case 1153: return 2; // Maori_100_BIN 121 case 1155: return 2; // Corsican_100_BIN 122 case 1157: return 2; // Yakut_100_BIN 123 case 1164: return 2; // Dari_100_BIN 124 case 2074: return 2; // Serbian_Latin_100_BIN 125 case 2092: return 2; // Azeri_Cyrillic_100_BIN 126 case 2107: return 2; // Sami_Sweden_Finland_100_BIN 127 case 2143: return 2; // Tamazight_100_BIN 128 case 3076: return 1; // Chinese_Hong_Kong_Stroke_90_BIN 129 case 3098: return 2; // Serbian_Cyrillic_100_BIN 130 case 5124: return 2; // Chinese_Traditional_Pinyin_100_BIN 131 case 5146: return 2; // Bosnian_Latin_100_BIN 132 case 8218: return 2; // Bosnian_Cyrillic_100_BIN 133 134 default: return 0; // other LCIDs have collation with version 0 135 } 136 } 137 138 internal int LCID 139 { 140 // First 20 bits of info field represent the lcid 141 get 142 { 143 return unchecked((int)(info & MaskLcid)); 144 } 145 set 146 { 147 int lcid = value & (int)MaskLcid; 148 Debug.Assert(lcid == value, "invalid set_LCID value"); 149 150 // Some new Katmai LCIDs do not have collation with version = 0 151 // since user has no way to specify collation version, we set the first (minimal) supported version for these collations 152 int versionBits = FirstSupportedCollationVersion(lcid) << LcidVersionBitOffset; 153 Debug.Assert((versionBits & MaskLcidVersion) == versionBits, "invalid version returned by FirstSupportedCollationVersion"); 154 155 // combine the current compare options with the new locale ID and its first supported version 156 info = (info & MaskCompareOpt) | unchecked((uint)lcid) | unchecked((uint)versionBits); 157 } 158 } 159 160 internal SqlCompareOptions SqlCompareOptions 161 { 162 get 163 { 164 SqlCompareOptions options = SqlCompareOptions.None; 165 if (0 != (info & IgnoreCase)) 166 options |= SqlCompareOptions.IgnoreCase; 167 if (0 != (info & IgnoreNonSpace)) 168 options |= SqlCompareOptions.IgnoreNonSpace; 169 if (0 != (info & IgnoreWidth)) 170 options |= SqlCompareOptions.IgnoreWidth; 171 if (0 != (info & IgnoreKanaType)) 172 options |= SqlCompareOptions.IgnoreKanaType; 173 if (0 != (info & BinarySort)) 174 options |= SqlCompareOptions.BinarySort; 175 return options; 176 } 177 set 178 { 179 Debug.Assert((value & SqlTypeWorkarounds.SqlStringValidSqlCompareOptionMask) == value, "invalid set_SqlCompareOptions value"); 180 uint tmp = 0; 181 if (0 != (value & SqlCompareOptions.IgnoreCase)) 182 tmp |= IgnoreCase; 183 if (0 != (value & SqlCompareOptions.IgnoreNonSpace)) 184 tmp |= IgnoreNonSpace; 185 if (0 != (value & SqlCompareOptions.IgnoreWidth)) 186 tmp |= IgnoreWidth; 187 if (0 != (value & SqlCompareOptions.IgnoreKanaType)) 188 tmp |= IgnoreKanaType; 189 if (0 != (value & SqlCompareOptions.BinarySort)) 190 tmp |= BinarySort; 191 info = (info & MaskLcid) | tmp; 192 } 193 } 194 195 AreSame(SqlCollation a, SqlCollation b)196 internal static bool AreSame(SqlCollation a, SqlCollation b) 197 { 198 if (a == null || b == null) 199 { 200 return a == b; 201 } 202 else 203 { 204 return a.info == b.info && a.sortId == b.sortId; 205 } 206 } 207 } 208 209 internal class RoutingInfo 210 { 211 internal byte Protocol { get; private set; } 212 internal UInt16 Port { get; private set; } 213 internal string ServerName { get; private set; } 214 RoutingInfo(byte protocol, UInt16 port, string servername)215 internal RoutingInfo(byte protocol, UInt16 port, string servername) 216 { 217 Protocol = protocol; 218 Port = port; 219 ServerName = servername; 220 } 221 } 222 223 sealed internal class SqlEnvChange 224 { 225 internal byte type; 226 internal byte oldLength; 227 internal int newLength; // 7206 TDS changes makes this length an int 228 internal int length; 229 internal string newValue; 230 internal string oldValue; 231 internal byte[] newBinValue; 232 internal byte[] oldBinValue; 233 internal long newLongValue; 234 internal long oldLongValue; 235 internal SqlCollation newCollation; 236 internal SqlCollation oldCollation; 237 internal RoutingInfo newRoutingInfo; 238 } 239 240 sealed internal class SqlLogin 241 { 242 internal int timeout; // login timeout 243 internal bool userInstance = false; // user instance 244 internal string hostName = ""; // client machine name 245 internal string userName = ""; // user id 246 internal string password = ""; // password 247 internal string applicationName = ""; // application name 248 internal string serverName = ""; // server name 249 internal string language = ""; // initial language 250 internal string database = ""; // initial database 251 internal string attachDBFilename = ""; // DB filename to be attached 252 internal bool useReplication = false; // user login for replication 253 internal bool useSSPI = false; // use integrated security 254 internal int packetSize = SqlConnectionString.DEFAULT.Packet_Size; // packet size 255 internal bool readOnlyIntent = false; // read-only intent 256 } 257 258 sealed internal class SqlLoginAck 259 { 260 internal byte majorVersion; 261 internal byte minorVersion; 262 internal short buildNum; 263 internal UInt32 tdsVersion; 264 } 265 266 sealed internal class _SqlMetaData : SqlMetaDataPriv 267 { 268 internal string column; 269 internal string baseColumn; 270 internal MultiPartTableName multiPartTableName; 271 internal readonly int ordinal; 272 internal byte updatability; // two bit field (0 is read only, 1 is updatable, 2 is updatability unknown) 273 internal byte tableNum; 274 internal bool isDifferentName; 275 internal bool isKey; 276 internal bool isHidden; 277 internal bool isExpression; 278 internal bool isIdentity; 279 internal bool isColumnSet; 280 internal byte op; // for altrow-columns only 281 internal ushort operand; // for altrow-columns only 282 _SqlMetaData(int ordinal)283 internal _SqlMetaData(int ordinal) : base() 284 { 285 this.ordinal = ordinal; 286 } 287 288 internal string serverName 289 { 290 get 291 { 292 return multiPartTableName.ServerName; 293 } 294 } 295 internal string catalogName 296 { 297 get 298 { 299 return multiPartTableName.CatalogName; 300 } 301 } 302 internal string schemaName 303 { 304 get 305 { 306 return multiPartTableName.SchemaName; 307 } 308 } 309 internal string tableName 310 { 311 get 312 { 313 return multiPartTableName.TableName; 314 } 315 } 316 317 internal bool IsNewKatmaiDateTimeType 318 { 319 get 320 { 321 return SqlDbType.Date == type || SqlDbType.Time == type || SqlDbType.DateTime2 == type || SqlDbType.DateTimeOffset == type; 322 } 323 } 324 325 internal bool IsLargeUdt 326 { 327 get 328 { 329 return type == SqlDbType.Udt && length == Int32.MaxValue; 330 } 331 } 332 Clone()333 public object Clone() 334 { 335 _SqlMetaData result = new _SqlMetaData(ordinal); 336 result.CopyFrom(this); 337 result.column = column; 338 result.baseColumn = baseColumn; 339 result.multiPartTableName = multiPartTableName; 340 result.updatability = updatability; 341 result.tableNum = tableNum; 342 result.isDifferentName = isDifferentName; 343 result.isKey = isKey; 344 result.isHidden = isHidden; 345 result.isExpression = isExpression; 346 result.isIdentity = isIdentity; 347 result.isColumnSet = isColumnSet; 348 result.op = op; 349 result.operand = operand; 350 return result; 351 } 352 } 353 354 sealed internal class _SqlMetaDataSet 355 { 356 internal ushort id; // for altrow-columns only 357 internal int[] indexMap; 358 internal int visibleColumns; 359 internal DataTable schemaTable; 360 private readonly _SqlMetaData[] _metaDataArray; 361 internal ReadOnlyCollection<DbColumn> dbColumnSchema; 362 _SqlMetaDataSet(int count)363 internal _SqlMetaDataSet(int count) 364 { 365 _metaDataArray = new _SqlMetaData[count]; 366 for (int i = 0; i < _metaDataArray.Length; ++i) 367 { 368 _metaDataArray[i] = new _SqlMetaData(i); 369 } 370 } 371 _SqlMetaDataSet(_SqlMetaDataSet original)372 private _SqlMetaDataSet(_SqlMetaDataSet original) 373 { 374 this.id = original.id; 375 // although indexMap is not immutable, in practice it is initialized once and then passed around 376 this.indexMap = original.indexMap; 377 this.visibleColumns = original.visibleColumns; 378 this.dbColumnSchema = original.dbColumnSchema; 379 if (original._metaDataArray == null) 380 { 381 _metaDataArray = null; 382 } 383 else 384 { 385 _metaDataArray = new _SqlMetaData[original._metaDataArray.Length]; 386 for (int idx = 0; idx < _metaDataArray.Length; idx++) 387 { 388 _metaDataArray[idx] = (_SqlMetaData)original._metaDataArray[idx].Clone(); 389 } 390 } 391 } 392 393 internal int Length 394 { 395 get 396 { 397 return _metaDataArray.Length; 398 } 399 } 400 401 internal _SqlMetaData this[int index] 402 { 403 get 404 { 405 return _metaDataArray[index]; 406 } 407 set 408 { 409 Debug.Assert(null == value, "used only by SqlBulkCopy"); 410 _metaDataArray[index] = value; 411 } 412 } 413 Clone()414 public object Clone() 415 { 416 return new _SqlMetaDataSet(this); 417 } 418 } 419 420 sealed internal class _SqlMetaDataSetCollection 421 { 422 private readonly List<_SqlMetaDataSet> _altMetaDataSetArray; 423 internal _SqlMetaDataSet metaDataSet; 424 _SqlMetaDataSetCollection()425 internal _SqlMetaDataSetCollection() 426 { 427 _altMetaDataSetArray = new List<_SqlMetaDataSet>(); 428 } 429 SetAltMetaData(_SqlMetaDataSet altMetaDataSet)430 internal void SetAltMetaData(_SqlMetaDataSet altMetaDataSet) 431 { 432 // If altmetadata with same id is found, override it rather than adding a new one 433 int newId = altMetaDataSet.id; 434 for (int i = 0; i < _altMetaDataSetArray.Count; i++) 435 { 436 if (_altMetaDataSetArray[i].id == newId) 437 { 438 // override the existing metadata with the same id 439 _altMetaDataSetArray[i] = altMetaDataSet; 440 return; 441 } 442 } 443 444 // if we did not find metadata to override, add as new 445 _altMetaDataSetArray.Add(altMetaDataSet); 446 } 447 GetAltMetaData(int id)448 internal _SqlMetaDataSet GetAltMetaData(int id) 449 { 450 foreach (_SqlMetaDataSet altMetaDataSet in _altMetaDataSetArray) 451 { 452 if (altMetaDataSet.id == id) 453 { 454 return altMetaDataSet; 455 } 456 } 457 Debug.Assert(false, "Can't match up altMetaDataSet with given id"); 458 return null; 459 } 460 Clone()461 public object Clone() 462 { 463 _SqlMetaDataSetCollection result = new _SqlMetaDataSetCollection(); 464 result.metaDataSet = metaDataSet == null ? null : (_SqlMetaDataSet)metaDataSet.Clone(); 465 foreach (_SqlMetaDataSet set in _altMetaDataSetArray) 466 { 467 result._altMetaDataSetArray.Add((_SqlMetaDataSet)set.Clone()); 468 } 469 return result; 470 } 471 } 472 473 internal class SqlMetaDataPriv 474 { 475 internal SqlDbType type; // SqlDbType enum value 476 internal byte tdsType; // underlying tds type 477 internal byte precision = TdsEnums.UNKNOWN_PRECISION_SCALE; // give default of unknown (-1) 478 internal byte scale = TdsEnums.UNKNOWN_PRECISION_SCALE; // give default of unknown (-1) 479 internal int length; 480 internal SqlCollation collation; 481 internal int codePage; 482 internal Encoding encoding; 483 internal bool isNullable; 484 internal bool isMultiValued = false; 485 486 // UDT specific metadata 487 // server metadata info 488 // additional temporary UDT meta data 489 internal string udtDatabaseName; 490 internal string udtSchemaName; 491 internal string udtTypeName; 492 internal string udtAssemblyQualifiedName; 493 494 // on demand 495 internal Type udtType; 496 497 // Xml specific metadata 498 internal string xmlSchemaCollectionDatabase; 499 internal string xmlSchemaCollectionOwningSchema; 500 internal string xmlSchemaCollectionName; 501 internal MetaType metaType; // cached metaType 502 503 // Structured type-specific metadata 504 internal string structuredTypeDatabaseName; 505 internal string structuredTypeSchemaName; 506 internal string structuredTypeName; 507 internal IList<SmiMetaData> structuredFields; 508 SqlMetaDataPriv()509 internal SqlMetaDataPriv() 510 { 511 } 512 CopyFrom(SqlMetaDataPriv original)513 internal virtual void CopyFrom(SqlMetaDataPriv original) 514 { 515 this.type = original.type; 516 this.tdsType = original.tdsType; 517 this.precision = original.precision; 518 this.scale = original.scale; 519 this.length = original.length; 520 this.collation = original.collation; 521 this.codePage = original.codePage; 522 this.encoding = original.encoding; 523 this.isNullable = original.isNullable; 524 this.isMultiValued = original.isMultiValued; 525 this.udtDatabaseName = original.udtDatabaseName; 526 this.udtSchemaName = original.udtSchemaName; 527 this.udtTypeName = original.udtTypeName; 528 this.udtAssemblyQualifiedName = original.udtAssemblyQualifiedName; 529 this.udtType = original.udtType; 530 this.xmlSchemaCollectionDatabase = original.xmlSchemaCollectionDatabase; 531 this.xmlSchemaCollectionOwningSchema = original.xmlSchemaCollectionOwningSchema; 532 this.xmlSchemaCollectionName = original.xmlSchemaCollectionName; 533 this.metaType = original.metaType; 534 535 this.structuredTypeDatabaseName = original.structuredTypeDatabaseName; 536 this.structuredTypeSchemaName = original.structuredTypeSchemaName; 537 this.structuredTypeName = original.structuredTypeName; 538 this.structuredFields = original.structuredFields; 539 } 540 } 541 542 sealed internal class _SqlRPC 543 { 544 internal string rpcName; 545 internal ushort ProcID; // Used instead of name 546 internal ushort options; 547 internal SqlParameter[] parameters; 548 internal byte[] paramoptions; 549 550 internal int? recordsAffected; 551 internal int cumulativeRecordsAffected; 552 553 internal int errorsIndexStart; 554 internal int errorsIndexEnd; 555 internal SqlErrorCollection errors; 556 557 internal int warningsIndexStart; 558 internal int warningsIndexEnd; 559 internal SqlErrorCollection warnings; 560 GetCommandTextOrRpcName()561 internal string GetCommandTextOrRpcName() 562 { 563 if (TdsEnums.RPC_PROCID_EXECUTESQL == ProcID) 564 { 565 // Param 0 is the actual sql executing 566 return (string)parameters[0].Value; 567 } 568 else 569 { 570 return rpcName; 571 } 572 } 573 } 574 575 sealed internal class SqlReturnValue : SqlMetaDataPriv 576 { 577 internal string parameter; 578 internal readonly SqlBuffer value; 579 SqlReturnValue()580 internal SqlReturnValue() : base() 581 { 582 value = new SqlBuffer(); 583 } 584 } 585 586 internal struct MultiPartTableName 587 { 588 private string _multipartName; 589 private string _serverName; 590 private string _catalogName; 591 private string _schemaName; 592 private string _tableName; 593 MultiPartTableNameSystem.Data.SqlClient.MultiPartTableName594 internal MultiPartTableName(string[] parts) 595 { 596 _multipartName = null; 597 _serverName = parts[0]; 598 _catalogName = parts[1]; 599 _schemaName = parts[2]; 600 _tableName = parts[3]; 601 } 602 MultiPartTableNameSystem.Data.SqlClient.MultiPartTableName603 internal MultiPartTableName(string multipartName) 604 { 605 _multipartName = multipartName; 606 _serverName = null; 607 _catalogName = null; 608 _schemaName = null; 609 _tableName = null; 610 } 611 612 internal string ServerName 613 { 614 get 615 { 616 ParseMultipartName(); 617 return _serverName; 618 } 619 set { _serverName = value; } 620 } 621 internal string CatalogName 622 { 623 get 624 { 625 ParseMultipartName(); 626 return _catalogName; 627 } 628 set { _catalogName = value; } 629 } 630 internal string SchemaName 631 { 632 get 633 { 634 ParseMultipartName(); 635 return _schemaName; 636 } 637 set { _schemaName = value; } 638 } 639 internal string TableName 640 { 641 get 642 { 643 ParseMultipartName(); 644 return _tableName; 645 } 646 set { _tableName = value; } 647 } 648 ParseMultipartNameSystem.Data.SqlClient.MultiPartTableName649 private void ParseMultipartName() 650 { 651 if (null != _multipartName) 652 { 653 string[] parts = MultipartIdentifier.ParseMultipartIdentifier(_multipartName, "[\"", "]\"", SR.SQL_TDSParserTableName, false); 654 _serverName = parts[0]; 655 _catalogName = parts[1]; 656 _schemaName = parts[2]; 657 _tableName = parts[3]; 658 _multipartName = null; 659 } 660 } 661 662 internal static readonly MultiPartTableName Null = new MultiPartTableName(new string[] { null, null, null, null }); 663 } 664 } 665