Describe the bug
I tried the new 6.1 PReview1 bits, a few of my tests failed with wrong data output, I tracked it down to sqlDataReader.GetChars apparently ignoring the buffer index. In the GetPooledChars loop below the buffer is always written at bufferIndex 0 ignoring the offset parameter. If you run the same code with 6.0.2 it works.
To reproduce
using System.Buffers;
using System.Data;
using Microsoft.Data.SqlClient;
namespace SqlRepro
{
internal class Program
{
static async Task Main(string[] args)
{
const CommandBehavior commandBehavior = CommandBehavior.SequentialAccess | CommandBehavior.SingleResult;
await using var sqlConnection = new SqlConnection("Server=localhost;Database=DatabaseTests;TrustServerCertificate=True;Trusted_Connection=True;");
await sqlConnection.OpenAsync();
const int length = 32000;
const string sqlCharWithArg = "SELECT CONVERT(BIGINT, 1) AS [Id], CONVERT(NVARCHAR(MAX), @input) AS [Value];";
var input = string.Create(length, length, (span, state) =>
{
for (int i = 0; i < state; i++)
{
span[i] = (char) Random.Shared.Next(0x30, 0x5A);
}
});
await using var sqlCommand = new SqlCommand();
sqlCommand.Connection = sqlConnection;
sqlCommand.CommandTimeout = 0;
sqlCommand.CommandText = sqlCharWithArg;
sqlCommand.Parameters.Add(new SqlParameter("@input", SqlDbType.NVarChar, -1) {Value = input});
await using var sqlReader = await sqlCommand.ExecuteReaderAsync(commandBehavior);
var succ = await sqlReader.ReadAsync();
long id = sqlReader.GetInt64(0);
if (id != 1)
{
Console.WriteLine("Id not 1");
}
var sliced = GetPooledChars(sqlReader, 1);
if (!sliced.SequenceEqual(input.ToCharArray()))
{
Console.WriteLine("sliced != input");
}
}
private static char[] GetPooledChars(SqlDataReader sqlDataReader, int ordinal)
{
var buffer = ArrayPool<char>.Shared.Rent(8192);
int offset = 0;
while (true)
{
var read = (int) sqlDataReader.GetChars(ordinal, offset, buffer, offset, buffer.Length - offset);
// in the second iteration, the buffer is already overwritten from offset 0, bufferIndex ignored?
if (read == 0)
{
break;
}
offset += read;
if (buffer.Length - offset < 128)
{
buffer = Resize(buffer);
}
}
var sliced = buffer.AsSpan(0, offset).ToArray();
ArrayPool<char>.Shared.Return(buffer);
return sliced;
static char[] Resize(char[] buffer)
{
var newBuffer = ArrayPool<char>.Shared.Rent(buffer.Length * 2);
Array.Copy(buffer, newBuffer, buffer.Length);
ArrayPool<char>.Shared.Return(buffer);
return newBuffer;
}
}
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.1.0-preview1.25120.4" />
</ItemGroup>
</Project>
Expected behavior
Slice should be the same as input in the repro
Further technical details
Microsoft.Data.SqlClient version: 6.1.0-preview1.25120.4
.NET target: 9.0
SQL Server version: SQL Server 2022
Operating system: Windows 11
Describe the bug
I tried the new 6.1 PReview1 bits, a few of my tests failed with wrong data output, I tracked it down to sqlDataReader.GetChars apparently ignoring the buffer index. In the GetPooledChars loop below the buffer is always written at bufferIndex 0 ignoring the offset parameter. If you run the same code with 6.0.2 it works.
To reproduce
Expected behavior
Slice should be the same as input in the repro
Further technical details
Microsoft.Data.SqlClient version: 6.1.0-preview1.25120.4
.NET target: 9.0
SQL Server version: SQL Server 2022
Operating system: Windows 11