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 _networkPacketTaskSource; private Timer _networkPacketTimeout; internal bool _syncOverAsync = true; - private SnapshotStatus _snapshotStatus; + private bool _snapshotReplay; private StateSnapshot _snapshot; private StateSnapshot _cachedSnapshot; internal ExecutionContext _executionContext; @@ -1187,11 +1178,13 @@ internal TdsOperationStatus TryProcessHeader() if (_partialHeaderBytesRead == _inputHeaderLen) { // All read - ReadOnlySpan header = _partialHeaderBuffer.AsSpan(0, TdsEnums.HEADER_LEN); _partialHeaderBytesRead = 0; - _messageStatus = Packet.GetStatusFromHeader(header); - _inBytesPacket = Packet.GetDataLengthFromHeader(header); - _spid = Packet.GetSpidFromHeader(header); + _inBytesPacket = ((int)_partialHeaderBuffer[TdsEnums.HEADER_LEN_FIELD_OFFSET] << 8 | + (int)_partialHeaderBuffer[TdsEnums.HEADER_LEN_FIELD_OFFSET + 1]) - _inputHeaderLen; + + _messageStatus = _partialHeaderBuffer[1]; + _spid = _partialHeaderBuffer[TdsEnums.SPID_OFFSET] << 8 | + _partialHeaderBuffer[TdsEnums.SPID_OFFSET + 1]; SqlClientEventSource.Log.TryAdvancedTraceEvent("TdsParserStateObject.TryProcessHeader | ADV | State Object Id {0}, Client Connection Id {1}, Server process Id (SPID) {2}", _objectID, _parser?.Connection?.ClientConnectionId, _spid); } @@ -1227,10 +1220,11 @@ internal TdsOperationStatus TryProcessHeader() else { // normal header processing... - ReadOnlySpan header = _inBuff.AsSpan(_inBytesUsed, TdsEnums.HEADER_LEN); - _messageStatus = Packet.GetStatusFromHeader(header); - _inBytesPacket = Packet.GetDataLengthFromHeader(header); - _spid = Packet.GetSpidFromHeader(header); + _messageStatus = _inBuff[_inBytesUsed + 1]; + _inBytesPacket = (_inBuff[_inBytesUsed + TdsEnums.HEADER_LEN_FIELD_OFFSET] << 8 | + _inBuff[_inBytesUsed + TdsEnums.HEADER_LEN_FIELD_OFFSET + 1]) - _inputHeaderLen; + _spid = _inBuff[_inBytesUsed + TdsEnums.SPID_OFFSET] << 8 | + _inBuff[_inBytesUsed + TdsEnums.SPID_OFFSET + 1]; #if NET SqlClientEventSource.Log.TryAdvancedTraceEvent("TdsParserStateObject.TryProcessHeader | ADV | State Object Id {0}, Client Connection Id {1}, Server process Id (SPID) {2}", _objectID, _parser?.Connection?.ClientConnectionId, _spid); #endif @@ -1370,7 +1364,9 @@ internal bool SetPacketSize(int size) // Allocate or re-allocate _inBuff. if (_inBuff == null) { - SetBuffer(new byte[size], 0, 0); + _inBuff = new byte[size]; + _inBytesRead = 0; + _inBytesUsed = 0; } else if (size != _inBuff.Length) { @@ -1380,24 +1376,28 @@ internal bool SetPacketSize(int size) // if we still have data left in the buffer we must keep that array reference and then copy into new one byte[] temp = _inBuff; + _inBuff = new byte[size]; + // copy remainder of unused data int remainingData = _inBytesRead - _inBytesUsed; - if ((temp.Length < _inBytesUsed + remainingData) || (size < remainingData)) + if ((temp.Length < _inBytesUsed + remainingData) || (_inBuff.Length < remainingData)) { - string errormessage = StringsHelper.GetString(Strings.SQL_InvalidInternalPacketSize) + ' ' + temp.Length + ", " + _inBytesUsed + ", " + remainingData + ", " + size; + string errormessage = StringsHelper.GetString(Strings.SQL_InvalidInternalPacketSize) + ' ' + temp.Length + ", " + _inBytesUsed + ", " + remainingData + ", " + _inBuff.Length; throw SQL.InvalidInternalPacketSize(errormessage); } + Buffer.BlockCopy(temp, _inBytesUsed, _inBuff, 0, remainingData); - byte[] inBuff = new byte[size]; - Buffer.BlockCopy(temp, _inBytesUsed, inBuff, 0, remainingData); - SetBuffer(inBuff, 0, remainingData); + _inBytesRead = _inBytesRead - _inBytesUsed; + _inBytesUsed = 0; AssertValidState(); } else { // buffer is empty - just create the new one that is double the size of the old one - SetBuffer(new byte[size], 0, 0); + _inBuff = new byte[size]; + _inBytesRead = 0; + _inBytesUsed = 0; } } @@ -1412,11 +1412,6 @@ internal bool SetPacketSize(int size) return false; } - internal int GetPacketSize() - { - return _inBuff.Length; - } - /////////////////////////////////////// // Buffer read methods - data values // /////////////////////////////////////// @@ -1443,17 +1438,12 @@ internal TdsOperationStatus TryPeekByte(out byte value) // bytes from the in buffer. public TdsOperationStatus TryReadByteArray(Span buff, int len) { - return TryReadByteArray(buff, len, out _, 0, false); - } - - public TdsOperationStatus TryReadByteArray(Span buff, int len, out int totalRead) - { - return TryReadByteArray(buff, len, out totalRead, 0, false); + return TryReadByteArray(buff, len, out _); } // NOTE: This method must be retriable WITHOUT replaying a snapshot // Every time you call this method increment the offset and decrease len by the value of totalRead - public TdsOperationStatus TryReadByteArray(Span buff, int len, out int totalRead, int startOffset, bool writeDataSizeToSnapshot) + public TdsOperationStatus TryReadByteArray(Span buff, int len, out int totalRead) { totalRead = 0; @@ -1478,10 +1468,6 @@ public TdsOperationStatus TryReadByteArray(Span buff, int len, out int tot Debug.Assert(buff.IsEmpty || buff.Length >= len, "Invalid length sent to ReadByteArray()!"); - - totalRead += startOffset; - len -= startOffset; - // loop through and read up to array length while (len > 0) { @@ -1508,58 +1494,12 @@ public TdsOperationStatus TryReadByteArray(Span buff, int len, out int tot _inBytesPacket -= bytesToRead; len -= bytesToRead; - if (writeDataSizeToSnapshot) - { - AddSnapshotDataSize(bytesToRead); - } - AssertValidState(); } return TdsOperationStatus.Done; } - public TdsOperationStatus TryReadByteArrayWithContinue(int length, out byte[] bytes) - { - bytes = null; - int offset = 0; - byte[] temp = null; - (bool canContinue, bool isStarting, bool isContinuing) = GetSnapshotStatuses(); - if (canContinue) - { - temp = TryTakeSnapshotStorage() as byte[]; - Debug.Assert(temp != null || !isContinuing, "if continuing stored buffer must be present to contain previous data to continue from"); - Debug.Assert(bytes == null || bytes.Length == length, "stored buffer length must be null or must have been created with the correct length"); - - if (temp != null) - { - offset = GetSnapshotTotalSize(); - } - } - - - if (temp == null) - { - temp = new byte[length]; - } - - TdsOperationStatus result = TryReadByteArray(temp, length, out _, offset, isStarting || isContinuing); - - if (result == TdsOperationStatus.Done) - { - bytes = temp; - } - else if (result == TdsOperationStatus.NeedMoreData) - { - if (canContinue) - { - SetSnapshotStorage(temp); - } - } - - return result; - } - // Takes no arguments and returns a byte from the buffer. If the buffer is empty, it is filled // before the byte is returned. internal TdsOperationStatus TryReadByte(out byte value) @@ -1679,7 +1619,7 @@ internal TdsOperationStatus TryReadInt32(out int value) TdsOperationStatus result = TryReadByteArray(buffer, 4); if (result != TdsOperationStatus.Done) { - value = 0; + value = default; return result; } } @@ -1906,13 +1846,21 @@ internal TdsOperationStatus TryReadString(int length, out string value) if (((_inBytesUsed + cBytes) > _inBytesRead) || (_inBytesPacket < cBytes)) { - TdsOperationStatus result = TryReadByteArrayWithContinue(cBytes, out buf); + if (_bTmp == null || _bTmp.Length < cBytes) + { + _bTmp = new byte[cBytes]; + } + + TdsOperationStatus result = TryReadByteArray(_bTmp, cBytes); if (result != TdsOperationStatus.Done) { value = null; return result; } + // assign local to point to parser scratch buffer + buf = _bTmp; + AssertValidState(); } else @@ -1960,7 +1908,6 @@ internal TdsOperationStatus TryReadStringWithEncoding(int length, System.Text.En } byte[] buf = null; int offset = 0; - (bool canContinue, bool isStarting, bool isContinuing) = GetSnapshotStatuses(); if (isPlp) { @@ -1977,39 +1924,21 @@ internal TdsOperationStatus TryReadStringWithEncoding(int length, System.Text.En { if (((_inBytesUsed + length) > _inBytesRead) || (_inBytesPacket < length)) { - int startOffset = 0; - if (canContinue) - { - buf = TryTakeSnapshotStorage() as byte[]; - Debug.Assert(buf != null || !isContinuing, "if continuing stored buffer must be present to contain previous data to continue from"); - Debug.Assert(buf == null || buf.Length == length, "stored buffer length must be null or must have been created with the correct length"); - - if (buf != null) - { - startOffset = GetSnapshotTotalSize(); - } - } - - if (buf == null || buf.Length < length) + if (_bTmp == null || _bTmp.Length < length) { - buf = new byte[length]; + _bTmp = new byte[length]; } - TdsOperationStatus result = TryReadByteArray(buf, length, out _, startOffset, canContinue); - + TdsOperationStatus result = TryReadByteArray(_bTmp, length); if (result != TdsOperationStatus.Done) { - if (result == TdsOperationStatus.NeedMoreData) - { - if (canContinue) - { - SetSnapshotStorage(buf); - } - } value = null; return result; } + // assign local to point to parser scratch buffer + buf = _bTmp; + AssertValidState(); } else @@ -2054,7 +1983,7 @@ internal TdsOperationStatus TryReadPlpLength(bool returnPlpNullIfNull, out ulong // bool firstchunk = false; bool isNull = false; - Debug.Assert(_longlenleft == 0, "Out of sync length read request"); + Debug.Assert(_longlenleft == 0, "Out of synch length read request"); if (_longlen == 0) { // First chunk is being read. Find out what type of chunk it is @@ -2123,28 +2052,17 @@ internal int ReadPlpBytesChunk(byte[] buff, int offset, int len) return value; } - 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(); - } - return TryReadPlpBytes(ref buff, offset, len, out totalBytesRead, canContinue, canContinue, compatibilityMode); - } - // Reads the requested number of bytes 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. // Returns the actual bytes read. // NOTE: This method must be retriable WITHOUT replaying a snapshot // Every time you call this method increment the offset and decrease len by the value of totalBytesRead - internal TdsOperationStatus TryReadPlpBytes(ref byte[] buff, int offset, int len, out int totalBytesRead, bool canContinue, bool writeDataSizeToSnapshot, bool compatibilityMode) + internal TdsOperationStatus TryReadPlpBytes(ref byte[] buff, int offset, int len, out int totalBytesRead) { - totalBytesRead = 0; + int bytesRead; + int bytesLeft; + byte[] newbuf; if (_longlen == 0) { @@ -2162,31 +2080,19 @@ internal TdsOperationStatus TryReadPlpBytes(ref byte[] buff, int offset, int len Debug.Assert(_longlen != TdsEnums.SQL_PLP_NULL, "Out of sync plp read request"); Debug.Assert((buff == null && offset == 0) || (buff.Length >= offset + len), "Invalid length sent to ReadPlpBytes()!"); - int bytesLeft = len; + bytesLeft = 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 + // if there is a snapshot and 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) - { - offset = _snapshot.GetPacketDataOffset(); - totalBytesRead = offset; - } + buff = _snapshot._plpBuffer; + _snapshot._plpBuffer = null; } - if ((ulong)(buff?.Length ?? 0) != _longlen) { // if the buffer is null or the wrong length create one to use @@ -2215,19 +2121,20 @@ internal TdsOperationStatus TryReadPlpBytes(ref byte[] buff, int offset, int len buff = new byte[_longlenleft]; } + totalBytesRead = 0; + while (bytesLeft > 0) { int bytesToRead = (int)Math.Min(_longlenleft, (ulong)bytesLeft); if (buff.Length < (offset + bytesToRead)) { // Grow the array - byte[] newbuf = new byte[offset + bytesToRead]; + newbuf = new byte[offset + bytesToRead]; Buffer.BlockCopy(buff, 0, newbuf, 0, offset); buff = newbuf; - newbuf = null; } - TdsOperationStatus result = TryReadByteArray(buff.AsSpan(offset), bytesToRead, out int bytesRead); + TdsOperationStatus result = TryReadByteArray(buff.AsSpan(offset), bytesToRead, out bytesRead); Debug.Assert(bytesRead <= bytesLeft, "Read more bytes than we needed"); Debug.Assert((ulong)bytesRead <= _longlenleft, "Read more bytes than is available"); @@ -2237,29 +2144,14 @@ 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) - { - AddSnapshotDataSize(bytesRead); - } + _snapshot._plpBuffer = buff; } return result; } - if (writeDataSizeToSnapshot && canContinue) - { - AddSnapshotDataSize(bytesRead); - } if (_longlenleft == 0) { @@ -2267,16 +2159,11 @@ 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 + _snapshot._plpBuffer = buff; } return result; } @@ -2286,15 +2173,7 @@ internal TdsOperationStatus TryReadPlpBytes(ref byte[] buff, int offset, int len // Catch the point where we read the entire plp data stream and clean up state if (_longlenleft == 0) // Data read complete - { break; - } - } - - if (canContinue) - { - SetSnapshotStorage(null); - ClearSnapshotDataSize(); } return TdsOperationStatus.Done; } @@ -2728,30 +2607,14 @@ internal TdsOperationStatus TryReadNetworkPacket() TdsOperationStatus result = TdsOperationStatus.InvalidData; if (_snapshot != null) { - if (_snapshotStatus != SnapshotStatus.NotActive) + if (_snapshotReplay) { #if DEBUG - string stackTrace = null; - if (s_checkNetworkPacketRetryStacks) - { - // in debug builds stack traces contain line numbers so if we want to be - // able to compare the stack traces they must all be created in the same - // location in the code - stackTrace = Environment.StackTrace; - } + // in debug builds stack traces contain line numbers so if we want to be + // able to compare the stack traces they must all be created in the same + // location in the code + string stackTrace = Environment.StackTrace; #endif - bool capturedAsContinue = false; - if (_snapshotStatus == SnapshotStatus.ReplayRunning || _snapshotStatus == SnapshotStatus.ReplayStarting) - { - if (_bTmpRead == 0 && _partialHeaderBytesRead == 0 && _longlenleft == 0 && _snapshot.ContinueEnabled) - { - // no temp between packets - // mark this point as continue-able - _snapshot.CaptureAsContinue(this); - capturedAsContinue = true; - } - } - if (_snapshot.MoveNext()) { #if DEBUG @@ -2762,46 +2625,24 @@ internal TdsOperationStatus TryReadNetworkPacket() #endif return TdsOperationStatus.Done; } +#if DEBUG else { -#if DEBUG if (s_checkNetworkPacketRetryStacks) { _lastStack = stackTrace; } -#endif - if (_bTmpRead == 0 && _partialHeaderBytesRead == 0 && _longlenleft == 0 && _snapshot.ContinueEnabled && !capturedAsContinue) - { - // no temp between packets - // mark this point as continue-able - _snapshot.CaptureAsContinue(this); - capturedAsContinue = true; - } } +#endif } // previous buffer is in snapshot _inBuff = new byte[_inBuff.Length]; - result = TdsOperationStatus.NeedMoreData; - } - - if (result == TdsOperationStatus.InvalidData && PartialPacket != null && !PartialPacket.ContainsCompletePacket) - { - result = TdsOperationStatus.NeedMoreData; } if (_syncOverAsync) { ReadSniSyncOverAsync(); - while (_inBytesRead == 0) - { - // a partial packet must have taken the packet data so we - // need to read more data to complete the packet, but we - // can't return NeedMoreData in sync mode so we have to - // spin fetching more data here until we have something - // that the caller can read - ReadSniSyncOverAsync(); - } return TdsOperationStatus.Done; } @@ -2825,10 +2666,7 @@ internal TdsOperationStatus TryReadNetworkPacket() internal void PrepareReplaySnapshot() { _networkPacketTaskSource = null; - if (!_snapshot.MoveToContinue()) - { - _snapshot.MoveToStart(); - } + _snapshot.MoveToStart(); } internal void ReadSniSyncOverAsync() @@ -2839,7 +2677,7 @@ internal void ReadSniSyncOverAsync() } PacketHandle readPacket = default; - bool readFromNetwork = !PartialPacketContainsCompletePacket(); + uint error; RuntimeHelpers.PrepareConstrainedRegions(); @@ -2849,14 +2687,7 @@ internal void ReadSniSyncOverAsync() Interlocked.Increment(ref _readingCount); shouldDecrement = true; - if (readFromNetwork) - { - readPacket = ReadSyncOverAsync(GetTimeoutRemaining(), out error); - } - else - { - error = TdsEnums.SNI_SUCCESS; - } + readPacket = ReadSyncOverAsync(GetTimeoutRemaining(), out error); Interlocked.Decrement(ref _readingCount); shouldDecrement = false; @@ -2870,12 +2701,9 @@ internal void ReadSniSyncOverAsync() { // Success - process results! - if (readFromNetwork) - { - Debug.Assert(!IsPacketEmpty(readPacket), "ReadNetworkPacket cannot be null in synchronous operation!"); - } + Debug.Assert(!IsPacketEmpty(readPacket), "ReadNetworkPacket cannot be null in synchronous operation!"); - ProcessSniPacket(readPacket, TdsEnums.SNI_SUCCESS); + ProcessSniPacket(readPacket, 0); #if DEBUG if (s_forcePendingReadsToWaitForUser) { @@ -2887,12 +2715,9 @@ internal void ReadSniSyncOverAsync() #endif } else - { + { // Failure! - if (readFromNetwork) - { - Debug.Assert(!IsValidPacket(readPacket), "unexpected readPacket without corresponding SNIPacketRelease"); - } + Debug.Assert(!IsValidPacket(readPacket), "unexpected readPacket without corresponding SNIPacketRelease"); ReadSniError(this, error); } @@ -2904,12 +2729,9 @@ internal void ReadSniSyncOverAsync() Interlocked.Decrement(ref _readingCount); } - if (readFromNetwork) + if (!IsPacketEmpty(readPacket)) { - if (!IsPacketEmpty(readPacket)) - { - ReleasePacket(readPacket); - } + ReleasePacket(readPacket); } AssertValidState(); @@ -3133,7 +2955,6 @@ internal void ReadSni(TaskCompletionSource completion) PacketHandle readPacket = default; uint error = 0; - bool readFromNetwork = true; RuntimeHelpers.PrepareConstrainedRegions(); try @@ -3177,35 +2998,21 @@ internal void ReadSni(TaskCompletionSource completion) finally { Interlocked.Increment(ref _readingCount); - try - { - handle = SessionHandle; - readFromNetwork = !PartialPacketContainsCompletePacket(); - if (readFromNetwork) - { - if (!handle.IsNull) - { - IncrementPendingCallbacks(); + handle = SessionHandle; + if (!handle.IsNull) + { + IncrementPendingCallbacks(); - readPacket = ReadAsync(handle, out error); + readPacket = ReadAsync(handle, out error); - if (!(TdsEnums.SNI_SUCCESS == error || TdsEnums.SNI_SUCCESS_IO_PENDING == error)) - { - DecrementPendingCallbacks(false); // Failure - we won't receive callback! - } - } - } - else + if (!(TdsEnums.SNI_SUCCESS == error || TdsEnums.SNI_SUCCESS_IO_PENDING == error)) { - readPacket = default; - error = TdsEnums.SNI_SUCCESS; + DecrementPendingCallbacks(false); // Failure - we won't receive callback! } } - finally - { - Interlocked.Decrement(ref _readingCount); - } + + Interlocked.Decrement(ref _readingCount); } if (handle.IsNull) @@ -3215,12 +3022,12 @@ internal void ReadSni(TaskCompletionSource completion) if (TdsEnums.SNI_SUCCESS == error) { // Success - process results! - Debug.Assert(!readFromNetwork || IsValidPacket(readPacket) , "ReadNetworkPacket should not have been null on this async operation!"); + Debug.Assert(IsValidPacket(readPacket), "ReadNetworkPacket should not have been null on this async operation!"); // Evaluate this condition for MANAGED_SNI. This may not be needed because the network call is happening Async and only the callback can receive a success. ReadAsyncCallback(IntPtr.Zero, readPacket, 0); // Only release packet for Managed SNI as for Native SNI packet is released in finally block. - if (TdsParserStateObjectFactory.UseManagedSNI && readFromNetwork && !IsPacketEmpty(readPacket)) + if (TdsParserStateObjectFactory.UseManagedSNI && !IsPacketEmpty(readPacket)) { ReleasePacket(readPacket); } @@ -3258,7 +3065,7 @@ internal void ReadSni(TaskCompletionSource completion) { if (!TdsParserStateObjectFactory.UseManagedSNI) { - if (readFromNetwork && !IsPacketEmpty(readPacket)) + if (!IsPacketEmpty(readPacket)) { // Be sure to release packet, otherwise it will be leaked by native. ReleasePacket(readPacket); @@ -3520,9 +3327,8 @@ internal void SetSnapshot() snapshot.Clear(); } _snapshot = snapshot; - Debug.Assert(_snapshot._storage == null); _snapshot.CaptureAsStart(this); - _snapshotStatus = SnapshotStatus.NotActive; + _snapshotReplay = false; } internal void ResetSnapshot() @@ -3534,102 +3340,9 @@ internal void ResetSnapshot() snapshot.Clear(); Interlocked.CompareExchange(ref _cachedSnapshot, snapshot, null); } - _snapshotStatus = SnapshotStatus.NotActive; - } - - internal bool IsSnapshotAvailable() - { - return _snapshot != null && _snapshot.ContinueEnabled; - } - /// - /// Returns true if the state object is in the state of continuing from a previously stored snapshot packet - /// meaning that consumers should resume from the point where they last needed more data instead of beginning - /// to process packets in the snapshot from the beginning again - /// - /// - internal bool IsSnapshotContinuing() - { - return _snapshot != null && - _snapshot.ContinueEnabled && - _snapshotStatus == TdsParserStateObject.SnapshotStatus.ContinueRunning; - } - - internal (bool CanContinue, bool IsStarting, bool IsContinuing) GetSnapshotStatuses() - { - bool canContinue = _snapshot != null && _snapshot.ContinueEnabled && _snapshotStatus != SnapshotStatus.NotActive; - bool isStarting = false; - bool isContinuing = false; - if (canContinue) - { - isStarting = _snapshotStatus == SnapshotStatus.ReplayStarting; - isContinuing = _snapshotStatus == SnapshotStatus.ContinueRunning; - } - return (canContinue, isStarting, isContinuing); - } - - internal int GetSnapshotStorageLength() - { - Debug.Assert(_snapshot != null && _snapshot.ContinueEnabled, "should not access snapshot accessor functions without first checking that the snapshot is available"); - return (_snapshot?._storage as IList)?.Count ?? 0; - } - - internal object TryTakeSnapshotStorage() - { - Debug.Assert(_snapshot != null, "should not access snapshot accessor functions without first checking that the snapshot is present"); - object buffer = null; - if (_snapshot != null) - { - buffer = _snapshot._storage; - _snapshot._storage = null; - } - return buffer; - } - - internal void SetSnapshotStorage(object buffer) - { - Debug.Assert(_snapshot != null, "should not access snapshot accessor functions without first checking that the snapshot is available"); - if (_snapshot != null) - { - _snapshot._storage = buffer; - } - } - - /// - /// stores the countOfBytesCopiedFromCurrentPacket of bytes copied from the current packet in the snapshot allowing the total - /// countOfBytesCopiedFromCurrentPacket to be calculated - /// - /// - internal void AddSnapshotDataSize(int countOfBytesCopiedFromCurrentPacket) - { - Debug.Assert(_snapshot != null && _snapshot.ContinueEnabled, "_snapshot must exist to store packet data size"); - _snapshot.SetPacketDataSize(countOfBytesCopiedFromCurrentPacket); + _snapshotReplay = false; } - internal void ClearSnapshotDataSize() - { - Debug.Assert(_snapshot != null, "_snapshot must exist to store packet data size"); - _snapshot?.ClearPacketDataSize(); - } - - internal int GetSnapshotTotalSize() - { - Debug.Assert(_snapshot != null && _snapshot.ContinueEnabled, "_snapshot must exist to read total size"); - Debug.Assert(_snapshotStatus != SnapshotStatus.NotActive, "_snapshot must be active read total size"); - return _snapshot.GetPacketDataOffset(); - } - - internal int GetSnapshotDataSize() - { - Debug.Assert(_snapshot != null && _snapshot.ContinueEnabled, "_snapshot must exist to read packet data size"); - Debug.Assert(_snapshotStatus != SnapshotStatus.NotActive, "_snapshot must be active read packet data size"); - return _snapshot.GetPacketDataSize(); - } - - internal int GetSnapshotPacketID() - { - Debug.Assert(_snapshot != null && _snapshot.ContinueEnabled, "_snapshot must exist to read packet data size"); - return _snapshot.GetPacketID(); - } /// /// Debug Only: Ensures that the TdsParserStateObject has no lingering state and can safely be re-used @@ -3642,7 +3355,7 @@ internal void AssertStateIsClean() if ((parser != null) && (parser.State != TdsParserState.Closed) && (parser.State != TdsParserState.Broken)) { // Async reads - Debug.Assert(_snapshot == null && _snapshotStatus == SnapshotStatus.NotActive, "StateObj has leftover snapshot state"); + Debug.Assert(_snapshot == null && !_snapshotReplay, "StateObj has leftover snapshot state"); Debug.Assert(!_asyncReadWithoutSnapshot, "StateObj has AsyncReadWithoutSnapshot still enabled"); Debug.Assert(_executionContext == null, "StateObj has a stored execution context from an async read"); // Async writes @@ -3735,7 +3448,7 @@ internal void CloneCleanupAltMetaDataSetArray() } } - internal sealed partial class StateSnapshot + sealed partial class StateSnapshot { private sealed partial class PacketData { @@ -3744,33 +3457,6 @@ private sealed partial class PacketData public PacketData NextPacket; public PacketData PrevPacket; - /// - /// Stores the data size of the total snapshot so far so that enumeration is not needed - /// to get the offset of the previous packet data in the stored buffer - /// - public int RunningDataSize; - - public int PacketID => Packet.GetIDFromHeader(Buffer.AsSpan(0, TdsEnums.HEADER_LEN)); - - internal int GetPacketDataOffset() - { - int previous = 0; - if (PrevPacket != null) - { - previous = PrevPacket.RunningDataSize; - } - return previous; - } - internal int GetPacketDataSize() - { - int previous = 0; - if (PrevPacket != null) - { - previous = PrevPacket.RunningDataSize; - } - return Math.Max(RunningDataSize - previous, 0); - } - internal void Clear() { Buffer = null; @@ -3781,21 +3467,15 @@ internal void Clear() PrevPacket.NextPacket = null; PrevPacket = null; } - SetDebugStackImpl(null); - SetDebugPacketId(0); - SetDebugDataHash(); + SetDebugStackInternal(null); + SetDebugPacketIdInternal(0); } - internal void SetDebugStack(string value) => SetDebugStackImpl(value); - internal void SetDebugPacketId(int value) => SetDebugPacketIdImpl(value); - internal void SetDebugDataHash() => SetDebugDataHashImpl(); - - internal void CheckDebugDataHash() => CheckDebugDataHashImpl(); + internal void SetDebugStack(string value) => SetDebugStackInternal(value); + internal void SetDebugPacketId(int value) => SetDebugPacketIdInternal(value); - partial void SetDebugStackImpl(string value); - partial void SetDebugPacketIdImpl(int value); - partial void SetDebugDataHashImpl(); - partial void CheckDebugDataHashImpl(); + partial void SetDebugStackInternal(string value); + partial void SetDebugPacketIdInternal(int value); } #if DEBUG @@ -3817,189 +3497,84 @@ public PacketDataDebugView(PacketData data) _data = data; } - public string Type { - - get - { - if (_data != null && _data.Buffer!=null) - { - switch (_data.Buffer[0]) - { - case 1: return nameof(TdsEnums.MT_SQL); - case 2: return nameof(TdsEnums.MT_LOGIN); - case 3: return nameof(TdsEnums.MT_RPC); - case 4: return nameof(TdsEnums.MT_TOKENS); - case 5: return nameof(TdsEnums.MT_BINARY); - case 6: return nameof(TdsEnums.MT_ATTN); - case 7: return nameof(TdsEnums.MT_BULK); - case 8: return nameof(TdsEnums.MT_FEDAUTH); - case 9: return nameof(TdsEnums.MT_CLOSE); - case 10: return nameof(TdsEnums.MT_ERROR); - case 11: return nameof(TdsEnums.MT_ACK); - case 12: return nameof(TdsEnums.MT_ECHO); - case 13: return nameof(TdsEnums.MT_LOGOUT); - case 14: return nameof(TdsEnums.MT_TRANS); - case 15: return nameof(TdsEnums.MT_OLEDB); - case 16: return nameof(TdsEnums.MT_LOGIN7); - case 17: return nameof(TdsEnums.MT_SSPI); - case 18: return nameof(TdsEnums.MT_PRELOGIN); - default: return _data.Buffer[0].ToString("X2"); - } - } - return ""; - } - } - - public string Status + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public PacketData[] Items { get { - if (_data != null && _data.Buffer != null && _data.Buffer.Length > 1) + PacketData[] items = Array.Empty(); + if (_data != null) { - int status = Packet.GetStatusFromHeader(_data.Buffer); - StringBuilder buffer = new StringBuilder(10); - - if ((status & TdsEnums.ST_EOM) == TdsEnums.ST_EOM) - { - if (buffer.Length > 0) - { - buffer.Append(','); - } - buffer.Append(nameof(TdsEnums.ST_EOM)); - } - if ((status & TdsEnums.ST_AACK) == TdsEnums.ST_AACK) + int count = 0; + for (PacketData current = _data; current != null; current = current?.NextPacket) { - if (buffer.Length > 0) - { - buffer.Append(','); - } - buffer.Append(nameof(TdsEnums.ST_AACK)); - } - if ((status & TdsEnums.ST_BATCH) == TdsEnums.ST_BATCH) - { - if (buffer.Length > 0) - { - buffer.Append(','); - } - buffer.Append(nameof(TdsEnums.ST_BATCH)); - } - if ((status & TdsEnums.ST_RESET_CONNECTION) == TdsEnums.ST_RESET_CONNECTION) - { - if (buffer.Length > 0) - { - buffer.Append(','); - } - buffer.Append(nameof(TdsEnums.ST_RESET_CONNECTION)); + count++; } - if ((status & TdsEnums.ST_RESET_CONNECTION_PRESERVE_TRANSACTION) == TdsEnums.ST_RESET_CONNECTION_PRESERVE_TRANSACTION) + items = new PacketData[count]; + int index = 0; + for (PacketData current = _data; current != null; current = current?.NextPacket, index++) { - if (buffer.Length > 0) - { - buffer.Append(','); - } - buffer.Append(nameof(TdsEnums.ST_RESET_CONNECTION_PRESERVE_TRANSACTION)); + items[index] = current; } - - return buffer.ToString(); } - - return ""; + return items; } } - - public int Length => _data.DataLength; - - public int Spid => _data.SPID; - - public int PacketID => _data.PacketID; - - public ReadOnlySpan HeaderBytes => _data.GetHeaderSpan(); - - public ReadOnlySpan Data => _data.Buffer.AsSpan(TdsEnums.HEADER_LEN); - - public int RunningDataSize => _data.RunningDataSize; - - public PacketData NextPacket => _data.NextPacket; - public PacketData PrevPacket => _data.PrevPacket; } - public int DebugPacketId; + public int PacketId; public string Stack; - public byte[] Hash; - - public int SPID => Packet.GetSpidFromHeader(Buffer.AsSpan(0, TdsEnums.HEADER_LEN)); - - public bool IsEOM => Packet.GetIsEOMFromHeader(Buffer.AsSpan(0, TdsEnums.HEADER_LEN)); - public int DataLength => Packet.GetDataLengthFromHeader(Buffer.AsSpan(0, TdsEnums.HEADER_LEN)); + partial void SetDebugStackInternal(string value) => Stack = value; - public ReadOnlySpan GetHeaderSpan() => Buffer.AsSpan(0, TdsEnums.HEADER_LEN); + partial void SetDebugPacketIdInternal(int value) => PacketId = value; - partial void SetDebugStackImpl(string value) => Stack = value; - partial void SetDebugPacketIdImpl(int value) => DebugPacketId = value; - - partial void SetDebugDataHashImpl() + public override string ToString() { - if (Buffer != null) + //return $"{PacketId}: [{Buffer.Length}] ( {GetPacketDataOffset():D4}, {GetPacketTotalSize():D4} ) {(NextPacket != null ? @"->" : string.Empty)}"; + string byteString = null; + if (Buffer != null && Buffer.Length >= 12) { - using (MD5 hasher = MD5.Create()) + ReadOnlySpan bytes = Buffer.AsSpan(0, 12); + StringBuilder buffer = new StringBuilder(12 * 3 + 10); + buffer.Append('{'); + for (int index = 0; index < bytes.Length; index++) { - Hash = hasher.ComputeHash(Buffer, 0, Read); + buffer.AppendFormat("{0:X2}", bytes[index]); + buffer.Append(", "); } + buffer.Append("..."); + buffer.Append('}'); + byteString = buffer.ToString(); } - else - { - Hash = null; - } - + return $"{PacketId}: [{Read}] {byteString} {(NextPacket != null ? @"->" : string.Empty)}"; } + } +#endif - partial void CheckDebugDataHashImpl() - { - if (Hash == null) - { - if (Buffer != null && Read > 0) - { - throw new InvalidOperationException("Packet modification detected. Hash is null but packet contains non-null buffer"); - } - } - else - { - byte[] checkHash = null; - using (MD5 hasher = MD5.Create()) - { - checkHash = hasher.ComputeHash(Buffer, 0, Read); - } - - for (int index = 0; index < Hash.Length; index++) - { - if (Hash[index] != checkHash[index]) - { - throw new InvalidOperationException("Packet modification detected. Hash from packet creation does not match hash from packet check"); - } - } - } - } + private sealed class PLPData + { + public readonly ulong SnapshotLongLen; + public readonly ulong SnapshotLongLenLeft; - public override string ToString() + public PLPData(ulong snapshotLongLen, ulong snapshotLongLenLeft) { - return $"{PacketID}({GetPacketDataOffset()},{GetPacketDataSize()})"; + SnapshotLongLen = snapshotLongLen; + SnapshotLongLenLeft = snapshotLongLenLeft; } } -#endif private sealed class StateObjectData { private int _inBytesUsed; private int _inBytesPacket; + private PLPData _plpData; private byte _messageStatus; internal NullBitmap _nullBitmapInfo; private _SqlMetaDataSet _cleanupMetaData; internal _SqlMetaDataSetCollection _cleanupAltMetaDataSetArray; private SnapshottedStateFlags _state; - private ulong _longLen; - private ulong _longLenLeft; internal void Capture(TdsParserStateObject stateObj, bool trackStack = true) { @@ -4007,8 +3582,10 @@ internal void Capture(TdsParserStateObject stateObj, bool trackStack = true) _inBytesPacket = stateObj._inBytesPacket; _messageStatus = stateObj._messageStatus; _nullBitmapInfo = stateObj._nullBitmapInfo; // _nullBitmapInfo must be cloned before it is updated - _longLen = stateObj._longlen; - _longLenLeft = stateObj._longlenleft; + if (stateObj._longlen != 0 || stateObj._longlenleft != 0) + { + _plpData = new PLPData(stateObj._longlen, stateObj._longlenleft); + } _cleanupMetaData = stateObj._cleanupMetaData; _cleanupAltMetaDataSetArray = stateObj._cleanupAltMetaDataSetArray; // _cleanupAltMetaDataSetArray must be cloned before it is updated _state = stateObj._snapshottedState; @@ -4028,8 +3605,7 @@ internal void Clear(TdsParserStateObject stateObj, bool trackStack = true) _inBytesPacket = 0; _messageStatus = 0; _nullBitmapInfo = default; - _longLen = 0; - _longLenLeft = 0; + _plpData = null; _cleanupMetaData = null; _cleanupAltMetaDataSetArray = null; _state = SnapshottedStateFlags.None; @@ -4062,26 +3638,24 @@ internal void Restore(TdsParserStateObject stateObj) //else _stateObj._hasOpenResult is already == _snapshotHasOpenResult stateObj._snapshottedState = _state; - // reset plp state - stateObj._longlen = _longLen; - stateObj._longlenleft = _longLenLeft; - // Reset partially read state (these only need to be maintained if doing async without snapshot) stateObj._bTmpRead = 0; stateObj._partialHeaderBytesRead = 0; + + // reset plp state + stateObj._longlen = _plpData?.SnapshotLongLen ?? 0; + stateObj._longlenleft = _plpData?.SnapshotLongLenLeft ?? 0; } } private TdsParserStateObject _stateObj; private StateObjectData _replayStateData; - private StateObjectData _continueStateData; - internal object _storage; + internal byte[] _plpBuffer; private PacketData _lastPacket; private PacketData _firstPacket; private PacketData _current; - private PacketData _continuePacket; private PacketData _sparePacket; #if DEBUG @@ -4124,10 +3698,7 @@ internal void CheckStack(string trace) Debug.Assert(_stateObj._permitReplayStackTraceToDiffer || prev.Stack == trace, "The stack trace on subsequent replays should be the same"); } } - #endif - public bool ContinueEnabled => !LocalAppContextSwitches.UseCompatibilityAsyncBehaviour; - internal void CloneNullBitmapInfo() { if (_stateObj._nullBitmapInfo.ReferenceEquals(_replayStateData?._nullBitmapInfo ?? default)) @@ -4148,19 +3719,10 @@ 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"); #if DEBUG for (PacketData current = _firstPacket; current != null; current = current.NextPacket) { - if (ReferenceEquals(current.Buffer, buffer)) - { - // multiple packets are permitted to be in the same buffer because of partial packets - // but their contents cannot overlap - if ((current.Read + current.DataLength) > read) - { - Debug.Fail("duplicate or overlapping packet appended to snapshot"); - } - } + Debug.Assert(!ReferenceEquals(current.Buffer, buffer)); } #endif PacketData packetData = _sparePacket; @@ -4177,7 +3739,6 @@ internal void AppendPacketData(byte[] buffer, int read) #if DEBUG packetData.SetDebugStack(_stateObj._lastStack); packetData.SetDebugPacketId(Interlocked.Increment(ref _packetCounter)); - packetData.SetDebugDataHash(); #endif if (_firstPacket is null) { @@ -4194,28 +3755,24 @@ internal void AppendPacketData(byte[] buffer, int read) internal bool MoveNext() { bool retval = false; - SnapshotStatus moveToMode = SnapshotStatus.ReplayRunning; bool moved = false; if (_current == null) { _current = _firstPacket; - moveToMode = SnapshotStatus.ReplayStarting; moved = true; } else if (_current.NextPacket != null) { - if (_stateObj._snapshotStatus == SnapshotStatus.ContinueRunning) - { - moveToMode = SnapshotStatus.ContinueRunning; - } _current = _current.NextPacket; moved = true; } if (moved) { - _stateObj.SetBuffer(_current.Buffer, 0, _current.Read); - _stateObj._snapshotStatus = moveToMode; + _stateObj._inBuff = _current.Buffer; + _stateObj._inBytesUsed = 0; + _stateObj._inBytesRead = _current.Read; + _stateObj._snapshotReplay = true; retval = true; } @@ -4231,22 +3788,6 @@ internal void MoveToStart() _stateObj.AssertValidState(); } - internal bool MoveToContinue() - { - if (ContinueEnabled) - { - if (_continuePacket != null && _continuePacket != _current) - { - _continueStateData.Restore(_stateObj); - _stateObj.SetBuffer(_current.Buffer, 0, _current.Read); - _stateObj._snapshotStatus = SnapshotStatus.ContinueRunning; - _stateObj.AssertValidState(); - return true; - } - } - return false; - } - internal void CaptureAsStart(TdsParserStateObject stateObj) { _firstPacket = null; @@ -4256,6 +3797,7 @@ internal void CaptureAsStart(TdsParserStateObject stateObj) _stateObj = stateObj; _replayStateData ??= new StateObjectData(); _replayStateData.Capture(stateObj); + #if DEBUG _rollingPend = 0; _rollingPendCount = 0; @@ -4267,86 +3809,6 @@ internal void CaptureAsStart(TdsParserStateObject stateObj) AppendPacketData(stateObj._inBuff, stateObj._inBytesRead); } - internal void CaptureAsContinue(TdsParserStateObject stateObj) - { - if (ContinueEnabled) - { - Debug.Assert(_stateObj == stateObj); - if (_current is not null) - { - _continueStateData ??= new StateObjectData(); - _continueStateData.Capture(stateObj, trackStack: false); - _continuePacket = _current; - } - } - } - - internal void SetPacketDataSize(int size) - { - PacketData target = _current; - // special case for the start of a snapshot when we expect to have only a single packet - // but have no current packet because we haven't started to replay yet. - if ( - target == null && - _firstPacket != null && - _firstPacket == _lastPacket - ) - { - target = _firstPacket; - } - - if (target == null) - { - throw new InvalidOperationException(); - } - int total = 0; - if (target.PrevPacket != null) - { - total = target.PrevPacket.RunningDataSize; - } - target.RunningDataSize = total + size; - } - - internal void ClearPacketDataSize() - { - PacketData current = _firstPacket; - while (current != null) - { - current.RunningDataSize = 0; - current = current.NextPacket; - } - } - - internal int GetPacketDataOffset() - { - int offset = 0; - if (_current != null) - { - offset = _current.GetPacketDataOffset(); - } - return offset; - } - - internal int GetPacketDataSize() - { - int offset = 0; - if (_current != null) - { - offset = _current.GetPacketDataSize(); - } - return offset; - } - - internal int GetPacketID() - { - int id = 0; - if (_current != null) - { - id = _current.PacketID; - } - return id; - } - internal void Clear() { ClearState(); @@ -4358,7 +3820,6 @@ private void ClearPackets() PacketData packet = _firstPacket; _firstPacket = null; _lastPacket = null; - _continuePacket = null; _current = null; packet.Clear(); _sparePacket = packet; @@ -4366,14 +3827,11 @@ private void ClearPackets() private void ClearState() { - _storage = null; _replayStateData.Clear(_stateObj); - _continueStateData?.Clear(_stateObj, trackStack: false); #if DEBUG _rollingPend = 0; _rollingPendCount = 0; _stateObj._lastStack = null; - _packetCounter = 0; #endif _stateObj = null; } diff --git a/src/Microsoft.Data.SqlClient/tests/Common/LocalAppContextSwitchesHelper.cs b/src/Microsoft.Data.SqlClient/tests/Common/LocalAppContextSwitchesHelper.cs index 2970b1f1ce..2460b9234e 100644 --- a/src/Microsoft.Data.SqlClient/tests/Common/LocalAppContextSwitchesHelper.cs +++ b/src/Microsoft.Data.SqlClient/tests/Common/LocalAppContextSwitchesHelper.cs @@ -27,8 +27,6 @@ public sealed class LocalAppContextSwitchesHelper : IDisposable private readonly PropertyInfo _makeReadAsyncBlockingProperty; private readonly PropertyInfo _useMinimumLoginTimeoutProperty; private readonly PropertyInfo _legacyVarTimeZeroScaleBehaviourProperty; - private readonly PropertyInfo _useCompatibilityProcessSniProperty; - private readonly PropertyInfo _useCompatibilityAsyncBehaviourProperty; private readonly PropertyInfo _useConnectionPoolV2Property; #if NETFRAMEWORK private readonly PropertyInfo _disableTnirByDefaultProperty; @@ -45,10 +43,6 @@ public sealed class LocalAppContextSwitchesHelper : IDisposable private readonly Tristate _useMinimumLoginTimeoutOriginal; private readonly FieldInfo _legacyVarTimeZeroScaleBehaviourField; private readonly Tristate _legacyVarTimeZeroScaleBehaviourOriginal; - private readonly FieldInfo _useCompatibilityProcessSniField; - private readonly Tristate _useCompatibilityProcessSniOriginal; - private readonly FieldInfo _useCompatibilityAsyncBehaviourField; - private readonly Tristate _useCompatibilityAsyncBehaviourOriginal; private readonly FieldInfo _useConnectionPoolV2Field; private readonly Tristate _useConnectionPoolV2Original; #if NETFRAMEWORK @@ -128,14 +122,6 @@ void InitProperty(string name, out PropertyInfo property) "LegacyVarTimeZeroScaleBehaviour", out _legacyVarTimeZeroScaleBehaviourProperty); - InitProperty( - "UseCompatibilityProcessSni", - out _useCompatibilityProcessSniProperty); - - InitProperty( - "UseCompatibilityAsyncBehaviour", - out _useCompatibilityAsyncBehaviourProperty); - InitProperty( "UseConnectionPoolV2", out _useConnectionPoolV2Property); @@ -186,16 +172,6 @@ void InitField(string name, out FieldInfo field, out Tristate value) out _legacyVarTimeZeroScaleBehaviourField, out _legacyVarTimeZeroScaleBehaviourOriginal); - InitField( - "s_useCompatibilityProcessSni", - out _useCompatibilityProcessSniField, - out _useCompatibilityProcessSniOriginal); - - InitField( - "s_useCompatibilityAsyncBehaviour", - out _useCompatibilityAsyncBehaviourField, - out _useCompatibilityAsyncBehaviourOriginal); - InitField( "s_useConnectionPoolV2", out _useConnectionPoolV2Field, @@ -253,14 +229,6 @@ void RestoreField(FieldInfo field, Tristate value) _legacyVarTimeZeroScaleBehaviourField, _legacyVarTimeZeroScaleBehaviourOriginal); - RestoreField( - _useCompatibilityProcessSniField, - _useCompatibilityProcessSniOriginal); - - RestoreField( - _useCompatibilityAsyncBehaviourField, - _useCompatibilityAsyncBehaviourOriginal); - RestoreField( _useConnectionPoolV2Field, _useConnectionPoolV2Original); @@ -325,23 +293,6 @@ public bool LegacyVarTimeZeroScaleBehaviour get => (bool)_legacyVarTimeZeroScaleBehaviourProperty.GetValue(null); } - /// - /// Access the LocalAppContextSwitches.UseCompatibilityProcessSni property. - /// - public bool UseCompatibilityProcessSni - { - get => (bool)_useCompatibilityProcessSniProperty.GetValue(null); - } - - /// - /// Access the LocalAppContextSwitches.UseCompatibilityAsyncBehaviour - /// property. - /// - public bool UseCompatibilityAsyncBehaviour - { - get => (bool)_useCompatibilityAsyncBehaviourProperty.GetValue(null); - } - /// /// Access the LocalAppContextSwitches.UseConnectionPoolV2 property. /// @@ -414,26 +365,6 @@ public Tristate LegacyVarTimeZeroScaleBehaviourField set => SetValue(_legacyVarTimeZeroScaleBehaviourField, value); } - /// - /// Get or set the LocalAppContextSwitches.UseCompatibilityProcessSni switch - /// value. - /// - public Tristate UseCompatibilityProcessSniField - { - get => GetValue(_useCompatibilityProcessSniField); - set => SetValue(_useCompatibilityProcessSniField, value); - } - - /// - /// Get or set the LocalAppContextSwitches.UseCompatibilityAsyncBehaviour - /// switch value. - /// - public Tristate UseCompatibilityAsyncBehaviourField - { - get => GetValue(_useCompatibilityAsyncBehaviourField); - set => SetValue(_useCompatibilityAsyncBehaviourField, value); - } - /// /// Get or set the LocalAppContextSwitches.UseConnectionPoolV2 switch value. /// diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/LocalAppContextSwitchesTests.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/LocalAppContextSwitchesTests.cs index 170e39a322..30896e545e 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/LocalAppContextSwitchesTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/LocalAppContextSwitchesTests.cs @@ -16,8 +16,6 @@ public class LocalAppContextSwitchesTests [InlineData("MakeReadAsyncBlocking", false)] [InlineData("UseMinimumLoginTimeout", true)] [InlineData("LegacyVarTimeZeroScaleBehaviour", true)] - [InlineData("UseCompatibilityProcessSni", false)] - [InlineData("UseCompatibilityAsyncBehaviour", false)] [InlineData("UseConnectionPoolV2", false)] #if NETFRAMEWORK [InlineData("DisableTnirByDefault", false)] diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.FunctionalTests.csproj b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.FunctionalTests.csproj index 56265208b4..ad2b1191a0 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.FunctionalTests.csproj +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.FunctionalTests.csproj @@ -28,7 +28,6 @@ - @@ -66,13 +65,10 @@ - - - diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/MultiplexerTests.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/MultiplexerTests.cs deleted file mode 100644 index 288586fb17..0000000000 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/MultiplexerTests.cs +++ /dev/null @@ -1,724 +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.Buffers.Binary; -using System.Collections.Generic; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using Xunit; - -namespace Microsoft.Data.SqlClient.Tests -{ - public class MultiplexerTests - { - public static bool IsUsingCompatibilityProcessSni - { - get - { - if (AppContext.TryGetSwitch(@"Switch.Microsoft.Data.SqlClient.UseCompatibilityProcessSni", out bool foundValue)) - { - return foundValue; - } - return false; - } - } - - public static bool IsUsingModernProcessSni => !IsUsingCompatibilityProcessSni; - - [ExcludeFromCodeCoverage] - public static IEnumerable IsAsync() - { - yield return new object[] { false }; - yield return new object[] { true }; - } - - [ExcludeFromCodeCoverage] - public static IEnumerable OnlyAsync() { yield return new object[] { true }; } - - [ConditionalTheory(nameof(IsUsingModernProcessSni)), MemberData(nameof(IsAsync))] - public static void PassThroughSinglePacket(bool isAsync) - { - int dataSize = 20; - var a = CreatePacket(dataSize, 0xF); - List input = new List { a }; - List expected = new List { a }; - - Assert.Equal(SumPacketLengths(expected), SumPacketLengths(input)); - - var output = MultiplexPacketList(isAsync, dataSize, input); - - ComparePacketLists(dataSize, expected, output); - } - - [ConditionalTheory(nameof(IsUsingModernProcessSni)), MemberData(nameof(IsAsync))] - public static void PassThroughMultiplePacket(bool isAsync) - { - int dataSize = 40; - List input = CreatePackets(dataSize, 5, 6, 7, 8); - List expected = input; - - Assert.Equal(SumPacketLengths(expected), SumPacketLengths(input)); - - var output = MultiplexPacketList(isAsync, dataSize, input); - - ComparePacketLists(dataSize, expected, output); - } - - [ConditionalTheory(nameof(IsUsingModernProcessSni)), MemberData(nameof(IsAsync))] - public static void PassThroughMultiplePacketWithShortEnd(bool isAsync) - { - int dataSize = 40; - List input = CreatePackets((dataSize, 20), 5, 6, 7, 8); - List expected = input; - - Assert.Equal(SumPacketLengths(expected), SumPacketLengths(input)); - - var output = MultiplexPacketList(isAsync, dataSize, input); - - ComparePacketLists(dataSize, expected, output); - } - - [ConditionalTheory(nameof(IsUsingModernProcessSni)), MemberData(nameof(IsAsync))] - public static void ReconstructSinglePacket(bool isAsync) - { - int dataSize = 4; - var a = CreatePacket(dataSize, 0xF); - List input = SplitPacket(a, 6); - List expected = new List { a }; - - Assert.Equal(SumPacketLengths(expected), SumPacketLengths(input)); - - var output = MultiplexPacketList(isAsync, dataSize, input); - - ComparePacketLists(dataSize, expected, output); - } - - [ConditionalTheory(nameof(IsUsingModernProcessSni)), MemberData(nameof(IsAsync))] - public static void Reconstruct2Packets_Part_PartFull(bool isAsync) - { - int dataSize = 4; - var expected = CreatePackets(dataSize, 0xAA, 0xBB); - - var input = SplitPackets(dataSize, expected, - 6, // partial first packet - (6 + 6), // end of packet 0, start of packet 1 - 6 // end of packet 1 - ); - - Assert.Equal(SumPacketLengths(expected), SumPacketLengths(input)); - - var output = MultiplexPacketList(isAsync, dataSize, input); - - ComparePacketLists(dataSize, expected, output); - } - - [ConditionalTheory(nameof(IsUsingModernProcessSni)), MemberData(nameof(IsAsync))] - public static void Reconstruct2Packets_Full_FullPart_Part(bool isAsync) - { - int dataSize = 30; - var expected = new List { CreatePacket(30, 5), CreatePacket(10, 6), CreatePacket(30, 7) }; - - var input = SplitPackets(38, expected, - (8 + 30), // full - (8 + 10) + (8 + 12), // full, part next - 18 // part end - ); - - Assert.Equal(SumPacketLengths(expected), SumPacketLengths(input)); - - var output = MultiplexPacketList(isAsync, dataSize, input); - - ComparePacketLists(dataSize, expected, output); - } - - [ConditionalTheory(nameof(IsUsingModernProcessSni)), MemberData(nameof(IsAsync))] - public static void ReconstructMultiplePacketSequence(bool isAsync) - { - int dataSize = 40; - List expected = CreatePackets(dataSize, 5, 6, 7, 8); - List input = SplitPackets(dataSize, expected, - (8 + 40), - (8 + 23), - (17) + (8 + 23), - (17) + (8 + 23), - (17) - ); - - Assert.Equal(SumPacketLengths(expected), SumPacketLengths(input)); - - var output = MultiplexPacketList(isAsync, dataSize, input); - - ComparePacketLists(dataSize, expected, output); - } - - [ConditionalTheory(nameof(IsUsingModernProcessSni)), MemberData(nameof(IsAsync))] - public static void ReconstructMultiplePacketSequenceWithShortEnd(bool isAsync) - { - int dataSize = 40; - List expected = CreatePackets((dataSize, 20), 5, 6, 7, 8); - List input = SplitPackets(dataSize, expected, - (8 + 40), - (8 + 23), - (17) + (8 + 23), - (17) + (8 + 20) - ); - - Assert.Equal(SumPacketLengths(expected), SumPacketLengths(input)); - - var output = MultiplexPacketList(isAsync, dataSize, input); - - ComparePacketLists(dataSize, expected, output); - } - - [ConditionalTheory(nameof(IsUsingModernProcessSni)), MemberData(nameof(IsAsync))] - public static void Reconstruct3Packets_PartPartPart(bool isAsync) - { - int dataSize = 62; - - var expected = new List { CreatePacket(26, 5), CreatePacket(10, 6), CreatePacket(10, 7) }; - - var input = SplitPackets(70, expected, - (8 + 26) + (8 + 10) + (8 + 10) // = 70: full, full, part - ); - - var output = MultiplexPacketList(isAsync, dataSize, input); - - ComparePacketLists(dataSize, expected, output); - } - - [ConditionalFact(nameof(IsUsingModernProcessSni))] - public static void TrailingPartialPacketInSnapshotNotDuplicated() - { - int dataSize = 120; - - var expected = new List { CreatePacket(120, 5), CreatePacket(90, 6), CreatePacket(13, 7), }; - - var input = SplitPackets(120, expected, - (8 + 120), - (8 + 90) + (8 + 13) - ); - - Assert.Equal(SumPacketLengths(expected), SumPacketLengths(input)); - - var output = MultiplexPacketList(true, dataSize, input); - - ComparePacketLists(dataSize, expected, output); - } - - [ConditionalFact(nameof(IsUsingModernProcessSni))] - public static void BetweenAsyncAttentionPacket() - { - int dataSize = 120; - var normalPacket = CreatePacket(120, 5); - var attentionPacket = CreatePacket(13, 6); - var input = new List { normalPacket, attentionPacket }; - - using var stateObject = new TdsParserStateObject(input, TdsEnums.HEADER_LEN + dataSize, isAsync: true); - - for (int index = 0; index < input.Count; index++) - { - stateObject.Current = input[index]; - stateObject.ProcessSniPacket(default, 0); - } - - Assert.NotNull(stateObject._inBuff); - Assert.Equal(21, stateObject._inBytesRead); - Assert.Equal(0, stateObject._inBytesUsed); - Assert.NotNull(stateObject._snapshot); - Assert.NotNull(stateObject._snapshot.List); - Assert.Equal(2, stateObject._snapshot.List.Count); - - } - - [ConditionalFact(nameof(IsUsingModernProcessSni))] - public static void MultipleFullPacketsInRemainderAreSplitCorrectly() - { - int dataSize = 800 - TdsEnums.HEADER_LEN; - List expected = new List - { - CreatePacket(dataSize, 5), CreatePacket(80, 6), CreatePacket(21, 7) - }; - - - List input = SplitPacket(CombinePackets(expected), 700); - - using var stateObject = new TdsParserStateObject(input, dataSize, isAsync: false); - - var output = MultiplexPacketList(false, dataSize, input); - - ComparePacketLists(dataSize, expected, output); - } - - [ExcludeFromCodeCoverage] - private static List MultiplexPacketList(bool isAsync, int dataSize, List input) - { - using var stateObject = new TdsParserStateObject(input, TdsEnums.HEADER_LEN + dataSize, isAsync); - var output = new List(); - - for (int index = 0; index < input.Count; index++) - { - stateObject.Current = input[index]; - - stateObject.ProcessSniPacket(default, 0); - - if (stateObject._inBytesRead > 0) - { - if ( - stateObject._inBytesRead < TdsEnums.HEADER_LEN - || - stateObject._inBytesRead != (TdsEnums.HEADER_LEN + - Packet.GetDataLengthFromHeader( - stateObject._inBuff.AsSpan(0, TdsEnums.HEADER_LEN))) - ) - { - Assert.Fail("incomplete packet exposed after call to ProcessSniPacket"); - } - - if (!isAsync) - { - output.Add(PacketData.Copy(stateObject._inBuff, stateObject._inBytesUsed, - stateObject._inBytesRead)); - } - } - } - - - if (!isAsync) - { - while (stateObject.PartialPacket != null) - { - stateObject.Current = default; - - stateObject.ProcessSniPacket(default, 0); - - if (stateObject._inBytesRead > 0) - { - if ( - stateObject._inBytesRead < TdsEnums.HEADER_LEN - || - stateObject._inBytesRead != (TdsEnums.HEADER_LEN + - Packet.GetDataLengthFromHeader( - stateObject._inBuff.AsSpan(0, TdsEnums.HEADER_LEN))) - ) - { - Assert.Fail( - "incomplete packet exposed after call to ProcessSniPacket with usePartialPacket"); - } - - output.Add(PacketData.Copy(stateObject._inBuff, stateObject._inBytesUsed, - stateObject._inBytesRead)); - } - } - - } - else - { - output = stateObject._snapshot.List; - } - - return output; - } - - [ExcludeFromCodeCoverage] - private static void ComparePacketLists(int dataSize, List expected, List output) - { - Assert.NotNull(expected); - Assert.NotNull(output); - Assert.Equal(expected.Count, output.Count); - - for (int index = 0; index < expected.Count; index++) - { - var a = expected[index]; - var b = output[index]; - - var compare = a.AsSpan().SequenceCompareTo(b.AsSpan()); - - if (compare != 0) - { - Assert.Fail($"expected data does not match output data at packet index {index}"); - } - } - } - - [ExcludeFromCodeCoverage] - public static PacketData CreatePacket(int dataSize, byte dataValue, int startOffset = 0, int endPadding = 0) - { - byte[] buffer = new byte[startOffset + TdsEnums.HEADER_LEN + dataSize + endPadding]; - Span packet = buffer.AsSpan(startOffset, TdsEnums.HEADER_LEN + dataSize); - WritePacket(packet, dataSize, dataValue, 1); - return new PacketData(buffer, startOffset, buffer.Length - endPadding); - } - - [ExcludeFromCodeCoverage] - public static List CreatePackets(DataSize sizes, params byte[] dataValues) - { - int count = dataValues.Length; - List list = new List(count); - - for (byte index = 0; index < count; index++) - { - int dataSize = sizes.GetSize(index == dataValues.Length - 1); - int packetSize = TdsEnums.HEADER_LEN + dataSize; - byte[] array = new byte[packetSize]; - WritePacket(array, dataSize, dataValues[index], index); - list.Add(new PacketData(array, 0, packetSize)); - } - - return list; - } - - [ExcludeFromCodeCoverage] - private static void WritePacket(Span buffer, int dataSize, byte dataValue, byte id) - { - Span header = buffer.Slice(0, TdsEnums.HEADER_LEN); - header[0] = 4; // Type, 4 - Raw Data - header[1] = 0; // Status, 0 - normal message - BinaryPrimitives.TryWriteInt16BigEndian(header.Slice(TdsEnums.HEADER_LEN_FIELD_OFFSET, 2), - (short)(TdsEnums.HEADER_LEN + dataSize)); // total length - BinaryPrimitives.TryWriteInt16BigEndian(header.Slice(TdsEnums.SPID_OFFSET, 2), short.MaxValue); // SPID - header[TdsEnums.HEADER_LEN_FIELD_OFFSET + 4] = id; // PacketID - header[TdsEnums.HEADER_LEN_FIELD_OFFSET + 5] = 0; // Window - - Span data = buffer.Slice(TdsEnums.HEADER_LEN, dataSize); - data.Fill(dataValue); - } - - [ExcludeFromCodeCoverage] - public static List SplitPacket(PacketData packet, int length) - { - List list = new List(2); - while (packet.Length > length) - { - list.Add(new PacketData(packet.Array, packet.Start, length)); - packet = new PacketData(packet.Array, packet.Start + length, packet.Length - length); - } - - if (packet.Length > 0) - { - list.Add(packet); - } - - return list; - } - - [ExcludeFromCodeCoverage] - public static List SplitPackets(int dataSize, List packets, params int[] lengths) - { - List list = new List(lengths.Length); - int packetSize = TdsEnums.HEADER_LEN + dataSize; - byte[][] arrays = new byte[lengths.Length][]; - for (int index = 0; index < lengths.Length; index++) - { - if (lengths[index] > packetSize) - { - throw new ArgumentOutOfRangeException( - $"segment size of an individual part cannot exceed the packet buffer size of the state object, max packet size: {packetSize}, supplied length: {lengths[index]}, at index: {index}"); - } - - arrays[index] = new byte[lengths[index]]; - } - - int targetOffset = 0; - int targetIndex = 0; - - int sourceOffset = 0; - int sourceIndex = 0; - - - do - { - Span targetSpan = Span.Empty; - if (targetOffset < arrays[targetIndex].Length) - { - targetSpan = arrays[targetIndex].AsSpan(targetOffset); - } - else - { - targetIndex += 1; - targetOffset = 0; - continue; - } - - Span sourceSpan = Span.Empty; - if (sourceOffset < packets[sourceIndex].Length) - { - sourceSpan = packets[sourceIndex].AsSpan(sourceOffset); - } - else - { - sourceIndex += 1; - sourceOffset = 0; - continue; - } - - int copy = Math.Min(targetSpan.Length, sourceSpan.Length); - if (copy > 0) - { - targetOffset += copy; - sourceOffset += copy; - sourceSpan.Slice(0, copy).CopyTo(targetSpan.Slice(0, copy)); - } - } while (sourceIndex < packets.Count && targetIndex < arrays.Length); - - foreach (var array in arrays) - { - list.Add(new PacketData(array, 0, array.Length)); - } - - return list; - } - - [ExcludeFromCodeCoverage] - public static PacketData CombinePackets(List packets) - { - int totalLength = SumPacketLengths(packets); - byte[] buffer = new byte[totalLength]; - int offset = 0; - for (int index = 0; index < packets.Count; index++) - { - PacketData packet = packets[index]; - Array.Copy(packet.Array, packet.Start, buffer, offset, packet.Length); - offset += packet.Length; - } - - return new PacketData(buffer, 0, totalLength); - } - - [ExcludeFromCodeCoverage] - public static int PacketSizeFromDataSize(int dataSize) => TdsEnums.HEADER_LEN + dataSize; - - [ExcludeFromCodeCoverage] - public static int DataSizeFromPacketSize(int packetSize) => packetSize - TdsEnums.HEADER_LEN; - - [ExcludeFromCodeCoverage] - public static int SumPacketLengths(List list) - { - int total = 0; - for (int index = 0; index < list.Count; index++) - { - total += list[index].Length; - } - return total; - } - - [ExcludeFromCodeCoverage] - public static List LoadPacketBinFiles(string directoryName) - { - // expects a set of files contained in a directory with the name - // formatted as packet_{number}_{dataSize}.bin each packet will be - // loaded into a byte[] - - string[] files = Directory.GetFiles(directoryName, "packet*.bin", SearchOption.TopDirectoryOnly); - SortedDictionary packets = new SortedDictionary(); - foreach (string file in files) - { - Match match = Regex.Match(file, @"packet_(?\d+)_(?\d+)\.bin"); - int number = int.Parse(match.Groups["number"].Value); - int size = int.Parse(match.Groups["size"].Value); - packets.Add( - number, - new PacketData( - System.IO.File.ReadAllBytes(file), - 0, - size - ) - ); - } - - return packets.Values.ToList(); - } - - [ExcludeFromCodeCoverage] - public static List NaiveReconstructPacketStream(List input) - { - int dataSize = input[0].Array.Length; - List output = new List(input.Count); - - byte[] currentBuffer = new byte[dataSize]; - int currentBufferOffset = 0; - - foreach (PacketData inputPacket in input) - { - int inputPacketOffset = 0; - while (inputPacketOffset < inputPacket.Length) - { - if (currentBufferOffset < dataSize) - { - int requiredCount = dataSize - currentBufferOffset; - int availableCount = inputPacket.Length - inputPacketOffset; - int copyCount = Math.Min(requiredCount, availableCount); - ReadOnlySpan copyFrom = inputPacket.Array.AsSpan(inputPacketOffset, copyCount); - Span copyTo = currentBuffer.AsSpan(currentBufferOffset, copyCount); - copyFrom.CopyTo(copyTo); - currentBufferOffset += copyCount; - inputPacketOffset += copyCount; - } - - if (currentBufferOffset == dataSize) - { - output.Add(new PacketData(currentBuffer, 0, dataSize)); - currentBufferOffset = 0; - currentBuffer = new byte[dataSize]; - } - } - } - - if (currentBufferOffset > 0) - { - output.Add(new PacketData(currentBuffer, 0, currentBufferOffset)); - } - - for (int index = 0; index < output.Count; index++) - { - PacketData packet = output[index]; - int expectedLength = 8 + Packet.GetDataLengthFromHeader(packet.Array); - if (expectedLength != packet.Length) - { - if (index != output.Count - 1) - { - throw new InvalidOperationException( - "non-terminal packet has a length mismatch between the packet header and amount of data available"); - } - else - { - byte[] remainder = new byte[dataSize]; - int remainderSize = packet.Length - expectedLength; - Span copyFrom = packet.Array.AsSpan(expectedLength, remainderSize); - Span copyTo = remainder.AsSpan(0, remainderSize); - copyFrom.CopyTo(copyTo); - copyFrom.Fill(0); - - PacketData replacementPacket = new PacketData(packet.Array, 0, expectedLength); - PacketData additionalPacket = new PacketData(remainder, 0, remainderSize); - output[index] = replacementPacket; - output.Add(additionalPacket); - } - } - } - - return output; - } - } - - [ExcludeFromCodeCoverage] - [DebuggerDisplay("{ToDebugString(),nq}")] - public readonly struct PacketData - { - public readonly byte[] Array; - public readonly int Start; - public readonly int Length; - - public PacketData(byte[] array, int start, int length) - { - Array = array; - Start = start; - Length = length; - } - - public Span AsSpan() - { - return Array == null ? Span.Empty : Array.AsSpan(Start, Length); - } - - public Span AsSpan(int start) - { - Span span = AsSpan(); - return span.Slice(start); - } - - public static PacketData Copy(byte[] array, int start, int length) - { - byte[] newArray = null; - if (array != null) - { - newArray = new byte[array.Length]; - Buffer.BlockCopy(array, start, newArray, start, length); - } - - return new PacketData(newArray, start, length); - } - - [ExcludeFromCodeCoverage] - public string ToDebugString() - { - StringBuilder buffer = new StringBuilder(128); - buffer.Append(Length); - - if (Array != null && Array.Length > 0) - { - if (Array.Length != Length) - { - buffer.AppendFormat(" (arr: {0})", Array.Length); - } - - buffer.Append(": {"); - buffer.AppendFormat("{0:D2}", Array[0]); - - int max = Math.Min(32, Array.Length); - for (int index = 1; index < max; index++) - { - buffer.Append(','); - buffer.AppendFormat("{0:D2}", Array[index]); - } - - if (Length > max) - { - buffer.Append(" ..."); - } - - buffer.Append('}'); - } - - return buffer.ToString(); - } - - } - - [ExcludeFromCodeCoverage] - [DebuggerStepThrough] - public struct DataSize - { - public DataSize(int commonSize) - { - CommonSize = commonSize; - LastSize = commonSize; - } - - public DataSize(int commonSize, int lastSize) - { - CommonSize = commonSize; - LastSize = lastSize; - } - - public int LastSize { get; set; } - public int CommonSize { get; set; } - - public int GetSize(bool isLast) - { - if (isLast) - { - return LastSize; - } - else - { - return CommonSize; - } - } - - public static implicit operator DataSize(int commonSize) - { - return new DataSize(commonSize, commonSize); - } - - public static implicit operator DataSize((int commonSize, int lastSize) values) - { - return new DataSize(values.commonSize, values.lastSize); - } - } -} diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/TdsParserStateObject.TestHarness.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/TdsParserStateObject.TestHarness.cs deleted file mode 100644 index 89807b0132..0000000000 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/TdsParserStateObject.TestHarness.cs +++ /dev/null @@ -1,198 +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.Collections.Generic; -using System.Diagnostics; -using System.Reflection; -using Microsoft.Data.SqlClient.Tests; - -using SwitchesHelper = Microsoft.Data.SqlClient.Tests.Common.LocalAppContextSwitchesHelper; - -namespace Microsoft.Data.SqlClient -{ - internal struct PacketHandle - { - } - - internal partial class TdsParserStateObject : IDisposable - { - internal int ObjectID = 1; - - internal class SQL - { - internal static Exception InvalidInternalPacketSize(string v) => throw new Exception(v ?? nameof(InvalidInternalPacketSize)); - - internal static Exception ParsingError(ParsingErrorState state) => throw new Exception(state.ToString()); - } - - internal static class SqlClientEventSource - { - internal static class Log - { - internal static void TryAdvancedTraceBinEvent(string message, params object[] values) - { - } - } - } - - private enum SnapshotStatus - { - NotActive, - ReplayStarting, - ReplayRunning - } - - internal enum TdsParserState - { - Closed, - OpenNotLoggedIn, - OpenLoggedIn, - Broken, - } - - private uint GetSniPacket(PacketHandle packet, ref uint dataSize) - { - return SniPacketGetData(packet, _inBuff, ref dataSize); - } - - private class StringsHelper - { - internal static string GetString(string sqlMisc_InvalidArraySizeMessage) => Strings.SqlMisc_InvalidArraySizeMessage; - } - - internal class Strings - { - internal static string SqlMisc_InvalidArraySizeMessage = nameof(SqlMisc_InvalidArraySizeMessage); - - } - - public class Parser - { - internal object ProcessSNIError(TdsParserStateObject tdsParserStateObject) => "ProcessSNIError"; - public TdsParserState State = TdsParserState.OpenLoggedIn; - } - - sealed internal class LastIOTimer - { - internal long _value; - } - - internal sealed class Snapshot - { - public List List; - - public Snapshot() => List = new List(); - [DebuggerStepThrough] - internal void AssertCurrent() { } - [DebuggerStepThrough] - internal void AppendPacketData(byte[] buffer, int read) => List.Add(new PacketData(buffer, 0, read)); - [DebuggerStepThrough] - internal void MoveNext() - { - - } - } - - public List Input; - public PacketData Current; - public bool IsAsync { get => _snapshot != null; } - - public int _packetSize; - - internal Snapshot _snapshot; - public int _inBytesRead; - public int _inBytesUsed; - public byte[] _inBuff; - - [DebuggerStepThrough] - public TdsParserStateObject(List input, int packetSize, bool isAsync) - { - _packetSize = packetSize; - _inBuff = new byte[_packetSize]; - Input = input; - if (isAsync) - { - _snapshot = new Snapshot(); - } - } - - [DebuggerStepThrough] - public void Dispose() - { - LocalAppContextSwitches.Dispose(); - } - - [DebuggerStepThrough] - private uint SniPacketGetData(PacketHandle packet, byte[] inBuff, ref uint dataSize) - { - Span target = inBuff.AsSpan(0, _packetSize); - Span source = Current.Array.AsSpan(Current.Start, Current.Length); - source.CopyTo(target); - dataSize = (uint)Current.Length; - return TdsEnums.SNI_SUCCESS; - } - - [DebuggerStepThrough] - void SetBuffer(byte[] buffer, int inBytesUsed, int inBytesRead) - { - _inBuff = buffer; - _inBytesUsed = inBytesUsed; - _inBytesRead = inBytesRead; - } - - // stubs - private LastIOTimer _lastSuccessfulIOTimer = new LastIOTimer(); - private Parser _parser = new Parser(); - private SnapshotStatus _snapshotStatus = SnapshotStatus.NotActive; - - [DebuggerStepThrough] - private void SniReadStatisticsAndTracing() { } - [DebuggerStepThrough] - private void AssertValidState() { } - - [DebuggerStepThrough] - private void AddError(object value) => throw new Exception(value as string ?? "AddError"); - - private SwitchesHelper LocalAppContextSwitches = new(); - -#if NETFRAMEWORK - private SniNativeWrapperImpl _native; - internal SniNativeWrapperImpl SniNativeWrapper - { - get - { - if (_native == null) - { - _native = new SniNativeWrapperImpl(this); - } - return _native; - } - } - - internal class SniNativeWrapperImpl - { - private readonly TdsParserStateObject _parent; - internal SniNativeWrapperImpl(TdsParserStateObject parent) => _parent = parent; - - internal uint SniPacketGetData(PacketHandle packet, byte[] inBuff, ref uint dataSize) => _parent.SniPacketGetData(packet, inBuff, ref dataSize); - } -#endif - } - - internal static class TdsEnums - { - public const uint SNI_SUCCESS = 0; // The operation completed successfully. - // header constants - public const int HEADER_LEN = 8; - public const int HEADER_LEN_FIELD_OFFSET = 2; - public const int SPID_OFFSET = 4; - } - - internal enum ParsingErrorState - { - CorruptedTdsStream = 18, - ProcessSniPacketFailed = 19, - } -} diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AsyncTest/AsyncCancelledConnectionsTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AsyncTest/AsyncCancelledConnectionsTest.cs index 0ae12be917..e71d6d62f6 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AsyncTest/AsyncCancelledConnectionsTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AsyncTest/AsyncCancelledConnectionsTest.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Text; @@ -37,13 +36,9 @@ public void CancelAsyncConnections() private void RunCancelAsyncConnections(SqlConnectionStringBuilder connectionStringBuilder) { SqlConnection.ClearAllPools(); - - ParallelLoopResult results = new ParallelLoopResult(); - ConcurrentDictionary tracker = new ConcurrentDictionary(); - - _random = new Random(4); // chosen via fair dice roll. _watch = Stopwatch.StartNew(); - + _random = new Random(4); // chosen via fair dice role. + ParallelLoopResult results = new ParallelLoopResult(); try { // Setup a timer so that we can see what is going on while our tasks run @@ -52,7 +47,7 @@ private void RunCancelAsyncConnections(SqlConnectionStringBuilder connectionStri results = Parallel.For( fromInclusive: 0, toExclusive: NumberOfTasks, - (int i) => DoManyAsync(i, tracker, connectionStringBuilder).GetAwaiter().GetResult()); + (int i) => DoManyAsync(connectionStringBuilder).GetAwaiter().GetResult()); } } catch (Exception ex) @@ -87,15 +82,15 @@ private void DisplaySummary() { count = _exceptionDetails.Count; } + _output.WriteLine($"{_watch.Elapsed} {_continue} Started:{_start} Done:{_done} InFlight:{_inFlight} RowsRead:{_rowsRead} ResultRead:{_resultRead} PoisonedEnded:{_poisonedEnded} nonPoisonedExceptions:{_nonPoisonedExceptions} PoisonedCleanupExceptions:{_poisonCleanUpExceptions} Count:{count} Found:{_found}"); } // This is the the main body that our Tasks run - private async Task DoManyAsync(int index, ConcurrentDictionary tracker, SqlConnectionStringBuilder connectionStringBuilder) + private async Task DoManyAsync(SqlConnectionStringBuilder connectionStringBuilder) { Interlocked.Increment(ref _start); Interlocked.Increment(ref _inFlight); - tracker[index] = true; using (SqlConnection marsConnection = new SqlConnection(connectionStringBuilder.ToString())) { @@ -105,15 +100,15 @@ private async Task DoManyAsync(int index, ConcurrentDictionary tracker } // First poison - await DoOneAsync(marsConnection, connectionStringBuilder.ToString(), poison: true, index); + await DoOneAsync(marsConnection, connectionStringBuilder.ToString(), poison: true); for (int i = 0; i < NumberOfNonPoisoned && _continue; i++) { // now run some without poisoning - await DoOneAsync(marsConnection, connectionStringBuilder.ToString(),false,index); + await DoOneAsync(marsConnection, connectionStringBuilder.ToString()); } } - tracker.TryRemove(index, out var _); + Interlocked.Decrement(ref _inFlight); Interlocked.Increment(ref _done); } @@ -122,7 +117,7 @@ private async Task DoManyAsync(int index, ConcurrentDictionary tracker // if we are poisoning we will // 1 - Interject some sleeps in the sql statement so that it will run long enough that we can cancel it // 2 - Setup a time bomb task that will cancel the command a random amount of time later - private async Task DoOneAsync(SqlConnection marsConnection, string connectionString, bool poison, int parent) + private async Task DoOneAsync(SqlConnection marsConnection, string connectionString, bool poison = false) { try { @@ -140,12 +135,12 @@ private async Task DoOneAsync(SqlConnection marsConnection, string connectionStr { if (marsConnection != null && marsConnection.State == System.Data.ConnectionState.Open) { - await RunCommand(marsConnection, builder.ToString(), poison, parent); + await RunCommand(marsConnection, builder.ToString(), poison); } else { await connection.OpenAsync(); - await RunCommand(connection, builder.ToString(), poison, parent); + await RunCommand(connection, builder.ToString(), poison); } } } @@ -181,7 +176,7 @@ private async Task DoOneAsync(SqlConnection marsConnection, string connectionStr } } - private async Task RunCommand(SqlConnection connection, string commandText, bool poison, int parent) + private async Task RunCommand(SqlConnection connection, string commandText, bool poison) { int rowsRead = 0; int resultRead = 0; @@ -216,7 +211,7 @@ private async Task RunCommand(SqlConnection connection, string commandText, bool } while (await reader.NextResultAsync() && _continue); } - catch (SqlException) when (poison) + catch when (poison) { // This looks a little strange, we failed to read above so this should fail too // But consider the case where this code is elsewhere (in the Dispose method of a class holding this logic) @@ -233,10 +228,6 @@ private async Task RunCommand(SqlConnection connection, string commandText, bool throw; } - catch (Exception ex) - { - Assert.Fail("unexpected exception: " + ex.GetType().Name + " " +ex.Message); - } } } finally diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Common/SystemDataInternals/TdsParserStateObjectHelper.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Common/SystemDataInternals/TdsParserStateObjectHelper.cs index dadbb5c58c..cd20701f74 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Common/SystemDataInternals/TdsParserStateObjectHelper.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/Common/SystemDataInternals/TdsParserStateObjectHelper.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Diagnostics; using System.Reflection; using Xunit; @@ -97,14 +96,17 @@ internal static object GetSessionHandle(object stateObject) { throw new ArgumentNullException(nameof(stateObject)); } + if (s_tdsParserStateObjectManaged is null) { throw new ArgumentException("Library being tested does not implement TdsParserStateObjectManaged", nameof(stateObject)); } + if (!s_tdsParserStateObjectManaged.IsInstanceOfType(stateObject)) { throw new ArgumentException("Object provided was not a TdsParserStateObjectManaged", nameof(stateObject)); } + return s_tdsParserStateObjectManagedSessionHandle.GetValue(stateObject); } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderStreamsTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderStreamsTest.cs index e5ff3c7559..cd516df696 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderStreamsTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderStreamsTest.cs @@ -29,7 +29,7 @@ public static class DataReaderStreamsTest )] public static async Task GetFieldValueAsync_OfStream(CommandBehavior behavior, bool isExecuteAsync) { - const int PacketSize = 512; // force minimum packet size so that the test data spans multiple packets to test sequential access spanning + const int PacketSize = 512; // force minimun packet size so that the test data spans multiple packets to test sequential access spanning string connectionString = SetConnectionStringPacketSize(DataTestUtility.TCPConnectionString, PacketSize); byte[] originalData = CreateBinaryData(PacketSize, forcedPacketCount: 4); string query = CreateBinaryDataQuery(originalData); 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 b00662a90f..5c9a19afcb 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs @@ -7,7 +7,6 @@ using System.Collections.Generic; using System.Data; using System.Data.SqlTypes; -using System.Diagnostics; using System.Linq; using System.Reflection; using System.Text; @@ -263,7 +262,7 @@ first_name varchar(100) null, } [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] - public static void CheckNullRowVersionIsDBNull() + public static void CheckNullRowVersionIsBDNull() { lock (s_rowVersionLock) {