Exemple #1
0
        internal Task TdsExecuteSQLBatch(string text, int timeout, TdsParserStateObject stateObj, bool sync, bool callerHasConnectionLock = false)
        {
            if (TdsParserState.Broken == State || TdsParserState.Closed == State)
            {
                return null;
            }

            if (stateObj.BcpLock)
            {
                throw SQL.ConnectionLockedForBcpEvent();
            }

            // Promote, Commit and Rollback requests for
            // delegated transactions often happen while there is an open result
            // set, so we need to handle them by using a different MARS session, 
            // otherwise we'll write on the physical state objects while someone
            // else is using it.  When we don't have MARS enabled, we need to 
            // lock the physical state object to syncronize it's use at least 
            // until we increment the open results count.  Once it's been 
            // incremented the delegated transaction requests will fail, so they
            // won't stomp on anything.

            // Only need to take the lock if neither the thread nor the caller claims to already have it
            bool needToTakeParserLock = (!callerHasConnectionLock) && (!_connHandler.ThreadHasParserLockForClose);
            Debug.Assert(!_connHandler.ThreadHasParserLockForClose || sync, "Thread shouldn't claim to have the parser lock if we are doing async writes");     // Since we have the possibility of pending with async writes, make sure the thread doesn't claim to already have the lock
            Debug.Assert(needToTakeParserLock || _connHandler._parserLock.ThreadMayHaveLock(), "Thread or caller claims to have connection lock, but lock is not taken");

            bool releaseConnectionLock = false;
            if (needToTakeParserLock)
            {
                _connHandler._parserLock.Wait(canReleaseFromAnyThread: !sync);
                releaseConnectionLock = true;
            }

            // Switch the writing mode
            // NOTE: We are not turning off async writes when we complete since SqlBulkCopy uses this method and expects _asyncWrite to not change
            _asyncWrite = !sync;

            try
            {
                // Check that the connection is still alive
                if ((_state == TdsParserState.Closed) || (_state == TdsParserState.Broken))
                {
                    throw ADP.ClosedConnectionError();
                }


                stateObj.SetTimeoutSeconds(timeout);
                stateObj.SniContext = SniContext.Snix_Execute;

                WriteRPCBatchHeaders(stateObj);

                stateObj._outputMessageType = TdsEnums.MT_SQL;

                WriteString(text, text.Length, 0, stateObj);

                Task executeTask = stateObj.ExecuteFlush();
                if (executeTask == null)
                {
                    stateObj.SniContext = SniContext.Snix_Read;
                }
                else
                {
                    Debug.Assert(!sync, "Should not have gotten a Task when writing in sync mode");

                    // Need to wait for flush - continuation will unlock the connection                    
                    bool taskReleaseConnectionLock = releaseConnectionLock;
                    releaseConnectionLock = false;
                    return executeTask.ContinueWith(t =>
                    {
                        Debug.Assert(!t.IsCanceled, "Task should not be canceled");
                        try
                        {
                            if (t.IsFaulted)
                            {
                                FailureCleanup(stateObj, t.Exception.InnerException);
                                throw t.Exception.InnerException;
                            }
                            else
                            {
                                stateObj.SniContext = SniContext.Snix_Read;
                            }
                        }
                        finally
                        {
                            if (taskReleaseConnectionLock)
                            {
                                _connHandler._parserLock.Release();
                            }
                        }
                    }, TaskScheduler.Default);
                }

                // Finished sync
                return null;
            }
            catch (Exception e)
            {
                if (!ADP.IsCatchableExceptionType(e))
                {
                    throw;
                }

                FailureCleanup(stateObj, e);

                throw;
            }
            finally
            {
                if (releaseConnectionLock)
                {
                    _connHandler._parserLock.Release();
                }
            }
        }
Exemple #2
0
        internal Task TdsExecuteRPC(_SqlRPC[] rpcArray, int timeout, bool inSchema, TdsParserStateObject stateObj, bool isCommandProc, bool sync = true,
          TaskCompletionSource<object> completion = null, int startRpc = 0, int startParam = 0)
        {
            bool firstCall = (completion == null);
            bool releaseConnectionLock = false;

            Debug.Assert(!firstCall || startRpc == 0, "startRpc is not 0 on first call");
            Debug.Assert(!firstCall || startParam == 0, "startParam is not 0 on first call");
            Debug.Assert(!firstCall || !_connHandler.ThreadHasParserLockForClose, "Thread should not already have connection lock");
            Debug.Assert(firstCall || _connHandler._parserLock.ThreadMayHaveLock(), "Connection lock not taken after the first call");
            try
            {
                _SqlRPC rpcext = null;
                int tempLen;

                // Promote, Commit and Rollback requests for
                // delegated transactions often happen while there is an open result
                // set, so we need to handle them by using a different MARS session, 
                // otherwise we'll write on the physical state objects while someone
                // else is using it.  When we don't have MARS enabled, we need to 
                // lock the physical state object to syncronize it's use at least 
                // until we increment the open results count.  Once it's been 
                // incremented the delegated transaction requests will fail, so they
                // won't stomp on anything.


                if (firstCall)
                {
                    _connHandler._parserLock.Wait(canReleaseFromAnyThread: !sync);
                    releaseConnectionLock = true;
                }
                try
                {
                    // Ensure that connection is alive
                    if ((TdsParserState.Broken == State) || (TdsParserState.Closed == State))
                    {
                        throw ADP.ClosedConnectionError();
                    }

                    // This validation step MUST be done after locking the connection to guarantee we don't 
                    //  accidentally execute after the transaction has completed on a different thread.
                    if (firstCall)
                    {
                        _asyncWrite = !sync;


                        stateObj.SetTimeoutSeconds(timeout);
                        stateObj.SniContext = SniContext.Snix_Execute;

                        WriteRPCBatchHeaders(stateObj);

                        stateObj._outputMessageType = TdsEnums.MT_RPC;
                    }

                    for (int ii = startRpc; ii < rpcArray.Length; ii++)
                    {
                        rpcext = rpcArray[ii];

                        if (startParam == 0 || ii > startRpc)
                        {
                            if (rpcext.ProcID != 0)
                            {
                                // Perf optimization for Shiloh and later,
                                Debug.Assert(rpcext.ProcID < 255, "rpcExec:ProcID can't be larger than 255");
                                WriteShort(0xffff, stateObj);
                                WriteShort((short)(rpcext.ProcID), stateObj);
                            }
                            else
                            {
                                Debug.Assert(!ADP.IsEmpty(rpcext.rpcName), "must have an RPC name");
                                tempLen = rpcext.rpcName.Length;
                                WriteShort(tempLen, stateObj);
                                WriteString(rpcext.rpcName, tempLen, 0, stateObj);
                            }

                            // Options
                            WriteShort((short)rpcext.options, stateObj);
                        }

                        // Stream out parameters
                        SqlParameter[] parameters = rpcext.parameters;

                        for (int i = (ii == startRpc) ? startParam : 0; i < parameters.Length; i++)
                        {
                            //                Debug.WriteLine("i:  " + i.ToString(CultureInfo.InvariantCulture));
                            // parameters can be unnamed
                            SqlParameter param = parameters[i];
                            // 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

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

                            // type (parameter record stores the MetaType class which is a helper that encapsulates all the type information we need here)
                            MetaType mt = param.InternalMetaType;

                            if (mt.IsNewKatmaiType)
                            {
                                WriteSmiParameter(param, i, 0 != (rpcext.paramoptions[i] & TdsEnums.RPC_PARAM_DEFAULT), stateObj);
                                continue;
                            }

                            if (
                              (!_isKatmai && !mt.Is90Supported))
                            {
                                throw ADP.VersionDoesNotSupportDataType(mt.TypeName);
                            }
                            object value = null;
                            bool isNull = true;
                            bool isSqlVal = false;
                            bool isDataFeed = false;
                            // if we have an output param, set the value to null so we do not send it across to the server
                            if (param.Direction == ParameterDirection.Output)
                            {
                                isSqlVal = param.ParamaterIsSqlType;  // We have to forward the TYPE info, we need to know what type we are returning.  Once we null the paramater we will no longer be able to distinguish what type were seeing.
                                param.Value = null;
                                param.ParamaterIsSqlType = isSqlVal;
                            }
                            else
                            {
                                value = param.GetCoercedValue();
                                isNull = param.IsNull;
                                if (!isNull)
                                {
                                    isSqlVal = param.CoercedValueIsSqlType;
                                    isDataFeed = param.CoercedValueIsDataFeed;
                                }
                            }

                            WriteParameterName(param.ParameterNameFixed, stateObj);

                            // Write parameter status
                            stateObj.WriteByte(rpcext.paramoptions[i]);

                            //
                            // fixup the types by using the NullableType property of the MetaType class
                            //
                            // following rules should be followed based on feedback from the M-SQL team
                            // 1) always use the BIG* types (ex: instead of SQLCHAR use SQLBIGCHAR)
                            // 2) always use nullable types (ex: instead of SQLINT use SQLINTN)
                            // 3) DECIMALN should always be sent as NUMERICN
                            //
                            stateObj.WriteByte(mt.NullableType);

                            // handle variants here: the SQLVariant writing routine will write the maxlen and actual len columns
                            if (mt.TDSType == TdsEnums.SQLVARIANT)
                            {
                                // devnote: Do we ever hit this codepath? Yes, when a null value is being writen out via a sql variant
                                // param.GetActualSize is not used
                                WriteSqlVariantValue(isSqlVal ? MetaType.GetComValueFromSqlVariant(value) : value, param.GetActualSize(), param.Offset, stateObj);
                                continue;
                            }

                            // MaxLen field is only written out for non-fixed length data types
                            // use the greater of the two sizes for maxLen
                            int actualSize;
                            int size = mt.IsSizeInCharacters ? param.GetParameterSize() * 2 : param.GetParameterSize();

                            //for UDTs, we calculate the length later when we get the bytes. This is a really expensive operation
                            if (mt.TDSType != TdsEnums.SQLUDT)
                                // getting the actualSize is expensive, cache here and use below
                                actualSize = param.GetActualSize();
                            else
                                actualSize = 0; //get this later

                            int codePageByteSize = 0;
                            int maxsize = 0;

                            if (mt.IsAnsiType)
                            {
                                // Avoid the following code block if ANSI but unfilled LazyMat blob
                                if ((!isNull) && (!isDataFeed))
                                {
                                    string s;

                                    if (isSqlVal)
                                    {
                                        if (value is SqlString)
                                        {
                                            s = ((SqlString)value).Value;
                                        }
                                        else
                                        {
                                            Debug.Assert(value is SqlChars, "Unknown value for Ansi datatype");
                                            s = new String(((SqlChars)value).Value);
                                        }
                                    }
                                    else
                                    {
                                        s = (string)value;
                                    }

                                    codePageByteSize = GetEncodingCharLength(s, actualSize, param.Offset, _defaultEncoding);
                                }

                                if (mt.IsPlp)
                                {
                                    WriteShort(TdsEnums.SQL_USHORTVARMAXLEN, stateObj);
                                }
                                else
                                {
                                    maxsize = (size > codePageByteSize) ? size : codePageByteSize;
                                    if (maxsize == 0)
                                    {
                                        // Yukon doesn't like 0 as MaxSize. Change it to 2 for unicode types (SQL9 - 682322)
                                        if (mt.IsNCharType)
                                            maxsize = 2;
                                        else
                                            maxsize = 1;
                                    }

                                    WriteParameterVarLen(mt, maxsize, false/*IsNull*/, stateObj);
                                }
                            }
                            else
                            {
                                // If type timestamp - treat as fixed type and always send over timestamp length, which is 8.
                                // For fixed types, we either send null or fixed length for type length.  We want to match that
                                // behavior for timestamps.  However, in the case of null, we still must send 8 because if we
                                // send null we will not receive a output val.  You can send null for fixed types and still
                                // receive a output value, but not for variable types.  So, always send 8 for timestamp because
                                // while the user sees it as a fixed type, we are actually representing it as a bigbinary which
                                // is variable.
                                if (mt.SqlDbType == SqlDbType.Timestamp)
                                {
                                    WriteParameterVarLen(mt, TdsEnums.TEXT_TIME_STAMP_LEN, false, stateObj);
                                }
                                else if (mt.SqlDbType == SqlDbType.Udt)
                                {
                                    throw ADP.DbTypeNotSupported(SqlDbType.Udt.ToString());
                                }
                                else if (mt.IsPlp)
                                {
                                    if (mt.SqlDbType != SqlDbType.Xml)
                                        WriteShort(TdsEnums.SQL_USHORTVARMAXLEN, stateObj);
                                }
                                else if ((!mt.IsVarTime) && (mt.SqlDbType != SqlDbType.Date))
                                {   // Time, Date, DateTime2, DateTimeoffset do not have the size written out
                                    maxsize = (size > actualSize) ? size : actualSize;
                                    if (maxsize == 0)
                                    {
                                        // Yukon doesn't like 0 as MaxSize. Change it to 2 for unicode types (SQL9 - 682322)
                                        if (mt.IsNCharType)
                                            maxsize = 2;
                                        else
                                            maxsize = 1;
                                    }

                                    WriteParameterVarLen(mt, maxsize, false/*IsNull*/, stateObj);
                                }
                            }

                            // scale and precision are only relevant for numeric and decimal types
                            if (mt.SqlDbType == SqlDbType.Decimal)
                            {
                                byte precision = param.GetActualPrecision();
                                byte scale = param.GetActualScale();

                                if (precision > TdsEnums.MAX_NUMERIC_PRECISION)
                                {
                                    throw SQL.PrecisionValueOutOfRange(precision);
                                }

                                // Make sure the value matches the scale the user enters
                                if (!isNull)
                                {
                                    if (isSqlVal)
                                    {
                                        value = AdjustSqlDecimalScale((SqlDecimal)value, scale);

                                        // If Precision is specified, verify value precision vs param precision
                                        if (precision != 0)
                                        {
                                            if (precision < ((SqlDecimal)value).Precision)
                                            {
                                                throw ADP.ParameterValueOutOfRange((SqlDecimal)value);
                                            }
                                        }
                                    }
                                    else
                                    {
                                        value = AdjustDecimalScale((Decimal)value, scale);

                                        SqlDecimal sqlValue = new SqlDecimal((Decimal)value);

                                        // If Precision is specified, verify value precision vs param precision
                                        if (precision != 0)
                                        {
                                            if (precision < sqlValue.Precision)
                                            {
                                                throw ADP.ParameterValueOutOfRange((Decimal)value);
                                            }
                                        }
                                    }
                                }

                                if (0 == precision)
                                {
                                    stateObj.WriteByte(TdsEnums.DEFAULT_NUMERIC_PRECISION);
                                }
                                else
                                    stateObj.WriteByte(precision);

                                stateObj.WriteByte(scale);
                            }
                            else if (mt.IsVarTime)
                            {
                                stateObj.WriteByte(param.GetActualScale());
                            }

                            // write out collation or xml metadata

                            if (mt.SqlDbType == SqlDbType.Xml)
                            {
                                if (((param.XmlSchemaCollectionDatabase != null) && (param.XmlSchemaCollectionDatabase != ADP.StrEmpty)) ||
                                    ((param.XmlSchemaCollectionOwningSchema != null) && (param.XmlSchemaCollectionOwningSchema != ADP.StrEmpty)) ||
                                    ((param.XmlSchemaCollectionName != null) && (param.XmlSchemaCollectionName != ADP.StrEmpty)))
                                {
                                    stateObj.WriteByte(1);   //Schema present flag

                                    if ((param.XmlSchemaCollectionDatabase != null) && (param.XmlSchemaCollectionDatabase != ADP.StrEmpty))
                                    {
                                        tempLen = (param.XmlSchemaCollectionDatabase).Length;
                                        stateObj.WriteByte((byte)(tempLen));
                                        WriteString(param.XmlSchemaCollectionDatabase, tempLen, 0, stateObj);
                                    }
                                    else
                                    {
                                        stateObj.WriteByte(0);       // No dbname
                                    }

                                    if ((param.XmlSchemaCollectionOwningSchema != null) && (param.XmlSchemaCollectionOwningSchema != ADP.StrEmpty))
                                    {
                                        tempLen = (param.XmlSchemaCollectionOwningSchema).Length;
                                        stateObj.WriteByte((byte)(tempLen));
                                        WriteString(param.XmlSchemaCollectionOwningSchema, tempLen, 0, stateObj);
                                    }
                                    else
                                    {
                                        stateObj.WriteByte(0);      // no xml schema name
                                    }
                                    if ((param.XmlSchemaCollectionName != null) && (param.XmlSchemaCollectionName != ADP.StrEmpty))
                                    {
                                        tempLen = (param.XmlSchemaCollectionName).Length;
                                        WriteShort((short)(tempLen), stateObj);
                                        WriteString(param.XmlSchemaCollectionName, tempLen, 0, stateObj);
                                    }
                                    else
                                    {
                                        WriteShort(0, stateObj);       // No xml schema collection name
                                    }
                                }
                                else
                                {
                                    stateObj.WriteByte(0);       // No schema
                                }
                            }
                            else if (mt.IsCharType)
                            {
                                // if it is not supplied, simply write out our default collation, otherwise, write out the one attached to the parameter
                                SqlCollation outCollation = (param.Collation != null) ? param.Collation : _defaultCollation;
                                Debug.Assert(_defaultCollation != null, "_defaultCollation is null!");

                                WriteUnsignedInt(outCollation.info, stateObj);
                                stateObj.WriteByte(outCollation.sortId);
                            }

                            if (0 == codePageByteSize)
                                WriteParameterVarLen(mt, actualSize, isNull, stateObj, isDataFeed);
                            else
                                WriteParameterVarLen(mt, codePageByteSize, isNull, stateObj, isDataFeed);

                            Task writeParamTask = null;
                            // write the value now
                            if (!isNull)
                            {
                                if (isSqlVal)
                                {
                                    writeParamTask = WriteSqlValue(value, mt, actualSize, codePageByteSize, param.Offset, stateObj);
                                }
                                else
                                {
                                    // for codePageEncoded types, WriteValue simply expects the number of characters
                                    // For plp types, we also need the encoded byte size
                                    writeParamTask = WriteValue(value, mt, param.GetActualScale(), actualSize, codePageByteSize, param.Offset, stateObj, param.Size, isDataFeed);
                                }
                            }

                            if (!sync)
                            {
                                if (writeParamTask == null)
                                {
                                    writeParamTask = stateObj.WaitForAccumulatedWrites();
                                }

                                if (writeParamTask != null)
                                {
                                    Task task = null;
                                    if (completion == null)
                                    {
                                        completion = new TaskCompletionSource<object>();
                                        task = completion.Task;
                                    }

                                    AsyncHelper.ContinueTask(writeParamTask, completion,
                                      () => TdsExecuteRPC(rpcArray, timeout, inSchema, stateObj, isCommandProc, sync, completion,
                                                            startRpc: ii, startParam: i + 1),
                                        connectionToDoom: _connHandler,
                                        onFailure: exc => TdsExecuteRPC_OnFailure(exc, stateObj));

                                    // Take care of releasing the locks
                                    if (releaseConnectionLock)
                                    {
                                        task.ContinueWith(_ =>
                                        {
                                            _connHandler._parserLock.Release();
                                        }, TaskScheduler.Default);
                                        releaseConnectionLock = false;
                                    }

                                    return task;
                                }
                            }
#if DEBUG
                            else
                            {
                                Debug.Assert(writeParamTask == null, "Should not have a task when executing sync");
                            }
#endif
                        } // parameter for loop

                        // If this is not the last RPC we are sending, add the batch flag
                        if (ii < (rpcArray.Length - 1))
                        {
                            stateObj.WriteByte(TdsEnums.YUKON_RPCBATCHFLAG);
                        }
                    } // rpc for loop

                    Task execFlushTask = stateObj.ExecuteFlush();
                    Debug.Assert(!sync || execFlushTask == null, "Should not get a task when executing sync");
                    if (execFlushTask != null)
                    {
                        Task task = null;

                        if (completion == null)
                        {
                            completion = new TaskCompletionSource<object>();
                            task = completion.Task;
                        }

                        bool taskReleaseConnectionLock = releaseConnectionLock;
                        execFlushTask.ContinueWith(tsk => ExecuteFlushTaskCallback(tsk, stateObj, completion, taskReleaseConnectionLock), TaskScheduler.Default);

                        // ExecuteFlushTaskCallback will take care of the locks for us
                        releaseConnectionLock = false;

                        return task;
                    }
                }
                catch (Exception e)
                {
                    if (!ADP.IsCatchableExceptionType(e))
                    {
                        throw;
                    }

                    FailureCleanup(stateObj, e);

                    throw;
                }
                FinalizeExecuteRPC(stateObj);
                if (completion != null)
                {
                    completion.SetResult(null);
                }
                return null;
            }
            catch (Exception e)
            {
                FinalizeExecuteRPC(stateObj);
                if (completion != null)
                {
                    completion.SetException(e);
                    return null;
                }
                else
                {
                    throw e;
                }
            }
            finally
            {
                Debug.Assert(firstCall || !releaseConnectionLock, "Shouldn't be releasing locks synchronously after the first call");
                if (releaseConnectionLock)
                {
                    _connHandler._parserLock.Release();
                }
            }
        }
Exemple #3
0
        internal SqlDataReader TdsExecuteTransactionManagerRequest(
                    byte[] buffer,
                    TdsEnums.TransactionManagerRequestType request,
                    string transactionName,
                    TdsEnums.TransactionManagerIsolationLevel isoLevel,
                    int timeout,
                    SqlInternalTransaction transaction,
                    TdsParserStateObject stateObj
        )
        {
            Debug.Assert(this == stateObj.Parser, "different parsers");

            if (TdsParserState.Broken == State || TdsParserState.Closed == State)
            {
                return null;
            }

            // Promote, Commit and Rollback requests for
            // delegated transactions often happen while there is an open result
            // set, so we need to handle them by using a different MARS session, 
            // otherwise we'll write on the physical state objects while someone
            // else is using it.  When we don't have MARS enabled, we need to 
            // lock the physical state object to syncronize it's use at least 
            // until we increment the open results count.  Once it's been 
            // incremented the delegated transaction requests will fail, so they
            // won't stomp on anything.


            Debug.Assert(!_connHandler.ThreadHasParserLockForClose || _connHandler._parserLock.ThreadMayHaveLock(), "Thread claims to have parser lock, but lock is not taken");
            bool callerHasConnectionLock = _connHandler.ThreadHasParserLockForClose;   // If the thread already claims to have the parser lock, then we will let the caller handle releasing it
            if (!callerHasConnectionLock)
            {
                _connHandler._parserLock.Wait(canReleaseFromAnyThread: false);
                _connHandler.ThreadHasParserLockForClose = true;
            }
            // Capture _asyncWrite (after taking lock) to restore it afterwards
            bool hadAsyncWrites = _asyncWrite;
            try
            {
                // Temprarily disable async writes
                _asyncWrite = false;


                stateObj._outputMessageType = TdsEnums.MT_TRANS;       // set message type
                stateObj.SetTimeoutSeconds(timeout);

                stateObj.SniContext = SniContext.Snix_Execute;

                const int marsHeaderSize = 18; // 4 + 2 + 8 + 4
                const int totalHeaderLength = 22; // 4 + 4 + 2 + 8 + 4
                Debug.Assert(stateObj._outBytesUsed == stateObj._outputHeaderLen, "Output bytes written before total header length");
                // Write total header length
                WriteInt(totalHeaderLength, stateObj);
                // Write mars header length
                WriteInt(marsHeaderSize, stateObj);
                WriteMarsHeaderData(stateObj, _currentTransaction);

                WriteShort((short)request, stateObj); // write TransactionManager Request type

                bool returnReader = false;

                switch (request)
                {
                    case TdsEnums.TransactionManagerRequestType.Begin:
                        Debug.Assert(null != transaction, "Should have specified an internalTransaction when doing a BeginTransaction request!");

                        // Only assign the passed in transaction if it is not equal to the current transaction.
                        // And, if it is not equal, the current actually should be null.  Anything else
                        // is a unexpected state.  The concern here is mainly for the mixed use of 
                        // T-SQL and API transactions. 

                        // Expected states:
                        // 1) _pendingTransaction = null, _currentTransaction = null, non null transaction
                        // passed in on BeginTransaction API call.
                        // 2) _currentTransaction != null, _pendingTransaction = null, non null transaction
                        // passed in but equivalent to _currentTransaction.

                        // #1 will occur on standard BeginTransactionAPI call.  #2 should only occur if
                        // t-sql transaction started followed by a call to SqlConnection.BeginTransaction.
                        // Any other state is unknown.
                        if (_currentTransaction != transaction)
                        {
                            Debug.Assert(_currentTransaction == null || true == _fResetConnection, "We should not have a current Tx at this point");
                            PendingTransaction = transaction;
                        }

                        stateObj.WriteByte((byte)isoLevel);

                        stateObj.WriteByte((byte)(transactionName.Length * 2)); // Write number of bytes (unicode string).
                        WriteString(transactionName, stateObj);
                        break;
                    case TdsEnums.TransactionManagerRequestType.Commit:

                        Debug.Assert(transactionName.Length == 0, "Should not have a transaction name on Commit");
                        stateObj.WriteByte((byte)0); // No xact name

                        stateObj.WriteByte(0);  // No flags

                        Debug.Assert(isoLevel == TdsEnums.TransactionManagerIsolationLevel.Unspecified, "Should not have isolation level other than unspecified on Commit!");
                        // WriteByte((byte) 0, stateObj); // IsolationLevel
                        // WriteByte((byte) 0, stateObj); // No begin xact name
                        break;
                    case TdsEnums.TransactionManagerRequestType.Rollback:

                        stateObj.WriteByte((byte)(transactionName.Length * 2)); // Write number of bytes (unicode string).
                        WriteString(transactionName, stateObj);

                        stateObj.WriteByte(0);  // No flags

                        Debug.Assert(isoLevel == TdsEnums.TransactionManagerIsolationLevel.Unspecified, "Should not have isolation level other than unspecified on Commit!");
                        // WriteByte((byte) 0, stateObj); // IsolationLevel
                        // WriteByte((byte) 0, stateObj); // No begin xact name
                        break;
                    case TdsEnums.TransactionManagerRequestType.Save:

                        stateObj.WriteByte((byte)(transactionName.Length * 2)); // Write number of bytes (unicode string).
                        WriteString(transactionName, stateObj);
                        break;
                    default:
                        Debug.Assert(false, "Unexpected TransactionManagerRequest");
                        break;
                }

                Task writeTask = stateObj.WritePacket(TdsEnums.HARDFLUSH);
                Debug.Assert(writeTask == null, "Writes should not pend when writing sync");
                stateObj._pendingData = true;
                stateObj._messageStatus = 0;

                SqlDataReader dtcReader = null;
                stateObj.SniContext = SniContext.Snix_Read;
                if (returnReader)
                {
                    dtcReader = new SqlDataReader(null, CommandBehavior.Default);
                    Debug.Assert(this == stateObj.Parser, "different parser");
#if DEBUG
                    // Remove the current owner of stateObj - otherwise we will hit asserts
                    stateObj.Owner = null;
#endif
                    dtcReader.Bind(stateObj);

                    // force consumption of metadata
                    _SqlMetaDataSet metaData = dtcReader.MetaData;
                }
                else
                {
                    Run(RunBehavior.UntilDone, null, null, null, stateObj);
                }


                return dtcReader;
            }
            catch (Exception e)
            {
                if (!ADP.IsCatchableExceptionType(e))
                {
                    throw;
                }

                FailureCleanup(stateObj, e);

                throw;
            }
            finally
            {
                // SQLHotfix 50000518
                // make sure we don't leave temporary fields set when leaving this function
                _pendingTransaction = null;

                _asyncWrite = hadAsyncWrites;

                if (!callerHasConnectionLock)
                {
                    _connHandler.ThreadHasParserLockForClose = false;
                    _connHandler._parserLock.Release();
                }
            }
        }
      internal Task TdsExecuteRPC(SqlCommand cmd, _SqlRPC[] rpcArray, int timeout, bool inSchema, SqlNotificationRequest notificationRequest, TdsParserStateObject stateObj, bool isCommandProc, bool sync = true, 
          TaskCompletionSource<object> completion = null, int startRpc = 0, int startParam = 0) {
          bool firstCall = (completion == null);
          bool releaseConnectionLock = false;

          Debug.Assert(cmd != null, @"cmd cannot be null inside TdsExecuteRPC");
          Debug.Assert(!firstCall || startRpc == 0, "startRpc is not 0 on first call");
          Debug.Assert(!firstCall || startParam == 0, "startParam is not 0 on first call");
          Debug.Assert(!firstCall || !_connHandler.ThreadHasParserLockForClose, "Thread should not already have connection lock");
          Debug.Assert(firstCall || _connHandler._parserLock.ThreadMayHaveLock(), "Connection lock not taken after the first call");
          try {
              _SqlRPC rpcext = null;
              int tempLen;

              // SQLBUDT #20010853 - Promote, Commit and Rollback requests for
              // delegated transactions often happen while there is an open result
              // set, so we need to handle them by using a different MARS session, 
              // otherwise we'll write on the physical state objects while someone
              // else is using it.  When we don't have MARS enabled, we need to 
              // lock the physical state object to syncronize it's use at least 
              // until we increment the open results count.  Once it's been 
              // incremented the delegated transaction requests will fail, so they
              // won't stomp on anything.


              if (firstCall) {
                  _connHandler._parserLock.Wait(canReleaseFromAnyThread:!sync);
                  releaseConnectionLock = true;
              }
              try {
                  // Ensure that connection is alive
                  if ((TdsParserState.Broken == State) || (TdsParserState.Closed == State)) {
                      throw ADP.ClosedConnectionError();
                  }

                  // This validation step MUST be done after locking the connection to guarantee we don't 
                  //  accidentally execute after the transaction has completed on a different thread.
                  if (firstCall) {
                      _asyncWrite = !sync;

                      _connHandler.CheckEnlistedTransactionBinding();

                      stateObj.SetTimeoutSeconds(timeout);
                      if ((!_fMARS) && (_physicalStateObj.HasOpenResult)) 
                      {
                          Bid.Trace("<sc.TdsParser.TdsExecuteRPC|ERR> Potential multi-threaded misuse of connection, non-MARs connection with an open result %d#\n", ObjectID);
                      }
                      stateObj.SniContext = SniContext.Snix_Execute;

                      if (_isYukon) {

                          WriteRPCBatchHeaders(stateObj, notificationRequest);
                      }

                      stateObj._outputMessageType = TdsEnums.MT_RPC;
                  }

                  for (int ii = startRpc; ii < rpcArray.Length; ii++) {
                      rpcext = rpcArray[ii];

                      if (startParam == 0 || ii > startRpc) {
                          if (rpcext.ProcID != 0 && _isShiloh) {
                              // Perf optimization for Shiloh and later,
                              Debug.Assert(rpcext.ProcID < 255, "rpcExec:ProcID can't be larger than 255");
                              WriteShort(0xffff, stateObj);
                              WriteShort((short)(rpcext.ProcID), stateObj);
                          }
                          else {
                              Debug.Assert(!ADP.IsEmpty(rpcext.rpcName), "must have an RPC name");
                              tempLen = rpcext.rpcName.Length;
                              WriteShort(tempLen, stateObj);
                              WriteString(rpcext.rpcName, tempLen, 0, stateObj);
                          }

                          // Options
                          WriteShort((short)rpcext.options, stateObj);
                      }

                      // Stream out parameters
                      SqlParameter[] parameters = rpcext.parameters;

                      for (int i = (ii == startRpc) ? startParam : 0; i < parameters.Length; i++) {
                          //                Debug.WriteLine("i:  " + i.ToString(CultureInfo.InvariantCulture));
                          // parameters can be unnamed
                          SqlParameter param = parameters[i];
                          // 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

                          // Throw an exception if ForceColumnEncryption is set on a parameter and the ColumnEncryption is not enabled on SqlConnection or SqlCommand
                          if (param.ForceColumnEncryption && 
                              !(cmd.ColumnEncryptionSetting == SqlCommandColumnEncryptionSetting.Enabled || 
                              (cmd.ColumnEncryptionSetting == SqlCommandColumnEncryptionSetting.UseConnectionSetting && cmd.Connection.IsColumnEncryptionSettingEnabled))) {
                              throw SQL.ParamInvalidForceColumnEncryptionSetting(param.ParameterName, rpcext.GetCommandTextOrRpcName());
                          }

                          // 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)) {
                              // 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());
                          }

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

                          // type (parameter record stores the MetaType class which is a helper that encapsulates all the type information we need here)
                          MetaType mt = param.InternalMetaType;

                          if (mt.IsNewKatmaiType) {
                              WriteSmiParameter(param, i, 0 != (rpcext.paramoptions[i] & TdsEnums.RPC_PARAM_DEFAULT), stateObj);
                              continue;
                          }

                          if ((!_isShiloh && !mt.Is70Supported) ||
                              (!_isYukon && !mt.Is80Supported) ||
                              (!_isKatmai && !mt.Is90Supported)) {
                              throw ADP.VersionDoesNotSupportDataType(mt.TypeName);
                          }
                          object value = null;
                          bool isNull = true;
                          bool isSqlVal = false;
                          bool isDataFeed = false;
                          // if we have an output param, set the value to null so we do not send it across to the server
                          if (param.Direction == ParameterDirection.Output) {
                              isSqlVal = param.ParamaterIsSqlType;  // We have to forward the TYPE info, we need to know what type we are returning.  Once we null the paramater we will no longer be able to distinguish what type were seeing.
                              param.Value = null;
                              param.ParamaterIsSqlType = isSqlVal;
                          }
                          else {
                              value = param.GetCoercedValue();
                              isNull = param.IsNull;
                              if (!isNull) {
                                  isSqlVal = param.CoercedValueIsSqlType;
                                  isDataFeed = param.CoercedValueIsDataFeed;
                              }
                          }

                          WriteParameterName(param.ParameterNameFixed, stateObj);

                          // Write parameter status
                          stateObj.WriteByte(rpcext.paramoptions[i]);

                          // MaxLen field is only written out for non-fixed length data types
                          // use the greater of the two sizes for maxLen
                          int actualSize;
                          int size = mt.IsSizeInCharacters ? param.GetParameterSize() * 2 : param.GetParameterSize();

                          //for UDTs, we calculate the length later when we get the bytes. This is a really expensive operation
                          if (mt.TDSType != TdsEnums.SQLUDT)
                              // getting the actualSize is expensive, cache here and use below
                              actualSize = param.GetActualSize();
                          else
                              actualSize = 0; //get this later

                          byte precision = 0;
                          byte scale = 0;

                          // scale and precision are only relevant for numeric and decimal types
                          // adjust the actual value scale and precision to match the user specified
                          if (mt.SqlDbType == SqlDbType.Decimal) {
                              precision = param.GetActualPrecision();
                              scale = param.GetActualScale();

                              if (precision > TdsEnums.MAX_NUMERIC_PRECISION) {
                                  throw SQL.PrecisionValueOutOfRange(precision);
                              }

                              // bug 49512, make sure the value matches the scale the user enters
                              if (!isNull) {
                                  if (isSqlVal) {
                                      value = AdjustSqlDecimalScale((SqlDecimal)value, scale);

                                      // If Precision is specified, verify value precision vs param precision
                                      if (precision != 0) {
                                          if (precision < ((SqlDecimal)value).Precision) {
                                              throw ADP.ParameterValueOutOfRange((SqlDecimal)value);
                                          }
                                      }
                                  }
                                  else {
                                      value = AdjustDecimalScale((Decimal)value, scale);

                                      SqlDecimal sqlValue = new SqlDecimal((Decimal)value);

                                      // If Precision is specified, verify value precision vs param precision
                                      if (precision != 0) {
                                          if (precision < sqlValue.Precision) {
                                              throw ADP.ParameterValueOutOfRange((Decimal)value);
                                          }
                                      }
                                  }
                              }
                          }

                          bool isParameterEncrypted = 0 != (rpcext.paramoptions[i] & TdsEnums.RPC_PARAM_ENCRYPTED);

                          // Additional information we need to send over wire to the server when writing encrypted parameters.
                          SqlColumnEncryptionInputParameterInfo encryptedParameterInfoToWrite = null;

                          // If the parameter is encrypted, we need to encrypt the value.
                          if (isParameterEncrypted) {
                              Debug.Assert(mt.TDSType != TdsEnums.SQLVARIANT &&
                                  mt.TDSType != TdsEnums.SQLUDT &&
                                  mt.TDSType != TdsEnums.SQLXMLTYPE &&
                                  mt.TDSType != TdsEnums.SQLIMAGE &&
                                  mt.TDSType != TdsEnums.SQLTEXT &&
                                  mt.TDSType != TdsEnums.SQLNTEXT, "Type unsupported for encryption");

                              byte[] serializedValue = null;
                              byte[] encryptedValue = null;

                              if (!isNull) {
                                  try {
                                    if (isSqlVal) {
                                        serializedValue = SerializeUnencryptedSqlValue(value, mt, actualSize, param.Offset, param.NormalizationRuleVersion, stateObj);
                                    }
                                    else {
                                        // for codePageEncoded types, WriteValue simply expects the number of characters
                                        // For plp types, we also need the encoded byte size
                                        serializedValue = SerializeUnencryptedValue(value, mt, param.GetActualScale(), actualSize, param.Offset, isDataFeed, param.NormalizationRuleVersion, stateObj);
                                    }

                                    Debug.Assert(serializedValue != null, "serializedValue should not be null in TdsExecuteRPC.");
                                    encryptedValue = SqlSecurityUtility.EncryptWithKey(serializedValue, param.CipherMetadata, _connHandler.ConnectionOptions.DataSource);
                                }
                                catch (Exception e) {
                                    throw SQL.ParamEncryptionFailed(param.ParameterName, null, e);
                                }

                                  Debug.Assert(encryptedValue != null && encryptedValue.Length > 0,
                                      "encryptedValue should not be null or empty in TdsExecuteRPC.");
                              }
                              else {
                                  encryptedValue = null;
                              }

                              // Change the datatype to varbinary(max).
                              // Since we don't know the size of the encrypted parameter on the server side, always set to (max).
                              //
                              mt = MetaType.MetaMaxVarBinary;
                              size = -1;
                              actualSize = (encryptedValue == null) ? 0 : encryptedValue.Length;

                              encryptedParameterInfoToWrite = new SqlColumnEncryptionInputParameterInfo(param.GetMetadataForTypeInfo(),
                                                                                                        param.CipherMetadata);

                              // Set the value to the encrypted value and mark isSqlVal as false for VARBINARY encrypted value.
                              value = encryptedValue;
                              isSqlVal = false;
                          }

                          Debug.Assert(isParameterEncrypted == (encryptedParameterInfoToWrite != null),
                                            "encryptedParameterInfoToWrite can be not null if and only if isParameterEncrypted is true.");

                          Debug.Assert(!isSqlVal || !isParameterEncrypted, "isParameterEncrypted can be true only if isSqlVal is false.");

                          //
                          // fixup the types by using the NullableType property of the MetaType class
                          //
                          // following rules should be followed based on feedback from the M-SQL team
                          // 1) always use the BIG* types (ex: instead of SQLCHAR use SQLBIGCHAR)
                          // 2) always use nullable types (ex: instead of SQLINT use SQLINTN)
                          // 3) DECIMALN should always be sent as NUMERICN
                          //
                          stateObj.WriteByte(mt.NullableType);

                          // handle variants here: the SQLVariant writing routine will write the maxlen and actual len columns
                          if (mt.TDSType == TdsEnums.SQLVARIANT) {
                              // devnote: Do we ever hit this codepath? Yes, when a null value is being writen out via a sql variant
                              // param.GetActualSize is not used
                              WriteSqlVariantValue(isSqlVal ? MetaType.GetComValueFromSqlVariant(value) : value, param.GetActualSize(), param.Offset, stateObj);
                              continue;
                          }

                          int codePageByteSize = 0;
                          int maxsize = 0;

                          if (mt.IsAnsiType) {
                              // Avoid the following code block if ANSI but unfilled LazyMat blob
                              if ((!isNull) && (!isDataFeed)) {
                                  string s;

                                  if (isSqlVal) {
                                      if (value is SqlString) {
                                          s = ((SqlString)value).Value;
                                      }
                                      else {
                                          Debug.Assert(value is SqlChars, "Unknown value for Ansi datatype");
                                          s = new String(((SqlChars)value).Value);
                                      }
                                  }
                                  else {
                                      s = (string)value;
                                  }

                                  codePageByteSize = GetEncodingCharLength(s, actualSize, param.Offset, _defaultEncoding);
                              }

                              if (mt.IsPlp) {
                                  WriteShort(TdsEnums.SQL_USHORTVARMAXLEN, stateObj);
                              }
                              else {
                                  maxsize = (size > codePageByteSize) ? size : codePageByteSize;
                                  if (maxsize == 0) {
                                      // Yukon doesn't like 0 as MaxSize. Change it to 2 for unicode types (SQL9 - 682322)
                                      if (mt.IsNCharType)
                                          maxsize = 2;
                                      else
                                          maxsize = 1;
                                  }

                                  WriteParameterVarLen(mt, maxsize, false/*IsNull*/, stateObj);
                              }
                          }
                          else {
                              // If type timestamp - treat as fixed type and always send over timestamp length, which is 8.
                              // For fixed types, we either send null or fixed length for type length.  We want to match that
                              // behavior for timestamps.  However, in the case of null, we still must send 8 because if we
                              // send null we will not receive a output val.  You can send null for fixed types and still
                              // receive a output value, but not for variable types.  So, always send 8 for timestamp because
                              // while the user sees it as a fixed type, we are actually representing it as a bigbinary which
                              // is variable.
                              if (mt.SqlDbType == SqlDbType.Timestamp) {
                                  WriteParameterVarLen(mt, TdsEnums.TEXT_TIME_STAMP_LEN, false, stateObj);
                              }
                              else if (mt.SqlDbType == SqlDbType.Udt) {
                                  byte[] udtVal = null;
                                  Microsoft.SqlServer.Server.Format format = Microsoft.SqlServer.Server.Format.Native;

                                  Debug.Assert(_isYukon, "Invalid DataType UDT for non-Yukon or later server!");

                                  if (!isNull) {
                                      udtVal = _connHandler.Connection.GetBytes(value, out format, out maxsize);

                                      Debug.Assert(null != udtVal, "GetBytes returned null instance. Make sure that it always returns non-null value");
                                      size = udtVal.Length;

                                      //it may be legitimate, but we dont support it yet
                                      if (size < 0 || (size >= UInt16.MaxValue && maxsize != -1))
                                          throw new IndexOutOfRangeException();
                                  }

                                  //if this is NULL value, write special null value
                                  byte[] lenBytes = BitConverter.GetBytes((Int64)size);

                                  if (ADP.IsEmpty(param.UdtTypeName))
                                      throw SQL.MustSetUdtTypeNameForUdtParams();

                                  // Split the input name. TypeName is returned as single 3 part name during DeriveParameters.
                                  // NOTE: ParseUdtTypeName throws if format is incorrect
                                  String[] names = SqlParameter.ParseTypeName(param.UdtTypeName, true /* is UdtTypeName */);
                                  if (!ADP.IsEmpty(names[0]) && TdsEnums.MAX_SERVERNAME < names[0].Length) {
                                      throw ADP.ArgumentOutOfRange("names");
                                  }
                                  if (!ADP.IsEmpty(names[1]) && TdsEnums.MAX_SERVERNAME < names[names.Length - 2].Length) {
                                      throw ADP.ArgumentOutOfRange("names");
                                  }
                                  if (TdsEnums.MAX_SERVERNAME < names[2].Length) {
                                      throw ADP.ArgumentOutOfRange("names");
                                  }

                                  WriteUDTMetaData(value, names[0], names[1], names[2], stateObj);

                                  // 
                                  if (!isNull) {
                                      WriteUnsignedLong((ulong)udtVal.Length, stateObj); // PLP length
                                      if (udtVal.Length > 0) { // Only write chunk length if its value is greater than 0
                                          WriteInt(udtVal.Length, stateObj);                  // Chunk length
                                          stateObj.WriteByteArray(udtVal, udtVal.Length, 0); // Value
                                      }
                                      WriteInt(0, stateObj);                              // Terminator
                                  }
                                  else {
                                      WriteUnsignedLong(TdsEnums.SQL_PLP_NULL, stateObj); // PLP Null.
                                  }
                                  continue; // End of UDT - continue to next parameter.
                                  // 
                              }
                              else if (mt.IsPlp) {
                                  if (mt.SqlDbType != SqlDbType.Xml)
                                      WriteShort(TdsEnums.SQL_USHORTVARMAXLEN, stateObj);
                              }
                              else if ((!mt.IsVarTime) && (mt.SqlDbType != SqlDbType.Date)) {   // Time, Date, DateTime2, DateTimeoffset do not have the size written out
                                  maxsize = (size > actualSize) ? size : actualSize;
                                  if (maxsize == 0 && IsYukonOrNewer) {
                                      // Yukon doesn't like 0 as MaxSize. Change it to 2 for unicode types (SQL9 - 682322)
                                      if (mt.IsNCharType)
                                          maxsize = 2;
                                      else
                                          maxsize = 1;
                                  }

                                  WriteParameterVarLen(mt, maxsize, false/*IsNull*/, stateObj);
                              }
                          }

                          // scale and precision are only relevant for numeric and decimal types
                          if (mt.SqlDbType == SqlDbType.Decimal) {
                              if (0 == precision) {
                                  if (_isShiloh)
                                      stateObj.WriteByte(TdsEnums.DEFAULT_NUMERIC_PRECISION);
                                  else
                                      stateObj.WriteByte(TdsEnums.SPHINX_DEFAULT_NUMERIC_PRECISION);
                              }
                              else
                                  stateObj.WriteByte(precision);

                              stateObj.WriteByte(scale);
                          }
                          else if (mt.IsVarTime) {
                              stateObj.WriteByte(param.GetActualScale());
                          }

                          // write out collation or xml metadata

                          if (_isYukon && (mt.SqlDbType == SqlDbType.Xml)) {
                              if (((param.XmlSchemaCollectionDatabase != null) && (param.XmlSchemaCollectionDatabase != ADP.StrEmpty)) ||
                                  ((param.XmlSchemaCollectionOwningSchema != null) && (param.XmlSchemaCollectionOwningSchema != ADP.StrEmpty)) ||
                                  ((param.XmlSchemaCollectionName != null) && (param.XmlSchemaCollectionName != ADP.StrEmpty))) {
                                  stateObj.WriteByte(1);   //Schema present flag

                                  if ((param.XmlSchemaCollectionDatabase != null) && (param.XmlSchemaCollectionDatabase != ADP.StrEmpty)) {
                                      tempLen = (param.XmlSchemaCollectionDatabase).Length;
                                      stateObj.WriteByte((byte)(tempLen));
                                      WriteString(param.XmlSchemaCollectionDatabase, tempLen, 0, stateObj);
                                  }
                                  else {
                                      stateObj.WriteByte(0);       // No dbname
                                  }

                                  if ((param.XmlSchemaCollectionOwningSchema != null) && (param.XmlSchemaCollectionOwningSchema != ADP.StrEmpty)) {
                                      tempLen = (param.XmlSchemaCollectionOwningSchema).Length;
                                      stateObj.WriteByte((byte)(tempLen));
                                      WriteString(param.XmlSchemaCollectionOwningSchema, tempLen, 0, stateObj);
                                  }
                                  else {
                                      stateObj.WriteByte(0);      // no xml schema name
                                  }
                                  if ((param.XmlSchemaCollectionName != null) && (param.XmlSchemaCollectionName != ADP.StrEmpty)) {
                                      tempLen = (param.XmlSchemaCollectionName).Length;
                                      WriteShort((short)(tempLen), stateObj);
                                      WriteString(param.XmlSchemaCollectionName, tempLen, 0, stateObj);
                                  }
                                  else {
                                      WriteShort(0, stateObj);       // No xml schema collection name
                                  }

                              }
                              else {
                                  stateObj.WriteByte(0);       // No schema
                              }
                          }
                          else if (_isShiloh && mt.IsCharType) {
                              // if it is not supplied, simply write out our default collation, otherwise, write out the one attached to the parameter
                              SqlCollation outCollation = (param.Collation != null) ? param.Collation : _defaultCollation;
                              Debug.Assert(_defaultCollation != null, "_defaultCollation is null!");

                              WriteUnsignedInt(outCollation.info, stateObj);
                              stateObj.WriteByte(outCollation.sortId);
                          }

                          if (0 == codePageByteSize)
                              WriteParameterVarLen(mt, actualSize, isNull, stateObj, isDataFeed);
                          else
                              WriteParameterVarLen(mt, codePageByteSize, isNull, stateObj, isDataFeed);

                          Task writeParamTask = null;
                          // write the value now
                          if (!isNull) {
                              if (isSqlVal) {
                                  writeParamTask = WriteSqlValue(value, mt, actualSize, codePageByteSize, param.Offset, stateObj);
                              }
                              else {
                                  // for codePageEncoded types, WriteValue simply expects the number of characters
                                  // For plp types, we also need the encoded byte size
                                  writeParamTask = WriteValue(value, mt, isParameterEncrypted ? (byte)0 : param.GetActualScale(), actualSize, codePageByteSize, isParameterEncrypted ? 0 : param.Offset, stateObj, isParameterEncrypted ? 0 : param.Size, isDataFeed);
                              }
                          }

                          // Send encryption metadata for encrypted parameters.
                          if (isParameterEncrypted) {
                              writeParamTask = WriteEncryptionMetadata(writeParamTask, encryptedParameterInfoToWrite, stateObj);
                          }

                          if (!sync) {
                              if (writeParamTask == null) {
                                  writeParamTask = stateObj.WaitForAccumulatedWrites();                             
                              }

                              if (writeParamTask != null) {
                                  Task task = null;
                                  if (completion == null) {
                                      completion = new TaskCompletionSource<object>();
                                      task = completion.Task;
                                  }

                                  AsyncHelper.ContinueTask(writeParamTask, completion,
                                      () => TdsExecuteRPC(cmd, rpcArray, timeout, inSchema, notificationRequest, stateObj, isCommandProc, sync, completion,
                                                            startRpc: ii, startParam: i + 1),
                                      connectionToDoom: _connHandler,
                                      onFailure: exc => TdsExecuteRPC_OnFailure(exc, stateObj));

                                  // Take care of releasing the locks
                                  if (releaseConnectionLock) {
                                      task.ContinueWith(_ => {
                                          _connHandler._parserLock.Release();
                                      }, TaskScheduler.Default);
                                      releaseConnectionLock = false;
                                  }

                                  return task;
                              }
                          }
#if DEBUG
                          else {
                              Debug.Assert(writeParamTask == null, "Should not have a task when executing sync");
                          }
#endif
                      } // parameter for loop

                      // If this is not the last RPC we are sending, add the batch flag
                      if (ii < (rpcArray.Length - 1)) {
                          if (_isYukon) {
                              stateObj.WriteByte(TdsEnums.YUKON_RPCBATCHFLAG);

                          }
                          else {
                              stateObj.WriteByte(TdsEnums.SHILOH_RPCBATCHFLAG);
                          }
                      }
                  } // rpc for loop

                  Task execFlushTask = stateObj.ExecuteFlush();
                  Debug.Assert(!sync || execFlushTask == null, "Should not get a task when executing sync");
                  if (execFlushTask != null) {
                      Task task = null;

                      if (completion == null) {
                          completion = new TaskCompletionSource<object>();
                          task = completion.Task;
                      }

                      bool taskReleaseConnectionLock = releaseConnectionLock;
                      execFlushTask.ContinueWith(tsk => ExecuteFlushTaskCallback(tsk, stateObj, completion, taskReleaseConnectionLock), TaskScheduler.Default);
                      
                      // ExecuteFlushTaskCallback will take care of the locks for us
                      releaseConnectionLock = false;
                      
                      return task;
                  }                  
              }
              catch (Exception e) {
                  // 
                  if (!ADP.IsCatchableExceptionType(e)) {
                      throw;
                  }

                  FailureCleanup(stateObj, e);

                  throw;
              }
              FinalizeExecuteRPC(stateObj);
              if (completion != null) {
                  completion.SetResult(null);
              }
              return null;
          }
          catch (Exception e) {
              FinalizeExecuteRPC(stateObj);
              if (completion != null) {
                  completion.SetException(e);
                  return null;
              }
              else {
                  throw e;
              }
          }
          finally {
              Debug.Assert(firstCall || !releaseConnectionLock, "Shouldn't be releasing locks synchronously after the first call");
              if (releaseConnectionLock) {
                  _connHandler._parserLock.Release();
              }
          }
      }
        internal SqlDataReader TdsExecuteTransactionManagerRequest(byte[] buffer, TdsEnums.TransactionManagerRequestType request, string transactionName, TdsEnums.TransactionManagerIsolationLevel isoLevel, int timeout, SqlInternalTransaction transaction, TdsParserStateObject stateObj, bool isDelegateControlRequest)
        {
            SqlDataReader reader2;
            if ((TdsParserState.Broken == this.State) || (this.State == TdsParserState.Closed))
            {
                return null;
            }
            bool lockTaken = false;
            lock (this._connHandler)
            {
                try
                {
                    try
                    {
                        if (this._isYukon && !this.MARSOn)
                        {
                            Monitor.Enter(this._physicalStateObj, ref lockTaken);
                        }
                        if (!isDelegateControlRequest)
                        {
                            this._connHandler.CheckEnlistedTransactionBinding();
                        }
                        stateObj._outputMessageType = 14;
                        stateObj.SetTimeoutSeconds(timeout);
                        stateObj.SniContext = SniContext.Snix_Execute;
                        if (this._isYukon)
                        {
                            this.WriteMarsHeader(stateObj, this._currentTransaction);
                        }
                        this.WriteShort((short) request, stateObj);
                        bool flag = false;
                        switch (request)
                        {
                            case TdsEnums.TransactionManagerRequestType.GetDTCAddress:
                                this.WriteShort(0, stateObj);
                                flag = true;
                                goto Label_0193;

                            case TdsEnums.TransactionManagerRequestType.Propagate:
                                if (buffer == null)
                                {
                                    break;
                                }
                                this.WriteShort(buffer.Length, stateObj);
                                this.WriteByteArray(buffer, buffer.Length, 0, stateObj);
                                goto Label_0193;

                            case TdsEnums.TransactionManagerRequestType.Begin:
                                if (this._currentTransaction != transaction)
                                {
                                    this.PendingTransaction = transaction;
                                }
                                this.WriteByte((byte) isoLevel, stateObj);
                                this.WriteByte((byte) (transactionName.Length * 2), stateObj);
                                this.WriteString(transactionName, stateObj);
                                goto Label_0193;

                            case TdsEnums.TransactionManagerRequestType.Commit:
                                this.WriteByte(0, stateObj);
                                this.WriteByte(0, stateObj);
                                goto Label_0193;

                            case TdsEnums.TransactionManagerRequestType.Rollback:
                                this.WriteByte((byte) (transactionName.Length * 2), stateObj);
                                this.WriteString(transactionName, stateObj);
                                this.WriteByte(0, stateObj);
                                goto Label_0193;

                            case TdsEnums.TransactionManagerRequestType.Save:
                                this.WriteByte((byte) (transactionName.Length * 2), stateObj);
                                this.WriteString(transactionName, stateObj);
                                goto Label_0193;

                            default:
                                goto Label_0193;
                        }
                        this.WriteShort(0, stateObj);
                    Label_0193:
                        stateObj.WritePacket(1);
                        stateObj._pendingData = true;
                        SqlDataReader reader = null;
                        stateObj.SniContext = SniContext.Snix_Read;
                        if (flag)
                        {
                            reader = new SqlDataReader(null, CommandBehavior.Default);
                            reader.Bind(stateObj);
                            _SqlMetaDataSet metaData = reader.MetaData;
                        }
                        else
                        {
                            this.Run(RunBehavior.UntilDone, null, null, null, stateObj);
                        }
                        if ((request == TdsEnums.TransactionManagerRequestType.Begin) || (request == TdsEnums.TransactionManagerRequestType.Propagate))
                        {
                            if ((transaction != null) && (transaction.TransactionId == this._retainedTransactionId))
                            {
                                return reader;
                            }
                            this._retainedTransactionId = 0L;
                        }
                        return reader;
                    }
                    catch (Exception exception)
                    {
                        if (!ADP.IsCatchableExceptionType(exception))
                        {
                            throw;
                        }
                        this.FailureCleanup(stateObj, exception);
                        throw;
                    }
                    return reader2;
                }
                finally
                {
                    this._pendingTransaction = null;
                    if (lockTaken)
                    {
                        Monitor.Exit(this._physicalStateObj);
                    }
                }
            }
            return reader2;
        }
 internal void TdsExecuteSQLBatch(string text, int timeout, SqlNotificationRequest notificationRequest, TdsParserStateObject stateObj)
 {
     if ((TdsParserState.Broken != this.State) && (this.State != TdsParserState.Closed))
     {
         if (stateObj.BcpLock)
         {
             throw SQL.ConnectionLockedForBcpEvent();
         }
         bool lockTaken = false;
         lock (this._connHandler)
         {
             try
             {
                 if (this._isYukon && !this.MARSOn)
                 {
                     Monitor.Enter(this._physicalStateObj, ref lockTaken);
                 }
                 this._connHandler.CheckEnlistedTransactionBinding();
                 stateObj.SetTimeoutSeconds(timeout);
                 if (!this._fMARS && this._physicalStateObj.HasOpenResult)
                 {
                     Bid.Trace("<sc.TdsParser.TdsExecuteSQLBatch|ERR> Potential multi-threaded misuse of connection, non-MARs connection with an open result %d#\n", this.ObjectID);
                 }
                 stateObj.SniContext = SniContext.Snix_Execute;
                 if (this._isYukon)
                 {
                     this.WriteMarsHeader(stateObj, this.CurrentTransaction);
                     if (notificationRequest != null)
                     {
                         this.WriteQueryNotificationHeader(notificationRequest, stateObj);
                     }
                 }
                 stateObj._outputMessageType = 1;
                 this.WriteString(text, text.Length, 0, stateObj);
                 stateObj.ExecuteFlush();
                 stateObj.SniContext = SniContext.Snix_Read;
             }
             catch (Exception exception)
             {
                 if (!ADP.IsCatchableExceptionType(exception))
                 {
                     throw;
                 }
                 this.FailureCleanup(stateObj, exception);
                 throw;
             }
             finally
             {
                 if (lockTaken)
                 {
                     Monitor.Exit(this._physicalStateObj);
                 }
             }
         }
     }
 }
 internal void TdsExecuteRPC(_SqlRPC[] rpcArray, int timeout, bool inSchema, SqlNotificationRequest notificationRequest, TdsParserStateObject stateObj, bool isCommandProc)
 {
     if ((TdsParserState.Broken != this.State) && (this.State != TdsParserState.Closed))
     {
         _SqlRPC lrpc = null;
         bool lockTaken = false;
         lock (this._connHandler)
         {
             try
             {
                 if (this._isYukon && !this.MARSOn)
                 {
                     Monitor.Enter(this._physicalStateObj, ref lockTaken);
                 }
                 this._connHandler.CheckEnlistedTransactionBinding();
                 stateObj.SetTimeoutSeconds(timeout);
                 if (!this._fMARS && this._physicalStateObj.HasOpenResult)
                 {
                     Bid.Trace("<sc.TdsParser.TdsExecuteRPC|ERR> Potential multi-threaded misuse of connection, non-MARs connection with an open result %d#\n", this.ObjectID);
                 }
                 stateObj.SniContext = SniContext.Snix_Execute;
                 if (this._isYukon)
                 {
                     this.WriteMarsHeader(stateObj, this.CurrentTransaction);
                     if (notificationRequest != null)
                     {
                         this.WriteQueryNotificationHeader(notificationRequest, stateObj);
                     }
                 }
                 stateObj._outputMessageType = 3;
                 for (int i = 0; i < rpcArray.Length; i++)
                 {
                     int length;
                     lrpc = rpcArray[i];
                     if ((lrpc.ProcID != 0) && this._isShiloh)
                     {
                         this.WriteShort(0xffff, stateObj);
                         this.WriteShort((short) lrpc.ProcID, stateObj);
                     }
                     else
                     {
                         length = lrpc.rpcName.Length;
                         this.WriteShort(length, stateObj);
                         this.WriteString(lrpc.rpcName, length, 0, stateObj);
                     }
                     this.WriteShort((short) lrpc.options, stateObj);
                     SqlParameter[] parameters = lrpc.parameters;
                     for (int j = 0; j < parameters.Length; j++)
                     {
                         SqlParameter param = parameters[j];
                         if (param == null)
                         {
                             break;
                         }
                         param.Validate(j, isCommandProc);
                         MetaType internalMetaType = param.InternalMetaType;
                         if (internalMetaType.IsNewKatmaiType)
                         {
                             this.WriteSmiParameter(param, j, 0 != (lrpc.paramoptions[j] & 2), stateObj);
                         }
                         else
                         {
                             bool flag2;
                             if (((!this._isShiloh && !internalMetaType.Is70Supported) || (!this._isYukon && !internalMetaType.Is80Supported)) || (!this._isKatmai && !internalMetaType.Is90Supported))
                             {
                                 throw ADP.VersionDoesNotSupportDataType(internalMetaType.TypeName);
                             }
                             object coercedValue = null;
                             if (param.Direction == ParameterDirection.Output)
                             {
                                 bool paramaterIsSqlType = param.ParamaterIsSqlType;
                                 param.Value = null;
                                 coercedValue = null;
                                 param.ParamaterIsSqlType = paramaterIsSqlType;
                             }
                             else
                             {
                                 coercedValue = param.GetCoercedValue();
                             }
                             bool isNull = ADP.IsNull(coercedValue, out flag2);
                             string parameterNameFixed = param.ParameterNameFixed;
                             this.WriteParameterName(parameterNameFixed, stateObj);
                             this.WriteByte(lrpc.paramoptions[j], stateObj);
                             this.WriteByte(internalMetaType.NullableType, stateObj);
                             if (internalMetaType.TDSType == 0x62)
                             {
                                 this.WriteSqlVariantValue(flag2 ? MetaType.GetComValueFromSqlVariant(coercedValue) : coercedValue, param.GetActualSize(), param.Offset, stateObj);
                             }
                             else
                             {
                                 int actualSize;
                                 int num4 = internalMetaType.IsSizeInCharacters ? (param.GetParameterSize() * 2) : param.GetParameterSize();
                                 if (internalMetaType.TDSType != 240)
                                 {
                                     actualSize = param.GetActualSize();
                                 }
                                 else
                                 {
                                     actualSize = 0;
                                 }
                                 int size = 0;
                                 int num = 0;
                                 if (internalMetaType.IsAnsiType)
                                 {
                                     if (!isNull)
                                     {
                                         string str;
                                         if (flag2)
                                         {
                                             if (coercedValue is SqlString)
                                             {
                                                 SqlString str3 = (SqlString) coercedValue;
                                                 str = str3.Value;
                                             }
                                             else
                                             {
                                                 str = new string(((SqlChars) coercedValue).Value);
                                             }
                                         }
                                         else
                                         {
                                             str = (string) coercedValue;
                                         }
                                         size = this.GetEncodingCharLength(str, actualSize, param.Offset, this._defaultEncoding);
                                     }
                                     if (internalMetaType.IsPlp)
                                     {
                                         this.WriteShort(0xffff, stateObj);
                                     }
                                     else
                                     {
                                         num = (num4 > size) ? num4 : size;
                                         if (num == 0)
                                         {
                                             if (internalMetaType.IsNCharType)
                                             {
                                                 num = 2;
                                             }
                                             else
                                             {
                                                 num = 1;
                                             }
                                         }
                                         this.WriteParameterVarLen(internalMetaType, num, false, stateObj);
                                     }
                                 }
                                 else if (internalMetaType.SqlDbType == SqlDbType.Timestamp)
                                 {
                                     this.WriteParameterVarLen(internalMetaType, 8, false, stateObj);
                                 }
                                 else
                                 {
                                     if (internalMetaType.SqlDbType == SqlDbType.Udt)
                                     {
                                         byte[] b = null;
                                         bool flag3 = ADP.IsNull(coercedValue);
                                         Format native = Format.Native;
                                         if (!flag3)
                                         {
                                             b = this._connHandler.Connection.GetBytes(coercedValue, out native, out num);
                                             num4 = b.Length;
                                             if ((num4 < 0) || ((num4 >= 0xffff) && (num != -1)))
                                             {
                                                 throw new IndexOutOfRangeException();
                                             }
                                         }
                                         BitConverter.GetBytes((long) num4);
                                         if (ADP.IsEmpty(param.UdtTypeName))
                                         {
                                             throw SQL.MustSetUdtTypeNameForUdtParams();
                                         }
                                         string[] strArray = SqlParameter.ParseTypeName(param.UdtTypeName, true);
                                         if (!ADP.IsEmpty(strArray[0]) && (0xff < strArray[0].Length))
                                         {
                                             throw ADP.ArgumentOutOfRange("names");
                                         }
                                         if (!ADP.IsEmpty(strArray[1]) && (0xff < strArray[strArray.Length - 2].Length))
                                         {
                                             throw ADP.ArgumentOutOfRange("names");
                                         }
                                         if (0xff < strArray[2].Length)
                                         {
                                             throw ADP.ArgumentOutOfRange("names");
                                         }
                                         this.WriteUDTMetaData(coercedValue, strArray[0], strArray[1], strArray[2], stateObj);
                                         if (!flag3)
                                         {
                                             this.WriteUnsignedLong((ulong) b.Length, stateObj);
                                             if (b.Length > 0)
                                             {
                                                 this.WriteInt(b.Length, stateObj);
                                                 this.WriteByteArray(b, b.Length, 0, stateObj);
                                             }
                                             this.WriteInt(0, stateObj);
                                         }
                                         else
                                         {
                                             this.WriteUnsignedLong(ulong.MaxValue, stateObj);
                                         }
                                         goto Label_080A;
                                     }
                                     if (internalMetaType.IsPlp)
                                     {
                                         if (internalMetaType.SqlDbType != SqlDbType.Xml)
                                         {
                                             this.WriteShort(0xffff, stateObj);
                                         }
                                     }
                                     else if (!internalMetaType.IsVarTime && (internalMetaType.SqlDbType != SqlDbType.Date))
                                     {
                                         num = (num4 > actualSize) ? num4 : actualSize;
                                         if ((num == 0) && this.IsYukonOrNewer)
                                         {
                                             if (internalMetaType.IsNCharType)
                                             {
                                                 num = 2;
                                             }
                                             else
                                             {
                                                 num = 1;
                                             }
                                         }
                                         this.WriteParameterVarLen(internalMetaType, num, false, stateObj);
                                     }
                                 }
                                 if (internalMetaType.SqlDbType == SqlDbType.Decimal)
                                 {
                                     byte actualPrecision = param.GetActualPrecision();
                                     byte actualScale = param.GetActualScale();
                                     if (actualPrecision > 0x26)
                                     {
                                         throw SQL.PrecisionValueOutOfRange(actualPrecision);
                                     }
                                     if (!isNull)
                                     {
                                         if (flag2)
                                         {
                                             coercedValue = AdjustSqlDecimalScale((SqlDecimal) coercedValue, actualScale);
                                             if (actualPrecision != 0)
                                             {
                                                 SqlDecimal num11 = (SqlDecimal) coercedValue;
                                                 if (actualPrecision < num11.Precision)
                                                 {
                                                     throw ADP.ParameterValueOutOfRange((SqlDecimal) coercedValue);
                                                 }
                                             }
                                         }
                                         else
                                         {
                                             coercedValue = AdjustDecimalScale((decimal) coercedValue, actualScale);
                                             SqlDecimal num10 = new SqlDecimal((decimal) coercedValue);
                                             if ((actualPrecision != 0) && (actualPrecision < num10.Precision))
                                             {
                                                 throw ADP.ParameterValueOutOfRange((decimal) coercedValue);
                                             }
                                         }
                                     }
                                     if (actualPrecision == 0)
                                     {
                                         if (this._isShiloh)
                                         {
                                             this.WriteByte(0x1d, stateObj);
                                         }
                                         else
                                         {
                                             this.WriteByte(0x1c, stateObj);
                                         }
                                     }
                                     else
                                     {
                                         this.WriteByte(actualPrecision, stateObj);
                                     }
                                     this.WriteByte(actualScale, stateObj);
                                 }
                                 else if (internalMetaType.IsVarTime)
                                 {
                                     this.WriteByte(param.GetActualScale(), stateObj);
                                 }
                                 if (this._isYukon && (internalMetaType.SqlDbType == SqlDbType.Xml))
                                 {
                                     if ((((param.XmlSchemaCollectionDatabase != null) && (param.XmlSchemaCollectionDatabase != ADP.StrEmpty)) || ((param.XmlSchemaCollectionOwningSchema != null) && (param.XmlSchemaCollectionOwningSchema != ADP.StrEmpty))) || ((param.XmlSchemaCollectionName != null) && (param.XmlSchemaCollectionName != ADP.StrEmpty)))
                                     {
                                         this.WriteByte(1, stateObj);
                                         if ((param.XmlSchemaCollectionDatabase != null) && (param.XmlSchemaCollectionDatabase != ADP.StrEmpty))
                                         {
                                             length = param.XmlSchemaCollectionDatabase.Length;
                                             this.WriteByte((byte) length, stateObj);
                                             this.WriteString(param.XmlSchemaCollectionDatabase, length, 0, stateObj);
                                         }
                                         else
                                         {
                                             this.WriteByte(0, stateObj);
                                         }
                                         if ((param.XmlSchemaCollectionOwningSchema != null) && (param.XmlSchemaCollectionOwningSchema != ADP.StrEmpty))
                                         {
                                             length = param.XmlSchemaCollectionOwningSchema.Length;
                                             this.WriteByte((byte) length, stateObj);
                                             this.WriteString(param.XmlSchemaCollectionOwningSchema, length, 0, stateObj);
                                         }
                                         else
                                         {
                                             this.WriteByte(0, stateObj);
                                         }
                                         if ((param.XmlSchemaCollectionName != null) && (param.XmlSchemaCollectionName != ADP.StrEmpty))
                                         {
                                             length = param.XmlSchemaCollectionName.Length;
                                             this.WriteShort((short) length, stateObj);
                                             this.WriteString(param.XmlSchemaCollectionName, length, 0, stateObj);
                                         }
                                         else
                                         {
                                             this.WriteShort(0, stateObj);
                                         }
                                     }
                                     else
                                     {
                                         this.WriteByte(0, stateObj);
                                     }
                                 }
                                 else if (this._isShiloh && internalMetaType.IsCharType)
                                 {
                                     SqlCollation collation = (param.Collation != null) ? param.Collation : this._defaultCollation;
                                     this.WriteUnsignedInt(collation.info, stateObj);
                                     this.WriteByte(collation.sortId, stateObj);
                                 }
                                 if (size == 0)
                                 {
                                     this.WriteParameterVarLen(internalMetaType, actualSize, isNull, stateObj);
                                 }
                                 else
                                 {
                                     this.WriteParameterVarLen(internalMetaType, size, isNull, stateObj);
                                 }
                                 if (!isNull)
                                 {
                                     if (flag2)
                                     {
                                         this.WriteSqlValue(coercedValue, internalMetaType, actualSize, size, param.Offset, stateObj);
                                     }
                                     else
                                     {
                                         this.WriteValue(coercedValue, internalMetaType, param.GetActualScale(), actualSize, size, param.Offset, stateObj);
                                     }
                                 }
                             Label_080A:;
                             }
                         }
                     }
                     if (i < (rpcArray.Length - 1))
                     {
                         if (this._isYukon)
                         {
                             this.WriteByte(0xff, stateObj);
                         }
                         else
                         {
                             this.WriteByte(0x80, stateObj);
                         }
                     }
                 }
                 stateObj.ExecuteFlush();
                 stateObj.SniContext = SniContext.Snix_Read;
             }
             catch (Exception exception)
             {
                 if (!ADP.IsCatchableExceptionType(exception))
                 {
                     throw;
                 }
                 this.FailureCleanup(stateObj, exception);
                 throw;
             }
             finally
             {
                 if (lockTaken)
                 {
                     Monitor.Exit(this._physicalStateObj);
                 }
             }
         }
     }
 }