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

Fix CommandText length for stored procedures #1484

Merged
merged 5 commits into from
May 30, 2022
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
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,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 @@ -5802,7 +5803,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 @@ -40,6 +40,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 @@ -1082,7 +1083,7 @@ public override void Prepare()
{
tdsReliabilitySection.Start();
#else
{
{
#endif //DEBUG
InternalPrepare();
}
Expand Down Expand Up @@ -1267,7 +1268,7 @@ public override void Cancel()
{
tdsReliabilitySection.Start();
#else
{
{
#endif //DEBUG
bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_activeConnection);

Expand Down Expand Up @@ -6690,7 +6691,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 @@ -29,7 +29,7 @@
<Link>TCECryptoNativeBaselineRsa.txt</Link>
</Content>
</ItemGroup>
<ItemGroup Condition="'$(TargetsWindows)' == 'true' AND !$(ReferenceType.Contains('NetStandard')) AND ('$(TestSet)' == '' OR '$(TestSet)' == 'AE')" >
<ItemGroup Condition="'$(TargetsWindows)' == 'true' AND !$(ReferenceType.Contains('NetStandard')) AND ('$(TestSet)' == '' OR '$(TestSet)' == 'AE')">
<Compile Include="AlwaysEncrypted\CspProviderExt.cs" />
<Compile Include="AlwaysEncrypted\TestFixtures\Setup\CertificateUtilityWin.cs" />
<Compile Include="AlwaysEncrypted\TestFixtures\Setup\CspProviderColumnMasterKey.cs" />
Expand Down Expand Up @@ -239,7 +239,7 @@
<None Include="SQL\ParameterTest\SqlParameterTest_ReleaseMode.bsl" />
<None Include="SQL\ParameterTest\SqlParameterTest_ReleaseMode_Azure.bsl" />
</ItemGroup>
<ItemGroup Condition="'$(TargetGroup)'=='netcoreapp' AND ('$(TestSet)' == '' OR '$(TestSet)' == '3')" >
<ItemGroup Condition="'$(TargetGroup)'=='netcoreapp' AND ('$(TestSet)' == '' OR '$(TestSet)' == '3')">
<Compile Include="TracingTests\EventCounterTest.cs" />
<Compile Include="TracingTests\DiagnosticTest.cs" />
<Compile Include="TracingTests\FakeDiagnosticListenerObserver.cs" />
Expand Down Expand Up @@ -269,6 +269,7 @@
<Compile Include="SQL\Common\SystemDataInternals\DataReaderHelper.cs" />
<Compile Include="SQL\Common\SystemDataInternals\TdsParserHelper.cs" />
<Compile Include="SQL\Common\SystemDataInternals\TdsParserStateObjectHelper.cs" />
<Compile Include="SQL\SqlCommand\SqlCommandStoredProcTest.cs" />
<Compile Include="TracingTests\TestTdsServer.cs" />
<Compile Include="XUnitAssemblyAttributes.cs" />
<Compile Include="$(CommonTestPath)\System\Collections\DictionaryExtensions.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);
}
}
}