-
Notifications
You must be signed in to change notification settings - Fork 313
Description
Description
SqlSequentialTextReader ReadAsync returns 0 before the end of a file is reached when the number of characters requested varies.
It reproduces when the number of characters requested is different on each read. If reading with the same constant count of characters to read (e.g. 4096) it will always return slightly oversized buffer that will trigger a read on each iteration from SqlDataReader and everything will be fine.
In PrepareByteBuffer ArrayPool.Shared returns instance with 8192 size for requested byteBufferSize 4097 (4096 chars). _decoder. Convert returns 4096 bytesUsed so it's not an encoding issue.
The second iteration requests 1 chare less. It creates a buffer with 4096 length that is entirely in _leftOverBytes size limit. It skips the read from SqlDataReader here
SqlClient/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlSequentialTextReader.cs
Line 175 in 6d2473d
if ((byteBufferUsed < byteBuffer.Length) || (byteBuffer.Length == 0)) |
and charsRead 0 at this time (unbuffered characters count read) returned as read bytes, which means the end of a file for the consumer.
To reproduce
var reader = await command.ExecuteReaderAsync(CommandBehavior.SequentialAccess, cancellationToken);
var textReader = dataReader.DbDataReader.GetTextReader(0);
var buffer = new char[4096];
var result = -1;
var start = 0;
while (result != 0)
{
result = await textReader.ReadAsync(buffer, start, buffer.Length - start);
start++;
}
Expected behavior
A number of characters ready to process on current iteration returned even if it was buffered earlier.
Further technical details
Microsoft.Data.SqlClient version: 6.0.2
.NET target: .NET 9.0
SQL Server version: SQL Server 2022
Operating system: Windows 10