diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
index 136bd66c38..9152859367 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
@@ -354,9 +354,6 @@
Microsoft\Data\SqlClient\OnChangedEventHandler.cs
-
- Microsoft\Data\SqlClient\Packet.cs
-
Microsoft\Data\SqlClient\ParameterPeekAheadValue.cs
@@ -762,9 +759,6 @@
Microsoft\Data\SqlClient\TdsParserStateObject.cs
-
- Microsoft\Data\SqlClient\TdsParserStateObject.Multiplexer.cs
-
Microsoft\Data\SqlClient\TdsParserStaticMethods.cs
diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.Windows.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.Windows.cs
index 11a23af07a..7576efe32a 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.Windows.cs
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.Windows.cs
@@ -26,10 +26,6 @@ internal void PostReadAsyncForMars()
_pMarsPhysicalConObj.IncrementPendingCallbacks();
SessionHandle handle = _pMarsPhysicalConObj.SessionHandle;
- // we do not need to consider partial packets when making this read because we
- // expect this read to pend. a partial packet should not exist at setup of the
- // parser
- Debug.Assert(_physicalStateObj.PartialPacket==null);
temp = _pMarsPhysicalConObj.ReadAsync(handle, out error);
Debug.Assert(temp.Type == PacketHandle.NativePointerType, "unexpected packet type when requiring NativePointer");
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 4d33636e75..c020e7bb3e 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
@@ -2049,19 +2049,11 @@ internal TdsOperationStatus TryRun(RunBehavior runBehavior, SqlCommand cmdHandle
if (!IsValidTdsToken(token))
{
-#if DEBUG
- string message = stateObj.DumpBuffer();
- Debug.Fail(message);
-#endif
+ Debug.Fail($"unexpected token; token = {token,-2:X2}");
_state = TdsParserState.Broken;
_connHandler.BreakConnection();
SqlClientEventSource.Log.TryTraceEvent(" Potential multi-threaded misuse of connection, unexpected TDS token found {0}", ObjectID);
-#if DEBUG
- throw new InvalidOperationException(message);
-#else
throw SQL.ParsingError();
-#endif
-
}
int tokenLength;
@@ -4119,7 +4111,6 @@ internal TdsOperationStatus TryProcessReturnValue(int length,
{
return result;
}
-
byte len;
result = stateObj.TryReadByte(out len);
if (result != TdsOperationStatus.Done)
@@ -5284,7 +5275,7 @@ private TdsOperationStatus TryCommonProcessMetaData(TdsParserStateObject stateOb
{
// If the column is encrypted, we should have a valid cipherTable
if (cipherTable != null)
- {
+ {
result = TryProcessTceCryptoMetadata(stateObj, col, cipherTable, columnEncryptionSetting, isReturnValue: false);
if (result != TdsOperationStatus.Done)
{
@@ -5570,12 +5561,6 @@ private TdsOperationStatus TryProcessColumnHeaderNoNBC(SqlMetaDataPriv col, TdsP
{
if (col.metaType.IsLong && !col.metaType.IsPlp)
{
- if (stateObj.IsSnapshotContinuing())
- {
- length = (ulong)stateObj.GetSnapshotStorageLength();
- isNull = length == 0;
- return TdsOperationStatus.Done;
- }
//
// we don't care about TextPtrs, simply go after the data after it
//
@@ -6029,17 +6014,34 @@ private TdsOperationStatus TryReadSqlStringValue(SqlBuffer value, byte type, int
if (isPlp)
{
- result = TryReadPlpUnicodeCharsWithContinue(
- stateObj,
- length,
- out string resultString
- );
+ char[] cc = null;
+ bool buffIsRented = false;
+ result = TryReadPlpUnicodeChars(ref cc, 0, length >> 1, stateObj, out length, supportRentedBuff: true, rentedBuff: ref buffIsRented);
if (result == TdsOperationStatus.Done)
{
- s = resultString;
+ if (length > 0)
+ {
+ s = new string(cc, 0, length);
+ }
+ else
+ {
+ s = string.Empty;
+ }
}
- else
+
+ if (buffIsRented)
+ {
+ // 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;
+ }
+
+ if (result != TdsOperationStatus.Done)
{
return result;
}
@@ -6399,7 +6401,9 @@ internal TdsOperationStatus TryReadSqlValue(SqlBuffer value,
}
else
{
- result = stateObj.TryReadByteArrayWithContinue(length, out b);
+ //Debug.Assert(length > 0 && length < (long)(Int32.MaxValue), "Bad length for column");
+ b = new byte[length];
+ result = stateObj.TryReadByteArray(b, length);
if (result != TdsOperationStatus.Done)
{
return result;
@@ -6470,7 +6474,8 @@ internal TdsOperationStatus TryReadSqlValue(SqlBuffer value,
case TdsEnums.SQLVECTOR:
// Vector data is read as non-plp binary value.
// This is same as reading varbinary(8000).
- result = stateObj.TryReadByteArrayWithContinue(length, out b);
+ byte[] buff = new byte[length];
+ result = stateObj.TryReadByteArray(buff, length);
if (result != TdsOperationStatus.Done)
{
return result;
@@ -6478,13 +6483,13 @@ internal TdsOperationStatus TryReadSqlValue(SqlBuffer value,
// Internally, we use Sqlbinary to deal with varbinary data and store it in
// SqlBuffer as SqlBinary value.
- value.SqlBinary = SqlBinary.WrapBytes(b);
+ value.SqlBinary = SqlBinary.WrapBytes(buff);
// Extract the metadata from the payload and set it as the vector attributes
// in the SqlBuffer. This metadata is further used when constructing a SqlVector
// object from binary payload.
- int elementCount = BinaryPrimitives.ReadUInt16LittleEndian(b.AsSpan(2));
- byte elementType = b[4];
+ int elementCount = BinaryPrimitives.ReadUInt16LittleEndian(buff.AsSpan(2));
+ byte elementType = buff[4];
value.SetVectorInfo(elementCount, elementType, false);
break;
@@ -8123,22 +8128,14 @@ internal TdsOperationStatus TryGetTokenLength(byte token, TdsParserStateObject s
case TdsEnums.SQLVarCnt:
if (0 != (token & 0x80))
{
- if (stateObj.IsSnapshotContinuing())
- {
- tokenLength = stateObj.GetSnapshotStorageLength();
- Debug.Assert(tokenLength != 0, "stored buffer length on continue must contain the length of the data required for the token");
- }
- else
+ ushort value;
+ result = stateObj.TryReadUInt16(out value);
+ if (result != TdsOperationStatus.Done)
{
- ushort value;
- result = stateObj.TryReadUInt16(out value);
- if (result != TdsOperationStatus.Done)
- {
- tokenLength = 0;
- return result;
- }
- tokenLength = value;
+ tokenLength = 0;
+ return result;
}
+ tokenLength = value;
return TdsOperationStatus.Done;
}
else if (0 == (token & 0x0c))
@@ -13075,84 +13072,6 @@ internal int ReadPlpUnicodeChars(ref char[] buff, int offst, int len, TdsParserS
return charsRead;
}
- internal TdsOperationStatus TryReadPlpUnicodeCharsWithContinue(TdsParserStateObject stateObj, int length, out string resultString)
- {
- resultString = null;
- char[] temp = null;
- bool buffIsRented = false;
- int startOffset = 0;
- (bool canContinue, bool isStarting, bool isContinuing) = stateObj.GetSnapshotStatuses();
-
- if (canContinue)
- {
- temp = stateObj.TryTakeSnapshotStorage() as char[];
- Debug.Assert(temp != null || !isContinuing, "if continuing stored buffer must be present to contain previous data to continue from");
- Debug.Assert(temp == null || length == int.MaxValue || temp.Length == length, "stored buffer length must be null or must have been created with the correct length");
-
- if (temp != null)
- {
- startOffset = stateObj.GetSnapshotTotalSize();
- }
- }
-
- TdsOperationStatus result = TryReadPlpUnicodeChars(
- ref temp,
- 0,
- length >> 1,
- stateObj,
- out length,
- supportRentedBuff: !canContinue, // do not use the arraypool if we are going to keep the buffer in the snapshot
- rentedBuff: ref buffIsRented,
- startOffset,
- canContinue
- );
-
- if (result == TdsOperationStatus.Done)
- {
- if (length > 0)
- {
- resultString = new string(temp, 0, length);
- }
- else
- {
- resultString = string.Empty;
- }
-
- if (buffIsRented)
- {
- // 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.
- temp.AsSpan(0, length).Clear();
- ArrayPool.Shared.Return(temp, clearArray: false);
- temp = null;
- }
- }
- else if (result == TdsOperationStatus.NeedMoreData)
- {
- if (canContinue)
- {
- stateObj.SetSnapshotStorage(temp);
- }
- }
-
- return result;
- }
-
- internal TdsOperationStatus TryReadPlpUnicodeChars(
- ref char[] buff,
- int offst,
- int len,
- TdsParserStateObject stateObj,
- out int totalCharsRead,
- bool supportRentedBuff,
- ref bool rentedBuff
- )
- {
- return TryReadPlpUnicodeChars(ref buff, offst, len, stateObj, out totalCharsRead, supportRentedBuff, ref rentedBuff, 0, false);
- }
-
// Reads the requested number of chars from a plp data stream, or the entire data if
// 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.
@@ -13164,13 +13083,11 @@ internal TdsOperationStatus TryReadPlpUnicodeChars(
TdsParserStateObject stateObj,
out int totalCharsRead,
bool supportRentedBuff,
- ref bool rentedBuff,
- int startOffsetByteCount,
- bool writeDataSizeToSnapshot
- )
+ ref bool rentedBuff)
{
int charsRead = 0;
int charsLeft = 0;
+ char[] newbuf;
if (stateObj._longlen == 0)
{
@@ -13180,22 +13097,16 @@ bool writeDataSizeToSnapshot
}
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)
- ||
- (buff.Length >= (startOffsetByteCount >> 1) + 1),
- "Invalid length sent to ReadPlpUnicodeChars()!"
- );
+
+ Debug.Assert((buff == null && offst == 0) || (buff.Length >= offst + len), "Invalid length sent to ReadPlpUnicodeChars()!");
charsLeft = len;
// 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 (buff == null && stateObj._longlen != TdsEnums.SQL_PLP_UNKNOWNLEN && len < (int.MaxValue >> 1))
{
- if (supportRentedBuff && stateObj._longlen < 1073741824) // 1 Gib
+ if (supportRentedBuff && len < 1073741824) // 1 Gib
{
buff = ArrayPool.Shared.Rent((int)Math.Min((int)stateObj._longlen, len));
rentedBuff = true;
@@ -13208,11 +13119,6 @@ bool writeDataSizeToSnapshot
}
TdsOperationStatus result;
-
- bool partialReadInProgress = (startOffsetByteCount & 0x1) == 1;
- bool restartingDataSizeCount = startOffsetByteCount == 0;
- int currentPacketId = 0;
-
if (stateObj._longlenleft == 0)
{
result = stateObj.TryReadPlpLength(false, out _);
@@ -13228,103 +13134,63 @@ bool writeDataSizeToSnapshot
}
}
- totalCharsRead = (startOffsetByteCount >> 1);
- charsLeft -= totalCharsRead;
- offst += totalCharsRead;
-
+ totalCharsRead = 0;
while (charsLeft > 0)
{
- if (!partialReadInProgress)
+ charsRead = (int)Math.Min((stateObj._longlenleft + 1) >> 1, (ulong)charsLeft);
+ if ((buff == null) || (buff.Length < (offst + charsRead)))
{
- charsRead = (int)Math.Min((stateObj._longlenleft + 1) >> 1, (ulong)charsLeft);
- if ((buff == null) || (buff.Length < (offst + charsRead)))
+ bool returnRentedBufferAfterCopy = rentedBuff;
+ if (supportRentedBuff && (offst + charsRead) < 1073741824) // 1 Gib
{
- char[] newbuf;
- bool returnRentedBufferAfterCopy = rentedBuff;
- if (supportRentedBuff && (offst + charsRead) < 1073741824) // 1 Gib
- {
- newbuf = ArrayPool.Shared.Rent(offst + charsRead);
- rentedBuff = true;
- }
- else
- {
- // grow by an arbitrary number of packets to avoid needing to reallocate
- // the newbuf on each loop iteration of long packet sequences which causes
- // a performance problem as we spend large amounts of time copying and in gc
- newbuf = new char[offst + charsRead + (stateObj.GetPacketSize() * 8)];
- 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;
- newbuf = null;
+ newbuf = ArrayPool.Shared.Rent(offst + charsRead);
+ rentedBuff = true;
}
- if (charsRead > 0)
+ else
{
- result = TryReadPlpUnicodeCharsChunk(buff, offst, charsRead, stateObj, out charsRead);
- if (result != TdsOperationStatus.Done)
- {
- return result;
- }
- charsLeft -= charsRead;
- offst += charsRead;
- totalCharsRead += charsRead;
+ newbuf = new char[offst + charsRead];
+ rentedBuff = false;
+ }
- if (writeDataSizeToSnapshot)
+ if (buff != null)
+ {
+ Buffer.BlockCopy(buff, 0, newbuf, 0, offst * 2);
+ if (returnRentedBufferAfterCopy)
{
- currentPacketId = IncrementSnapshotDataSize(stateObj, restartingDataSizeCount, currentPacketId, charsRead * 2);
+ buff.AsSpan(0, offst).Clear();
+ ArrayPool.Shared.Return(buff, clearArray: false);
}
}
+ buff = newbuf;
}
-
- // Special case single byte
- if (
- (stateObj._longlenleft == 1 || partialReadInProgress)
- && (charsLeft > 0)
- )
+ if (charsRead > 0)
{
- byte b1 = 0;
- byte b2 = 0;
- if (partialReadInProgress)
+ result = TryReadPlpUnicodeCharsChunk(buff, offst, charsRead, stateObj, out charsRead);
+ if (result != TdsOperationStatus.Done)
{
- partialReadInProgress = false;
- // we're resuming with a partial char in the buffer so we need to load the byte
- // from the char buffer and put it into b1 so we can combine it with the second
- // half later
- b1 = (byte)(buff[offst] & 0x00ff);
+ return result;
}
- else
+ charsLeft -= charsRead;
+ offst += charsRead;
+ totalCharsRead += charsRead;
+ }
+ // Special case single byte left
+ if (stateObj._longlenleft == 1 && (charsLeft > 0))
+ {
+ byte b1;
+ result = stateObj.TryReadByte(out b1);
+ if (result != TdsOperationStatus.Done)
{
- result = stateObj.TryReadByte(out b1);
- if (result != TdsOperationStatus.Done)
- {
- return result;
- }
- stateObj._longlenleft--;
- if (writeDataSizeToSnapshot)
- {
- // we need to write the single b1 byte to the array because we may run out of data
- // and need to wait for another packet
- buff[offst] = (char)((b1 & 0xff));
- currentPacketId = IncrementSnapshotDataSize(stateObj, restartingDataSizeCount, currentPacketId, 1);
- }
-
- result = stateObj.TryReadPlpLength(false, out _);
- if (result != TdsOperationStatus.Done)
- {
- return result;
- }
- Debug.Assert((stateObj._longlenleft != 0), "ReadPlpUnicodeChars: Odd byte left at the end!");
+ return result;
}
-
+ stateObj._longlenleft--;
+ result = stateObj.TryReadPlpLength(false, out _);
+ if (result != TdsOperationStatus.Done)
+ {
+ return result;
+ }
+ Debug.Assert((stateObj._longlenleft != 0), "ReadPlpUnicodeChars: Odd byte left at the end!");
+ byte b2;
result = stateObj.TryReadByte(out b2);
if (result != TdsOperationStatus.Done)
{
@@ -13337,11 +13203,6 @@ bool writeDataSizeToSnapshot
charsRead++;
charsLeft--;
totalCharsRead++;
-
- if (writeDataSizeToSnapshot)
- {
- currentPacketId = IncrementSnapshotDataSize(stateObj, restartingDataSizeCount, currentPacketId, 1);
- }
}
if (stateObj._longlenleft == 0)
{
@@ -13354,48 +13215,9 @@ bool writeDataSizeToSnapshot
}
if (stateObj._longlenleft == 0) // Data read complete
- {
break;
- }
}
-
- if (writeDataSizeToSnapshot)
- {
- stateObj.SetSnapshotStorage(null);
- stateObj.ClearSnapshotDataSize();
- }
-
return TdsOperationStatus.Done;
-
- static int IncrementSnapshotDataSize(TdsParserStateObject stateObj, bool resetting, int previousPacketId, int value)
- {
- int current = 0;
- if (resetting)
- {
- int currentPacketId = stateObj.GetSnapshotPacketID();
- if (previousPacketId == currentPacketId)
- {
- // we have already reset it the first time we saw it so just add normally
- current = stateObj.GetSnapshotDataSize();
- }
- else
- {
- // a packet we haven't seen before, reset the size
- current = 0;
- }
-
- stateObj.AddSnapshotDataSize(current + value);
-
- // return new packetid so next time we see this packet we know it isn't new
- return currentPacketId;
- }
- else
- {
- current = stateObj.GetSnapshotDataSize();
- stateObj.AddSnapshotDataSize(current + value);
- return previousPacketId;
- }
- }
}
internal int ReadPlpAnsiChars(ref char[] buff, int offst, int len, SqlMetaDataPriv metadata, TdsParserStateObject stateObj)
diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.netcore.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.netcore.cs
index d7dc8b62f4..4ab44115e0 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.netcore.cs
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.netcore.cs
@@ -167,22 +167,14 @@ private void ReadSniError(TdsParserStateObject stateObj, uint error)
stateObj.SendAttention(mustTakeWriteLock: true);
PacketHandle syncReadPacket = default;
- bool readFromNetwork = true;
RuntimeHelpers.PrepareConstrainedRegions();
bool shouldDecrement = false;
try
{
Interlocked.Increment(ref _readingCount);
shouldDecrement = true;
- readFromNetwork = !PartialPacketContainsCompletePacket();
- if (readFromNetwork)
- {
- syncReadPacket = ReadSyncOverAsync(stateObj.GetTimeoutRemaining(), out error);
- }
- else
- {
- error = TdsEnums.SNI_SUCCESS;
- }
+
+ syncReadPacket = ReadSyncOverAsync(stateObj.GetTimeoutRemaining(), out error);
Interlocked.Decrement(ref _readingCount);
shouldDecrement = false;
@@ -195,7 +187,7 @@ private void ReadSniError(TdsParserStateObject stateObj, uint error)
}
else
{
- Debug.Assert(!readFromNetwork || !IsValidPacket(syncReadPacket), "unexpected syncReadPacket without corresponding SNIPacketRelease");
+ Debug.Assert(!IsValidPacket(syncReadPacket), "unexpected syncReadPacket without corresponding SNIPacketRelease");
fail = true; // Subsequent read failed, time to give up.
}
}
@@ -206,7 +198,7 @@ private void ReadSniError(TdsParserStateObject stateObj, uint error)
Interlocked.Decrement(ref _readingCount);
}
- if (readFromNetwork && !IsPacketEmpty(syncReadPacket))
+ if (!IsPacketEmpty(syncReadPacket))
{
// Be sure to release packet, otherwise it will be leaked by native.
ReleasePacket(syncReadPacket);
@@ -247,9 +239,60 @@ private void ReadSniError(TdsParserStateObject stateObj, uint error)
AssertValidState();
}
- private uint GetSniPacket(PacketHandle packet, ref uint dataSize)
+ public void ProcessSniPacket(PacketHandle packet, uint error)
{
- return SniPacketGetData(packet, _inBuff, ref dataSize);
+ if (error != 0)
+ {
+ if ((_parser.State == TdsParserState.Closed) || (_parser.State == TdsParserState.Broken))
+ {
+ // Do nothing with callback if closed or broken and error not 0 - callback can occur
+ // after connection has been closed. PROBLEM IN NETLIB - DESIGN FLAW.
+ return;
+ }
+
+ AddError(_parser.ProcessSNIError(this));
+ AssertValidState();
+ }
+ else
+ {
+ uint dataSize = 0;
+
+ uint getDataError = SniPacketGetData(packet, _inBuff, ref dataSize);
+
+ if (getDataError == TdsEnums.SNI_SUCCESS)
+ {
+ if (_inBuff.Length < dataSize)
+ {
+ Debug.Assert(true, "Unexpected dataSize on Read");
+ throw SQL.InvalidInternalPacketSize(StringsHelper.GetString(Strings.SqlMisc_InvalidArraySizeMessage));
+ }
+
+ _lastSuccessfulIOTimer._value = DateTime.UtcNow.Ticks;
+ _inBytesRead = (int)dataSize;
+ _inBytesUsed = 0;
+
+ if (_snapshot != null)
+ {
+ _snapshot.AppendPacketData(_inBuff, _inBytesRead);
+ if (_snapshotReplay)
+ {
+ _snapshot.MoveNext();
+#if DEBUG
+ _snapshot.AssertCurrent();
+#endif
+ }
+ }
+
+ SniReadStatisticsAndTracing();
+ SqlClientEventSource.Log.TryAdvancedTraceBinEvent("TdsParser.ReadNetworkPacketAsyncCallback | INFO | ADV | State Object Id {0}, Packet read. In Buffer: {1}, In Bytes Read: {2}", ObjectID, _inBuff, _inBytesRead);
+
+ AssertValidState();
+ }
+ else
+ {
+ throw SQL.ParsingError(ParsingErrorState.ProcessSniPacketFailed);
+ }
+ }
}
private void SetBufferSecureStrings()
@@ -321,7 +364,7 @@ public void ReadAsyncCallback(IntPtr key, PacketHandle packet, uint error)
bool processFinallyBlock = true;
try
{
- Debug.Assert((packet.Type == 0 && PartialPacketContainsCompletePacket()) || (CheckPacket(packet, source) && source != null), "AsyncResult null on callback");
+ Debug.Assert(CheckPacket(packet, source) && source != null, "AsyncResult null on callback");
if (_parser.MARSOn)
{
diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
index 74063fdb5f..172f4c3897 100644
--- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
@@ -459,9 +459,6 @@
Microsoft\Data\SqlClient\OnChangedEventHandler.cs
-
- Microsoft\Data\SqlClient\Packet.cs
-
Microsoft\Data\SqlClient\PacketHandle.Windows.cs
@@ -855,9 +852,6 @@
Microsoft\Data\SqlClient\TdsParserStateObject.cs
-
- Microsoft\Data\SqlClient\TdsParserStateObject.Multiplexer.cs
-
Microsoft\Data\SqlClient\TdsParserStateObjectFactory.Windows.cs
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 5610f4c12b..cce4c8426e 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
@@ -4171,7 +4171,6 @@ internal TdsOperationStatus TryProcessReturnValue(int length,
{
return result;
}
-
byte len;
result = stateObj.TryReadByte(out len);
if (result != TdsOperationStatus.Done)
@@ -5767,12 +5766,6 @@ private TdsOperationStatus TryProcessColumnHeaderNoNBC(SqlMetaDataPriv col, TdsP
{
if (col.metaType.IsLong && !col.metaType.IsPlp)
{
- if (stateObj.IsSnapshotContinuing())
- {
- length = (ulong)stateObj.GetSnapshotStorageLength();
- isNull = length == 0;
- return TdsOperationStatus.Done;
- }
//
// we don't care about TextPtrs, simply go after the data after it
//
@@ -6226,19 +6219,20 @@ private TdsOperationStatus TryReadSqlStringValue(SqlBuffer value, byte type, int
if (isPlp)
{
- result = TryReadPlpUnicodeCharsWithContinue(
- stateObj,
- length,
- out string resultString
- );
+ char[] cc = null;
+ result = TryReadPlpUnicodeChars(ref cc, 0, length >> 1, stateObj, out length);
- if (result == TdsOperationStatus.Done)
+ if (result != TdsOperationStatus.Done)
{
- s = resultString;
+ return result;
+ }
+ if (length > 0)
+ {
+ s = new string(cc, 0, length);
}
else
{
- return result;
+ s = string.Empty;
}
}
else
@@ -6595,7 +6589,9 @@ internal TdsOperationStatus TryReadSqlValue(SqlBuffer value,
}
else
{
- result = stateObj.TryReadByteArrayWithContinue(length, out b);
+ //Debug.Assert(length > 0 && length < (long)(Int32.MaxValue), "Bad length for column");
+ b = new byte[length];
+ result = stateObj.TryReadByteArray(b, length);
if (result != TdsOperationStatus.Done)
{
return result;
@@ -6666,7 +6662,8 @@ internal TdsOperationStatus TryReadSqlValue(SqlBuffer value,
case TdsEnums.SQLVECTOR:
// Vector data is read as non-plp binary value.
// This is same as reading varbinary(8000).
- result = stateObj.TryReadByteArrayWithContinue(length, out b);
+ byte[] buff = new byte[length];
+ result = stateObj.TryReadByteArray(buff, length);
if (result != TdsOperationStatus.Done)
{
return result;
@@ -6674,13 +6671,13 @@ internal TdsOperationStatus TryReadSqlValue(SqlBuffer value,
// Internally, we use Sqlbinary to deal with varbinary data and store it in
// SqlBuffer as SqlBinary value.
- value.SqlBinary = SqlTypeWorkarounds.SqlBinaryCtor(b, true);
+ value.SqlBinary = SqlTypeWorkarounds.SqlBinaryCtor(buff, true);
// Extract the metadata from the payload and set it as the vector attributes
// in the SqlBuffer. This metadata is further used when constructing a SqlVector
// object from binary payload.
- int elementCount = BinaryPrimitives.ReadUInt16LittleEndian(b.AsSpan(2));
- byte elementType = b[4];
+ int elementCount = BinaryPrimitives.ReadUInt16LittleEndian(buff.AsSpan(2));
+ byte elementType = buff[4];
value.SetVectorInfo(elementCount, elementType, false);
break;
@@ -8305,22 +8302,14 @@ internal TdsOperationStatus TryGetTokenLength(byte token, TdsParserStateObject s
case TdsEnums.SQLVarCnt:
if (0 != (token & 0x80))
{
- if (stateObj.IsSnapshotContinuing())
- {
- tokenLength = stateObj.GetSnapshotStorageLength();
- Debug.Assert(tokenLength != 0, "stored buffer length on continue must contain the length of the data required for the token");
- }
- else
+ ushort value;
+ result = stateObj.TryReadUInt16(out value);
+ if (result != TdsOperationStatus.Done)
{
- ushort value;
- result = stateObj.TryReadUInt16(out value);
- if (result != TdsOperationStatus.Done)
- {
- tokenLength = 0;
- return result;
- }
- tokenLength = value;
+ tokenLength = 0;
+ return result;
}
+ tokenLength = value;
return TdsOperationStatus.Done;
}
else if (0 == (token & 0x0c))
@@ -13246,9 +13235,8 @@ 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, supportRentedBuff: false, ref rentedBuff);
+ TdsOperationStatus result = TryReadPlpUnicodeChars(ref buff, offst, len, stateObj, out charsRead);
if (result != TdsOperationStatus.Done)
{
throw SQL.SynchronousCallMayNotPend();
@@ -13256,84 +13244,6 @@ internal int ReadPlpUnicodeChars(ref char[] buff, int offst, int len, TdsParserS
return charsRead;
}
- internal TdsOperationStatus TryReadPlpUnicodeCharsWithContinue(TdsParserStateObject stateObj, int length, out string resultString)
- {
- resultString = null;
- char[] temp = null;
- bool buffIsRented = false;
- int startOffset = 0;
- (bool canContinue, bool isStarting, bool isContinuing) = stateObj.GetSnapshotStatuses();
-
- if (canContinue)
- {
- temp = stateObj.TryTakeSnapshotStorage() as char[];
- Debug.Assert(temp != null || !isContinuing, "if continuing stored buffer must be present to contain previous data to continue from");
- Debug.Assert(temp == null || length == int.MaxValue || temp.Length == length, "stored buffer length must be null or must have been created with the correct length");
-
- if (temp != null)
- {
- startOffset = stateObj.GetSnapshotTotalSize();
- }
- }
-
- TdsOperationStatus result = TryReadPlpUnicodeChars(
- ref temp,
- 0,
- length >> 1,
- stateObj,
- out length,
- supportRentedBuff: !canContinue, // do not use the arraypool if we are going to keep the buffer in the snapshot
- rentedBuff: ref buffIsRented,
- startOffset,
- canContinue
- );
-
- if (result == TdsOperationStatus.Done)
- {
- if (length > 0)
- {
- resultString = new string(temp, 0, length);
- }
- else
- {
- resultString = string.Empty;
- }
-
- if (buffIsRented)
- {
- // 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.
- temp.AsSpan(0, length).Clear();
- ArrayPool.Shared.Return(temp, clearArray: false);
- temp = null;
- }
- }
- else if (result == TdsOperationStatus.NeedMoreData)
- {
- if (canContinue)
- {
- stateObj.SetSnapshotStorage(temp);
- }
- }
-
- return result;
- }
-
- internal TdsOperationStatus TryReadPlpUnicodeChars(
- ref char[] buff,
- int offst,
- int len,
- TdsParserStateObject stateObj,
- out int totalCharsRead,
- bool supportRentedBuff,
- ref bool rentedBuff
- )
- {
- return TryReadPlpUnicodeChars(ref buff, offst, len, stateObj, out totalCharsRead, supportRentedBuff, ref rentedBuff, 0, false);
- }
-
// Reads the requested number of chars from a plp data stream, or the entire data if
// 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.
@@ -13343,15 +13253,12 @@ internal TdsOperationStatus TryReadPlpUnicodeChars(
int offst,
int len,
TdsParserStateObject stateObj,
- out int totalCharsRead,
- bool supportRentedBuff,
- ref bool rentedBuff,
- int startOffsetByteCount,
- bool writeDataSizeToSnapshot
- )
+ out int totalCharsRead)
{
int charsRead = 0;
int charsLeft = 0;
+ char[] newbuf;
+ TdsOperationStatus result;
if (stateObj._longlen == 0)
{
@@ -13360,40 +13267,18 @@ bool writeDataSizeToSnapshot
return TdsOperationStatus.Done; // No data
}
- 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)
- ||
- (buff.Length >= (startOffsetByteCount >> 1) + 1),
- "Invalid length sent to ReadPlpUnicodeChars()!"
- );
+ 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, 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 && stateObj._longlen < (int.MaxValue >> 1))
+ // 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 (supportRentedBuff && stateObj._longlen < 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;
- }
+ buff = new char[(int)Math.Min((int)stateObj._longlen, len)];
}
- TdsOperationStatus result;
-
- bool partialReadInProgress = (startOffsetByteCount & 0x1) == 1;
- bool restartingDataSizeCount = startOffsetByteCount == 0;
- int currentPacketId = 0;
-
if (stateObj._longlenleft == 0)
{
result = stateObj.TryReadPlpLength(false, out _);
@@ -13409,104 +13294,48 @@ bool writeDataSizeToSnapshot
}
}
- totalCharsRead = (startOffsetByteCount >> 1);
- charsLeft -= totalCharsRead;
- offst += totalCharsRead;
-
-
+ totalCharsRead = 0;
while (charsLeft > 0)
{
- if (!partialReadInProgress)
+ charsRead = (int)Math.Min((stateObj._longlenleft + 1) >> 1, (ulong)charsLeft);
+ if ((buff == null) || (buff.Length < (offst + charsRead)))
{
- 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];
+ if (buff != null)
{
- char[] newbuf;
- bool returnRentedBufferAfterCopy = rentedBuff;
- if (supportRentedBuff && (offst + charsRead) < 1073741824) // 1 Gib
- {
- newbuf = ArrayPool.Shared.Rent(offst + charsRead);
- rentedBuff = true;
- }
- else
- {
- // grow by an arbitrary number of packets to avoid needing to reallocate
- // the newbuf on each loop iteration of long packet sequences which causes
- // a performance problem as we spend large amounts of time copying and in gc
- newbuf = new char[offst + charsRead + (stateObj.GetPacketSize() * 8)];
- 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;
- newbuf = null;
+ Buffer.BlockCopy(buff, 0, newbuf, 0, offst * 2);
}
- if (charsRead > 0)
+ buff = newbuf;
+ }
+ if (charsRead > 0)
+ {
+ result = TryReadPlpUnicodeCharsChunk(buff, offst, charsRead, stateObj, out charsRead);
+ if (result != TdsOperationStatus.Done)
{
- result = TryReadPlpUnicodeCharsChunk(buff, offst, charsRead, stateObj, out charsRead);
- if (result != TdsOperationStatus.Done)
- {
- return result;
- }
- charsLeft -= charsRead;
- offst += charsRead;
- totalCharsRead += charsRead;
-
- if (writeDataSizeToSnapshot)
- {
- currentPacketId = IncrementSnapshotDataSize(stateObj, restartingDataSizeCount, currentPacketId, charsRead * 2);
- }
+ return result;
}
+ charsLeft -= charsRead;
+ offst += charsRead;
+ totalCharsRead += charsRead;
}
-
- // Special case single byte
- if (
- (stateObj._longlenleft == 1 || partialReadInProgress)
- && (charsLeft > 0)
- )
+ // Special case single byte left
+ if (stateObj._longlenleft == 1 && (charsLeft > 0))
{
- byte b1 = 0;
- byte b2 = 0;
- if (partialReadInProgress)
+ byte b1;
+ result = stateObj.TryReadByte(out b1);
+ if (result != TdsOperationStatus.Done)
{
- partialReadInProgress = false;
- // we're resuming with a partial char in the buffer so we need to load the byte
- // from the char buffer and put it into b1 so we can combine it with the second
- // half later
- b1 = (byte)(buff[offst] & 0x00ff);
+ return result;
}
- else
+ stateObj._longlenleft--;
+ result = stateObj.TryReadPlpLength(false, out _);
+ if (result != TdsOperationStatus.Done)
{
- result = stateObj.TryReadByte(out b1);
- if (result != TdsOperationStatus.Done)
- {
- return result;
- }
- stateObj._longlenleft--;
- if (writeDataSizeToSnapshot)
- {
- // we need to write the single b1 byte to the array because we may run out of data
- // and need to wait for another packet
- buff[offst] = (char)((b1 & 0xff));
- currentPacketId = IncrementSnapshotDataSize(stateObj, restartingDataSizeCount, currentPacketId, 1);
- }
-
- result = stateObj.TryReadPlpLength(false, out _);
- if (result != TdsOperationStatus.Done)
- {
- return result;
- }
- Debug.Assert((stateObj._longlenleft != 0), "ReadPlpUnicodeChars: Odd byte left at the end!");
+ return result;
}
-
+ Debug.Assert((stateObj._longlenleft != 0), "ReadPlpUnicodeChars: Odd byte left at the end!");
+ byte b2;
result = stateObj.TryReadByte(out b2);
if (result != TdsOperationStatus.Done)
{
@@ -13519,11 +13348,6 @@ bool writeDataSizeToSnapshot
charsRead++;
charsLeft--;
totalCharsRead++;
-
- if (writeDataSizeToSnapshot)
- {
- currentPacketId = IncrementSnapshotDataSize(stateObj, restartingDataSizeCount, currentPacketId, 1);
- }
}
if (stateObj._longlenleft == 0)
{
@@ -13536,48 +13360,9 @@ bool writeDataSizeToSnapshot
}
if (stateObj._longlenleft == 0) // Data read complete
- {
break;
- }
- }
-
- if (writeDataSizeToSnapshot)
- {
- stateObj.SetSnapshotStorage(null);
- stateObj.ClearSnapshotDataSize();
}
-
return TdsOperationStatus.Done;
-
- static int IncrementSnapshotDataSize(TdsParserStateObject stateObj, bool resetting, int previousPacketId, int value)
- {
- int current = 0;
- if (resetting)
- {
- int currentPacketId = stateObj.GetSnapshotPacketID();
- if (previousPacketId == currentPacketId)
- {
- // we have already reset it the first time we saw it so just add normally
- current = stateObj.GetSnapshotDataSize();
- }
- else
- {
- // a packet we haven't seen before, reset the size
- current = 0;
- }
-
- stateObj.AddSnapshotDataSize(current + value);
-
- // return new packetid so next time we see this packet we know it isn't new
- return currentPacketId;
- }
- else
- {
- current = stateObj.GetSnapshotDataSize();
- stateObj.AddSnapshotDataSize(current + value);
- return previousPacketId;
- }
- }
}
internal int ReadPlpAnsiChars(ref char[] buff, int offst, int len, SqlMetaDataPriv metadata, TdsParserStateObject stateObj)
diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.netfx.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.netfx.cs
index cf54e0340f..a64afdce80 100644
--- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.netfx.cs
+++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.netfx.cs
@@ -259,22 +259,14 @@ private void ReadSniError(TdsParserStateObject stateObj, uint error)
stateObj.SendAttention(mustTakeWriteLock: true);
PacketHandle syncReadPacket = default;
- bool readFromNetwork = true;
RuntimeHelpers.PrepareConstrainedRegions();
bool shouldDecrement = false;
try
{
Interlocked.Increment(ref _readingCount);
shouldDecrement = true;
- readFromNetwork = !PartialPacketContainsCompletePacket();
- if (readFromNetwork)
- {
- syncReadPacket = ReadSyncOverAsync(stateObj.GetTimeoutRemaining(), out error);
- }
- else
- {
- error = TdsEnums.SNI_SUCCESS;
- }
+
+ syncReadPacket = ReadSyncOverAsync(stateObj.GetTimeoutRemaining(), out error);
Interlocked.Decrement(ref _readingCount);
shouldDecrement = false;
@@ -287,7 +279,7 @@ private void ReadSniError(TdsParserStateObject stateObj, uint error)
}
else
{
- Debug.Assert(!readFromNetwork || !IsValidPacket(syncReadPacket), "unexpected syncReadPacket without corresponding SNIPacketRelease");
+ Debug.Assert(!IsValidPacket(syncReadPacket), "unexpected syncReadPacket without corresponding SNIPacketRelease");
fail = true; // Subsequent read failed, time to give up.
}
}
@@ -298,7 +290,7 @@ private void ReadSniError(TdsParserStateObject stateObj, uint error)
Interlocked.Decrement(ref _readingCount);
}
- if (readFromNetwork && !IsPacketEmpty(syncReadPacket))
+ if (!IsPacketEmpty(syncReadPacket))
{
// Be sure to release packet, otherwise it will be leaked by native.
ReleasePacket(syncReadPacket);
@@ -339,9 +331,60 @@ private void ReadSniError(TdsParserStateObject stateObj, uint error)
AssertValidState();
}
- private uint GetSniPacket(PacketHandle packet, ref uint dataSize)
+ public void ProcessSniPacket(PacketHandle packet, uint error)
{
- return SniPacketGetData(packet, _inBuff, ref dataSize);
+ if (error != 0)
+ {
+ if ((_parser.State == TdsParserState.Closed) || (_parser.State == TdsParserState.Broken))
+ {
+ // Do nothing with callback if closed or broken and error not 0 - callback can occur
+ // after connection has been closed. PROBLEM IN NETLIB - DESIGN FLAW.
+ return;
+ }
+
+ AddError(_parser.ProcessSNIError(this));
+ AssertValidState();
+ }
+ else
+ {
+ uint dataSize = 0;
+
+ uint getDataError = SniPacketGetData(packet, _inBuff, ref dataSize);
+
+ if (getDataError == TdsEnums.SNI_SUCCESS)
+ {
+ if (_inBuff.Length < dataSize)
+ {
+ Debug.Assert(true, "Unexpected dataSize on Read");
+ throw SQL.InvalidInternalPacketSize(StringsHelper.GetString(Strings.SqlMisc_InvalidArraySizeMessage));
+ }
+
+ _lastSuccessfulIOTimer._value = DateTime.UtcNow.Ticks;
+ _inBytesRead = (int)dataSize;
+ _inBytesUsed = 0;
+
+ if (_snapshot != null)
+ {
+ _snapshot.AppendPacketData(_inBuff, _inBytesRead);
+ if (_snapshotReplay)
+ {
+ _snapshot.MoveNext();
+#if DEBUG
+ _snapshot.AssertCurrent();
+#endif
+ }
+ }
+
+ SniReadStatisticsAndTracing();
+ SqlClientEventSource.Log.TryAdvancedTraceBinEvent("TdsParser.ReadNetworkPacketAsyncCallback | INFO | ADV | State Object Id {0}, Packet read. In Buffer: {1}, In Bytes Read: {2}", ObjectID, _inBuff, _inBytesRead);
+
+ AssertValidState();
+ }
+ else
+ {
+ throw SQL.ParsingError(ParsingErrorState.ProcessSniPacketFailed);
+ }
+ }
}
public void ReadAsyncCallback(IntPtr key, PacketHandle packet, uint error)
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs
index dfd94453f2..28d3510407 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/LocalAppContextSwitches.cs
@@ -20,8 +20,6 @@ private enum Tristate : byte
internal const string SuppressInsecureTlsWarningString = @"Switch.Microsoft.Data.SqlClient.SuppressInsecureTLSWarning";
internal const string UseMinimumLoginTimeoutString = @"Switch.Microsoft.Data.SqlClient.UseOneSecFloorInTimeoutCalculationDuringLogin";
internal const string LegacyVarTimeZeroScaleBehaviourString = @"Switch.Microsoft.Data.SqlClient.LegacyVarTimeZeroScaleBehaviour";
- internal const string UseCompatibilityProcessSniString = @"Switch.Microsoft.Data.SqlClient.UseCompatibilityProcessSni";
- internal const string UseCompatibilityAsyncBehaviourString = @"Switch.Microsoft.Data.SqlClient.UseCompatibilityAsyncBehaviour";
internal const string UseConnectionPoolV2String = @"Switch.Microsoft.Data.SqlClient.UseConnectionPoolV2";
// this field is accessed through reflection in tests and should not be renamed or have the type changed without refactoring NullRow related tests
@@ -31,10 +29,9 @@ private enum Tristate : byte
private static Tristate s_useMinimumLoginTimeout;
// this field is accessed through reflection in Microsoft.Data.SqlClient.Tests.SqlParameterTests and should not be renamed or have the type changed without refactoring related tests
private static Tristate s_legacyVarTimeZeroScaleBehaviour;
- private static Tristate s_useCompatibilityProcessSni;
- private static Tristate s_useCompatibilityAsyncBehaviour;
private static Tristate s_useConnectionPoolV2;
+
#if NET
static LocalAppContextSwitches()
{
@@ -89,66 +86,6 @@ public static bool DisableTnirByDefault
}
}
#endif
- ///
- /// In TdsParser the ProcessSni function changed significantly when the packet
- /// multiplexing code needed for high speed multi-packet column values was added.
- /// In case of compatibility problems this switch will change TdsParser to use
- /// the previous version of the function.
- ///
- public static bool UseCompatibilityProcessSni
- {
- get
- {
- if (s_useCompatibilityProcessSni == Tristate.NotInitialized)
- {
- if (AppContext.TryGetSwitch(UseCompatibilityProcessSniString, out bool returnedValue) && returnedValue)
- {
- s_useCompatibilityProcessSni = Tristate.True;
- }
- else
- {
- s_useCompatibilityProcessSni = Tristate.False;
- }
- }
- return s_useCompatibilityProcessSni == Tristate.True;
- }
- }
-
- ///
- /// In TdsParser the async multi-packet column value fetch behaviour is capable of
- /// using a continue snapshot state in addition to the original replay from start
- /// logic.
- /// This switch disables use of the continue snapshot state. This switch will always
- /// return true if is enabled because the
- /// continue state is not stable without the multiplexer.
- ///
- public static bool UseCompatibilityAsyncBehaviour
- {
- get
- {
- if (UseCompatibilityProcessSni)
- {
- // If ProcessSni compatibility mode has been enabled then the packet
- // multiplexer has been disabled. The new async behaviour using continue
- // point capture is only stable if the multiplexer is enabled so we must
- // return true to enable compatibility async behaviour using only restarts.
- return true;
- }
-
- if (s_useCompatibilityAsyncBehaviour == Tristate.NotInitialized)
- {
- if (AppContext.TryGetSwitch(UseCompatibilityAsyncBehaviourString, out bool returnedValue) && returnedValue)
- {
- s_useCompatibilityAsyncBehaviour = Tristate.True;
- }
- else
- {
- s_useCompatibilityAsyncBehaviour = Tristate.False;
- }
- }
- return s_useCompatibilityAsyncBehaviour == Tristate.True;
- }
- }
///
/// When using Encrypt=false in the connection string, a security warning is output to the console if the TLS version is 1.2 or lower.
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Packet.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Packet.cs
deleted file mode 100644
index b81270bf08..0000000000
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Packet.cs
+++ /dev/null
@@ -1,189 +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.
-
-using System;
-using System.Diagnostics;
-using System.Runtime.CompilerServices;
-
-namespace Microsoft.Data.SqlClient
-{
- ///
- /// Contains a buffer for a partial or full packet and methods to get information about the status of
- /// the packet that the buffer represents.
- /// This class is used to contain partial packet data and helps ensure that the packet data is completely
- /// received before a full packet is made available to the rest of the library
- ///
- internal sealed partial class Packet
- {
- public const int UnknownDataLength = -1;
-
- private int _dataLength;
- private int _totalLength;
- private byte[] _buffer;
-
- public Packet()
- {
- _dataLength = UnknownDataLength;
- }
-
- ///
- /// If the packet data has enough bytes available to determine the amount of data that should be present
- /// in the packet then this property will be set to the count of data bytes in the packet,
- /// otherwise this will be -1
- ///
- public int DataLength
- {
- get
- {
- CheckDisposed();
- return _dataLength;
- }
- set
- {
- CheckDisposed();
- _dataLength = value;
- }
- }
-
- ///
- /// A byte array containing bytes of data
- ///
- public byte[] Buffer
- {
- get
- {
- CheckDisposed();
- return _buffer;
- }
- set
- {
- CheckDisposed();
- _buffer = value;
- }
- }
-
- ///
- /// The total count of bytes currently in the array including the tds header bytes
- ///
- public int CurrentLength
- {
- get
- {
- CheckDisposed();
- return _totalLength;
- }
- set
- {
- CheckDisposed();
- _totalLength = value;
- }
- }
-
- ///
- /// If the packet data has enough bytes available to determine the length amount of data that should be present
- /// in the packet then this property will return the count of data bytes that are expected to be in the packet.
- /// If there are not enough bytes to determine the data byte count then this property will throw an exception.
- ///
- /// Call to check if there will be a value before using this property.
- ///
- public int RequiredLength
- {
- get
- {
- CheckDisposed();
- if (!HasDataLength)
- {
- throw new InvalidOperationException($"cannot get {nameof(RequiredLength)} when {nameof(HasDataLength)} is false");
- }
- return TdsEnums.HEADER_LEN + _dataLength;
- }
- }
-
- ///
- /// returns a boolean value indicating if there are enough total bytes available in the to read the tds header
- ///
- public bool HasHeader => _totalLength >= TdsEnums.HEADER_LEN;
-
- ///
- /// returns a boolean value indicating if the value has been set.
- ///
- public bool HasDataLength => _dataLength >= 0;
-
- ///
- /// returns a boolean value indicating whether the contains enough
- /// data for a valid tds header, has a set and that the
- /// is greater than or equal to the + tds header length.
- ///
- public bool ContainsCompletePacket => _dataLength != UnknownDataLength && (TdsEnums.HEADER_LEN + _dataLength) <= _totalLength;
-
- ///
- /// returns a containing the first 8 bytes of the array which will
- /// contain the TDS header bytes. This can be passed to static functions on to extract information from the
- /// tds packet header.
- /// Call before using this function.
- ///
- ///
- public ReadOnlySpan GetHeaderSpan() => _buffer.AsSpan(0, TdsEnums.HEADER_LEN);
-
- [Conditional("DEBUG")]
- internal void CheckDisposed() => CheckDisposedImpl();
-
- [Conditional("DEBUG")]
- internal void SetCreatedBy(int creator) => SetCreatedByImpl(creator);
-
- partial void SetCreatedByImpl(int creator);
-
- partial void CheckDisposedImpl();
-
- public static void ThrowDisposed()
- {
- throw new ObjectDisposedException(nameof(Packet));
- }
-
- internal static byte GetStatusFromHeader(ReadOnlySpan header) => header[1];
-
- internal static int GetDataLengthFromHeader(ReadOnlySpan header)
- {
- return (header[TdsEnums.HEADER_LEN_FIELD_OFFSET] << 8 | header[TdsEnums.HEADER_LEN_FIELD_OFFSET + 1]) - TdsEnums.HEADER_LEN;
- }
- internal static int GetSpidFromHeader(ReadOnlySpan header)
- {
- return (header[TdsEnums.SPID_OFFSET] << 8 | header[TdsEnums.SPID_OFFSET + 1]);
- }
- internal static int GetIDFromHeader(ReadOnlySpan header)
- {
- return header[TdsEnums.HEADER_LEN_FIELD_OFFSET + 4];
- }
-
- internal static int GetDataLengthFromHeader(Packet packet) => GetDataLengthFromHeader(packet.GetHeaderSpan());
-
- internal static bool GetIsEOMFromHeader(ReadOnlySpan header) => GetStatusFromHeader(header) == 1;
- }
-
-#if DEBUG
- internal sealed partial class Packet
- {
- private int _createdBy;
- private bool _disposed;
-
- public int CreatedBy => _createdBy;
-
- [Conditional("DEBUG")]
- public void Dispose()
- {
- _disposed = true;
- }
-
- partial void SetCreatedByImpl(int creator) => _createdBy = creator;
-
- partial void CheckDisposedImpl()
- {
- if (_disposed)
- {
- ThrowDisposed();
- }
- }
- }
-#endif
-}
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..2b656501a5 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCachedBuffer.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCachedBuffer.cs
@@ -37,24 +37,10 @@ private SqlCachedBuffer(List cachedBytes)
///
internal static TdsOperationStatus TryCreate(SqlMetaDataPriv metadata, TdsParser parser, TdsParserStateObject stateObj, out SqlCachedBuffer buffer)
{
- buffer = null;
-
- (bool canContinue, bool isStarting, _) = stateObj.GetSnapshotStatuses();
+ byte[] byteArr;
- List cachedBytes = null;
- if (canContinue)
- {
- cachedBytes = stateObj.TryTakeSnapshotStorage() as List;
- if (isStarting)
- {
- cachedBytes = null;
- }
- }
-
- if (cachedBytes == null)
- {
- cachedBytes = new List();
- }
+ List cachedBytes = new();
+ buffer = null;
// the very first length is already read.
TdsOperationStatus result = parser.TryPlpBytesLeft(stateObj, out ulong plplength);
@@ -73,25 +59,13 @@ internal static TdsOperationStatus TryCreate(SqlMetaDataPriv metadata, TdsParser
}
do
{
- bool returnAfterAdd = false;
int cb = (plplength > (ulong)MaxChunkSize) ? MaxChunkSize : (int)plplength;
- byte[] byteArr = new byte[cb];
- // pass false for the writeDataSizeToSnapshot parameter because we want to only take data
- // from the current packet and not try to do a continue-capable multi packet read
- result = stateObj.TryReadPlpBytes(ref byteArr, 0, cb, out cb, canContinue, writeDataSizeToSnapshot: false, compatibilityMode: false);
+ byteArr = new byte[cb];
+ result = stateObj.TryReadPlpBytes(ref byteArr, 0, cb, out cb);
if (result != TdsOperationStatus.Done)
{
- if (result == TdsOperationStatus.NeedMoreData && canContinue && cb == byteArr.Length)
- {
- // succeeded in getting the data but failed to find the next plp length
- returnAfterAdd = true;
- }
- else
- {
- return result;
- }
+ return result;
}
-
Debug.Assert(cb == byteArr.Length);
if (cachedBytes.Count == 0)
{
@@ -100,13 +74,6 @@ internal static TdsOperationStatus TryCreate(SqlMetaDataPriv metadata, TdsParser
}
cachedBytes.Add(byteArr);
plplength -= (ulong)cb;
-
- if (returnAfterAdd)
- {
- stateObj.SetSnapshotStorage(cachedBytes);
- return result;
- }
-
} while (plplength > 0);
result = parser.TryPlpBytesLeft(stateObj, out plplength);
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 926b714462..353dd0346f 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlDataReader.cs
@@ -4595,12 +4595,7 @@ private TdsOperationStatus TryResetBlobState()
#if DEBUG
else
{
- Debug.Assert(
- (_sharedState._columnDataBytesRemaining == 0 || _sharedState._columnDataBytesRemaining == -1)
- &&
- (_stateObj._longlen == 0 || _stateObj.IsSnapshotContinuing()),
- "Haven't read header yet, but column is partially read?"
- );
+ Debug.Assert((_sharedState._columnDataBytesRemaining == 0 || _sharedState._columnDataBytesRemaining == -1) && _stateObj._longlen == 0, "Haven't read header yet, but column is partially read?");
}
#endif
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
deleted file mode 100644
index 77bd1c982b..0000000000
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.Multiplexer.cs
+++ /dev/null
@@ -1,551 +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.
-
-using System;
-using System.Diagnostics;
-
-namespace Microsoft.Data.SqlClient
-{
- partial class TdsParserStateObject
- {
- private Packet _partialPacket;
- internal Packet PartialPacket => _partialPacket;
-
- public void ProcessSniPacket(PacketHandle packet, uint error)
- {
- if (LocalAppContextSwitches.UseCompatibilityProcessSni)
- {
- ProcessSniPacketCompat(packet, error);
- return;
- }
-
- if (error != 0)
- {
- if ((_parser.State == TdsParserState.Closed) || (_parser.State == TdsParserState.Broken))
- {
- // Do nothing with callback if closed or broken and error not 0 - callback can occur
- // after connection has been closed. PROBLEM IN NETLIB - DESIGN FLAW.
- return;
- }
-
- AddError(_parser.ProcessSNIError(this));
- AssertValidState();
- }
- else
- {
- uint dataSize = 0;
- bool usedPartialPacket = false;
- uint getDataError = 0;
-
- if (PartialPacketContainsCompletePacket())
- {
- Packet partialPacket = _partialPacket;
- SetBuffer(partialPacket.Buffer, 0, partialPacket.CurrentLength);
- ClearPartialPacket();
- getDataError = TdsEnums.SNI_SUCCESS;
- usedPartialPacket = true;
- }
- else
- {
- if (_inBytesRead != 0)
- {
- SetBuffer(new byte[_inBuff.Length], 0, 0);
- }
- getDataError = GetSniPacket(packet, ref dataSize);
- }
-
- if (getDataError == TdsEnums.SNI_SUCCESS)
- {
- if (_inBuff.Length < dataSize)
- {
- Debug.Assert(true, "Unexpected dataSize on Read");
- throw SQL.InvalidInternalPacketSize(StringsHelper.GetString(Strings.SqlMisc_InvalidArraySizeMessage));
- }
-
- if (!usedPartialPacket)
- {
- _lastSuccessfulIOTimer._value = DateTime.UtcNow.Ticks;
-
- SetBuffer(_inBuff, 0, (int)dataSize);
- }
-
- bool recurse = false;
- bool appended = false;
- do
- {
- if (recurse && appended)
- {
- SetBuffer(new byte[_inBuff.Length], 0, 0);
- appended = false;
- }
- MultiplexPackets(
- _inBuff, _inBytesUsed, _inBytesRead,
- PartialPacket,
- out int newDataOffset,
- out int newDataLength,
- out Packet remainderPacket,
- out bool consumeInputDirectly,
- out bool consumePartialPacket,
- out bool remainderPacketProduced,
- out recurse
- );
- bool bufferIsPartialCompleted = false;
-
- // if a partial packet was reconstructed it must be handled first
- if (consumePartialPacket)
- {
- if (_snapshot != null)
- {
- _snapshot.AppendPacketData(PartialPacket.Buffer, PartialPacket.CurrentLength);
- SetBuffer(new byte[_inBuff.Length], 0, 0);
- appended = true;
- }
- else
- {
- SetBuffer(PartialPacket.Buffer, 0, PartialPacket.CurrentLength);
-
- }
- bufferIsPartialCompleted = true;
- ClearPartialPacket();
- }
-
- // if the remaining data can be processed directly it must be second
- if (consumeInputDirectly)
- {
- // if some data was taken from the new packet adjust the counters
- if (dataSize != newDataLength || 0 != newDataOffset)
- {
- SetBuffer(_inBuff, newDataOffset, newDataLength);
- }
-
- if (_snapshot != null)
- {
- _snapshot.AppendPacketData(_inBuff, _inBytesRead);
- // 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);
- appended = true;
- }
- else
- {
- SetBuffer(_inBuff, 0, _inBytesRead);
- }
- bufferIsPartialCompleted = true;
- }
- else
- {
- // whatever is in the input buffer should not be directly consumed
- // and is contained in the partial or remainder packets so make sure
- // we don't process it
- if (!bufferIsPartialCompleted)
- {
- SetBuffer(_inBuff, 0, 0);
- }
- }
-
- // if there is a remainder it must be last
- if (remainderPacketProduced)
- {
- SetPartialPacket(remainderPacket);
- 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);
- }
- }
-
- } while (recurse && _snapshot != null);
-
- if (_snapshot != null)
- {
- if (_snapshotStatus != SnapshotStatus.NotActive && appended)
- {
- _snapshot.MoveNext();
- }
- }
-
- SniReadStatisticsAndTracing();
- SqlClientEventSource.Log.TryAdvancedTraceBinEvent("TdsParser.ReadNetworkPacketAsyncCallback | INFO | ADV | State Object Id {0}, Packet read. In Buffer {1}, In Bytes Read: {2}", ObjectID, _inBuff, (ushort)_inBytesRead);
-
- AssertValidState();
- }
- else
- {
- throw SQL.ParsingError(ParsingErrorState.ProcessSniPacketFailed);
- }
- }
- }
-
- private void SetPartialPacket(Packet packet)
- {
- if (_partialPacket != null && packet != null)
- {
- throw new InvalidOperationException("partial packet cannot be non-null when setting to non=null");
- }
- _partialPacket = packet;
- }
-
- private void ClearPartialPacket()
- {
- Packet partialPacket = _partialPacket;
- _partialPacket = null;
-#if DEBUG
- if (partialPacket != null)
- {
- partialPacket.Dispose();
- }
-#endif
- }
-
- // this check is used in two places that must be identical so it is
- // extracted into a method, do not inline this method
- internal bool PartialPacketContainsCompletePacket()
- {
- Packet partialPacket = _partialPacket;
- return partialPacket != null && partialPacket.ContainsCompletePacket;
- }
-
- private static void MultiplexPackets(
- byte[] dataBuffer, int dataOffset, int dataLength,
- Packet partialPacket,
- out int newDataOffset,
- out int newDataLength,
- out Packet remainderPacket,
- out bool consumeInputDirectly,
- out bool consumePartialPacket,
- out bool createdRemainderPacket,
- out bool recurse
- )
- {
- Debug.Assert(dataBuffer != null);
-
- ReadOnlySpan data = dataBuffer.AsSpan(dataOffset, dataLength);
- remainderPacket = null;
- consumeInputDirectly = false;
- consumePartialPacket = false;
- createdRemainderPacket = false;
- recurse = false;
-
- newDataLength = dataLength;
- newDataOffset = dataOffset;
-
- int bytesConsumed = 0;
-
- if (partialPacket != null)
- {
- if (!partialPacket.HasDataLength)
- {
- // we need to get enough bytes to read the packet header
- int headerBytesNeeded = Math.Max(0, TdsEnums.HEADER_LEN - partialPacket.CurrentLength);
- if (headerBytesNeeded > 0)
- {
- int headerBytesAvailable = Math.Min(data.Length, headerBytesNeeded);
-
- Span headerTarget = partialPacket.Buffer.AsSpan(partialPacket.CurrentLength, headerBytesAvailable);
- ReadOnlySpan headerSource = data.Slice(0, headerBytesAvailable);
- headerSource.CopyTo(headerTarget);
-
- partialPacket.CurrentLength = partialPacket.CurrentLength + headerBytesAvailable;
- bytesConsumed += headerBytesAvailable;
- data = data.Slice(headerBytesAvailable);
- }
- if (partialPacket.HasHeader)
- {
- partialPacket.DataLength = Packet.GetDataLengthFromHeader(partialPacket);
- }
- }
-
- if (partialPacket.HasDataLength)
- {
- if (partialPacket.CurrentLength < partialPacket.RequiredLength)
- {
- // the packet length is known so take as much data as possible from the incoming
- // data to try and complete the packet
-
- int payloadBytesNeeded = partialPacket.DataLength - (partialPacket.CurrentLength - TdsEnums.HEADER_LEN);
- int payloadBytesAvailable = Math.Min(data.Length, payloadBytesNeeded);
-
- ReadOnlySpan payloadSource = data.Slice(0, payloadBytesAvailable);
- Span payloadTarget = partialPacket.Buffer.AsSpan(partialPacket.CurrentLength, payloadBytesAvailable);
- payloadSource.CopyTo(payloadTarget);
-
- partialPacket.CurrentLength = partialPacket.CurrentLength + payloadBytesAvailable;
- bytesConsumed += payloadBytesAvailable;
- data = data.Slice(payloadBytesAvailable);
- }
- else if (partialPacket.CurrentLength > partialPacket.RequiredLength)
- {
- // the partial packet contains a complete packet of data and also contains
- // data from a following packet
-
- // the TDS spec requires that all packets be of the defined packet size apart from
- // the last packet of a response. This means that is should not possible to have more than
- // 2 packet fragments in a single buffer like this:
- // - first packet caused the partial
- // - second packet is the one we have just unpacked
- // - third packet is the extra data we have found
- // however, due to the timing of cancellation it is possible that a response token stream
- // has ended before an attention message response is sent leaving us with a short final
- // packet and an additional short cancel packet following it
-
- // this should only happen when the caller is trying to consume the partial packet
- // and does not have new input data
-
- int remainderLength = partialPacket.CurrentLength - partialPacket.RequiredLength;
-
- partialPacket.CurrentLength = partialPacket.RequiredLength;
-
- remainderPacket = new Packet
- {
- Buffer = new byte[dataBuffer.Length],
- CurrentLength = remainderLength,
- };
- remainderPacket.SetCreatedBy(1);
-
- ReadOnlySpan remainderSource = partialPacket.Buffer.AsSpan(TdsEnums.HEADER_LEN + partialPacket.DataLength, remainderLength);
- Span remainderTarget = remainderPacket.Buffer.AsSpan(0, remainderLength);
- remainderSource.CopyTo(remainderTarget);
-
- createdRemainderPacket = true;
-
- recurse = SetupRemainderPacket(remainderPacket);
- }
-
- if (partialPacket.CurrentLength == partialPacket.RequiredLength)
- {
- // partial packet has been completed
- consumePartialPacket = true;
- }
- }
-
- if (bytesConsumed > 0)
- {
- if (data.Length > 0)
- {
- // some data has been taken from the buffer and put into the partial
- // packet buffer. We have data left so move the data we have to the
- // start of the buffer so we can pass the buffer back as zero based
- // to the caller avoiding offset calculations in the rest of this method
- Buffer.BlockCopy(
- dataBuffer, dataOffset + bytesConsumed, // from
- dataBuffer, dataOffset, // to
- dataLength - bytesConsumed // for
- );
-#if DEBUG
- // for debugging purposes fill the removed data area with an easily
- // recognisable pattern so we can see if it is misused
- Span removed = dataBuffer.AsSpan(dataOffset + (dataLength - bytesConsumed), bytesConsumed);
- removed.Fill(0xFF);
-#endif
-
- // then recreate the data span so that we're looking at the data
- // that has been moved
- data = dataBuffer.AsSpan(dataOffset, dataLength - bytesConsumed);
- }
-
- newDataLength = dataLength - bytesConsumed;
- }
- }
-
- // partial packet handling should not make decisions about consuming the input buffer
- Debug.Assert(!consumeInputDirectly);
- // partial packet handling may only create a remainder packet when it is trying to consume the partial packet and has no incoming data
- Debug.Assert(!createdRemainderPacket || data.Length == 0);
-
- if (data.Length > 0)
- {
- if (data.Length >= TdsEnums.HEADER_LEN)
- {
- // we have enough bytes to read the packet header and see how
- // much data we are expecting it to contain
- int packetDataLength = Packet.GetDataLengthFromHeader(data);
-
- if (data.Length == TdsEnums.HEADER_LEN + packetDataLength)
- {
- if (!consumePartialPacket)
- {
- // we can tell the caller that they should directly consume the data in
- // the input buffer, this is the happy path
- consumeInputDirectly = true;
- }
- else
- {
- // we took some data from the input to reconstruct the partial packet
- // so we can't tell the caller to directly consume the packet in the
- // input buffer, we need to construct a new remainder packet and then
- // tell them to consume it
- remainderPacket = new Packet
- {
- Buffer = dataBuffer,
- CurrentLength = data.Length
- };
- remainderPacket.SetCreatedBy(2);
- createdRemainderPacket = true;
- recurse = SetupRemainderPacket(remainderPacket);
- }
- }
- else if (data.Length < TdsEnums.HEADER_LEN + packetDataLength)
- {
- // an incomplete packet so create a remainder packet to pass back
- remainderPacket = new Packet
- {
- Buffer = dataBuffer,
- DataLength = packetDataLength,
- CurrentLength = data.Length
- };
- remainderPacket.SetCreatedBy(3);
- createdRemainderPacket = true;
- recurse = SetupRemainderPacket(remainderPacket);
- }
- else // implied: current length > required length
- {
- // more data than required so need to split it out, but we can't do that
- // here so we need to tell the caller to take the remainder packet and then
- // call this function again
- if (consumePartialPacket)
- {
- // we are already telling the caller to consume the partial packet so we
- // can't tell them it to also consume the data in the buffer directly
- // so create a remainder packet and pass it back.
- remainderPacket = new Packet
- {
- Buffer = new byte[dataBuffer.Length],
- CurrentLength = data.Length
- };
- remainderPacket.SetCreatedBy(4);
- ReadOnlySpan remainderSource = data;
- Span remainderTarget = remainderPacket.Buffer.AsSpan(0, remainderPacket.CurrentLength);
- remainderSource.CopyTo(remainderTarget);
-
- createdRemainderPacket = true;
-
- recurse = SetupRemainderPacket(remainderPacket);
- }
- else
- {
- newDataLength = TdsEnums.HEADER_LEN + packetDataLength;
- int remainderLength = data.Length - (TdsEnums.HEADER_LEN + packetDataLength);
- remainderPacket = new Packet
- {
- Buffer = new byte[dataBuffer.Length],
- CurrentLength = remainderLength
- };
- remainderPacket.SetCreatedBy(5);
-
- ReadOnlySpan remainderSource = data.Slice(TdsEnums.HEADER_LEN + packetDataLength);
- Span remainderTarget = remainderPacket.Buffer.AsSpan(0, remainderLength);
- remainderSource.CopyTo(remainderTarget);
-#if DEBUG
- // for debugging purposes fill the removed data area with an easily
- // recognisable pattern so we can see if it is misused
- Span removed = dataBuffer.AsSpan(TdsEnums.HEADER_LEN + packetDataLength, remainderLength);
- removed.Fill(0xFF);
-#endif
- createdRemainderPacket = true;
- recurse = SetupRemainderPacket(remainderPacket);
-
- consumeInputDirectly = true;
- }
- }
- }
- else
- {
- // either:
- // 1) we took some data from the input to reconstruct the partial packet
- // 2) there was less than a single packet header of data received
- // in both cases we can't tell the caller to directly consume the packet
- // in the input buffer, we need to construct a new remainder packet with
- // the incomplete data and let the caller deal with it
- remainderPacket = new Packet
- {
- Buffer = dataBuffer,
- CurrentLength = data.Length
- };
- remainderPacket.SetCreatedBy(6);
- createdRemainderPacket = true;
- recurse = SetupRemainderPacket(remainderPacket);
- }
- }
-
- if (consumePartialPacket && consumeInputDirectly)
- {
- throw new InvalidOperationException($"MultiplexPackets cannot return both {nameof(consumePartialPacket)} and {nameof(consumeInputDirectly)}");
- }
- }
-
- private static bool SetupRemainderPacket(Packet packet)
- {
- Debug.Assert(packet != null);
- bool containsFullPacket = false;
- if (packet.HasHeader)
- {
- packet.DataLength = Packet.GetDataLengthFromHeader(packet);
- if (packet.HasDataLength && packet.CurrentLength >= packet.RequiredLength)
- {
- containsFullPacket = true;
- }
- }
-
- return containsFullPacket;
- }
-
-
- public void ProcessSniPacketCompat(PacketHandle packet, uint error)
- {
- if (error != 0)
- {
- if ((_parser.State == TdsParserState.Closed) || (_parser.State == TdsParserState.Broken))
- {
- // Do nothing with callback if closed or broken and error not 0 - callback can occur
- // after connection has been closed. PROBLEM IN NETLIB - DESIGN FLAW.
- return;
- }
-
- AddError(_parser.ProcessSNIError(this));
- AssertValidState();
- }
- else
- {
- uint dataSize = 0;
- uint getDataError = SniPacketGetData(packet, _inBuff, ref dataSize);
-
- if (getDataError == TdsEnums.SNI_SUCCESS)
- {
- if (_inBuff.Length < dataSize)
- {
- Debug.Assert(true, "Unexpected dataSize on Read");
- throw SQL.InvalidInternalPacketSize(StringsHelper.GetString(Strings.SqlMisc_InvalidArraySizeMessage));
- }
-
- _lastSuccessfulIOTimer._value = DateTime.UtcNow.Ticks;
- _inBytesRead = (int)dataSize;
- _inBytesUsed = 0;
-
- if (_snapshot != null)
- {
- _snapshot.AppendPacketData(_inBuff, _inBytesRead);
- if (_snapshotStatus != SnapshotStatus.NotActive)
- {
- _snapshot.MoveNext();
-#if DEBUG
- _snapshot.AssertCurrent();
-#endif
- }
- }
-
- SniReadStatisticsAndTracing();
- SqlClientEventSource.Log.TryAdvancedTraceBinEvent("TdsParser.ReadNetworkPacketAsyncCallback | INFO | ADV | State Object Id {0}, Packet read. In Buffer: {1}, In Bytes Read: {2}", ObjectID, _inBuff, _inBytesRead);
-
- AssertValidState();
- }
- else
- {
- throw SQL.ParsingError(ParsingErrorState.ProcessSniPacketFailed);
- }
- }
- }
- }
-}
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 16b924fd7b..da0e06cc14 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs
@@ -7,7 +7,6 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Security;
-using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@@ -91,14 +90,6 @@ public TimeoutState(int value)
public int IdentityValue => _value;
}
- private enum SnapshotStatus
- {
- NotActive,
- ReplayStarting,
- ReplayRunning,
- ContinueRunning
- }
-
private const int AttentionTimeoutSeconds = 5;
// Ticks to consider a connection "good" after a successful I/O (10,000 ticks = 1 ms)
@@ -251,7 +242,7 @@ private enum SnapshotStatus
internal TaskCompletionSource