From 6a9cd8c92ae33a6ce08a057d3f1dd57e6ae0bddf Mon Sep 17 00:00:00 2001 From: Michel Zehnder Date: Tue, 5 Nov 2024 21:05:47 +0100 Subject: [PATCH 01/13] Align IsColumnEncryptionSupported between netfx and netcore --- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 35 ++++++------------- 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs index 4ccaf1ca18..418e5aba58 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -254,9 +254,6 @@ internal static void Assert(string message) // NOTE: You must take the internal connection's _parserLock before modifying this internal bool _asyncWrite = false; - // TCE supported flag, used to determine if new TDS fields are present. This is - // useful when talking to downlevel/uplevel server. - private bool _serverSupportsColumnEncryption = false; // now data length is 1 byte // First bit is 1 indicating client support failover partner with readonly intent @@ -265,17 +262,7 @@ internal static void Assert(string message) /// /// Get or set if column encryption is supported by the server. /// - internal bool IsColumnEncryptionSupported - { - get - { - return _serverSupportsColumnEncryption; - } - set - { - _serverSupportsColumnEncryption = value; - } - } + internal bool IsColumnEncryptionSupported { get; set; } = false; /// /// TCE version supported by the server @@ -4670,7 +4657,7 @@ internal TdsOperationStatus TryProcessReturnValue(int length, } // Check if the column is encrypted. - if (_serverSupportsColumnEncryption) + if (IsColumnEncryptionSupported) { rec.isEncrypted = (TdsEnums.IsEncrypted == (flags & TdsEnums.IsEncrypted)); } @@ -4854,7 +4841,7 @@ internal TdsOperationStatus TryProcessReturnValue(int length, } // For encrypted parameters, read the unencrypted type and encryption information. - if (_serverSupportsColumnEncryption && rec.isEncrypted) + if (IsColumnEncryptionSupported && rec.isEncrypted) { result = TryProcessTceCryptoMetadata(stateObj, rec, cipherTable: null, columnEncryptionSetting: columnEncryptionSetting, isReturnValue: true); if (result != TdsOperationStatus.Done) @@ -5576,7 +5563,7 @@ internal TdsOperationStatus TryProcessMetaData(int cColumns, TdsParserStateObjec // Read the cipher info table first SqlTceCipherInfoTable cipherTable = null; - if (_serverSupportsColumnEncryption) + if (IsColumnEncryptionSupported) { TdsOperationStatus result = TryProcessCipherInfoTable(stateObj, out cipherTable); if (result != TdsOperationStatus.Done) @@ -5853,7 +5840,7 @@ private TdsOperationStatus TryCommonProcessMetaData(TdsParserStateObject stateOb } col.IsColumnSet = (TdsEnums.IsColumnSet == (flags & TdsEnums.IsColumnSet)); - if (fColMD && _serverSupportsColumnEncryption) + if (fColMD && IsColumnEncryptionSupported) { col.isEncrypted = (TdsEnums.IsEncrypted == (flags & TdsEnums.IsEncrypted)); } @@ -5900,7 +5887,7 @@ private TdsOperationStatus TryCommonProcessMetaData(TdsParserStateObject stateOb } // Read the TCE column cryptoinfo - if (fColMD && _serverSupportsColumnEncryption && col.isEncrypted) + if (fColMD && IsColumnEncryptionSupported && col.isEncrypted) { // If the column is encrypted, we should have a valid cipherTable if (cipherTable != null) @@ -11361,7 +11348,7 @@ internal Task WriteBulkCopyDone(TdsParserStateObject stateObj) /// internal void LoadColumnEncryptionKeys(_SqlMetaDataSet metadataCollection, SqlConnection connection, SqlCommand command = null) { - if (_serverSupportsColumnEncryption && ShouldEncryptValuesForBulkCopy()) + if (IsColumnEncryptionSupported && ShouldEncryptValuesForBulkCopy()) { for (int col = 0; col < metadataCollection.Length; col++) { @@ -11409,7 +11396,7 @@ internal void WriteEncryptionEntries(ref SqlTceCipherInfoTable cekTable, TdsPars /// internal void WriteCekTable(_SqlMetaDataSet metadataCollection, TdsParserStateObject stateObj) { - if (!_serverSupportsColumnEncryption) + if (!IsColumnEncryptionSupported) { return; } @@ -11479,7 +11466,7 @@ internal void WriteTceUserTypeAndTypeInfo(SqlMetaDataPriv mdPriv, TdsParserState /// internal void WriteCryptoMetadata(_SqlMetaData md, TdsParserStateObject stateObj) { - if (!_serverSupportsColumnEncryption || // TCE Feature supported + if (!IsColumnEncryptionSupported || // TCE Feature supported !md.isEncrypted || // Column is not encrypted !ShouldEncryptValuesForBulkCopy()) { // TCE disabled on connection string @@ -11546,7 +11533,7 @@ internal void WriteBulkCopyMetaData(_SqlMetaDataSet metadataCollection, int coun flags |= (UInt16)(md.IsIdentity ? (UInt16)TdsEnums.Identity : (UInt16)0); // Write the next byte of flags - if (_serverSupportsColumnEncryption) + if (IsColumnEncryptionSupported) { // TCE Supported if (ShouldEncryptValuesForBulkCopy()) { // TCE enabled on connection options @@ -11636,7 +11623,7 @@ internal bool ShouldEncryptValuesForBulkCopy() /// internal object EncryptColumnValue(object value, SqlMetaDataPriv metadata, string column, TdsParserStateObject stateObj, bool isDataFeed, bool isSqlType) { - Debug.Assert(_serverSupportsColumnEncryption, "Server doesn't support encryption, yet we received encryption metadata"); + Debug.Assert(IsColumnEncryptionSupported, "Server doesn't support encryption, yet we received encryption metadata"); Debug.Assert(ShouldEncryptValuesForBulkCopy(), "Encryption attempted when not requested"); if (isDataFeed) From 402be7c29cc5c33927a299bc0beab31321cf86fb Mon Sep 17 00:00:00 2001 From: Michel Zehnder Date: Tue, 5 Nov 2024 21:24:32 +0100 Subject: [PATCH 02/13] Align code styles --- .../netfx/src/Microsoft/Data/SqlClient/TdsParser.cs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs index 418e5aba58..8b2b32c5d1 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -285,13 +285,8 @@ internal static void Assert(string message) /// /// Get if data classification is enabled by the server. /// - internal bool IsDataClassificationEnabled - { - get - { - return (DataClassificationVersion != TdsEnums.DATA_CLASSIFICATION_NOT_ENABLED); - } - } + internal bool IsDataClassificationEnabled => + (DataClassificationVersion != TdsEnums.DATA_CLASSIFICATION_NOT_ENABLED); /// /// Get or set data classification version. A value of 0 means that sensitivity classification is not enabled. @@ -4333,8 +4328,7 @@ private TdsOperationStatus TryProcessFedAuthInfo(TdsParserStateObject stateObj, // read how many FedAuthInfo options there are uint optionsCount; - TdsOperationStatus result = stateObj.TryReadUInt32(out optionsCount); - if (result != TdsOperationStatus.Done) + if (stateObj.TryReadUInt32(out optionsCount) != TdsOperationStatus.Done) { SqlClientEventSource.Log.TryTraceEvent(" Failed to read CountOfInfoIDs in FEDAUTHINFO token stream."); throw SQL.ParsingError(ParsingErrorState.FedAuthInfoFailedToReadCountOfInfoIds); From 7d31e5a3bc591e3bd84702cfe4f68ea0e85fcef4 Mon Sep 17 00:00:00 2001 From: Michel Zehnder Date: Tue, 5 Nov 2024 21:25:32 +0100 Subject: [PATCH 03/13] Align variable order --- .../netfx/src/Microsoft/Data/SqlClient/TdsParser.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs index 8b2b32c5d1..c82336a33f 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -250,14 +250,13 @@ internal static void Assert(string message) // size of Guid (e.g. _clientConnectionId, ActivityId.Id) private const int GUID_SIZE = 16; private byte[] _tempGuidBytes; - - // NOTE: You must take the internal connection's _parserLock before modifying this - internal bool _asyncWrite = false; - - + // now data length is 1 byte // First bit is 1 indicating client support failover partner with readonly intent private static readonly byte[] s_FeatureExtDataAzureSQLSupportFeatureRequest = { 0x01 }; + + // NOTE: You must take the internal connection's _parserLock before modifying this + internal bool _asyncWrite = false; /// /// Get or set if column encryption is supported by the server. From a816094a08a5c521055f8309000a345c398f1265 Mon Sep 17 00:00:00 2001 From: Michel Zehnder Date: Tue, 5 Nov 2024 21:28:42 +0100 Subject: [PATCH 04/13] Align Task return --- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs index c82336a33f..49f3a97b2c 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -39,18 +39,6 @@ internal sealed partial class TdsParser internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); - static Task completedTask; - static Task CompletedTask - { - get - { - if (completedTask == null) - { - completedTask = Task.FromResult(null); - } - return completedTask; - } - } internal int ObjectID { @@ -12565,7 +12553,7 @@ public override Task WriteAsync(byte[] buffer, int offset, int count, Cancellati } if (task == null) { - return CompletedTask; + return Task.CompletedTask; } else { @@ -12698,7 +12686,7 @@ public override Task WriteAsync(char value) return _next.WriteAsync(value); } - return CompletedTask; + return Task.CompletedTask; } public override Task WriteAsync(char[] buffer, int index, int count) @@ -12713,7 +12701,7 @@ public override Task WriteAsync(char[] buffer, int index, int count) return _next.WriteAsync(buffer, index, count); } - return CompletedTask; + return Task.CompletedTask; } public override Task WriteAsync(string value) From 0a7272ff883ef8acfc77cd9bcac267f82b0daec9 Mon Sep 17 00:00:00 2001 From: Michel Zehnder Date: Tue, 5 Nov 2024 21:39:55 +0100 Subject: [PATCH 05/13] Coding style alignments --- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 31 ++++++++----------- 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs index 49f3a97b2c..eb93ae868d 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -10449,15 +10449,15 @@ internal Task TdsExecuteRPC(SqlCommand cmd, IList<_SqlRPC> rpcArray, int timeout String[] names = SqlParameter.ParseTypeName(param.UdtTypeName, isUdtTypeName: true); if (!ADP.IsEmpty(names[0]) && TdsEnums.MAX_SERVERNAME < names[0].Length) { - throw ADP.ArgumentOutOfRange("names"); + throw ADP.ArgumentOutOfRange(nameof(names)); } if (!ADP.IsEmpty(names[1]) && TdsEnums.MAX_SERVERNAME < names[names.Length - 2].Length) { - throw ADP.ArgumentOutOfRange("names"); + throw ADP.ArgumentOutOfRange(nameof(names)); } if (TdsEnums.MAX_SERVERNAME < names[2].Length) { - throw ADP.ArgumentOutOfRange("names"); + throw ADP.ArgumentOutOfRange(nameof(names)); } WriteUDTMetaData(value, names[0], names[1], names[2], stateObj); @@ -10512,7 +10512,9 @@ internal Task TdsExecuteRPC(SqlCommand cmd, IList<_SqlRPC> rpcArray, int timeout stateObj.WriteByte(TdsEnums.SQL70_DEFAULT_NUMERIC_PRECISION); } else + { stateObj.WriteByte(precision); + } stateObj.WriteByte(scale); } @@ -10898,7 +10900,6 @@ private void WriteParameterName(string rawParameterName, TdsParserStateObject st } } - private static readonly IEnumerable __tvpEmptyValue = new List().AsReadOnly(); private void WriteSmiParameter(SqlParameter param, int paramIndex, bool sendDefault, TdsParserStateObject stateObj, bool isAnonymous, bool advancedTraceIsOn) { // @@ -10925,7 +10926,7 @@ private void WriteSmiParameter(SqlParameter param, int paramIndex, bool sendDefa // Value for TVP default is empty list, not NULL if (SqlDbType.Structured == metaData.SqlDbType && metaData.IsMultiValued) { - value = __tvpEmptyValue; + value = Array.Empty(); typeCode = ExtendedClrTypeCode.IEnumerableOfSqlDataRecord; } else @@ -11949,25 +11950,25 @@ private int GetNotificationHeaderSize(SqlNotificationRequest notificationRequest if (callbackId == null) { - throw ADP.ArgumentNull("CallbackId"); + throw ADP.ArgumentNull(nameof(callbackId)); } else if (UInt16.MaxValue < callbackId.Length) { - throw ADP.ArgumentOutOfRange("CallbackId"); + throw ADP.ArgumentOutOfRange(nameof(callbackId)); } if (service == null) { - throw ADP.ArgumentNull("Service"); + throw ADP.ArgumentNull(nameof(service)); } else if (UInt16.MaxValue < service.Length) { - throw ADP.ArgumentOutOfRange("Service"); + throw ADP.ArgumentOutOfRange(nameof(service)); } if (-1 > timeout) { - throw ADP.ArgumentOutOfRange("Timeout"); + throw ADP.ArgumentOutOfRange(nameof(timeout)); } // Header Length (uint) (included in size) (already written to output buffer) @@ -12551,14 +12552,8 @@ public override Task WriteAsync(byte[] buffer, int offset, int count, Cancellati _parser.WriteInt(count, _stateObj); // write length of chunk task = _stateObj.WriteByteArray(buffer, count, offset, canAccumulate: false); } - if (task == null) - { - return Task.CompletedTask; - } - else - { - return task; - } + + return task ?? Task.CompletedTask; } #if DEBUG finally From 719c09619dfcb94fd8127b514e032d6725ab84f9 Mon Sep 17 00:00:00 2001 From: Michel Zehnder Date: Tue, 5 Nov 2024 21:05:47 +0100 Subject: [PATCH 06/13] Align IsColumnEncryptionSupported between netfx and netcore --- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 35 ++++++------------- 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs index 4ccaf1ca18..418e5aba58 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -254,9 +254,6 @@ internal static void Assert(string message) // NOTE: You must take the internal connection's _parserLock before modifying this internal bool _asyncWrite = false; - // TCE supported flag, used to determine if new TDS fields are present. This is - // useful when talking to downlevel/uplevel server. - private bool _serverSupportsColumnEncryption = false; // now data length is 1 byte // First bit is 1 indicating client support failover partner with readonly intent @@ -265,17 +262,7 @@ internal static void Assert(string message) /// /// Get or set if column encryption is supported by the server. /// - internal bool IsColumnEncryptionSupported - { - get - { - return _serverSupportsColumnEncryption; - } - set - { - _serverSupportsColumnEncryption = value; - } - } + internal bool IsColumnEncryptionSupported { get; set; } = false; /// /// TCE version supported by the server @@ -4670,7 +4657,7 @@ internal TdsOperationStatus TryProcessReturnValue(int length, } // Check if the column is encrypted. - if (_serverSupportsColumnEncryption) + if (IsColumnEncryptionSupported) { rec.isEncrypted = (TdsEnums.IsEncrypted == (flags & TdsEnums.IsEncrypted)); } @@ -4854,7 +4841,7 @@ internal TdsOperationStatus TryProcessReturnValue(int length, } // For encrypted parameters, read the unencrypted type and encryption information. - if (_serverSupportsColumnEncryption && rec.isEncrypted) + if (IsColumnEncryptionSupported && rec.isEncrypted) { result = TryProcessTceCryptoMetadata(stateObj, rec, cipherTable: null, columnEncryptionSetting: columnEncryptionSetting, isReturnValue: true); if (result != TdsOperationStatus.Done) @@ -5576,7 +5563,7 @@ internal TdsOperationStatus TryProcessMetaData(int cColumns, TdsParserStateObjec // Read the cipher info table first SqlTceCipherInfoTable cipherTable = null; - if (_serverSupportsColumnEncryption) + if (IsColumnEncryptionSupported) { TdsOperationStatus result = TryProcessCipherInfoTable(stateObj, out cipherTable); if (result != TdsOperationStatus.Done) @@ -5853,7 +5840,7 @@ private TdsOperationStatus TryCommonProcessMetaData(TdsParserStateObject stateOb } col.IsColumnSet = (TdsEnums.IsColumnSet == (flags & TdsEnums.IsColumnSet)); - if (fColMD && _serverSupportsColumnEncryption) + if (fColMD && IsColumnEncryptionSupported) { col.isEncrypted = (TdsEnums.IsEncrypted == (flags & TdsEnums.IsEncrypted)); } @@ -5900,7 +5887,7 @@ private TdsOperationStatus TryCommonProcessMetaData(TdsParserStateObject stateOb } // Read the TCE column cryptoinfo - if (fColMD && _serverSupportsColumnEncryption && col.isEncrypted) + if (fColMD && IsColumnEncryptionSupported && col.isEncrypted) { // If the column is encrypted, we should have a valid cipherTable if (cipherTable != null) @@ -11361,7 +11348,7 @@ internal Task WriteBulkCopyDone(TdsParserStateObject stateObj) /// internal void LoadColumnEncryptionKeys(_SqlMetaDataSet metadataCollection, SqlConnection connection, SqlCommand command = null) { - if (_serverSupportsColumnEncryption && ShouldEncryptValuesForBulkCopy()) + if (IsColumnEncryptionSupported && ShouldEncryptValuesForBulkCopy()) { for (int col = 0; col < metadataCollection.Length; col++) { @@ -11409,7 +11396,7 @@ internal void WriteEncryptionEntries(ref SqlTceCipherInfoTable cekTable, TdsPars /// internal void WriteCekTable(_SqlMetaDataSet metadataCollection, TdsParserStateObject stateObj) { - if (!_serverSupportsColumnEncryption) + if (!IsColumnEncryptionSupported) { return; } @@ -11479,7 +11466,7 @@ internal void WriteTceUserTypeAndTypeInfo(SqlMetaDataPriv mdPriv, TdsParserState /// internal void WriteCryptoMetadata(_SqlMetaData md, TdsParserStateObject stateObj) { - if (!_serverSupportsColumnEncryption || // TCE Feature supported + if (!IsColumnEncryptionSupported || // TCE Feature supported !md.isEncrypted || // Column is not encrypted !ShouldEncryptValuesForBulkCopy()) { // TCE disabled on connection string @@ -11546,7 +11533,7 @@ internal void WriteBulkCopyMetaData(_SqlMetaDataSet metadataCollection, int coun flags |= (UInt16)(md.IsIdentity ? (UInt16)TdsEnums.Identity : (UInt16)0); // Write the next byte of flags - if (_serverSupportsColumnEncryption) + if (IsColumnEncryptionSupported) { // TCE Supported if (ShouldEncryptValuesForBulkCopy()) { // TCE enabled on connection options @@ -11636,7 +11623,7 @@ internal bool ShouldEncryptValuesForBulkCopy() /// internal object EncryptColumnValue(object value, SqlMetaDataPriv metadata, string column, TdsParserStateObject stateObj, bool isDataFeed, bool isSqlType) { - Debug.Assert(_serverSupportsColumnEncryption, "Server doesn't support encryption, yet we received encryption metadata"); + Debug.Assert(IsColumnEncryptionSupported, "Server doesn't support encryption, yet we received encryption metadata"); Debug.Assert(ShouldEncryptValuesForBulkCopy(), "Encryption attempted when not requested"); if (isDataFeed) From a1b38203654cc9c5af61106dd1277ddba0be5ece Mon Sep 17 00:00:00 2001 From: Michel Zehnder Date: Tue, 5 Nov 2024 21:24:32 +0100 Subject: [PATCH 07/13] Align code styles --- .../netfx/src/Microsoft/Data/SqlClient/TdsParser.cs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs index 418e5aba58..8b2b32c5d1 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -285,13 +285,8 @@ internal static void Assert(string message) /// /// Get if data classification is enabled by the server. /// - internal bool IsDataClassificationEnabled - { - get - { - return (DataClassificationVersion != TdsEnums.DATA_CLASSIFICATION_NOT_ENABLED); - } - } + internal bool IsDataClassificationEnabled => + (DataClassificationVersion != TdsEnums.DATA_CLASSIFICATION_NOT_ENABLED); /// /// Get or set data classification version. A value of 0 means that sensitivity classification is not enabled. @@ -4333,8 +4328,7 @@ private TdsOperationStatus TryProcessFedAuthInfo(TdsParserStateObject stateObj, // read how many FedAuthInfo options there are uint optionsCount; - TdsOperationStatus result = stateObj.TryReadUInt32(out optionsCount); - if (result != TdsOperationStatus.Done) + if (stateObj.TryReadUInt32(out optionsCount) != TdsOperationStatus.Done) { SqlClientEventSource.Log.TryTraceEvent(" Failed to read CountOfInfoIDs in FEDAUTHINFO token stream."); throw SQL.ParsingError(ParsingErrorState.FedAuthInfoFailedToReadCountOfInfoIds); From 03b5413081cc244049f19e68e77da439032b71df Mon Sep 17 00:00:00 2001 From: Michel Zehnder Date: Tue, 5 Nov 2024 21:25:32 +0100 Subject: [PATCH 08/13] Align variable order --- .../netfx/src/Microsoft/Data/SqlClient/TdsParser.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs index 8b2b32c5d1..c82336a33f 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -250,14 +250,13 @@ internal static void Assert(string message) // size of Guid (e.g. _clientConnectionId, ActivityId.Id) private const int GUID_SIZE = 16; private byte[] _tempGuidBytes; - - // NOTE: You must take the internal connection's _parserLock before modifying this - internal bool _asyncWrite = false; - - + // now data length is 1 byte // First bit is 1 indicating client support failover partner with readonly intent private static readonly byte[] s_FeatureExtDataAzureSQLSupportFeatureRequest = { 0x01 }; + + // NOTE: You must take the internal connection's _parserLock before modifying this + internal bool _asyncWrite = false; /// /// Get or set if column encryption is supported by the server. From 96259129446f25f1fbd66014b13c0bc1f31de166 Mon Sep 17 00:00:00 2001 From: Michel Zehnder Date: Tue, 5 Nov 2024 21:28:42 +0100 Subject: [PATCH 09/13] Align Task return --- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs index c82336a33f..49f3a97b2c 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -39,18 +39,6 @@ internal sealed partial class TdsParser internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); - static Task completedTask; - static Task CompletedTask - { - get - { - if (completedTask == null) - { - completedTask = Task.FromResult(null); - } - return completedTask; - } - } internal int ObjectID { @@ -12565,7 +12553,7 @@ public override Task WriteAsync(byte[] buffer, int offset, int count, Cancellati } if (task == null) { - return CompletedTask; + return Task.CompletedTask; } else { @@ -12698,7 +12686,7 @@ public override Task WriteAsync(char value) return _next.WriteAsync(value); } - return CompletedTask; + return Task.CompletedTask; } public override Task WriteAsync(char[] buffer, int index, int count) @@ -12713,7 +12701,7 @@ public override Task WriteAsync(char[] buffer, int index, int count) return _next.WriteAsync(buffer, index, count); } - return CompletedTask; + return Task.CompletedTask; } public override Task WriteAsync(string value) From efece9e56a2998dd6882a6461f3ae0d6987315bd Mon Sep 17 00:00:00 2001 From: Michel Zehnder Date: Tue, 5 Nov 2024 21:39:55 +0100 Subject: [PATCH 10/13] Coding style alignments --- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 31 ++++++++----------- 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs index 49f3a97b2c..eb93ae868d 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -10449,15 +10449,15 @@ internal Task TdsExecuteRPC(SqlCommand cmd, IList<_SqlRPC> rpcArray, int timeout String[] names = SqlParameter.ParseTypeName(param.UdtTypeName, isUdtTypeName: true); if (!ADP.IsEmpty(names[0]) && TdsEnums.MAX_SERVERNAME < names[0].Length) { - throw ADP.ArgumentOutOfRange("names"); + throw ADP.ArgumentOutOfRange(nameof(names)); } if (!ADP.IsEmpty(names[1]) && TdsEnums.MAX_SERVERNAME < names[names.Length - 2].Length) { - throw ADP.ArgumentOutOfRange("names"); + throw ADP.ArgumentOutOfRange(nameof(names)); } if (TdsEnums.MAX_SERVERNAME < names[2].Length) { - throw ADP.ArgumentOutOfRange("names"); + throw ADP.ArgumentOutOfRange(nameof(names)); } WriteUDTMetaData(value, names[0], names[1], names[2], stateObj); @@ -10512,7 +10512,9 @@ internal Task TdsExecuteRPC(SqlCommand cmd, IList<_SqlRPC> rpcArray, int timeout stateObj.WriteByte(TdsEnums.SQL70_DEFAULT_NUMERIC_PRECISION); } else + { stateObj.WriteByte(precision); + } stateObj.WriteByte(scale); } @@ -10898,7 +10900,6 @@ private void WriteParameterName(string rawParameterName, TdsParserStateObject st } } - private static readonly IEnumerable __tvpEmptyValue = new List().AsReadOnly(); private void WriteSmiParameter(SqlParameter param, int paramIndex, bool sendDefault, TdsParserStateObject stateObj, bool isAnonymous, bool advancedTraceIsOn) { // @@ -10925,7 +10926,7 @@ private void WriteSmiParameter(SqlParameter param, int paramIndex, bool sendDefa // Value for TVP default is empty list, not NULL if (SqlDbType.Structured == metaData.SqlDbType && metaData.IsMultiValued) { - value = __tvpEmptyValue; + value = Array.Empty(); typeCode = ExtendedClrTypeCode.IEnumerableOfSqlDataRecord; } else @@ -11949,25 +11950,25 @@ private int GetNotificationHeaderSize(SqlNotificationRequest notificationRequest if (callbackId == null) { - throw ADP.ArgumentNull("CallbackId"); + throw ADP.ArgumentNull(nameof(callbackId)); } else if (UInt16.MaxValue < callbackId.Length) { - throw ADP.ArgumentOutOfRange("CallbackId"); + throw ADP.ArgumentOutOfRange(nameof(callbackId)); } if (service == null) { - throw ADP.ArgumentNull("Service"); + throw ADP.ArgumentNull(nameof(service)); } else if (UInt16.MaxValue < service.Length) { - throw ADP.ArgumentOutOfRange("Service"); + throw ADP.ArgumentOutOfRange(nameof(service)); } if (-1 > timeout) { - throw ADP.ArgumentOutOfRange("Timeout"); + throw ADP.ArgumentOutOfRange(nameof(timeout)); } // Header Length (uint) (included in size) (already written to output buffer) @@ -12551,14 +12552,8 @@ public override Task WriteAsync(byte[] buffer, int offset, int count, Cancellati _parser.WriteInt(count, _stateObj); // write length of chunk task = _stateObj.WriteByteArray(buffer, count, offset, canAccumulate: false); } - if (task == null) - { - return Task.CompletedTask; - } - else - { - return task; - } + + return task ?? Task.CompletedTask; } #if DEBUG finally From f32dd0356c16810fb788baf46f9be0040b17c427 Mon Sep 17 00:00:00 2001 From: Michel Zehnder Date: Wed, 6 Nov 2024 21:46:13 +0100 Subject: [PATCH 11/13] Align Array renting netcore/netfx --- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 80 ++++++++++++++----- 1 file changed, 61 insertions(+), 19 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs index eb93ae868d..1e114bc459 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -6710,19 +6710,35 @@ private TdsOperationStatus TryReadSqlStringValue(SqlBuffer value, byte type, int if (isPlp) { char[] cc = null; - - result = TryReadPlpUnicodeChars(ref cc, 0, length >> 1, stateObj, out length); - if (result != TdsOperationStatus.Done) + bool buffIsRented = false; + result = TryReadPlpUnicodeChars(ref cc, 0, length >> 1, stateObj, out length, supportRentedBuff: true, rentedBuff: ref buffIsRented); + + if (result == TdsOperationStatus.Done) { - return result; + if (length > 0) + { + s = new string(cc, 0, length); + } + else + { + s = ""; + } } - if (length > 0) + + if (buffIsRented) { - s = new string(cc, 0, length); + // do not use clearArray:true on the rented array because it can be massively larger + // than the space we've used and we would incur performance clearing memory that + // we haven't used and can't leak out information. + // clear only the length that we know we have used. + cc.AsSpan(0, length).Clear(); + ArrayPool.Shared.Return(cc, clearArray: false); + cc = null; } - else + + if (result != TdsOperationStatus.Done) { - s = ""; + return result; } } else @@ -13680,8 +13696,9 @@ private TdsOperationStatus TryReadPlpUnicodeCharsChunk(char[] buff, int offst, i internal int ReadPlpUnicodeChars(ref char[] buff, int offst, int len, TdsParserStateObject stateObj) { int charsRead; + bool rentedBuff = false; Debug.Assert(stateObj._syncOverAsync, "Should not attempt pends in a synchronous call"); - TdsOperationStatus result = TryReadPlpUnicodeChars(ref buff, offst, len, stateObj, out charsRead); + TdsOperationStatus result = TryReadPlpUnicodeChars(ref buff, offst, len, stateObj, out charsRead, supportRentedBuff: false, ref rentedBuff); if (result != TdsOperationStatus.Done) { throw SQL.SynchronousCallMayNotPend(); @@ -13693,13 +13710,12 @@ internal int ReadPlpUnicodeChars(ref char[] buff, int offst, int len, TdsParserS // requested length is -1 or larger than the actual length of data. First call to this method // should be preceeded by a call to ReadPlpLength or ReadDataLength. // Returns the actual chars read. - internal TdsOperationStatus TryReadPlpUnicodeChars(ref char[] buff, int offst, int len, TdsParserStateObject stateObj, out int totalCharsRead) + internal TdsOperationStatus TryReadPlpUnicodeChars(ref char[] buff, int offst, int len, TdsParserStateObject stateObj, out int totalCharsRead, bool supportRentedBuff, ref bool rentedBuff) { int charsRead = 0; int charsLeft = 0; char[] newbuf; - TdsOperationStatus result; - + if (stateObj._longlen == 0) { Debug.Assert(stateObj._longlenleft == 0); @@ -13707,18 +13723,29 @@ internal TdsOperationStatus TryReadPlpUnicodeChars(ref char[] buff, int offst, i return TdsOperationStatus.Done; // No data } - Debug.Assert(((ulong)stateObj._longlen != TdsEnums.SQL_PLP_NULL), - "Out of sync plp read request"); + Debug.Assert(((ulong)stateObj._longlen != TdsEnums.SQL_PLP_NULL), "Out of sync plp read request"); Debug.Assert((buff == null && offst == 0) || (buff.Length >= offst + len), "Invalid length sent to ReadPlpUnicodeChars()!"); charsLeft = len; - // If total length is known up front, allocate the whole buffer in one shot instead of realloc'ing and copying over each time - if (buff == null && stateObj._longlen != TdsEnums.SQL_PLP_UNKNOWNLEN) + // If total length is known up front, the length isn't specified as unknown + // and the caller doesn't pass int.max/2 indicating that it doesn't know the length + // allocate the whole buffer in one shot instead of realloc'ing and copying over each time + if (buff == null && stateObj._longlen != TdsEnums.SQL_PLP_UNKNOWNLEN && len < (int.MaxValue >> 1)) { - buff = new char[(int)Math.Min((int)stateObj._longlen, len)]; + if (supportRentedBuff && len < 1073741824) // 1 Gib + { + buff = ArrayPool.Shared.Rent((int)Math.Min((int)stateObj._longlen, len)); + rentedBuff = true; + } + else + { + buff = new char[(int)Math.Min((int)stateObj._longlen, len)]; + rentedBuff = false; + } } + TdsOperationStatus result; if (stateObj._longlenleft == 0) { result = stateObj.TryReadPlpLength(false, out _); @@ -13740,11 +13767,26 @@ internal TdsOperationStatus TryReadPlpUnicodeChars(ref char[] buff, int offst, i charsRead = (int)Math.Min((stateObj._longlenleft + 1) >> 1, (ulong)charsLeft); if ((buff == null) || (buff.Length < (offst + charsRead))) { - // Grow the array - newbuf = new char[offst + charsRead]; + bool returnRentedBufferAfterCopy = rentedBuff; + if (supportRentedBuff && (offst + charsRead) < 1073741824) // 1 Gib + { + newbuf = ArrayPool.Shared.Rent(offst + charsRead); + rentedBuff = true; + } + else + { + newbuf = new char[offst + charsRead]; + rentedBuff = false; + } + if (buff != null) { Buffer.BlockCopy(buff, 0, newbuf, 0, offst * 2); + if (returnRentedBufferAfterCopy) + { + buff.AsSpan(0, offst).Clear(); + ArrayPool.Shared.Return(buff, clearArray: false); + } } buff = newbuf; } From 33dc5576670e83d6de3bbd33b22348c3001a7a10 Mon Sep 17 00:00:00 2001 From: Michel Zehnder Date: Thu, 7 Nov 2024 10:18:45 +0100 Subject: [PATCH 12/13] Align more ArrayPool usages --- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 160 +++++++++--------- 1 file changed, 84 insertions(+), 76 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs index 1e114bc459..bd67062786 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -12807,119 +12807,127 @@ private async Task WriteXmlFeed(XmlDataFeed feed, TdsParserStateObject stateObj, private async Task WriteTextFeed(TextDataFeed feed, Encoding encoding, bool needBom, TdsParserStateObject stateObj, int size, bool useReadBlock) { Debug.Assert(encoding == null || !needBom); - char[] inBuff = new char[constTextBufferSize]; + char[] inBuff = ArrayPool.Shared.Rent(constTextBufferSize); encoding = encoding ?? new UnicodeEncoding(false, false); - ConstrainedTextWriter writer = new ConstrainedTextWriter(new StreamWriter(new TdsOutputStream(this, stateObj, null), encoding), size); - - if (needBom) + + using (ConstrainedTextWriter writer = new ConstrainedTextWriter(new StreamWriter(new TdsOutputStream(this, stateObj, null), encoding), size)) { - if (_asyncWrite) - { - await writer.WriteAsync((char)TdsEnums.XMLUNICODEBOM).ConfigureAwait(false); - } - else + if (needBom) { - writer.Write((char)TdsEnums.XMLUNICODEBOM); + if (_asyncWrite) + { + await writer.WriteAsync((char)TdsEnums.XMLUNICODEBOM).ConfigureAwait(false); + } + else + { + writer.Write((char)TdsEnums.XMLUNICODEBOM); + } } - } - - int nWritten = 0; - do - { - int nRead = 0; - if (_asyncWrite) + int nWritten = 0; + do { - if (useReadBlock) + int nRead = 0; + + if (_asyncWrite) { - nRead = await feed._source.ReadBlockAsync(inBuff, 0, constTextBufferSize).ConfigureAwait(false); + if (useReadBlock) + { + nRead = await feed._source.ReadBlockAsync(inBuff, 0, constTextBufferSize).ConfigureAwait(false); + } + else + { + nRead = await feed._source.ReadAsync(inBuff, 0, constTextBufferSize).ConfigureAwait(false); + } } else { - nRead = await feed._source.ReadAsync(inBuff, 0, constTextBufferSize).ConfigureAwait(false); + if (useReadBlock) + { + nRead = feed._source.ReadBlock(inBuff, 0, constTextBufferSize); + } + else + { + nRead = feed._source.Read(inBuff, 0, constTextBufferSize); + } } - } - else - { - if (useReadBlock) + + if (nRead == 0) + { + break; + } + + if (_asyncWrite) { - nRead = feed._source.ReadBlock(inBuff, 0, constTextBufferSize); + await writer.WriteAsync(inBuff, 0, nRead).ConfigureAwait(false); } else { - nRead = feed._source.Read(inBuff, 0, constTextBufferSize); + writer.Write(inBuff, 0, nRead); } - } - if (nRead == 0) - { - break; - } + nWritten += nRead; + } while (!writer.IsComplete); if (_asyncWrite) { - await writer.WriteAsync(inBuff, 0, nRead).ConfigureAwait(false); + await writer.FlushAsync().ConfigureAwait(false); } else { - writer.Write(inBuff, 0, nRead); + writer.Flush(); } - - nWritten += nRead; - } while (!writer.IsComplete); - - if (_asyncWrite) - { - await writer.FlushAsync().ConfigureAwait(false); - } - else - { - writer.Flush(); } + ArrayPool.Shared.Return(inBuff, clearArray: true); } private async Task WriteStreamFeed(StreamDataFeed feed, TdsParserStateObject stateObj, int len) { - TdsOutputStream output = new TdsOutputStream(this, stateObj, null); - byte[] buff = new byte[constBinBufferSize]; - int nWritten = 0; - do + byte[] buff = ArrayPool.Shared.Rent(constBinBufferSize); + + using (TdsOutputStream output = new TdsOutputStream(this, stateObj, null)) { - int nRead = 0; - int readSize = constBinBufferSize; - if (len > 0 && nWritten + readSize > len) + int nWritten = 0; + do { - readSize = len - nWritten; - } + int nRead = 0; + int readSize = constBinBufferSize; + if (len > 0 && nWritten + readSize > len) + { + readSize = len - nWritten; + } - Debug.Assert(readSize >= 0); + Debug.Assert(readSize >= 0); - if (_asyncWrite) - { - nRead = await feed._source.ReadAsync(buff, 0, readSize).ConfigureAwait(false); - } - else - { - nRead = feed._source.Read(buff, 0, readSize); - } + if (_asyncWrite) + { + nRead = await feed._source.ReadAsync(buff, 0, readSize).ConfigureAwait(false); + } + else + { + nRead = feed._source.Read(buff, 0, readSize); + } - if (nRead == 0) - { - return; - } + if (nRead == 0) + { + return; + } - if (_asyncWrite) - { - await output.WriteAsync(buff, 0, nRead).ConfigureAwait(false); - } - else - { - output.Write(buff, 0, nRead); - } + if (_asyncWrite) + { + await output.WriteAsync(buff, 0, nRead).ConfigureAwait(false); + } + else + { + output.Write(buff, 0, nRead); + } + + nWritten += nRead; + } while (len <= 0 || nWritten < len); + } - nWritten += nRead; - } while (len <= 0 || nWritten < len); + ArrayPool.Shared.Return(buff, clearArray: true); } private Task NullIfCompletedWriteTask(Task task) From 43a4fd6a2399178ccd068ca332563dc494d526c2 Mon Sep 17 00:00:00 2001 From: Michel Zehnder Date: Fri, 15 Nov 2024 09:40:09 +0100 Subject: [PATCH 13/13] Fix code review suggestions --- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 13 ++++++++++--- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 17 ++++++++++++----- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs index 8718aa1879..a5419e040e 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -6013,7 +6013,7 @@ private TdsOperationStatus TryReadSqlStringValue(SqlBuffer value, byte type, int } else { - s = ""; + s = string.Empty; } } @@ -12859,7 +12859,14 @@ internal int ReadPlpUnicodeChars(ref char[] buff, int offst, int len, TdsParserS // requested length is -1 or larger than the actual length of data. First call to this method // should be preceeded by a call to ReadPlpLength or ReadDataLength. // Returns the actual chars read. - internal TdsOperationStatus TryReadPlpUnicodeChars(ref char[] buff, int offst, int len, TdsParserStateObject stateObj, out int totalCharsRead, bool supportRentedBuff, ref bool rentedBuff) + internal TdsOperationStatus TryReadPlpUnicodeChars( + ref char[] buff, + int offst, + int len, + TdsParserStateObject stateObj, + out int totalCharsRead, + bool supportRentedBuff, + ref bool rentedBuff) { int charsRead = 0; int charsLeft = 0; @@ -12872,7 +12879,7 @@ internal TdsOperationStatus TryReadPlpUnicodeChars(ref char[] buff, int offst, i return TdsOperationStatus.Done; // No data } - Debug.Assert(((ulong)stateObj._longlen != TdsEnums.SQL_PLP_NULL), "Out of sync plp read request"); + Debug.Assert((ulong)stateObj._longlen != TdsEnums.SQL_PLP_NULL, "Out of sync plp read request"); Debug.Assert((buff == null && offst == 0) || (buff.Length >= offst + len), "Invalid length sent to ReadPlpUnicodeChars()!"); charsLeft = len; diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs index bd67062786..0490c8aa48 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -6721,7 +6721,7 @@ private TdsOperationStatus TryReadSqlStringValue(SqlBuffer value, byte type, int } else { - s = ""; + s = string.Empty; } } @@ -10528,9 +10528,9 @@ internal Task TdsExecuteRPC(SqlCommand cmd, IList<_SqlRPC> rpcArray, int timeout stateObj.WriteByte(TdsEnums.SQL70_DEFAULT_NUMERIC_PRECISION); } else - { + { stateObj.WriteByte(precision); - } + } stateObj.WriteByte(scale); } @@ -13718,7 +13718,14 @@ internal int ReadPlpUnicodeChars(ref char[] buff, int offst, int len, TdsParserS // requested length is -1 or larger than the actual length of data. First call to this method // should be preceeded by a call to ReadPlpLength or ReadDataLength. // Returns the actual chars read. - internal TdsOperationStatus TryReadPlpUnicodeChars(ref char[] buff, int offst, int len, TdsParserStateObject stateObj, out int totalCharsRead, bool supportRentedBuff, ref bool rentedBuff) + internal TdsOperationStatus TryReadPlpUnicodeChars( + ref char[] buff, + int offst, + int len, + TdsParserStateObject stateObj, + out int totalCharsRead, + bool supportRentedBuff, + ref bool rentedBuff) { int charsRead = 0; int charsLeft = 0; @@ -13731,7 +13738,7 @@ internal TdsOperationStatus TryReadPlpUnicodeChars(ref char[] buff, int offst, i return TdsOperationStatus.Done; // No data } - Debug.Assert(((ulong)stateObj._longlen != TdsEnums.SQL_PLP_NULL), "Out of sync plp read request"); + Debug.Assert((ulong)stateObj._longlen != TdsEnums.SQL_PLP_NULL, "Out of sync plp read request"); Debug.Assert((buff == null && offst == 0) || (buff.Length >= offst + len), "Invalid length sent to ReadPlpUnicodeChars()!"); charsLeft = len;