Skip to content

Commit 2216abd

Browse files
authored
Merge | SqlCommand The Mop Up (#3738)
1 parent 0e8ae37 commit 2216abd

File tree

12 files changed

+1961
-3609
lines changed

12 files changed

+1961
-3609
lines changed

src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,9 @@
585585
<Compile Include="$(CommonSourceRoot)Microsoft\Data\SqlClient\SqlCommand.cs">
586586
<Link>Microsoft\Data\SqlClient\SqlCommand.cs</Link>
587587
</Compile>
588+
<Compile Include="$(CommonSourceRoot)Microsoft\Data\SqlClient\SqlCommand.Batch.cs">
589+
<Link>Microsoft\Data\SqlClient\SqlCommand.Batch.cs</Link>
590+
</Compile>
588591
<Compile Include="$(CommonSourceRoot)Microsoft\Data\SqlClient\SqlCommand.Encryption.cs">
589592
<Link>Microsoft\Data\SqlClient\SqlCommand.Encryption.cs</Link>
590593
</Compile>
@@ -840,8 +843,7 @@
840843
<Compile Include="$(CommonSourceRoot)System\Diagnostics\CodeAnalysis.cs">
841844
<Link>System\Diagnostics\CodeAnalysis.cs</Link>
842845
</Compile>
843-
844-
<Compile Include="Microsoft\Data\SqlClient\SqlCommand.netcore.cs" />
846+
845847
<Compile Include="Microsoft\Data\SqlClient\SqlInternalConnectionTds.cs" />
846848
<Compile Include="Microsoft.Data.SqlClient.TypeForwards.cs" />
847849
</ItemGroup>

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

Lines changed: 0 additions & 1780 deletions
This file was deleted.

src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -756,6 +756,9 @@
756756
<Compile Include="$(CommonSourceRoot)Microsoft\Data\SqlClient\SqlCommand.cs">
757757
<Link>Microsoft\Data\SqlClient\SqlCommand.cs</Link>
758758
</Compile>
759+
<Compile Include="$(CommonSourceRoot)Microsoft\Data\SqlClient\SqlCommand.Batch.cs">
760+
<Link>Microsoft\Data\SqlClient\SqlCommand.Batch.cs</Link>
761+
</Compile>
759762
<Compile Include="$(CommonSourceRoot)Microsoft\Data\SqlClient\SqlCommand.Encryption.cs">
760763
<Link>Microsoft\Data\SqlClient\SqlCommand.Encryption.cs</Link>
761764
</Compile>
@@ -1014,8 +1017,7 @@
10141017
<Compile Include="$(CommonSourceRoot)System\Runtime\CompilerServices\IsExternalInit.netfx.cs">
10151018
<Link>System\Runtime\CompilerServices\IsExternalInit.netfx.cs</Link>
10161019
</Compile>
1017-
1018-
<Compile Include="Microsoft\Data\SqlClient\SqlCommand.netfx.cs" />
1020+
10191021
<Compile Include="Microsoft\Data\SqlClient\SqlInternalConnectionTds.cs" />
10201022
</ItemGroup>
10211023
<!-- Resources -->

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

Lines changed: 0 additions & 1791 deletions
This file was deleted.
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
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.Collections.Generic;
6+
using System.Data;
7+
using System.Diagnostics;
8+
9+
namespace Microsoft.Data.SqlClient
10+
{
11+
// @TODO: There's a good question here - should this be a separate type of SqlCommand?
12+
public sealed partial class SqlCommand
13+
{
14+
#region Internal Methods
15+
16+
internal void AddBatchCommand(SqlBatchCommand batchCommand)
17+
{
18+
Debug.Assert(_batchRPCMode, "Command is not in batch RPC Mode");
19+
Debug.Assert(_RPCList is not null);
20+
21+
_SqlRPC rpc = new _SqlRPC { batchCommand = batchCommand };
22+
string commandText = batchCommand.CommandText;
23+
CommandType cmdType = batchCommand.CommandType;
24+
25+
CommandText = commandText;
26+
CommandType = cmdType;
27+
28+
SetColumnEncryptionSetting(batchCommand.ColumnEncryptionSetting);
29+
30+
// @TODO: Hmm, maybe we could have get/put become a IDisposable thing
31+
GetStateObject();
32+
if (cmdType is CommandType.StoredProcedure)
33+
{
34+
BuildRPC(inSchema: false, batchCommand.Parameters, ref rpc);
35+
}
36+
else
37+
{
38+
// All batch sql statements must be executed inside sp_executesql, including those
39+
// without parameters
40+
BuildExecuteSql(CommandBehavior.Default, commandText, batchCommand.Parameters, ref rpc);
41+
}
42+
43+
_RPCList.Add(rpc);
44+
ReliablePutStateObject();
45+
}
46+
47+
internal SqlBatchCommand GetBatchCommand(int index) =>
48+
_RPCList[index].batchCommand;
49+
50+
// @TODO: This should be a property.
51+
internal SqlBatchCommand GetCurrentBatchCommand()
52+
{
53+
return _batchRPCMode
54+
? _RPCList[_currentlyExecutingBatch].batchCommand
55+
: _rpcArrayOf1?[0].batchCommand;
56+
}
57+
58+
// @TODO: 1) This should be a property
59+
// @TODO: 2) This could be a `int?`
60+
internal int GetCurrentBatchIndex() =>
61+
_batchRPCMode ? _currentlyExecutingBatch : -1;
62+
63+
// @TODO: Indicate this is for batch RPC usage
64+
internal SqlException GetErrors(int commandIndex)
65+
{
66+
SqlException result = null;
67+
68+
_SqlRPC rpc = _RPCList[commandIndex];
69+
int length = rpc.errorsIndexEnd - rpc.errorsIndexStart;
70+
if (length > 0)
71+
{
72+
SqlErrorCollection errors = new SqlErrorCollection();
73+
for (int i = rpc.errorsIndexStart; i < rpc.errorsIndexEnd; i++)
74+
{
75+
errors.Add(rpc.errors[i]);
76+
}
77+
for (int i = rpc.warningsIndexStart; i < rpc.warningsIndexEnd; i++)
78+
{
79+
errors.Add(rpc.warnings[i]);
80+
}
81+
82+
result = SqlException.CreateException(
83+
errors,
84+
_activeConnection.ServerVersion,
85+
_activeConnection.ClientConnectionId,
86+
innerException: null,
87+
batchCommand: null);
88+
}
89+
90+
return result;
91+
}
92+
93+
// @TODO: Should be renamed to indicate only applies to batch RPC mode
94+
internal int? GetRecordsAffected(int commandIndex)
95+
{
96+
Debug.Assert(_batchRPCMode, "Command is not in batch RPC mode");
97+
Debug.Assert(_RPCList is not null, "Batch commands have been cleared");
98+
return _RPCList[commandIndex].recordsAffected;
99+
}
100+
101+
// @TODO: Rename to match naming conventions
102+
internal void SetBatchRPCMode(bool value, int commandCount = 1)
103+
{
104+
_batchRPCMode = value;
105+
ClearBatchCommand();
106+
if (_batchRPCMode)
107+
{
108+
if (_RPCList is null)
109+
{
110+
// @TODO: Could this be done with an array?
111+
_RPCList = new List<_SqlRPC>(commandCount);
112+
}
113+
else
114+
{
115+
_RPCList.Capacity = commandCount;
116+
}
117+
}
118+
}
119+
120+
// @TODO: Rename to match naming conventions
121+
internal void SetBatchRPCModeReadyToExecute()
122+
{
123+
Debug.Assert(_batchRPCMode, "Command is not in batch RPC Mode");
124+
Debug.Assert(_RPCList is not null, "No batch commands specified");
125+
126+
_currentlyExecutingBatch = 0;
127+
}
128+
129+
#endregion
130+
131+
#region Private Methods
132+
133+
private void ClearBatchCommand()
134+
{
135+
_RPCList?.Clear();
136+
_currentlyExecutingBatch = 0;
137+
}
138+
139+
#endregion
140+
}
141+
}

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1351,7 +1351,11 @@ private SqlDataReader TryFetchInputParameterEncryptionInfo(
13511351
_sqlRPCParameterEncryptionReqArray = new _SqlRPC[1];
13521352

13531353
_SqlRPC rpc = null;
1354-
GetRPCObject(systemParamCount: 0, GetParameterCount(_parameters), ref rpc);
1354+
GetRPCObject(
1355+
systemParamCount: 0,
1356+
GetParameterCount(_parameters),
1357+
ref rpc,
1358+
forSpDescribeParameterEncryption: false);
13551359
Debug.Assert(rpc is not null, "GetRPCObject should not return rpc as null.");
13561360

13571361
rpc.rpcName = CommandText;

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ public override int ExecuteNonQuery()
138138
finally
139139
{
140140
SqlStatistics.StopTimer(statistics);
141-
WriteEndExecuteEvent(success, sqlExceptionNumber, synchronous: true);
141+
WriteEndExecuteEvent(success, sqlExceptionNumber, isSynchronous: true);
142142
}
143143
}
144144

@@ -414,7 +414,7 @@ private int EndExecuteNonQueryInternal(IAsyncResult asyncResult)
414414
finally
415415
{
416416
SqlStatistics.StopTimer(statistics);
417-
WriteEndExecuteEvent(success, sqlExceptionNumber, synchronous: false);
417+
WriteEndExecuteEvent(success, sqlExceptionNumber, isSynchronous: false);
418418
}
419419
}
420420

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

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ public SqlDataReader EndExecuteReader(IAsyncResult asyncResult)
146146
finally
147147
{
148148
SqlStatistics.StopTimer(statistics);
149-
WriteEndExecuteEvent(success, sqlExceptionNumber, synchronous: true);
149+
WriteEndExecuteEvent(success, sqlExceptionNumber, isSynchronous: true);
150150

151151
if (e is not null)
152152
{
@@ -409,7 +409,11 @@ private _SqlRPC BuildExecute(bool inSchema)
409409
int userParameterCount = CountSendableParameters(_parameters);
410410

411411
_SqlRPC rpc = null;
412-
GetRPCObject(systemParameterCount, userParameterCount, ref rpc);
412+
GetRPCObject(
413+
systemParameterCount,
414+
userParameterCount,
415+
ref rpc,
416+
forSpDescribeParameterEncryption: false);
413417
rpc.ProcID = TdsEnums.RPC_PROCID_EXECUTE;
414418
rpc.rpcName = TdsEnums.SP_EXECUTE;
415419

@@ -445,7 +449,11 @@ private void BuildExecuteSql(
445449
int userParamCount = CountSendableParameters(parameters);
446450
int systemParamCount = userParamCount > 0 ? 2 : 1;
447451

448-
GetRPCObject(systemParamCount, userParamCount, ref rpc);
452+
GetRPCObject(
453+
systemParamCount,
454+
userParamCount,
455+
ref rpc,
456+
forSpDescribeParameterEncryption: false);
449457
rpc.ProcID = TdsEnums.RPC_PROCID_EXECUTESQL;
450458
rpc.rpcName = TdsEnums.SP_EXECUTESQL;
451459

@@ -465,7 +473,10 @@ private void BuildExecuteSql(
465473
if (userParamCount > 0)
466474
{
467475
// @TODO: Why does batch RPC mode use different parameters?
468-
string paramList = BuildParamList(_stateObj.Parser, _batchRPCMode ? parameters : _parameters);
476+
string paramList = BuildParamList(
477+
_stateObj.Parser,
478+
_batchRPCMode ? parameters : _parameters,
479+
includeReturnValue: false);
469480
sqlParam = rpc.systemParams[1];
470481
sqlParam.SqlDbType = (paramList.Length << 1) <= TdsEnums.TYPE_SIZE_LIMIT
471482
? SqlDbType.NVarChar
@@ -488,7 +499,11 @@ private _SqlRPC BuildPrepExec(CommandBehavior behavior)
488499
int userParameterCount = CountSendableParameters(_parameters);
489500

490501
_SqlRPC rpc = null;
491-
GetRPCObject(systemParameterCount, userParameterCount, ref rpc);
502+
GetRPCObject(
503+
systemParameterCount,
504+
userParameterCount,
505+
ref rpc,
506+
forSpDescribeParameterEncryption: false);
492507
rpc.ProcID = TdsEnums.RPC_PROCID_PREPEXEC;
493508
rpc.rpcName = TdsEnums.SP_PREPEXEC;
494509

@@ -503,7 +518,10 @@ private _SqlRPC BuildPrepExec(CommandBehavior behavior)
503518
rpc.systemParamOptions[0] = TdsEnums.RPC_PARAM_BYREF;
504519

505520
// @batch_params
506-
string paramList = BuildParamList(_stateObj.Parser, _parameters);
521+
string paramList = BuildParamList(
522+
_stateObj.Parser,
523+
_parameters,
524+
includeReturnValue: false);
507525
sqlParam = rpc.systemParams[1];
508526
// @TODO: This pattern is used quite a bit - it could be factored out
509527
sqlParam.SqlDbType = (paramList.Length << 1) <= TdsEnums.TYPE_SIZE_LIMIT
@@ -519,8 +537,8 @@ private _SqlRPC BuildPrepExec(CommandBehavior behavior)
519537
sqlParam.SqlDbType = (text.Length << 1) <= TdsEnums.TYPE_SIZE_LIMIT
520538
? SqlDbType.NVarChar
521539
: SqlDbType.NText;
522-
sqlParam.Size = text.Length;
523540
sqlParam.Value = text;
541+
sqlParam.Size = text.Length;
524542
sqlParam.Direction = ParameterDirection.Input;
525543

526544
SetUpRPCParameters(rpc, inSchema: false, _parameters);
@@ -538,7 +556,11 @@ private void BuildRPC(bool inSchema, SqlParameterCollection parameters, ref _Sql
538556
Debug.Assert(CommandType is CommandType.StoredProcedure, "Command must be a stored proc to execute an RPC");
539557

540558
int userParameterCount = CountSendableParameters(parameters);
541-
GetRPCObject(systemParamCount: 0, userParameterCount, ref rpc);
559+
GetRPCObject(
560+
systemParamCount: 0,
561+
userParameterCount,
562+
ref rpc,
563+
forSpDescribeParameterEncryption: false);
542564
rpc.ProcID = 0;
543565

544566
// TDS Protocol allows rpc name with maximum length of 1046 bytes for ProcName
@@ -710,7 +732,7 @@ private SqlDataReader EndExecuteReaderInternal(IAsyncResult asyncResult)
710732
finally
711733
{
712734
SqlStatistics.StopTimer(statistics);
713-
WriteEndExecuteEvent(success, sqlExceptionNumber, synchronous: false);
735+
WriteEndExecuteEvent(success, sqlExceptionNumber, isSynchronous: false);
714736
}
715737
}
716738

@@ -1359,7 +1381,7 @@ private SqlDataReader RunExecuteReaderTds(
13591381
$"Command Text '{CommandText}'");
13601382
}
13611383

1362-
string text = GetCommandText(cmdBehavior) + GetResetOptionsString(cmdBehavior);
1384+
string text = GetCommandText(cmdBehavior) + GetOptionsResetString(cmdBehavior);
13631385

13641386
// If the query requires enclave computations, pass the enclave package in the
13651387
// SqlBatch TDS stream
@@ -1467,7 +1489,7 @@ private SqlDataReader RunExecuteReaderTds(
14671489
// If we need to augment the command because a user has changed the command
14681490
// behavior (e.g. FillSchema) then batch sql them over. This is inefficient (3
14691491
// round trips) but the only way we can get metadata only from a stored proc.
1470-
optionSettings = GetSetOptionsString(cmdBehavior);
1492+
optionSettings = GetOptionsSetString(cmdBehavior);
14711493

14721494
if (returnStream)
14731495
{
@@ -1506,7 +1528,7 @@ private SqlDataReader RunExecuteReaderTds(
15061528
}
15071529

15081530
// And turn OFF when the ds exhausts the stream on Close()
1509-
optionSettings = GetResetOptionsString(cmdBehavior);
1531+
optionSettings = GetOptionsResetString(cmdBehavior);
15101532
}
15111533

15121534
// Execute sproc

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ public override object ExecuteScalar()
7070
finally
7171
{
7272
SqlStatistics.StopTimer(statistics);
73-
WriteEndExecuteEvent(success, sqlExceptionNumber, synchronous: true);
73+
WriteEndExecuteEvent(success, sqlExceptionNumber, isSynchronous: true);
7474
}
7575
}
7676

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ public XmlReader ExecuteXmlReader()
126126
finally
127127
{
128128
SqlStatistics.StopTimer(statistics);
129-
WriteEndExecuteEvent(success, sqlExceptionNumber, synchronous: true);
129+
WriteEndExecuteEvent(success, sqlExceptionNumber, isSynchronous: true);
130130
}
131131
}
132132

@@ -448,7 +448,7 @@ private XmlReader EndExecuteXmlReaderInternal(IAsyncResult asyncResult)
448448
}
449449
finally
450450
{
451-
WriteEndExecuteEvent(success, sqlExceptionNumber, synchronous: false);
451+
WriteEndExecuteEvent(success, sqlExceptionNumber, isSynchronous: false);
452452
}
453453
}
454454

0 commit comments

Comments
 (0)