Skip to content

Commit 4a89c1b

Browse files
[Hotfix 4.0.2] | Fix CommandText length for stored procedures (#1484) (#1721)
1 parent ee571af commit 4a89c1b

File tree

4 files changed

+71
-4
lines changed

4 files changed

+71
-4
lines changed

src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ namespace Microsoft.Data.SqlClient
3434
public sealed partial class SqlCommand : DbCommand, ICloneable
3535
{
3636
private static int _objectTypeCount; // EventSource Counter
37+
private const int MaxRPCNameLength = 1046;
3738
internal readonly int ObjectID = Interlocked.Increment(ref _objectTypeCount); private string _commandText;
3839

3940
private static readonly Func<AsyncCallback, object, IAsyncResult> s_beginExecuteReaderAsync = BeginExecuteReaderAsyncCallback;
@@ -5802,7 +5803,20 @@ private void BuildRPC(bool inSchema, SqlParameterCollection parameters, ref _Sql
58025803
GetRPCObject(0, userParameterCount, ref rpc);
58035804

58045805
rpc.ProcID = 0;
5805-
rpc.rpcName = this.CommandText; // just get the raw command text
5806+
5807+
// TDS Protocol allows rpc name with maximum length of 1046 bytes for ProcName
5808+
// 4-part name 1 + 128 + 1 + 1 + 1 + 128 + 1 + 1 + 1 + 128 + 1 + 1 + 1 + 128 + 1 = 523
5809+
// each char takes 2 bytes. 523 * 2 = 1046
5810+
int commandTextLength = ADP.CharSize * CommandText.Length;
5811+
5812+
if (commandTextLength <= MaxRPCNameLength)
5813+
{
5814+
rpc.rpcName = CommandText; // just get the raw command text
5815+
}
5816+
else
5817+
{
5818+
throw ADP.InvalidArgumentLength(nameof(CommandText), MaxRPCNameLength);
5819+
}
58065820

58075821
SetUpRPCParameters(rpc, inSchema, parameters);
58085822
}

src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ namespace Microsoft.Data.SqlClient
4040
public sealed class SqlCommand : DbCommand, ICloneable
4141
{
4242
private static int _objectTypeCount; // EventSource Counter
43+
private const int MaxRPCNameLength = 1046;
4344
internal readonly int ObjectID = System.Threading.Interlocked.Increment(ref _objectTypeCount);
4445

4546
private string _commandText;
@@ -1082,7 +1083,7 @@ public override void Prepare()
10821083
{
10831084
tdsReliabilitySection.Start();
10841085
#else
1085-
{
1086+
{
10861087
#endif //DEBUG
10871088
InternalPrepare();
10881089
}
@@ -1267,7 +1268,7 @@ public override void Cancel()
12671268
{
12681269
tdsReliabilitySection.Start();
12691270
#else
1270-
{
1271+
{
12711272
#endif //DEBUG
12721273
bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_activeConnection);
12731274

@@ -6690,7 +6691,19 @@ private void BuildRPC(bool inSchema, SqlParameterCollection parameters, ref _Sql
66906691
int count = CountSendableParameters(parameters);
66916692
GetRPCObject(count, ref rpc);
66926693

6693-
rpc.rpcName = this.CommandText; // just get the raw command text
6694+
// TDS Protocol allows rpc name with maximum length of 1046 bytes for ProcName
6695+
// 4-part name 1 + 128 + 1 + 1 + 1 + 128 + 1 + 1 + 1 + 128 + 1 + 1 + 1 + 128 + 1 = 523
6696+
// each char takes 2 bytes. 523 * 2 = 1046
6697+
int commandTextLength = ADP.CharSize * CommandText.Length;
6698+
6699+
if (commandTextLength <= MaxRPCNameLength)
6700+
{
6701+
rpc.rpcName = CommandText; // just get the raw command text
6702+
}
6703+
else
6704+
{
6705+
throw ADP.InvalidArgumentLength(nameof(CommandText), MaxRPCNameLength);
6706+
}
66946707

66956708
SetUpRPCParameters(rpc, 0, inSchema, parameters);
66966709
}

src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@
269269
<Compile Include="SQL\Common\SystemDataInternals\DataReaderHelper.cs" />
270270
<Compile Include="SQL\Common\SystemDataInternals\TdsParserHelper.cs" />
271271
<Compile Include="SQL\Common\SystemDataInternals\TdsParserStateObjectHelper.cs" />
272+
<Compile Include="SQL\SqlCommand\SqlCommandStoredProcTest.cs" />
272273
<Compile Include="TracingTests\TestTdsServer.cs" />
273274
<Compile Include="XUnitAssemblyAttributes.cs" />
274275
<Compile Include="$(CommonTestPath)\System\Collections\DictionaryExtensions.cs">
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System;
6+
using System.Data;
7+
using Xunit;
8+
9+
namespace Microsoft.Data.SqlClient.ManualTesting.Tests
10+
{
11+
public class SqlCommandStoredProcTest
12+
{
13+
private static readonly string s_tcp_connStr = DataTestUtility.TCPConnectionString;
14+
15+
[ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))]
16+
public static void ShouldFailWithExceededLengthForSP()
17+
{
18+
string baseCommandText = "random text\u0000\u400a\u7300\u7400\u6100\u7400\u6500\u6d00\u6500\u6e00\u7400\u0000\u0006\u01ff\u0900\uf004\u0000\uffdc\u0001";
19+
string exceededLengthText = baseCommandText + new string(' ', 2000);
20+
using SqlConnection conn = new(s_tcp_connStr);
21+
conn.Open();
22+
using SqlCommand command = new()
23+
{
24+
Connection = conn,
25+
CommandType = CommandType.StoredProcedure,
26+
CommandText = exceededLengthText
27+
};
28+
29+
// It should fail on the driver as the length of RPC is over 1046
30+
// 4-part name 1 + 128 + 1 + 1 + 1 + 128 + 1 + 1 + 1 + 128 + 1 + 1 + 1 + 128 + 1 = 523
31+
// each char takes 2 bytes. 523 * 2 = 1046
32+
Assert.Throws<ArgumentException>(() => command.ExecuteScalar());
33+
34+
command.CommandText = baseCommandText;
35+
var ex = Assert.Throws<SqlException>(() => command.ExecuteScalar());
36+
Assert.StartsWith("Could not find stored procedure", ex.Message);
37+
}
38+
}
39+
}

0 commit comments

Comments
 (0)