From ba63be85d144505defe704cb223d62b89746081e Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Fri, 28 Aug 2026 18:56:59 -0500 Subject: [PATCH 01/17] SqlFedAuthInfo => FedAuthInfoToken +Comments --- .../Connection/SqlConnectionInternal.cs | 32 +++++------ .../Data/SqlClient/Parser/TdsParser.cs | 14 ++--- .../SqlClient/Parser/Tokens/SqlFedAuthInfo.cs | 25 --------- .../Parser/Tokens/TdsFedAuthInfoToken.cs | 53 +++++++++++++++++++ 4 files changed, 76 insertions(+), 48 deletions(-) delete mode 100644 src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/SqlFedAuthInfo.cs create mode 100644 src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsFedAuthInfoToken.cs diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Connection/SqlConnectionInternal.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Connection/SqlConnectionInternal.cs index c4dd11d07a..335a9701aa 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Connection/SqlConnectionInternal.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Connection/SqlConnectionInternal.cs @@ -1841,8 +1841,8 @@ internal void OnFeatureExtAck(int featureId, byte[] data) /// Generates (if appropriate) and sends a Federated Authentication Access token to the /// server, using the Federated Authentication Info. /// - /// Federated Authentication Info. - internal void OnFedAuthInfo(SqlFedAuthInfo fedAuthInfo) + /// Federated Authentication Info. + internal void OnFedAuthInfo(TdsFedAuthInfoToken fedAuthInfoToken) { // @TODO: Seriously, put this into a hash set or give it a helper or something! We're gonna forget one in *one* spot and cause a big ol bug someday. Debug.Assert((ConnectionOptions._hasUserIdKeyword && ConnectionOptions._hasPasswordKeyword) @@ -1856,7 +1856,7 @@ internal void OnFedAuthInfo(SqlFedAuthInfo fedAuthInfo) || ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryWorkloadIdentity || (ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryIntegrated && _fedAuthRequired), "Credentials aren't provided for calling MSAL"); - Debug.Assert(fedAuthInfo != null, "info should not be null."); + Debug.Assert(fedAuthInfoToken != null, "info should not be null."); Debug.Assert(_dbConnectionPoolAuthenticationContextKey == null, "_dbConnectionPoolAuthenticationContextKey should be null."); SqlClientEventSource.Log.TryTraceEvent(" {0}, Generating federated authentication token", ObjectID); @@ -1877,8 +1877,8 @@ internal void OnFedAuthInfo(SqlFedAuthInfo fedAuthInfo) // Construct the dbAuthenticationContextKey with information from FedAuthInfo and // store for later use, when inserting in to the token cache. _dbConnectionPoolAuthenticationContextKey = new DbConnectionPoolAuthenticationContextKey( - fedAuthInfo.StsUrl, - fedAuthInfo.Spn); + fedAuthInfoToken.StsUrl, + fedAuthInfoToken.Spn); // Try to retrieve the authentication context from the pool, if one does exist for // this key. @@ -1921,7 +1921,7 @@ internal void OnFedAuthInfo(SqlFedAuthInfo fedAuthInfo) else if (_forceExpiryLocked) { attemptRefreshTokenLocked = TryGetFedAuthTokenLocked( - fedAuthInfo, + fedAuthInfoToken, dbConnectionPoolAuthenticationContext, out _fedAuthToken); } @@ -1948,7 +1948,7 @@ internal void OnFedAuthInfo(SqlFedAuthInfo fedAuthInfo) // context before trying to update. If the lock could not be obtained, it // will return false, without attempting to fetch a new token. attemptRefreshTokenLocked = TryGetFedAuthTokenLocked( - fedAuthInfo, + fedAuthInfoToken, dbConnectionPoolAuthenticationContext, out _fedAuthToken); @@ -1986,7 +1986,7 @@ internal void OnFedAuthInfo(SqlFedAuthInfo fedAuthInfo) if (dbConnectionPoolAuthenticationContext == null || attemptRefreshTokenUnLocked) { // Get the Federated Authentication Token. - _fedAuthToken = GetFedAuthToken(fedAuthInfo); + _fedAuthToken = GetFedAuthToken(fedAuthInfoToken); Debug.Assert(_fedAuthToken != null, "_fedAuthToken should not be null."); if (_dbConnectionPool != null) @@ -3015,8 +3015,8 @@ private void FailoverPermissionDemand() => /// /// Get the Federated Authentication Token. /// - /// Information obtained from server as Federated Authentication Info. - private SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) + /// Information obtained from server as Federated Authentication Info. + private SqlFedAuthToken GetFedAuthToken(TdsFedAuthInfoToken fedAuthInfoToken) { // Number of milliseconds to sleep for the initial back off, if a // retry period is not specified by the provider. @@ -3042,8 +3042,8 @@ private SqlFedAuthToken GetFedAuthToken(SqlFedAuthInfo fedAuthInfo) { var authParamsBuilder = new SqlAuthenticationParametersBuilder( authenticationMethod: ConnectionOptions.Authentication, - resource: fedAuthInfo.Spn, - authority: fedAuthInfo.StsUrl, + resource: fedAuthInfoToken.Spn, + authority: fedAuthInfoToken.StsUrl, serverName: ConnectionOptions.DataSource, databaseName: ConnectionOptions.InitialCatalog) .WithConnectionId(_clientConnectionId) @@ -4469,7 +4469,7 @@ internal static bool ShouldDisableTnir( /// Tries to acquire a lock on the authentication context. If successful in acquiring the /// lock, gets a new token and assigns it in the out parameter. Else returns false. /// - /// Federated Authentication Info + /// Federated Authentication Info /// /// Authentication Context cached in the connection pool. /// @@ -4477,12 +4477,12 @@ internal static bool ShouldDisableTnir( /// Out parameter, carrying the token if we acquired a lock and got the token. /// private bool TryGetFedAuthTokenLocked( - SqlFedAuthInfo fedAuthInfo, + TdsFedAuthInfoToken fedAuthInfoToken, DbConnectionPoolAuthenticationContext dbConnectionPoolAuthenticationContext, out SqlFedAuthToken fedAuthToken) { - Debug.Assert(fedAuthInfo != null, "fedAuthInfo should not be null."); + Debug.Assert(fedAuthInfoToken != null, "fedAuthInfo should not be null."); Debug.Assert(dbConnectionPoolAuthenticationContext != null, "dbConnectionPoolAuthenticationContext should not be null."); @@ -4526,7 +4526,7 @@ private bool TryGetFedAuthTokenLocked( if (authenticationContextLocked) { // Get the Federated Authentication Token. - fedAuthToken = GetFedAuthToken(fedAuthInfo); + fedAuthToken = GetFedAuthToken(fedAuthInfoToken); Debug.Assert(fedAuthToken != null, "fedAuthToken should not be null."); } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs index 9c5febc828..e40d2affce 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs @@ -2777,14 +2777,14 @@ internal TdsOperationStatus TryRun(RunBehavior runBehavior, SqlCommand cmdHandle case TdsEnums.SQLFEDAUTHINFO: { _connHandler._federatedAuthenticationInfoReceived = true; - SqlFedAuthInfo info; + TdsFedAuthInfoToken infoToken; - result = TryProcessFedAuthInfo(stateObj, tokenLength, out info); + result = TryProcessFedAuthInfo(stateObj, tokenLength, out infoToken); if (result != TdsOperationStatus.Done) { return result; } - _connHandler.OnFedAuthInfo(info); + _connHandler.OnFedAuthInfo(infoToken); break; } case TdsEnums.SQLSESSIONSTATE: @@ -4390,9 +4390,9 @@ private TdsOperationStatus TryProcessLoginAck(TdsParserStateObject stateObj) return TdsOperationStatus.Done; } - private TdsOperationStatus TryProcessFedAuthInfo(TdsParserStateObject stateObj, int tokenLen, out SqlFedAuthInfo sqlFedAuthInfo) + private TdsOperationStatus TryProcessFedAuthInfo(TdsParserStateObject stateObj, int tokenLen, out TdsFedAuthInfoToken tdsFedAuthInfoToken) { - sqlFedAuthInfo = null; + tdsFedAuthInfoToken = null; string spn = null; string stsUrl = null; @@ -4521,8 +4521,8 @@ private TdsOperationStatus TryProcessFedAuthInfo(TdsParserStateObject stateObj, throw SQL.ParsingError(ParsingErrorState.FedAuthInfoDoesNotContainStsurlAndSpn); } - sqlFedAuthInfo = new(spn, stsUrl); - SqlClientEventSource.Log.TryTraceEvent(" Processed FEDAUTHINFO token stream: {0}", sqlFedAuthInfo); + tdsFedAuthInfoToken = new(spn, stsUrl); + SqlClientEventSource.Log.TryTraceEvent(" Processed FEDAUTHINFO token stream: {0}", tdsFedAuthInfoToken); return TdsOperationStatus.Done; } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/SqlFedAuthInfo.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/SqlFedAuthInfo.cs deleted file mode 100644 index 67e9c5529f..0000000000 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/SqlFedAuthInfo.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#nullable enable - -namespace Microsoft.Data.SqlClient.Parser.Tokens; - -internal sealed class SqlFedAuthInfo -{ - internal SqlFedAuthInfo(string spn, string stsurl) - { - Spn = spn; - StsUrl = stsurl; - } - - internal string Spn { get; } - - internal string StsUrl { get; } - - public override string ToString() - { - return $"SPN: {Spn}, STSURL: {StsUrl}"; - } -} diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsFedAuthInfoToken.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsFedAuthInfoToken.cs new file mode 100644 index 0000000000..c033e01eec --- /dev/null +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsFedAuthInfoToken.cs @@ -0,0 +1,53 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#nullable enable + +namespace Microsoft.Data.SqlClient.Parser.Tokens; + +/// +/// Represents a token used for Federated Authentication in TDS protocol communication. +/// This corresponds with the SQLFEDAUTHINFO token in the TDS spec. +/// +/// +/// The TdsFedAuthInfoToken encapsulates information required for Federated Authentication +/// when establishing a connection with a SQL Server. It contains the Service Principal Name (SPN) +/// and the Security Token Service (STS) URL required for obtaining authentication tokens. +/// +internal sealed class TdsFedAuthInfoToken +{ + /// + /// Represents a token used for Federated Authentication in the TDS (Tabular Data Stream) protocol. + /// + /// + /// The TdsFedAuthInfoToken is responsible for encapsulating the Service Principal Name (SPN) + /// and Security Token Service (STS) URL, both of which are essential for Federated Authentication. + /// This token is used when negotiating secure connections with SQL Server instances. + /// + internal TdsFedAuthInfoToken(string spn, string stsUrl) + { + Spn = spn; + StsUrl = stsUrl; + } + + /// + /// Gets the Service Principal Name (SPN) associated with the federated authentication token. + /// The SPN is used in authentication processes to uniquely identify a service instance + /// in the context of Kerberos-based security mechanisms. + /// + internal string Spn { get; } + + /// + /// Gets the Security Token Service (STS) URL associated with the federated authentication token. + /// The STS URL is used to identify the authentication service endpoint responsible + /// for issuing security tokens in the federated authentication process. + /// + internal string StsUrl { get; } + + /// + public override string ToString() + { + return $"SPN: {Spn}, STSURL: {StsUrl}"; + } +} From e026fd485a9e0dfa558d34b25dfc7c97a2698d3e Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Mon, 31 Aug 2026 16:14:34 -0500 Subject: [PATCH 02/17] SqlReturnValue => TdsReturnValueToken +Comments --- .../Data/SqlClient/Parser/TdsParser.cs | 19 +++++++++++-------- ...lReturnValue.cs => TdsReturnValueToken.cs} | 4 ++-- .../Microsoft/Data/SqlClient/SqlCommand.cs | 4 ++-- 3 files changed, 15 insertions(+), 12 deletions(-) rename src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/{SqlReturnValue.cs => TdsReturnValueToken.cs} (83%) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs index e40d2affce..26ba039fc9 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs @@ -2935,16 +2935,19 @@ internal TdsOperationStatus TryRun(RunBehavior runBehavior, SqlCommand cmdHandle break; case TdsEnums.SQLRETURNVALUE: { - SqlReturnValue returnValue; - result = TryProcessReturnValue(tokenLength, stateObj, out returnValue, - cmdHandler?.ColumnEncryptionSetting ?? SqlCommandColumnEncryptionSetting.UseConnectionSetting); + result = TryProcessReturnValue( + tokenLength, + stateObj, + out TdsReturnValueToken returnValueToken, + cmdHandler?.ColumnEncryptionSetting ?? SqlCommandColumnEncryptionSetting.UseConnectionSetting); + if (result != TdsOperationStatus.Done) { return result; } if (cmdHandler != null) { - cmdHandler.OnReturnValue(returnValue, stateObj); + cmdHandler.OnReturnValue(returnValueToken, stateObj); } break; } @@ -4621,11 +4624,11 @@ internal TdsOperationStatus TryProcessError(byte token, TdsParserStateObject sta internal TdsOperationStatus TryProcessReturnValue(int length, TdsParserStateObject stateObj, - out SqlReturnValue returnValue, + out TdsReturnValueToken returnValueToken, SqlCommandColumnEncryptionSetting columnEncryptionSetting) { - returnValue = null; - SqlReturnValue rec = new SqlReturnValue(); + returnValueToken = null; + TdsReturnValueToken rec = new TdsReturnValueToken(); rec.length = length; // In 2005 this length is -1 TdsOperationStatus result = stateObj.TryReadUInt16(out _); if (result != TdsOperationStatus.Done) @@ -4920,7 +4923,7 @@ internal TdsOperationStatus TryProcessReturnValue(int length, } } - returnValue = rec; + returnValueToken = rec; return TdsOperationStatus.Done; } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/SqlReturnValue.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsReturnValueToken.cs similarity index 83% rename from src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/SqlReturnValue.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsReturnValueToken.cs index dcac65ac5c..16dd3e0a7f 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/SqlReturnValue.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsReturnValueToken.cs @@ -4,7 +4,7 @@ namespace Microsoft.Data.SqlClient.Parser.Tokens; -internal sealed class SqlReturnValue : SqlMetaDataPriv +internal sealed class TdsReturnValueToken : SqlMetaDataPriv { // @TODO: Make auto properties (and rename to match) internal string parameter; @@ -14,7 +14,7 @@ internal sealed class SqlReturnValue : SqlMetaDataPriv internal ushort parmIndex; //2005 or later only #endif - internal SqlReturnValue() : base() + internal TdsReturnValueToken() : base() { value = new SqlBuffer(); } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.cs index 422f2bd778..ce7ed6d45a 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.cs @@ -1672,7 +1672,7 @@ internal void OnReturnStatus(int status) } } - internal void OnReturnValue(SqlReturnValue returnValue, TdsParserStateObject stateObj) + internal void OnReturnValue(TdsReturnValueToken returnValue, TdsParserStateObject stateObj) { // Move the return value to the corresponding output parameter. // Return parameters are sent in the order in which they were defined in the procedure. @@ -1817,7 +1817,7 @@ thisParam.CipherMetadata is not null && thisParam.SetSqlBuffer(returnValue.value); } - // @TODO: This seems fishy to me, it seems like it should be part of the SqlReturnValue class + // @TODO: This seems fishy to me, it seems like it should be part of the TdsReturnValueToken class MetaType mt = MetaType.GetMetaTypeFromSqlDbType(returnValue.type, isMultiValued: false); if (returnValue.type is SqlDbType.Decimal) From 1757fa0f2e95677304f92142834bdf80b7c5f019 Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Mon, 31 Aug 2026 16:28:01 -0500 Subject: [PATCH 03/17] Fields -> auto properties, add comments. --- .../Data/SqlClient/Parser/TdsParser.cs | 4 +-- .../Parser/Tokens/TdsReturnValueToken.cs | 30 ++++++++++++++----- .../Microsoft/Data/SqlClient/SqlCommand.cs | 14 ++++----- 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs index 26ba039fc9..6d98fe350a 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs @@ -4910,13 +4910,13 @@ internal TdsOperationStatus TryProcessReturnValue(int length, if (isNull) { - GetNullSqlValue(rec.value, rec, SqlCommandColumnEncryptionSetting.Disabled, _connHandler); + GetNullSqlValue(rec.Value, rec, SqlCommandColumnEncryptionSetting.Disabled, _connHandler); } else { // We should never do any decryption here, so pass disabled as the command encryption override. // We only read the binary value and decryption will be performed by OnReturnValue(). - result = TryReadSqlValue(rec.value, rec, intlen, stateObj, SqlCommandColumnEncryptionSetting.Disabled, columnName: null /*Not used*/); + result = TryReadSqlValue(rec.Value, rec, intlen, stateObj, SqlCommandColumnEncryptionSetting.Disabled, columnName: null /*Not used*/); if (result != TdsOperationStatus.Done) { return result; diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsReturnValueToken.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsReturnValueToken.cs index 16dd3e0a7f..994f3fc633 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsReturnValueToken.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsReturnValueToken.cs @@ -4,18 +4,34 @@ namespace Microsoft.Data.SqlClient.Parser.Tokens; +/// +/// Represents a token that encapsulates information about a return value from a SQL Server stored +/// procedure or function. This represents the SQLRETURNVALUE token. +/// +/// +/// This token is used internally by the TDS (Tabular Data Stream) parser to handle return values +/// associated with parameters in TDS responses. It contains metadata describing the parameter and +/// its value. +/// internal sealed class TdsReturnValueToken : SqlMetaDataPriv { - // @TODO: Make auto properties (and rename to match) + /// + /// Gets or sets the name of the parameter associated with a returned value. + /// + // @TODO: This cannot be an auto property yet because this value is set via an out parameter in TryReadString. internal string parameter; - internal readonly SqlBuffer value; - - #if NETFRAMEWORK - internal ushort parmIndex; //2005 or later only - #endif + /// + /// Constructs a new instance of TddsReturnValueToken with the value initialized to a new + /// instance. + /// internal TdsReturnValueToken() : base() { - value = new SqlBuffer(); + Value = new SqlBuffer(); } + + /// + /// Gets the value associated with the return parameter token. + /// + internal SqlBuffer Value { get; } } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.cs index ce7ed6d45a..fe32537d81 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.cs @@ -1684,9 +1684,9 @@ internal void OnReturnValue(TdsReturnValueToken returnValue, TdsParserStateObjec if (_inPrepare) { // Store the returned prepare handle if we are returning from sp_prepare - if (!returnValue.value.IsNull) + if (!returnValue.Value.IsNull) { - _prepareHandle = returnValue.value.Int32; + _prepareHandle = returnValue.Value.Int32; } _inPrepare = false; @@ -1728,7 +1728,7 @@ thisParam.CipherMetadata is not null && throw ADP.ClosedConnectionError(); } - if (!returnValue.value.IsNull) + if (!returnValue.Value.IsNull) { try { @@ -1737,7 +1737,7 @@ thisParam.CipherMetadata is not null && // Get the key information from the parameter and decrypt the value. returnValue.cipherMD.EncryptionInfo = thisParam.CipherMetadata.EncryptionInfo; byte[] unencryptedBytes = SqlSecurityUtility.DecryptWithKey( - returnValue.value.ByteArray, + returnValue.Value.ByteArray, returnValue.cipherMD, _activeConnection, this); @@ -1796,9 +1796,9 @@ thisParam.CipherMetadata is not null && _activeConnection.CheckGetExtendedUDTInfo(returnValue, fThrow: true); // Extract the byte array from the param value - object data = returnValue.value.IsNull + object data = returnValue.Value.IsNull ? DBNull.Value - : returnValue.value.ByteArray; + : returnValue.Value.ByteArray; // Call the connection to instantiate the UDT object thisParam.Value = _activeConnection.GetUdtValue(data, returnValue, returnDBNull: false); @@ -1814,7 +1814,7 @@ thisParam.CipherMetadata is not null && } else { - thisParam.SetSqlBuffer(returnValue.value); + thisParam.SetSqlBuffer(returnValue.Value); } // @TODO: This seems fishy to me, it seems like it should be part of the TdsReturnValueToken class From bc3da80d08ede5adbcb89bdee2fcd250dd67ae4e Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Mon, 31 Aug 2026 16:39:08 -0500 Subject: [PATCH 04/17] _SqlMetaDataSet => TdsColumnMetadataToken --- .../Data/SqlClient/Parser/TdsParser.cs | 32 +++++++-------- .../SqlClient/Parser/TdsParserStateObject.cs | 4 +- ...taDataSet.cs => TdsColumnMetadataToken.cs} | 12 +++--- .../Tokens/_SqlMetaDataSetCollection.cs | 14 +++---- .../Microsoft/Data/SqlClient/SqlBulkCopy.cs | 16 ++++---- .../Microsoft/Data/SqlClient/SqlCommand.cs | 4 +- .../Microsoft/Data/SqlClient/SqlDataReader.cs | 20 +++++----- .../Data/SqlClient/SqlMetaDataSetTest.cs | 40 +++++++++---------- 8 files changed, 71 insertions(+), 71 deletions(-) rename src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/{_SqlMetaDataSet.cs => TdsColumnMetadataToken.cs} (91%) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs index 6d98fe350a..ec9a84011e 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs @@ -2586,7 +2586,7 @@ internal TdsOperationStatus TryRun(RunBehavior runBehavior, SqlCommand cmdHandle { if (dataStream != null) { - _SqlMetaDataSet metaDataSet; + TdsColumnMetadataToken metaDataSet; result = TryProcessColInfo(dataStream.MetaData, dataStream, stateObj, out metaDataSet); if (result != TdsOperationStatus.Done) { @@ -2800,7 +2800,7 @@ internal TdsOperationStatus TryRun(RunBehavior runBehavior, SqlCommand cmdHandle { if (tokenLength != TdsEnums.VARNULL) { - _SqlMetaDataSet metadata; + TdsColumnMetadataToken metadata; result = TryProcessMetaData(tokenLength, stateObj, out metadata, cmdHandler?.ColumnEncryptionSetting ?? SqlCommandColumnEncryptionSetting.UseConnectionSetting); if (result != TdsOperationStatus.Done) @@ -3004,7 +3004,7 @@ internal TdsOperationStatus TryRun(RunBehavior runBehavior, SqlCommand cmdHandle stateObj._cleanupAltMetaDataSetArray = new _SqlMetaDataSetCollection(); } - _SqlMetaDataSet cleanupAltMetaDataSet; + TdsColumnMetadataToken cleanupAltMetaDataSet; result = TryProcessAltMetaData(tokenLength, stateObj, out cleanupAltMetaDataSet); if (result != TdsOperationStatus.Done) { @@ -5105,7 +5105,7 @@ internal void DrainData(TdsParserStateObject stateObj) SqlDataReader.SharedState sharedState = stateObj._readerState; if (sharedState != null && sharedState._dataReady) { - _SqlMetaDataSet metadata = stateObj._cleanupMetaData; + TdsColumnMetadataToken metadata = stateObj._cleanupMetaData; TdsOperationStatus result; if (stateObj._partialHeaderBytesRead > 0) { @@ -5185,13 +5185,13 @@ internal void ThrowUnsupportedCollationEncountered(TdsParserStateObject stateObj ThrowExceptionAndWarning(stateObj); } - internal TdsOperationStatus TryProcessAltMetaData(int cColumns, TdsParserStateObject stateObj, out _SqlMetaDataSet metaData) + internal TdsOperationStatus TryProcessAltMetaData(int cColumns, TdsParserStateObject stateObj, out TdsColumnMetadataToken metaData) { Debug.Assert(cColumns > 0, "should have at least 1 column in altMetaData!"); metaData = null; - _SqlMetaDataSet altMetaDataSet = new _SqlMetaDataSet(cColumns, null); + TdsColumnMetadataToken altMetaDataSet = new TdsColumnMetadataToken(cColumns, null); TdsOperationStatus result = stateObj.TryReadUInt16(out altMetaDataSet.id); if (result != TdsOperationStatus.Done) @@ -5444,7 +5444,7 @@ internal TdsOperationStatus TryProcessCipherInfoTable(TdsParserStateObject state return TdsOperationStatus.Done; } - internal TdsOperationStatus TryProcessMetaData(int cColumns, TdsParserStateObject stateObj, out _SqlMetaDataSet metaData, SqlCommandColumnEncryptionSetting columnEncryptionSetting) + internal TdsOperationStatus TryProcessMetaData(int cColumns, TdsParserStateObject stateObj, out TdsColumnMetadataToken metaData, SqlCommandColumnEncryptionSetting columnEncryptionSetting) { Debug.Assert(cColumns > 0, "should have at least 1 column in metadata!"); @@ -5462,7 +5462,7 @@ internal TdsOperationStatus TryProcessMetaData(int cColumns, TdsParserStateObjec } // Read the ColumnData fields - _SqlMetaDataSet newMetaData = new _SqlMetaDataSet(cColumns, cipherTable); + TdsColumnMetadataToken newMetaData = new TdsColumnMetadataToken(cColumns, cipherTable); for (int i = 0; i < cColumns; i++) { result = TryCommonProcessMetaData(stateObj, newMetaData[i], cipherTable, fColMD: true, columnEncryptionSetting: columnEncryptionSetting); @@ -6017,7 +6017,7 @@ private TdsOperationStatus TryProcessOneTable(TdsParserStateObject stateObj, ref } // augments current metadata with table and key information - private TdsOperationStatus TryProcessColInfo(_SqlMetaDataSet columns, SqlDataReader reader, TdsParserStateObject stateObj, out _SqlMetaDataSet metaData) + private TdsOperationStatus TryProcessColInfo(TdsColumnMetadataToken columns, SqlDataReader reader, TdsParserStateObject stateObj, out TdsColumnMetadataToken metaData) { Debug.Assert(columns != null && columns.Length > 0, "no metadata available!"); @@ -6211,7 +6211,7 @@ internal TdsOperationStatus TryGetAltRowId(TdsParserStateObject stateObj, out in } // Used internally by BulkCopy only - private TdsOperationStatus TryProcessRow(_SqlMetaDataSet columns, object[] buffer, int[] map, TdsParserStateObject stateObj) + private TdsOperationStatus TryProcessRow(TdsColumnMetadataToken columns, object[] buffer, int[] map, TdsParserStateObject stateObj) { SqlBuffer data = new SqlBuffer(); @@ -6411,12 +6411,12 @@ internal static object GetNullSqlValue(SqlBuffer nullVal, return nullVal; } - internal TdsOperationStatus TrySkipRow(_SqlMetaDataSet columns, TdsParserStateObject stateObj) + internal TdsOperationStatus TrySkipRow(TdsColumnMetadataToken columns, TdsParserStateObject stateObj) { return TrySkipRow(columns, 0, stateObj); } - internal TdsOperationStatus TrySkipRow(_SqlMetaDataSet columns, int startCol, TdsParserStateObject stateObj) + internal TdsOperationStatus TrySkipRow(TdsColumnMetadataToken columns, int startCol, TdsParserStateObject stateObj) { for (int i = startCol; i < columns.Length; i++) { @@ -9907,7 +9907,7 @@ internal SqlDataReader TdsExecuteTransactionManagerRequest( dtcReader.Bind(stateObj); // force consumption of metadata - _SqlMetaDataSet metaData = dtcReader.MetaData; + TdsColumnMetadataToken metaData = dtcReader.MetaData; } else { @@ -11435,7 +11435,7 @@ internal Task WriteBulkCopyDone(TdsParserStateObject stateObj) /// decrypt the CEK and keep it ready for encryption. /// /// - internal void LoadColumnEncryptionKeys(_SqlMetaDataSet metadataCollection, SqlConnection connection, SqlCommand command = null) + internal void LoadColumnEncryptionKeys(TdsColumnMetadataToken metadataCollection, SqlConnection connection, SqlCommand command = null) { if (IsColumnEncryptionSupported && ShouldEncryptValuesForBulkCopy()) { @@ -11482,7 +11482,7 @@ internal void WriteEncryptionEntries(ref SqlTceCipherInfoTable cekTable, TdsPars /// Writes a CEK Table (as part of COLMETADATA token) for bulk copy. /// /// - internal void WriteCekTable(_SqlMetaDataSet metadataCollection, TdsParserStateObject stateObj) + internal void WriteCekTable(TdsColumnMetadataToken metadataCollection, TdsParserStateObject stateObj) { if (!IsColumnEncryptionSupported) { @@ -11585,7 +11585,7 @@ internal void WriteCryptoMetadata(_SqlMetaData md, TdsParserStateObject stateObj stateObj.WriteByte(md.cipherMD.NormalizationRuleVersion); } - internal void WriteBulkCopyMetaData(_SqlMetaDataSet metadataCollection, int count, TdsParserStateObject stateObj) + internal void WriteBulkCopyMetaData(TdsColumnMetadataToken metadataCollection, int count, TdsParserStateObject stateObj) { if (!(State == TdsParserState.OpenNotLoggedIn || State == TdsParserState.OpenLoggedIn)) { diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParserStateObject.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParserStateObject.cs index d8aa9355d7..51379a3a74 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParserStateObject.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParserStateObject.cs @@ -226,7 +226,7 @@ private enum SnapshotStatus private readonly byte[] _partialHeaderBuffer = new byte[TdsEnums.HEADER_LEN]; // Scratch buffer for ProcessHeader internal int _partialHeaderBytesRead; - internal _SqlMetaDataSet _cleanupMetaData; + internal TdsColumnMetadataToken _cleanupMetaData; internal _SqlMetaDataSetCollection _cleanupAltMetaDataSetArray; private SniContext _sniContext = SniContext.Undefined; @@ -4932,7 +4932,7 @@ private sealed class StateObjectData private int _inBytesPacket; private byte _messageStatus; internal NullBitmap _nullBitmapInfo; - private _SqlMetaDataSet _cleanupMetaData; + private TdsColumnMetadataToken _cleanupMetaData; internal _SqlMetaDataSetCollection _cleanupAltMetaDataSetArray; private SnapshottedStateFlags _state; private ulong _longLen; diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/_SqlMetaDataSet.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadataToken.cs similarity index 91% rename from src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/_SqlMetaDataSet.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadataToken.cs index dbb420dc72..5b112d21b0 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/_SqlMetaDataSet.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadataToken.cs @@ -9,7 +9,7 @@ namespace Microsoft.Data.SqlClient.Parser.Tokens; -internal sealed class _SqlMetaDataSet +internal sealed class TdsColumnMetadataToken { internal readonly SqlTceCipherInfoTable cekTable; // table of "column encryption keys" used for this metadataset internal ushort id; // for altrow-columns only @@ -21,7 +21,7 @@ internal sealed class _SqlMetaDataSet private int _hiddenColumnCount; private int[] _visibleColumnMap; - internal _SqlMetaDataSet(int count) + internal TdsColumnMetadataToken(int count) { _hiddenColumnCount = -1; _metaDataArray = new _SqlMetaData[count]; @@ -31,13 +31,13 @@ internal _SqlMetaDataSet(int count) } } - internal _SqlMetaDataSet(int count, SqlTceCipherInfoTable cipherTable) + internal TdsColumnMetadataToken(int count, SqlTceCipherInfoTable cipherTable) : this(count) { cekTable = cipherTable; } - private _SqlMetaDataSet(_SqlMetaDataSet original) + private TdsColumnMetadataToken(TdsColumnMetadataToken original) { id = original.id; _hiddenColumnCount = original._hiddenColumnCount; @@ -109,9 +109,9 @@ public int GetVisibleColumnIndex(int index) } } - public _SqlMetaDataSet Clone() + public TdsColumnMetadataToken Clone() { - return new _SqlMetaDataSet(this); + return new TdsColumnMetadataToken(this); } private void SetupHiddenColumns() diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/_SqlMetaDataSetCollection.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/_SqlMetaDataSetCollection.cs index 0c19c8ce58..cc303c5f62 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/_SqlMetaDataSetCollection.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/_SqlMetaDataSetCollection.cs @@ -9,15 +9,15 @@ namespace Microsoft.Data.SqlClient.Parser.Tokens; internal sealed class _SqlMetaDataSetCollection { - private readonly List<_SqlMetaDataSet> _altMetaDataSetArray; - internal _SqlMetaDataSet metaDataSet; + private readonly List _altMetaDataSetArray; + internal TdsColumnMetadataToken metaDataSet; internal _SqlMetaDataSetCollection() { - _altMetaDataSetArray = new List<_SqlMetaDataSet>(); + _altMetaDataSetArray = new List(); } - internal void SetAltMetaData(_SqlMetaDataSet altMetaDataSet) + internal void SetAltMetaData(TdsColumnMetadataToken altMetaDataSet) { // If altmetadata with same id is found, override it rather than adding a new one int newId = altMetaDataSet.id; @@ -35,9 +35,9 @@ internal void SetAltMetaData(_SqlMetaDataSet altMetaDataSet) _altMetaDataSetArray.Add(altMetaDataSet); } - internal _SqlMetaDataSet GetAltMetaData(int id) + internal TdsColumnMetadataToken GetAltMetaData(int id) { - foreach (_SqlMetaDataSet altMetaDataSet in _altMetaDataSetArray) + foreach (TdsColumnMetadataToken altMetaDataSet in _altMetaDataSetArray) { if (altMetaDataSet.id == id) { @@ -52,7 +52,7 @@ public object Clone() { _SqlMetaDataSetCollection result = new _SqlMetaDataSetCollection() { metaDataSet = metaDataSet?.Clone() }; - foreach (_SqlMetaDataSet set in _altMetaDataSetArray) + foreach (TdsColumnMetadataToken set in _altMetaDataSetArray) { result._altMetaDataSetArray.Add(set.Clone()); } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs index 4dd0e5f3c4..8a75ce01f0 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs @@ -54,10 +54,10 @@ internal Row(int rowCount) // The controlling class for one result (metadata + rows) internal sealed class Result { - private readonly _SqlMetaDataSet _metadata; + private readonly TdsColumnMetadataToken _metadata; private readonly List _rowset; - internal Result(_SqlMetaDataSet metadata) + internal Result(TdsColumnMetadataToken metadata) { _metadata = metadata; _rowset = new List(); @@ -65,7 +65,7 @@ internal Result(_SqlMetaDataSet metadata) internal int Count => _rowset.Count; - internal _SqlMetaDataSet MetaData => _metadata; + internal TdsColumnMetadataToken MetaData => _metadata; internal Row this[int index] => _rowset[index]; @@ -90,7 +90,7 @@ internal BulkCopySimpleResultSet() // Callback function for the tdsparser // (note that setting the metadata adds a resultset) - internal void SetMetaData(_SqlMetaDataSet metadata) + internal void SetMetaData(TdsColumnMetadataToken metadata) { _resultSet = new Result(metadata); _results.Add(_resultSet); @@ -254,7 +254,7 @@ private int RowNumber // Per-operation clone of the destination table metadata, used when CacheMetadata is // enabled so that column-pruning in AnalyzeTargetAndCreateUpdateBulkCommand does not // mutate the cached BulkCopySimpleResultSet. - private _SqlMetaDataSet _operationMetaData; + private TdsColumnMetadataToken _operationMetaData; #if DEBUG internal static bool s_setAlwaysTaskOnWrite; //when set and in DEBUG mode, TdsParser::WriteBulkCopyValue will always return a task @@ -700,7 +700,7 @@ private void CacheMetadataIfEnabled(BulkCopySimpleResultSet result) // metaDataSet is passed in by the caller so that when CacheMetadata is enabled, the // caller can supply a clone, allowing this method to null-prune unmatched/rejected // columns freely without mutating the shared cache. - private string AnalyzeTargetAndCreateUpdateBulkCommand(BulkCopySimpleResultSet internalResults, _SqlMetaDataSet metaDataSet) + private string AnalyzeTargetAndCreateUpdateBulkCommand(BulkCopySimpleResultSet internalResults, TdsColumnMetadataToken metaDataSet) { Debug.Assert(internalResults != null, "Where are the results from the initial query?"); Debug.Assert(metaDataSet != null, "metaDataSet must not be null"); @@ -1090,7 +1090,7 @@ private void WriteMetaData(BulkCopySimpleResultSet internalResults) { _stateObj.SetTimeoutSeconds(BulkCopyTimeout); - _SqlMetaDataSet metadataCollection = _operationMetaData ?? internalResults[MetaDataResultId].MetaData; + TdsColumnMetadataToken metadataCollection = _operationMetaData ?? internalResults[MetaDataResultId].MetaData; _stateObj._outputMessageType = TdsEnums.MT_BULK; _parser.WriteBulkCopyMetaData(metadataCollection, _sortedColumnMappings.Count, _stateObj); } @@ -3101,7 +3101,7 @@ private void WriteToServerInternalRestContinuedAsync(BulkCopySimpleResultSet int // CreateAndExecuteInitialQueryAsync). Clone the metadata set so that // AnalyzeTargetAndCreateUpdateBulkCommand can null-prune unmatched/rejected // columns without mutating the cache across WriteToServer calls. - _SqlMetaDataSet metaDataSet = CachedMetadata != null + TdsColumnMetadataToken metaDataSet = CachedMetadata != null ? internalResults[MetaDataResultId].MetaData.Clone() : internalResults[MetaDataResultId].MetaData; updateBulkCommandText = AnalyzeTargetAndCreateUpdateBulkCommand(internalResults, metaDataSet); diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.cs index fe32537d81..dbed2ddd9b 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.cs @@ -176,7 +176,7 @@ public sealed partial class SqlCommand : DbCommand, ICloneable /// /// Cut down on object creation and cache all the cached metadata /// - private _SqlMetaDataSet _cachedMetaData; + private TdsColumnMetadataToken _cachedMetaData; /// /// Column Encryption Override. Defaults to SqlConnectionSetting, in which case it will be @@ -896,7 +896,7 @@ internal int InternalRecordsAffected // @TODO: Autoproperty // @TODO: MetaData or Metadata? - internal _SqlMetaDataSet MetaData => _cachedMetaData; + internal TdsColumnMetadataToken MetaData => _cachedMetaData; // @TODO: Rename to match conventions. internal int ObjectID { get; } = Interlocked.Increment(ref _objectTypeCount); diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs index 3ab2c28e46..00c92c0e7c 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs @@ -76,7 +76,7 @@ internal class SharedState private SqlStreamingXml _streamingXml; // Used by Getchars on an Xml column for sequential access // buffers and metadata - private _SqlMetaDataSet _metaData; // current metaData for the stream, it is lazily loaded + private TdsColumnMetadataToken _metaData; // current metaData for the stream, it is lazily loaded private _SqlMetaDataSetCollection _altMetaDataSetCollection; private FieldNameLookup _fieldNameLookup; private CommandBehavior _commandBehavior; @@ -250,7 +250,7 @@ internal long ColumnDataBytesRemaining() return _sharedState._columnDataBytesRemaining; } - internal _SqlMetaDataSet MetaData + internal TdsColumnMetadataToken MetaData { get { @@ -283,7 +283,7 @@ internal _SqlMetaDataSet MetaData internal virtual SmiExtendedMetaData[] GetInternalSmiMetaData() { SmiExtendedMetaData[] metaDataReturn = null; - _SqlMetaDataSet metaData = this.MetaData; + TdsColumnMetadataToken metaData = this.MetaData; if (metaData != null && 0 < metaData.Length) { @@ -416,7 +416,7 @@ public override int VisibleFieldCount { throw ADP.DataReaderClosed(); } - _SqlMetaDataSet md = this.MetaData; + TdsColumnMetadataToken md = this.MetaData; if (md == null) { return 0; @@ -462,7 +462,7 @@ internal void Bind(TdsParserStateObject stateObj) #endif internal DataTable BuildSchemaTable() { - _SqlMetaDataSet md = this.MetaData; + TdsColumnMetadataToken md = this.MetaData; Debug.Assert(md != null, "BuildSchemaTable - unexpected null metadata information"); DataTable schemaTable = new DataTable("SchemaTable"); @@ -3452,7 +3452,7 @@ private TdsOperationStatus TryNextResult(out bool more) more = false; return result; } - _SqlMetaDataSet altMetaDataSet = _altMetaDataSetCollection.GetAltMetaData(altRowId); + TdsColumnMetadataToken altMetaDataSet = _altMetaDataSetCollection.GetAltMetaData(altRowId); if (altMetaDataSet != null) { _metaData = altMetaDataSet; @@ -4182,7 +4182,7 @@ private void RestoreServerSettings(TdsParser parser, TdsParserStateObject stateO } } - internal TdsOperationStatus TrySetAltMetaDataSet(_SqlMetaDataSet metaDataSet, bool metaDataConsumed) + internal TdsOperationStatus TrySetAltMetaDataSet(TdsColumnMetadataToken metaDataSet, bool metaDataConsumed) { if (_altMetaDataSetCollection == null) { @@ -4264,7 +4264,7 @@ internal TdsOperationStatus TrySetSensitivityClassification(SensitivityClassific return TdsOperationStatus.Done; } - internal TdsOperationStatus TrySetMetaData(_SqlMetaDataSet metaData, bool moreInfo) + internal TdsOperationStatus TrySetMetaData(TdsColumnMetadataToken metaData, bool moreInfo) { _metaData = metaData; @@ -5648,7 +5648,7 @@ internal sealed class Snapshot public long _columnDataBytesRead; public long _columnDataBytesRemaining; - public _SqlMetaDataSet _metadata; + public TdsColumnMetadataToken _metadata; public _SqlMetaDataSetCollection _altMetaDataSetCollection; public MultiPartTableName[] _tableNames; @@ -5831,7 +5831,7 @@ public ReadOnlyCollection GetColumnSchema() private ReadOnlyCollection BuildColumnSchema() { - _SqlMetaDataSet md = MetaData; + TdsColumnMetadataToken md = MetaData; DbColumn[] columnSchema = new DbColumn[md.Length]; for (int i = 0; i < md.Length; i++) { diff --git a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlMetaDataSetTest.cs b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlMetaDataSetTest.cs index e9ee4694bd..bdcc22f39f 100644 --- a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlMetaDataSetTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlMetaDataSetTest.cs @@ -18,13 +18,13 @@ public class SqlMetaDataSetTest public void SqlMetaDataSet_Clone_ProducesIndependentCopy() { // Arrange: create a metadata set with 3 columns simulating a destination table - _SqlMetaDataSet original = new _SqlMetaDataSet(3); + TdsColumnMetadataToken original = new TdsColumnMetadataToken(3); original[0].column = "col1"; original[1].column = "col2"; original[2].column = "col3"; // Act: clone and then null out an entry in the clone (simulating column pruning) - _SqlMetaDataSet clone = original.Clone(); + TdsColumnMetadataToken clone = original.Clone(); clone[2] = null; // Assert: the original is not affected by the mutation of the clone @@ -40,14 +40,14 @@ public void SqlMetaDataSet_Clone_ProducesIndependentCopy() public void SqlMetaDataSet_Clone_NullingMultipleEntries_OriginalRetainsAll() { // Arrange: simulate a table with 4 columns - _SqlMetaDataSet original = new _SqlMetaDataSet(4); + TdsColumnMetadataToken original = new TdsColumnMetadataToken(4); original[0].column = "id"; original[1].column = "name"; original[2].column = "email"; original[3].column = "phone"; // Act: clone and null out entries 1 and 3 (simulating mapping only id and email) - _SqlMetaDataSet clone = original.Clone(); + TdsColumnMetadataToken clone = original.Clone(); clone[1] = null; clone[3] = null; @@ -71,21 +71,21 @@ public void SqlMetaDataSet_Clone_RepeatedCloneAndPrune_OriginalSurvives() { // Arrange: simulate the scenario where multiple WriteToServer calls each // clone and prune different subsets of columns - _SqlMetaDataSet original = new _SqlMetaDataSet(3); + TdsColumnMetadataToken original = new TdsColumnMetadataToken(3); original[0].column = "col1"; original[1].column = "col2"; original[2].column = "col3"; // First operation: map only col1 and col2 (prune col3) - _SqlMetaDataSet clone1 = original.Clone(); + TdsColumnMetadataToken clone1 = original.Clone(); clone1[2] = null; // Second operation: map only col1 and col3 (prune col2) - _SqlMetaDataSet clone2 = original.Clone(); + TdsColumnMetadataToken clone2 = original.Clone(); clone2[1] = null; // Third operation: map all columns (no pruning needed) - _SqlMetaDataSet clone3 = original.Clone(); + TdsColumnMetadataToken clone3 = original.Clone(); // Assert: original is fully intact after all operations Assert.NotNull(original[0]); @@ -112,12 +112,12 @@ public void SqlMetaDataSet_Clone_PreservesOrdinals() { // Verify that cloned entries maintain correct ordinal values, // which are used for column matching in AnalyzeTargetAndCreateUpdateBulkCommand - _SqlMetaDataSet original = new _SqlMetaDataSet(3); + TdsColumnMetadataToken original = new TdsColumnMetadataToken(3); original[0].column = "col1"; original[1].column = "col2"; original[2].column = "col3"; - _SqlMetaDataSet clone = original.Clone(); + TdsColumnMetadataToken clone = original.Clone(); Assert.Equal(original[0].ordinal, clone[0].ordinal); Assert.Equal(original[1].ordinal, clone[1].ordinal); @@ -134,11 +134,11 @@ public void SqlMetaDataSet_Clone_PreservesCekTable() cekTable[0] = new SqlTceCipherInfoEntry(ordinal: 0); cekTable[1] = new SqlTceCipherInfoEntry(ordinal: 1); - _SqlMetaDataSet original = new _SqlMetaDataSet(2, cekTable); + TdsColumnMetadataToken original = new TdsColumnMetadataToken(2, cekTable); original[0].column = "col1"; original[1].column = "col2"; - _SqlMetaDataSet clone = original.Clone(); + TdsColumnMetadataToken clone = original.Clone(); Assert.NotNull(clone.cekTable); Assert.Same(original.cekTable, clone.cekTable); @@ -152,11 +152,11 @@ public void SqlMetaData_Clone_PreservesIsEncrypted() // WriteBulkCopyMetaData checks md.isEncrypted to set the TDS IsEncrypted flag // and WriteCryptoMetadata checks it to decide whether to write cipher metadata. // If lost, encrypted columns are sent as plaintext. - _SqlMetaDataSet original = new _SqlMetaDataSet(1); + TdsColumnMetadataToken original = new TdsColumnMetadataToken(1); original[0].column = "encrypted_col"; original[0].isEncrypted = true; - _SqlMetaDataSet clone = original.Clone(); + TdsColumnMetadataToken clone = original.Clone(); Assert.True(clone[0].isEncrypted); } @@ -177,12 +177,12 @@ public void SqlMetaData_Clone_PreservesCipherMetadata() normalizationRuleVersion: 1 ); - _SqlMetaDataSet original = new _SqlMetaDataSet(1); + TdsColumnMetadataToken original = new TdsColumnMetadataToken(1); original[0].column = "encrypted_col"; original[0].isEncrypted = true; original[0].cipherMD = cipherMD; - _SqlMetaDataSet clone = original.Clone(); + TdsColumnMetadataToken clone = original.Clone(); Assert.NotNull(clone[0].cipherMD); Assert.Equal(2, clone[0].cipherMD.CipherAlgorithmId); @@ -203,12 +203,12 @@ public void SqlMetaData_Clone_PreservesBaseTI() baseTI.precision = 0; baseTI.scale = 0; - _SqlMetaDataSet original = new _SqlMetaDataSet(1); + TdsColumnMetadataToken original = new TdsColumnMetadataToken(1); original[0].column = "encrypted_col"; original[0].isEncrypted = true; original[0].baseTI = baseTI; - _SqlMetaDataSet clone = original.Clone(); + TdsColumnMetadataToken clone = original.Clone(); Assert.NotNull(clone[0].baseTI); Assert.Equal(System.Data.SqlDbType.NVarChar, clone[0].baseTI.type); @@ -238,7 +238,7 @@ public void SqlMetaDataSet_Clone_PreservesFullAlwaysEncryptedMetadata() SqlMetaDataPriv baseTI = new SqlMetaDataPriv(); baseTI.type = System.Data.SqlDbType.Int; - _SqlMetaDataSet original = new _SqlMetaDataSet(2, cekTable); + TdsColumnMetadataToken original = new TdsColumnMetadataToken(2, cekTable); original[0].column = "id"; original[1].column = "secret"; original[1].isEncrypted = true; @@ -246,7 +246,7 @@ public void SqlMetaDataSet_Clone_PreservesFullAlwaysEncryptedMetadata() original[1].baseTI = baseTI; // Clone and prune column 0 (simulating mapping only the encrypted column) - _SqlMetaDataSet clone = original.Clone(); + TdsColumnMetadataToken clone = original.Clone(); clone[0] = null; // The pruning must not affect the encrypted column's metadata From 664743880f1c230f9bf45e9d785fa1bbbfa88fbf Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Mon, 31 Aug 2026 16:59:22 -0500 Subject: [PATCH 05/17] Fields => properties where possible, add comments --- .../Data/SqlClient/Parser/TdsParser.cs | 4 +- .../Parser/Tokens/TdsColumnMetadataToken.cs | 119 +++++++++++------- .../Microsoft/Data/SqlClient/SqlDataReader.cs | 18 +-- .../Data/SqlClient/SqlMetaDataSetTest.cs | 12 +- 4 files changed, 89 insertions(+), 64 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs index ec9a84011e..5febaf6ea6 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs @@ -11493,14 +11493,14 @@ internal void WriteCekTable(TdsColumnMetadataToken metadataCollection, TdsParser // Note- Cek table (with 0 entries) will be present if TCE // was enabled and server supports it! // OR if encryption was disabled in connection options - if (metadataCollection.cekTable == null || + if (metadataCollection.CekTable == null || !ShouldEncryptValuesForBulkCopy()) { WriteShort(0x00, stateObj); return; } - SqlTceCipherInfoTable cekTable = metadataCollection.cekTable; + SqlTceCipherInfoTable cekTable = metadataCollection.CekTable; ushort count = (ushort)cekTable.Size; WriteShort(count, stateObj); diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadataToken.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadataToken.cs index 5b112d21b0..a25a770b95 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadataToken.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadataToken.cs @@ -11,30 +11,30 @@ namespace Microsoft.Data.SqlClient.Parser.Tokens; internal sealed class TdsColumnMetadataToken { - internal readonly SqlTceCipherInfoTable cekTable; // table of "column encryption keys" used for this metadataset - internal ushort id; // for altrow-columns only - - internal DataTable schemaTable; - private readonly _SqlMetaData[] _metaDataArray; - internal ReadOnlyCollection dbColumnSchema; - - private int _hiddenColumnCount; + /// + /// Represents the identifier associated with an alternative metadata set in the TDS stream. + /// + // @TODO: This cannot be converted to an auto-property yet because it is accessed as an out parameter from TryReadUint16 + internal ushort id; + + private readonly _SqlMetaData[] _metadataArray; + private int _hiddenColumnCount; // @TODO: -1 is used as magic value for "unread hidden columns". We should derive all this from _visibleColumnMap? private int[] _visibleColumnMap; internal TdsColumnMetadataToken(int count) { _hiddenColumnCount = -1; - _metaDataArray = new _SqlMetaData[count]; - for (int i = 0; i < _metaDataArray.Length; ++i) + _metadataArray = new _SqlMetaData[count]; + for (int i = 0; i < _metadataArray.Length; ++i) { - _metaDataArray[i] = new _SqlMetaData(i); + _metadataArray[i] = new _SqlMetaData(i); } } internal TdsColumnMetadataToken(int count, SqlTceCipherInfoTable cipherTable) : this(count) { - cekTable = cipherTable; + CekTable = cipherTable; } private TdsColumnMetadataToken(TdsColumnMetadataToken original) @@ -42,32 +42,64 @@ private TdsColumnMetadataToken(TdsColumnMetadataToken original) id = original.id; _hiddenColumnCount = original._hiddenColumnCount; _visibleColumnMap = original._visibleColumnMap; - dbColumnSchema = original.dbColumnSchema; - schemaTable = original.schemaTable; - cekTable = original.cekTable; + DbColumnSchema = original.DbColumnSchema; + SchemaTable = original.SchemaTable; + CekTable = original.CekTable; - if (original._metaDataArray == null) + if (original._metadataArray == null) { - _metaDataArray = null; + _metadataArray = null; } else { - _metaDataArray = new _SqlMetaData[original._metaDataArray.Length]; - for (int idx = 0; idx < _metaDataArray.Length; idx++) + _metadataArray = new _SqlMetaData[original._metadataArray.Length]; + for (int idx = 0; idx < _metadataArray.Length; idx++) { - _metaDataArray[idx] = (_SqlMetaData)original._metaDataArray[idx].Clone(); + _metadataArray[idx] = (_SqlMetaData)original._metadataArray[idx].Clone(); } } } - internal int Length + /// + /// Provides access to the metadata array associated with this token. Allows retrieval and + /// updating of metadata at a specific index. + /// + internal _SqlMetaData this[int index] { - get + get => _metadataArray[index]; + + set { - return _metaDataArray.Length; + // @TODO: If we're only allowed to set this to null, it feels like we're doing something wrong. + Debug.Assert(value == null, "used only by SqlBulkCopy"); + _metadataArray[index] = value; } } + /// + /// Represents the table of column encryption keys (CEKs) associated with this metadata token. + /// + internal SqlTceCipherInfoTable CekTable { get; } + + /// + /// Represents the schema of database columns associated with this metadata token. + /// + internal ReadOnlyCollection DbColumnSchema { get; set; } + + /// + /// Gets the number of metadata entries in the associated metadata array. This includes both + /// visible and hidden columns. + /// + internal int Length => _metadataArray.Length; + + /// + /// Represents the schema metadata for the columns associated with this token. + /// + internal DataTable SchemaTable { get; set; } + + /// + /// Gets the number of visible columns excluding hidden columns. + /// internal int VisibleColumnCount { get @@ -76,42 +108,34 @@ internal int VisibleColumnCount { SetupHiddenColumns(); } + return Length - _hiddenColumnCount; } } - internal _SqlMetaData this[int index] + /// + /// Creates a new instance of TdsColumnMetadataToken that is a deep copy of the current + /// instance. + /// + public TdsColumnMetadataToken Clone() { - get - { - return _metaDataArray[index]; - } - set - { - Debug.Assert(value == null, "used only by SqlBulkCopy"); - _metaDataArray[index] = value; - } + return new TdsColumnMetadataToken(this); } + /// + /// Retrieves the index of a visible column based on the provided index, + /// considering hidden columns. + /// + /// The input index of the column to retrieve. + /// The index of the visible column corresponding to the given input index. public int GetVisibleColumnIndex(int index) { if (_hiddenColumnCount == -1) { SetupHiddenColumns(); } - if (_visibleColumnMap is null) - { - return index; - } - else - { - return _visibleColumnMap[index]; - } - } - public TdsColumnMetadataToken Clone() - { - return new TdsColumnMetadataToken(this); + return _visibleColumnMap?[index] ?? index; } private void SetupHiddenColumns() @@ -119,7 +143,7 @@ private void SetupHiddenColumns() int hiddenColumnCount = 0; for (int index = 0; index < Length; index++) { - if (_metaDataArray[index].IsHidden) + if (_metadataArray[index].IsHidden) { hiddenColumnCount += 1; } @@ -131,7 +155,7 @@ private void SetupHiddenColumns() int mapIndex = 0; for (int metaDataIndex = 0; metaDataIndex < Length; metaDataIndex++) { - if (!_metaDataArray[metaDataIndex].IsHidden) + if (!_metadataArray[metaDataIndex].IsHidden) { visibleColumnMap[mapIndex] = metaDataIndex; mapIndex += 1; @@ -139,6 +163,7 @@ private void SetupHiddenColumns() } _visibleColumnMap = visibleColumnMap; } + _hiddenColumnCount = hiddenColumnCount; } } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs index 00c92c0e7c..ec546bf7ad 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs @@ -1468,15 +1468,15 @@ public override DataTable GetSchemaTable() try { statistics = SqlStatistics.StartTimer(Statistics); - if (_metaData == null || _metaData.schemaTable == null) + if (_metaData == null || _metaData.SchemaTable == null) { if (this.MetaData != null) { - _metaData.schemaTable = BuildSchemaTable(); - Debug.Assert(_metaData.schemaTable != null, "No schema information yet!"); + _metaData.SchemaTable = BuildSchemaTable(); + Debug.Assert(_metaData.SchemaTable != null, "No schema information yet!"); } } - return _metaData?.schemaTable; + return _metaData?.SchemaTable; } finally { @@ -4273,7 +4273,7 @@ internal TdsOperationStatus TrySetMetaData(TdsColumnMetadataToken metaData, bool if (_metaData != null) { #if NETFRAMEWORK - _metaData.schemaTable = null; + _metaData.SchemaTable = null; #endif _data = SqlBuffer.CreateBufferArray(metaData.Length); } @@ -5807,19 +5807,19 @@ public ReadOnlyCollection GetColumnSchema() try { statistics = SqlStatistics.StartTimer(Statistics); - if (_metaData == null || _metaData.dbColumnSchema == null) + if (_metaData == null || _metaData.DbColumnSchema == null) { if (this.MetaData != null) { - _metaData.dbColumnSchema = BuildColumnSchema(); - Debug.Assert(_metaData.dbColumnSchema != null, "No schema information yet!"); + _metaData.DbColumnSchema = BuildColumnSchema(); + Debug.Assert(_metaData.DbColumnSchema != null, "No schema information yet!"); // filter table? } } if (_metaData != null) { - return _metaData.dbColumnSchema; + return _metaData.DbColumnSchema; } return s_emptySchema; } diff --git a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlMetaDataSetTest.cs b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlMetaDataSetTest.cs index bdcc22f39f..c74c7c21e5 100644 --- a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlMetaDataSetTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlMetaDataSetTest.cs @@ -140,9 +140,9 @@ public void SqlMetaDataSet_Clone_PreservesCekTable() TdsColumnMetadataToken clone = original.Clone(); - Assert.NotNull(clone.cekTable); - Assert.Same(original.cekTable, clone.cekTable); - Assert.Equal(2, clone.cekTable.Size); + Assert.NotNull(clone.CekTable); + Assert.Same(original.CekTable, clone.CekTable); + Assert.Equal(2, clone.CekTable.Size); } [Fact] @@ -257,13 +257,13 @@ public void SqlMetaDataSet_Clone_PreservesFullAlwaysEncryptedMetadata() Assert.Equal(System.Data.SqlDbType.Int, clone[1].baseTI.type); // The cekTable must be preserved on the clone - Assert.NotNull(clone.cekTable); - Assert.Equal(1, clone.cekTable.Size); + Assert.NotNull(clone.CekTable); + Assert.Equal(1, clone.CekTable.Size); // The original must remain completely intact Assert.NotNull(original[0]); Assert.NotNull(original[1]); - Assert.NotNull(original.cekTable); + Assert.NotNull(original.CekTable); Assert.True(original[1].isEncrypted); } } From d79ea46823460878eb5594f763f9f9fc73a66daf Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Mon, 31 Aug 2026 16:59:22 -0500 Subject: [PATCH 06/17] Fields => properties where possible, add comments --- .../Data/SqlClient/Parser/Tokens/TdsColumnMetadataToken.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadataToken.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadataToken.cs index a25a770b95..3f060738ef 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadataToken.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadataToken.cs @@ -9,6 +9,10 @@ namespace Microsoft.Data.SqlClient.Parser.Tokens; +/// +/// Represents a metadata token that contains column information from the TDS stream. This +/// corresponds to a SQLCOLMETADATA or SQLALTMETADATA token. +/// internal sealed class TdsColumnMetadataToken { /// From 509f9fc7cc9c86276544cabd9c48fdf97e40ff59 Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Tue, 1 Sep 2026 12:40:41 -0500 Subject: [PATCH 07/17] _SqlMetaData => TdsColumnMetadata --- .../Data/SqlClient/Parser/TdsParser.cs | 16 +++---- .../{_SqlMetaData.cs => TdsColumnMetadata.cs} | 44 +++++++++---------- .../Parser/Tokens/TdsColumnMetadataToken.cs | 12 ++--- .../Microsoft/Data/SqlClient/SqlBulkCopy.cs | 14 +++--- .../Microsoft/Data/SqlClient/SqlDataReader.cs | 34 +++++++------- .../Microsoft/Data/SqlClient/SqlDbColumn.cs | 4 +- 6 files changed, 62 insertions(+), 62 deletions(-) rename src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/{_SqlMetaData.cs => TdsColumnMetadata.cs} (67%) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs index 5febaf6ea6..46646fa6c5 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs @@ -5220,7 +5220,7 @@ internal TdsOperationStatus TryProcessAltMetaData(int cColumns, TdsParserStateOb for (int i = 0; i < cColumns; i++) { // internal meta data class - _SqlMetaData col = altMetaDataSet[i]; + TdsColumnMetadata col = altMetaDataSet[i]; result = stateObj.TryReadByte(out col.op); if (result != TdsOperationStatus.Done) @@ -5684,7 +5684,7 @@ private TdsOperationStatus TryProcessTypeInfo(TdsParserStateObject stateObj, Sql return TdsOperationStatus.Done; } - private TdsOperationStatus TryCommonProcessMetaData(TdsParserStateObject stateObj, _SqlMetaData col, SqlTceCipherInfoTable cipherTable, bool fColMD, SqlCommandColumnEncryptionSetting columnEncryptionSetting) + private TdsOperationStatus TryCommonProcessMetaData(TdsParserStateObject stateObj, TdsColumnMetadata col, SqlTceCipherInfoTable cipherTable, bool fColMD, SqlCommandColumnEncryptionSetting columnEncryptionSetting) { byte byteLen; uint userType; @@ -6025,7 +6025,7 @@ private TdsOperationStatus TryProcessColInfo(TdsColumnMetadataToken columns, Sql for (int i = 0; i < columns.Length; i++) { - _SqlMetaData col = columns[i]; + TdsColumnMetadata col = columns[i]; TdsOperationStatus result = stateObj.TryReadByte(out _); if (result != TdsOperationStatus.Done) @@ -6217,7 +6217,7 @@ private TdsOperationStatus TryProcessRow(TdsColumnMetadataToken columns, object[ for (int i = 0; i < columns.Length; i++) { - _SqlMetaData md = columns[i]; + TdsColumnMetadata md = columns[i]; Debug.Assert(md != null, "_SqlMetaData should not be null for column " + i.ToString(CultureInfo.InvariantCulture)); bool isNull; @@ -6420,7 +6420,7 @@ internal TdsOperationStatus TrySkipRow(TdsColumnMetadataToken columns, int start { for (int i = startCol; i < columns.Length; i++) { - _SqlMetaData md = columns[i]; + TdsColumnMetadata md = columns[i]; TdsOperationStatus result = TrySkipValue(md, i, stateObj); if (result != TdsOperationStatus.Done) @@ -11443,7 +11443,7 @@ internal void LoadColumnEncryptionKeys(TdsColumnMetadataToken metadataCollection { if (metadataCollection[col] != null) { - _SqlMetaData md = metadataCollection[col]; + TdsColumnMetadata md = metadataCollection[col]; if (md.isEncrypted) { SqlSecurityUtility.DecryptSymmetricKey(md.cipherMD, connection, command); @@ -11552,7 +11552,7 @@ internal void WriteTceUserTypeAndTypeInfo(SqlMetaDataPriv mdPriv, TdsParserState /// Writes the crypto metadata (as part of COLMETADATA token) for encrypted columns. /// /// - internal void WriteCryptoMetadata(_SqlMetaData md, TdsParserStateObject stateObj) + internal void WriteCryptoMetadata(TdsColumnMetadata md, TdsParserStateObject stateObj) { if (!IsColumnEncryptionSupported || // TCE Feature supported !md.isEncrypted || // Column is not encrypted @@ -11602,7 +11602,7 @@ internal void WriteBulkCopyMetaData(TdsColumnMetadataToken metadataCollection, i { if (metadataCollection[i] != null) { - _SqlMetaData md = metadataCollection[i]; + TdsColumnMetadata md = metadataCollection[i]; // read user type - 4 bytes 2005, 2 backwards WriteInt(0x0, stateObj); diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/_SqlMetaData.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadata.cs similarity index 67% rename from src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/_SqlMetaData.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadata.cs index e83725184c..f5c85a066c 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/_SqlMetaData.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadata.cs @@ -7,10 +7,10 @@ namespace Microsoft.Data.SqlClient.Parser.Tokens; -internal sealed class _SqlMetaData : SqlMetaDataPriv +internal sealed class TdsColumnMetadata : SqlMetaDataPriv { [Flags] - private enum _SqlMetadataFlags : int + private enum MetadataFlags : int { None = 0, @@ -33,14 +33,14 @@ private enum _SqlMetadataFlags : int internal byte tableNum; internal byte op; // for altrow-columns only internal ushort operand; // for altrow-columns only - private _SqlMetadataFlags flags; + private MetadataFlags flags; - internal _SqlMetaData(int ordinal) : base() + internal TdsColumnMetadata(int ordinal) : base() { this.ordinal = ordinal; } - private bool HasFlag(_SqlMetadataFlags flag) + private bool HasFlag(MetadataFlags flag) { return (flags & flag) != 0; } @@ -76,52 +76,52 @@ internal string tableName public byte Updatability { - get => (byte)(flags & _SqlMetadataFlags.IsUpdatableMask); - set => flags = (_SqlMetadataFlags)((value & (byte)_SqlMetadataFlags.IsUpdatableMask) | ((int)flags & ~(byte)_SqlMetadataFlags.IsUpdatableMask)); + get => (byte)(flags & MetadataFlags.IsUpdatableMask); + set => flags = (MetadataFlags)((value & (byte)MetadataFlags.IsUpdatableMask) | ((int)flags & ~(byte)MetadataFlags.IsUpdatableMask)); } public bool IsReadOnly { - get => !HasFlag(_SqlMetadataFlags.IsUpdatableMask); + get => !HasFlag(MetadataFlags.IsUpdatableMask); } public bool IsDifferentName { - get => HasFlag(_SqlMetadataFlags.IsDifferentName); - set => Set(_SqlMetadataFlags.IsDifferentName, value); + get => HasFlag(MetadataFlags.IsDifferentName); + set => Set(MetadataFlags.IsDifferentName, value); } public bool IsKey { - get => HasFlag(_SqlMetadataFlags.IsKey); - set => Set(_SqlMetadataFlags.IsKey, value); + get => HasFlag(MetadataFlags.IsKey); + set => Set(MetadataFlags.IsKey, value); } public bool IsHidden { - get => HasFlag(_SqlMetadataFlags.IsHidden); - set => Set(_SqlMetadataFlags.IsHidden, value); + get => HasFlag(MetadataFlags.IsHidden); + set => Set(MetadataFlags.IsHidden, value); } public bool IsExpression { - get => HasFlag(_SqlMetadataFlags.IsExpression); - set => Set(_SqlMetadataFlags.IsExpression, value); + get => HasFlag(MetadataFlags.IsExpression); + set => Set(MetadataFlags.IsExpression, value); } public bool IsIdentity { - get => HasFlag(_SqlMetadataFlags.IsIdentity); - set => Set(_SqlMetadataFlags.IsIdentity, value); + get => HasFlag(MetadataFlags.IsIdentity); + set => Set(MetadataFlags.IsIdentity, value); } public bool IsColumnSet { - get => HasFlag(_SqlMetadataFlags.IsColumnSet); - set => Set(_SqlMetadataFlags.IsColumnSet, value); + get => HasFlag(MetadataFlags.IsColumnSet); + set => Set(MetadataFlags.IsColumnSet, value); } - private void Set(_SqlMetadataFlags flag, bool value) + private void Set(MetadataFlags flag, bool value) { flags = value ? flags | flag : flags & ~flag; } @@ -144,7 +144,7 @@ internal bool IsLargeUdt public object Clone() { - _SqlMetaData result = new _SqlMetaData(ordinal); + TdsColumnMetadata result = new TdsColumnMetadata(ordinal); result.CopyFrom(this); result.column = column; result.baseColumn = baseColumn; diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadataToken.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadataToken.cs index 3f060738ef..00691db2db 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadataToken.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadataToken.cs @@ -21,17 +21,17 @@ internal sealed class TdsColumnMetadataToken // @TODO: This cannot be converted to an auto-property yet because it is accessed as an out parameter from TryReadUint16 internal ushort id; - private readonly _SqlMetaData[] _metadataArray; + private readonly TdsColumnMetadata[] _metadataArray; private int _hiddenColumnCount; // @TODO: -1 is used as magic value for "unread hidden columns". We should derive all this from _visibleColumnMap? private int[] _visibleColumnMap; internal TdsColumnMetadataToken(int count) { _hiddenColumnCount = -1; - _metadataArray = new _SqlMetaData[count]; + _metadataArray = new TdsColumnMetadata[count]; for (int i = 0; i < _metadataArray.Length; ++i) { - _metadataArray[i] = new _SqlMetaData(i); + _metadataArray[i] = new TdsColumnMetadata(i); } } @@ -56,10 +56,10 @@ private TdsColumnMetadataToken(TdsColumnMetadataToken original) } else { - _metadataArray = new _SqlMetaData[original._metadataArray.Length]; + _metadataArray = new TdsColumnMetadata[original._metadataArray.Length]; for (int idx = 0; idx < _metadataArray.Length; idx++) { - _metadataArray[idx] = (_SqlMetaData)original._metadataArray[idx].Clone(); + _metadataArray[idx] = (TdsColumnMetadata)original._metadataArray[idx].Clone(); } } } @@ -68,7 +68,7 @@ private TdsColumnMetadataToken(TdsColumnMetadataToken original) /// Provides access to the metadata array associated with this token. Allows retrieval and /// updating of metadata at a specific index. /// - internal _SqlMetaData this[int index] + internal TdsColumnMetadata this[int index] { get => _metadataArray[index]; diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs index 8a75ce01f0..3526cd93f6 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs @@ -28,9 +28,9 @@ namespace Microsoft.Data.SqlClient internal sealed class _ColumnMapping { internal readonly int _sourceColumnOrdinal; - internal readonly _SqlMetaData _metadata; + internal readonly TdsColumnMetadata _metadata; - internal _ColumnMapping(int columnId, _SqlMetaData metadata) + internal _ColumnMapping(int columnId, TdsColumnMetadata metadata) { _sourceColumnOrdinal = columnId; _metadata = metadata; @@ -797,7 +797,7 @@ private string AnalyzeTargetAndCreateUpdateBulkCommand(BulkCopySimpleResultSet i _sortedColumnMappings = new List<_ColumnMapping>(metaDataSet.Length); for (int i = 0; i < metaDataSet.Length; i++) { - _SqlMetaData metadata = metaDataSet[i]; + TdsColumnMetadata metadata = metaDataSet[i]; bool matched = false; bool rejected = false; @@ -1161,7 +1161,7 @@ private void Dispose(bool disposing) // Unified method to read a value from the current row private object GetValueFromSourceRow(int destRowIndex, out bool isSqlType, out bool isDataFeed, out bool isNull) { - _SqlMetaData metadata = _sortedColumnMappings[destRowIndex]._metadata; + TdsColumnMetadata metadata = _sortedColumnMappings[destRowIndex]._metadata; int sourceOrdinal = _sortedColumnMappings[destRowIndex]._sourceColumnOrdinal; switch (_rowSourceType) @@ -1437,7 +1437,7 @@ private bool ReadFromRowSource() private SourceColumnMetadata GetColumnMetadata(int ordinal) { int sourceOrdinal = _sortedColumnMappings[ordinal]._sourceColumnOrdinal; - _SqlMetaData metadata = _sortedColumnMappings[ordinal]._metadata; + TdsColumnMetadata metadata = _sortedColumnMappings[ordinal]._metadata; // Handle special Sql data types for SqlDataReader and DataTables ValueMethod method; @@ -1756,7 +1756,7 @@ private object ValidateBulkCopyVariant(object value) } } - private object ConvertValue(object value, _SqlMetaData metadata, bool isNull, ref bool isSqlType, out bool coercedToDataFeed) + private object ConvertValue(object value, TdsColumnMetadata metadata, bool isNull, ref bool isSqlType, out bool coercedToDataFeed) { coercedToDataFeed = false; @@ -2505,7 +2505,7 @@ private Task ReadWriteColumnValueAsync(int col) bool isNull; object value = GetValueFromSourceRow(col, out isSqlType, out isDataFeed, out isNull); //this will return Task/null in future: as rTask - _SqlMetaData metadata = _sortedColumnMappings[col]._metadata; + TdsColumnMetadata metadata = _sortedColumnMappings[col]._metadata; if (!isDataFeed) { value = ConvertValue(value, metadata, isNull, ref isSqlType, out isDataFeed); diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs index ec546bf7ad..3284b7f21b 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs @@ -291,7 +291,7 @@ internal virtual SmiExtendedMetaData[] GetInternalSmiMetaData() int returnIndex = 0; for (int index = 0; index < metaData.Length; index++) { - _SqlMetaData colMetaData = metaData[index]; + TdsColumnMetadata colMetaData = metaData[index]; if (!colMetaData.IsHidden) { @@ -549,7 +549,7 @@ internal DataTable BuildSchemaTable() for (int i = 0; i < md.Length; i++) { - _SqlMetaData col = md[i]; + TdsColumnMetadata col = md[i]; DataRow schemaRow = schemaTable.NewRow(); schemaRow[columnName] = col.column; @@ -1187,7 +1187,7 @@ public override string GetDataTypeName(int i) } } - private string GetDataTypeNameInternal(_SqlMetaData metaData) + private string GetDataTypeNameInternal(TdsColumnMetadata metaData) { string dataTypeName = null; @@ -1263,7 +1263,7 @@ public override Type GetFieldType(int i) #if !NETFRAMEWORK [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicFields)] #endif - private Type GetFieldTypeInternal(_SqlMetaData metaData) + private Type GetFieldTypeInternal(TdsColumnMetadata metaData) { Type fieldType = null; @@ -1320,16 +1320,16 @@ private static Type GetVectorFieldType(byte vectorElementType) internal virtual int GetLocaleId(int i) { - _SqlMetaData sqlMetaData = MetaData[i]; + TdsColumnMetadata tdsColumnMetadata = MetaData[i]; int lcid; - if (sqlMetaData.cipherMD != null) + if (tdsColumnMetadata.cipherMD != null) { // If this column is encrypted, get the collation from baseTI // - if (sqlMetaData.baseTI.collation != null) + if (tdsColumnMetadata.baseTI.collation != null) { - lcid = sqlMetaData.baseTI.collation.LCID; + lcid = tdsColumnMetadata.baseTI.collation.LCID; } else { @@ -1338,9 +1338,9 @@ internal virtual int GetLocaleId(int i) } else { - if (sqlMetaData.collation != null) + if (tdsColumnMetadata.collation != null) { - lcid = sqlMetaData.collation.LCID; + lcid = tdsColumnMetadata.collation.LCID; } else { @@ -1383,7 +1383,7 @@ public override Type GetProviderSpecificFieldType(int i) #if !NETFRAMEWORK [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicFields)] #endif - private Type GetProviderSpecificFieldTypeInternal(_SqlMetaData metaData) + private Type GetProviderSpecificFieldTypeInternal(TdsColumnMetadata metaData) { Type providerSpecificFieldType = null; @@ -2599,7 +2599,7 @@ private object GetSqlValueInternal(int i) // NOTE: This method is called by the fast-paths in Async methods and, therefore, should be resilient to the DataReader being closed // Always make sure to take reference copies of anything set to null in TryCloseInternal() - private object GetSqlValueFromSqlBufferInternal(SqlBuffer data, _SqlMetaData metaData) + private object GetSqlValueFromSqlBufferInternal(SqlBuffer data, TdsColumnMetadata metaData) { Debug.Assert(!data.IsEmpty || data.IsNull || metaData.type == SqlDbType.Timestamp, "Data has been read, but the buffer is empty"); @@ -2770,7 +2770,7 @@ private object GetValueInternal(int i) // NOTE: This method is called by the fast-paths in Async methods and, therefore, should be resilient to the DataReader being closed // Always make sure to take reference copies of anything set to null in TryCloseInternal() - private object GetValueFromSqlBufferInternal(SqlBuffer data, _SqlMetaData metaData) + private object GetValueFromSqlBufferInternal(SqlBuffer data, TdsColumnMetadata metaData) { Debug.Assert(!data.IsEmpty || data.IsNull || metaData.type == SqlDbType.Timestamp, "Data has been read, but the buffer is empty"); @@ -2843,7 +2843,7 @@ private T GetFieldValueInternal(int i, bool isAsync) return GetFieldValueFromSqlBufferInternal(_data[i], _metaData[i], isAsync: isAsync); } - private T GetFieldValueFromSqlBufferInternal(SqlBuffer data, _SqlMetaData metaData, bool isAsync) + private T GetFieldValueFromSqlBufferInternal(SqlBuffer data, TdsColumnMetadata metaData, bool isAsync) { // this block of type specific shortcuts uses RyuJIT jit behaviors to achieve fast implementations of the primitive types // RyuJIT will be able to determine at compilation time that the typeof(T)==typeof() options are constant @@ -3760,7 +3760,7 @@ private TdsOperationStatus TryReadColumnData() // bother to read here. if (!_data[_sharedState._nextColumnDataToRead].IsNull) { - _SqlMetaData columnMetaData = _metaData[_sharedState._nextColumnDataToRead]; + TdsColumnMetadata columnMetaData = _metaData[_sharedState._nextColumnDataToRead]; TdsOperationStatus result = _parser.TryReadSqlValue(_data[_sharedState._nextColumnDataToRead], columnMetaData, (int)_sharedState._columnDataBytesRemaining, _stateObj, _command != null ? _command.ColumnEncryptionSetting : SqlCommandColumnEncryptionSetting.UseConnectionSetting, @@ -3870,7 +3870,7 @@ internal TdsOperationStatus TryReadColumnInternal(int i, bool readHeaderOnly = f do { - _SqlMetaData columnMetaData = _metaData[_sharedState._nextColumnHeaderToRead]; + TdsColumnMetadata columnMetaData = _metaData[_sharedState._nextColumnHeaderToRead]; if (isSequentialAccess) { @@ -5835,7 +5835,7 @@ private ReadOnlyCollection BuildColumnSchema() DbColumn[] columnSchema = new DbColumn[md.Length]; for (int i = 0; i < md.Length; i++) { - _SqlMetaData col = md[i]; + TdsColumnMetadata col = md[i]; SqlDbColumn dbColumn = new SqlDbColumn(md[i]); if (_typeSystem <= SqlConnectionOptions.TypeSystem.SQLServer2005 && col.Is2008DateTimeType) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDbColumn.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDbColumn.cs index d370545b24..eed14600b6 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDbColumn.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDbColumn.cs @@ -12,9 +12,9 @@ namespace Microsoft.Data.SqlClient { internal class SqlDbColumn : DbColumn { - private readonly _SqlMetaData _metadata; + private readonly TdsColumnMetadata _metadata; - internal SqlDbColumn(_SqlMetaData md) + internal SqlDbColumn(TdsColumnMetadata md) { _metadata = md; Populate(); From 5153c14d1a20ad47caa9b7857e541f01afe27c34 Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Tue, 1 Sep 2026 14:01:06 -0500 Subject: [PATCH 08/17] Fields to properties where possible, and adding comments errywhere. --- .../Data/SqlClient/Parser/TdsParser.cs | 4 +- .../Parser/Tokens/TdsColumnMetadata.cs | 222 +++++++++++------- .../Microsoft/Data/SqlClient/SqlBulkCopy.cs | 10 +- .../Microsoft/Data/SqlClient/SqlDataReader.cs | 38 +-- .../Microsoft/Data/SqlClient/SqlDbColumn.cs | 10 +- .../Data/SqlClient/SqlMetaDataSetTest.cs | 6 +- 6 files changed, 172 insertions(+), 118 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs index 46646fa6c5..4241245176 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs @@ -11675,8 +11675,8 @@ internal void WriteBulkCopyMetaData(TdsColumnMetadataToken metadataCollection, i if (md.metaType.IsLong && !md.metaType.IsPlp) { - WriteShort(md.tableName.Length, stateObj); - WriteString(md.tableName, stateObj); + WriteShort(md.TableName.Length, stateObj); + WriteString(md.TableName, stateObj); } WriteCryptoMetadata(md, stateObj); diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadata.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadata.cs index f5c85a066c..7518e9cf59 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadata.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadata.cs @@ -7,10 +7,13 @@ namespace Microsoft.Data.SqlClient.Parser.Tokens; +/// +/// Represents metadata information for a specific column in a TDS stream. +/// internal sealed class TdsColumnMetadata : SqlMetaDataPriv { [Flags] - private enum MetadataFlags : int + private enum MetadataFlags { None = 0, @@ -26,133 +29,184 @@ private enum MetadataFlags : int IsUpdatableMask = (Updatable | UpdateableUnknown) // two bit field (0 is read only, 1 is updatable, 2 is updatability unknown) } - internal string column; + /// + /// Represents the original name of the column in the data source if an alias is used. + /// + // @TODO: This cannot be an auto property yet because this value is set via an out parameter in TryReadString. internal string baseColumn; + + /// + /// Represents the name of the column in the TDS stream. + /// + // @TODO: This cannot be an auto property yet because this value is set via an out parameter in TryReadString. + internal string column; + + /// + /// Stores the multipart name details of the table associated with the column, including server, + /// catalog, schema, and table names. + /// + // @TODO: This cannot be an auto property yet because this value is set via an out parameter in TryProcessOneTable. internal MultiPartTableName multiPartTableName; - internal readonly int ordinal; - internal byte tableNum; + + /// + /// Represents the operation type associated with the column metadata, primarily used for + /// alternate-row column processing. + /// + // @TODO: This cannot be an auto property yet because this value is set via an out parameter in TryRead*. internal byte op; // for altrow-columns only - internal ushort operand; // for altrow-columns only - private MetadataFlags flags; + + /// + /// Identifies the table associated with a column, if applicable, within the context of a + /// metadata structure. + /// + // @TODO: This cannot be an auto property yet because this value is set via an out parameter in TryRead*. + internal byte tableNum; + + private MetadataFlags _flags; internal TdsColumnMetadata(int ordinal) : base() { - this.ordinal = ordinal; + Ordinal = ordinal; } - private bool HasFlag(MetadataFlags flag) - { - return (flags & flag) != 0; - } + /// + /// Gets the catalog name associated with the column's metadata. + /// + internal string CatalogName => multiPartTableName.CatalogName; - internal string serverName - { - get - { - return multiPartTableName.ServerName; - } - } - internal string catalogName - { - get - { - return multiPartTableName.CatalogName; - } - } - internal string schemaName - { - get - { - return multiPartTableName.SchemaName; - } - } - internal string tableName - { - get - { - return multiPartTableName.TableName; - } - } - public byte Updatability - { - get => (byte)(flags & MetadataFlags.IsUpdatableMask); - set => flags = (MetadataFlags)((value & (byte)MetadataFlags.IsUpdatableMask) | ((int)flags & ~(byte)MetadataFlags.IsUpdatableMask)); - } + internal bool Is2008DateTimeType => type is SqlDbType.Date + or SqlDbType.Time + or SqlDbType.DateTime2 + or SqlDbType.DateTimeOffset; - public bool IsReadOnly + /// + /// Indicates whether the column is part of a sparse column set. + /// + public bool IsColumnSet { - get => !HasFlag(MetadataFlags.IsUpdatableMask); + get => HasFlag(MetadataFlags.IsColumnSet); + set => SetFlag(MetadataFlags.IsColumnSet, value); } + /// + /// Indicates whether the column has a name that differs from its original name in the data + /// source. + /// public bool IsDifferentName { get => HasFlag(MetadataFlags.IsDifferentName); - set => Set(MetadataFlags.IsDifferentName, value); + set => SetFlag(MetadataFlags.IsDifferentName, value); } - public bool IsKey + /// + /// Indicates whether the column in the metadata is based on an expression + /// rather than a direct column reference. + /// + public bool IsExpression { - get => HasFlag(MetadataFlags.IsKey); - set => Set(MetadataFlags.IsKey, value); + get => HasFlag(MetadataFlags.IsExpression); + set => SetFlag(MetadataFlags.IsExpression, value); } + /// + /// Indicates whether the column is hidden in the result set metadata. + /// public bool IsHidden { get => HasFlag(MetadataFlags.IsHidden); - set => Set(MetadataFlags.IsHidden, value); - } - - public bool IsExpression - { - get => HasFlag(MetadataFlags.IsExpression); - set => Set(MetadataFlags.IsExpression, value); + set => SetFlag(MetadataFlags.IsHidden, value); } + /// + /// Indicates whether the column is an identity column in the database. + /// public bool IsIdentity { get => HasFlag(MetadataFlags.IsIdentity); - set => Set(MetadataFlags.IsIdentity, value); + set => SetFlag(MetadataFlags.IsIdentity, value); } - public bool IsColumnSet - { - get => HasFlag(MetadataFlags.IsColumnSet); - set => Set(MetadataFlags.IsColumnSet, value); - } - - private void Set(MetadataFlags flag, bool value) - { - flags = value ? flags | flag : flags & ~flag; - } - - internal bool Is2008DateTimeType + /// + /// Indicates whether the column is part of the primary key in the table or view. + /// + public bool IsKey { - get - { - return SqlDbType.Date == type || SqlDbType.Time == type || SqlDbType.DateTime2 == type || SqlDbType.DateTimeOffset == type; - } - } - - internal bool IsLargeUdt + get => HasFlag(MetadataFlags.IsKey); + set => SetFlag(MetadataFlags.IsKey, value); + } + + /// + /// Indicates whether the column is a large UDT with the maximum allowable length. + /// + internal bool IsLargeUdt => type == SqlDbType.Udt && length == int.MaxValue; + + /// + /// Indicates whether the column is read-only in the context of the data source. + /// + public bool IsReadOnly => !HasFlag(MetadataFlags.IsUpdatableMask); + + /// + /// Gets the zero-based position of the column within the result set metadata. + /// + internal int Ordinal { get; } + + /// + /// Gets or sets the operand value associated with the column metadata. + /// + internal ushort Operand { get; set; } + + /// + /// Represents the schema name associated with the table containing the column. + /// + internal string SchemaName => multiPartTableName.SchemaName; + + /// + /// Gets the name of the server associated with the column's metadata. + /// + internal string ServerName => multiPartTableName.ServerName; + + /// + /// Represents the name of the table associated with the column metadata. + /// + internal string TableName => multiPartTableName.TableName; + + /// + /// Indicates whether the associated column can be updated. + /// + public byte Updatability { - get - { - return type == SqlDbType.Udt && length == int.MaxValue; - } + get => (byte)(_flags & MetadataFlags.IsUpdatableMask); + set => _flags = (MetadataFlags)((value & (byte)MetadataFlags.IsUpdatableMask) | ((int)_flags & ~(byte)MetadataFlags.IsUpdatableMask)); } + /// + /// Creates a copy of the current TdsColumnMetadata instance. + /// + /// + /// A new TdsColumnMetadata object that is a copy of the current instance. + /// public object Clone() { - TdsColumnMetadata result = new TdsColumnMetadata(ordinal); + TdsColumnMetadata result = new TdsColumnMetadata(Ordinal); result.CopyFrom(this); result.column = column; result.baseColumn = baseColumn; result.multiPartTableName = multiPartTableName; result.tableNum = tableNum; - result.flags = flags; + result._flags = _flags; result.op = op; - result.operand = operand; + result.Operand = Operand; return result; } + + private bool HasFlag(MetadataFlags flag) + { + return (_flags & flag) != 0; + } + + private void SetFlag(MetadataFlags flag, bool value) + { + _flags = value ? _flags | flag : _flags & ~flag; + } } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs index 3526cd93f6..163fe80a57 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs @@ -809,7 +809,7 @@ private string AnalyzeTargetAndCreateUpdateBulkCommand(BulkCopySimpleResultSet i // Are we missing a mapping between the result column and // this local column (by ordinal or name)? - if (localColumn._destinationColumnOrdinal != metadata.ordinal + if (localColumn._destinationColumnOrdinal != metadata.Ordinal && UnquotedName(localColumn.MappedDestinationColumn) != metadata.column) { // Yes, so move on to the next local column. @@ -1826,11 +1826,11 @@ private object ConvertValue(object value, TdsColumnMetadata metadata, bool isNul } catch (SqlTruncateException) { - throw SQL.BulkLoadCannotConvertValue(value.GetType(), mt, metadata.ordinal, RowNumber, metadata.isEncrypted, metadata.column, value.ToString(), ADP.ParameterValueOutOfRange(sqlValue)); + throw SQL.BulkLoadCannotConvertValue(value.GetType(), mt, metadata.Ordinal, RowNumber, metadata.isEncrypted, metadata.column, value.ToString(), ADP.ParameterValueOutOfRange(sqlValue)); } catch (Exception e) { - throw SQL.BulkLoadCannotConvertValue(value.GetType(), mt, metadata.ordinal, RowNumber, metadata.isEncrypted, metadata.column, value.ToString(), e); + throw SQL.BulkLoadCannotConvertValue(value.GetType(), mt, metadata.Ordinal, RowNumber, metadata.isEncrypted, metadata.column, value.ToString(), e); } } @@ -1921,7 +1921,7 @@ private object ConvertValue(object value, TdsColumnMetadata metadata, bool isNul default: Debug.Fail("Unknown TdsType!" + type.NullableType.ToString("x2", (IFormatProvider)null)); - throw SQL.BulkLoadCannotConvertValue(value.GetType(), type, metadata.ordinal, RowNumber, metadata.isEncrypted, metadata.column, value.ToString(), null); + throw SQL.BulkLoadCannotConvertValue(value.GetType(), type, metadata.Ordinal, RowNumber, metadata.isEncrypted, metadata.column, value.ToString(), null); } if (typeChanged) @@ -1934,7 +1934,7 @@ private object ConvertValue(object value, TdsColumnMetadata metadata, bool isNul } catch (Exception e) when (ADP.IsCatchableExceptionType(e)) { - throw SQL.BulkLoadCannotConvertValue(value.GetType(), type, metadata.ordinal, RowNumber, metadata.isEncrypted, metadata.column, value.ToString(), e); + throw SQL.BulkLoadCannotConvertValue(value.GetType(), type, metadata.Ordinal, RowNumber, metadata.isEncrypted, metadata.column, value.ToString(), e); } } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs index 3284b7f21b..192ff9e257 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs @@ -344,10 +344,10 @@ internal virtual SmiExtendedMetaData[] GetInternalSmiMetaData() typeSpecificNamePart2, typeSpecificNamePart3, colMetaData.IsNullable, - colMetaData.serverName, - colMetaData.catalogName, - colMetaData.schemaName, - colMetaData.tableName, + colMetaData.ServerName, + colMetaData.CatalogName, + colMetaData.SchemaName, + colMetaData.TableName, colMetaData.baseColumn, colMetaData.IsKey, colMetaData.IsIdentity, @@ -553,7 +553,7 @@ internal DataTable BuildSchemaTable() DataRow schemaRow = schemaTable.NewRow(); schemaRow[columnName] = col.column; - schemaRow[ordinal] = col.ordinal; + schemaRow[ordinal] = col.Ordinal; // // be sure to return character count for string types, byte count otherwise // col.length is always byte count so for unicode types, half the length @@ -706,21 +706,21 @@ internal DataTable BuildSchemaTable() schemaRow[isReadOnly] = col.IsReadOnly; schemaRow[isColumnSet] = col.IsColumnSet; - if (!string.IsNullOrEmpty(col.serverName)) + if (!string.IsNullOrEmpty(col.ServerName)) { - schemaRow[baseServerName] = col.serverName; + schemaRow[baseServerName] = col.ServerName; } - if (!string.IsNullOrEmpty(col.catalogName)) + if (!string.IsNullOrEmpty(col.CatalogName)) { - schemaRow[baseCatalogName] = col.catalogName; + schemaRow[baseCatalogName] = col.CatalogName; } - if (!string.IsNullOrEmpty(col.schemaName)) + if (!string.IsNullOrEmpty(col.SchemaName)) { - schemaRow[baseSchemaName] = col.schemaName; + schemaRow[baseSchemaName] = col.SchemaName; } - if (!string.IsNullOrEmpty(col.tableName)) + if (!string.IsNullOrEmpty(col.TableName)) { - schemaRow[baseTableName] = col.tableName; + schemaRow[baseTableName] = col.TableName; } if (!string.IsNullOrEmpty(col.baseColumn)) { @@ -2923,8 +2923,8 @@ private T GetFieldValueFromSqlBufferInternal(SqlBuffer data, TdsColumnMetadat if (IsCommandBehavior(CommandBehavior.SequentialAccess)) { // Wrap the sequential stream in an XmlReader - _currentStream = new SqlSequentialStream(this, metaData.ordinal); - _lastColumnWithDataChunkRead = metaData.ordinal; + _currentStream = new SqlSequentialStream(this, metaData.Ordinal); + _lastColumnWithDataChunkRead = metaData.Ordinal; return (T)(object)SqlTypeWorkarounds.SqlXmlCreateSqlXmlReader(_currentStream, closeInput: true, async: isAsync); } else @@ -2973,8 +2973,8 @@ private T GetFieldValueFromSqlBufferInternal(SqlBuffer data, TdsColumnMetadat encoding = metaData.encoding; } - _currentTextReader = new SqlSequentialTextReader(this, metaData.ordinal, encoding); - _lastColumnWithDataChunkRead = metaData.ordinal; + _currentTextReader = new SqlSequentialTextReader(this, metaData.Ordinal, encoding); + _lastColumnWithDataChunkRead = metaData.Ordinal; return (T)(object)_currentTextReader; } else @@ -3003,8 +3003,8 @@ private T GetFieldValueFromSqlBufferInternal(SqlBuffer data, TdsColumnMetadat if ((metaType.SqlDbType != SqlDbType.Variant) && (IsCommandBehavior(CommandBehavior.SequentialAccess))) { - _currentStream = new SqlSequentialStream(this, metaData.ordinal); - _lastColumnWithDataChunkRead = metaData.ordinal; + _currentStream = new SqlSequentialStream(this, metaData.Ordinal); + _lastColumnWithDataChunkRead = metaData.Ordinal; return (T)(object)_currentStream; } else diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDbColumn.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDbColumn.cs index eed14600b6..60ab6d321c 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDbColumn.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDbColumn.cs @@ -23,13 +23,13 @@ internal SqlDbColumn(TdsColumnMetadata md) private void Populate() { AllowDBNull = _metadata.IsNullable; - BaseCatalogName = _metadata.catalogName; + BaseCatalogName = _metadata.CatalogName; BaseColumnName = _metadata.baseColumn; - BaseSchemaName = _metadata.schemaName; - BaseServerName = _metadata.serverName; - BaseTableName = _metadata.tableName; + BaseSchemaName = _metadata.SchemaName; + BaseServerName = _metadata.ServerName; + BaseTableName = _metadata.TableName; ColumnName = _metadata.column; - ColumnOrdinal = _metadata.ordinal; + ColumnOrdinal = _metadata.Ordinal; ColumnSize = (_metadata.metaType.IsSizeInCharacters && (_metadata.length != 0x7fffffff)) ? (_metadata.length / 2) : _metadata.length; IsAutoIncrement = _metadata.IsIdentity; IsIdentity = _metadata.IsIdentity; diff --git a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlMetaDataSetTest.cs b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlMetaDataSetTest.cs index c74c7c21e5..d005024bc3 100644 --- a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlMetaDataSetTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlMetaDataSetTest.cs @@ -119,9 +119,9 @@ public void SqlMetaDataSet_Clone_PreservesOrdinals() TdsColumnMetadataToken clone = original.Clone(); - Assert.Equal(original[0].ordinal, clone[0].ordinal); - Assert.Equal(original[1].ordinal, clone[1].ordinal); - Assert.Equal(original[2].ordinal, clone[2].ordinal); + Assert.Equal(original[0].Ordinal, clone[0].Ordinal); + Assert.Equal(original[1].Ordinal, clone[1].Ordinal); + Assert.Equal(original[2].Ordinal, clone[2].Ordinal); } [Fact] From a67ceb8b9e5f6ba9119f67dcac126bb1ab9fc2a5 Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Tue, 1 Sep 2026 14:12:49 -0500 Subject: [PATCH 09/17] _SqlMetaDataSetCollection.cs => TdsAltMetadataCollection.cs --- .../src/Microsoft/Data/SqlClient/Parser/TdsParser.cs | 2 +- .../Data/SqlClient/Parser/TdsParserStateObject.cs | 6 +++--- ...taDataSetCollection.cs => TdsAltMetadataCollection.cs} | 6 +++--- .../Data/SqlClient/Parser/Tokens/TdsColumnMetadata.cs | 3 ++- .../src/Microsoft/Data/SqlClient/SqlDataReader.cs | 8 ++++---- 5 files changed, 13 insertions(+), 12 deletions(-) rename src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/{_SqlMetaDataSetCollection.cs => TdsAltMetadataCollection.cs} (89%) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs index 4241245176..329fb6d8b1 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs @@ -3001,7 +3001,7 @@ internal TdsOperationStatus TryRun(RunBehavior runBehavior, SqlCommand cmdHandle if (stateObj._cleanupAltMetaDataSetArray == null) { // create object on demand (lazy creation) - stateObj._cleanupAltMetaDataSetArray = new _SqlMetaDataSetCollection(); + stateObj._cleanupAltMetaDataSetArray = new TdsAltMetadataCollection(); } TdsColumnMetadataToken cleanupAltMetaDataSet; diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParserStateObject.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParserStateObject.cs index 51379a3a74..d52101baf6 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParserStateObject.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParserStateObject.cs @@ -227,7 +227,7 @@ private enum SnapshotStatus internal int _partialHeaderBytesRead; internal TdsColumnMetadataToken _cleanupMetaData; - internal _SqlMetaDataSetCollection _cleanupAltMetaDataSetArray; + internal TdsAltMetadataCollection _cleanupAltMetaDataSetArray; private SniContext _sniContext = SniContext.Undefined; #if DEBUG @@ -4933,7 +4933,7 @@ private sealed class StateObjectData private byte _messageStatus; internal NullBitmap _nullBitmapInfo; private TdsColumnMetadataToken _cleanupMetaData; - internal _SqlMetaDataSetCollection _cleanupAltMetaDataSetArray; + internal TdsAltMetadataCollection _cleanupAltMetaDataSetArray; private SnapshottedStateFlags _state; private ulong _longLen; private ulong _longLenLeft; @@ -5096,7 +5096,7 @@ internal void CloneCleanupAltMetaDataSetArray() { if (_stateObj._cleanupAltMetaDataSetArray != null && object.ReferenceEquals(_replayStateData?._cleanupAltMetaDataSetArray ?? default, _stateObj._cleanupAltMetaDataSetArray)) { - _stateObj._cleanupAltMetaDataSetArray = (_SqlMetaDataSetCollection)_stateObj._cleanupAltMetaDataSetArray.Clone(); + _stateObj._cleanupAltMetaDataSetArray = (TdsAltMetadataCollection)_stateObj._cleanupAltMetaDataSetArray.Clone(); } } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/_SqlMetaDataSetCollection.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsAltMetadataCollection.cs similarity index 89% rename from src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/_SqlMetaDataSetCollection.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsAltMetadataCollection.cs index cc303c5f62..ffee6f8c0f 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/_SqlMetaDataSetCollection.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsAltMetadataCollection.cs @@ -7,12 +7,12 @@ namespace Microsoft.Data.SqlClient.Parser.Tokens; -internal sealed class _SqlMetaDataSetCollection +internal sealed class TdsAltMetadataCollection { private readonly List _altMetaDataSetArray; internal TdsColumnMetadataToken metaDataSet; - internal _SqlMetaDataSetCollection() + internal TdsAltMetadataCollection() { _altMetaDataSetArray = new List(); } @@ -50,7 +50,7 @@ internal TdsColumnMetadataToken GetAltMetaData(int id) public object Clone() { - _SqlMetaDataSetCollection result = new _SqlMetaDataSetCollection() { metaDataSet = metaDataSet?.Clone() }; + TdsAltMetadataCollection result = new TdsAltMetadataCollection() { metaDataSet = metaDataSet?.Clone() }; foreach (TdsColumnMetadataToken set in _altMetaDataSetArray) { diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadata.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadata.cs index 7518e9cf59..3c0c6da0cf 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadata.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadata.cs @@ -8,7 +8,8 @@ namespace Microsoft.Data.SqlClient.Parser.Tokens; /// -/// Represents metadata information for a specific column in a TDS stream. +/// Represents metadata information for a specific column in a TDS stream. Corresponds with a +/// single column withing a SQLCOLMETADATA/SQLALTMETADATA token. /// internal sealed class TdsColumnMetadata : SqlMetaDataPriv { diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs index 192ff9e257..a1fc0135c7 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs @@ -77,7 +77,7 @@ internal class SharedState // buffers and metadata private TdsColumnMetadataToken _metaData; // current metaData for the stream, it is lazily loaded - private _SqlMetaDataSetCollection _altMetaDataSetCollection; + private TdsAltMetadataCollection _altMetaDataSetCollection; private FieldNameLookup _fieldNameLookup; private CommandBehavior _commandBehavior; @@ -4186,11 +4186,11 @@ internal TdsOperationStatus TrySetAltMetaDataSet(TdsColumnMetadataToken metaData { if (_altMetaDataSetCollection == null) { - _altMetaDataSetCollection = new _SqlMetaDataSetCollection(); + _altMetaDataSetCollection = new TdsAltMetadataCollection(); } else if (_snapshot != null && object.ReferenceEquals(_snapshot._altMetaDataSetCollection, _altMetaDataSetCollection)) { - _altMetaDataSetCollection = (_SqlMetaDataSetCollection)_altMetaDataSetCollection.Clone(); + _altMetaDataSetCollection = (TdsAltMetadataCollection)_altMetaDataSetCollection.Clone(); } _altMetaDataSetCollection.SetAltMetaData(metaDataSet); _metaDataConsumed = metaDataConsumed; @@ -5649,7 +5649,7 @@ internal sealed class Snapshot public long _columnDataBytesRemaining; public TdsColumnMetadataToken _metadata; - public _SqlMetaDataSetCollection _altMetaDataSetCollection; + public TdsAltMetadataCollection _altMetaDataSetCollection; public MultiPartTableName[] _tableNames; public SqlSequentialStream _currentStream; From aa22ede58e7394686cd0940ec2c154aaea8e568e Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Tue, 1 Sep 2026 14:19:38 -0500 Subject: [PATCH 10/17] Fields => properties where possible, add comments. --- .../Data/SqlClient/Parser/TdsParser.cs | 4 +- .../Parser/Tokens/TdsAltMetadataCollection.cs | 73 +++++++++++++------ .../Microsoft/Data/SqlClient/SqlDataReader.cs | 8 +- 3 files changed, 56 insertions(+), 29 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs index 329fb6d8b1..3c7e059445 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs @@ -3011,7 +3011,7 @@ internal TdsOperationStatus TryRun(RunBehavior runBehavior, SqlCommand cmdHandle return result; } - stateObj._cleanupAltMetaDataSetArray.SetAltMetaData(cleanupAltMetaDataSet); + stateObj._cleanupAltMetaDataSetArray.SetAltMetadata(cleanupAltMetaDataSet); if (dataStream != null) { byte metadataConsumedByte; @@ -3049,7 +3049,7 @@ internal TdsOperationStatus TryRun(RunBehavior runBehavior, SqlCommand cmdHandle return result; } - result = TrySkipRow(stateObj._cleanupAltMetaDataSetArray.GetAltMetaData(altRowId), stateObj); + result = TrySkipRow(stateObj._cleanupAltMetaDataSetArray.GetAltMetadata(altRowId), stateObj); if (result != TdsOperationStatus.Done) { // skip altRow diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsAltMetadataCollection.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsAltMetadataCollection.cs index ffee6f8c0f..ef85b22d12 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsAltMetadataCollection.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsAltMetadataCollection.cs @@ -7,55 +7,82 @@ namespace Microsoft.Data.SqlClient.Parser.Tokens; +/// +/// Represents a collection of alternative metadata sets within a SQLALTMETADATA or SQLALTROW token. +/// internal sealed class TdsAltMetadataCollection { - private readonly List _altMetaDataSetArray; - internal TdsColumnMetadataToken metaDataSet; + private readonly List _altMetadataSetArray; internal TdsAltMetadataCollection() { - _altMetaDataSetArray = new List(); + _altMetadataSetArray = new List(); } - internal void SetAltMetaData(TdsColumnMetadataToken altMetaDataSet) + /// + /// Gets or sets the primary metadata set associated with an alternative metadata collection. + /// + internal TdsColumnMetadataToken MetadataSet { get; set; } + + /// + /// Creates a deep copy of the current TdsAltMetadataCollection instance, including all its elements. + /// + public object Clone() { - // If altmetadata with same id is found, override it rather than adding a new one - int newId = altMetaDataSet.id; - for (int i = 0; i < _altMetaDataSetArray.Count; i++) + TdsAltMetadataCollection result = new() { MetadataSet = MetadataSet?.Clone() }; + + foreach (TdsColumnMetadataToken set in _altMetadataSetArray) { - if (_altMetaDataSetArray[i].id == newId) - { - // override the existing metadata with the same id - _altMetaDataSetArray[i] = altMetaDataSet; - return; - } + result._altMetadataSetArray.Add(set.Clone()); } - // if we did not find metadata to override, add as new - _altMetaDataSetArray.Add(altMetaDataSet); + return result; } - internal TdsColumnMetadataToken GetAltMetaData(int id) + /// + /// Retrieves the alternate metadata set that corresponds to the specified identifier. + /// + /// The identifier of the alternate metadata set to retrieve. + /// + /// The instance that matches the specified identifier, + /// or null if no matching metadata set is found. + /// + internal TdsColumnMetadataToken GetAltMetadata(int id) { - foreach (TdsColumnMetadataToken altMetaDataSet in _altMetaDataSetArray) + foreach (TdsColumnMetadataToken altMetaDataSet in _altMetadataSetArray) { if (altMetaDataSet.id == id) { return altMetaDataSet; } } + + // @TODO: Debug fail usage here isn't ideal. Debug.Fail("Can't match up altMetaDataSet with given id"); return null; } - public object Clone() + /// + /// Updates or adds an alternative metadata set to the collection. + /// + /// + /// Alternative metadata set to update or add to the collection. + /// + internal void SetAltMetadata(TdsColumnMetadataToken altMetadataSet) { - TdsAltMetadataCollection result = new TdsAltMetadataCollection() { metaDataSet = metaDataSet?.Clone() }; - - foreach (TdsColumnMetadataToken set in _altMetaDataSetArray) + // If alt metadata with same id is found, override it rather than adding a new one + int newId = altMetadataSet.id; + for (int i = 0; i < _altMetadataSetArray.Count; i++) { - result._altMetaDataSetArray.Add(set.Clone()); + if (_altMetadataSetArray[i].id == newId) + { + // override the existing metadata with the same id + _altMetadataSetArray[i] = altMetadataSet; + return; + } } - return result; + + // if we did not find metadata to override, add as new + _altMetadataSetArray.Add(altMetadataSet); } } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs index a1fc0135c7..deb1a6e4c8 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs @@ -3191,7 +3191,7 @@ private TdsOperationStatus TryHasMoreResults(out bool moreResults) if (_altRowStatus == ALTROWSTATUS.Null) { // cache the regular metadata - _altMetaDataSetCollection.metaDataSet = _metaData; + _altMetaDataSetCollection.MetadataSet = _metaData; _metaData = null; } else @@ -3452,7 +3452,7 @@ private TdsOperationStatus TryNextResult(out bool more) more = false; return result; } - TdsColumnMetadataToken altMetaDataSet = _altMetaDataSetCollection.GetAltMetaData(altRowId); + TdsColumnMetadataToken altMetaDataSet = _altMetaDataSetCollection.GetAltMetadata(altRowId); if (altMetaDataSet != null) { _metaData = altMetaDataSet; @@ -3461,7 +3461,7 @@ private TdsOperationStatus TryNextResult(out bool more) break; case ALTROWSTATUS.Done: // restore the row-metaData - _metaData = _altMetaDataSetCollection.metaDataSet; + _metaData = _altMetaDataSetCollection.MetadataSet; Debug.Assert(_altRowStatus == ALTROWSTATUS.Done, "invalid AltRowStatus"); _altRowStatus = ALTROWSTATUS.Null; break; @@ -4192,7 +4192,7 @@ internal TdsOperationStatus TrySetAltMetaDataSet(TdsColumnMetadataToken metaData { _altMetaDataSetCollection = (TdsAltMetadataCollection)_altMetaDataSetCollection.Clone(); } - _altMetaDataSetCollection.SetAltMetaData(metaDataSet); + _altMetaDataSetCollection.SetAltMetadata(metaDataSet); _metaDataConsumed = metaDataConsumed; if (_metaDataConsumed && _parser != null) { From cfdc403d8448093ace75270583c6bdb73ebd8129 Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Tue, 1 Sep 2026 17:44:39 -0500 Subject: [PATCH 11/17] MultipartTableName => TdsTableName, add comments. --- .../Data/SqlClient/Parser/TdsParser.cs | 24 +++++------ .../Parser/Tokens/TdsColumnMetadata.cs | 24 +++++------ ...{MultiPartTableName.cs => TdsTableName.cs} | 43 ++++++++++++++++--- .../Microsoft/Data/SqlClient/SqlDataReader.cs | 6 +-- 4 files changed, 64 insertions(+), 33 deletions(-) rename src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/{MultiPartTableName.cs => TdsTableName.cs} (57%) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs index 3c7e059445..6af1ed6130 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs @@ -2965,7 +2965,7 @@ internal TdsOperationStatus TryRun(RunBehavior runBehavior, SqlCommand cmdHandle { if (dataStream != null) { - MultiPartTableName[] tableNames; + TdsTableName[] tableNames; result = TryProcessTableName(tokenLength, stateObj, out tableNames); if (result != TdsOperationStatus.Done) { @@ -5733,7 +5733,7 @@ private TdsOperationStatus TryCommonProcessMetaData(TdsParserStateObject stateOb if (col.metaType.IsLong && !col.metaType.IsPlp) { int unusedLen = 0xFFFF; //We ignore this value - result = TryProcessOneTable(stateObj, ref unusedLen, out col.multiPartTableName); + result = TryProcessOneTable(stateObj, ref unusedLen, out col.tableName); if (result != TdsOperationStatus.Done) { return result; @@ -5888,12 +5888,12 @@ private void WriteUDTMetaData(object value, string database, string schema, stri } } - internal TdsOperationStatus TryProcessTableName(int length, TdsParserStateObject stateObj, out MultiPartTableName[] multiPartTableNames) + internal TdsOperationStatus TryProcessTableName(int length, TdsParserStateObject stateObj, out TdsTableName[] multiPartTableNames) { int tablesAdded = 0; - MultiPartTableName[] tables = new MultiPartTableName[1]; - MultiPartTableName mpt; + TdsTableName[] tables = new TdsTableName[1]; + TdsTableName mpt; while (length > 0) { // UNDONE: BUG(?) SQL 8.003 returns two tables sometimes @@ -5911,7 +5911,7 @@ internal TdsOperationStatus TryProcessTableName(int length, TdsParserStateObject } else { - MultiPartTableName[] newTables = new MultiPartTableName[tables.Length + 1]; + TdsTableName[] newTables = new TdsTableName[tables.Length + 1]; Array.Copy(tables, 0, newTables, 0, tables.Length); newTables[tables.Length] = mpt; tables = newTables; @@ -5924,15 +5924,15 @@ internal TdsOperationStatus TryProcessTableName(int length, TdsParserStateObject return TdsOperationStatus.Done; } - private TdsOperationStatus TryProcessOneTable(TdsParserStateObject stateObj, ref int length, out MultiPartTableName multiPartTableName) + private TdsOperationStatus TryProcessOneTable(TdsParserStateObject stateObj, ref int length, out TdsTableName tdsTableName) { ushort tableLen; - MultiPartTableName mpt; + TdsTableName mpt; string value; - multiPartTableName = default(MultiPartTableName); + tdsTableName = default(TdsTableName); - mpt = new MultiPartTableName(); + mpt = new TdsTableName(); byte nParts; // Find out how many parts in the TDS stream @@ -6012,7 +6012,7 @@ private TdsOperationStatus TryProcessOneTable(TdsParserStateObject stateObj, ref } Debug.Assert(nParts == 0, "ProcessTableName:Unidentified parts in the table name token stream!"); - multiPartTableName = mpt; + tdsTableName = mpt; return TdsOperationStatus.Done; } @@ -6073,7 +6073,7 @@ private TdsOperationStatus TryProcessColInfo(TdsColumnMetadataToken columns, Sql if ((reader.TableNames != null) && (col.tableNum > 0)) { Debug.Assert(reader.TableNames.Length >= col.tableNum, "invalid tableNames array!"); - col.multiPartTableName = reader.TableNames[col.tableNum - 1]; + col.tableName = reader.TableNames[col.tableNum - 1]; } // MDAC 60109: expressions are readonly diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadata.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadata.cs index 3c0c6da0cf..9b9123a75a 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadata.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadata.cs @@ -42,13 +42,6 @@ private enum MetadataFlags // @TODO: This cannot be an auto property yet because this value is set via an out parameter in TryReadString. internal string column; - /// - /// Stores the multipart name details of the table associated with the column, including server, - /// catalog, schema, and table names. - /// - // @TODO: This cannot be an auto property yet because this value is set via an out parameter in TryProcessOneTable. - internal MultiPartTableName multiPartTableName; - /// /// Represents the operation type associated with the column metadata, primarily used for /// alternate-row column processing. @@ -56,6 +49,13 @@ private enum MetadataFlags // @TODO: This cannot be an auto property yet because this value is set via an out parameter in TryRead*. internal byte op; // for altrow-columns only + /// + /// Stores the multipart name details of the table associated with the column, including server, + /// catalog, schema, and table names. + /// + // @TODO: This cannot be an auto property yet because this value is set via an out parameter in TryProcessOneTable. + internal TdsTableName tableName; + /// /// Identifies the table associated with a column, if applicable, within the context of a /// metadata structure. @@ -73,7 +73,7 @@ internal TdsColumnMetadata(int ordinal) : base() /// /// Gets the catalog name associated with the column's metadata. /// - internal string CatalogName => multiPartTableName.CatalogName; + internal string CatalogName => tableName.CatalogName; internal bool Is2008DateTimeType => type is SqlDbType.Date @@ -160,17 +160,17 @@ public bool IsKey /// /// Represents the schema name associated with the table containing the column. /// - internal string SchemaName => multiPartTableName.SchemaName; + internal string SchemaName => tableName.SchemaName; /// /// Gets the name of the server associated with the column's metadata. /// - internal string ServerName => multiPartTableName.ServerName; + internal string ServerName => tableName.ServerName; /// /// Represents the name of the table associated with the column metadata. /// - internal string TableName => multiPartTableName.TableName; + internal string TableName => tableName.TableName; /// /// Indicates whether the associated column can be updated. @@ -193,7 +193,7 @@ public object Clone() result.CopyFrom(this); result.column = column; result.baseColumn = baseColumn; - result.multiPartTableName = multiPartTableName; + result.tableName = tableName; result.tableNum = tableNum; result._flags = _flags; result.op = op; diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/MultiPartTableName.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsTableName.cs similarity index 57% rename from src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/MultiPartTableName.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsTableName.cs index 516eced6fd..73d1cb7582 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/MultiPartTableName.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsTableName.cs @@ -7,15 +7,23 @@ namespace Microsoft.Data.SqlClient.Parser.Tokens; -internal struct MultiPartTableName +/// +/// Represents a TDS-specific table name, which may include server, catalog, schema, and table segments. +/// +internal struct TdsTableName { + internal static readonly TdsTableName Null = new([null, null, null, null]); + private string _multipartName; private string _serverName; private string _catalogName; private string _schemaName; private string _tableName; - internal MultiPartTableName(string[] parts) + /// + /// Constructs a new instance from an array of name parts. + /// + internal TdsTableName(string[] parts) { _multipartName = null; _serverName = parts[0]; @@ -24,8 +32,14 @@ internal MultiPartTableName(string[] parts) _tableName = parts[3]; } - internal MultiPartTableName(string multipartName) + /// + /// Constructs a new instance from a multipart name. This name must be parsed before its + /// constituent parts can be accessed. + /// + /// Multipart name. + internal TdsTableName(string multipartName) { + // @TODO: Is it faster to just parse this now versus rather than check everytime the parts are accessed? _multipartName = multipartName; _serverName = null; _catalogName = null; @@ -33,6 +47,9 @@ internal MultiPartTableName(string multipartName) _tableName = null; } + /// + /// Gets or sets the server name associated with the TDS-specific table name. + /// internal string ServerName { get @@ -42,6 +59,10 @@ internal string ServerName } set { _serverName = value; } } + + /// + /// Gets or sets the catalog name segment of the TDS-specific table name. + /// internal string CatalogName { get @@ -51,6 +72,10 @@ internal string CatalogName } set { _catalogName = value; } } + + /// + /// Gets or sets the schema name associated with the TDS-specific table name. + /// internal string SchemaName { get @@ -60,6 +85,10 @@ internal string SchemaName } set { _schemaName = value; } } + + /// + /// Gets or sets the table name segment of the TDS-specific table representation. + /// internal string TableName { get @@ -74,7 +103,11 @@ private void ParseMultipartName() { if (_multipartName != null) { - string[] parts = MultipartIdentifier.ParseMultipartIdentifier(_multipartName, Strings.SQL_TDSParserTableName, false); + string[] parts = MultipartIdentifier.ParseMultipartIdentifier( + _multipartName, + Strings.SQL_TDSParserTableName, + throwOnEmptyMultipartIdentifier: false); + _serverName = parts[0]; _catalogName = parts[1]; _schemaName = parts[2]; @@ -82,6 +115,4 @@ private void ParseMultipartName() _multipartName = null; } } - - internal static readonly MultiPartTableName Null = new(new string[] { null, null, null, null }); } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs index deb1a6e4c8..f31a1889be 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs @@ -86,7 +86,7 @@ internal class SharedState internal readonly int ObjectID = Interlocked.Increment(ref s_objectTypeCount); // metadata (no explicit table, use 'Table') - private MultiPartTableName[] _tableNames = null; + private TdsTableName[] _tableNames = null; private string _resetOptionsString; private int _lastColumnWithDataChunkRead; @@ -395,7 +395,7 @@ private SqlStatistics Statistics } } - internal MultiPartTableName[] TableNames + internal TdsTableName[] TableNames { get { @@ -5650,7 +5650,7 @@ internal sealed class Snapshot public TdsColumnMetadataToken _metadata; public TdsAltMetadataCollection _altMetaDataSetCollection; - public MultiPartTableName[] _tableNames; + public TdsTableName[] _tableNames; public SqlSequentialStream _currentStream; public SqlSequentialTextReader _currentTextReader; From c3df7554e99a32410136a589bf9df26c06335f6b Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Tue, 1 Sep 2026 17:54:39 -0500 Subject: [PATCH 12/17] RoutingInfo => TdsEnvChangeRoutingInfo --- .../Data/SqlClient/Connection/ServerInfo.cs | 12 +++--- .../Connection/SqlConnectionInternal.cs | 2 +- .../Data/SqlClient/Parser/TdsParser.cs | 4 +- .../SqlClient/Parser/Tokens/RoutingInfo.cs | 28 ------------ .../Parser/Tokens/TdsEnvChangeRoutingInfo.cs | 43 +++++++++++++++++++ .../Microsoft/Data/SqlClient/SqlEnvChange.cs | 2 +- 6 files changed, 53 insertions(+), 38 deletions(-) delete mode 100644 src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/RoutingInfo.cs create mode 100644 src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsEnvChangeRoutingInfo.cs diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Connection/ServerInfo.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Connection/ServerInfo.cs index 49e6167b49..051aff00a2 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Connection/ServerInfo.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Connection/ServerInfo.cs @@ -59,23 +59,23 @@ private ServerInfo(SqlConnectionOptions userOptions, string serverName) /// internal ServerInfo( SqlConnectionOptions userOptions, - RoutingInfo routing, + TdsEnvChangeRoutingInfo routingInfo, string preRoutingServerName, string serverSpn) { - Debug.Assert(userOptions != null && routing != null); - Debug.Assert(routing.ServerName != null, "server name should never be null"); + Debug.Assert(userOptions != null && routingInfo != null); + Debug.Assert(routingInfo.ServerName != null, "server name should never be null"); // Ensure user server name is not null // NOTE: string.Format should be used here to ensure invariant culture is used for port number. - UserServerName = routing == null || routing.ServerName == null + UserServerName = routingInfo == null || routingInfo.ServerName == null ? string.Empty - : string.Format(CultureInfo.InvariantCulture, "{0},{1}", routing.ServerName, routing.Port); + : string.Format(CultureInfo.InvariantCulture, "{0},{1}", routingInfo.ServerName, routingInfo.Port); PreRoutingServerName = preRoutingServerName; UserProtocol = TdsEnums.TCP; SetDerivedNames(UserProtocol, UserServerName); - ResolvedDatabaseName = routing?.DatabaseName ?? userOptions.InitialCatalog; + ResolvedDatabaseName = routingInfo?.DatabaseName ?? userOptions.InitialCatalog; ServerSPN = serverSpn; } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Connection/SqlConnectionInternal.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Connection/SqlConnectionInternal.cs index 335a9701aa..93c06da3aa 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Connection/SqlConnectionInternal.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Connection/SqlConnectionInternal.cs @@ -730,7 +730,7 @@ internal SqlConnectionPoolGroupProviderInfo PoolGroupProviderInfo internal string RoutingDestination { get; private set; } - internal RoutingInfo RoutingInfo { get; private set; } = null; + internal TdsEnvChangeRoutingInfo RoutingInfo { get; private set; } = null; internal int ServerProcessId { diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs index 6af1ed6130..ea6466a2e8 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs @@ -3411,7 +3411,7 @@ private TdsOperationStatus TryProcessEnvChange(int tokenLength, TdsParserStateOb { return result; } - env._newRoutingInfo = new RoutingInfo(protocol, port, serverName); + env._newRoutingInfo = new TdsEnvChangeRoutingInfo(protocol, port, serverName); ushort oldLength; result = stateObj.TryReadUInt16(out oldLength); if (result != TdsOperationStatus.Done) @@ -3519,7 +3519,7 @@ private TdsOperationStatus TryProcessEnhancedRoutingToken(SqlEnvChange env, TdsP return result; } - env._newRoutingInfo = new RoutingInfo(protocol, port, serverName, databaseName); + env._newRoutingInfo = new TdsEnvChangeRoutingInfo(protocol, port, serverName, databaseName); ushort oldLength; result = stateObj.TryReadUInt16(out oldLength); diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/RoutingInfo.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/RoutingInfo.cs deleted file mode 100644 index c40c4cd970..0000000000 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/RoutingInfo.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace Microsoft.Data.SqlClient.Parser.Tokens; - -internal class RoutingInfo -{ - internal RoutingInfo(byte protocol, ushort port, string serverName, string databaseName = null) - { - Protocol = protocol; - Port = port; - ServerName = serverName; - DatabaseName = databaseName; - } - - internal byte Protocol { get; private set; } - - internal ushort Port { get; private set; } - - internal string ServerName { get; private set; } - - /// - /// The DatabaseName property is only used when routing via an EnhancedRouting ENVCHANGE token. - /// It is not used when routing via the normal Routing ENVCHANGE token. - /// - internal string DatabaseName { get; private set; } -} diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsEnvChangeRoutingInfo.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsEnvChangeRoutingInfo.cs new file mode 100644 index 0000000000..dc3133e591 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsEnvChangeRoutingInfo.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.Data.SqlClient.Parser.Tokens; + +/// +/// Represents the routing information related to environment changes in the TDS protocol. This +/// information is included within an SQLENVCHANGE token when the type is ENV_ROUTING or +/// ENV_ENHANCEDROUTING. +/// +internal class TdsEnvChangeRoutingInfo +{ + internal TdsEnvChangeRoutingInfo(byte protocol, ushort port, string serverName, string databaseName = null) + { + Protocol = protocol; + Port = port; + ServerName = serverName; + DatabaseName = databaseName; + } + + /// + /// The DatabaseName property is only used when routing via an EnhancedRouting ENVCHANGE token. + /// It is not used when routing via the normal Routing ENVCHANGE token. + /// + internal string DatabaseName { get; private set; } + + /// + /// Represents the port used for establishing a connection in a TDS routing environment. + /// + internal ushort Port { get; private set; } + + /// + /// Represents the protocol used for routing in an EnhancedRouting ENVCHANGE token. + /// + // @TODO: Should this be an enum of protocol types? + internal byte Protocol { get; private set; } + + /// + /// Specifies the name of the server involved in the routing process of the TDS environment. + /// + internal string ServerName { get; private set; } +} diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlEnvChange.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlEnvChange.cs index d627442aa9..f83ca30b44 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlEnvChange.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlEnvChange.cs @@ -28,7 +28,7 @@ internal sealed class SqlEnvChange internal long _oldLongValue; internal SqlCollation _newCollation; internal SqlCollation _oldCollation; - internal RoutingInfo _newRoutingInfo; + internal TdsEnvChangeRoutingInfo _newRoutingInfo; internal bool _newBinRented; internal bool _oldBinRented; From f621c6d44861ebec5fefc2c83015355358f02f8d Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Wed, 2 Sep 2026 17:25:42 -0500 Subject: [PATCH 13/17] SqlMetaDataPriv.cs => TdsTypeInfo --- .../Data/SqlClient/Parser/TdsParser.cs | 112 +++++++++--------- .../Parser/Tokens/TdsColumnMetadata.cs | 6 +- .../Parser/Tokens/TdsReturnValueToken.cs | 2 +- .../{SqlMetaDataPriv.cs => TdsTypeInfo.cs} | 30 +++-- .../Data/SqlClient/SqlCachedBuffer.cs | 2 +- .../Microsoft/Data/SqlClient/SqlConnection.cs | 4 +- .../Data/SqlClient/SqlMetaDataSetTest.cs | 12 +- 7 files changed, 91 insertions(+), 77 deletions(-) rename src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/{SqlMetaDataPriv.cs => TdsTypeInfo.cs} (79%) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs index ea6466a2e8..4068db7eb6 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs @@ -4724,18 +4724,18 @@ internal TdsOperationStatus TryProcessReturnValue(int length, } rec.metaType = MetaType.GetSqlDataType(tdsType, userType, tdsLen); - rec.type = rec.metaType.SqlDbType; + rec.DbType = rec.metaType.SqlDbType; // always use the nullable type for parameters if 2005 or later // older servers sometimes send fixed length return values - rec.tdsType = rec.metaType.NullableType; + rec.TdsType = rec.metaType.NullableType; rec.IsNullable = true; if (tdsLen == TdsEnums.SQL_USHORTVARMAXLEN) { rec.metaType = MetaType.GetMaxMetaTypeFromMetaType(rec.metaType); } - if (rec.type == SqlDbType.Decimal) + if (rec.DbType == SqlDbType.Decimal) { result = stateObj.TryReadByte(out rec.precision); if (result != TdsOperationStatus.Done) @@ -4760,7 +4760,7 @@ internal TdsOperationStatus TryProcessReturnValue(int length, if (tdsType == TdsEnums.SQLUDT) { - result = TryProcessUDTMetaData((SqlMetaDataPriv)rec, stateObj); + result = TryProcessUDTMetaData((TdsTypeInfo)rec, stateObj); if (result != TdsOperationStatus.Done) { return result; @@ -4776,7 +4776,7 @@ internal TdsOperationStatus TryProcessReturnValue(int length, } } - if (rec.type == SqlDbType.Xml) + if (rec.DbType == SqlDbType.Xml) { // Read schema info byte schemapresent; @@ -4903,7 +4903,7 @@ internal TdsOperationStatus TryProcessReturnValue(int length, intlen = (int)valLen; } - if (rec.type == SqlDbTypeExtensions.Vector) + if (rec.DbType == SqlDbTypeExtensions.Vector) { rec.length = tdsLen; } @@ -4928,7 +4928,7 @@ internal TdsOperationStatus TryProcessReturnValue(int length, } internal TdsOperationStatus TryProcessTceCryptoMetadata(TdsParserStateObject stateObj, - SqlMetaDataPriv col, + TdsTypeInfo col, SqlTceCipherInfoTable cipherTable, SqlCommandColumnEncryptionSetting columnEncryptionSetting, bool isReturnValue) @@ -4965,7 +4965,7 @@ internal TdsOperationStatus TryProcessTceCryptoMetadata(TdsParserStateObject sta } // Read the base TypeInfo - col.baseTI = new SqlMetaDataPriv(); + col.baseTI = new TdsTypeInfo(); result = TryProcessTypeInfo(stateObj, col.baseTI, userType); if (result != TdsOperationStatus.Done) { @@ -5480,7 +5480,7 @@ internal TdsOperationStatus TryProcessMetaData(int cColumns, TdsParserStateObjec private bool IsVarTimeTds(byte tdsType) => tdsType == TdsEnums.SQLTIME || tdsType == TdsEnums.SQLDATETIME2 || tdsType == TdsEnums.SQLDATETIMEOFFSET; - private TdsOperationStatus TryProcessTypeInfo(TdsParserStateObject stateObj, SqlMetaDataPriv col, UInt32 userType) + private TdsOperationStatus TryProcessTypeInfo(TdsParserStateObject stateObj, TdsTypeInfo col, UInt32 userType) { byte byteLen; byte tdsType; @@ -5512,8 +5512,8 @@ private TdsOperationStatus TryProcessTypeInfo(TdsParserStateObject stateObj, Sql } col.metaType = MetaType.GetSqlDataType(tdsType, userType, col.length); - col.type = col.metaType.SqlDbType; - col.tdsType = (col.IsNullable ? col.metaType.NullableType : col.metaType.TDSType); + col.DbType = col.metaType.SqlDbType; + col.TdsType = (col.IsNullable ? col.metaType.NullableType : col.metaType.TDSType); if (TdsEnums.SQLUDT == tdsType) { @@ -5596,7 +5596,7 @@ private TdsOperationStatus TryProcessTypeInfo(TdsParserStateObject stateObj, Sql } } - if (col.type == SqlDbType.Decimal) + if (col.DbType == SqlDbType.Decimal) { result = stateObj.TryReadByte(out col.precision); if (result != TdsOperationStatus.Done) @@ -5672,7 +5672,7 @@ private TdsOperationStatus TryProcessTypeInfo(TdsParserStateObject stateObj, Sql } } - if (col.type == SqlDbTypeExtensions.Vector) + if (col.DbType == SqlDbTypeExtensions.Vector) { result = stateObj.TryReadByte(out col.scale); if (result != TdsOperationStatus.Done) @@ -5772,7 +5772,7 @@ private TdsOperationStatus TryCommonProcessMetaData(TdsParserStateObject stateOb return TdsOperationStatus.Done; } - private TdsOperationStatus TryProcessUDTMetaData(SqlMetaDataPriv metaData, TdsParserStateObject stateObj) + private TdsOperationStatus TryProcessUDTMetaData(TdsTypeInfo metaData, TdsParserStateObject stateObj) { ushort shortLength; byte byteLength; @@ -6092,7 +6092,7 @@ private TdsOperationStatus TryProcessColInfo(TdsColumnMetadataToken columns, Sql // for long columns, reads off textptrs, reads length, check nullability // for other columns, reads length, checks nullability // returns length and nullability - internal TdsOperationStatus TryProcessColumnHeader(SqlMetaDataPriv col, TdsParserStateObject stateObj, int columnOrdinal, out bool isNull, out ulong length) + internal TdsOperationStatus TryProcessColumnHeader(TdsTypeInfo col, TdsParserStateObject stateObj, int columnOrdinal, out bool isNull, out ulong length) { // query NBC row information first if (stateObj.IsNullCompressionBitSet(columnOrdinal)) @@ -6106,7 +6106,7 @@ internal TdsOperationStatus TryProcessColumnHeader(SqlMetaDataPriv col, TdsParse return TryProcessColumnHeaderNoNBC(col, stateObj, out isNull, out length); } - private TdsOperationStatus TryProcessColumnHeaderNoNBC(SqlMetaDataPriv col, TdsParserStateObject stateObj, out bool isNull, out ulong length) + private TdsOperationStatus TryProcessColumnHeaderNoNBC(TdsTypeInfo col, TdsParserStateObject stateObj, out bool isNull, out ulong length) { if (col.metaType.IsLong && !col.metaType.IsPlp) { @@ -6285,17 +6285,17 @@ internal static bool ShouldHonorTceForRead( } internal static object GetNullSqlValue(SqlBuffer nullVal, - SqlMetaDataPriv md, + TdsTypeInfo md, SqlCommandColumnEncryptionSetting columnEncryptionSetting, SqlConnectionInternal connection) { - SqlDbType type = md.type; + SqlDbType type = md.DbType; if (type == SqlDbType.VarBinary && // if its a varbinary md.isEncrypted &&// and encrypted ShouldHonorTceForRead(columnEncryptionSetting, connection)) { - type = md.baseTI.type; // the use the actual (plaintext) type + type = md.baseTI.DbType; // the use the actual (plaintext) type } switch (type) @@ -6404,7 +6404,7 @@ internal static object GetNullSqlValue(SqlBuffer nullVal, break; default: - Debug.Fail("unknown null sqlType!" + md.type.ToString()); + Debug.Fail("unknown null sqlType!" + md.DbType.ToString()); break; } @@ -6434,7 +6434,7 @@ internal TdsOperationStatus TrySkipRow(TdsColumnMetadataToken columns, int start /// /// This method skips bytes of a single column value from the media. It supports NBCROW and handles all types of values, including PLP and long /// - internal TdsOperationStatus TrySkipValue(SqlMetaDataPriv md, int columnOrdinal, TdsParserStateObject stateObj) + internal TdsOperationStatus TrySkipValue(TdsTypeInfo md, int columnOrdinal, TdsParserStateObject stateObj) { if (stateObj.IsNullCompressionBitSet(columnOrdinal)) { @@ -6470,7 +6470,7 @@ internal TdsOperationStatus TrySkipValue(SqlMetaDataPriv md, int columnOrdinal, } int length; - result = TryGetTokenLength(md.tdsType, stateObj, out length); + result = TryGetTokenLength(md.TdsType, stateObj, out length); if (result != TdsOperationStatus.Done) { return result; @@ -6485,7 +6485,7 @@ internal TdsOperationStatus TrySkipValue(SqlMetaDataPriv md, int columnOrdinal, else { int length; - result = TryGetTokenLength(md.tdsType, stateObj, out length); + result = TryGetTokenLength(md.TdsType, stateObj, out length); if (result != TdsOperationStatus.Done) { return result; @@ -6610,14 +6610,14 @@ out string resultString /// /// Deserializes the unencrypted bytes into a value based on the target type info. /// - internal bool DeserializeUnencryptedValue(SqlBuffer value, byte[] unencryptedBytes, SqlMetaDataPriv md, TdsParserStateObject stateObj, byte normalizationVersion) + internal bool DeserializeUnencryptedValue(SqlBuffer value, byte[] unencryptedBytes, TdsTypeInfo md, TdsParserStateObject stateObj, byte normalizationVersion) { if (normalizationVersion != 0x01) { throw SQL.UnsupportedNormalizationVersion(normalizationVersion); } - byte tdsType = md.baseTI.tdsType; + byte tdsType = md.baseTI.TdsType; int length = unencryptedBytes.Length; // For normalized types, the length and scale of the actual type might be different than the value's. @@ -6906,7 +6906,7 @@ internal bool DeserializeUnencryptedValue(SqlBuffer value, byte[] unencryptedByt } internal TdsOperationStatus TryReadSqlValue(SqlBuffer value, - SqlMetaDataPriv md, + TdsTypeInfo md, int length, TdsParserStateObject stateObj, SqlCommandColumnEncryptionSetting columnEncryptionOverride, @@ -6914,7 +6914,7 @@ internal TdsOperationStatus TryReadSqlValue(SqlBuffer value, SqlCommand command = null) { bool isPlp = md.metaType.IsPlp; - byte tdsType = md.tdsType; + byte tdsType = md.TdsType; TdsOperationStatus result; Debug.Assert(isPlp || !IsNull(md.metaType, (ulong)length), "null value should not get here!"); @@ -8667,25 +8667,25 @@ internal int GetEncodingCharLength(string value, int numChars, int charOffset, E // Returns the data stream length of the data identified by tds type or SqlMetaData returns // Returns either the total size or the size of the first chunk for partially length prefixed types. // - internal TdsOperationStatus TryGetDataLength(SqlMetaDataPriv colmeta, TdsParserStateObject stateObj, out ulong length) + internal TdsOperationStatus TryGetDataLength(TdsTypeInfo colmeta, TdsParserStateObject stateObj, out ulong length) { // Handle 2005 specific tokens if (colmeta.metaType.IsPlp) { - Debug.Assert(colmeta.tdsType == TdsEnums.SQLXMLTYPE || - colmeta.tdsType == TdsEnums.SQLBIGVARCHAR || - colmeta.tdsType == TdsEnums.SQLBIGVARBINARY || - colmeta.tdsType == TdsEnums.SQLNVARCHAR || - colmeta.tdsType == TdsEnums.SQLJSON || + Debug.Assert(colmeta.TdsType == TdsEnums.SQLXMLTYPE || + colmeta.TdsType == TdsEnums.SQLBIGVARCHAR || + colmeta.TdsType == TdsEnums.SQLBIGVARBINARY || + colmeta.TdsType == TdsEnums.SQLNVARCHAR || + colmeta.TdsType == TdsEnums.SQLJSON || // Large UDTs is WinFS-only - colmeta.tdsType == TdsEnums.SQLUDT, + colmeta.TdsType == TdsEnums.SQLUDT, "GetDataLength:Invalid streaming datatype"); return stateObj.TryReadPlpLength(true, out length); } else { int intLength; - TdsOperationStatus result = TryGetTokenLength(colmeta.tdsType, stateObj, out intLength); + TdsOperationStatus result = TryGetTokenLength(colmeta.TdsType, stateObj, out intLength); if (result != TdsOperationStatus.Done) { length = 0; @@ -11512,20 +11512,20 @@ internal void WriteCekTable(TdsColumnMetadataToken metadataCollection, TdsParser /// Writes the UserType and TYPE_INFO values for CryptoMetadata (for bulk copy). /// /// - internal void WriteTceUserTypeAndTypeInfo(SqlMetaDataPriv mdPriv, TdsParserStateObject stateObj) + internal void WriteTceUserTypeAndTypeInfo(TdsTypeInfo mdPriv, TdsParserStateObject stateObj) { // Write the UserType (4 byte value) WriteInt(0x0, stateObj); // TODO: fix this- timestamp columns have 0x50 value here - Debug.Assert(SqlDbType.Xml != mdPriv.type); - Debug.Assert(SqlDbType.Udt != mdPriv.type); + Debug.Assert(SqlDbType.Xml != mdPriv.DbType); + Debug.Assert(SqlDbType.Udt != mdPriv.DbType); - stateObj.WriteByte(mdPriv.tdsType); + stateObj.WriteByte(mdPriv.TdsType); - switch (mdPriv.type) + switch (mdPriv.DbType) { case SqlDbType.Decimal: - WriteTokenLength(mdPriv.tdsType, mdPriv.length, stateObj); + WriteTokenLength(mdPriv.TdsType, mdPriv.length, stateObj); stateObj.WriteByte(mdPriv.precision); stateObj.WriteByte(mdPriv.scale); break; @@ -11538,7 +11538,7 @@ internal void WriteTceUserTypeAndTypeInfo(SqlMetaDataPriv mdPriv, TdsParserState stateObj.WriteByte(mdPriv.scale); break; default: - WriteTokenLength(mdPriv.tdsType, mdPriv.length, stateObj); + WriteTokenLength(mdPriv.TdsType, mdPriv.length, stateObj); if (mdPriv.metaType.IsCharType) { WriteUnsignedInt(mdPriv.collation._info, stateObj); @@ -11629,11 +11629,11 @@ internal void WriteBulkCopyMetaData(TdsColumnMetadataToken metadataCollection, i // discuss ... // xml datatype does not have token length in its metadata. So it should be a noop. - switch (md.type) + switch (md.DbType) { case SqlDbType.Decimal: - stateObj.WriteByte(md.tdsType); - WriteTokenLength(md.tdsType, md.length, stateObj); + stateObj.WriteByte(md.TdsType); + WriteTokenLength(md.TdsType, md.length, stateObj); stateObj.WriteByte(md.precision); stateObj.WriteByte(md.scale); break; @@ -11646,25 +11646,25 @@ internal void WriteBulkCopyMetaData(TdsColumnMetadataToken metadataCollection, i WriteTokenLength(TdsEnums.SQLBIGVARBINARY, md.length, stateObj); break; case SqlDbType.Date: - stateObj.WriteByte(md.tdsType); + stateObj.WriteByte(md.TdsType); break; case SqlDbType.Time: case SqlDbType.DateTime2: case SqlDbType.DateTimeOffset: - stateObj.WriteByte(md.tdsType); + stateObj.WriteByte(md.TdsType); stateObj.WriteByte(md.scale); break; case SqlDbTypeExtensions.Json: stateObj.WriteByteArray(s_jsonMetadataSubstituteSequence, s_jsonMetadataSubstituteSequence.Length, 0); break; case SqlDbTypeExtensions.Vector: - stateObj.WriteByte(md.tdsType); - WriteTokenLength(md.tdsType, md.length, stateObj); + stateObj.WriteByte(md.TdsType); + WriteTokenLength(md.TdsType, md.length, stateObj); stateObj.WriteByte(md.scale); break; default: - stateObj.WriteByte(md.tdsType); - WriteTokenLength(md.tdsType, md.length, stateObj); + stateObj.WriteByte(md.TdsType); + WriteTokenLength(md.TdsType, md.length, stateObj); if (md.metaType.IsCharType) { WriteUnsignedInt(md.collation._info, stateObj); @@ -11707,7 +11707,7 @@ internal bool ShouldEncryptValuesForBulkCopy() /// Encrypts a column value (for SqlBulkCopy) /// /// - internal object EncryptColumnValue(object value, SqlMetaDataPriv metadata, string column, TdsParserStateObject stateObj, bool isDataFeed, bool isSqlType) + internal object EncryptColumnValue(object value, TdsTypeInfo metadata, string column, TdsParserStateObject stateObj, bool isDataFeed, bool isSqlType) { Debug.Assert(IsColumnEncryptionSupported, "Server doesn't support encryption, yet we received encryption metadata"); Debug.Assert(ShouldEncryptValuesForBulkCopy(), "Encryption attempted when not requested"); @@ -11812,7 +11812,7 @@ internal object EncryptColumnValue(object value, SqlMetaDataPriv metadata, strin null); } - internal Task WriteBulkCopyValue(object value, SqlMetaDataPriv metadata, TdsParserStateObject stateObj, bool isSqlType, bool isDataFeed, bool isNull) + internal Task WriteBulkCopyValue(object value, TdsTypeInfo metadata, TdsParserStateObject stateObj, bool isSqlType, bool isDataFeed, bool isNull) { Debug.Assert(!isSqlType || value is INullable, "isSqlType is true, but value can not be type cast to an INullable"); Debug.Assert(!isDataFeed ^ value is DataFeed, "Incorrect value for isDataFeed"); @@ -11947,7 +11947,7 @@ internal Task WriteBulkCopyValue(object value, SqlMetaDataPriv metadata, TdsPars case SqlDbType.NText: case SqlDbType.Image: stateObj.WriteByteArray(s_longDataHeader, s_longDataHeader.Length, 0); - WriteTokenLength(metadata.tdsType, ccbStringBytes == 0 ? ccb : ccbStringBytes, stateObj); + WriteTokenLength(metadata.TdsType, ccbStringBytes == 0 ? ccb : ccbStringBytes, stateObj); break; case SqlDbType.VarChar: @@ -11963,7 +11963,7 @@ internal Task WriteBulkCopyValue(object value, SqlMetaDataPriv metadata, TdsPars } else { - WriteTokenLength(metadata.tdsType, ccbStringBytes == 0 ? ccb : ccbStringBytes, stateObj); + WriteTokenLength(metadata.TdsType, ccbStringBytes == 0 ? ccb : ccbStringBytes, stateObj); } if (isSqlType) @@ -14140,7 +14140,7 @@ static int IncrementSnapshotDataSize(TdsParserStateObject stateObj, bool resetti } } - internal int ReadPlpAnsiChars(ref char[] buff, int offst, int len, SqlMetaDataPriv metadata, TdsParserStateObject stateObj) + internal int ReadPlpAnsiChars(ref char[] buff, int offst, int len, TdsTypeInfo metadata, TdsParserStateObject stateObj) { int charsRead = 0; int charsLeft = 0; diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadata.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadata.cs index 9b9123a75a..3ef03111bc 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadata.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadata.cs @@ -11,7 +11,7 @@ namespace Microsoft.Data.SqlClient.Parser.Tokens; /// Represents metadata information for a specific column in a TDS stream. Corresponds with a /// single column withing a SQLCOLMETADATA/SQLALTMETADATA token. /// -internal sealed class TdsColumnMetadata : SqlMetaDataPriv +internal sealed class TdsColumnMetadata : TdsTypeInfo { [Flags] private enum MetadataFlags @@ -76,7 +76,7 @@ internal TdsColumnMetadata(int ordinal) : base() internal string CatalogName => tableName.CatalogName; - internal bool Is2008DateTimeType => type is SqlDbType.Date + internal bool Is2008DateTimeType => DbType is SqlDbType.Date or SqlDbType.Time or SqlDbType.DateTime2 or SqlDbType.DateTimeOffset; @@ -140,7 +140,7 @@ public bool IsKey /// /// Indicates whether the column is a large UDT with the maximum allowable length. /// - internal bool IsLargeUdt => type == SqlDbType.Udt && length == int.MaxValue; + internal bool IsLargeUdt => DbType == SqlDbType.Udt && length == int.MaxValue; /// /// Indicates whether the column is read-only in the context of the data source. diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsReturnValueToken.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsReturnValueToken.cs index 994f3fc633..6e658b4013 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsReturnValueToken.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsReturnValueToken.cs @@ -13,7 +13,7 @@ namespace Microsoft.Data.SqlClient.Parser.Tokens; /// associated with parameters in TDS responses. It contains metadata describing the parameter and /// its value. /// -internal sealed class TdsReturnValueToken : SqlMetaDataPriv +internal sealed class TdsReturnValueToken : TdsTypeInfo { /// /// Gets or sets the name of the parameter associated with a returned value. diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/SqlMetaDataPriv.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsTypeInfo.cs similarity index 79% rename from src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/SqlMetaDataPriv.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsTypeInfo.cs index 60a005a8c6..c9c7bd3ed6 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/SqlMetaDataPriv.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsTypeInfo.cs @@ -8,7 +8,11 @@ namespace Microsoft.Data.SqlClient.Parser.Tokens; -internal class SqlMetaDataPriv +/// +/// Represents metadata information for a SQL Server TDS data type, including type properties and +/// encoding settings. Provides functionality for handling encryption and normalization details. +/// +internal class TdsTypeInfo { [Flags] private enum SqlMetaDataPrivFlags : byte @@ -18,8 +22,8 @@ private enum SqlMetaDataPrivFlags : byte IsMultiValued = 1 << 2 } - internal SqlDbType type; // SqlDbType enum value - internal byte tdsType; // underlying tds type + + internal byte precision = TdsEnums.UNKNOWN_PRECISION_SCALE; // give default of unknown (-1) internal byte scale = TdsEnums.UNKNOWN_PRECISION_SCALE; // give default of unknown (-1) private SqlMetaDataPrivFlags flags; @@ -28,17 +32,27 @@ private enum SqlMetaDataPrivFlags : byte internal int codePage; internal Encoding encoding; internal bool isEncrypted; // TCE encrypted? - internal SqlMetaDataPriv baseTI; // for encrypted columns, represents the TYPE_INFO for plaintext value + internal TdsTypeInfo baseTI; // for encrypted columns, represents the TYPE_INFO for plaintext value internal SqlCipherMetadata cipherMD; // Cipher related metadata for encrypted columns. internal MetaType metaType; // cached metaType public SqlMetaDataUdt udt; public SqlMetaDataXmlSchemaCollection xmlSchemaCollection; - internal SqlMetaDataPriv() + internal TdsTypeInfo() { } + /// + /// Represents the database type of the current instance. + /// + internal SqlDbType DbType { get; set; } + + /// + /// Gets or sets the TDS (Tabular Data Stream) type of the instance. + /// + internal byte TdsType { get; set; } + /// /// Is the algorithm handle for the cipher encryption initialized? /// @@ -91,10 +105,10 @@ private void Set(SqlMetaDataPrivFlags flag, bool value) flags = value ? flags | flag : flags & ~flag; } - internal virtual void CopyFrom(SqlMetaDataPriv original) + internal virtual void CopyFrom(TdsTypeInfo original) { - this.type = original.type; - this.tdsType = original.tdsType; + this.DbType = original.DbType; + this.TdsType = original.TdsType; this.precision = original.precision; this.scale = original.scale; this.length = original.length; diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCachedBuffer.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCachedBuffer.cs index 9d5fd35951..90fac26804 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCachedBuffer.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCachedBuffer.cs @@ -37,7 +37,7 @@ private SqlCachedBuffer(List cachedBytes) /// /// Reads off from the network buffer and caches bytes. Only reads one column value in the current row. /// - internal static TdsOperationStatus TryCreate(SqlMetaDataPriv metadata, TdsParser parser, TdsParserStateObject stateObj, out SqlCachedBuffer buffer) + internal static TdsOperationStatus TryCreate(TdsTypeInfo metadata, TdsParser parser, TdsParserStateObject stateObj, out SqlCachedBuffer buffer) { buffer = null; diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnection.cs index 4f62234bce..298d3d45c2 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -3092,7 +3092,7 @@ private Assembly ResolveTypeAssembly(AssemblyName asmRef, bool throwOnError) } } - internal void CheckGetExtendedUDTInfo(SqlMetaDataPriv metaData, bool fThrow) + internal void CheckGetExtendedUDTInfo(TdsTypeInfo metaData, bool fThrow) { if (metaData.udt?.Type == null) { // If null, we have not obtained extended info. @@ -3108,7 +3108,7 @@ internal void CheckGetExtendedUDTInfo(SqlMetaDataPriv metaData, bool fThrow) } } - internal object GetUdtValue(object value, SqlMetaDataPriv metaData, bool returnDBNull) + internal object GetUdtValue(object value, TdsTypeInfo metaData, bool returnDBNull) { if (returnDBNull && ADP.IsNull(value)) { diff --git a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlMetaDataSetTest.cs b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlMetaDataSetTest.cs index d005024bc3..a484c0217a 100644 --- a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlMetaDataSetTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlMetaDataSetTest.cs @@ -197,8 +197,8 @@ public void SqlMetaData_Clone_PreservesBaseTI() // Verify that cloning preserves baseTI, which represents the plaintext // TYPE_INFO for encrypted columns. WriteCryptoMetadata calls // WriteTceUserTypeAndTypeInfo(md.baseTI) to send the unencrypted type info. - SqlMetaDataPriv baseTI = new SqlMetaDataPriv(); - baseTI.type = System.Data.SqlDbType.NVarChar; + TdsTypeInfo baseTI = new TdsTypeInfo(); + baseTI.DbType = System.Data.SqlDbType.NVarChar; baseTI.length = 100; baseTI.precision = 0; baseTI.scale = 0; @@ -211,7 +211,7 @@ public void SqlMetaData_Clone_PreservesBaseTI() TdsColumnMetadataToken clone = original.Clone(); Assert.NotNull(clone[0].baseTI); - Assert.Equal(System.Data.SqlDbType.NVarChar, clone[0].baseTI.type); + Assert.Equal(System.Data.SqlDbType.NVarChar, clone[0].baseTI.DbType); Assert.Equal(100, clone[0].baseTI.length); } @@ -235,8 +235,8 @@ public void SqlMetaDataSet_Clone_PreservesFullAlwaysEncryptedMetadata() normalizationRuleVersion: 1 ); - SqlMetaDataPriv baseTI = new SqlMetaDataPriv(); - baseTI.type = System.Data.SqlDbType.Int; + TdsTypeInfo baseTI = new TdsTypeInfo(); + baseTI.DbType = System.Data.SqlDbType.Int; TdsColumnMetadataToken original = new TdsColumnMetadataToken(2, cekTable); original[0].column = "id"; @@ -254,7 +254,7 @@ public void SqlMetaDataSet_Clone_PreservesFullAlwaysEncryptedMetadata() Assert.True(clone[1].isEncrypted); Assert.NotNull(clone[1].cipherMD); Assert.NotNull(clone[1].baseTI); - Assert.Equal(System.Data.SqlDbType.Int, clone[1].baseTI.type); + Assert.Equal(System.Data.SqlDbType.Int, clone[1].baseTI.DbType); // The cekTable must be preserved on the clone Assert.NotNull(clone.CekTable); From 80da46bd4dad0b4dde7ff67390112c02ee15d568 Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Wed, 2 Sep 2026 18:24:39 -0500 Subject: [PATCH 14/17] Fields => Properties where possible, add lotsa comments. --- .../Data/SqlClient/Parser/TdsParser.cs | 230 ++++++++-------- .../SqlClient/Parser/Tokens/TdsTypeInfo.cs | 165 ++++++----- .../Microsoft/Data/SqlClient/SqlBulkCopy.cs | 68 ++--- .../Data/SqlClient/SqlCachedBuffer.cs | 2 +- .../Microsoft/Data/SqlClient/SqlCommand.cs | 16 +- .../Microsoft/Data/SqlClient/SqlConnection.cs | 16 +- .../Microsoft/Data/SqlClient/SqlDataReader.cs | 256 +++++++++--------- .../Microsoft/Data/SqlClient/SqlDbColumn.cs | 10 +- .../Data/SqlClient/SqlMetaDataSetTest.cs | 44 +-- 9 files changed, 425 insertions(+), 382 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs index 4068db7eb6..4707cb9086 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/TdsParser.cs @@ -4687,7 +4687,7 @@ internal TdsOperationStatus TryProcessReturnValue(int length, // Check if the column is encrypted. if (IsColumnEncryptionSupported) { - rec.isEncrypted = (TdsEnums.IsEncrypted == (flags & TdsEnums.IsEncrypted)); + rec.IsEncrypted = (TdsEnums.IsEncrypted == (flags & TdsEnums.IsEncrypted)); } // read the type @@ -4723,16 +4723,16 @@ internal TdsOperationStatus TryProcessReturnValue(int length, } } - rec.metaType = MetaType.GetSqlDataType(tdsType, userType, tdsLen); - rec.DbType = rec.metaType.SqlDbType; + rec.MetaType = MetaType.GetSqlDataType(tdsType, userType, tdsLen); + rec.DbType = rec.MetaType.SqlDbType; // always use the nullable type for parameters if 2005 or later // older servers sometimes send fixed length return values - rec.TdsType = rec.metaType.NullableType; + rec.TdsType = rec.MetaType.NullableType; rec.IsNullable = true; if (tdsLen == TdsEnums.SQL_USHORTVARMAXLEN) { - rec.metaType = MetaType.GetMaxMetaTypeFromMetaType(rec.metaType); + rec.MetaType = MetaType.GetMaxMetaTypeFromMetaType(rec.MetaType); } if (rec.DbType == SqlDbType.Decimal) @@ -4749,7 +4749,7 @@ internal TdsOperationStatus TryProcessReturnValue(int length, } } - if (rec.metaType.IsVarTime) + if (rec.MetaType.IsVarTime) { result = stateObj.TryReadByte(out rec.scale); if (result != TdsOperationStatus.Done) @@ -4793,13 +4793,13 @@ internal TdsOperationStatus TryProcessReturnValue(int length, { return result; } - if (rec.xmlSchemaCollection is null) + if (rec.XmlSchemaCollection is null) { - rec.xmlSchemaCollection = new SqlMetaDataXmlSchemaCollection(); + rec.XmlSchemaCollection = new SqlMetaDataXmlSchemaCollection(); } if (len != 0) { - result = stateObj.TryReadString(len, out rec.xmlSchemaCollection.Database); + result = stateObj.TryReadString(len, out rec.XmlSchemaCollection.Database); if (result != TdsOperationStatus.Done) { return result; @@ -4813,7 +4813,7 @@ internal TdsOperationStatus TryProcessReturnValue(int length, } if (len != 0) { - result = stateObj.TryReadString(len, out rec.xmlSchemaCollection.OwningSchema); + result = stateObj.TryReadString(len, out rec.XmlSchemaCollection.OwningSchema); if (result != TdsOperationStatus.Done) { return result; @@ -4829,7 +4829,7 @@ internal TdsOperationStatus TryProcessReturnValue(int length, if (slen != 0) { - result = stateObj.TryReadString(slen, out rec.xmlSchemaCollection.Name); + result = stateObj.TryReadString(slen, out rec.XmlSchemaCollection.Name); if (result != TdsOperationStatus.Done) { return result; @@ -4837,7 +4837,7 @@ internal TdsOperationStatus TryProcessReturnValue(int length, } } } - else if (rec.metaType.IsCharType && rec.metaType.SqlDbType != SqlDbTypeExtensions.Json) + else if (rec.MetaType.IsCharType && rec.MetaType.SqlDbType != SqlDbTypeExtensions.Json) { // read the collation for 8.x servers result = TryProcessCollation(stateObj, out rec.collation); @@ -4848,7 +4848,7 @@ internal TdsOperationStatus TryProcessReturnValue(int length, if (rec.collation.IsUTF8) { // UTF8 collation - rec.encoding = s_utf8EncodingWithoutBom; + rec.Encoding = s_utf8EncodingWithoutBom; } else { @@ -4857,19 +4857,19 @@ internal TdsOperationStatus TryProcessReturnValue(int length, // If the column lcid is the same as the default, use the default encoder if (codePage == _defaultCodePage) { - rec.codePage = _defaultCodePage; - rec.encoding = _defaultEncoding; + rec.CodePage = _defaultCodePage; + rec.Encoding = _defaultEncoding; } else { - rec.codePage = codePage; - rec.encoding = System.Text.Encoding.GetEncoding(rec.codePage); + rec.CodePage = codePage; + rec.Encoding = System.Text.Encoding.GetEncoding(rec.CodePage); } } } // For encrypted parameters, read the unencrypted type and encryption information. - if (IsColumnEncryptionSupported && rec.isEncrypted) + if (IsColumnEncryptionSupported && rec.IsEncrypted) { result = TryProcessTceCryptoMetadata(stateObj, rec, cipherTable: null, columnEncryptionSetting: columnEncryptionSetting, isReturnValue: true); if (result != TdsOperationStatus.Done) @@ -4890,7 +4890,7 @@ internal TdsOperationStatus TryProcessReturnValue(int length, // always read as sql types int intlen; - if (rec.metaType.IsPlp) + if (rec.MetaType.IsPlp) { intlen = int.MaxValue; // If plp data, read it all } @@ -4965,8 +4965,8 @@ internal TdsOperationStatus TryProcessTceCryptoMetadata(TdsParserStateObject sta } // Read the base TypeInfo - col.baseTI = new TdsTypeInfo(); - result = TryProcessTypeInfo(stateObj, col.baseTI, userType); + col.BaseTypeInfo = new TdsTypeInfo(); + result = TryProcessTypeInfo(stateObj, col.BaseTypeInfo, userType); if (result != TdsOperationStatus.Done) { return result; @@ -5014,7 +5014,7 @@ internal TdsOperationStatus TryProcessTceCryptoMetadata(TdsParserStateObject sta return result; } - Debug.Assert(col.cipherMD == null, "col.cipherMD should be null in TryProcessTceCryptoMetadata."); + Debug.Assert(col.CipherMetadata == null, "col.cipherMD should be null in TryProcessTceCryptoMetadata."); // Check if TCE is enable and if it is set the crypto MD for the column. // TCE is enabled if the command is set to enabled or to resultset only and this is not a return value @@ -5025,7 +5025,7 @@ internal TdsOperationStatus TryProcessTceCryptoMetadata(TdsParserStateObject sta _connHandler != null && _connHandler.ConnectionOptions != null && _connHandler.ConnectionOptions.ColumnEncryptionSetting == SqlConnectionColumnEncryptionSetting.Enabled)) { - col.cipherMD = new SqlCipherMetadata(cipherTable != null ? (SqlTceCipherInfoEntry)cipherTable[index] : null, + col.CipherMetadata = new SqlCipherMetadata(cipherTable != null ? (SqlTceCipherInfoEntry)cipherTable[index] : null, index, cipherAlgorithmId: cipherAlgorithmId, cipherAlgorithmName: cipherAlgorithmName, @@ -5035,7 +5035,7 @@ internal TdsOperationStatus TryProcessTceCryptoMetadata(TdsParserStateObject sta else { // If TCE is disabled mark the MD as not encrypted. - col.isEncrypted = false; + col.IsEncrypted = false; } return TdsOperationStatus.Done; @@ -5129,7 +5129,7 @@ internal void DrainData(TdsParserStateObject stateObj) // iia. if we still have bytes left from a partially read column, skip if (sharedState._nextColumnDataToRead < sharedState._nextColumnHeaderToRead) { - if ((sharedState._nextColumnHeaderToRead > 0) && (metadata[sharedState._nextColumnHeaderToRead - 1].metaType.IsPlp)) + if ((sharedState._nextColumnHeaderToRead > 0) && (metadata[sharedState._nextColumnHeaderToRead - 1].MetaType.IsPlp)) { if (stateObj._longlen != 0) { @@ -5511,9 +5511,9 @@ private TdsOperationStatus TryProcessTypeInfo(TdsParserStateObject stateObj, Tds } } - col.metaType = MetaType.GetSqlDataType(tdsType, userType, col.length); - col.DbType = col.metaType.SqlDbType; - col.TdsType = (col.IsNullable ? col.metaType.NullableType : col.metaType.TDSType); + col.MetaType = MetaType.GetSqlDataType(tdsType, userType, col.length); + col.DbType = col.MetaType.SqlDbType; + col.TdsType = (col.IsNullable ? col.MetaType.NullableType : col.MetaType.TDSType); if (TdsEnums.SQLUDT == tdsType) { @@ -5532,8 +5532,8 @@ private TdsOperationStatus TryProcessTypeInfo(TdsParserStateObject stateObj, Tds tdsType == TdsEnums.SQLNVARCHAR || tdsType == TdsEnums.SQLUDT, "Invalid streaming datatype"); - col.metaType = MetaType.GetMaxMetaTypeFromMetaType(col.metaType); - Debug.Assert(col.metaType.IsLong, "Max datatype not IsLong"); + col.MetaType = MetaType.GetMaxMetaTypeFromMetaType(col.MetaType); + Debug.Assert(col.MetaType.IsLong, "Max datatype not IsLong"); col.length = int.MaxValue; if (tdsType == TdsEnums.SQLXMLTYPE) { @@ -5551,13 +5551,13 @@ private TdsOperationStatus TryProcessTypeInfo(TdsParserStateObject stateObj, Tds { return result; } - if (col.xmlSchemaCollection is null) + if (col.XmlSchemaCollection is null) { - col.xmlSchemaCollection = new SqlMetaDataXmlSchemaCollection(); + col.XmlSchemaCollection = new SqlMetaDataXmlSchemaCollection(); } if (byteLen != 0) { - result = stateObj.TryReadString(byteLen, out col.xmlSchemaCollection.Database); + result = stateObj.TryReadString(byteLen, out col.XmlSchemaCollection.Database); if (result != TdsOperationStatus.Done) { return result; @@ -5571,7 +5571,7 @@ private TdsOperationStatus TryProcessTypeInfo(TdsParserStateObject stateObj, Tds } if (byteLen != 0) { - result = stateObj.TryReadString(byteLen, out col.xmlSchemaCollection.OwningSchema); + result = stateObj.TryReadString(byteLen, out col.XmlSchemaCollection.OwningSchema); if (result != TdsOperationStatus.Done) { return result; @@ -5586,7 +5586,7 @@ private TdsOperationStatus TryProcessTypeInfo(TdsParserStateObject stateObj, Tds } if (byteLen != 0) { - result = stateObj.TryReadString(shortLen, out col.xmlSchemaCollection.Name); + result = stateObj.TryReadString(shortLen, out col.XmlSchemaCollection.Name); if (result != TdsOperationStatus.Done) { return result; @@ -5610,7 +5610,7 @@ private TdsOperationStatus TryProcessTypeInfo(TdsParserStateObject stateObj, Tds } } - if (col.metaType.IsVarTime) + if (col.MetaType.IsVarTime) { result = stateObj.TryReadByte(out col.scale); if (result != TdsOperationStatus.Done) @@ -5622,7 +5622,7 @@ private TdsOperationStatus TryProcessTypeInfo(TdsParserStateObject stateObj, Tds // calculate actual column length here // TODO: variable-length calculation needs to be encapsulated better - switch (col.metaType.SqlDbType) + switch (col.MetaType.SqlDbType) { case SqlDbType.Time: col.length = MetaType.GetTimeSizeFromScale(col.scale); @@ -5643,7 +5643,7 @@ private TdsOperationStatus TryProcessTypeInfo(TdsParserStateObject stateObj, Tds } // read the collation for 7.x servers - if (col.metaType.IsCharType && (tdsType != TdsEnums.SQLXMLTYPE) && (tdsType != TdsEnums.SQLJSON)) + if (col.MetaType.IsCharType && (tdsType != TdsEnums.SQLXMLTYPE) && (tdsType != TdsEnums.SQLJSON)) { result = TryProcessCollation(stateObj, out col.collation); if (result != TdsOperationStatus.Done) @@ -5653,7 +5653,7 @@ private TdsOperationStatus TryProcessTypeInfo(TdsParserStateObject stateObj, Tds if (col.collation.IsUTF8) { // UTF8 collation - col.encoding = s_utf8EncodingWithoutBom; + col.Encoding = s_utf8EncodingWithoutBom; } else { @@ -5661,13 +5661,13 @@ private TdsOperationStatus TryProcessTypeInfo(TdsParserStateObject stateObj, Tds if (codePage == _defaultCodePage) { - col.codePage = _defaultCodePage; - col.encoding = _defaultEncoding; + col.CodePage = _defaultCodePage; + col.Encoding = _defaultEncoding; } else { - col.codePage = codePage; - col.encoding = System.Text.Encoding.GetEncoding(col.codePage); + col.CodePage = codePage; + col.Encoding = System.Text.Encoding.GetEncoding(col.CodePage); } } } @@ -5719,7 +5719,7 @@ private TdsOperationStatus TryCommonProcessMetaData(TdsParserStateObject stateOb if (fColMD && IsColumnEncryptionSupported) { - col.isEncrypted = (TdsEnums.IsEncrypted == (flags & TdsEnums.IsEncrypted)); + col.IsEncrypted = (TdsEnums.IsEncrypted == (flags & TdsEnums.IsEncrypted)); } // Read TypeInfo @@ -5730,7 +5730,7 @@ private TdsOperationStatus TryCommonProcessMetaData(TdsParserStateObject stateOb } // Read tablename if present - if (col.metaType.IsLong && !col.metaType.IsPlp) + if (col.MetaType.IsLong && !col.MetaType.IsPlp) { int unusedLen = 0xFFFF; //We ignore this value result = TryProcessOneTable(stateObj, ref unusedLen, out col.tableName); @@ -5741,7 +5741,7 @@ private TdsOperationStatus TryCommonProcessMetaData(TdsParserStateObject stateOb } // Read the TCE column cryptoinfo - if (fColMD && IsColumnEncryptionSupported && col.isEncrypted) + if (fColMD && IsColumnEncryptionSupported && col.IsEncrypted) { // If the column is encrypted, we should have a valid cipherTable if (cipherTable != null) @@ -5791,13 +5791,13 @@ private TdsOperationStatus TryProcessUDTMetaData(TdsTypeInfo metaData, TdsParser { return result; } - if (metaData.udt is null) + if (metaData.Udt is null) { - metaData.udt = new SqlMetaDataUdt(); + metaData.Udt = new SqlMetaDataUdt(); } if (byteLength != 0) { - result = stateObj.TryReadString(byteLength, out metaData.udt.DatabaseName); + result = stateObj.TryReadString(byteLength, out metaData.Udt.DatabaseName); if (result != TdsOperationStatus.Done) { return result; @@ -5812,7 +5812,7 @@ private TdsOperationStatus TryProcessUDTMetaData(TdsTypeInfo metaData, TdsParser } if (byteLength != 0) { - result = stateObj.TryReadString(byteLength, out metaData.udt.SchemaName); + result = stateObj.TryReadString(byteLength, out metaData.Udt.SchemaName); if (result != TdsOperationStatus.Done) { return result; @@ -5827,7 +5827,7 @@ private TdsOperationStatus TryProcessUDTMetaData(TdsTypeInfo metaData, TdsParser } if (byteLength != 0) { - result = stateObj.TryReadString(byteLength, out metaData.udt.TypeName); + result = stateObj.TryReadString(byteLength, out metaData.Udt.TypeName); if (result != TdsOperationStatus.Done) { return result; @@ -5841,7 +5841,7 @@ private TdsOperationStatus TryProcessUDTMetaData(TdsTypeInfo metaData, TdsParser } if (shortLength != 0) { - result = stateObj.TryReadString(shortLength, out metaData.udt.AssemblyQualifiedName); + result = stateObj.TryReadString(shortLength, out metaData.Udt.AssemblyQualifiedName); if (result != TdsOperationStatus.Done) { return result; @@ -6108,7 +6108,7 @@ internal TdsOperationStatus TryProcessColumnHeader(TdsTypeInfo col, TdsParserSta private TdsOperationStatus TryProcessColumnHeaderNoNBC(TdsTypeInfo col, TdsParserStateObject stateObj, out bool isNull, out ulong length) { - if (col.metaType.IsLong && !col.metaType.IsPlp) + if (col.MetaType.IsLong && !col.MetaType.IsPlp) { if (stateObj.IsSnapshotContinuing()) { @@ -6170,7 +6170,7 @@ private TdsOperationStatus TryProcessColumnHeaderNoNBC(TdsTypeInfo col, TdsParse return result; } - isNull = IsNull(col.metaType, longlen); + isNull = IsNull(col.MetaType, longlen); length = (isNull ? 0 : longlen); return TdsOperationStatus.Done; } @@ -6239,7 +6239,7 @@ private TdsOperationStatus TryProcessRow(TdsColumnMetadataToken columns, object[ // We only read up to 2Gb. Throw if data is larger. Very large data // should be read in chunks in sequential read mode // For Plp columns, we may have gotten only the length of the first chunk - result = TryReadSqlValue(data, md, md.metaType.IsPlp ? (int.MaxValue) : (int)len, stateObj, + result = TryReadSqlValue(data, md, md.MetaType.IsPlp ? (int.MaxValue) : (int)len, stateObj, SqlCommandColumnEncryptionSetting.Disabled /*Column Encryption Disabled for Bulk Copy*/, md.column); if (result != TdsOperationStatus.Done) @@ -6292,10 +6292,10 @@ internal static object GetNullSqlValue(SqlBuffer nullVal, SqlDbType type = md.DbType; if (type == SqlDbType.VarBinary && // if its a varbinary - md.isEncrypted &&// and encrypted + md.IsEncrypted &&// and encrypted ShouldHonorTceForRead(columnEncryptionSetting, connection)) { - type = md.baseTI.DbType; // the use the actual (plaintext) type + type = md.BaseTypeInfo.DbType; // the use the actual (plaintext) type } switch (type) @@ -6442,7 +6442,7 @@ internal TdsOperationStatus TrySkipValue(TdsTypeInfo md, int columnOrdinal, TdsP } TdsOperationStatus result; - if (md.metaType.IsPlp) + if (md.MetaType.IsPlp) { result = TrySkipPlpValue(ulong.MaxValue, stateObj, out _); if (result != TdsOperationStatus.Done) @@ -6450,9 +6450,9 @@ internal TdsOperationStatus TrySkipValue(TdsTypeInfo md, int columnOrdinal, TdsP return result; } } - else if (md.metaType.IsLong) + else if (md.MetaType.IsLong) { - Debug.Assert(!md.metaType.IsPlp, "Plp types must be handled using SkipPlpValue"); + Debug.Assert(!md.MetaType.IsPlp, "Plp types must be handled using SkipPlpValue"); byte textPtrLen; result = stateObj.TryReadByte(out textPtrLen); @@ -6492,7 +6492,7 @@ internal TdsOperationStatus TrySkipValue(TdsTypeInfo md, int columnOrdinal, TdsP } // if false, no value to skip - it's null - if (!IsNull(md.metaType, (ulong)length)) + if (!IsNull(md.MetaType, (ulong)length)) { result = stateObj.TrySkipBytes(length); if (result != TdsOperationStatus.Done) @@ -6617,14 +6617,14 @@ internal bool DeserializeUnencryptedValue(SqlBuffer value, byte[] unencryptedByt throw SQL.UnsupportedNormalizationVersion(normalizationVersion); } - byte tdsType = md.baseTI.TdsType; + byte tdsType = md.BaseTypeInfo.TdsType; int length = unencryptedBytes.Length; // For normalized types, the length and scale of the actual type might be different than the value's. - int denormalizedLength = md.baseTI.length; - byte denormalizedScale = md.baseTI.scale; + int denormalizedLength = md.BaseTypeInfo.length; + byte denormalizedScale = md.BaseTypeInfo.scale; - Debug.Assert(false == md.baseTI.isEncrypted, "Double encryption detected"); + Debug.Assert(false == md.BaseTypeInfo.IsEncrypted, "Double encryption detected"); //DEVNOTE: When modifying the following routines (for deserialization) please pay attention to // deserialization code in DecryptWithKey () method and modify it accordingly. switch (tdsType) @@ -6787,7 +6787,7 @@ internal bool DeserializeUnencryptedValue(SqlBuffer value, byte[] unencryptedByt // If this is a fixed length type, pad with zeros to get to the fixed length size. if (tdsType == TdsEnums.SQLBINARY || tdsType == TdsEnums.SQLBIGBINARY) { - byte[] bytes = new byte[md.baseTI.length]; + byte[] bytes = new byte[md.BaseTypeInfo.length]; Buffer.BlockCopy(unencryptedBytes, 0, bytes, 0, unencryptedBytes.Length); unencryptedBytes = bytes; } @@ -6818,7 +6818,7 @@ internal bool DeserializeUnencryptedValue(SqlBuffer value, byte[] unencryptedByt bits[i] = BinaryPrimitives.ReadInt32LittleEndian(span.Slice(index)); index += 4; } - value.SetToDecimal(md.baseTI.precision, md.baseTI.scale, fPositive, bits); + value.SetToDecimal(md.BaseTypeInfo.precision, md.BaseTypeInfo.scale, fPositive, bits); break; case TdsEnums.SQLCHAR: @@ -6827,7 +6827,7 @@ internal bool DeserializeUnencryptedValue(SqlBuffer value, byte[] unencryptedByt case TdsEnums.SQLBIGVARCHAR: case TdsEnums.SQLTEXT: { - System.Text.Encoding encoding = md.baseTI.encoding; + System.Text.Encoding encoding = md.BaseTypeInfo.Encoding; if (encoding == null) { @@ -6844,7 +6844,7 @@ internal bool DeserializeUnencryptedValue(SqlBuffer value, byte[] unencryptedByt // If this is a fixed length type, pad with spaces to get to the fixed length size. if (tdsType == TdsEnums.SQLCHAR || tdsType == TdsEnums.SQLBIGCHAR) { - strValue = strValue.PadRight(md.baseTI.length); + strValue = strValue.PadRight(md.BaseTypeInfo.length); } value.SetToString(strValue); @@ -6860,7 +6860,7 @@ internal bool DeserializeUnencryptedValue(SqlBuffer value, byte[] unencryptedByt // If this is a fixed length type, pad with spaces to get to the fixed length size. if (tdsType == TdsEnums.SQLNCHAR) { - strValue = strValue.PadRight(md.baseTI.length / ADP.CharSize); + strValue = strValue.PadRight(md.BaseTypeInfo.length / ADP.CharSize); } value.SetToString(strValue); @@ -6891,7 +6891,7 @@ internal bool DeserializeUnencryptedValue(SqlBuffer value, byte[] unencryptedByt break; default: - MetaType metaType = md.baseTI.metaType; + MetaType metaType = md.BaseTypeInfo.MetaType; // If we don't have a metatype already, construct one to get the proper type name. if (metaType == null) @@ -6913,11 +6913,11 @@ internal TdsOperationStatus TryReadSqlValue(SqlBuffer value, string columnName, SqlCommand command = null) { - bool isPlp = md.metaType.IsPlp; + bool isPlp = md.MetaType.IsPlp; byte tdsType = md.TdsType; TdsOperationStatus result; - Debug.Assert(isPlp || !IsNull(md.metaType, (ulong)length), "null value should not get here!"); + Debug.Assert(isPlp || !IsNull(md.MetaType, (ulong)length), "null value should not get here!"); if (isPlp) { // We must read the column value completely, no matter what length is passed in @@ -6951,7 +6951,7 @@ internal TdsOperationStatus TryReadSqlValue(SqlBuffer value, return result; } - if (md.isEncrypted + if (md.IsEncrypted && (columnEncryptionOverride == SqlCommandColumnEncryptionSetting.Enabled || columnEncryptionOverride == SqlCommandColumnEncryptionSetting.ResultSetOnly || (columnEncryptionOverride == SqlCommandColumnEncryptionSetting.UseConnectionSetting @@ -6961,7 +6961,7 @@ internal TdsOperationStatus TryReadSqlValue(SqlBuffer value, try { // CipherInfo is present, decrypt and read - byte[] unencryptedBytes = SqlSecurityUtility.DecryptWithKey(b, md.cipherMD, _connHandler.Connection, command); + byte[] unencryptedBytes = SqlSecurityUtility.DecryptWithKey(b, md.CipherMetadata, _connHandler.Connection, command); if (unencryptedBytes != null) { @@ -7050,7 +7050,7 @@ internal TdsOperationStatus TryReadSqlValue(SqlBuffer value, case TdsEnums.SQLNVARCHAR: case TdsEnums.SQLNTEXT: case TdsEnums.SQLJSON: - result = TryReadSqlStringValue(value, tdsType, length, md.encoding, isPlp, stateObj); + result = TryReadSqlStringValue(value, tdsType, length, md.Encoding, isPlp, stateObj); if (result != TdsOperationStatus.Done) { return result; @@ -8670,7 +8670,7 @@ internal int GetEncodingCharLength(string value, int numChars, int charOffset, E internal TdsOperationStatus TryGetDataLength(TdsTypeInfo colmeta, TdsParserStateObject stateObj, out ulong length) { // Handle 2005 specific tokens - if (colmeta.metaType.IsPlp) + if (colmeta.MetaType.IsPlp) { Debug.Assert(colmeta.TdsType == TdsEnums.SQLXMLTYPE || colmeta.TdsType == TdsEnums.SQLBIGVARCHAR || @@ -11444,9 +11444,9 @@ internal void LoadColumnEncryptionKeys(TdsColumnMetadataToken metadataCollection if (metadataCollection[col] != null) { TdsColumnMetadata md = metadataCollection[col]; - if (md.isEncrypted) + if (md.IsEncrypted) { - SqlSecurityUtility.DecryptSymmetricKey(md.cipherMD, connection, command); + SqlSecurityUtility.DecryptSymmetricKey(md.CipherMetadata, connection, command); } } } @@ -11539,7 +11539,7 @@ internal void WriteTceUserTypeAndTypeInfo(TdsTypeInfo mdPriv, TdsParserStateObje break; default: WriteTokenLength(mdPriv.TdsType, mdPriv.length, stateObj); - if (mdPriv.metaType.IsCharType) + if (mdPriv.MetaType.IsCharType) { WriteUnsignedInt(mdPriv.collation._info, stateObj); stateObj.WriteByte(mdPriv.collation._sortId); @@ -11555,34 +11555,34 @@ internal void WriteTceUserTypeAndTypeInfo(TdsTypeInfo mdPriv, TdsParserStateObje internal void WriteCryptoMetadata(TdsColumnMetadata md, TdsParserStateObject stateObj) { if (!IsColumnEncryptionSupported || // TCE Feature supported - !md.isEncrypted || // Column is not encrypted + !md.IsEncrypted || // Column is not encrypted !ShouldEncryptValuesForBulkCopy()) { // TCE disabled on connection string return; } // Write the ordinal - WriteShort(md.cipherMD.CekTableOrdinal, stateObj); + WriteShort(md.CipherMetadata.CekTableOrdinal, stateObj); // Write UserType and TYPEINFO - WriteTceUserTypeAndTypeInfo(md.baseTI, stateObj); + WriteTceUserTypeAndTypeInfo(md.BaseTypeInfo, stateObj); // Write Encryption Algo - stateObj.WriteByte(md.cipherMD.CipherAlgorithmId); + stateObj.WriteByte(md.CipherMetadata.CipherAlgorithmId); - if (TdsEnums.CustomCipherAlgorithmId == md.cipherMD.CipherAlgorithmId) + if (TdsEnums.CustomCipherAlgorithmId == md.CipherMetadata.CipherAlgorithmId) { // Write the algorithm name - Debug.Assert(md.cipherMD.CipherAlgorithmName.Length < 256); - stateObj.WriteByte((byte)md.cipherMD.CipherAlgorithmName.Length); - WriteString(md.cipherMD.CipherAlgorithmName, stateObj); + Debug.Assert(md.CipherMetadata.CipherAlgorithmName.Length < 256); + stateObj.WriteByte((byte)md.CipherMetadata.CipherAlgorithmName.Length); + WriteString(md.CipherMetadata.CipherAlgorithmName, stateObj); } // Write Encryption Algo Type - stateObj.WriteByte(md.cipherMD.EncryptionType); + stateObj.WriteByte(md.CipherMetadata.EncryptionType); // Write Normalization Version - stateObj.WriteByte(md.cipherMD.NormalizationRuleVersion); + stateObj.WriteByte(md.CipherMetadata.NormalizationRuleVersion); } internal void WriteBulkCopyMetaData(TdsColumnMetadataToken metadataCollection, int count, TdsParserStateObject stateObj) @@ -11618,7 +11618,7 @@ internal void WriteBulkCopyMetaData(TdsColumnMetadataToken metadataCollection, i { // TCE Supported if (ShouldEncryptValuesForBulkCopy()) { // TCE enabled on connection options - flags |= (UInt16)(md.isEncrypted ? (UInt16)(TdsEnums.IsEncrypted << 8) : (UInt16)0); + flags |= (UInt16)(md.IsEncrypted ? (UInt16)(TdsEnums.IsEncrypted << 8) : (UInt16)0); } } @@ -11665,7 +11665,7 @@ internal void WriteBulkCopyMetaData(TdsColumnMetadataToken metadataCollection, i default: stateObj.WriteByte(md.TdsType); WriteTokenLength(md.TdsType, md.length, stateObj); - if (md.metaType.IsCharType) + if (md.MetaType.IsCharType) { WriteUnsignedInt(md.collation._info, stateObj); stateObj.WriteByte(md.collation._sortId); @@ -11673,7 +11673,7 @@ internal void WriteBulkCopyMetaData(TdsColumnMetadataToken metadataCollection, i break; } - if (md.metaType.IsLong && !md.metaType.IsPlp) + if (md.MetaType.IsLong && !md.MetaType.IsPlp) { WriteShort(md.TableName.Length, stateObj); WriteString(md.TableName, stateObj); @@ -11718,7 +11718,7 @@ internal object EncryptColumnValue(object value, TdsTypeInfo metadata, string co } int actualLengthInBytes; - switch (metadata.baseTI.metaType.NullableType) + switch (metadata.BaseTypeInfo.MetaType.NullableType) { case TdsEnums.SQLBIGBINARY: case TdsEnums.SQLBIGVARBINARY: @@ -11733,11 +11733,11 @@ internal object EncryptColumnValue(object value, TdsTypeInfo metadata, string co // to report the size of data to be copied out (for serialization). If we underreport the // size, truncation will happen for us! actualLengthInBytes = (isSqlType) ? ((SqlBinary)value).Length : ((byte[])value).Length; - if (metadata.baseTI.length > 0 && - actualLengthInBytes > metadata.baseTI.length) + if (metadata.BaseTypeInfo.length > 0 && + actualLengthInBytes > metadata.BaseTypeInfo.length) { // see comments above - actualLengthInBytes = metadata.baseTI.length; + actualLengthInBytes = metadata.BaseTypeInfo.length; } break; @@ -11756,10 +11756,10 @@ internal object EncryptColumnValue(object value, TdsTypeInfo metadata, string co actualLengthInBytes = _defaultEncoding.GetByteCount(stringValue); // If the string length is > max length, then use the max length (see comments above) - if (metadata.baseTI.length > 0 && - actualLengthInBytes > metadata.baseTI.length) + if (metadata.BaseTypeInfo.length > 0 && + actualLengthInBytes > metadata.BaseTypeInfo.length) { - actualLengthInBytes = metadata.baseTI.length; // this ensure truncation! + actualLengthInBytes = metadata.BaseTypeInfo.length; // this ensure truncation! } break; @@ -11768,16 +11768,16 @@ internal object EncryptColumnValue(object value, TdsTypeInfo metadata, string co case TdsEnums.SQLNTEXT: actualLengthInBytes = ((isSqlType) ? ((SqlString)value).Value.Length : ((string)value).Length) * 2; - if (metadata.baseTI.length > 0 && - actualLengthInBytes > metadata.baseTI.length) + if (metadata.BaseTypeInfo.length > 0 && + actualLengthInBytes > metadata.BaseTypeInfo.length) { // see comments above - actualLengthInBytes = metadata.baseTI.length; + actualLengthInBytes = metadata.BaseTypeInfo.length; } break; default: - actualLengthInBytes = metadata.baseTI.length; + actualLengthInBytes = metadata.BaseTypeInfo.length; break; } @@ -11786,28 +11786,28 @@ internal object EncryptColumnValue(object value, TdsTypeInfo metadata, string co { // SqlType serializedValue = SerializeUnencryptedSqlValue(value, - metadata.baseTI.metaType, + metadata.BaseTypeInfo.MetaType, actualLengthInBytes, offset: 0, - normalizationVersion: metadata.cipherMD.NormalizationRuleVersion, + normalizationVersion: metadata.CipherMetadata.NormalizationRuleVersion, stateObj: stateObj); } else { serializedValue = SerializeUnencryptedValue(value, - metadata.baseTI.metaType, - metadata.baseTI.scale, + metadata.BaseTypeInfo.MetaType, + metadata.BaseTypeInfo.scale, actualLengthInBytes, offset: 0, isDataFeed: isDataFeed, - normalizationVersion: metadata.cipherMD.NormalizationRuleVersion, + normalizationVersion: metadata.CipherMetadata.NormalizationRuleVersion, stateObj: stateObj); } Debug.Assert(serializedValue != null, "serializedValue should not be null in TdsExecuteRPC."); return SqlSecurityUtility.EncryptWithKey( serializedValue, - metadata.cipherMD, + metadata.CipherMetadata, _connHandler.Connection, null); } @@ -11830,9 +11830,9 @@ internal Task WriteBulkCopyValue(object value, TdsTypeInfo metadata, TdsParserSt } try { - if (metadata.encoding != null) + if (metadata.Encoding != null) { - _defaultEncoding = metadata.encoding; + _defaultEncoding = metadata.Encoding; } if (metadata.collation != null) { @@ -11845,9 +11845,9 @@ internal Task WriteBulkCopyValue(object value, TdsTypeInfo metadata, TdsParserSt _defaultCollation = metadata.collation; _defaultLCID = _defaultCollation.LCID; } - _defaultCodePage = metadata.codePage; + _defaultCodePage = metadata.CodePage; - MetaType metatype = metadata.metaType; + MetaType metatype = metadata.MetaType; int ccb = 0; int ccbStringBytes = 0; @@ -14171,7 +14171,7 @@ internal int ReadPlpAnsiChars(ref char[] buff, int offst, int len, TdsTypeInfo m if (stateObj._plpdecoder == null) { - Encoding enc = metadata.encoding; + Encoding enc = metadata.Encoding; if (enc == null) { diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsTypeInfo.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsTypeInfo.cs index c9c7bd3ed6..1447d74298 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsTypeInfo.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsTypeInfo.cs @@ -15,96 +15,129 @@ namespace Microsoft.Data.SqlClient.Parser.Tokens; internal class TdsTypeInfo { [Flags] - private enum SqlMetaDataPrivFlags : byte + private enum TdsTypeInfoFlags : byte { None = 0, IsNullable = 1 << 1, IsMultiValued = 1 << 2 } + /// + /// Represents the collation settings associated with character-based SQL Server types. + /// + // @TODO: This cannot be an auto property yet because this value is set via an out parameter in TryRead*. + internal SqlCollation collation; - internal byte precision = TdsEnums.UNKNOWN_PRECISION_SCALE; // give default of unknown (-1) - internal byte scale = TdsEnums.UNKNOWN_PRECISION_SCALE; // give default of unknown (-1) - private SqlMetaDataPrivFlags flags; + /// + /// Represents the length property associated with SQL Server type information. Used for + /// defining the size or limit of a data type within the TDS protocol. + /// + // @TODO: This cannot be an auto property yet because this value is set via an out parameter in TryRead*. internal int length; - internal SqlCollation collation; - internal int codePage; - internal Encoding encoding; - internal bool isEncrypted; // TCE encrypted? - internal TdsTypeInfo baseTI; // for encrypted columns, represents the TYPE_INFO for plaintext value - internal SqlCipherMetadata cipherMD; // Cipher related metadata for encrypted columns. - internal MetaType metaType; // cached metaType - public SqlMetaDataUdt udt; - public SqlMetaDataXmlSchemaCollection xmlSchemaCollection; + + /// + /// Specifies the numeric precision for a numeric SQL Server data type. Defaults to + /// UNKNOWN_PRECISION_SCALE when not explicitly set. + /// + // @TODO: This cannot be an auto property yet because this value is set via an out parameter in TryRead*. + internal byte precision = TdsEnums.UNKNOWN_PRECISION_SCALE; + + + /// + /// Specifies the scale component of a numeric SQL Server type. Defaults to + /// UNKNOWN_PRECISION_SCALE when not explicitly set. + /// + // @TODO: This cannot be an auto property yet because this value is set via an out parameter in TryRead*. + internal byte scale = TdsEnums.UNKNOWN_PRECISION_SCALE; // give default of unknown (-1) + + private TdsTypeInfoFlags flags; internal TdsTypeInfo() { } + /// + /// Gets or sets the plaintext type information for the column if the column is encrypted. + /// + internal TdsTypeInfo BaseTypeInfo { get; set; } + + /// + /// Gets or sets the encryption-related metadata associated with the SQL Server TDS data type. + /// + internal SqlCipherMetadata CipherMetadata { get; set; } + + /// + /// Gets or sets the code page used for character encoding associated with the TDS data type + /// instance. + /// + internal int CodePage { get; set; } + /// /// Represents the database type of the current instance. /// internal SqlDbType DbType { get; set; } /// - /// Gets or sets the TDS (Tabular Data Stream) type of the instance. + /// Gets or sets the character encoding used for string data representation. /// - internal byte TdsType { get; set; } + internal Encoding Encoding { get; set; } /// /// Is the algorithm handle for the cipher encryption initialized? /// - internal bool IsAlgorithmInitialized() - { - if (cipherMD != null) - { - return cipherMD.IsAlgorithmInitialized(); - } + internal bool IsAlgorithmInitialized() => CipherMetadata?.IsAlgorithmInitialized() ?? false; - return false; - } + /// + /// Gets or sets whether the type described by the current instance is TCE encrypted. + /// + internal bool IsEncrypted { get; set; } - public bool IsNullable + /// + /// Indicates whether the associated SQL Server TDS data type supports multiple values. + /// + public bool IsMultiValued { - get => HasFlag(SqlMetaDataPrivFlags.IsNullable); - set => Set(SqlMetaDataPrivFlags.IsNullable, value); + get => HasFlag(TdsTypeInfoFlags.IsMultiValued); + set => Set(TdsTypeInfoFlags.IsMultiValued, value); } - public bool IsMultiValued + /// + /// Indicates whether the associated database column allows null values. + /// + public bool IsNullable { - get => HasFlag(SqlMetaDataPrivFlags.IsMultiValued); - set => Set(SqlMetaDataPrivFlags.IsMultiValued, value); + get => HasFlag(TdsTypeInfoFlags.IsNullable); + set => Set(TdsTypeInfoFlags.IsNullable, value); } + /// + /// Gets or sets the cached MetaType information associated with the current instance. + /// + internal MetaType MetaType { get; set; } + /// /// Returns the normalization rule version byte. /// - /// - internal byte NormalizationRuleVersion - { - get - { - if (cipherMD != null) - { - return cipherMD.NormalizationRuleVersion; - } + internal byte NormalizationRuleVersion => CipherMetadata?.NormalizationRuleVersion ?? 0x00; - return 0x00; - } - } + /// + /// Gets or sets the TDS (Tabular Data Stream) type of the instance. + /// + internal byte TdsType { get; set; } - private bool HasFlag(SqlMetaDataPrivFlags flag) - { - return (flags & flag) != 0; - } + /// + /// Gets or sets the UDT metadata associated with this object. + /// + public SqlMetaDataUdt Udt { get; set; } - private void Set(SqlMetaDataPrivFlags flag, bool value) - { - flags = value ? flags | flag : flags & ~flag; - } + /// + /// Gets or sets the metadata for the XML schema collection associated with this data type. + /// + public SqlMetaDataXmlSchemaCollection XmlSchemaCollection { get; set; } + // @TODO: Can this be converted to Clone like all the other token types do? internal virtual void CopyFrom(TdsTypeInfo original) { this.DbType = original.DbType; @@ -113,25 +146,35 @@ internal virtual void CopyFrom(TdsTypeInfo original) this.scale = original.scale; this.length = original.length; this.collation = original.collation; - this.codePage = original.codePage; - this.encoding = original.encoding; - this.metaType = original.metaType; + this.CodePage = original.CodePage; + this.Encoding = original.Encoding; + this.MetaType = original.MetaType; this.flags = original.flags; - if (original.udt != null) + if (original.Udt != null) { - udt = new SqlMetaDataUdt(); - udt.CopyFrom(original.udt); + Udt = new SqlMetaDataUdt(); + Udt.CopyFrom(original.Udt); } - if (original.xmlSchemaCollection != null) + if (original.XmlSchemaCollection != null) { - xmlSchemaCollection = new SqlMetaDataXmlSchemaCollection(); - xmlSchemaCollection.CopyFrom(original.xmlSchemaCollection); + XmlSchemaCollection = new SqlMetaDataXmlSchemaCollection(); + XmlSchemaCollection.CopyFrom(original.XmlSchemaCollection); } - this.isEncrypted = original.isEncrypted; - this.baseTI = original.baseTI; - this.cipherMD = original.cipherMD; + this.IsEncrypted = original.IsEncrypted; + this.BaseTypeInfo = original.BaseTypeInfo; + this.CipherMetadata = original.CipherMetadata; + } + + private bool HasFlag(TdsTypeInfoFlags flag) + { + return (flags & flag) != 0; + } + + private void Set(TdsTypeInfoFlags flag, bool value) + { + flags = value ? flags | flag : flags & ~flag; } } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs index 163fe80a57..66a19acbe4 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBulkCopy.cs @@ -827,7 +827,7 @@ private string AnalyzeTargetAndCreateUpdateBulkCommand(BulkCopySimpleResultSet i // // We will not process timestamp or identity columns. // - if (metadata.type == SqlDbType.Timestamp + if (metadata.DbType == SqlDbType.Timestamp || (metadata.IsIdentity && !IsCopyOption(SqlBulkCopyOptions.KeepIdentity))) { rejected = true; @@ -848,28 +848,28 @@ private string AnalyzeTargetAndCreateUpdateBulkCommand(BulkCopySimpleResultSet i } // Some datatypes need special handling ... - if (metadata.type == SqlDbType.Variant) + if (metadata.DbType == SqlDbType.Variant) { AppendColumnNameAndTypeName(updateBulkCommandText, metadata.column, "sql_variant"); } - else if (metadata.type == SqlDbType.Udt) + else if (metadata.DbType == SqlDbType.Udt) { AppendColumnNameAndTypeName(updateBulkCommandText, metadata.column, "varbinary"); } - else if (metadata.type == SqlDbTypeExtensions.Json) + else if (metadata.DbType == SqlDbTypeExtensions.Json) { AppendColumnNameAndTypeName(updateBulkCommandText, metadata.column, "json"); } - else if (metadata.type == SqlDbTypeExtensions.Vector) + else if (metadata.DbType == SqlDbTypeExtensions.Vector) { AppendColumnNameAndTypeName(updateBulkCommandText, metadata.column, "vector"); } else { - AppendColumnNameAndTypeName(updateBulkCommandText, metadata.column, metadata.type.ToString()); + AppendColumnNameAndTypeName(updateBulkCommandText, metadata.column, metadata.DbType.ToString()); } - switch (metadata.metaType.NullableType) + switch (metadata.MetaType.NullableType) { case TdsEnums.SQLNUMERICN: case TdsEnums.SQLDECIMALN: @@ -898,10 +898,10 @@ private string AnalyzeTargetAndCreateUpdateBulkCommand(BulkCopySimpleResultSet i default: { // For non-long non-fixed types we need to add the Size - if (!metadata.metaType.IsFixed && !metadata.metaType.IsLong) + if (!metadata.MetaType.IsFixed && !metadata.MetaType.IsLong) { int size = metadata.length; - switch (metadata.metaType.NullableType) + switch (metadata.MetaType.NullableType) { case TdsEnums.SQLNCHAR: case TdsEnums.SQLNVARCHAR: @@ -916,7 +916,7 @@ private string AnalyzeTargetAndCreateUpdateBulkCommand(BulkCopySimpleResultSet i } updateBulkCommandText.AppendFormat((IFormatProvider)null, "({0})", size); } - else if (metadata.metaType.IsPlp && !(metadata.metaType.SqlDbType is SqlDbType.Xml or SqlDbTypeExtensions.Json or SqlDbTypeExtensions.Vector)) + else if (metadata.MetaType.IsPlp && !(metadata.MetaType.SqlDbType is SqlDbType.Xml or SqlDbTypeExtensions.Json or SqlDbTypeExtensions.Vector)) { // Partial length column prefix (max) updateBulkCommandText.Append("(max)"); @@ -930,7 +930,7 @@ private string AnalyzeTargetAndCreateUpdateBulkCommand(BulkCopySimpleResultSet i object rowvalue = rowset[i][CollationId]; bool shouldSendCollation; - switch (metadata.type) + switch (metadata.DbType) { case SqlDbType.Char: case SqlDbType.NChar: @@ -1240,7 +1240,7 @@ private object GetValueFromSourceRow(int destRowIndex, out bool isSqlType, out b object value = _sqlDataReaderRowSource.GetValue(sourceOrdinal); isNull = ((value == null) || (value == DBNull.Value)); - if ((!isNull) && (metadata.type == SqlDbType.Udt)) + if ((!isNull) && (metadata.DbType == SqlDbType.Udt)) { var columnAsINullable = value as INullable; isNull = (columnAsINullable != null) && columnAsINullable.IsNull; @@ -1444,7 +1444,7 @@ private SourceColumnMetadata GetColumnMetadata(int ordinal) bool isSqlType; bool isDataFeed; - if (((_sqlDataReaderRowSource != null) || (_dataTableSource != null)) && ((metadata.metaType.NullableType == TdsEnums.SQLDECIMALN) || (metadata.metaType.NullableType == TdsEnums.SQLNUMERICN))) + if (((_sqlDataReaderRowSource != null) || (_dataTableSource != null)) && ((metadata.MetaType.NullableType == TdsEnums.SQLDECIMALN) || (metadata.MetaType.NullableType == TdsEnums.SQLNUMERICN))) { isDataFeed = false; @@ -1487,28 +1487,28 @@ private SourceColumnMetadata GetColumnMetadata(int ordinal) } } // Check for data streams - else if (_enableStreaming && (metadata.length == MAX_LENGTH || metadata.metaType.SqlDbType == SqlDbTypeExtensions.Json)) + else if (_enableStreaming && (metadata.length == MAX_LENGTH || metadata.MetaType.SqlDbType == SqlDbTypeExtensions.Json)) { isSqlType = false; if (_sqlDataReaderRowSource != null) { // MetaData property is not set for SMI, but since streaming is disabled we do not need it - MetaType mtSource = _sqlDataReaderRowSource.MetaData[sourceOrdinal].metaType; + MetaType mtSource = _sqlDataReaderRowSource.MetaData[sourceOrdinal].MetaType; // There is no memory gain for non-sequential access for binary - if ((metadata.type == SqlDbType.VarBinary) && (mtSource.IsBinType) && (mtSource.SqlDbType != SqlDbType.Timestamp) && _sqlDataReaderRowSource.IsCommandBehavior(CommandBehavior.SequentialAccess)) + if ((metadata.DbType == SqlDbType.VarBinary) && (mtSource.IsBinType) && (mtSource.SqlDbType != SqlDbType.Timestamp) && _sqlDataReaderRowSource.IsCommandBehavior(CommandBehavior.SequentialAccess)) { isDataFeed = true; method = ValueMethod.DataFeedStream; } // For text and XML there is memory gain from streaming on destination side even if reader is non-sequential - else if ((metadata.type is SqlDbType.VarChar or SqlDbType.NVarChar or SqlDbTypeExtensions.Json) && mtSource.IsCharType && mtSource.SqlDbType != SqlDbType.Xml) + else if ((metadata.DbType is SqlDbType.VarChar or SqlDbType.NVarChar or SqlDbTypeExtensions.Json) && mtSource.IsCharType && mtSource.SqlDbType != SqlDbType.Xml) { isDataFeed = true; method = ValueMethod.DataFeedText; } - else if ((metadata.type == SqlDbType.Xml) && (mtSource.SqlDbType == SqlDbType.Xml)) + else if ((metadata.DbType == SqlDbType.Xml) && (mtSource.SqlDbType == SqlDbType.Xml)) { isDataFeed = true; method = ValueMethod.DataFeedXml; @@ -1521,12 +1521,12 @@ private SourceColumnMetadata GetColumnMetadata(int ordinal) } else if (_dbDataReaderRowSource != null) { - if (metadata.type == SqlDbType.VarBinary) + if (metadata.DbType == SqlDbType.VarBinary) { isDataFeed = true; method = ValueMethod.DataFeedStream; } - else if ((metadata.type == SqlDbType.VarChar) || (metadata.type == SqlDbType.NVarChar)) + else if ((metadata.DbType == SqlDbType.VarChar) || (metadata.DbType == SqlDbType.NVarChar)) { isDataFeed = true; method = ValueMethod.DataFeedText; @@ -1769,7 +1769,7 @@ private object ConvertValue(object value, TdsColumnMetadata metadata, bool isNul return value; } - MetaType type = metadata.metaType; + MetaType type = metadata.MetaType; bool typeChanged = false; // If the column is encrypted then we are going to transparently encrypt this column @@ -1779,13 +1779,13 @@ private object ConvertValue(object value, TdsColumnMetadata metadata, bool isNul byte scale = metadata.scale; byte precision = metadata.precision; int length = metadata.length; - if (metadata.isEncrypted) + if (metadata.IsEncrypted) { Debug.Assert(_parser.ShouldEncryptValuesForBulkCopy()); - type = metadata.baseTI.metaType; - scale = metadata.baseTI.scale; - precision = metadata.baseTI.precision; - length = metadata.baseTI.length; + type = metadata.BaseTypeInfo.MetaType; + scale = metadata.BaseTypeInfo.scale; + precision = metadata.BaseTypeInfo.precision; + length = metadata.BaseTypeInfo.length; } try @@ -1826,11 +1826,11 @@ private object ConvertValue(object value, TdsColumnMetadata metadata, bool isNul } catch (SqlTruncateException) { - throw SQL.BulkLoadCannotConvertValue(value.GetType(), mt, metadata.Ordinal, RowNumber, metadata.isEncrypted, metadata.column, value.ToString(), ADP.ParameterValueOutOfRange(sqlValue)); + throw SQL.BulkLoadCannotConvertValue(value.GetType(), mt, metadata.Ordinal, RowNumber, metadata.IsEncrypted, metadata.column, value.ToString(), ADP.ParameterValueOutOfRange(sqlValue)); } catch (Exception e) { - throw SQL.BulkLoadCannotConvertValue(value.GetType(), mt, metadata.Ordinal, RowNumber, metadata.isEncrypted, metadata.column, value.ToString(), e); + throw SQL.BulkLoadCannotConvertValue(value.GetType(), mt, metadata.Ordinal, RowNumber, metadata.IsEncrypted, metadata.column, value.ToString(), e); } } @@ -1876,7 +1876,7 @@ private object ConvertValue(object value, TdsColumnMetadata metadata, bool isNul int maxStringLength = length / 2; if (str.Length > maxStringLength) { - if (metadata.isEncrypted) + if (metadata.IsEncrypted) { str = ""; } @@ -1921,7 +1921,7 @@ private object ConvertValue(object value, TdsColumnMetadata metadata, bool isNul default: Debug.Fail("Unknown TdsType!" + type.NullableType.ToString("x2", (IFormatProvider)null)); - throw SQL.BulkLoadCannotConvertValue(value.GetType(), type, metadata.Ordinal, RowNumber, metadata.isEncrypted, metadata.column, value.ToString(), null); + throw SQL.BulkLoadCannotConvertValue(value.GetType(), type, metadata.Ordinal, RowNumber, metadata.IsEncrypted, metadata.column, value.ToString(), null); } if (typeChanged) @@ -1934,7 +1934,7 @@ private object ConvertValue(object value, TdsColumnMetadata metadata, bool isNul } catch (Exception e) when (ADP.IsCatchableExceptionType(e)) { - throw SQL.BulkLoadCannotConvertValue(value.GetType(), type, metadata.Ordinal, RowNumber, metadata.isEncrypted, metadata.column, value.ToString(), e); + throw SQL.BulkLoadCannotConvertValue(value.GetType(), type, metadata.Ordinal, RowNumber, metadata.IsEncrypted, metadata.column, value.ToString(), e); } } @@ -2512,7 +2512,7 @@ private Task ReadWriteColumnValueAsync(int col) // If column encryption is requested via connection string option, perform encryption here if (!isNull && // if value is not NULL - metadata.isEncrypted) + metadata.IsEncrypted) { // If we are transparently encrypting Debug.Assert(_parser.ShouldEncryptValuesForBulkCopy()); value = _parser.EncryptColumnValue(value, metadata, metadata.column, _stateObj, isDataFeed, isSqlType); @@ -2522,7 +2522,7 @@ private Task ReadWriteColumnValueAsync(int col) //write part Task writeTask = null; - if (metadata.type != SqlDbType.Variant) + if (metadata.DbType != SqlDbType.Variant) { //this is the most common path writeTask = _parser.WriteBulkCopyValue(value, metadata, _stateObj, isSqlType, isDataFeed, isNull); //returns Task/Null @@ -2530,7 +2530,7 @@ private Task ReadWriteColumnValueAsync(int col) else { // Target type shouldn't be encrypted - Debug.Assert(!metadata.isEncrypted, "Can't encrypt SQL Variant type"); + Debug.Assert(!metadata.IsEncrypted, "Can't encrypt SQL Variant type"); SqlBuffer.StorageType variantInternalType = SqlBuffer.StorageType.Empty; if ((_sqlDataReaderRowSource != null) && (_connection.Is2008OrNewer)) { diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCachedBuffer.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCachedBuffer.cs index 90fac26804..bd2797ae75 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCachedBuffer.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCachedBuffer.cs @@ -67,7 +67,7 @@ internal static TdsOperationStatus TryCreate(TdsTypeInfo metadata, TdsParser par } // For now we only handle Plp data from the parser directly. - Debug.Assert(metadata.metaType.IsPlp, "SqlCachedBuffer call on a non-plp data"); + Debug.Assert(metadata.MetaType.IsPlp, "SqlCachedBuffer call on a non-plp data"); do { if (plplength == 0) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.cs index dbed2ddd9b..ed9bc01d62 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.cs @@ -1705,7 +1705,7 @@ internal void OnReturnValue(TdsReturnValueToken returnValue, TdsParserStateObjec // If the parameter's direction is InputOutput, Output, or ReturnValue and it needs // to be transparently encrypted/decrypted, then simply decrypt, deserialize, and // set the value. - if (returnValue.cipherMD is not null && + if (returnValue.CipherMetadata is not null && thisParam.CipherMetadata is not null && (thisParam.Direction == ParameterDirection.Output || thisParam.Direction == ParameterDirection.InputOutput || @@ -1713,11 +1713,11 @@ thisParam.CipherMetadata is not null && { // @TODO: make this a separate method // Validate type of the return value is valid for encryption - if (returnValue.tdsType != TdsEnums.SQLBIGVARBINARY) + if (returnValue.TdsType != TdsEnums.SQLBIGVARBINARY) { throw SQL.InvalidDataTypeForEncryptedParameter( thisParam.GetPrefixedParameterName(), - returnValue.tdsType, + returnValue.TdsType, expectedDataType: TdsEnums.SQLBIGVARBINARY); } @@ -1735,10 +1735,10 @@ thisParam.CipherMetadata is not null && Debug.Assert(_activeConnection is not null, @"_activeConnection should not be null"); // Get the key information from the parameter and decrypt the value. - returnValue.cipherMD.EncryptionInfo = thisParam.CipherMetadata.EncryptionInfo; + returnValue.CipherMetadata.EncryptionInfo = thisParam.CipherMetadata.EncryptionInfo; byte[] unencryptedBytes = SqlSecurityUtility.DecryptWithKey( returnValue.Value.ByteArray, - returnValue.cipherMD, + returnValue.CipherMetadata, _activeConnection, this); @@ -1818,9 +1818,9 @@ thisParam.CipherMetadata is not null && } // @TODO: This seems fishy to me, it seems like it should be part of the TdsReturnValueToken class - MetaType mt = MetaType.GetMetaTypeFromSqlDbType(returnValue.type, isMultiValued: false); + MetaType mt = MetaType.GetMetaTypeFromSqlDbType(returnValue.DbType, isMultiValued: false); - if (returnValue.type is SqlDbType.Decimal) + if (returnValue.DbType is SqlDbType.Decimal) { thisParam.ScaleInternal = returnValue.scale; thisParam.PrecisionInternal = returnValue.precision; @@ -1829,7 +1829,7 @@ thisParam.CipherMetadata is not null && { thisParam.ScaleInternal = returnValue.scale; } - else if (returnValue.type is SqlDbType.Xml) + else if (returnValue.DbType is SqlDbType.Xml) { if (thisParam.Value is SqlCachedBuffer cachedBuffer) { diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnection.cs index 298d3d45c2..038b04d7d1 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -3094,16 +3094,16 @@ private Assembly ResolveTypeAssembly(AssemblyName asmRef, bool throwOnError) internal void CheckGetExtendedUDTInfo(TdsTypeInfo metaData, bool fThrow) { - if (metaData.udt?.Type == null) + if (metaData.Udt?.Type == null) { // If null, we have not obtained extended info. - Debug.Assert(!string.IsNullOrEmpty(metaData.udt?.AssemblyQualifiedName), "Unexpected state on GetUDTInfo"); + Debug.Assert(!string.IsNullOrEmpty(metaData.Udt?.AssemblyQualifiedName), "Unexpected state on GetUDTInfo"); // Parameter throwOnError determines whether exception from Assembly.Load is thrown. - metaData.udt.Type = - Type.GetType(typeName: metaData.udt.AssemblyQualifiedName, assemblyResolver: asmRef => ResolveTypeAssembly(asmRef, fThrow), typeResolver: null, throwOnError: fThrow); + metaData.Udt.Type = + Type.GetType(typeName: metaData.Udt.AssemblyQualifiedName, assemblyResolver: asmRef => ResolveTypeAssembly(asmRef, fThrow), typeResolver: null, throwOnError: fThrow); - if (fThrow && metaData.udt.Type == null) + if (fThrow && metaData.Udt.Type == null) { - throw SQL.UDTUnexpectedResult(metaData.udt.AssemblyQualifiedName); + throw SQL.UDTUnexpectedResult(metaData.Udt.AssemblyQualifiedName); } } } @@ -3120,7 +3120,7 @@ internal object GetUdtValue(object value, TdsTypeInfo metaData, bool returnDBNul // Since the serializer doesn't handle nulls... if (ADP.IsNull(value)) { - Type t = metaData.udt?.Type; + Type t = metaData.Udt?.Type; Debug.Assert(t != null, "Unexpected null of udtType on GetUdtValue!"); o = t.InvokeMember("Null", BindingFlags.Public | BindingFlags.GetProperty | BindingFlags.Static, null, null, Array.Empty(), CultureInfo.InvariantCulture); Debug.Assert(o != null); @@ -3131,7 +3131,7 @@ internal object GetUdtValue(object value, TdsTypeInfo metaData, bool returnDBNul MemoryStream stm = new MemoryStream((byte[])value); - o = Server.SerializationHelperSql9.Deserialize(stm, metaData.udt?.Type); + o = Server.SerializationHelperSql9.Deserialize(stm, metaData.Udt?.Type); Debug.Assert(o != null, "object could NOT be created"); return o; diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs index f31a1889be..9f66021958 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs @@ -301,19 +301,19 @@ internal virtual SmiExtendedMetaData[] GetInternalSmiMetaData() string typeSpecificNamePart2 = null; string typeSpecificNamePart3 = null; - if (SqlDbType.Xml == colMetaData.type) + if (SqlDbType.Xml == colMetaData.DbType) { - typeSpecificNamePart1 = colMetaData.xmlSchemaCollection?.Database; - typeSpecificNamePart2 = colMetaData.xmlSchemaCollection?.OwningSchema; - typeSpecificNamePart3 = colMetaData.xmlSchemaCollection?.Name; + typeSpecificNamePart1 = colMetaData.XmlSchemaCollection?.Database; + typeSpecificNamePart2 = colMetaData.XmlSchemaCollection?.OwningSchema; + typeSpecificNamePart3 = colMetaData.XmlSchemaCollection?.Name; } - else if (SqlDbType.Udt == colMetaData.type) + else if (SqlDbType.Udt == colMetaData.DbType) { Connection.CheckGetExtendedUDTInfo(colMetaData, true); // Ensure that colMetaData.udtType is set - typeSpecificNamePart1 = colMetaData.udt?.DatabaseName; - typeSpecificNamePart2 = colMetaData.udt?.SchemaName; - typeSpecificNamePart3 = colMetaData.udt?.TypeName; + typeSpecificNamePart1 = colMetaData.Udt?.DatabaseName; + typeSpecificNamePart2 = colMetaData.Udt?.SchemaName; + typeSpecificNamePart3 = colMetaData.Udt?.TypeName; } int length = colMetaData.length; @@ -321,21 +321,21 @@ internal virtual SmiExtendedMetaData[] GetInternalSmiMetaData() { length = (int)SmiMetaData.UnlimitedMaxLengthIndicator; } - else if (SqlDbType.NChar == colMetaData.type - || SqlDbType.NVarChar == colMetaData.type) + else if (SqlDbType.NChar == colMetaData.DbType + || SqlDbType.NVarChar == colMetaData.DbType) { length /= ADP.CharSize; } metaDataReturn[returnIndex] = new SmiQueryMetaData( - colMetaData.type, + colMetaData.DbType, length, colMetaData.precision, colMetaData.scale, collation != null ? collation.LCID : _defaultLCID, collation != null ? collation.SqlCompareOptions : SqlCompareOptions.None, - colMetaData.udt?.Type, + colMetaData.Udt?.Type, isMultiValued: false, fieldMetaData: null, extendedProperties: null, @@ -559,40 +559,40 @@ internal DataTable BuildSchemaTable() // col.length is always byte count so for unicode types, half the length // // For MAX and XML datatypes, we get 0x7fffffff from the server. Do not divide this. - if (col.cipherMD != null) + if (col.CipherMetadata != null) { - Debug.Assert(col.baseTI != null && col.baseTI.metaType != null, "col.baseTI and col.baseTI.metaType should not be null."); - schemaRow[size] = (col.baseTI.metaType.IsSizeInCharacters && (col.baseTI.length != 0x7fffffff)) ? (col.baseTI.length / 2) : col.baseTI.length; + Debug.Assert(col.BaseTypeInfo != null && col.BaseTypeInfo.MetaType != null, "col.baseTI and col.baseTI.metaType should not be null."); + schemaRow[size] = (col.BaseTypeInfo.MetaType.IsSizeInCharacters && (col.BaseTypeInfo.length != 0x7fffffff)) ? (col.BaseTypeInfo.length / 2) : col.BaseTypeInfo.length; } else { - schemaRow[size] = (col.metaType.IsSizeInCharacters && (col.length != 0x7fffffff)) ? (col.length / 2) : col.length; + schemaRow[size] = (col.MetaType.IsSizeInCharacters && (col.length != 0x7fffffff)) ? (col.length / 2) : col.length; } schemaRow[dataType] = GetFieldTypeInternal(col); schemaRow[providerSpecificDataType] = GetProviderSpecificFieldTypeInternal(col); - schemaRow[nonVersionedProviderType] = (int)(col.cipherMD != null ? col.baseTI.type : col.type); // SqlDbType enum value - does not change with TypeSystem. + schemaRow[nonVersionedProviderType] = (int)(col.CipherMetadata != null ? col.BaseTypeInfo.DbType : col.DbType); // SqlDbType enum value - does not change with TypeSystem. schemaRow[dataTypeName] = GetDataTypeNameInternal(col); if (_typeSystem <= SqlConnectionOptions.TypeSystem.SQLServer2005 && col.Is2008DateTimeType) { schemaRow[providerType] = SqlDbType.NVarChar; - switch (col.type) + switch (col.DbType) { case SqlDbType.Date: schemaRow[size] = TdsEnums.WHIDBEY_DATE_LENGTH; break; case SqlDbType.Time: Debug.Assert(TdsEnums.UNKNOWN_PRECISION_SCALE == col.scale || (0 <= col.scale && col.scale <= 7), "Invalid scale for Time column: " + col.scale); - schemaRow[size] = TdsEnums.WHIDBEY_TIME_LENGTH[TdsEnums.UNKNOWN_PRECISION_SCALE != col.scale ? col.scale : col.metaType.Scale]; + schemaRow[size] = TdsEnums.WHIDBEY_TIME_LENGTH[TdsEnums.UNKNOWN_PRECISION_SCALE != col.scale ? col.scale : col.MetaType.Scale]; break; case SqlDbType.DateTime2: Debug.Assert(TdsEnums.UNKNOWN_PRECISION_SCALE == col.scale || (0 <= col.scale && col.scale <= 7), "Invalid scale for DateTime2 column: " + col.scale); - schemaRow[size] = TdsEnums.WHIDBEY_DATETIME2_LENGTH[TdsEnums.UNKNOWN_PRECISION_SCALE != col.scale ? col.scale : col.metaType.Scale]; + schemaRow[size] = TdsEnums.WHIDBEY_DATETIME2_LENGTH[TdsEnums.UNKNOWN_PRECISION_SCALE != col.scale ? col.scale : col.MetaType.Scale]; break; case SqlDbType.DateTimeOffset: Debug.Assert(TdsEnums.UNKNOWN_PRECISION_SCALE == col.scale || (0 <= col.scale && col.scale <= 7), "Invalid scale for DateTimeOffset column: " + col.scale); - schemaRow[size] = TdsEnums.WHIDBEY_DATETIMEOFFSET_LENGTH[TdsEnums.UNKNOWN_PRECISION_SCALE != col.scale ? col.scale : col.metaType.Scale]; + schemaRow[size] = TdsEnums.WHIDBEY_DATETIMEOFFSET_LENGTH[TdsEnums.UNKNOWN_PRECISION_SCALE != col.scale ? col.scale : col.MetaType.Scale]; break; } } @@ -605,31 +605,31 @@ internal DataTable BuildSchemaTable() // TypeSystem.SQLServer2005 and above // SqlDbType enum value - always the actual type for SQLServer2005. - schemaRow[providerType] = (int)(col.cipherMD != null ? col.baseTI.type : col.type); + schemaRow[providerType] = (int)(col.CipherMetadata != null ? col.BaseTypeInfo.DbType : col.DbType); - if (col.type == SqlDbType.Udt) + if (col.DbType == SqlDbType.Udt) { // Additional metadata for UDTs. Debug.Assert(Connection.Parser.Capabilities.UserDefinedTypes, "Invalid Column type received from the server"); - schemaRow[udtAssemblyQualifiedName] = col.udt?.AssemblyQualifiedName; + schemaRow[udtAssemblyQualifiedName] = col.Udt?.AssemblyQualifiedName; } - else if (col.type == SqlDbType.Xml) + else if (col.DbType == SqlDbType.Xml) { // Additional metadata for Xml. - schemaRow[xmlSchemaCollectionDatabase] = col.xmlSchemaCollection?.Database; - schemaRow[xmlSchemaCollectionOwningSchema] = col.xmlSchemaCollection?.OwningSchema; - schemaRow[xmlSchemaCollectionName] = col.xmlSchemaCollection?.Name; + schemaRow[xmlSchemaCollectionDatabase] = col.XmlSchemaCollection?.Database; + schemaRow[xmlSchemaCollectionOwningSchema] = col.XmlSchemaCollection?.OwningSchema; + schemaRow[xmlSchemaCollectionName] = col.XmlSchemaCollection?.Name; } } - if (col.cipherMD != null) + if (col.CipherMetadata != null) { - Debug.Assert(col.baseTI != null, @"col.baseTI should not be null."); - if (TdsEnums.UNKNOWN_PRECISION_SCALE != col.baseTI.precision) + Debug.Assert(col.BaseTypeInfo != null, @"col.baseTI should not be null."); + if (TdsEnums.UNKNOWN_PRECISION_SCALE != col.BaseTypeInfo.precision) { - schemaRow[precision] = col.baseTI.precision; + schemaRow[precision] = col.BaseTypeInfo.precision; } else { - schemaRow[precision] = col.baseTI.metaType.Precision; + schemaRow[precision] = col.BaseTypeInfo.MetaType.Precision; } } else if (TdsEnums.UNKNOWN_PRECISION_SCALE != col.precision) @@ -638,23 +638,23 @@ internal DataTable BuildSchemaTable() } else { - schemaRow[precision] = col.metaType.Precision; + schemaRow[precision] = col.MetaType.Precision; } if (_typeSystem <= SqlConnectionOptions.TypeSystem.SQLServer2005 && col.Is2008DateTimeType) { schemaRow[scale] = MetaType.MetaNVarChar.Scale; } - else if (col.cipherMD != null) + else if (col.CipherMetadata != null) { - Debug.Assert(col.baseTI != null, @"col.baseTI should not be null."); - if (TdsEnums.UNKNOWN_PRECISION_SCALE != col.baseTI.scale) + Debug.Assert(col.BaseTypeInfo != null, @"col.baseTI should not be null."); + if (TdsEnums.UNKNOWN_PRECISION_SCALE != col.BaseTypeInfo.scale) { - schemaRow[scale] = col.baseTI.scale; + schemaRow[scale] = col.BaseTypeInfo.scale; } else { - schemaRow[scale] = col.baseTI.metaType.Scale; + schemaRow[scale] = col.BaseTypeInfo.MetaType.Scale; } } else if (TdsEnums.UNKNOWN_PRECISION_SCALE != col.scale) @@ -663,7 +663,7 @@ internal DataTable BuildSchemaTable() } else { - schemaRow[scale] = col.metaType.Scale; + schemaRow[scale] = col.MetaType.Scale; } schemaRow[allowDBNull] = col.IsNullable; @@ -680,19 +680,19 @@ internal DataTable BuildSchemaTable() schemaRow[isIdentity] = col.IsIdentity; schemaRow[isAutoIncrement] = col.IsIdentity; - if (col.cipherMD != null) + if (col.CipherMetadata != null) { - Debug.Assert(col.baseTI != null, @"col.baseTI should not be null."); - Debug.Assert(col.baseTI.metaType != null, @"col.baseTI.metaType should not be null."); - schemaRow[isLong] = col.baseTI.metaType.IsLong; + Debug.Assert(col.BaseTypeInfo != null, @"col.baseTI should not be null."); + Debug.Assert(col.BaseTypeInfo.MetaType != null, @"col.baseTI.metaType should not be null."); + schemaRow[isLong] = col.BaseTypeInfo.MetaType.IsLong; } else { - schemaRow[isLong] = col.metaType.IsLong; + schemaRow[isLong] = col.MetaType.IsLong; } // mark unique for timestamp columns - if (SqlDbType.Timestamp == col.type) + if (SqlDbType.Timestamp == col.DbType) { schemaRow[isUnique] = true; schemaRow[isRowVersion] = true; @@ -1203,20 +1203,20 @@ private string GetDataTypeNameInternal(TdsColumnMetadata metaData) { // TypeSystem.SQLServer2005 and above - if (metaData.type == SqlDbType.Udt) + if (metaData.DbType == SqlDbType.Udt) { - dataTypeName = metaData.udt?.DatabaseName + "." + metaData.udt?.SchemaName + "." + metaData.udt?.TypeName; + dataTypeName = metaData.Udt?.DatabaseName + "." + metaData.Udt?.SchemaName + "." + metaData.Udt?.TypeName; } else { // For all other types, including Xml - use data in MetaType. - if (metaData.cipherMD != null) + if (metaData.CipherMetadata != null) { - Debug.Assert(metaData.baseTI != null && metaData.baseTI.metaType != null, "metaData.baseTI and metaData.baseTI.metaType should not be null."); - dataTypeName = metaData.baseTI.metaType.TypeName; + Debug.Assert(metaData.BaseTypeInfo != null && metaData.BaseTypeInfo.MetaType != null, "metaData.baseTI and metaData.baseTI.metaType should not be null."); + dataTypeName = metaData.BaseTypeInfo.MetaType.TypeName; } else { - dataTypeName = metaData.metaType.TypeName; + dataTypeName = metaData.MetaType.TypeName; } } } @@ -1279,25 +1279,25 @@ private Type GetFieldTypeInternal(TdsColumnMetadata metaData) else { // TypeSystem.SQLServer2005 and above - if (metaData.type == SqlDbType.Udt) + if (metaData.DbType == SqlDbType.Udt) { Connection.CheckGetExtendedUDTInfo(metaData, false); - fieldType = metaData.udt?.Type; + fieldType = metaData.Udt?.Type; } - else if (metaData.type == SqlDbTypeExtensions.Vector) + else if (metaData.DbType == SqlDbTypeExtensions.Vector) { fieldType = GetVectorFieldType(metaData.scale); } else { // For all other types, including Xml - use data in MetaType. - if (metaData.cipherMD != null) + if (metaData.CipherMetadata != null) { - Debug.Assert(metaData.baseTI != null && metaData.baseTI.metaType != null, "metaData.baseTI and metaData.baseTI.metaType should not be null."); - fieldType = metaData.baseTI.metaType.ClassType; + Debug.Assert(metaData.BaseTypeInfo != null && metaData.BaseTypeInfo.MetaType != null, "metaData.baseTI and metaData.baseTI.metaType should not be null."); + fieldType = metaData.BaseTypeInfo.MetaType.ClassType; } else { - fieldType = metaData.metaType.ClassType; // Com+ type. + fieldType = metaData.MetaType.ClassType; // Com+ type. } } } @@ -1323,13 +1323,13 @@ internal virtual int GetLocaleId(int i) TdsColumnMetadata tdsColumnMetadata = MetaData[i]; int lcid; - if (tdsColumnMetadata.cipherMD != null) + if (tdsColumnMetadata.CipherMetadata != null) { // If this column is encrypted, get the collation from baseTI // - if (tdsColumnMetadata.baseTI.collation != null) + if (tdsColumnMetadata.BaseTypeInfo.collation != null) { - lcid = tdsColumnMetadata.baseTI.collation.LCID; + lcid = tdsColumnMetadata.BaseTypeInfo.collation.LCID; } else { @@ -1398,27 +1398,27 @@ private Type GetProviderSpecificFieldTypeInternal(TdsColumnMetadata metaData) else { // TypeSystem.SQLServer2005 and above - if (metaData.type == SqlDbType.Udt) + if (metaData.DbType == SqlDbType.Udt) { Connection.CheckGetExtendedUDTInfo(metaData, false); - providerSpecificFieldType = metaData.udt?.Type; + providerSpecificFieldType = metaData.Udt?.Type; } - else if (metaData.type == SqlDbTypeExtensions.Vector) + else if (metaData.DbType == SqlDbTypeExtensions.Vector) { providerSpecificFieldType = GetVectorFieldType(metaData.scale); } else { // For all other types, including Xml - use data in MetaType. - if (metaData.cipherMD != null) + if (metaData.CipherMetadata != null) { - Debug.Assert(metaData.baseTI != null && metaData.baseTI.metaType != null, + Debug.Assert(metaData.BaseTypeInfo != null && metaData.BaseTypeInfo.MetaType != null, "metaData.baseTI and metaData.baseTI.metaType should not be null."); - providerSpecificFieldType = metaData.baseTI.metaType.SqlType; // SqlType type. + providerSpecificFieldType = metaData.BaseTypeInfo.MetaType.SqlType; // SqlType type. } else { - providerSpecificFieldType = metaData.metaType.SqlType; // SqlType type. + providerSpecificFieldType = metaData.MetaType.SqlType; // SqlType type. } } } @@ -1499,7 +1499,7 @@ public virtual XmlReader GetXmlReader(int i) // If this ever changes, the following code should be changed to be like GetStream/GetTextReader CheckDataIsReady(columnIndex: i); - MetaType mt = _metaData[i].metaType; + MetaType mt = _metaData[i].MetaType; // XmlReader only allowed on XML types if (mt.SqlDbType != SqlDbType.Xml) @@ -1538,14 +1538,14 @@ public override Stream GetStream(int i) CheckDataIsReady(columnIndex: i); // Streaming is not supported on encrypted columns. - if (_metaData[i] != null && _metaData[i].cipherMD != null) + if (_metaData[i] != null && _metaData[i].CipherMetadata != null) { throw SQL.StreamNotSupportOnEncryptedColumn(_metaData[i].column); } // Stream is only for Binary, Image, VarBinary, Udt and Xml types // NOTE: IsBinType also includes Timestamp for some reason... - MetaType mt = _metaData[i].metaType; + MetaType mt = _metaData[i].MetaType; if (((!mt.IsBinType) || (mt.SqlDbType == SqlDbType.Timestamp)) && (mt.SqlDbType != SqlDbType.Variant)) { throw SQL.StreamNotSupportOnColumnType(_metaData[i].column); @@ -1596,7 +1596,7 @@ public override long GetBytes(int i, long dataIndex, byte[] buffer, int bufferIn CheckDataIsReady(columnIndex: i, allowPartiallyReadColumn: true); // don't allow get bytes on non-long or non-binary columns - MetaType mt = _metaData[i].metaType; + MetaType mt = _metaData[i].MetaType; if (!(mt.IsLong || mt.IsBinType) || (SqlDbType.Xml == mt.SqlDbType)) { throw SQL.NonBlobColumn(_metaData[i].column); @@ -1647,7 +1647,7 @@ private TdsOperationStatus TryGetBytesInternal(int i, long dataIndex, byte[] buf { Debug.Assert(!HasActiveStreamOrTextReaderOnColumn(i), "Column has an active Stream or TextReader"); - if (_metaData[i] != null && _metaData[i].cipherMD != null) + if (_metaData[i] != null && _metaData[i].CipherMetadata != null) { throw SQL.SequentialAccessNotSupportedOnEncryptedColumn(_metaData[i].column); } @@ -1668,7 +1668,7 @@ private TdsOperationStatus TryGetBytesInternal(int i, long dataIndex, byte[] buf } // If there are an unknown (-1) number of bytes left for a PLP, read its size - if ((-1 == _sharedState._columnDataBytesRemaining) && (_metaData[i].metaType.IsPlp)) + if ((-1 == _sharedState._columnDataBytesRemaining) && (_metaData[i].MetaType.IsPlp)) { ulong left; result = _parser.TryPlpBytesLeft(_stateObj, out left); @@ -1687,7 +1687,7 @@ private TdsOperationStatus TryGetBytesInternal(int i, long dataIndex, byte[] buf // if no buffer is passed in, return the number total of bytes, or -1 if (buffer == null) { - if (_metaData[i].metaType.IsPlp) + if (_metaData[i].MetaType.IsPlp) { remaining = (long)_parser.PlpBytesTotalLength(_stateObj); return TdsOperationStatus.Done; @@ -1710,7 +1710,7 @@ private TdsOperationStatus TryGetBytesInternal(int i, long dataIndex, byte[] buf long cb = dataIndex - _columnDataBytesRead; // if dataIndex is outside of the data range, return 0 - if ((cb > _sharedState._columnDataBytesRemaining) && !_metaData[i].metaType.IsPlp) + if ((cb > _sharedState._columnDataBytesRemaining) && !_metaData[i].MetaType.IsPlp) { return TdsOperationStatus.Done; } @@ -1735,7 +1735,7 @@ private TdsOperationStatus TryGetBytesInternal(int i, long dataIndex, byte[] buf // Skip if needed if (cb > 0) { - if (_metaData[i].metaType.IsPlp) + if (_metaData[i].MetaType.IsPlp) { ulong skipped; result = _parser.TrySkipPlpValue((ulong)cb, _stateObj, out skipped); @@ -1783,17 +1783,17 @@ private TdsOperationStatus TryGetBytesInternal(int i, long dataIndex, byte[] buf // the use of GetBytes on string data columns, but // GetSqlBinary isn't supposed to. What we end up // doing isn't exactly pretty, but it does work. - if (_metaData[i].metaType.IsBinType) + if (_metaData[i].MetaType.IsBinType) { data = GetSqlBinary(i).Value; } else { - Debug.Assert(_metaData[i].metaType.IsLong, "non long type?"); - Debug.Assert(_metaData[i].metaType.IsCharType, "non-char type?"); + Debug.Assert(_metaData[i].MetaType.IsLong, "non long type?"); + Debug.Assert(_metaData[i].MetaType.IsCharType, "non-char type?"); SqlString temp = GetSqlString(i); - if (_metaData[i].metaType.IsNCharType) + if (_metaData[i].MetaType.IsNCharType) { data = temp.GetUnicodeBytes(); } @@ -1922,7 +1922,7 @@ internal TdsOperationStatus TryGetBytesInternalSequential(int i, byte[] buffer, else { // if plp columns, do partial reads. Don't read the entire value in one shot. - if (_metaData[i].metaType.IsPlp) + if (_metaData[i].MetaType.IsPlp) { // Read in data result = _stateObj.TryReadPlpBytes(ref buffer, index, length, out bytesRead); @@ -1964,14 +1964,14 @@ public override TextReader GetTextReader(int i) // Xml type is not supported MetaType mt = null; - if (_metaData[i].cipherMD != null) + if (_metaData[i].CipherMetadata != null) { - Debug.Assert(_metaData[i].baseTI != null, "_metaData[i].baseTI should not be null."); - mt = _metaData[i].baseTI.metaType; + Debug.Assert(_metaData[i].BaseTypeInfo != null, "_metaData[i].baseTI should not be null."); + mt = _metaData[i].BaseTypeInfo.MetaType; } else { - mt = _metaData[i].metaType; + mt = _metaData[i].MetaType; } Debug.Assert(mt != null, @"mt should not be null."); @@ -1984,7 +1984,7 @@ public override TextReader GetTextReader(int i) // For non-variant types with sequential access, we support proper streaming if ((mt.SqlDbType != SqlDbType.Variant) && (IsCommandBehavior(CommandBehavior.SequentialAccess))) { - if (_metaData[i].cipherMD != null) + if (_metaData[i].CipherMetadata != null) { throw SQL.SequentialAccessNotSupportedOnEncryptedColumn(_metaData[i].column); } @@ -2001,7 +2001,7 @@ public override TextReader GetTextReader(int i) } else { - encoding = _metaData[i].encoding; + encoding = _metaData[i].Encoding; } _currentTextReader = new SqlSequentialTextReader(this, i, encoding); _lastColumnWithDataChunkRead = i; @@ -2049,27 +2049,27 @@ public override long GetChars(int i, long dataIndex, char[] buffer, int bufferIn } MetaType mt = null; - if (_metaData[i].cipherMD != null) + if (_metaData[i].CipherMetadata != null) { - Debug.Assert(_metaData[i].baseTI != null, @"_metaData[i].baseTI should not be null."); - mt = _metaData[i].baseTI.metaType; + Debug.Assert(_metaData[i].BaseTypeInfo != null, @"_metaData[i].baseTI should not be null."); + mt = _metaData[i].BaseTypeInfo.MetaType; } else { - mt = _metaData[i].metaType; + mt = _metaData[i].MetaType; } Debug.Assert(mt != null, "mt should not be null."); SqlDbType sqlDbType; - if (_metaData[i].cipherMD != null) + if (_metaData[i].CipherMetadata != null) { - Debug.Assert(_metaData[i].baseTI != null, @"_metaData[i].baseTI should not be null."); - sqlDbType = _metaData[i].baseTI.type; + Debug.Assert(_metaData[i].BaseTypeInfo != null, @"_metaData[i].baseTI should not be null."); + sqlDbType = _metaData[i].BaseTypeInfo.DbType; } else { - sqlDbType = _metaData[i].type; + sqlDbType = _metaData[i].DbType; } try @@ -2084,7 +2084,7 @@ public override long GetChars(int i, long dataIndex, char[] buffer, int bufferIn throw ADP.InvalidDataLength(length); } - if (_metaData[i].cipherMD != null) + if (_metaData[i].CipherMetadata != null) { throw SQL.SequentialAccessNotSupportedOnEncryptedColumn(_metaData[i].column); } @@ -2219,11 +2219,11 @@ private long GetCharsFromPlpData(int i, long dataIndex, char[] buffer, int buffe AssertReaderState(requireData: true, permitAsync: false, columnIndex: i, enforceSequentialAccess: true); Debug.Assert(!HasActiveStreamOrTextReaderOnColumn(i), "Column has active Stream or TextReader"); // don't allow get bytes on non-long or non-binary columns - Debug.Assert(_metaData[i].metaType.IsPlp, "GetCharsFromPlpData called on a non-plp column!"); + Debug.Assert(_metaData[i].MetaType.IsPlp, "GetCharsFromPlpData called on a non-plp column!"); // Must be sequential reading Debug.Assert(IsCommandBehavior(CommandBehavior.SequentialAccess), "GetCharsFromPlpData called for non-Sequential access"); - if (!_metaData[i].metaType.IsCharType) + if (!_metaData[i].MetaType.IsCharType) { throw SQL.NonCharColumn(_metaData[i].column); } @@ -2253,7 +2253,7 @@ private long GetCharsFromPlpData(int i, long dataIndex, char[] buffer, int buffe _stateObj._plpdecoder = null; } - bool isUnicode = _metaData[i].metaType.IsNCharType; + bool isUnicode = _metaData[i].MetaType.IsNCharType; // If there are an unknown (-1) number of bytes left for a PLP, read its size if (-1 == _sharedState._columnDataBytesRemaining) @@ -2601,7 +2601,7 @@ private object GetSqlValueInternal(int i) // Always make sure to take reference copies of anything set to null in TryCloseInternal() private object GetSqlValueFromSqlBufferInternal(SqlBuffer data, TdsColumnMetadata metaData) { - Debug.Assert(!data.IsEmpty || data.IsNull || metaData.type == SqlDbType.Timestamp, "Data has been read, but the buffer is empty"); + Debug.Assert(!data.IsEmpty || data.IsNull || metaData.DbType == SqlDbType.Timestamp, "Data has been read, but the buffer is empty"); // Convert 2008 types to string if (_typeSystem <= SqlConnectionOptions.TypeSystem.SQLServer2005 && metaData.Is2008DateTimeType) @@ -2612,7 +2612,7 @@ private object GetSqlValueFromSqlBufferInternal(SqlBuffer data, TdsColumnMetadat { return data.SqlValue; } - else if (metaData.type == SqlDbType.Udt) + else if (metaData.DbType == SqlDbType.Udt) { SqlConnection connection = _connection; if (connection != null) @@ -2772,7 +2772,7 @@ private object GetValueInternal(int i) // Always make sure to take reference copies of anything set to null in TryCloseInternal() private object GetValueFromSqlBufferInternal(SqlBuffer data, TdsColumnMetadata metaData) { - Debug.Assert(!data.IsEmpty || data.IsNull || metaData.type == SqlDbType.Timestamp, "Data has been read, but the buffer is empty"); + Debug.Assert(!data.IsEmpty || data.IsNull || metaData.DbType == SqlDbType.Timestamp, "Data has been read, but the buffer is empty"); if (_typeSystem <= SqlConnectionOptions.TypeSystem.SQLServer2005 && metaData.Is2008DateTimeType) { @@ -2789,7 +2789,7 @@ private object GetValueFromSqlBufferInternal(SqlBuffer data, TdsColumnMetadata m { return data.Value; } - else if (metaData.type == SqlDbTypeExtensions.Vector) + else if (metaData.DbType == SqlDbTypeExtensions.Vector) { if (data.IsNull) { @@ -2806,7 +2806,7 @@ private object GetValueFromSqlBufferInternal(SqlBuffer data, TdsColumnMetadata m } } } - else if (metaData.type == SqlDbType.Udt) + else if (metaData.DbType == SqlDbType.Udt) { SqlConnection connection = _connection; if (connection != null) @@ -2905,7 +2905,7 @@ private T GetFieldValueFromSqlBufferInternal(SqlBuffer data, TdsColumnMetadat #endif else if (typeof(T) == typeof(SqlVector)) { - MetaType metaType = metaData.metaType; + MetaType metaType = metaData.MetaType; if (metaType.SqlDbType != SqlDbTypeExtensions.Vector) { throw SQL.VectorNotSupportedOnColumnType(metaData.column); @@ -2915,7 +2915,7 @@ private T GetFieldValueFromSqlBufferInternal(SqlBuffer data, TdsColumnMetadat else if (typeof(T) == typeof(XmlReader)) { // XmlReader only allowed on XML types - if (metaData.metaType.SqlDbType != SqlDbType.Xml) + if (metaData.MetaType.SqlDbType != SqlDbType.Xml) { throw SQL.XmlReaderNotSupportOnColumnType(metaData.column); } @@ -2944,11 +2944,11 @@ private T GetFieldValueFromSqlBufferInternal(SqlBuffer data, TdsColumnMetadat else if (typeof(T) == typeof(TextReader)) { // Xml type is not supported - MetaType metaType = metaData.metaType; - if (metaData.cipherMD != null) + MetaType metaType = metaData.MetaType; + if (metaData.CipherMetadata != null) { - Debug.Assert(metaData.baseTI != null, "_metaData[i].baseTI should not be null."); - metaType = metaData.baseTI.metaType; + Debug.Assert(metaData.BaseTypeInfo != null, "_metaData[i].baseTI should not be null."); + metaType = metaData.BaseTypeInfo.MetaType; } if ( @@ -2962,7 +2962,7 @@ private T GetFieldValueFromSqlBufferInternal(SqlBuffer data, TdsColumnMetadat // For non-variant types with sequential access, we support proper streaming if ((metaType.SqlDbType != SqlDbType.Variant) && IsCommandBehavior(CommandBehavior.SequentialAccess)) { - if (metaData.cipherMD != null) + if (metaData.CipherMetadata != null) { throw SQL.SequentialAccessNotSupportedOnEncryptedColumn(metaData.column); } @@ -2970,7 +2970,7 @@ private T GetFieldValueFromSqlBufferInternal(SqlBuffer data, TdsColumnMetadat System.Text.Encoding encoding = SqlUnicodeEncoding.SqlUnicodeEncodingInstance; if (!metaType.IsNCharType) { - encoding = metaData.encoding; + encoding = metaData.Encoding; } _currentTextReader = new SqlSequentialTextReader(this, metaData.Ordinal, encoding); @@ -2986,13 +2986,13 @@ private T GetFieldValueFromSqlBufferInternal(SqlBuffer data, TdsColumnMetadat } else if (typeof(T) == typeof(Stream)) { - if (metaData != null && metaData.cipherMD != null) + if (metaData != null && metaData.CipherMetadata != null) { throw SQL.StreamNotSupportOnEncryptedColumn(metaData.column); } // Stream is only for Binary, Image, VarBinary, Udt, Xml and Timestamp(RowVersion) types - MetaType metaType = metaData.metaType; + MetaType metaType = metaData.MetaType; if ( (!metaType.IsBinType || metaType.SqlDbType == SqlDbType.Timestamp) && metaType.SqlDbType != SqlDbType.Variant @@ -3015,7 +3015,7 @@ private T GetFieldValueFromSqlBufferInternal(SqlBuffer data, TdsColumnMetadat } else if (typeof(T) == typeof(JsonDocument)) { - MetaType metaType = metaData.metaType; + MetaType metaType = metaData.MetaType; if (metaType.SqlDbType != SqlDbTypeExtensions.Json) { throw SQL.JsonDocumentNotSupportedOnColumnType(metaData.column); @@ -3050,7 +3050,7 @@ private T GetFieldValueFromSqlBufferInternal(SqlBuffer data, TdsColumnMetadat } else { - if (typeof(T) == typeof(string) && metaData.metaType.SqlDbType == SqlDbTypeExtensions.Vector) + if (typeof(T) == typeof(string) && metaData.MetaType.SqlDbType == SqlDbTypeExtensions.Vector) { return (T)(object)data.String; } @@ -3824,7 +3824,7 @@ internal TdsOperationStatus TryReadColumnInternal(int i, bool readHeaderOnly = f Debug.Assert(i == _sharedState._nextColumnDataToRead || // Either we haven't read the column yet ((i + 1 < _sharedState._nextColumnDataToRead) && (IsCommandBehavior(CommandBehavior.SequentialAccess))) || // Or we're in sequential mode and we've read way past the column (i.e. it was not the last column we read) (!_data[i].IsEmpty || _data[i].IsNull) || // Or we should have data stored for the column (unless the column was null) - (_metaData[i].type == SqlDbType.Timestamp), // Or Dev11 Bug #336820, Dev10 Bug #479607 (SqlClient: IsDBNull always returns false for timestamp datatype) + (_metaData[i].DbType == SqlDbType.Timestamp), // Or Dev11 Bug #336820, Dev10 Bug #479607 (SqlClient: IsDBNull always returns false for timestamp datatype) // Due to a bug in TdsParser.GetNullSqlValue, Timestamps' IsNull is not correctly set - so we need to bypass the check "Gone past column, be we have no data stored for it"); return TdsOperationStatus.Done; @@ -3903,7 +3903,7 @@ internal TdsOperationStatus TryReadColumnInternal(int i, bool readHeaderOnly = f if (isNull) { - if (columnMetaData.type != SqlDbType.Timestamp) + if (columnMetaData.DbType != SqlDbType.Timestamp) { TdsParser.GetNullSqlValue(_data[_sharedState._nextColumnDataToRead], columnMetaData, @@ -3954,7 +3954,7 @@ internal TdsOperationStatus TryReadColumnInternal(int i, bool readHeaderOnly = f // Trigger new behavior for RowVersion to send DBNull.Value by allowing entry for Timestamp or discard entry for Timestamp for legacy support. // if LegacyRowVersionNullBehavior is enabled, Timestamp type must enter "else" block. - if (isNull && (!LocalAppContextSwitches.LegacyRowVersionNullBehavior || columnMetaData.type != SqlDbType.Timestamp)) + if (isNull && (!LocalAppContextSwitches.LegacyRowVersionNullBehavior || columnMetaData.DbType != SqlDbType.Timestamp)) { TdsParser.GetNullSqlValue(_data[_sharedState._nextColumnDataToRead], columnMetaData, @@ -4012,7 +4012,7 @@ private bool WillHaveEnoughData(int targetColumn, bool headerOnly = false) { AssertReaderState(requireData: true, permitAsync: true, columnIndex: targetColumn); - if ((_lastColumnWithDataChunkRead == _sharedState._nextColumnDataToRead) && (_metaData[_lastColumnWithDataChunkRead].metaType.IsPlp)) + if ((_lastColumnWithDataChunkRead == _sharedState._nextColumnDataToRead) && (_metaData[_lastColumnWithDataChunkRead].MetaType.IsPlp)) { // In the middle of reading a Plp - no idea how much is left return false; @@ -4048,22 +4048,22 @@ private bool WillHaveEnoughData(int targetColumn, bool headerOnly = false) if (!_stateObj.IsNullCompressionBitSet(currentColumn)) { // NOTE: This is mostly duplicated from TryProcessColumnHeaderNoNBC and TryGetTokenLength - var metaType = _metaData[currentColumn].metaType; + var metaType = _metaData[currentColumn].MetaType; if ((metaType.IsLong) || (metaType.IsPlp) || (metaType.SqlDbType == SqlDbType.Udt) || (metaType.SqlDbType == SqlDbType.Structured)) { // Plp, Udt and TVP types have an unknowable size - so return that the estimate failed return false; } int maxHeaderSize; - byte typeAndMask = (byte)(_metaData[currentColumn].tdsType & TdsEnums.SQLLenMask); + byte typeAndMask = (byte)(_metaData[currentColumn].TdsType & TdsEnums.SQLLenMask); if ((typeAndMask == TdsEnums.SQLVarLen) || (typeAndMask == TdsEnums.SQLVarCnt)) { - if (0 != (_metaData[currentColumn].tdsType & 0x80)) + if (0 != (_metaData[currentColumn].TdsType & 0x80)) { // UInt16 represents size maxHeaderSize = 2; } - else if (0 == (_metaData[currentColumn].tdsType & 0x0c)) + else if (0 == (_metaData[currentColumn].TdsType & 0x0c)) { // UInt32 represents size maxHeaderSize = 4; @@ -4102,7 +4102,7 @@ private TdsOperationStatus TryResetBlobState() // If we haven't already entirely read the column if (_sharedState._nextColumnDataToRead < _sharedState._nextColumnHeaderToRead) { - if ((_sharedState._nextColumnHeaderToRead > 0) && (_metaData[_sharedState._nextColumnHeaderToRead - 1].metaType.IsPlp)) + if ((_sharedState._nextColumnHeaderToRead > 0) && (_metaData[_sharedState._nextColumnHeaderToRead - 1].MetaType.IsPlp)) { if (_stateObj._longlen != 0) { @@ -5848,7 +5848,7 @@ private ReadOnlyCollection BuildColumnSchema() } else { - dbColumn.SqlNumericScale = col.metaType.Scale; + dbColumn.SqlNumericScale = col.MetaType.Scale; } if (_browseModeInfoConsumed) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDbColumn.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDbColumn.cs index 60ab6d321c..cb2ee9a269 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDbColumn.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDbColumn.cs @@ -30,12 +30,12 @@ private void Populate() BaseTableName = _metadata.TableName; ColumnName = _metadata.column; ColumnOrdinal = _metadata.Ordinal; - ColumnSize = (_metadata.metaType.IsSizeInCharacters && (_metadata.length != 0x7fffffff)) ? (_metadata.length / 2) : _metadata.length; + ColumnSize = (_metadata.MetaType.IsSizeInCharacters && (_metadata.length != 0x7fffffff)) ? (_metadata.length / 2) : _metadata.length; IsAutoIncrement = _metadata.IsIdentity; IsIdentity = _metadata.IsIdentity; - IsLong = _metadata.metaType.IsLong; + IsLong = _metadata.MetaType.IsLong; - if (SqlDbType.Timestamp == _metadata.type) + if (SqlDbType.Timestamp == _metadata.DbType) { IsUnique = true; } @@ -50,12 +50,12 @@ private void Populate() } else { - NumericPrecision = _metadata.metaType.Precision; + NumericPrecision = _metadata.MetaType.Precision; } IsReadOnly = _metadata.IsReadOnly; - UdtAssemblyQualifiedName = _metadata.udt?.AssemblyQualifiedName; + UdtAssemblyQualifiedName = _metadata.Udt?.AssemblyQualifiedName; } diff --git a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlMetaDataSetTest.cs b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlMetaDataSetTest.cs index a484c0217a..5770831bcc 100644 --- a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlMetaDataSetTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlMetaDataSetTest.cs @@ -154,11 +154,11 @@ public void SqlMetaData_Clone_PreservesIsEncrypted() // If lost, encrypted columns are sent as plaintext. TdsColumnMetadataToken original = new TdsColumnMetadataToken(1); original[0].column = "encrypted_col"; - original[0].isEncrypted = true; + original[0].IsEncrypted = true; TdsColumnMetadataToken clone = original.Clone(); - Assert.True(clone[0].isEncrypted); + Assert.True(clone[0].IsEncrypted); } [Fact] @@ -179,16 +179,16 @@ public void SqlMetaData_Clone_PreservesCipherMetadata() TdsColumnMetadataToken original = new TdsColumnMetadataToken(1); original[0].column = "encrypted_col"; - original[0].isEncrypted = true; - original[0].cipherMD = cipherMD; + original[0].IsEncrypted = true; + original[0].CipherMetadata = cipherMD; TdsColumnMetadataToken clone = original.Clone(); - Assert.NotNull(clone[0].cipherMD); - Assert.Equal(2, clone[0].cipherMD.CipherAlgorithmId); - Assert.Equal("AEAD_AES_256_CBC_HMAC_SHA256", clone[0].cipherMD.CipherAlgorithmName); - Assert.Equal(1, clone[0].cipherMD.EncryptionType); - Assert.Equal(1, clone[0].cipherMD.NormalizationRuleVersion); + Assert.NotNull(clone[0].CipherMetadata); + Assert.Equal(2, clone[0].CipherMetadata.CipherAlgorithmId); + Assert.Equal("AEAD_AES_256_CBC_HMAC_SHA256", clone[0].CipherMetadata.CipherAlgorithmName); + Assert.Equal(1, clone[0].CipherMetadata.EncryptionType); + Assert.Equal(1, clone[0].CipherMetadata.NormalizationRuleVersion); } [Fact] @@ -205,14 +205,14 @@ public void SqlMetaData_Clone_PreservesBaseTI() TdsColumnMetadataToken original = new TdsColumnMetadataToken(1); original[0].column = "encrypted_col"; - original[0].isEncrypted = true; - original[0].baseTI = baseTI; + original[0].IsEncrypted = true; + original[0].BaseTypeInfo = baseTI; TdsColumnMetadataToken clone = original.Clone(); - Assert.NotNull(clone[0].baseTI); - Assert.Equal(System.Data.SqlDbType.NVarChar, clone[0].baseTI.DbType); - Assert.Equal(100, clone[0].baseTI.length); + Assert.NotNull(clone[0].BaseTypeInfo); + Assert.Equal(System.Data.SqlDbType.NVarChar, clone[0].BaseTypeInfo.DbType); + Assert.Equal(100, clone[0].BaseTypeInfo.length); } [Fact] @@ -241,9 +241,9 @@ public void SqlMetaDataSet_Clone_PreservesFullAlwaysEncryptedMetadata() TdsColumnMetadataToken original = new TdsColumnMetadataToken(2, cekTable); original[0].column = "id"; original[1].column = "secret"; - original[1].isEncrypted = true; - original[1].cipherMD = cipherMD; - original[1].baseTI = baseTI; + original[1].IsEncrypted = true; + original[1].CipherMetadata = cipherMD; + original[1].BaseTypeInfo = baseTI; // Clone and prune column 0 (simulating mapping only the encrypted column) TdsColumnMetadataToken clone = original.Clone(); @@ -251,10 +251,10 @@ public void SqlMetaDataSet_Clone_PreservesFullAlwaysEncryptedMetadata() // The pruning must not affect the encrypted column's metadata Assert.NotNull(clone[1]); - Assert.True(clone[1].isEncrypted); - Assert.NotNull(clone[1].cipherMD); - Assert.NotNull(clone[1].baseTI); - Assert.Equal(System.Data.SqlDbType.Int, clone[1].baseTI.DbType); + Assert.True(clone[1].IsEncrypted); + Assert.NotNull(clone[1].CipherMetadata); + Assert.NotNull(clone[1].BaseTypeInfo); + Assert.Equal(System.Data.SqlDbType.Int, clone[1].BaseTypeInfo.DbType); // The cekTable must be preserved on the clone Assert.NotNull(clone.CekTable); @@ -264,7 +264,7 @@ public void SqlMetaDataSet_Clone_PreservesFullAlwaysEncryptedMetadata() Assert.NotNull(original[0]); Assert.NotNull(original[1]); Assert.NotNull(original.CekTable); - Assert.True(original[1].isEncrypted); + Assert.True(original[1].IsEncrypted); } } } From 86fa7531e6750b835f0784e7ae50ea1e1408918c Mon Sep 17 00:00:00 2001 From: Benjamin Russell Date: Fri, 11 Sep 2026 13:10:18 -0500 Subject: [PATCH 15/17] Comments from :robot: Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../Data/SqlClient/Parser/Tokens/TdsColumnMetadata.cs | 2 +- .../Data/SqlClient/Parser/Tokens/TdsEnvChangeRoutingInfo.cs | 2 +- .../Data/SqlClient/Parser/Tokens/TdsFedAuthInfoToken.cs | 3 +-- .../Data/SqlClient/Parser/Tokens/TdsReturnValueToken.cs | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadata.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadata.cs index 3ef03111bc..267c96a1e2 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadata.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadata.cs @@ -9,7 +9,7 @@ namespace Microsoft.Data.SqlClient.Parser.Tokens; /// /// Represents metadata information for a specific column in a TDS stream. Corresponds with a -/// single column withing a SQLCOLMETADATA/SQLALTMETADATA token. +/// single column within a SQLCOLMETADATA/SQLALTMETADATA token. /// internal sealed class TdsColumnMetadata : TdsTypeInfo { diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsEnvChangeRoutingInfo.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsEnvChangeRoutingInfo.cs index dc3133e591..8f8e4ca1d9 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsEnvChangeRoutingInfo.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsEnvChangeRoutingInfo.cs @@ -31,7 +31,7 @@ internal TdsEnvChangeRoutingInfo(byte protocol, ushort port, string serverName, internal ushort Port { get; private set; } /// - /// Represents the protocol used for routing in an EnhancedRouting ENVCHANGE token. + /// Represents the protocol used for routing in a Routing or EnhancedRouting ENVCHANGE token. /// // @TODO: Should this be an enum of protocol types? internal byte Protocol { get; private set; } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsFedAuthInfoToken.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsFedAuthInfoToken.cs index c033e01eec..fc2509c6c0 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsFedAuthInfoToken.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsFedAuthInfoToken.cs @@ -33,8 +33,7 @@ internal TdsFedAuthInfoToken(string spn, string stsUrl) /// /// Gets the Service Principal Name (SPN) associated with the federated authentication token. - /// The SPN is used in authentication processes to uniquely identify a service instance - /// in the context of Kerberos-based security mechanisms. + /// The server-provided SPN is passed to the authentication provider as the token resource. /// internal string Spn { get; } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsReturnValueToken.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsReturnValueToken.cs index 6e658b4013..b67358cb1f 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsReturnValueToken.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsReturnValueToken.cs @@ -22,7 +22,7 @@ internal sealed class TdsReturnValueToken : TdsTypeInfo internal string parameter; /// - /// Constructs a new instance of TddsReturnValueToken with the value initialized to a new +/// Constructs a new instance of TdsReturnValueToken with the value initialized to a new /// instance. /// internal TdsReturnValueToken() : base() From 62353db69230092ceabfaa6f1b3351197ef9baa2 Mon Sep 17 00:00:00 2001 From: Benjamin Russell Date: Fri, 11 Sep 2026 13:21:50 -0500 Subject: [PATCH 16/17] Really wish the :robot: would give all the comments up front... Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../Data/SqlClient/Parser/Tokens/TdsAltMetadataCollection.cs | 2 +- .../Data/SqlClient/Parser/Tokens/TdsColumnMetadataToken.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsAltMetadataCollection.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsAltMetadataCollection.cs index ef85b22d12..6f32066c85 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsAltMetadataCollection.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsAltMetadataCollection.cs @@ -25,7 +25,7 @@ internal TdsAltMetadataCollection() internal TdsColumnMetadataToken MetadataSet { get; set; } /// - /// Creates a deep copy of the current TdsAltMetadataCollection instance, including all its elements. + /// Clones the collection and each metadata-set object; nested schema and encryption metadata may remain shared. /// public object Clone() { diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadataToken.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadataToken.cs index 00691db2db..e4e2a85474 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadataToken.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Parser/Tokens/TdsColumnMetadataToken.cs @@ -118,8 +118,8 @@ internal int VisibleColumnCount } /// - /// Creates a new instance of TdsColumnMetadataToken that is a deep copy of the current - /// instance. + /// Creates a new instance with independently cloned column metadata entries. Cached schema data, + /// the CEK table, and nested encryption metadata remain shared with the current instance. /// public TdsColumnMetadataToken Clone() { From 6eb0f3064ac67b850c728ccf8f4a2224b1aed9c0 Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Wed, 7 Oct 2026 13:22:51 -0400 Subject: [PATCH 17/17] Fix broken references in tests --- .../Microsoft/Data/SqlClient/SqlBulkCopyVariantTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlBulkCopyVariantTests.cs b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlBulkCopyVariantTests.cs index dd1e7bc9ba..7db62c5476 100644 --- a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlBulkCopyVariantTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlBulkCopyVariantTests.cs @@ -117,9 +117,9 @@ public void MoneyBufferPreservesAndClearsSmallMoneyMetadata() public void DecryptedMoneyRetainsSourceType(int tdsType, int length, bool isSmallMoney) { SqlBuffer buffer = new SqlBuffer(); - SqlMetaDataPriv metadata = new SqlMetaDataPriv + TdsTypeInfo metadata = new TdsTypeInfo { - baseTI = new SqlMetaDataPriv { tdsType = (byte)tdsType, length = length } + BaseTypeInfo = new TdsTypeInfo { TdsType = (byte)tdsType, length = length } }; // Always Encrypted normalizes both subtypes to eight bytes. This encodes -1.2345; // the original type/length, including nullable SQLMONEYN, must determine the subtype.