1 //------------------------------------------------------------------------------
2 // <copyright file="TdsParserHelperClasses.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 System.Data.SqlClient {
11 using System.Collections.Generic;
13 using System.Data.Common;
14 using System.Data.ProviderBase;
15 using System.Data.Sql;
16 using System.Data.SqlTypes;
17 using System.Diagnostics;
18 using System.Reflection;
20 using System.Threading;
21 using System.Security;
22 using System.Globalization;
24 using Microsoft.SqlServer.Server; // for SMI metadata
26 internal enum CallbackType {
31 internal enum EncryptionOptions {
39 internal enum PreLoginHandshakeStatus {
44 internal enum PreLoginOptions {
56 internal enum RunBehavior {
57 UntilDone = 1, // 0001 binary
58 ReturnImmediately = 2, // 0010 binary
59 Clean = 5, // 0101 binary - Clean AND UntilDone
60 Attention = 13 // 1101 binary - Clean AND UntilDone AND Attention
63 internal enum TdsParserState {
71 /// Struct encapsulating the data to be sent to the server as part of Federated Authentication Feature Extension.
73 internal struct FederatedAuthenticationFeatureExtensionData
75 internal TdsEnums.FedAuthLibrary libraryType;
76 internal bool fedAuthRequiredPreLoginResponse;
77 internal SqlAuthenticationMethod authentication;
78 internal byte[] accessToken;
82 /// <para> Represents a single encrypted value for a CEK. It contains the encrypted CEK,
83 /// the store type, name,the key path and encryption algorithm.</para>
85 internal struct SqlEncryptionKeyInfo {
86 internal byte[] encryptedKey; // the encrypted "column encryption key"
87 internal int databaseId;
89 internal int cekVersion;
90 internal byte[] cekMdVersion;
91 internal string keyPath;
92 internal string keyStoreName;
93 internal string algorithmName;
94 internal byte normalizationRuleVersion;
98 /// <para> Encapsulates one entry in the CipherInfo table sent as part of Colmetadata.
99 /// The same CEK is encrypted multiple times with different master keys (for master key
100 /// rotation scenario) We need to keep all these around until we can resolve the CEK
101 /// using the correct master key.</para>
103 internal struct SqlTceCipherInfoEntry {
106 /// List of Column Encryption Key Information.
108 private readonly List<SqlEncryptionKeyInfo> _columnEncryptionKeyValues;
113 private readonly int _ordinal;
118 private int _databaseId;
128 private int _cekVersion;
133 private byte[] _cekMdVersion;
136 /// Return the ordinal.
138 internal int Ordinal {
145 /// Return the DatabaseID.
147 internal int DatabaseId {
154 /// Return the CEK ID.
163 /// Return the CEK Version.
165 internal int CekVersion {
172 /// Return the CEK MD Version.
174 internal byte[] CekMdVersion {
176 return _cekMdVersion;
181 /// Return the list of Column Encryption Key Values.
183 internal List<SqlEncryptionKeyInfo> ColumnEncryptionKeyValues {
185 return _columnEncryptionKeyValues;
190 /// Add an entry to the list of ColumnEncryptionKeyValues.
192 /// <param name="encryptedKey"></param>
193 /// <param name="databaseId"></param>
194 /// <param name="cekId"></param>
195 /// <param name="cekVersion"></param>
196 /// <param name="cekMdVersion"></param>
197 /// <param name="keyPath"></param>
198 /// <param name="keyStoreName"></param>
199 /// <param name="algorithmName"></param>
200 internal void Add(byte[] encryptedKey, int databaseId, int cekId, int cekVersion, byte[] cekMdVersion, string keyPath, string keyStoreName, string algorithmName) {
202 Debug.Assert(_columnEncryptionKeyValues != null, "_columnEncryptionKeyValues should already be initialized.");
204 SqlEncryptionKeyInfo encryptionKey = new SqlEncryptionKeyInfo();
205 encryptionKey.encryptedKey = encryptedKey;
206 encryptionKey.databaseId = databaseId;
207 encryptionKey.cekId = cekId;
208 encryptionKey.cekVersion = cekVersion;
209 encryptionKey.cekMdVersion = cekMdVersion;
210 encryptionKey.keyPath = keyPath;
211 encryptionKey.keyStoreName = keyStoreName;
212 encryptionKey.algorithmName = algorithmName;
213 _columnEncryptionKeyValues.Add(encryptionKey);
215 if (0 == _databaseId) {
216 _databaseId = databaseId;
218 _cekVersion = cekVersion;
219 _cekMdVersion = cekMdVersion;
222 Debug.Assert(_databaseId == databaseId);
223 Debug.Assert(_cekId == cekId);
224 Debug.Assert(_cekVersion == cekVersion);
225 Debug.Assert (_cekMdVersion != null && cekMdVersion != null && _cekMdVersion.Length == _cekMdVersion.Length);
232 /// <param name="ordinal"></param>
233 internal SqlTceCipherInfoEntry(int ordinal = 0) : this() {
238 _cekMdVersion = null;
239 _columnEncryptionKeyValues = new List<SqlEncryptionKeyInfo>();
244 /// <para> Represents a table with various CEKs used in a resultset. Each entry corresponds to one (unique) CEK. The CEK
245 /// may have been encrypted using multiple master keys (giving us multiple CEK values). All these values form one single
246 /// entry in this table.</para>
248 internal struct SqlTceCipherInfoTable {
249 private readonly SqlTceCipherInfoEntry [] keyList;
251 internal SqlTceCipherInfoTable (int tabSize) {
252 Debug.Assert (0 < tabSize, "Invalid Table Size");
253 keyList = new SqlTceCipherInfoEntry[tabSize];
256 internal SqlTceCipherInfoEntry this [int index] {
258 Debug.Assert (index < keyList.Length, "Invalid index specified.");
259 return keyList[index];
262 Debug.Assert (index < keyList.Length, "Invalid index specified.");
263 keyList[index] = value;
269 return keyList.Length;
274 sealed internal class SqlCollation {
275 // First 20 bits of info field represent the lcid, bits 21-25 are compare options
276 private const uint IgnoreCase = 1 << 20; // bit 21 - IgnoreCase
277 private const uint IgnoreNonSpace = 1 << 21; // bit 22 - IgnoreNonSpace / IgnoreAccent
278 private const uint IgnoreWidth = 1 << 22; // bit 23 - IgnoreWidth
279 private const uint IgnoreKanaType = 1 << 23; // bit 24 - IgnoreKanaType
280 private const uint BinarySort = 1 << 24; // bit 25 - BinarySort
282 internal const uint MaskLcid = 0xfffff;
283 private const int LcidVersionBitOffset = 28;
284 private const uint MaskLcidVersion = unchecked((uint)(0xf << LcidVersionBitOffset));
285 private const uint MaskCompareOpt = IgnoreCase | IgnoreNonSpace | IgnoreWidth | IgnoreKanaType | BinarySort;
288 internal byte sortId;
290 static int FirstSupportedCollationVersion(int lcid)
292 // NOTE: switch-case works ~3 times faster in this case than search with Dictionary
295 case 1044: return 2; // Norwegian_100_BIN
296 case 1047: return 2; // Romansh_100_BIN
297 case 1056: return 2; // Urdu_100_BIN
298 case 1065: return 2; // Persian_100_BIN
299 case 1068: return 2; // Azeri_Latin_100_BIN
300 case 1070: return 2; // Upper_Sorbian_100_BIN
301 case 1071: return 1; // ----n_FYROM_90_BIN
302 case 1081: return 1; // Indic_General_90_BIN
303 case 1082: return 2; // Maltese_100_BIN
304 case 1083: return 2; // Sami_Norway_100_BIN
305 case 1087: return 1; // Kazakh_90_BIN
306 case 1090: return 2; // Turkmen_100_BIN
307 case 1091: return 1; // Uzbek_Latin_90_BIN
308 case 1092: return 1; // Tatar_90_BIN
309 case 1093: return 2; // Bengali_100_BIN
310 case 1101: return 2; // Assamese_100_BIN
311 case 1105: return 2; // Tibetan_100_BIN
312 case 1106: return 2; // Welsh_100_BIN
313 case 1107: return 2; // Khmer_100_BIN
314 case 1108: return 2; // Lao_100_BIN
315 case 1114: return 1; // Syriac_90_BIN
316 case 1121: return 2; // Nepali_100_BIN
317 case 1122: return 2; // Frisian_100_BIN
318 case 1123: return 2; // Pashto_100_BIN
319 case 1125: return 1; // Divehi_90_BIN
320 case 1133: return 2; // Bashkir_100_BIN
321 case 1146: return 2; // Mapudungan_100_BIN
322 case 1148: return 2; // Mohawk_100_BIN
323 case 1150: return 2; // Breton_100_BIN
324 case 1152: return 2; // Uighur_100_BIN
325 case 1153: return 2; // Maori_100_BIN
326 case 1155: return 2; // Corsican_100_BIN
327 case 1157: return 2; // Yakut_100_BIN
328 case 1164: return 2; // Dari_100_BIN
329 case 2074: return 2; // Serbian_Latin_100_BIN
330 case 2092: return 2; // Azeri_Cyrillic_100_BIN
331 case 2107: return 2; // Sami_Sweden_Finland_100_BIN
332 case 2143: return 2; // Tamazight_100_BIN
333 case 3076: return 1; // Chinese_Hong_Kong_Stroke_90_BIN
334 case 3098: return 2; // Serbian_Cyrillic_100_BIN
335 case 5124: return 2; // Chinese_Traditional_Pinyin_100_BIN
336 case 5146: return 2; // Bosnian_Latin_100_BIN
337 case 8218: return 2; // Bosnian_Cyrillic_100_BIN
339 default: return 0; // other LCIDs have collation with version 0
344 // First 20 bits of info field represent the lcid
346 return unchecked((int)(info & MaskLcid));
349 int lcid = value & (int)MaskLcid;
350 Debug.Assert(lcid == value, "invalid set_LCID value");
352 // VSTFDEVDIV 479474: some new Katmai LCIDs do not have collation with version = 0
353 // since user has no way to specify collation version, we set the first (minimal) supported version for these collations
354 int versionBits = FirstSupportedCollationVersion(lcid) << LcidVersionBitOffset;
355 Debug.Assert((versionBits & MaskLcidVersion) == versionBits, "invalid version returned by FirstSupportedCollationVersion");
357 // combine the current compare options with the new locale ID and its first supported version
358 info = (info & MaskCompareOpt) | unchecked((uint)lcid) | unchecked((uint)versionBits);
362 internal SqlCompareOptions SqlCompareOptions {
364 SqlCompareOptions options = SqlCompareOptions.None;
365 if (0 != (info & IgnoreCase))
366 options |= SqlCompareOptions.IgnoreCase;
367 if (0 != (info & IgnoreNonSpace))
368 options |= SqlCompareOptions.IgnoreNonSpace;
369 if (0 != (info & IgnoreWidth))
370 options |= SqlCompareOptions.IgnoreWidth;
371 if (0 != (info & IgnoreKanaType))
372 options |= SqlCompareOptions.IgnoreKanaType;
373 if (0 != (info & BinarySort))
374 options |= SqlCompareOptions.BinarySort;
378 Debug.Assert((value & SqlString.x_iValidSqlCompareOptionMask) == value, "invalid set_SqlCompareOptions value");
380 if (0 != (value & SqlCompareOptions.IgnoreCase))
382 if (0 != (value & SqlCompareOptions.IgnoreNonSpace))
383 tmp |= IgnoreNonSpace;
384 if (0 != (value & SqlCompareOptions.IgnoreWidth))
386 if (0 != (value & SqlCompareOptions.IgnoreKanaType))
387 tmp |= IgnoreKanaType;
388 if (0 != (value & SqlCompareOptions.BinarySort))
390 info = (info & MaskLcid) | tmp;
394 internal string TraceString() {
395 return String.Format(/*IFormatProvider*/ null, "(LCID={0}, Opts={1})", this.LCID, (int)this.SqlCompareOptions);
398 static internal bool AreSame(SqlCollation a, SqlCollation b) {
399 if (a == null || b == null) {
403 return a.info == b.info && a.sortId == b.sortId;
410 internal class RoutingInfo {
411 internal byte Protocol { get; private set; }
412 internal UInt16 Port { get; private set; }
413 internal string ServerName { get; private set; }
415 internal RoutingInfo(byte protocol, UInt16 port, string servername) {
418 ServerName = servername;
422 sealed internal class SqlEnvChange {
424 internal byte oldLength;
425 internal int newLength; // 7206 TDS changes makes this length an int
427 internal string newValue;
428 internal string oldValue;
429 internal byte[] newBinValue;
430 internal byte[] oldBinValue;
431 internal long newLongValue;
432 internal long oldLongValue;
433 internal SqlCollation newCollation;
434 internal SqlCollation oldCollation;
435 internal RoutingInfo newRoutingInfo;
438 sealed internal class SqlLogin {
439 internal SqlAuthenticationMethod authentication = SqlAuthenticationMethod.NotSpecified; // Authentication type
440 internal int timeout; // login timeout
441 internal bool userInstance = false; // user instance
442 internal string hostName = ""; // client machine name
443 internal string userName = ""; // user id
444 internal string password = ""; // password
445 internal string applicationName = ""; // application name
446 internal string serverName = ""; // server name
447 internal string language = ""; // initial language
448 internal string database = ""; // initial database
449 internal string attachDBFilename = ""; // DB filename to be attached
450 internal string newPassword = ""; // new password for reset password
451 internal bool useReplication = false; // user login for replication
452 internal bool useSSPI = false; // use integrated security
453 internal int packetSize = SqlConnectionString.DEFAULT.Packet_Size; // packet size
454 internal bool readOnlyIntent = false; // read-only intent
455 internal SqlCredential credential; // user id and password in SecureString
456 internal SecureString newSecurePassword; // new password in SecureString for resetting pasword
459 sealed internal class SqlLoginAck {
460 internal string programName;
461 internal byte majorVersion;
462 internal byte minorVersion;
463 internal short buildNum;
464 internal bool isVersion8;
465 internal UInt32 tdsVersion;
468 sealed internal class SqlFedAuthInfo {
470 internal string stsurl;
471 public override string ToString() {
472 return String.Format(CultureInfo.InvariantCulture, "STSURL: {0}, SPN: {1}", stsurl ?? String.Empty, spn ?? String.Empty);
476 sealed internal class SqlFedAuthToken {
477 internal UInt32 dataLen;
478 internal byte[] accessToken;
479 internal long expirationFileTime;
482 sealed internal class _SqlMetaData : SqlMetaDataPriv, ICloneable {
484 internal string column;
485 internal string baseColumn;
486 internal MultiPartTableName multiPartTableName;
487 internal readonly int ordinal;
488 internal byte updatability; // two bit field (0 is read only, 1 is updatable, 2 is updatability unknown)
489 internal byte tableNum;
490 internal bool isDifferentName;
492 internal bool isHidden;
493 internal bool isExpression;
494 internal bool isIdentity;
495 internal bool isColumnSet;
496 internal byte op; // for altrow-columns only
497 internal ushort operand; // for altrow-columns only
499 internal _SqlMetaData(int ordinal) : base() {
500 this.ordinal = ordinal;
503 internal string serverName {
505 return multiPartTableName.ServerName;
508 internal string catalogName {
510 return multiPartTableName.CatalogName;
513 internal string schemaName {
515 return multiPartTableName.SchemaName;
518 internal string tableName {
520 return multiPartTableName.TableName;
524 internal bool IsNewKatmaiDateTimeType {
526 return SqlDbType.Date == type || SqlDbType.Time == type || SqlDbType.DateTime2 == type || SqlDbType.DateTimeOffset == type;
530 internal bool IsLargeUdt {
532 return type == SqlDbType.Udt && length == Int32.MaxValue;
536 public object Clone() {
537 _SqlMetaData result = new _SqlMetaData(ordinal);
538 result.CopyFrom(this);
539 result.column = column;
540 result.baseColumn = baseColumn;
541 result.multiPartTableName = multiPartTableName;
542 result.updatability = updatability;
543 result.tableNum = tableNum;
544 result.isDifferentName = isDifferentName;
545 result.isKey = isKey;
546 result.isHidden = isHidden;
547 result.isExpression = isExpression;
548 result.isIdentity = isIdentity;
549 result.isColumnSet = isColumnSet;
551 result.operand = operand;
556 sealed internal class _SqlMetaDataSet : ICloneable {
557 internal ushort id; // for altrow-columns only
558 internal int[] indexMap;
559 internal int visibleColumns;
560 internal DataTable schemaTable;
561 internal readonly SqlTceCipherInfoTable? cekTable; // table of "column encryption keys" used for this metadataset
562 internal readonly _SqlMetaData[] metaDataArray;
564 internal _SqlMetaDataSet(int count, SqlTceCipherInfoTable? cipherTable) {
565 cekTable = cipherTable;
566 metaDataArray = new _SqlMetaData[count];
567 for(int i = 0; i < metaDataArray.Length; ++i) {
568 metaDataArray[i] = new _SqlMetaData(i);
572 private _SqlMetaDataSet(_SqlMetaDataSet original) {
573 this.id = original.id;
574 // although indexMap is not immutable, in practice it is initialized once and then passed around
575 this.indexMap = original.indexMap;
576 this.visibleColumns = original.visibleColumns;
577 this.schemaTable = original.schemaTable;
578 if (original.metaDataArray == null) {
579 metaDataArray = null;
582 metaDataArray = new _SqlMetaData[original.metaDataArray.Length];
583 for (int idx=0; idx<metaDataArray.Length; idx++) {
584 metaDataArray[idx] = (_SqlMetaData)original.metaDataArray[idx].Clone();
589 internal int Length {
591 return metaDataArray.Length;
595 internal _SqlMetaData this [int index] {
597 return metaDataArray[index];
600 Debug.Assert(null == value, "used only by SqlBulkCopy");
601 metaDataArray[index] = value;
605 public object Clone() {
606 return new _SqlMetaDataSet(this);
610 sealed internal class _SqlMetaDataSetCollection : ICloneable {
611 private readonly List<_SqlMetaDataSet> altMetaDataSetArray;
612 internal _SqlMetaDataSet metaDataSet;
614 internal _SqlMetaDataSetCollection () {
615 altMetaDataSetArray = new List<_SqlMetaDataSet>();
618 internal void SetAltMetaData(_SqlMetaDataSet altMetaDataSet) {
619 // VSTFDEVDIV 479675: if altmetadata with same id is found, override it rather than adding a new one
620 int newId = altMetaDataSet.id;
621 for (int i = 0; i < altMetaDataSetArray.Count; i++) {
622 if (altMetaDataSetArray[i].id == newId) {
623 // override the existing metadata with the same id
624 altMetaDataSetArray[i] = altMetaDataSet;
629 // if we did not find metadata to override, add as new
630 altMetaDataSetArray.Add(altMetaDataSet);
633 internal _SqlMetaDataSet GetAltMetaData(int id) {
634 foreach (_SqlMetaDataSet altMetaDataSet in altMetaDataSetArray) {
635 if (altMetaDataSet.id == id) {
636 return altMetaDataSet;
639 Debug.Assert (false, "Can't match up altMetaDataSet with given id");
643 public object Clone()
645 _SqlMetaDataSetCollection result = new _SqlMetaDataSetCollection();
646 result.metaDataSet = metaDataSet == null ? null : (_SqlMetaDataSet)metaDataSet.Clone();
647 foreach (_SqlMetaDataSet set in altMetaDataSetArray) {
648 result.altMetaDataSetArray.Add((_SqlMetaDataSet)set.Clone());
655 /// Represents Encryption related information of the cipher data.
657 internal class SqlCipherMetadata {
660 /// Cipher Info Entry.
662 private SqlTceCipherInfoEntry? _sqlTceCipherInfoEntry;
665 /// Encryption Algorithm Id.
667 private readonly byte _cipherAlgorithmId;
670 /// Encryption Algorithm Name.
672 private readonly string _cipherAlgorithmName;
677 private readonly byte _encryptionType;
680 /// Normalization Rule Version.
682 private readonly byte _normalizationRuleVersion;
685 /// Encryption Algorithm Handle.
687 private SqlClientEncryptionAlgorithm _sqlClientEncryptionAlgorithm;
690 /// Sql Encryption Key Info.
692 private SqlEncryptionKeyInfo? _sqlEncryptionKeyInfo;
695 /// Ordinal (into the Cek Table).
697 private readonly ushort _ordinal;
700 /// Return the Encryption Info Entry.
702 internal SqlTceCipherInfoEntry? EncryptionInfo {
704 return _sqlTceCipherInfoEntry;
707 Debug.Assert(!_sqlTceCipherInfoEntry.HasValue, "We can only set the EncryptionInfo once.");
708 _sqlTceCipherInfoEntry = value;
713 /// Return the cipher's encryption algorithm id.
715 internal byte CipherAlgorithmId {
717 return _cipherAlgorithmId;
722 /// Return the cipher's encryption algorithm name (could be null).
724 internal string CipherAlgorithmName {
726 return _cipherAlgorithmName;
731 /// Return EncryptionType (Deterministic, Randomized, etc.)
733 internal byte EncryptionType {
735 return _encryptionType;
740 /// Return normalization rule version.
742 internal byte NormalizationRuleVersion {
744 return _normalizationRuleVersion;
749 /// Return the cipher encyrption algorithm handle.
751 internal SqlClientEncryptionAlgorithm CipherAlgorithm {
753 return _sqlClientEncryptionAlgorithm;
756 Debug.Assert(_sqlClientEncryptionAlgorithm == null, "_sqlClientEncryptionAlgorithm should not be set more than once.");
757 _sqlClientEncryptionAlgorithm = value;
762 /// Return Encryption Key Info.
764 internal SqlEncryptionKeyInfo? EncryptionKeyInfo {
766 return _sqlEncryptionKeyInfo;
770 Debug.Assert(!_sqlEncryptionKeyInfo.HasValue, "_sqlEncryptionKeyInfo should not be set more than once.");
771 _sqlEncryptionKeyInfo = value;
776 /// Return Ordinal into Cek Table.
778 internal ushort CekTableOrdinal {
787 /// <param name="sqlTceCipherInfoEntry"></param>
788 /// <param name="sqlClientEncryptionAlgorithm"></param>
789 /// <param name="cipherAlgorithmId"></param>
790 /// <param name="encryptionType"></param>
791 /// <param name="normalizationRuleVersion"></param>
792 internal SqlCipherMetadata (SqlTceCipherInfoEntry? sqlTceCipherInfoEntry,
794 byte cipherAlgorithmId,
795 string cipherAlgorithmName,
797 byte normalizationRuleVersion) {
798 Debug.Assert(!sqlTceCipherInfoEntry.Equals(default(SqlTceCipherInfoEntry)), "sqlTceCipherInfoEntry should not be un-initialized.");
800 _sqlTceCipherInfoEntry = sqlTceCipherInfoEntry;
802 _cipherAlgorithmId = cipherAlgorithmId;
803 _cipherAlgorithmName = cipherAlgorithmName;
804 _encryptionType = encryptionType;
805 _normalizationRuleVersion = normalizationRuleVersion;
806 _sqlEncryptionKeyInfo = null;
810 /// Do we have an handle to the cipher encryption algorithm already ?
812 /// <returns></returns>
813 internal bool IsAlgorithmInitialized() {
814 return (null != _sqlClientEncryptionAlgorithm) ? true : false;
818 internal class SqlMetaDataPriv {
819 internal SqlDbType type; // SqlDbType enum value
820 internal byte tdsType; // underlying tds type
821 internal byte precision = TdsEnums.UNKNOWN_PRECISION_SCALE; // give default of unknown (-1)
822 internal byte scale = TdsEnums.UNKNOWN_PRECISION_SCALE; // give default of unknown (-1)
824 internal SqlCollation collation;
825 internal int codePage;
826 internal Encoding encoding;
827 internal bool isNullable;
828 internal bool isMultiValued = false;
830 // UDT specific metadata
831 // server metadata info
832 // additional temporary UDT meta data
833 internal string udtDatabaseName;
834 internal string udtSchemaName;
835 internal string udtTypeName;
836 internal string udtAssemblyQualifiedName;
838 internal Type udtType;
840 // Xml specific metadata
841 internal string xmlSchemaCollectionDatabase;
842 internal string xmlSchemaCollectionOwningSchema;
843 internal string xmlSchemaCollectionName;
844 internal MetaType metaType; // cached metaType
846 // Structured type-specific metadata
847 internal string structuredTypeDatabaseName;
848 internal string structuredTypeSchemaName;
849 internal string structuredTypeName;
850 internal IList<SmiMetaData> structuredFields;
852 internal bool isEncrypted; // TCE encrypted?
853 internal SqlMetaDataPriv baseTI; // for encrypted columns, represents the TYPE_INFO for plaintext value
854 internal SqlCipherMetadata cipherMD; // Cipher related metadata for encrypted columns.
855 internal SqlMetaDataPriv() {
858 internal virtual void CopyFrom(SqlMetaDataPriv original) {
859 this.type = original.type;
860 this.tdsType = original.tdsType;
861 this.precision = original.precision;
862 this.scale = original.scale;
863 this.length = original.length;
864 this.collation = original.collation;
865 this.codePage = original.codePage;
866 this.encoding = original.encoding;
867 this.isNullable = original.isNullable;
868 this.isMultiValued = original.isMultiValued;
869 this.udtDatabaseName = original.udtDatabaseName;
870 this.udtSchemaName = original.udtSchemaName;
871 this.udtTypeName = original.udtTypeName;
872 this.udtAssemblyQualifiedName = original.udtAssemblyQualifiedName;
873 this.udtType = original.udtType;
874 this.xmlSchemaCollectionDatabase = original.xmlSchemaCollectionDatabase;
875 this.xmlSchemaCollectionOwningSchema = original.xmlSchemaCollectionOwningSchema;
876 this.xmlSchemaCollectionName = original.xmlSchemaCollectionName;
877 this.metaType = original.metaType;
881 this.structuredTypeDatabaseName = original.structuredTypeDatabaseName;
882 this.structuredTypeSchemaName = original.structuredTypeSchemaName;
883 this.structuredTypeName = original.structuredTypeName;
884 this.structuredFields = original.structuredFields;
888 /// Is the algorithm handle for the cipher encryption initialized ?
890 /// <returns></returns>
891 internal bool IsAlgorithmInitialized() {
892 if (null != cipherMD) {
893 return cipherMD.IsAlgorithmInitialized();
900 /// Returns the normalization rule version byte.
902 /// <returns></returns>
903 internal byte NormalizationRuleVersion {
905 if (null != cipherMD){
906 return cipherMD.NormalizationRuleVersion;
915 /// Class encapsulating additional information when sending encrypted input parameters.
917 sealed internal class SqlColumnEncryptionInputParameterInfo
920 /// Metadata of the parameter to write the TYPE_INFO of the unencrypted column data type.
922 private readonly SmiParameterMetaData _smiParameterMetadata;
925 /// Column encryption related metadata.
927 private readonly SqlCipherMetadata _cipherMetadata;
930 /// Serialized format for a subset of members.
931 /// Does not include _smiParameterMetadata's serialization.
933 private readonly byte[] _serializedWireFormat;
936 /// Return the SMI Parameter Metadata.
938 internal SmiParameterMetaData ParameterMetadata {
940 return _smiParameterMetadata;
945 /// Return the serialized format for some members.
946 /// This is pre-calculated and cached since members are immutable.
947 /// Does not include _smiParameterMetadata's serialization.
949 internal byte[] SerializedWireFormat
952 return _serializedWireFormat;
959 /// <param name="smiParameterMetadata"></param>
960 /// <param name="cipherMetadata"></param>
961 internal SqlColumnEncryptionInputParameterInfo(SmiParameterMetaData smiParameterMetadata, SqlCipherMetadata cipherMetadata) {
962 Debug.Assert(smiParameterMetadata != null, "smiParameterMetadata should not be null.");
963 Debug.Assert(cipherMetadata != null, "cipherMetadata should not be null");
964 Debug.Assert(cipherMetadata.EncryptionKeyInfo.HasValue, "cipherMetadata.EncryptionKeyInfo.HasValue should be true.");
966 _smiParameterMetadata = smiParameterMetadata;
967 _cipherMetadata = cipherMetadata;
968 _serializedWireFormat = SerializeToWriteFormat();
972 /// Serializes some data members to wire format.
974 private byte[] SerializeToWriteFormat() {
977 // CipherAlgorithmId.
978 totalLength += sizeof(byte);
981 totalLength += sizeof(byte);
983 // Database id of the encryption key.
984 totalLength += sizeof(int);
986 // Id of the encryption key.
987 totalLength += sizeof(int);
989 // Version of the encryption key.
990 totalLength += sizeof(int);
992 // Metadata version of the encryption key.
993 totalLength += _cipherMetadata.EncryptionKeyInfo.Value.cekMdVersion.Length;
995 // Normalization Rule Version.
996 totalLength += sizeof(byte);
998 byte[] serializedWireFormat = new byte[totalLength];
1000 // No:of bytes consumed till now. Running variable.
1001 int consumedBytes = 0;
1003 // 1 - Write Cipher Algorithm Id.
1004 serializedWireFormat[consumedBytes++] = _cipherMetadata.CipherAlgorithmId;
1006 // 2 - Write Encryption Type.
1007 serializedWireFormat[consumedBytes++] = _cipherMetadata.EncryptionType;
1009 // 3 - Write the database id of the encryption key.
1010 SerializeIntIntoBuffer(_cipherMetadata.EncryptionKeyInfo.Value.databaseId, serializedWireFormat, ref consumedBytes);
1012 // 4 - Write the id of the encryption key.
1013 SerializeIntIntoBuffer(_cipherMetadata.EncryptionKeyInfo.Value.cekId, serializedWireFormat, ref consumedBytes);
1015 // 5 - Write the version of the encryption key.
1016 SerializeIntIntoBuffer(_cipherMetadata.EncryptionKeyInfo.Value.cekVersion, serializedWireFormat, ref consumedBytes);
1018 // 6 - Write the metadata version of the encryption key.
1019 Buffer.BlockCopy(_cipherMetadata.EncryptionKeyInfo.Value.cekMdVersion, 0, serializedWireFormat, consumedBytes, _cipherMetadata.EncryptionKeyInfo.Value.cekMdVersion.Length);
1020 consumedBytes += _cipherMetadata.EncryptionKeyInfo.Value.cekMdVersion.Length;
1022 // 7 - Write Normalization Rule Version.
1023 serializedWireFormat[consumedBytes++] = _cipherMetadata.NormalizationRuleVersion;
1025 return serializedWireFormat;
1029 /// Serializes an int into the provided buffer and offset.
1031 private void SerializeIntIntoBuffer(int value, byte[] buffer, ref int offset) {
1032 buffer[offset++] = (byte)(value & 0xff);
1033 buffer[offset++] = (byte)((value >> 8) & 0xff);
1034 buffer[offset++] = (byte)((value >> 16) & 0xff);
1035 buffer[offset++] = (byte)((value >> 24) & 0xff);
1039 sealed internal class _SqlRPC {
1040 internal string rpcName;
1041 internal string databaseName; // Used for UDTs
1042 internal ushort ProcID; // Used instead of name
1043 internal ushort options;
1044 internal SqlParameter[] parameters;
1045 internal byte[] paramoptions;
1047 internal int? recordsAffected;
1048 internal int cumulativeRecordsAffected;
1050 internal int errorsIndexStart;
1051 internal int errorsIndexEnd;
1052 internal SqlErrorCollection errors;
1054 internal int warningsIndexStart;
1055 internal int warningsIndexEnd;
1056 internal SqlErrorCollection warnings;
1057 internal bool needsFetchParameterEncryptionMetadata;
1058 internal string GetCommandTextOrRpcName() {
1059 if (TdsEnums.RPC_PROCID_EXECUTESQL == ProcID) {
1060 // Param 0 is the actual sql executing
1061 return (string)parameters[0].Value;
1069 sealed internal class SqlReturnValue : SqlMetaDataPriv {
1071 internal ushort parmIndex; //Yukon or later only
1072 internal string parameter;
1073 internal readonly SqlBuffer value;
1075 internal SqlReturnValue() : base() {
1076 value = new SqlBuffer();
1080 internal struct MultiPartTableName {
1081 private string _multipartName;
1082 private string _serverName;
1083 private string _catalogName;
1084 private string _schemaName;
1085 private string _tableName;
1087 internal MultiPartTableName(string[] parts) {
1088 _multipartName = null;
1089 _serverName = parts[0];
1090 _catalogName = parts[1];
1091 _schemaName = parts[2];
1092 _tableName = parts[3];
1095 internal MultiPartTableName(string multipartName) {
1096 _multipartName = multipartName;
1098 _catalogName = null;
1103 internal string ServerName {
1105 ParseMultipartName();
1108 set { _serverName = value; }
1110 internal string CatalogName {
1112 ParseMultipartName();
1113 return _catalogName;
1115 set { _catalogName = value; }
1117 internal string SchemaName {
1119 ParseMultipartName();
1122 set { _schemaName = value; }
1124 internal string TableName {
1126 ParseMultipartName();
1129 set { _tableName = value; }
1132 private void ParseMultipartName() {
1133 if (null != _multipartName) {
1134 string[] parts = MultipartIdentifier.ParseMultipartIdentifier(_multipartName, "[\"", "]\"", Res.SQL_TDSParserTableName, false);
1135 _serverName = parts[0];
1136 _catalogName = parts[1];
1137 _schemaName = parts[2];
1138 _tableName = parts[3];
1139 _multipartName = null;
1143 internal static readonly MultiPartTableName Null = new MultiPartTableName(new string[] {null, null, null, null});