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 cf53e8ecff..8fbeb3050c 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 @@ -13055,6 +13055,8 @@ internal TdsOperationStatus TryReadPlpUnicodeCharsWithContinue(TdsParserStateObj char[] temp = null; bool buffIsRented = false; int startOffset = 0; + + stateObj.RequestContinue(true); (bool canContinue, bool isStarting, bool isContinuing) = stateObj.GetSnapshotStatuses(); if (canContinue) @@ -13149,7 +13151,7 @@ bool writeDataSizeToSnapshot if (stateObj._longlen == 0) { Debug.Assert(stateObj._longlenleft == 0); - totalCharsRead = 0; + totalCharsRead = startOffsetByteCount / 2; return TdsOperationStatus.Done; // No data } @@ -13169,14 +13171,15 @@ bool writeDataSizeToSnapshot // later needing to repeatedly allocate new target buffers and copy data as we discover new data if (buff == null && stateObj._longlen != TdsEnums.SQL_PLP_UNKNOWNLEN && stateObj._longlen < (int.MaxValue >> 1)) { - if (supportRentedBuff && stateObj._longlen < 1073741824) // 1 Gib + int stateLen = (int)stateObj._longlen >> 1; + if (supportRentedBuff && stateLen < 1073741824) // 1 Gib { - buff = ArrayPool.Shared.Rent((int)Math.Min((int)stateObj._longlen, len)); + buff = ArrayPool.Shared.Rent(Math.Min(stateLen, len)); rentedBuff = true; } else { - buff = new char[(int)Math.Min((int)stateObj._longlen, len)]; + buff = new char[Math.Min(stateLen, len)]; rentedBuff = false; } } 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 d65b0db83e..d38190a359 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 @@ -13312,7 +13312,7 @@ bool writeDataSizeToSnapshot if (stateObj._longlen == 0) { Debug.Assert(stateObj._longlenleft == 0); - totalCharsRead = 0; + totalCharsRead = startOffsetByteCount / 2; return TdsOperationStatus.Done; // No data } @@ -13327,19 +13327,20 @@ bool writeDataSizeToSnapshot ); charsLeft = len; - // 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 total data length is known up front from the plp header by being not SQL_PLP_UNKNOWNLEN + // and the number of chars required is less than int.max/2 allocate the entire buffer now to avoid + // later needing to repeatedly allocate new target buffers and copy data as we discover new data if (buff == null && stateObj._longlen != TdsEnums.SQL_PLP_UNKNOWNLEN && stateObj._longlen < (int.MaxValue >> 1)) { - if (supportRentedBuff && stateObj._longlen < 1073741824) // 1 Gib + int stateLen = (int)stateObj._longlen >> 1; + if (supportRentedBuff && stateLen < 1073741824) // 1 Gib { - buff = ArrayPool.Shared.Rent((int)Math.Min((int)stateObj._longlen, len)); + buff = ArrayPool.Shared.Rent(Math.Min(stateLen, len)); rentedBuff = true; } else { - buff = new char[(int)Math.Min((int)stateObj._longlen, len)]; + buff = new char[Math.Min(stateLen, len)]; rentedBuff = false; } } 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 82ef37fb6b..a03eefce09 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCachedBuffer.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCachedBuffer.cs @@ -39,10 +39,10 @@ internal static TdsOperationStatus TryCreate(SqlMetaDataPriv metadata, TdsParser { buffer = null; - (bool canContinue, bool isStarting, _) = stateObj.GetSnapshotStatuses(); + (bool canContinue, bool isStarting, bool isContinuing) = stateObj.GetSnapshotStatuses(); List cachedBytes = null; - if (canContinue) + if (canContinue && isContinuing) { cachedBytes = stateObj.TryTakeSnapshotStorage() as List; if (isStarting) @@ -81,7 +81,7 @@ internal static TdsOperationStatus TryCreate(SqlMetaDataPriv metadata, TdsParser result = stateObj.TryReadPlpBytes(ref byteArr, 0, cb, out cb, canContinue, writeDataSizeToSnapshot: false, compatibilityMode: false); if (result != TdsOperationStatus.Done) { - if (result == TdsOperationStatus.NeedMoreData && canContinue && cb == byteArr.Length) + if (result == TdsOperationStatus.NeedMoreData && canContinue && cb == byteArr.Length && (isContinuing || !isStarting)) { // succeeded in getting the data but failed to find the next plp length returnAfterAdd = true; 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 5e37fc1a47..22da8a07a3 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs @@ -4238,9 +4238,7 @@ private TdsOperationStatus TryResetBlobState() else { Debug.Assert( - (_sharedState._columnDataBytesRemaining == 0 || _sharedState._columnDataBytesRemaining == -1) - && - (_stateObj._longlen == 0 || _stateObj.IsSnapshotContinuing()), + (_sharedState._columnDataBytesRemaining == 0 || _sharedState._columnDataBytesRemaining == -1), "Haven't read header yet, but column is partially read?" ); } @@ -5395,6 +5393,10 @@ private static Task GetFieldValueAsyncExecute(Task task, object state) { return Task.FromResult(reader.GetFieldValueFromSqlBufferInternal(reader._data[columnIndex], reader._metaData[columnIndex], isAsync: true)); } + else + { + return reader.ExecuteAsyncCall(context); + } } } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.Multiplexer.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.Multiplexer.cs index 77bd1c982b..9f0627f623 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.Multiplexer.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.Multiplexer.cs @@ -41,6 +41,10 @@ public void ProcessSniPacket(PacketHandle packet, uint error) if (PartialPacketContainsCompletePacket()) { Packet partialPacket = _partialPacket; + // the partial packet can contain more than a single packet worth of data so to consume the + // partial packet we must use the CurrentLength not just the RequiredLength and then later + // the multiplexer will split out the complete packet for consumption and maintain the + // additional data SetBuffer(partialPacket.Buffer, 0, partialPacket.CurrentLength); ClearPartialPacket(); getDataError = TdsEnums.SNI_SUCCESS; @@ -50,7 +54,7 @@ public void ProcessSniPacket(PacketHandle packet, uint error) { if (_inBytesRead != 0) { - SetBuffer(new byte[_inBuff.Length], 0, 0); + NewBuffer(_inBuff.Length); } getDataError = GetSniPacket(packet, ref dataSize); } @@ -76,7 +80,7 @@ public void ProcessSniPacket(PacketHandle packet, uint error) { if (recurse && appended) { - SetBuffer(new byte[_inBuff.Length], 0, 0); + NewBuffer(_inBuff.Length); appended = false; } MultiplexPackets( @@ -95,16 +99,19 @@ out recurse // if a partial packet was reconstructed it must be handled first if (consumePartialPacket) { + // the partial packet has been processed by the multiplexer and should now have + // only data from a single packet in it so we should use RequiredLength which + // is defined by the packet header here not CurrentLength + Debug.Assert(PartialPacket != null && PartialPacket.RequiredLength == PartialPacket.CurrentLength); if (_snapshot != null) { - _snapshot.AppendPacketData(PartialPacket.Buffer, PartialPacket.CurrentLength); - SetBuffer(new byte[_inBuff.Length], 0, 0); + _snapshot.AppendPacketData(PartialPacket.Buffer, PartialPacket.RequiredLength); + NewBuffer(_inBuff.Length); appended = true; } else { - SetBuffer(PartialPacket.Buffer, 0, PartialPacket.CurrentLength); - + SetBuffer(PartialPacket.Buffer, 0, PartialPacket.RequiredLength); } bufferIsPartialCompleted = true; ClearPartialPacket(); @@ -125,7 +132,7 @@ out recurse // if we SetBuffer here to clear the packet buffer we will break the attention handling which relies // on the attention containing packet remaining in the active buffer even if we're appending to the // snapshot so we will have to use the appended variable to prevent the same buffer being added again - //// SetBuffer(new byte[_inBuff.Length], 0, 0); + //// NewBuffer(_inBuff.Length); appended = true; } else @@ -141,7 +148,7 @@ out recurse // we don't process it if (!bufferIsPartialCompleted) { - SetBuffer(_inBuff, 0, 0); + NewBuffer(_inBuff.Length); } } @@ -149,11 +156,20 @@ out recurse if (remainderPacketProduced) { SetPartialPacket(remainderPacket); + if (appended && recurse) + { + // if we've appended to the snapshot already we can't recurse and append to it again because the + // snapshot might be cleared by the async cleanup functions + // the only way to get a recurse output from the multiplexer is if it has produced a remainder packet so + // assert that this is the case and the put the remainder packet in the partial packet so that it + // can be picked up in another call. + recurse = false; + } if (!bufferIsPartialCompleted) { // we are keeping the partial packet buffer so replace it with a new one // unless we have already set the buffer to the partial packet buffer - SetBuffer(new byte[_inBuff.Length], 0, 0); + NewBuffer(_inBuff.Length); } } @@ -301,7 +317,7 @@ out bool recurse remainderPacket = new Packet { Buffer = new byte[dataBuffer.Length], - CurrentLength = remainderLength, + CurrentLength = remainderLength }; remainderPacket.SetCreatedBy(1); diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs index 343eaeca5a..b72e38631f 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs @@ -1293,6 +1293,13 @@ internal void SetBuffer(byte[] buffer, int inBytesUsed, int inBytesRead/*, [Call _inBytesRead = inBytesRead; } + internal void NewBuffer(int size) + { + _inBuff = new byte[size]; + _inBytesUsed = 0; + _inBytesRead = 0; + } + // This ensure that there is data available to be read in the buffer and that the header has been processed // NOTE: This method (and all it calls) should be retryable without replaying a snapshot internal TdsOperationStatus TryPrepareBuffer() @@ -1404,7 +1411,7 @@ internal bool SetPacketSize(int size) // Allocate or re-allocate _inBuff. if (_inBuff == null) { - SetBuffer(new byte[size], 0, 0); + NewBuffer(size); } else if (size != _inBuff.Length) { @@ -1431,7 +1438,7 @@ internal bool SetPacketSize(int size) else { // buffer is empty - just create the new one that is double the size of the old one - SetBuffer(new byte[size], 0, 0); + NewBuffer(size); } } @@ -1994,11 +2001,13 @@ internal TdsOperationStatus TryReadStringWithEncoding(int length, System.Text.En } byte[] buf = null; int offset = 0; + RequestContinue(true); (bool canContinue, bool isStarting, bool isContinuing) = GetSnapshotStatuses(); if (isPlp) { - TdsOperationStatus result = TryReadPlpBytes(ref buf, 0, int.MaxValue, out length); + bool compatibilityMode = LocalAppContextSwitches.UseCompatibilityAsyncBehaviour; + TdsOperationStatus result = TryReadPlpBytes(ref buf, 0, int.MaxValue, out length, canContinue && !compatibilityMode, canContinue && !compatibilityMode, compatibilityMode); if (result != TdsOperationStatus.Done) { value = null; @@ -2160,12 +2169,10 @@ internal int ReadPlpBytesChunk(byte[] buff, int offset, int len) internal TdsOperationStatus TryReadPlpBytes(ref byte[] buff, int offset, int len, out int totalBytesRead) { bool canContinue = false; - bool isStarting = false; - bool isContinuing = false; bool compatibilityMode = LocalAppContextSwitches.UseCompatibilityAsyncBehaviour; if (!compatibilityMode) { - (canContinue, isStarting, isContinuing) = GetSnapshotStatuses(); + (canContinue, _, _) = GetSnapshotStatuses(); } return TryReadPlpBytes(ref buff, offset, len, out totalBytesRead, canContinue, canContinue, compatibilityMode); } @@ -2189,7 +2196,16 @@ internal TdsOperationStatus TryReadPlpBytes(ref byte[] buff, int offset, int len } AssertValidState(); - totalBytesRead = 0; + if (writeDataSizeToSnapshot && canContinue && _snapshot != null) + { + // if there is a snapshot which it contains a stored plp buffer take it + // and try to use it if it is the right length + buff = TryTakeSnapshotStorage() as byte[]; + if (buff != null) + { + totalBytesRead = _snapshot.GetPacketDataOffset(); + } + } return TdsOperationStatus.Done; // No data } @@ -2201,22 +2217,25 @@ internal TdsOperationStatus TryReadPlpBytes(ref byte[] buff, int offset, int 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 && _longlen != TdsEnums.SQL_PLP_UNKNOWNLEN) { - if (compatibilityMode && _snapshot != null && _snapshotStatus != SnapshotStatus.NotActive) - { - // legacy replay path perf optimization - // if there is a snapshot which contains a stored plp buffer take it - // and try to use it if it is the right length - buff = TryTakeSnapshotStorage() as byte[]; - } - else if (writeDataSizeToSnapshot && canContinue && _snapshot != null) + if (_snapshot != null) { - // if there is a snapshot which it contains a stored plp buffer take it - // and try to use it if it is the right length - buff = TryTakeSnapshotStorage() as byte[]; - if (buff != null) + if (compatibilityMode && _snapshotStatus != SnapshotStatus.NotActive) + { + // legacy replay path perf optimization + // if there is a snapshot which contains a stored plp buffer take it + // and try to use it if it is the right length + buff = TryTakeSnapshotStorage() as byte[]; + } + else { - offset = _snapshot.GetPacketDataOffset(); - totalBytesRead = offset; + // if there is a snapshot which it contains a stored plp buffer take it + // and try to use it if it is the right length + buff = TryTakeSnapshotStorage() as byte[]; + if (buff != null && writeDataSizeToSnapshot && canContinue) + { + offset = _snapshot.GetPacketDataOffset(); + totalBytesRead = offset; + } } } @@ -2231,17 +2250,19 @@ internal TdsOperationStatus TryReadPlpBytes(ref byte[] buff, int offset, int len if (_longlenleft == 0) { TdsOperationStatus result = TryReadPlpLength(false, out _); - if (result != TdsOperationStatus.Done) + if (result != TdsOperationStatus.Done || _longlenleft == 0) { - totalBytesRead = 0; + if (_snapshot != null) + { + if (writeDataSizeToSnapshot && canContinue && buff != null) + { + totalBytesRead = _snapshot.GetPacketDataOffset(); + ClearSnapshotDataSize(); + } + SetSnapshotStorage(null); + } return result; } - if (_longlenleft == 0) - { - // Data read complete - totalBytesRead = 0; - return TdsOperationStatus.Done; - } } if (buff == null) @@ -2271,21 +2292,24 @@ internal TdsOperationStatus TryReadPlpBytes(ref byte[] buff, int offset, int len _longlenleft -= (ulong)bytesRead; if (result != TdsOperationStatus.Done) { - if (compatibilityMode && _snapshot != null) - { - // legacy replay path perf optimization - // a partial read has happened so store the target buffer in the snapshot - // so it can be re-used when another packet arrives and we read again - SetSnapshotStorage(buff); - } - else if (canContinue) + if (_snapshot != null) { - // a partial read has happened so store the target buffer in the snapshot - // so it can be re-used when another packet arrives and we read again - SetSnapshotStorage(buff); - if (writeDataSizeToSnapshot) + if (compatibilityMode) + { + // legacy replay path perf optimization + // a partial read has happened so store the target buffer in the snapshot + // so it can be re-used when another packet arrives and we read again + SetSnapshotStorage(buff); + } + else if (canContinue) { - AddSnapshotDataSize(bytesRead); + // a partial read has happened so store the target buffer in the snapshot + // so it can be re-used when another packet arrives and we read again + SetSnapshotStorage(buff); + if (writeDataSizeToSnapshot) + { + AddSnapshotDataSize(bytesRead); + } } } return result; @@ -2301,16 +2325,19 @@ internal TdsOperationStatus TryReadPlpBytes(ref byte[] buff, int offset, int len result = TryReadPlpLength(false, out _); if (result != TdsOperationStatus.Done) { - if (compatibilityMode && _snapshot != null) + if (_snapshot != null) { - // a partial read has happened so store the target buffer in the snapshot - // so it can be re-used when another packet arrives and we read again - SetSnapshotStorage(buff); - } - else if (canContinue && result == TdsOperationStatus.NeedMoreData) - { - SetSnapshotStorage(buff); - // data bytes read from the current packet must be 0 here so do not save the snapshot data size + if (compatibilityMode) + { + // a partial read has happened so store the target buffer in the snapshot + // so it can be re-used when another packet arrives and we read again + SetSnapshotStorage(buff); + } + else if (result == TdsOperationStatus.NeedMoreData) + { + SetSnapshotStorage(buff); + // data bytes read from the current packet must be 0 here so do not save the snapshot data size + } } return result; } @@ -2332,7 +2359,6 @@ internal TdsOperationStatus TryReadPlpBytes(ref byte[] buff, int offset, int len } return TdsOperationStatus.Done; } - ///////////////////////////////////////// // Value Skip Logic // ///////////////////////////////////////// @@ -3224,6 +3250,12 @@ internal void ReadSni(TaskCompletionSource completion) } else { + // this call to IncrementPendingCallbacks is required for balance + // the _pendingCallbacks counter will be unconditionally decremented in ReadAsyncCallback + // so we must make sure that even though we are not making a network call that we do + // not cause an incorrect decrement which will cause disconnection from the native + // component + IncrementPendingCallbacks(); readPacket = default; error = TdsEnums.SNI_SUCCESS; } @@ -3630,6 +3662,9 @@ internal void AddSnapshotDataSize(int countOfBytesCopiedFromCurrentPacket) _snapshot.SetPacketDataSize(countOfBytesCopiedFromCurrentPacket); } + /// + /// clears the stored count of bytes to be copied from the all packets in the snapshot + /// internal void ClearSnapshotDataSize() { Debug.Assert(_snapshot != null, "_snapshot must exist to store packet data size"); @@ -3656,6 +3691,19 @@ internal int GetSnapshotPacketID() return _snapshot.GetPacketID(); } + /// + /// sets a value on the snapshot to allow the ContinueEnabled property to return true.
+ /// this function should be called only by functions that explicitly support the snapshot status + /// status + ///
+ internal void RequestContinue(bool value) + { + if (_snapshot != null) + { + _snapshot.RequestContinue(value); + } + } + /// /// Debug Only: Ensures that the TdsParserStateObject has no lingering state and can safely be re-used /// @@ -3764,8 +3812,9 @@ internal sealed partial class StateSnapshot { private sealed partial class PacketData { - public byte[] Buffer; - public int Read; + public readonly byte[] Buffer; + public readonly int Read; + public PacketData NextPacket; public PacketData PrevPacket; @@ -3775,6 +3824,12 @@ private sealed partial class PacketData /// public int RunningDataSize; + public PacketData(byte[] buffer, int read) + { + Buffer = buffer; + Read = read; + } + public int PacketID => Packet.GetIDFromHeader(Buffer.AsSpan(0, TdsEnums.HEADER_LEN)); internal int GetPacketDataOffset() @@ -3796,21 +3851,6 @@ internal int GetPacketDataSize() return Math.Max(RunningDataSize - previous, 0); } - internal void Clear() - { - Buffer = null; - Read = 0; - NextPacket = null; - if (PrevPacket != null) - { - PrevPacket.NextPacket = null; - PrevPacket = null; - } - SetDebugStackImpl(null); - SetDebugPacketId(0); - SetDebugDataHash(); - } - internal void SetDebugStack(string value) => SetDebugStackImpl(value); internal void SetDebugPacketId(int value) => SetDebugPacketIdImpl(value); internal void SetDebugDataHash() => SetDebugDataHashImpl(); @@ -4102,12 +4142,12 @@ internal void Restore(TdsParserStateObject stateObj) private StateObjectData _continueStateData; internal object _storage; + internal bool _continueRequested; private PacketData _lastPacket; private PacketData _firstPacket; private PacketData _current; private PacketData _continuePacket; - private PacketData _sparePacket; #if DEBUG private int _packetCounter; @@ -4151,7 +4191,7 @@ internal void CheckStack(string trace) } #endif - public bool ContinueEnabled => !LocalAppContextSwitches.UseCompatibilityAsyncBehaviour; + public bool ContinueEnabled => _continueRequested && !LocalAppContextSwitches.UseCompatibilityAsyncBehaviour; internal void CloneNullBitmapInfo() { @@ -4172,8 +4212,8 @@ internal void CloneCleanupAltMetaDataSetArray() internal void AppendPacketData(byte[] buffer, int read) { Debug.Assert(buffer != null, "packet data cannot be null"); - Debug.Assert(read >= TdsEnums.HEADER_LEN, "minimum packet length is TdsEnums.HEADER_LEN"); - Debug.Assert(TdsEnums.HEADER_LEN + Packet.GetDataLengthFromHeader(buffer) == read, "partially read packets cannot be appended to the snapshot"); + Debug.Assert(LocalAppContextSwitches.UseCompatibilityProcessSni || read >= TdsEnums.HEADER_LEN, "minimum packet length is TdsEnums.HEADER_LEN"); + Debug.Assert(LocalAppContextSwitches.UseCompatibilityProcessSni || TdsEnums.HEADER_LEN + Packet.GetDataLengthFromHeader(buffer) == read, "partially read packets cannot be appended to the snapshot"); #if DEBUG for (PacketData current = _firstPacket; current != null; current = current.NextPacket) { @@ -4188,17 +4228,7 @@ internal void AppendPacketData(byte[] buffer, int read) } } #endif - PacketData packetData = _sparePacket; - if (packetData is null) - { - packetData = new PacketData(); - } - else - { - _sparePacket = null; - } - packetData.Buffer = buffer; - packetData.Read = read; + PacketData packetData = new PacketData(buffer, read); #if DEBUG packetData.SetDebugStack(_stateObj._lastStack); packetData.SetDebugPacketId(Interlocked.Increment(ref _packetCounter)); @@ -4297,6 +4327,7 @@ internal void CaptureAsContinue(TdsParserStateObject stateObj) if (ContinueEnabled) { Debug.Assert(_stateObj == stateObj); + Debug.Assert(_stateObj._bTmpRead == 0); if (_current is not null) { _continueStateData ??= new StateObjectData(); @@ -4306,6 +4337,11 @@ internal void CaptureAsContinue(TdsParserStateObject stateObj) } } + internal void RequestContinue(bool value) + { + _continueRequested = value; + } + internal void SetPacketDataSize(int size) { PacketData target = _current; @@ -4380,18 +4416,16 @@ internal void Clear() private void ClearPackets() { - PacketData packet = _firstPacket; _firstPacket = null; _lastPacket = null; _continuePacket = null; _current = null; - packet.Clear(); - _sparePacket = packet; } private void ClearState() { _storage = null; + _continueRequested = false; _replayStateData.Clear(_stateObj); _continueStateData?.Clear(_stateObj, trackStack: false); #if DEBUG diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStaticMethods.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStaticMethods.cs index e21bfc7826..569a3fbc16 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStaticMethods.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStaticMethods.cs @@ -100,6 +100,10 @@ static internal void AliasRegistryLookup(ref string host, ref string protocol) // If this logic changed, SNIPacketSetData needs to be changed as well internal static byte[] ObfuscatePassword(string password) { + if (string.IsNullOrEmpty(password)) + { + return Array.Empty(); + } byte[] bObfuscated = new byte[password.Length << 1]; int s; byte bLo; @@ -118,6 +122,10 @@ internal static byte[] ObfuscatePassword(string password) internal static byte[] ObfuscatePassword(byte[] password) { + if (password == null || password.Length == 0) + { + return Array.Empty(); + } byte bLo; byte bHi; diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/TdsParserStateObject.TestHarness.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/TdsParserStateObject.TestHarness.cs index 89807b0132..6e8f64fcc8 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/TdsParserStateObject.TestHarness.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/TdsParserStateObject.TestHarness.cs @@ -142,6 +142,14 @@ void SetBuffer(byte[] buffer, int inBytesUsed, int inBytesRead) _inBytesRead = inBytesRead; } + [DebuggerStepThrough] + internal void NewBuffer(int size) + { + _inBuff = new byte[size]; + _inBytesUsed = 0; + _inBytesRead = 0; + } + // stubs private LastIOTimer _lastSuccessfulIOTimer = new LastIOTimer(); private Parser _parser = new Parser(); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs index e9786bea16..b071eeaf5a 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs @@ -1035,11 +1035,18 @@ [Value] [nvarchar](max) NULL } } - int counter = 0; - while (counter < 10) + SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString); + builder.PersistSecurityInfo = true; + builder.Pooling = false; + + for (int packetSize = 512; packetSize<2048; packetSize+=3) { - using (var cmd = cn.CreateCommand()) + builder.PacketSize = packetSize; + using (SqlConnection sizedConnection = new SqlConnection(builder.ToString())) + using (SqlCommand cmd = sizedConnection.CreateCommand()) { + sizedConnection.Open(); + cmd.CommandText = $"SELECT [d].[Id], [d].[DocumentIdentificationId], [d].[Name], [d].[Value] FROM [{tableName}] AS [d]"; using (var reader = await cmd.ExecuteReaderAsync()) { @@ -1063,7 +1070,6 @@ [Value] [nvarchar](max) NULL } } } - counter++; } } finally