From eb3fcbf4906f1f0746d4abe5128fc30f02391c89 Mon Sep 17 00:00:00 2001 From: Wraith2 Date: Mon, 24 Oct 2022 02:09:24 +0100 Subject: [PATCH 1/6] port changes --- .../Microsoft/Data/SqlClient/SqlCommand.cs | 426 ++++++++---------- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 13 +- .../Data/SqlClient/TdsParserHelperClasses.cs | 32 +- 3 files changed, 224 insertions(+), 247 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs index 7294c745d1..4abf0cd3f2 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs @@ -4449,7 +4449,7 @@ private SqlDataReader TryFetchInputParameterEncryptionInfo(int timeout, { // In BatchRPCMode, the actual T-SQL query is in the first parameter and not present as the rpcName, as is the case with non-BatchRPCMode. // So input parameters start at parameters[1]. parameters[0] is the actual T-SQL Statement. rpcName is sp_executesql. - if (_SqlRPCBatchArray[i].parameters.Length > 1) + if (_SqlRPCBatchArray[i].systemParams.Length > 1) { _SqlRPCBatchArray[i].needsFetchParameterEncryptionMetadata = true; @@ -4494,20 +4494,11 @@ private SqlDataReader TryFetchInputParameterEncryptionInfo(int timeout, _sqlRPCParameterEncryptionReqArray = new _SqlRPC[1]; _SqlRPC rpc = null; - GetRPCObject(GetParameterCount(_parameters), ref rpc); + GetRPCObject(0, GetParameterCount(_parameters), ref rpc); Debug.Assert(rpc != null, "GetRPCObject should not return rpc as null."); rpc.rpcName = CommandText; - - int i = 0; - - if (_parameters != null) - { - foreach (SqlParameter sqlParam in _parameters) - { - rpc.parameters[i++] = sqlParam; - } - } + rpc.userParams = _parameters; // Prepare the RPC request for describe parameter encryption procedure. PrepareDescribeParameterEncryptionRequest(rpc, ref _sqlRPCParameterEncryptionReqArray[0], serializedAttestationParameters); @@ -4573,20 +4564,23 @@ private void PrepareDescribeParameterEncryptionRequest(_SqlRPC originalRpcReques // Construct the RPC request for sp_describe_parameter_encryption // sp_describe_parameter_encryption always has 2 parameters (stmt, paramlist). //sp_describe_parameter_encryption can have an optional 3rd parameter (attestationParametes), used to identify and execute attestation protocol - GetRPCObject(attestationParameters == null ? 2 : 3, ref describeParameterEncryptionRequest, forSpDescribeParameterEncryption: true); + GetRPCObject(attestationParameters == null ? 2 : 3, 0, ref describeParameterEncryptionRequest, forSpDescribeParameterEncryption: true); describeParameterEncryptionRequest.rpcName = "sp_describe_parameter_encryption"; // Prepare @tsql parameter - SqlParameter sqlParam; string text; // In BatchRPCMode, The actual T-SQL query is in the first parameter and not present as the rpcName, as is the case with non-BatchRPCMode. if (BatchRPCMode) { - Debug.Assert(originalRpcRequest.parameters != null && originalRpcRequest.parameters.Length > 0, + Debug.Assert(originalRpcRequest.systemParamCount > 0, "originalRpcRequest didn't have at-least 1 parameter in BatchRPCMode, in PrepareDescribeParameterEncryptionRequest."); - text = (string)originalRpcRequest.parameters[0].Value; - sqlParam = GetSqlParameterWithQueryText(text); + text = (string)originalRpcRequest.systemParams[0].Value; + //@tsql + SqlParameter tsqlParam = describeParameterEncryptionRequest.systemParams[0]; + tsqlParam.SqlDbType = ((text.Length << 1) <= TdsEnums.TYPE_SIZE_LIMIT) ? SqlDbType.NVarChar : SqlDbType.NText; + tsqlParam.Value = text; + tsqlParam.Size = text.Length; } else { @@ -4595,42 +4589,57 @@ private void PrepareDescribeParameterEncryptionRequest(_SqlRPC originalRpcReques { // For stored procedures, we need to prepare @tsql in the following format // N'EXEC sp_name @param1=@param1, @param1=@param2, ..., @paramN=@paramN' - sqlParam = BuildStoredProcedureStatementForColumnEncryption(text, originalRpcRequest.parameters); + describeParameterEncryptionRequest.systemParams[0] = BuildStoredProcedureStatementForColumnEncryption(text, originalRpcRequest.userParams); } else { - sqlParam = GetSqlParameterWithQueryText(text); + //@tsql + SqlParameter tsqlParam = describeParameterEncryptionRequest.systemParams[0]; + tsqlParam.SqlDbType = ((text.Length << 1) <= TdsEnums.TYPE_SIZE_LIMIT) ? SqlDbType.NVarChar : SqlDbType.NText; + tsqlParam.Value = text; + tsqlParam.Size = text.Length; } } Debug.Assert(text != null, "@tsql parameter is null in PrepareDescribeParameterEncryptionRequest."); - describeParameterEncryptionRequest.parameters[0] = sqlParam; string parameterList = null; // In BatchRPCMode, the input parameters start at parameters[1]. parameters[0] is the T-SQL statement. rpcName is sp_executesql. // And it is already in the format expected out of BuildParamList, which is not the case with Non-BatchRPCMode. if (BatchRPCMode) { - if (originalRpcRequest.parameters.Length > 1) + if (originalRpcRequest.systemParamCount > 1) { - parameterList = (string)originalRpcRequest.parameters[1].Value; + parameterList = (string)originalRpcRequest.systemParams[1].Value; } } else { // Prepare @params parameter // Need to create new parameters as we cannot have the same parameter being part of two SqlCommand objects - SqlParameter paramCopy; SqlParameterCollection tempCollection = new SqlParameterCollection(); - if (_parameters != null) - { - for (int i = 0; i < _parameters.Count; i++) - { - SqlParameter param = originalRpcRequest.parameters[i]; - paramCopy = new SqlParameter(param.ParameterName, param.SqlDbType, param.Size, param.Direction, param.Precision, param.Scale, param.SourceColumn, param.SourceVersion, - param.SourceColumnNullMapping, param.Value, param.XmlSchemaCollectionDatabase, param.XmlSchemaCollectionOwningSchema, param.XmlSchemaCollectionName); + if (originalRpcRequest.userParams != null) + { + for (int i = 0; i < originalRpcRequest.userParams.Count; i++) + { + SqlParameter param = originalRpcRequest.userParams[i]; + SqlParameter paramCopy = new SqlParameter( + param.ParameterName, + param.SqlDbType, + param.Size, + param.Direction, + param.Precision, + param.Scale, + param.SourceColumn, + param.SourceVersion, + param.SourceColumnNullMapping, + param.Value, + param.XmlSchemaCollectionDatabase, + param.XmlSchemaCollectionOwningSchema, + param.XmlSchemaCollectionName + ); paramCopy.CompareInfo = param.CompareInfo; paramCopy.TypeName = param.TypeName; paramCopy.UdtTypeName = param.UdtTypeName; @@ -4659,20 +4668,17 @@ private void PrepareDescribeParameterEncryptionRequest(_SqlRPC originalRpcReques parameterList = BuildParamList(tdsParser, tempCollection, includeReturnValue: true); } - sqlParam = new SqlParameter(null, ((parameterList.Length << 1) <= TdsEnums.TYPE_SIZE_LIMIT) ? SqlDbType.NVarChar : SqlDbType.NText, parameterList.Length); - sqlParam.Value = parameterList; - describeParameterEncryptionRequest.parameters[1] = sqlParam; + SqlParameter paramsParam = new SqlParameter(null, ((parameterList.Length << 1) <= TdsEnums.TYPE_SIZE_LIMIT) ? SqlDbType.NVarChar : SqlDbType.NText, parameterList.Length); + paramsParam.Value = parameterList; + paramsParam.Size = parameterList.Length; + paramsParam.Value = parameterList; if (attestationParameters != null) { - var attestationParametersParam = new SqlParameter(null, SqlDbType.VarBinary) - { - Direction = ParameterDirection.Input, - Size = attestationParameters.Length, - Value = attestationParameters - }; - - describeParameterEncryptionRequest.parameters[2] = attestationParametersParam; + SqlParameter attestationParametersParam = describeParameterEncryptionRequest.systemParams[2]; + attestationParametersParam.Direction = ParameterDirection.Input; + attestationParametersParam.Size = attestationParameters.Length; + attestationParametersParam.Value = attestationParameters; } } @@ -4824,9 +4830,6 @@ private void ReadDescribeEncryptionParameterResults(SqlDataReader ds, ReadOnlyDi throw SQL.UnexpectedDescribeParamFormatParameterMetadata(); } - int paramIdx = 0; - int parameterStartIndex = 0; - // Find the RPC command that generated this tce request if (BatchRPCMode) { @@ -4849,10 +4852,8 @@ private void ReadDescribeEncryptionParameterResults(SqlDataReader ds, ReadOnlyDi Debug.Assert(rpc != null, "rpc should not be null here."); - // This is the index in the parameters array where the actual parameters start. - // In BatchRPCMode, parameters[0] has the t-sql, parameters[1] has the param list - // and actual parameters of the query start at parameters[2]. - parameterStartIndex = (BatchRPCMode ? 2 : 0); + int userParamCount = rpc.userParams?.Count ?? 0; + int recievedMetadataCount = 0; if (!enclaveMetadataExists || ds.NextResult()) { @@ -4867,16 +4868,16 @@ private void ReadDescribeEncryptionParameterResults(SqlDataReader ds, ReadOnlyDi // When the RPC object gets reused, the parameter array has more parameters that the valid params for the command. // Null is used to indicate the end of the valid part of the array. Refer to GetRPCObject(). - for (paramIdx = parameterStartIndex; paramIdx < rpc.parameters.Length && rpc.parameters[paramIdx] != null; paramIdx++) + for (int index = 0; index < userParamCount; index++) { - SqlParameter sqlParameter = rpc.parameters[paramIdx]; + SqlParameter sqlParameter = rpc.userParams[index]; Debug.Assert(sqlParameter != null, "sqlParameter should not be null."); if (sqlParameter.ParameterNameFixed.Equals(parameterName, StringComparison.Ordinal)) { Debug.Assert(sqlParameter.CipherMetadata == null, "param.CipherMetadata should be null."); sqlParameter.HasReceivedMetadata = true; - + recievedMetadataCount += 1; // Found the param, setup the encryption info. byte columnEncryptionType = ds.GetByte((int)DescribeParameterEncryptionResultSet2.ColumnEncryptionType); if ((byte)SqlClientEncryptionType.PlainText != columnEncryptionType) @@ -4904,7 +4905,9 @@ private void ReadDescribeEncryptionParameterResults(SqlDataReader ds, ReadOnlyDi // This is effective only for BatchRPCMode even though we set it for non-BatchRPCMode also, // since for non-BatchRPCMode mode, paramoptions gets thrown away and reconstructed in BuildExecuteSql. - rpc.paramoptions[paramIdx] |= TdsEnums.RPC_PARAM_ENCRYPTED; + int options = (int)(rpc.userParamMap[index] >> 32); + options |= TdsEnums.RPC_PARAM_ENCRYPTED; + rpc.userParamMap[index] = ((((long)options) << 32) | (long)index); } break; @@ -4915,15 +4918,19 @@ private void ReadDescribeEncryptionParameterResults(SqlDataReader ds, ReadOnlyDi // When the RPC object gets reused, the parameter array has more parameters that the valid params for the command. // Null is used to indicate the end of the valid part of the array. Refer to GetRPCObject(). - for (paramIdx = parameterStartIndex; paramIdx < rpc.parameters.Length && rpc.parameters[paramIdx] != null; paramIdx++) + if (recievedMetadataCount != userParamCount) { - if (!rpc.parameters[paramIdx].HasReceivedMetadata && rpc.parameters[paramIdx].Direction != ParameterDirection.ReturnValue) + for (int index = 0; index < userParamCount; index++) { - // Encryption MD wasn't sent by the server - we expect the metadata to be sent for all the parameters - // that were sent in the original sp_describe_parameter_encryption but not necessarily for return values, - // since there might be multiple return values but server will only send for one of them. - // For parameters that don't need encryption, the encryption type is set to plaintext. - throw SQL.ParamEncryptionMetadataMissing(rpc.parameters[paramIdx].ParameterName, rpc.GetCommandTextOrRpcName()); + SqlParameter sqlParameter = rpc.userParams[index]; + if (!sqlParameter.HasReceivedMetadata && sqlParameter.Direction != ParameterDirection.ReturnValue) + { + // Encryption MD wasn't sent by the server - we expect the metadata to be sent for all the parameters + // that were sent in the original sp_describe_parameter_encryption but not necessarily for return values, + // since there might be multiple return values but server will only send for one of them. + // For parameters that don't need encryption, the encryption type is set to plaintext. + throw SQL.ParamEncryptionMetadataMissing(sqlParameter.ParameterName, rpc.GetCommandTextOrRpcName()); + } } } @@ -6093,40 +6100,41 @@ internal void OnDoneDescribeParameterEncryptionProc(TdsParserStateObject stateOb /// Please consider the changes being done in this function for the above function as well. /// internal void OnDoneProc() - { // called per rpc batch complete + { + // called per rpc batch complete if (BatchRPCMode) { + OnDone(_stateObj, _currentlyExecutingBatch, _SqlRPCBatchArray, _rowsAffected); + _currentlyExecutingBatch++; + Debug.Assert(_parameterCollectionList.Count >= _currentlyExecutingBatch, "OnDoneProc: Too many DONEPROC events"); + } + } - // track the records affected for the just completed rpc batch - // _rowsAffected is cumulative for ExecuteNonQuery across all rpc batches - _SqlRPCBatchArray[_currentlyExecutingBatch].cumulativeRecordsAffected = _rowsAffected; + private static void OnDone(TdsParserStateObject stateObj, int index, _SqlRPC[] array, int rowsAffected) + { + _SqlRPC current = array[index]; + _SqlRPC previous = (index > 0) ? array[index - 1] : null; - _SqlRPCBatchArray[_currentlyExecutingBatch].recordsAffected = - (((0 < _currentlyExecutingBatch) && (0 <= _rowsAffected)) - ? (_rowsAffected - Math.Max(_SqlRPCBatchArray[_currentlyExecutingBatch - 1].cumulativeRecordsAffected, 0)) - : _rowsAffected); + // track the records affected for the just completed rpc batch + // _rowsAffected is cumulative for ExecuteNonQuery across all rpc batches + current.cumulativeRecordsAffected = rowsAffected; - // track the error collection (not available from TdsParser after ExecuteNonQuery) - // and the which errors are associated with the just completed rpc batch - _SqlRPCBatchArray[_currentlyExecutingBatch].errorsIndexStart = - ((0 < _currentlyExecutingBatch) - ? _SqlRPCBatchArray[_currentlyExecutingBatch - 1].errorsIndexEnd - : 0); - _SqlRPCBatchArray[_currentlyExecutingBatch].errorsIndexEnd = _stateObj.ErrorCount; - _SqlRPCBatchArray[_currentlyExecutingBatch].errors = _stateObj._errors; + current.recordsAffected = + (((previous != null) && (0 <= rowsAffected)) + ? (rowsAffected - Math.Max(previous.cumulativeRecordsAffected, 0)) + : rowsAffected); - // track the warning collection (not available from TdsParser after ExecuteNonQuery) - // and the which warnings are associated with the just completed rpc batch - _SqlRPCBatchArray[_currentlyExecutingBatch].warningsIndexStart = - ((0 < _currentlyExecutingBatch) - ? _SqlRPCBatchArray[_currentlyExecutingBatch - 1].warningsIndexEnd - : 0); - _SqlRPCBatchArray[_currentlyExecutingBatch].warningsIndexEnd = _stateObj.WarningCount; - _SqlRPCBatchArray[_currentlyExecutingBatch].warnings = _stateObj._warnings; + // track the error collection (not available from TdsParser after ExecuteNonQuery) + // and the which errors are associated with the just completed rpc batch + current.errorsIndexStart = previous?.errorsIndexEnd ?? 0; + current.errorsIndexEnd = stateObj.ErrorCount; + current.errors = stateObj._errors; - _currentlyExecutingBatch++; - Debug.Assert(_parameterCollectionList.Count >= _currentlyExecutingBatch, "OnDoneProc: Too many DONEPROC events"); - } + // track the warning collection (not available from TdsParser after ExecuteNonQuery) + // and the which warnings are associated with the just completed rpc batch + current.warningsIndexStart = previous?.warningsIndexEnd ?? 0; + current.warningsIndexEnd = stateObj.WarningCount; + current.warnings = stateObj._warnings; } // @@ -6459,10 +6467,10 @@ private SqlParameter GetParameterForOutputValueExtraction(SqlParameterCollection return null; } - private void GetRPCObject(int paramCount, ref _SqlRPC rpc, bool forSpDescribeParameterEncryption = false) + private void GetRPCObject(int systemParamCount, int userParamCount, ref _SqlRPC rpc, bool forSpDescribeParameterEncryption = false) { // Designed to minimize necessary allocations - int ii; + if (rpc == null) { if (!forSpDescribeParameterEncryption) @@ -6489,6 +6497,7 @@ private void GetRPCObject(int paramCount, ref _SqlRPC rpc, bool forSpDescribePar rpc.ProcID = 0; rpc.rpcName = null; rpc.options = 0; + rpc.systemParamCount = systemParamCount; rpc.recordsAffected = default(int?); rpc.cumulativeRecordsAffected = -1; @@ -6502,37 +6511,39 @@ private void GetRPCObject(int paramCount, ref _SqlRPC rpc, bool forSpDescribePar rpc.warnings = null; rpc.needsFetchParameterEncryptionMetadata = false; + 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 expressable in 2 bytes @@ -6543,17 +6554,18 @@ 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; + if (parameter.Direction == ParameterDirection.InputOutput || parameter.Direction == ParameterDirection.Output) + { + options = TdsEnums.RPC_PARAM_BYREF; + } // Set the encryped bit, if the parameter is to be encrypted. if (parameter.CipherMetadata != null) { - rpc.paramoptions[j] |= TdsEnums.RPC_PARAM_ENCRYPTED; + options |= TdsEnums.RPC_PARAM_ENCRYPTED; } // set default value bit @@ -6566,9 +6578,12 @@ private void SetUpRPCParameters(_SqlRPC rpc, int startCount, bool inSchema, SqlP // SQLBUVSTS 179488 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; + // detect incorrectly derived type names unchanged by the caller and fix them if (parameter.IsDerivedParameterTypeName) { @@ -6588,7 +6603,6 @@ private void SetUpRPCParameters(_SqlRPC rpc, int startCount, bool inSchema, SqlP } // Must set parameter option bit for LOB_COOKIE if unfilled LazyMat blob - j++; } } @@ -6603,36 +6617,38 @@ 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; - int count = CountSendableParameters(_parameters); + const int systemParameterCount = 3; + 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.Value = _prepareHandle; - rpc.parameters[0] = sqlParam; - rpc.paramoptions[0] = TdsEnums.RPC_PARAM_BYREF; + sqlParam.Value = 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.Value = paramList; - rpc.parameters[1] = sqlParam; + sqlParam.Size = paramList.Length; //@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; } @@ -6689,14 +6705,14 @@ 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); // 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; - + rpc.ProcID = 0; if (commandTextLength <= MaxRPCNameLength) { rpc.rpcName = CommandText; // just get the raw command text @@ -6706,33 +6722,7 @@ private void BuildRPC(bool inSchema, SqlParameterCollection parameters, ref _Sql throw ADP.InvalidArgumentLength(nameof(CommandText), MaxRPCNameLength); } - SetUpRPCParameters(rpc, 0, inSchema, parameters); - } - - // - // build the RPC record header for sp_unprepare - // - // prototype for sp_unprepare is: - // sp_unprepare(@handle) - // - // CONSIDER: instead of creating each time, define at load time and then put the new value in - private _SqlRPC BuildUnprepare() - { - Debug.Assert(_prepareHandle != 0, "Invalid call to sp_unprepare without a valid handle!"); - - _SqlRPC rpc = null; - GetRPCObject(1, ref rpc); - SqlParameter sqlParam; - - rpc.ProcID = TdsEnums.RPC_PROCID_UNPREPARE; - rpc.rpcName = TdsEnums.SP_UNPREPARE; - - //@handle - sqlParam = new SqlParameter(null, SqlDbType.Int); - sqlParam.Value = _prepareHandle; - rpc.parameters[0] = sqlParam; - - return rpc; + SetUpRPCParameters(rpc, inSchema, parameters); } // @@ -6744,24 +6734,23 @@ private _SqlRPC BuildUnprepare() private _SqlRPC BuildExecute(bool inSchema) { Debug.Assert(_prepareHandle != -1, "Invalid call to sp_execute without a valid handle!"); - int j = 1; - int count = CountSendableParameters(_parameters); + const int systemParameterCount = 1; + 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; } @@ -6775,20 +6764,20 @@ private void BuildExecuteSql(CommandBehavior behavior, string commandText, SqlPa Debug.Assert(_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; @@ -6797,19 +6786,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); } } @@ -6821,7 +6813,7 @@ private void BuildExecuteSql(CommandBehavior behavior, string commandText, SqlPa /// Stored procedure name /// SqlParameter list /// A string SqlParameter containing the constructed sql statement value - private SqlParameter BuildStoredProcedureStatementForColumnEncryption(string storedProcedureName, SqlParameter[] parameters) + private SqlParameter BuildStoredProcedureStatementForColumnEncryption(string storedProcedureName, SqlParameterCollection parameters) { Debug.Assert(CommandType == CommandType.StoredProcedure, "BuildStoredProcedureStatementForColumnEncryption() should only be called for stored procedures"); Debug.Assert(!string.IsNullOrWhiteSpace(storedProcedureName), "storedProcedureName cannot be null or empty in BuildStoredProcedureStatementForColumnEncryption"); @@ -6865,27 +6857,29 @@ private SqlParameter BuildStoredProcedureStatementForColumnEncryption(string sto // @param1=@param1, @param1=@param2, ..., @paramn=@paramn // Append the first parameter - int i = 0; - - if (parameters.Count() > 0) + int index = 0; + int count = parameters.Count; + if (count > 0) { // Skip the return value parameters. - while (i < parameters.Count() && parameters[i].Direction == ParameterDirection.ReturnValue) + while (index < parameters.Count && parameters[index].Direction == ParameterDirection.ReturnValue) { - i++; + index++; } - if (i < parameters.Count()) + if (index < count) { // Possibility of a SQL Injection issue through parameter names and how to construct valid identifier for parameters. // Since the parameters comes from application itself, there should not be a security vulnerability. // Also since the query is not executed, but only analyzed there is no possibility for elevation of priviledge, but only for // incorrect results which would only affect the user that attempts the injection. - execStatement.AppendFormat(@" {0}={0}", parameters[i].ParameterNameFixed); + execStatement.AppendFormat(@" {0}={0}", parameters[index].ParameterNameFixed); // InputOutput and Output parameters need to be marked as such. - if (parameters[i].Direction == ParameterDirection.Output || - parameters[i].Direction == ParameterDirection.InputOutput) + if ( + parameters[index].Direction == ParameterDirection.Output || + parameters[index].Direction == ParameterDirection.InputOutput + ) { execStatement.AppendFormat(@" OUTPUT"); } @@ -6893,18 +6887,20 @@ private SqlParameter BuildStoredProcedureStatementForColumnEncryption(string sto } // Move to the next parameter. - i++; + index++; // Append the rest of parameters - for (; i < parameters.Count(); i++) + for (; index < count; index++) { - if (parameters[i].Direction != ParameterDirection.ReturnValue) + if (parameters[index].Direction != ParameterDirection.ReturnValue) { - execStatement.AppendFormat(@", {0}={0}", parameters[i].ParameterNameFixed); + execStatement.AppendFormat(@", {0}={0}", parameters[index].ParameterNameFixed); // InputOutput and Output parameters need to be marked as such. - if (parameters[i].Direction == ParameterDirection.Output || - parameters[i].Direction == ParameterDirection.InputOutput) + if ( + parameters[index].Direction == ParameterDirection.Output || + parameters[index].Direction == ParameterDirection.InputOutput + ) { execStatement.AppendFormat(@" OUTPUT"); } @@ -6924,9 +6920,7 @@ internal string BuildParamList(TdsParser parser, SqlParameterCollection paramete StringBuilder paramList = new StringBuilder(); bool fAddSeparator = false; - int count = 0; - - count = parameters.Count; + int count = parameters.Count; for (int i = 0; i < count; i++) { SqlParameter sqlParam = parameters[i]; @@ -7073,7 +7067,7 @@ internal string BuildParamList(TdsParser parser, SqlParameterCollection paramete // Adds quotes to each part of a SQL identifier that may be multi-part, while leaving // the result as a single composite name. - private string ParseAndQuoteIdentifier(string identifier, bool isUdtTypeName) + private static string ParseAndQuoteIdentifier(string identifier, bool isUdtTypeName) { string[] strings = SqlParameter.ParseTypeName(identifier, isUdtTypeName); return ADP.BuildMultiPartName(strings); @@ -7159,48 +7153,6 @@ private string GetCommandText(CommandBehavior behavior) return GetSetOptionsString(behavior) + this.CommandText; } - // - // build the RPC record header for sp_executesql and add the parameters - // - // the prototype for sp_prepare is: - // sp_prepare(@handle int OUTPUT, @batch_params ntext, @batch_text ntext, @options int default 0x1) - private _SqlRPC BuildPrepare(CommandBehavior behavior) - { - Debug.Assert(System.Data.CommandType.Text == this.CommandType, "invalid use of sp_prepare for stored proc invocation!"); - - _SqlRPC rpc = null; - GetRPCObject(3, ref rpc); - SqlParameter sqlParam; - - rpc.ProcID = TdsEnums.RPC_PROCID_PREPARE; - rpc.rpcName = TdsEnums.SP_PREPARE; - - //@handle - sqlParam = new SqlParameter(null, SqlDbType.Int); - sqlParam.Direction = ParameterDirection.Output; - rpc.parameters[0] = sqlParam; - rpc.paramoptions[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.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.Value = text; - rpc.parameters[2] = sqlParam; - - /* - //@options - sqlParam = new SqlParameter(null, SqlDbType.Int); - rpc.Parameters[3] = sqlParam; - */ - return rpc; - } - internal void CheckThrowSNIException() { var stateObj = _stateObj; diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs index 1edad799ae..c35ae13e63 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -9963,16 +9963,17 @@ internal Task TdsExecuteRPC(SqlCommand cmd, _SqlRPC[] rpcArray, int timeout, boo WriteEnclaveInfo(stateObj, enclavePackage); // Stream out parameters - SqlParameter[] parameters = rpcext.parameters; + int parametersLength = rpcext.userParamCount + rpcext.systemParamCount; bool isAdvancedTraceOn = SqlClientEventSource.Log.IsAdvancedTraceOn(); bool enableOptimizedParameterBinding = cmd.EnableOptimizedParameterBinding; - for (int i = (ii == startRpc) ? startParam : 0; i < parameters.Length; i++) + for (int i = (ii == startRpc) ? startParam : 0; i < parametersLength; i++) { // Debug.WriteLine("i: " + i.ToString(CultureInfo.InvariantCulture)); // parameters can be unnamed - SqlParameter param = parameters[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) { @@ -10010,7 +10011,7 @@ internal Task TdsExecuteRPC(SqlCommand cmd, _SqlRPC[] rpcArray, int timeout, boo if (mt.Is2008Type) { - WriteSmiParameter(param, i, 0 != (rpcext.paramoptions[i] & TdsEnums.RPC_PARAM_DEFAULT), stateObj, enableOptimizedParameterBinding, isAdvancedTraceOn); + WriteSmiParameter(param, i, 0 != (options & TdsEnums.RPC_PARAM_DEFAULT), stateObj, enableOptimizedParameterBinding, isAdvancedTraceOn); continue; } @@ -10045,7 +10046,7 @@ internal Task TdsExecuteRPC(SqlCommand cmd, _SqlRPC[] rpcArray, int timeout, boo WriteParameterName(param.ParameterNameFixed, stateObj, enableOptimizedParameterBinding); // Write parameter status - stateObj.WriteByte(rpcext.paramoptions[i]); + stateObj.WriteByte(options); // MaxLen field is only written out for non-fixed length data types // use the greater of the two sizes for maxLen @@ -10108,7 +10109,7 @@ internal Task TdsExecuteRPC(SqlCommand cmd, _SqlRPC[] rpcArray, int timeout, boo } } - bool isParameterEncrypted = 0 != (rpcext.paramoptions[i] & TdsEnums.RPC_PARAM_ENCRYPTED); + bool isParameterEncrypted = 0 != (options & TdsEnums.RPC_PARAM_ENCRYPTED); // Additional information we need to send over wire to the server when writing encrypted parameters. SqlColumnEncryptionInputParameterInfo encryptedParameterInfoToWrite = null; diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserHelperClasses.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserHelperClasses.cs index bf113efe3b..62f7f59b91 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserHelperClasses.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserHelperClasses.cs @@ -1158,11 +1158,16 @@ private void SerializeIntIntoBuffer(int value, byte[] buffer, ref int offset) sealed internal class _SqlRPC { internal string rpcName; - internal string databaseName; // Used for UDTs internal ushort ProcID; // Used instead of name internal ushort options; - internal SqlParameter[] parameters; - internal byte[] paramoptions; + + internal SqlParameter[] systemParams; + internal byte[] systemParamOptions; + internal int systemParamCount; + + internal SqlParameterCollection userParams; + internal long[] userParamMap; + internal int userParamCount; internal int? recordsAffected; internal int cumulativeRecordsAffected; @@ -1175,18 +1180,37 @@ sealed internal class _SqlRPC internal int warningsIndexEnd; internal SqlErrorCollection warnings; internal bool needsFetchParameterEncryptionMetadata; + internal string GetCommandTextOrRpcName() { if (TdsEnums.RPC_PROCID_EXECUTESQL == ProcID) { // Param 0 is the actual sql executing - return (string)parameters[0].Value; + return (string)systemParams[0].Value; } else { return rpcName; } } + + internal SqlParameter GetParameterByIndex(int index, out byte options) + { + SqlParameter retval; + if (index < systemParamCount) + { + retval = systemParams[index]; + options = systemParamOptions[index]; + } + else + { + long data = userParamMap[index - systemParamCount]; + int paramIndex = (int)(data & int.MaxValue); + options = (byte)((data >> 32) & 0xFF); + retval = userParams[paramIndex]; + } + return retval; + } } sealed internal class SqlReturnValue : SqlMetaDataPriv From 1d8296ce44365edc21cd30c5d1dcc3e858213fb8 Mon Sep 17 00:00:00 2001 From: Wraith2 Date: Mon, 24 Oct 2022 11:18:31 +0100 Subject: [PATCH 2/6] set the parameters on the rpc --- .../netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs index 4abf0cd3f2..8cb48691da 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs @@ -6606,6 +6606,8 @@ private void SetUpRPCParameters(_SqlRPC rpc, bool inSchema, SqlParameterCollecti } } + rpc.userParamCount = userParamCount; + rpc.userParams = parameters; } // From 557bbc560d6954879bbf719d674ebafdb2d5f4c4 Mon Sep 17 00:00:00 2001 From: Wraith2 Date: Mon, 24 Oct 2022 21:54:21 +0100 Subject: [PATCH 3/6] fix parameter conuting --- .../netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs index 8cb48691da..81e60e7e29 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs @@ -6581,9 +6581,6 @@ private void SetUpRPCParameters(_SqlRPC rpc, bool inSchema, SqlParameterCollecti options |= TdsEnums.RPC_PARAM_DEFAULT; } - rpc.userParamMap[userParamCount] = ((((long)options) << 32) | (long)index); - userParamCount += 1; - // detect incorrectly derived type names unchanged by the caller and fix them if (parameter.IsDerivedParameterTypeName) { @@ -6602,6 +6599,9 @@ private void SetUpRPCParameters(_SqlRPC rpc, bool inSchema, SqlParameterCollecti } } + rpc.userParamMap[userParamCount] = ((((long)options) << 32) | (long)index); + userParamCount += 1; + // Must set parameter option bit for LOB_COOKIE if unfilled LazyMat blob } } From f4002ca450f28eda14c1c399c6ff134aa7a780f1 Mon Sep 17 00:00:00 2001 From: Wraith2 Date: Tue, 1 Nov 2022 01:30:31 +0000 Subject: [PATCH 4/6] sync encryption param building code and make sure that the important properties are always set. --- .../netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs | 2 ++ .../netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs index bca9f13ea7..b1b6d2c193 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs @@ -4148,6 +4148,7 @@ private void PrepareDescribeParameterEncryptionRequest(_SqlRPC originalRpcReques //@parameters SqlParameter paramsParam = describeParameterEncryptionRequest.systemParams[1]; + paramsParam.Direction = ParameterDirection.Input; paramsParam.SqlDbType = ((parameterList.Length << 1) <= TdsEnums.TYPE_SIZE_LIMIT) ? SqlDbType.NVarChar : SqlDbType.NText; paramsParam.Size = parameterList.Length; paramsParam.Value = parameterList; @@ -4156,6 +4157,7 @@ private void PrepareDescribeParameterEncryptionRequest(_SqlRPC originalRpcReques { SqlParameter attestationParametersParam = describeParameterEncryptionRequest.systemParams[2]; attestationParametersParam.Direction = ParameterDirection.Input; + attestationParametersParam.SqlDbType = SqlDbType.VarBinary; attestationParametersParam.Size = attestationParameters.Length; attestationParametersParam.Value = attestationParameters; } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs index 81e60e7e29..2b9f14de8c 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs @@ -4668,8 +4668,9 @@ private void PrepareDescribeParameterEncryptionRequest(_SqlRPC originalRpcReques parameterList = BuildParamList(tdsParser, tempCollection, includeReturnValue: true); } - SqlParameter paramsParam = new SqlParameter(null, ((parameterList.Length << 1) <= TdsEnums.TYPE_SIZE_LIMIT) ? SqlDbType.NVarChar : SqlDbType.NText, parameterList.Length); - paramsParam.Value = parameterList; + SqlParameter paramsParam = describeParameterEncryptionRequest.systemParams[1]; + paramsParam.Direction = ParameterDirection.Input; + paramsParam.SqlDbType = ((parameterList.Length << 1) <= TdsEnums.TYPE_SIZE_LIMIT) ? SqlDbType.NVarChar : SqlDbType.NText; paramsParam.Size = parameterList.Length; paramsParam.Value = parameterList; @@ -4677,6 +4678,7 @@ private void PrepareDescribeParameterEncryptionRequest(_SqlRPC originalRpcReques { SqlParameter attestationParametersParam = describeParameterEncryptionRequest.systemParams[2]; attestationParametersParam.Direction = ParameterDirection.Input; + attestationParametersParam.SqlDbType = SqlDbType.VarBinary; attestationParametersParam.Size = attestationParameters.Length; attestationParametersParam.Value = attestationParameters; } From 615104d4006fd1cfe0df9c672c31d93d3e16d85e Mon Sep 17 00:00:00 2001 From: Wraith2 Date: Tue, 1 Nov 2022 02:22:22 +0000 Subject: [PATCH 5/6] audit reuse of sysparams and make sure all locations have direction, size value and type set. --- .../src/Microsoft/Data/SqlClient/SqlCommand.cs | 10 ++++++++-- .../netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs | 12 +++++++++--- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs index b1b6d2c193..b576ef3510 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs @@ -4058,6 +4058,7 @@ private void PrepareDescribeParameterEncryptionRequest(_SqlRPC originalRpcReques tsqlParam.SqlDbType = ((text.Length << 1) <= TdsEnums.TYPE_SIZE_LIMIT) ? SqlDbType.NVarChar : SqlDbType.NText; tsqlParam.Value = text; tsqlParam.Size = text.Length; + tsqlParam.Direction = ParameterDirection.Input; } else { @@ -4075,6 +4076,7 @@ private void PrepareDescribeParameterEncryptionRequest(_SqlRPC originalRpcReques tsqlParam.SqlDbType = ((text.Length << 1) <= TdsEnums.TYPE_SIZE_LIMIT) ? SqlDbType.NVarChar : SqlDbType.NText; tsqlParam.Value = text; tsqlParam.Size = text.Length; + tsqlParam.Direction = ParameterDirection.Input; } } @@ -4148,18 +4150,18 @@ private void PrepareDescribeParameterEncryptionRequest(_SqlRPC originalRpcReques //@parameters SqlParameter paramsParam = describeParameterEncryptionRequest.systemParams[1]; - paramsParam.Direction = ParameterDirection.Input; paramsParam.SqlDbType = ((parameterList.Length << 1) <= TdsEnums.TYPE_SIZE_LIMIT) ? SqlDbType.NVarChar : SqlDbType.NText; paramsParam.Size = parameterList.Length; paramsParam.Value = parameterList; + paramsParam.Direction = ParameterDirection.Input; if (attestationParameters != null) { SqlParameter attestationParametersParam = describeParameterEncryptionRequest.systemParams[2]; - attestationParametersParam.Direction = ParameterDirection.Input; attestationParametersParam.SqlDbType = SqlDbType.VarBinary; attestationParametersParam.Size = attestationParameters.Length; attestationParametersParam.Value = attestationParameters; + attestationParametersParam.Direction = ParameterDirection.Input; } } @@ -5793,6 +5795,7 @@ private _SqlRPC BuildPrepExec(CommandBehavior behavior) sqlParam.SqlDbType = ((paramList.Length << 1) <= TdsEnums.TYPE_SIZE_LIMIT) ? SqlDbType.NVarChar : SqlDbType.NText; sqlParam.Value = paramList; sqlParam.Size = paramList.Length; + sqlParam.Direction = ParameterDirection.Input; //@batch_text string text = GetCommandText(behavior); @@ -5800,6 +5803,7 @@ private _SqlRPC BuildPrepExec(CommandBehavior behavior) sqlParam.SqlDbType = ((text.Length << 1) <= TdsEnums.TYPE_SIZE_LIMIT) ? SqlDbType.NVarChar : SqlDbType.NText; sqlParam.Size = text.Length; sqlParam.Value = text; + sqlParam.Direction = ParameterDirection.Input; SetUpRPCParameters(rpc, false, _parameters); return rpc; @@ -5901,6 +5905,7 @@ private _SqlRPC BuildExecute(bool inSchema) //@handle SqlParameter sqlParam = rpc.systemParams[0]; sqlParam.SqlDbType = SqlDbType.Int; + sqlParam.Size = 4; sqlParam.Value = _prepareHandle; sqlParam.Direction = ParameterDirection.Input; @@ -5953,6 +5958,7 @@ private void BuildExecuteSql(CommandBehavior behavior, string commandText, SqlPa sqlParam.SqlDbType = ((paramList.Length << 1) <= TdsEnums.TYPE_SIZE_LIMIT) ? SqlDbType.NVarChar : SqlDbType.NText; sqlParam.Size = paramList.Length; sqlParam.Value = paramList; + sqlParam.Direction = ParameterDirection.Input; bool inSchema = (0 != (behavior & CommandBehavior.SchemaOnly)); SetUpRPCParameters(rpc, inSchema, parameters); diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs index 2b9f14de8c..1ff35bbde6 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs @@ -4598,6 +4598,7 @@ private void PrepareDescribeParameterEncryptionRequest(_SqlRPC originalRpcReques tsqlParam.SqlDbType = ((text.Length << 1) <= TdsEnums.TYPE_SIZE_LIMIT) ? SqlDbType.NVarChar : SqlDbType.NText; tsqlParam.Value = text; tsqlParam.Size = text.Length; + tsqlParam.Direction = ParameterDirection.Input; } } @@ -4669,18 +4670,18 @@ private void PrepareDescribeParameterEncryptionRequest(_SqlRPC originalRpcReques } SqlParameter paramsParam = describeParameterEncryptionRequest.systemParams[1]; - paramsParam.Direction = ParameterDirection.Input; paramsParam.SqlDbType = ((parameterList.Length << 1) <= TdsEnums.TYPE_SIZE_LIMIT) ? SqlDbType.NVarChar : SqlDbType.NText; paramsParam.Size = parameterList.Length; paramsParam.Value = parameterList; + paramsParam.Direction = ParameterDirection.Input; if (attestationParameters != null) { SqlParameter attestationParametersParam = describeParameterEncryptionRequest.systemParams[2]; - attestationParametersParam.Direction = ParameterDirection.Input; attestationParametersParam.SqlDbType = SqlDbType.VarBinary; attestationParametersParam.Size = attestationParameters.Length; attestationParametersParam.Value = attestationParameters; + attestationParametersParam.Direction = ParameterDirection.Input; } } @@ -6633,8 +6634,9 @@ private _SqlRPC BuildPrepExec(CommandBehavior behavior) //@handle sqlParam = rpc.systemParams[0]; + sqlParam.SqlDbType = SqlDbType.Int; sqlParam.Value = _prepareHandle; - sqlParam.Value = 4; + sqlParam.Size = 4; sqlParam.Direction = ParameterDirection.InputOutput; rpc.systemParamOptions[0] = TdsEnums.RPC_PARAM_BYREF; @@ -6644,6 +6646,7 @@ private _SqlRPC BuildPrepExec(CommandBehavior behavior) sqlParam.SqlDbType = ((paramList.Length << 1) <= TdsEnums.TYPE_SIZE_LIMIT) ? SqlDbType.NVarChar : SqlDbType.NText; sqlParam.Value = paramList; sqlParam.Size = paramList.Length; + sqlParam.Direction = ParameterDirection.Input; //@batch_text string text = GetCommandText(behavior); @@ -6651,6 +6654,7 @@ private _SqlRPC BuildPrepExec(CommandBehavior behavior) sqlParam.SqlDbType = ((text.Length << 1) <= TdsEnums.TYPE_SIZE_LIMIT) ? SqlDbType.NVarChar : SqlDbType.NText; sqlParam.Size = text.Length; sqlParam.Value = text; + sqlParam.Direction = ParameterDirection.Input; SetUpRPCParameters(rpc, false, _parameters); return rpc; @@ -6752,6 +6756,7 @@ private _SqlRPC BuildExecute(bool inSchema) SqlParameter sqlParam = rpc.systemParams[0]; sqlParam.SqlDbType = SqlDbType.Int; sqlParam.Value = _prepareHandle; + sqlParam.Size = 4; sqlParam.Direction = ParameterDirection.Input; SetUpRPCParameters(rpc, inSchema, _parameters); @@ -6803,6 +6808,7 @@ private void BuildExecuteSql(CommandBehavior behavior, string commandText, SqlPa sqlParam.SqlDbType = ((paramList.Length << 1) <= TdsEnums.TYPE_SIZE_LIMIT) ? SqlDbType.NVarChar : SqlDbType.NText; sqlParam.Size = paramList.Length; sqlParam.Value = paramList; + sqlParam.Direction = ParameterDirection.Input; bool inSchema = (0 != (behavior & CommandBehavior.SchemaOnly)); SetUpRPCParameters(rpc, inSchema, parameters); From 27a369c46ff8c51fce7dedb4d4220665ffba0950 Mon Sep 17 00:00:00 2001 From: Wraith2 Date: Tue, 1 Nov 2022 22:31:07 +0000 Subject: [PATCH 6/6] review feedback --- .../netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs index 1ff35bbde6..fdfa0772ea 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs @@ -4581,6 +4581,7 @@ private void PrepareDescribeParameterEncryptionRequest(_SqlRPC originalRpcReques tsqlParam.SqlDbType = ((text.Length << 1) <= TdsEnums.TYPE_SIZE_LIMIT) ? SqlDbType.NVarChar : SqlDbType.NText; tsqlParam.Value = text; tsqlParam.Size = text.Length; + tsqlParam.Direction = ParameterDirection.Input; } else {