Unsized varchar output parameters change capacity on command reuse, causing history-dependent truncation

未关闭
#4,953 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

维护者通常 2 天内回复

还没有人认领这个 Issue。

评估

难度
3/5
预计耗时
1-2 天
新手友好度
54/100
Issue 类型
缺陷
描述清晰度
基本清楚
活跃度
活跃
技术栈
csharp, sql
领域
databases

调研方向

Start with the value-based Size inference in src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlParameter.cs and declaration sizing in SqlCommand.cs around lines 2208–2255. Trace how an output parameter's previous value affects the next unprepared execution, then add or locate tests for repeated executions with short, empty, and longer outputs. Done means capacity does not shrink unexpectedly, or the requirement for explicit Size is enforced or documented.

由索引模型根据 Issue 内容生成。

描述

Describe the bug

Reusing an unprepared text command with an unsized VarChar output parameter can silently change its capacity based on the previous execution's result.

A short output reduces the inferred capacity for the next execution, potentially truncating a longer result. An empty output triggers the default 8,000-byte declaration, increasing capacity again. Consequently, the same source value can be returned completely or truncated depending on what a previous execution returned.

This report concerns the existing value-based output-size inference behavior, not a proposed change to input parameter sizing.

Exception message: N/A; the concern is silent truncation.
Stack trace: N/A.
To reproduce

Create a console application referencing Microsoft.Data.SqlClient. Set SQLCLIENT_REPRO_CONNECTION_STRING directly in your environment to a connection string for a database whose default collation is UTF-8. Do not call Prepare().

using System;
using System.Data;
using System.Text;
using Microsoft.Data.SqlClient;

string connectionString =
    Environment.GetEnvironmentVariable("SQLCLIENT_REPRO_CONNECTION_STRING")
    ?? throw new InvalidOperationException(
        "Set SQLCLIENT_REPRO_CONNECTION_STRING to a UTF-8 database connection string.");

using var connection = new SqlConnection(connectionString);
connection.Open();

using var command = connection.CreateCommand();
command.CommandText = "SET @output = @source;";

var source = command.Parameters.Add("@source", SqlDbType.NVarChar, -1);

var output = command.Parameters.Add("@output", SqlDbType.VarChar);
output.Direction = ParameterDirection.Output;

// Leave Size unset; the initial value provides inferred capacity.
output.Value = "abcdefgh";

string shortValue = new('\u00e9', 2);
string longValue = new('\u00e9', 4);

int execution = 0;
foreach (string value in new[]
{
    shortValue,
    longValue,
    "",
    longValue,
    shortValue,
    longValue
})
{
    source.Value = value;
    command.ExecuteNonQuery();

    string result = (string)output.Value;
    Console.WriteLine(
        $"Execution={++execution}, requested characters={value.Length}, " +
        $"returned characters={result.Length}, " +
        $"returned bytes={Encoding.UTF8.GetByteCount(result)}");
}

The following sequence is predicted by the existing declaration-sizing code. Each é occupies two UTF-8 bytes:

Execution Requested value Inferred SQL capacity Returned value
1 éé varchar(8), from initial ASCII value éé
2 éééé varchar(4), from previous output éé — truncated
3 Empty string varchar(4) Empty string
4 éééé varchar(8000), from empty-value fallback éééé — complete
5 éé varchar(8), from previous output éé
6 éééé varchar(4), from previous output éé — truncated

Executions 2, 4, and 6 request the same value but return different results because preceding outputs changed the parameter's capacity. This is not limited to multibyte text: varying ASCII output lengths can produce the same history-dependent behavior.

Expected behavior

Once an output parameter has been accepted with an inferred capacity, returning a shorter value should not silently reduce that capacity for subsequent executions.

If output parameters require explicit capacity for reliable reuse, that requirement should be enforced or clearly documented rather than allowing previous results to redefine it.

Further technical details

Microsoft.Data.SqlClient version: No specific released version has yet been confirmed by executing the complete reproduction. Source analysis used the existing implementation at commit 0666194d36d26c140ded15b2f768e7ba9411a970.

.NET target: The complete console reproduction has not yet been executed. .NET SDK 10.0.403 is available in the investigation environment.

SQL Server version: SQL Server 17.0.1145.4 was used for focused server-contract probes of UTF-8 truncation. The complete reproduction requires a database with a UTF-8 default collation.

Operating system: Windows.

Additional context

With Size unset, SqlParameter.Size derives its value from the current Value, which contains the previous output. For ANSI text, the existing declaration builder expands that inferred character count to the previous value's encoded byte length. The resulting SQL declaration constrains assignments inside the next batch.

This makes output capacity depend on execution history. A previous output is not input to an output-only parameter, but it nevertheless influences the next batch's behavior through its declaration.

Relevant source:

Verification status: This report is based on source analysis and focused SQL Server probes confirming multibyte truncation at smaller byte capacities. The complete repeated-command reproduction has not yet been executed against Microsoft.Data.SqlClient; the sequence above is not presented as captured application output.

Workaround: Set an explicit output Size large enough for the expected encoded data, or use Size = -1 where MAX output is appropriate.

主要语言
C#
星标
1k
派生
352
平均合并
4 天 12 小时
30 天内合并 PR
86

环境准备

在 Codespaces 中打开

在浏览器里用你自己的 GitHub 账号启动这个项目的开发容器。

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

dotnet/SqlClient 的其他 Issue

查看 dotnet/SqlClient 的全部 Issue

相似的 Issue

更多 C# Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。