Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.
/ corefx Public archive

Commit

Permalink
Merge pull request #34049 from Wraith2/sqlperf-rpc
Browse files Browse the repository at this point in the history
Optimize SqlClient rpc parameter usage
  • Loading branch information
tarikulsabbir authored Apr 4, 2019
2 parents 2b70a8f + 5b0018a commit 5458fe2
Show file tree
Hide file tree
Showing 3 changed files with 455 additions and 424 deletions.
132 changes: 70 additions & 62 deletions src/System.Data.SqlClient/src/System/Data/SqlClient/SqlCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3258,10 +3258,9 @@ private SqlParameter GetParameterForOutputValueExtraction(SqlParameterCollection
return null;
}

private void GetRPCObject(int paramCount, ref _SqlRPC rpc)
private void GetRPCObject(int systemParamCount, int userParamCount, ref _SqlRPC rpc)
{
// Designed to minimize necessary allocations
int ii;
if (rpc == null)
{
if (_rpcArrayOf1 == null)
Expand All @@ -3272,42 +3271,39 @@ private void GetRPCObject(int paramCount, ref _SqlRPC rpc)
rpc = _rpcArrayOf1[0];
}

rpc.ProcID = 0;
rpc.rpcName = null;
rpc.options = 0;
rpc.systemParamCount = systemParamCount;


int currentCount = rpc.systemParams?.Length ?? 0;
// Make sure there is enough space in the parameters and paramoptions arrays
if (rpc.parameters == null || rpc.parameters.Length < paramCount)
{
rpc.parameters = new SqlParameter[paramCount];
}
else if (rpc.parameters.Length > paramCount)
if (currentCount < systemParamCount)
{
rpc.parameters[paramCount] = null; // Terminator
Array.Resize(ref rpc.systemParams, systemParamCount);
Array.Resize(ref rpc.systemParamOptions, systemParamCount);
for (int index = currentCount; index < systemParamCount; index++)
{
rpc.systemParams[index] = new SqlParameter();
}
}
if (rpc.paramoptions == null || (rpc.paramoptions.Length < paramCount))
for (int ii = 0; ii < systemParamCount; ii++)
{
rpc.paramoptions = new byte[paramCount];
rpc.systemParamOptions[ii] = 0;
}
else
if ((rpc.userParamMap?.Length ?? 0) < userParamCount)
{
for (ii = 0; ii < paramCount; ii++)
rpc.paramoptions[ii] = 0;
Array.Resize(ref rpc.userParamMap, userParamCount);
}
}

private void SetUpRPCParameters(_SqlRPC rpc, int startCount, bool inSchema, SqlParameterCollection parameters)
private void SetUpRPCParameters(_SqlRPC rpc, bool inSchema, SqlParameterCollection parameters)
{
int ii;
int paramCount = GetParameterCount(parameters);
int j = startCount;
TdsParser parser = _activeConnection.Parser;
int userParamCount = 0;

for (ii = 0; ii < paramCount; ii++)
for (int index = 0; index < paramCount; index++)
{
SqlParameter parameter = parameters[ii];
parameter.Validate(ii, CommandType.StoredProcedure == CommandType);
SqlParameter parameter = parameters[index];
parameter.Validate(index, CommandType.StoredProcedure == CommandType);

// func will change type to that with a 4 byte length if the type has a two
// byte length and a parameter length > than that expressible in 2 bytes
Expand All @@ -3318,12 +3314,12 @@ private void SetUpRPCParameters(_SqlRPC rpc, int startCount, bool inSchema, SqlP

if (ShouldSendParameter(parameter))
{
rpc.parameters[j] = parameter;
byte options = 0;

// set output bit
if (parameter.Direction == ParameterDirection.InputOutput ||
parameter.Direction == ParameterDirection.Output)
rpc.paramoptions[j] = TdsEnums.RPC_PARAM_BYREF;
options = TdsEnums.RPC_PARAM_BYREF;

// set default value bit
if (parameter.Direction != ParameterDirection.Output)
Expand Down Expand Up @@ -3352,50 +3348,58 @@ private void SetUpRPCParameters(_SqlRPC rpc, int startCount, bool inSchema, SqlP
// TVPs use DEFAULT and do not allow NULL, even for schema only.
if (null == parameter.Value && (!inSchema || SqlDbType.Structured == parameter.SqlDbType))
{
rpc.paramoptions[j] |= TdsEnums.RPC_PARAM_DEFAULT;
options |= TdsEnums.RPC_PARAM_DEFAULT;
}
}

rpc.userParamMap[userParamCount] = ((((long)options) << 32) | (long)index);
userParamCount += 1;
// Must set parameter option bit for LOB_COOKIE if unfilled LazyMat blob
j++;

}
}
rpc.userParamCount = userParamCount;
rpc.userParams = parameters;
}

private _SqlRPC BuildPrepExec(CommandBehavior behavior)
{
Debug.Assert(System.Data.CommandType.Text == this.CommandType, "invalid use of sp_prepexec for stored proc invocation!");
SqlParameter sqlParam;
int j = 3;
const int systemParameterCount = 3;

int count = CountSendableParameters(_parameters);
int userParameterCount = CountSendableParameters(_parameters);

_SqlRPC rpc = null;
GetRPCObject(count + j, ref rpc);
GetRPCObject(systemParameterCount, userParameterCount, ref rpc);

rpc.ProcID = TdsEnums.RPC_PROCID_PREPEXEC;
rpc.rpcName = TdsEnums.SP_PREPEXEC;

//@handle
sqlParam = new SqlParameter(null, SqlDbType.Int);
sqlParam.Direction = ParameterDirection.InputOutput;
sqlParam = rpc.systemParams[0];
sqlParam.SqlDbType = SqlDbType.Int;
sqlParam.Value = _prepareHandle;
rpc.parameters[0] = sqlParam;
rpc.paramoptions[0] = TdsEnums.RPC_PARAM_BYREF;
sqlParam.Size = 4;
sqlParam.Direction = ParameterDirection.InputOutput;
rpc.systemParamOptions[0] = TdsEnums.RPC_PARAM_BYREF;

//@batch_params
string paramList = BuildParamList(_stateObj.Parser, _parameters);
sqlParam = new SqlParameter(null, ((paramList.Length << 1) <= TdsEnums.TYPE_SIZE_LIMIT) ? SqlDbType.NVarChar : SqlDbType.NText, paramList.Length);
sqlParam = rpc.systemParams[1];
sqlParam.SqlDbType = ((paramList.Length << 1) <= TdsEnums.TYPE_SIZE_LIMIT) ? SqlDbType.NVarChar : SqlDbType.NText;
sqlParam.Size = paramList.Length;
sqlParam.Value = paramList;
rpc.parameters[1] = sqlParam;

//@batch_text
string text = GetCommandText(behavior);
sqlParam = new SqlParameter(null, ((text.Length << 1) <= TdsEnums.TYPE_SIZE_LIMIT) ? SqlDbType.NVarChar : SqlDbType.NText, text.Length);
sqlParam = rpc.systemParams[2];
sqlParam.SqlDbType = ((text.Length << 1) <= TdsEnums.TYPE_SIZE_LIMIT) ? SqlDbType.NVarChar : SqlDbType.NText;
sqlParam.Size = text.Length;
sqlParam.Value = text;
rpc.parameters[2] = sqlParam;

SetUpRPCParameters(rpc, j, false, _parameters);
SetUpRPCParameters(rpc, false, _parameters);

return rpc;
}

Expand Down Expand Up @@ -3450,12 +3454,13 @@ private int GetParameterCount(SqlParameterCollection parameters)
private void BuildRPC(bool inSchema, SqlParameterCollection parameters, ref _SqlRPC rpc)
{
Debug.Assert(this.CommandType == System.Data.CommandType.StoredProcedure, "Command must be a stored proc to execute an RPC");
int count = CountSendableParameters(parameters);
GetRPCObject(count, ref rpc);
int userParameterCount = CountSendableParameters(parameters);
GetRPCObject(0, userParameterCount, ref rpc);

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

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

//
Expand All @@ -3472,24 +3477,23 @@ private void BuildRPC(bool inSchema, SqlParameterCollection parameters, ref _Sql
private _SqlRPC BuildExecute(bool inSchema)
{
Debug.Assert((int)_prepareHandle != -1, "Invalid call to sp_execute without a valid handle!");
int j = 1;
const int systemParameterCount = 1;

int count = CountSendableParameters(_parameters);
int userParameterCount = CountSendableParameters(_parameters);

_SqlRPC rpc = null;
GetRPCObject(count + j, ref rpc);

SqlParameter sqlParam;
GetRPCObject(systemParameterCount, userParameterCount, ref rpc);

rpc.ProcID = TdsEnums.RPC_PROCID_EXECUTE;
rpc.rpcName = TdsEnums.SP_EXECUTE;

//@handle
sqlParam = new SqlParameter(null, SqlDbType.Int);
SqlParameter sqlParam = rpc.systemParams[0];
sqlParam.SqlDbType = SqlDbType.Int;
sqlParam.Value = _prepareHandle;
rpc.parameters[0] = sqlParam;
sqlParam.Direction = ParameterDirection.Input;

SetUpRPCParameters(rpc, j, inSchema, _parameters);
SetUpRPCParameters(rpc, inSchema, _parameters);
return rpc;
}

Expand All @@ -3503,20 +3507,21 @@ private void BuildExecuteSql(CommandBehavior behavior, string commandText, SqlPa

Debug.Assert((int)_prepareHandle == -1, "This command has an existing handle, use sp_execute!");
Debug.Assert(CommandType.Text == this.CommandType, "invalid use of sp_executesql for stored proc invocation!");
int j;
int systemParamCount;
SqlParameter sqlParam;

int cParams = CountSendableParameters(parameters);
if (cParams > 0)
int userParamCount = CountSendableParameters(parameters);
if (userParamCount > 0)
{
j = 2;
systemParamCount = 2;
}
else
{
j = 1;
systemParamCount = 1;
}

GetRPCObject(cParams + j, ref rpc);
GetRPCObject(systemParamCount, userParamCount, ref rpc);

rpc.ProcID = TdsEnums.RPC_PROCID_EXECUTESQL;
rpc.rpcName = TdsEnums.SP_EXECUTESQL;

Expand All @@ -3525,19 +3530,22 @@ private void BuildExecuteSql(CommandBehavior behavior, string commandText, SqlPa
{
commandText = GetCommandText(behavior);
}
sqlParam = new SqlParameter(null, ((commandText.Length << 1) <= TdsEnums.TYPE_SIZE_LIMIT) ? SqlDbType.NVarChar : SqlDbType.NText, commandText.Length);
sqlParam = rpc.systemParams[0];
sqlParam.SqlDbType = ((commandText.Length << 1) <= TdsEnums.TYPE_SIZE_LIMIT) ? SqlDbType.NVarChar : SqlDbType.NText;
sqlParam.Size = commandText.Length;
sqlParam.Value = commandText;
rpc.parameters[0] = sqlParam;
sqlParam.Direction = ParameterDirection.Input;

if (cParams > 0)
if (userParamCount > 0)
{
string paramList = BuildParamList(_stateObj.Parser, BatchRPCMode ? parameters : _parameters);
sqlParam = new SqlParameter(null, ((paramList.Length << 1) <= TdsEnums.TYPE_SIZE_LIMIT) ? SqlDbType.NVarChar : SqlDbType.NText, paramList.Length);
sqlParam = rpc.systemParams[1];
sqlParam.SqlDbType = ((paramList.Length << 1) <= TdsEnums.TYPE_SIZE_LIMIT) ? SqlDbType.NVarChar : SqlDbType.NText;
sqlParam.Size = paramList.Length;
sqlParam.Value = paramList;
rpc.parameters[1] = sqlParam;

bool inSchema = (0 != (behavior & CommandBehavior.SchemaOnly));
SetUpRPCParameters(rpc, j, inSchema, parameters);
SetUpRPCParameters(rpc, inSchema, parameters);
}
}

Expand Down
Loading

0 comments on commit 5458fe2

Please sign in to comment.