Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Hotfix 2.1.5] | Fix CommandText length for stored procedures (#1484) #1726

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<LangVersion>9.0</LangVersion>
<TargetsWindows Condition="'$(OS)' == 'Windows_NT' AND '$(OSGroup)' == ''">true</TargetsWindows>
<TargetsWindows Condition="'$(OS)' != 'Windows_NT' AND '$(OSGroup)' == ''">false</TargetsWindows>
<TargetsWindows Condition="'$(OSGroup)' == 'Windows_NT'">true</TargetsWindows>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ namespace Microsoft.Data.SqlClient
public sealed partial class SqlCommand : DbCommand, ICloneable
{
private static int _objectTypeCount; // EventSource Counter
private const int MaxRPCNameLength = 1046;
internal readonly int ObjectID = Interlocked.Increment(ref _objectTypeCount); private string _commandText;

private static readonly Func<AsyncCallback, object, IAsyncResult> s_beginExecuteReaderAsync = BeginExecuteReaderAsyncCallback;
Expand Down Expand Up @@ -5595,7 +5596,20 @@ private void BuildRPC(bool inSchema, SqlParameterCollection parameters, ref _Sql
GetRPCObject(0, userParameterCount, ref rpc);

rpc.ProcID = 0;
rpc.rpcName = this.CommandText; // just get the raw command text

// TDS Protocol allows rpc name with maximum length of 1046 bytes for ProcName
// 4-part name 1 + 128 + 1 + 1 + 1 + 128 + 1 + 1 + 1 + 128 + 1 + 1 + 1 + 128 + 1 = 523
// each char takes 2 bytes. 523 * 2 = 1046
int commandTextLength = ADP.CharSize * CommandText.Length;

if (commandTextLength <= MaxRPCNameLength)
{
rpc.rpcName = CommandText; // just get the raw command text
}
else
{
throw ADP.InvalidArgumentLength(nameof(CommandText), MaxRPCNameLength);
}

SetUpRPCParameters(rpc, inSchema, parameters);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ namespace Microsoft.Data.SqlClient
public sealed class SqlCommand : DbCommand, ICloneable
{
private static int _objectTypeCount; // EventSource Counter
private const int MaxRPCNameLength = 1046;
internal readonly int ObjectID = System.Threading.Interlocked.Increment(ref _objectTypeCount);

private string _commandText;
Expand Down Expand Up @@ -1053,7 +1054,7 @@ override public void Prepare()
{
tdsReliabilitySection.Start();
#else
{
{
#endif //DEBUG
InternalPrepare();
}
Expand Down Expand Up @@ -1239,7 +1240,7 @@ override public void Cancel()
{
tdsReliabilitySection.Start();
#else
{
{
#endif //DEBUG
bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_activeConnection);

Expand Down Expand Up @@ -6570,7 +6571,19 @@ private void BuildRPC(bool inSchema, SqlParameterCollection parameters, ref _Sql
int count = CountSendableParameters(parameters);
GetRPCObject(count, ref rpc);

rpc.rpcName = this.CommandText; // just get the raw command text
// TDS Protocol allows rpc name with maximum length of 1046 bytes for ProcName
// 4-part name 1 + 128 + 1 + 1 + 1 + 128 + 1 + 1 + 1 + 128 + 1 + 1 + 1 + 128 + 1 = 523
// each char takes 2 bytes. 523 * 2 = 1046
int commandTextLength = ADP.CharSize * CommandText.Length;

if (commandTextLength <= MaxRPCNameLength)
{
rpc.rpcName = CommandText; // just get the raw command text
}
else
{
throw ADP.InvalidArgumentLength(nameof(CommandText), MaxRPCNameLength);
}

SetUpRPCParameters(rpc, 0, inSchema, parameters);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@
<Compile Include="SQL\ParallelTransactionsTest\ParallelTransactionsTest.cs" />
<Compile Include="SQL\SqlBulkCopyTest\CopyWidenNullInexactNumerics.cs" />
<Compile Include="SQL\SqlCommand\SqlCommandSetTest.cs" />
<Compile Include="SQL\SqlCommand\SqlCommandStoredProcTest.cs" />
<Compile Include="SQL\SqlCredentialTest\SqlCredentialTest.cs" />
<Compile Include="SQL\SqlDependencyTest\SqlDependencyTest.cs" />
<Compile Include="SQL\SqlSchemaInfoTest\SqlSchemaInfoTest.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// 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.Data;
using Xunit;

namespace Microsoft.Data.SqlClient.ManualTesting.Tests
{
public class SqlCommandStoredProcTest
{
private static readonly string s_tcp_connStr = DataTestUtility.TCPConnectionString;

[ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))]
public static void ShouldFailWithExceededLengthForSP()
{
string baseCommandText = "random text\u0000\u400a\u7300\u7400\u6100\u7400\u6500\u6d00\u6500\u6e00\u7400\u0000\u0006\u01ff\u0900\uf004\u0000\uffdc\u0001";
string exceededLengthText = baseCommandText + new string(' ', 2000);
using SqlConnection conn = new(s_tcp_connStr);
conn.Open();
using SqlCommand command = new()
{
Connection = conn,
CommandType = CommandType.StoredProcedure,
CommandText = exceededLengthText
};

// It should fail on the driver as the length of RPC is over 1046
// 4-part name 1 + 128 + 1 + 1 + 1 + 128 + 1 + 1 + 1 + 128 + 1 + 1 + 1 + 128 + 1 = 523
// each char takes 2 bytes. 523 * 2 = 1046
Assert.Throws<ArgumentException>(() => command.ExecuteScalar());

command.CommandText = baseCommandText;
var ex = Assert.Throws<SqlException>(() => command.ExecuteScalar());
Assert.StartsWith("Could not find stored procedure", ex.Message);
}
}
}