Skip to content

Commit

Permalink
Add SqlCommand.DisableOutputParameters Feature (dotnet#1041)
Browse files Browse the repository at this point in the history
  • Loading branch information
Wraith2 authored and Davoud Eshtehari committed Aug 31, 2021
1 parent ea162b2 commit a351b5d
Show file tree
Hide file tree
Showing 14 changed files with 419 additions and 60 deletions.
95 changes: 81 additions & 14 deletions doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml
Original file line number Diff line number Diff line change
Expand Up @@ -286,8 +286,13 @@ The following console application creates updates data within the **AdventureWor
<exception cref="T:System.InvalidOperationException">
The
<see cref="T:Microsoft.Data.SqlClient.SqlConnection" />
closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support).
</exception>
closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support).

- or -

<see cref="P:Microssoft.Data.SqlClient.SqlCommand.EnableOptimizedParameterBinding" />
is set to true and a parameter with direction Output or InputOutput has been added to the <see cref="P:Microsoft.Data.SqlClient.SqlCommand.Parameters" /> collection.
</exception>
<exception cref="T:System.IO.IOException">
An error occurred in a
<see cref="T:System.IO.Stream" />
Expand Down Expand Up @@ -399,8 +404,13 @@ To set up this example, create a new Windows application. Put a <xref:System.Win
<exception cref="T:System.InvalidOperationException">
The
<see cref="T:Microsoft.Data.SqlClient.SqlConnection" />
closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support).
</exception>
closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support).

- or -

<see cref="P:Microssoft.Data.SqlClient.SqlCommand.EnableOptimizedParameterBinding" />
is set to true and a parameter with direction Output or InputOutput has been added to the <see cref="P:Microsoft.Data.SqlClient.SqlCommand.Parameters" /> collection.
</exception>
</BeginExecuteNonQuery>
<BeginExecuteReader name="default">
<summary>
Expand Down Expand Up @@ -477,8 +487,13 @@ The following console application starts the process of retrieving a data reader
<exception cref="T:System.InvalidOperationException">
The
<see cref="T:Microsoft.Data.SqlClient.SqlConnection" />
closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support).
</exception>
closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support).

- or -

<see cref="P:Microssoft.Data.SqlClient.SqlCommand.EnableOptimizedParameterBinding" />
is set to true and a parameter with direction Output or InputOutput has been added to the <see cref="P:Microsoft.Data.SqlClient.SqlCommand.Parameters" /> collection.
</exception>
<exception cref="T:System.IO.IOException">
An error occurred in a
<see cref="T:System.IO.Stream" />
Expand Down Expand Up @@ -584,8 +599,13 @@ This example also passes the `CommandBehavior.CloseConnection` and `CommandBehav
<exception cref="T:System.InvalidOperationException">
The
<see cref="T:Microsoft.Data.SqlClient.SqlConnection" />
closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support).
</exception>
closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support).

- or -

<see cref="P:Microssoft.Data.SqlClient.SqlCommand.EnableOptimizedParameterBinding" />
is set to true and a parameter with direction Output or InputOutput has been added to the <see cref="P:Microsoft.Data.SqlClient.SqlCommand.Parameters" /> collection.
</exception>
<exception cref="T:System.IO.IOException">
An error occurred in a
<see cref="T:System.IO.Stream" />
Expand Down Expand Up @@ -702,8 +722,13 @@ To set up this example, create a new Windows application. Put a <xref:System.Win
<exception cref="T:System.InvalidOperationException">
The
<see cref="T:Microsoft.Data.SqlClient.SqlConnection" />
closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support).
</exception>
closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support).

- or -

<see cref="P:Microssoft.Data.SqlClient.SqlCommand.EnableOptimizedParameterBinding" />
is set to true and a parameter with direction Output or InputOutput has been added to the <see cref="P:Microsoft.Data.SqlClient.SqlCommand.Parameters" /> collection.
</exception>
<exception cref="T:System.IO.IOException">
An error occurred in a
<see cref="T:System.IO.Stream" />
Expand Down Expand Up @@ -831,8 +856,13 @@ This example passes the `CommandBehavior.CloseConnection` value in the `behavior
<exception cref="T:System.InvalidOperationException">
The
<see cref="T:Microsoft.Data.SqlClient.SqlConnection" />
closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support).
</exception>
closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support).

- or -

<see cref="P:Microssoft.Data.SqlClient.SqlCommand.EnableOptimizedParameterBinding" />
is set to true and a parameter with direction Output or InputOutput has been added to the <see cref="P:Microsoft.Data.SqlClient.SqlCommand.Parameters" /> collection.
</exception>
<exception cref="T:System.IO.IOException">
An error occurred in a
<see cref="T:System.IO.Stream" />
Expand Down Expand Up @@ -940,6 +970,11 @@ The following console application starts the process of retrieving XML data asyn
The
<see cref="T:Microsoft.Data.SqlClient.SqlConnection" />
closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support).

- or -

<see cref="P:Microssoft.Data.SqlClient.SqlCommand.EnableOptimizedParameterBinding" />
is set to true and a parameter with direction Output or InputOutput has been added to the <see cref="P:Microsoft.Data.SqlClient.SqlCommand.Parameters" /> collection.
</exception>
<exception cref="T:System.IO.IOException">
An error occurred in a
Expand Down Expand Up @@ -1067,8 +1102,13 @@ To set up this example, create a new Windows application. Put a <xref:System.Win
<exception cref="T:System.InvalidOperationException">
The
<see cref="T:Microsoft.Data.SqlClient.SqlConnection" />
closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support).
</exception>
closed or dropped during a streaming operation. For more information about streaming, see [SqlClient Streaming Support](/sql/connect/ado-net/sqlclient-streaming-support).

- or -

<see cref="P:Microssoft.Data.SqlClient.SqlCommand.EnableOptimizedParameterBinding" />
is set to true and a parameter with direction Output or InputOutput has been added to the <see cref="P:Microsoft.Data.SqlClient.SqlCommand.Parameters" /> collection.
</exception>
<exception cref="T:System.IO.IOException">
An error occurred in a
<see cref="T:System.IO.Stream" />
Expand Down Expand Up @@ -1343,6 +1383,33 @@ The <xref:Microsoft.Data.SqlClient.SqlCommand.CreateParameter%2A> method is a st
To be added.
</remarks>
</Dispose>
<EnableOptimizedParameterBinding>
<summary>
Gets or sets a value indicating whether the command object should optimize parameter performance by disabling Output and InputOutput directions when submitting the command to the SQL Server.
</summary>
<value>
A value indicating whether the command object should optimize parameter performance by disabling Output and InputOuput parameter directions when submitting the command to the SQL Server.
The default is <see langword="false" />.
</value>
<remarks>
<format type="text/markdown">
<![CDATA[
## Remarks
You must set the value for this property before the command is executed for it to take effect.
When a command is submitted to the server with parameters a list of the parameter names is sent as part of the submission. The list is used on the server to match Output and InputOutput parameters to the results of the query execution so that the values can be returned to the caller. This option disables the construction and submission of the parameter name list and as a consequence disables the use of Output and InputOutput parameters. The return parameter is not affected by this option.
A command sent with this option changes the way parameters are handled on the server, because there is no need to maintain an output parameter map. The result of this change is that queries with large numbers of input parameters may execute much faster.
The fewest number of parameters where this will take effect depends on the individual situation and should be detected by measuring query duration with and without the option enabled. Any query with more than 24 parameters may show lower overall query duration. Queries with parameter counts lower than 24 are unlikely to show a difference.
> [!NOTE]
If the option is enabled and a parameter with Direction Output or InputOutput is present in the Parameters collection an InvalidOperationException will be thrown when the command is executed.
]]>
</format>
</remarks>
</EnableOptimizedParameterBinding>
<EndExecuteNonQuery name="IAsyncResult">
<param name="asyncResult">
The
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,8 @@ public SqlCommand(string cmdText, Microsoft.Data.SqlClient.SqlConnection connect
[System.ComponentModel.DesignOnlyAttribute(true)]
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
public override bool DesignTimeVisible { get { throw null; } set { } }
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml' path='docs/members[@name="SqlCommand"]/EnableOptimizedParameterBinding/*'/>
public bool EnableOptimizedParameterBinding { get { throw null; } set { } }
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml' path='docs/members[@name="SqlCommand"]/Parameters/*'/>
public new Microsoft.Data.SqlClient.SqlParameterCollection Parameters { get { throw null; } }
/// <include file='../../../../doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml' path='docs/members[@name="SqlCommand"]/Transaction/*'/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -692,6 +692,9 @@ public override bool DesignTimeVisible
}
}

/// <include file='../../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml' path='docs/members[@name="SqlCommand"]/EnableOptimizedParameterBinding/*'/>
public bool EnableOptimizedParameterBinding { get; set; }

/// <include file='../../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlCommand.xml' path='docs/members[@name="SqlCommand"]/Parameters/*'/>
new public SqlParameterCollection Parameters
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,11 @@ internal static Exception ParameterCannotBeEmpty(string paramName)
return ADP.ArgumentNull(System.StringsHelper.GetString(Strings.SQL_ParameterCannotBeEmpty, paramName));
}

internal static Exception ParameterDirectionInvalidForOptimizedBinding(string paramName)
{
return ADP.InvalidOperation(System.StringsHelper.GetString(Strings.SQL_ParameterDirectionInvalidForOptimizedBinding, paramName));
}

internal static Exception ActiveDirectoryInteractiveTimeout()
{
return ADP.TimeoutException(Strings.SQL_Timeout_Active_Directory_Interactive_Authentication);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8988,14 +8988,19 @@ internal Task TdsExecuteRPC(SqlCommand cmd, _SqlRPC[] rpcArray, int timeout, boo
int parametersLength = rpcext.userParamCount + rpcext.systemParamCount;

bool isAdvancedTraceOn = SqlClientEventSource.Log.IsAdvancedTraceOn();
bool enableOptimizedParameterBinding = cmd.EnableOptimizedParameterBinding;

for (int i = (ii == startRpc) ? startParam : 0; i < parametersLength; i++)
{
byte options = 0;
SqlParameter param = rpcext.GetParameterByIndex(i, out options);
// Since we are reusing the parameters array, we cannot rely on length to indicate no of parameters.
if (param == null)
{
break; // End of parameters for this execute
}

ParameterDirection parameterDirection = param.Direction;

// Throw an exception if ForceColumnEncryption is set on a parameter and the ColumnEncryption is not enabled on SqlConnection or SqlCommand
if (param.ForceColumnEncryption &&
Expand All @@ -9007,12 +9012,17 @@ internal Task TdsExecuteRPC(SqlCommand cmd, _SqlRPC[] rpcArray, int timeout, boo

// Check if the applications wants to force column encryption to avoid sending sensitive data to server
if (param.ForceColumnEncryption && param.CipherMetadata == null
&& (param.Direction == ParameterDirection.Input || param.Direction == ParameterDirection.InputOutput))
&& (parameterDirection == ParameterDirection.Input || parameterDirection == ParameterDirection.InputOutput))
{
// Application wants a parameter to be encrypted before sending it to server, however server doesnt think this parameter needs encryption.
throw SQL.ParamUnExpectedEncryptionMetadata(param.ParameterName, rpcext.GetCommandTextOrRpcName());
}

if (enableOptimizedParameterBinding && (parameterDirection == ParameterDirection.Output || parameterDirection == ParameterDirection.InputOutput))
{
throw SQL.ParameterDirectionInvalidForOptimizedBinding(param.ParameterName);
}

// Validate parameters are not variable length without size and with null value.
param.Validate(i, isCommandProc);

Expand All @@ -9021,7 +9031,7 @@ internal Task TdsExecuteRPC(SqlCommand cmd, _SqlRPC[] rpcArray, int timeout, boo

if (mt.IsNewKatmaiType)
{
WriteSmiParameter(param, i, 0 != (options & TdsEnums.RPC_PARAM_DEFAULT), stateObj, isAdvancedTraceOn);
WriteSmiParameter(param, i, 0 != (options & TdsEnums.RPC_PARAM_DEFAULT), stateObj, enableOptimizedParameterBinding, isAdvancedTraceOn);
continue;
}

Expand All @@ -9031,7 +9041,7 @@ internal Task TdsExecuteRPC(SqlCommand cmd, _SqlRPC[] rpcArray, int timeout, boo
throw ADP.VersionDoesNotSupportDataType(mt.TypeName);
}

Task writeParamTask = TDSExecuteRPCAddParameter(stateObj, param, mt, options, cmd);
Task writeParamTask = TDSExecuteRPCAddParameter(stateObj, param, mt, options, cmd, enableOptimizedParameterBinding);

if (!sync)
{
Expand Down Expand Up @@ -9148,7 +9158,7 @@ internal Task TdsExecuteRPC(SqlCommand cmd, _SqlRPC[] rpcArray, int timeout, boo
}
}

private Task TDSExecuteRPCAddParameter(TdsParserStateObject stateObj, SqlParameter param, MetaType mt, byte options, SqlCommand command)
private Task TDSExecuteRPCAddParameter(TdsParserStateObject stateObj, SqlParameter param, MetaType mt, byte options, SqlCommand command, bool isAnonymous)
{
int tempLen;
object value = null;
Expand All @@ -9173,7 +9183,7 @@ private Task TDSExecuteRPCAddParameter(TdsParserStateObject stateObj, SqlParamet
}
}

WriteParameterName(param.ParameterNameFixed, stateObj);
WriteParameterName(param.ParameterNameFixed, stateObj, isAnonymous);

// Write parameter status
stateObj.WriteByte(options);
Expand Down Expand Up @@ -9695,11 +9705,11 @@ private void ExecuteFlushTaskCallback(Task tsk, TdsParserStateObject stateObj, T
}


private void WriteParameterName(string parameterName, TdsParserStateObject stateObj)
private void WriteParameterName(string parameterName, TdsParserStateObject stateObj, bool isAnonymous)
{
// paramLen
// paramName
if (!string.IsNullOrEmpty(parameterName))
if (!isAnonymous && !string.IsNullOrEmpty(parameterName))
{
Debug.Assert(parameterName.Length <= 0xff, "parameter name can only be 255 bytes, shouldn't get to TdsParser!");
int tempLen = parameterName.Length & 0xff;
Expand All @@ -9712,7 +9722,7 @@ private void WriteParameterName(string parameterName, TdsParserStateObject state
}
}

private void WriteSmiParameter(SqlParameter param, int paramIndex, bool sendDefault, TdsParserStateObject stateObj, bool advancedTraceIsOn)
private void WriteSmiParameter(SqlParameter param, int paramIndex, bool sendDefault, TdsParserStateObject stateObj, bool isAnonymous, bool advancedTraceIsOn)
{
//
// Determine Metadata
Expand Down Expand Up @@ -9771,7 +9781,7 @@ private void WriteSmiParameter(SqlParameter param, int paramIndex, bool sendDefa
//
// Write parameter metadata
//
WriteSmiParameterMetaData(metaData, sendDefault, stateObj);
WriteSmiParameterMetaData(metaData, sendDefault, isAnonymous, stateObj);

//
// Now write the value
Expand All @@ -9790,7 +9800,7 @@ private void WriteSmiParameter(SqlParameter param, int paramIndex, bool sendDefa
}

// Writes metadata portion of parameter stream from an SmiParameterMetaData object.
private void WriteSmiParameterMetaData(SmiParameterMetaData metaData, bool sendDefault, TdsParserStateObject stateObj)
private void WriteSmiParameterMetaData(SmiParameterMetaData metaData, bool sendDefault, bool isAnonymous, TdsParserStateObject stateObj)
{
// Determine status
byte status = 0;
Expand All @@ -9805,7 +9815,7 @@ private void WriteSmiParameterMetaData(SmiParameterMetaData metaData, bool sendD
}

// Write everything out
WriteParameterName(metaData.Name, stateObj);
WriteParameterName(metaData.Name, stateObj, isAnonymous);
stateObj.WriteByte(status);
WriteSmiTypeInfo(metaData, stateObj);
}
Expand Down
Loading

0 comments on commit a351b5d

Please sign in to comment.