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