internal void ThrowUnsupportedCollationEncountered(TdsParserStateObject stateObj) { stateObj.AddError(new SqlError(0, 0, TdsEnums.MIN_ERROR_CLASS, _server, SQLMessage.CultureIdError(), "", 0)); if (null != stateObj) { DrainData(stateObj); stateObj._pendingData = false; } ThrowExceptionAndWarning(stateObj); }
private bool TryProcessLoginAck(TdsParserStateObject stateObj, out SqlLoginAck sqlLoginAck) { SqlLoginAck a = new SqlLoginAck(); sqlLoginAck = null; // read past interface type and version if (!stateObj.TrySkipBytes(1)) { return false; } byte[] b = new byte[TdsEnums.VERSION_SIZE]; if (!stateObj.TryReadByteArray(b, 0, b.Length)) { return false; } a.tdsVersion = (UInt32)((((((b[0] << 8) | b[1]) << 8) | b[2]) << 8) | b[3]); // bytes are in motorola order (high byte first) UInt32 majorMinor = a.tdsVersion & 0xff00ffff; UInt32 increment = (a.tdsVersion >> 16) & 0xff; // Server responds: // 0x07000000 -> Sphinx // Notice server response format is different for bwd compat // 0x07010000 -> Shiloh RTM // Notice server response format is different for bwd compat // 0x71000001 -> Shiloh SP1 // 0x72xx0002 -> Yukon RTM // information provided by S. Ashwin switch (majorMinor) { case TdsEnums.YUKON_MAJOR << 24 | TdsEnums.YUKON_RTM_MINOR: // Yukon if (increment != TdsEnums.YUKON_INCREMENT) { throw SQL.InvalidTDSVersion(); } break; case TdsEnums.KATMAI_MAJOR << 24 | TdsEnums.KATMAI_MINOR: if (increment != TdsEnums.KATMAI_INCREMENT) { throw SQL.InvalidTDSVersion(); } _isKatmai = true; break; case TdsEnums.DENALI_MAJOR << 24 | TdsEnums.DENALI_MINOR: if (increment != TdsEnums.DENALI_INCREMENT) { throw SQL.InvalidTDSVersion(); } _isDenali = true; break; default: throw SQL.InvalidTDSVersion(); } _isKatmai |= _isDenali; stateObj._outBytesUsed = stateObj._outputHeaderLen; byte len; if (!stateObj.TryReadByte(out len)) { return false; } if (!stateObj.TrySkipBytes(len * ADP.CharSize)) { return false; } if (!stateObj.TryReadByte(out a.majorVersion)) { return false; } if (!stateObj.TryReadByte(out a.minorVersion)) { return false; } byte buildNumHi, buildNumLo; if (!stateObj.TryReadByte(out buildNumHi)) { return false; } if (!stateObj.TryReadByte(out buildNumLo)) { return false; } a.buildNum = (short)((buildNumHi << 8) + buildNumLo); Debug.Assert(_state == TdsParserState.OpenNotLoggedIn, "ProcessLoginAck called with state not TdsParserState.OpenNotLoggedIn"); _state = TdsParserState.OpenLoggedIn; { if (_fMARS) { _resetConnectionEvent = new AutoResetEvent(true); } } // Fail if SSE UserInstance and we have not received this info. if (_connHandler.ConnectionOptions.UserInstance && ADP.IsEmpty(_connHandler.InstanceName)) { stateObj.AddError(new SqlError(0, 0, TdsEnums.FATAL_ERROR_CLASS, Server, SQLMessage.UserInstanceFailure(), "", 0)); ThrowExceptionAndWarning(stateObj); } sqlLoginAck = a; return true; }
// Main parse loop for the top-level tds tokens, calls back into the I*Handler interfaces internal bool TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, out bool dataReady) { Debug.Assert((SniContext.Undefined != stateObj.SniContext) && // SniContext must not be Undefined ((stateObj._attentionSent) || ((SniContext.Snix_Execute != stateObj.SniContext) && (SniContext.Snix_SendRows != stateObj.SniContext))), // SniContext should not be Execute or SendRows unless attention was sent (and, therefore, we are looking for an ACK) String.Format("Unexpected SniContext on call to TryRun; SniContext={0}", stateObj.SniContext)); if (TdsParserState.Broken == State || TdsParserState.Closed == State) { dataReady = true; return true; // Just in case this is called in a loop, expecting data to be returned. } dataReady = false; do { // If there is data ready, but we didn't exit the loop, then something is wrong Debug.Assert(!dataReady, "dataReady not expected - did we forget to skip the row?"); if (stateObj._internalTimeout) { runBehavior = RunBehavior.Attention; } if (TdsParserState.Broken == State || TdsParserState.Closed == State) break; // jump out of the loop if the state is already broken or closed. if (!stateObj._accumulateInfoEvents && (stateObj._pendingInfoEvents != null)) { if (RunBehavior.Clean != (RunBehavior.Clean & runBehavior)) { SqlConnection connection = null; if (_connHandler != null) connection = _connHandler.Connection; // SqlInternalConnection holds the user connection object as a weak ref // We are omitting checks for error.Class in the code below (see processing of INFO) since we know (and assert) that error class // error.Class < TdsEnums.MIN_ERROR_CLASS for info message. // Also we know that TdsEnums.MIN_ERROR_CLASS<TdsEnums.MAX_USER_CORRECTABLE_ERROR_CLASS if ((connection != null) && connection.FireInfoMessageEventOnUserErrors) { foreach (SqlError error in stateObj._pendingInfoEvents) FireInfoMessageEvent(connection, stateObj, error); } else foreach (SqlError error in stateObj._pendingInfoEvents) stateObj.AddWarning(error); } stateObj._pendingInfoEvents = null; } byte token; if (!stateObj.TryReadByte(out token)) { return false; } if (!IsValidTdsToken(token)) { Debug.Assert(false, String.Format((IFormatProvider)null, "unexpected token; token = {0,-2:X2}", token)); _state = TdsParserState.Broken; _connHandler.BreakConnection(); throw SQL.ParsingError(); } int tokenLength; if (!TryGetTokenLength(token, stateObj, out tokenLength)) { return false; } switch (token) { case TdsEnums.SQLERROR: case TdsEnums.SQLINFO: { if (token == TdsEnums.SQLERROR) { stateObj._errorTokenReceived = true; // Keep track of the fact error token was received - for Done processing. } SqlError error; if (!TryProcessError(token, stateObj, out error)) { return false; } if (token == TdsEnums.SQLINFO && stateObj._accumulateInfoEvents) { Debug.Assert(error.Class < TdsEnums.MIN_ERROR_CLASS, "INFO with class > TdsEnums.MIN_ERROR_CLASS"); if (stateObj._pendingInfoEvents == null) stateObj._pendingInfoEvents = new List<SqlError>(); stateObj._pendingInfoEvents.Add(error); stateObj._syncOverAsync = true; break; } if (RunBehavior.Clean != (RunBehavior.Clean & runBehavior)) { // If FireInfoMessageEventOnUserErrors is true, we have to fire event without waiting. // Otherwise we can go ahead and add it to errors/warnings collection. SqlConnection connection = null; if (_connHandler != null) connection = _connHandler.Connection; // SqlInternalConnection holds the user connection object as a weak ref if ((connection != null) && (connection.FireInfoMessageEventOnUserErrors == true) && (error.Class <= TdsEnums.MAX_USER_CORRECTABLE_ERROR_CLASS)) { // Fire SqlInfoMessage here FireInfoMessageEvent(connection, stateObj, error); } else { // insert error/info into the appropriate exception - warning if info, exception if error if (error.Class < TdsEnums.MIN_ERROR_CLASS) { stateObj.AddWarning(error); } else if (error.Class < TdsEnums.FATAL_ERROR_CLASS) { // Continue results processing for all non-fatal errors (<20) stateObj.AddError(error); // Add it to collection - but do NOT change run behavior UNLESS // we are in an ExecuteReader call - at which time we will be throwing // anyways so we need to consume all errors. This is not the case // if we have already given out a reader. If we have already given out // a reader we need to throw the error but not halt further processing. We used to // halt processing. if (null != dataStream) { if (!dataStream.IsInitialized) { runBehavior = RunBehavior.UntilDone; } } } else { stateObj.AddError(error); // Else we have a fatal error and we need to change the behavior // since we want the complete error information in the exception. // Besides - no further results will be received. runBehavior = RunBehavior.UntilDone; } } } else if (error.Class >= TdsEnums.FATAL_ERROR_CLASS) { stateObj.AddError(error); } break; } case TdsEnums.SQLCOLINFO: { if (null != dataStream) { _SqlMetaDataSet metaDataSet; if (!TryProcessColInfo(dataStream.MetaData, dataStream, stateObj, out metaDataSet)) { return false; } if (!dataStream.TrySetMetaData(metaDataSet, false)) { return false; } dataStream.BrowseModeInfoConsumed = true; } else { // no dataStream if (!stateObj.TrySkipBytes(tokenLength)) { return false; } } break; } case TdsEnums.SQLDONE: case TdsEnums.SQLDONEPROC: case TdsEnums.SQLDONEINPROC: { // RunBehavior can be modified if (!TryProcessDone(cmdHandler, dataStream, ref runBehavior, stateObj)) { return false; } if ((token == TdsEnums.SQLDONEPROC) && (cmdHandler != null)) { cmdHandler.OnDoneProc(); } break; } case TdsEnums.SQLORDER: { // don't do anything with the order token so read off the pipe if (!stateObj.TrySkipBytes(tokenLength)) { return false; } break; } case TdsEnums.SQLALTMETADATA: { stateObj.CloneCleanupAltMetaDataSetArray(); if (stateObj._cleanupAltMetaDataSetArray == null) { // create object on demand (lazy creation) stateObj._cleanupAltMetaDataSetArray = new _SqlMetaDataSetCollection(); } _SqlMetaDataSet cleanupAltMetaDataSet; if (!TryProcessAltMetaData(tokenLength, stateObj, out cleanupAltMetaDataSet)) { return false; } stateObj._cleanupAltMetaDataSetArray.SetAltMetaData(cleanupAltMetaDataSet); if (null != dataStream) { byte metadataConsumedByte; if (!stateObj.TryPeekByte(out metadataConsumedByte)) { return false; } if (!dataStream.TrySetAltMetaDataSet(cleanupAltMetaDataSet, (TdsEnums.SQLALTMETADATA != metadataConsumedByte))) { return false; } } break; } case TdsEnums.SQLALTROW: { if (!stateObj.TryStartNewRow(isNullCompressed: false)) { // altrows are not currently null compressed return false; } // read will call run until dataReady. Must not read any data if returnimmetiately set if (RunBehavior.ReturnImmediately != (RunBehavior.ReturnImmediately & runBehavior)) { ushort altRowId; if (!stateObj.TryReadUInt16(out altRowId)) { // get altRowId return false; } if (!TrySkipRow(stateObj._cleanupAltMetaDataSetArray.GetAltMetaData(altRowId), stateObj)) { // skip altRow return false; } } else { dataReady = true; } break; } case TdsEnums.SQLENVCHANGE: { // ENVCHANGE must be processed synchronously (since it can modify the state of many objects) stateObj._syncOverAsync = true; SqlEnvChange[] env; if (!TryProcessEnvChange(tokenLength, stateObj, out env)) { return false; } for (int ii = 0; ii < env.Length; ii++) { if (env[ii] != null && !this.Connection.IgnoreEnvChange) { switch (env[ii].type) { case TdsEnums.ENV_BEGINTRAN: // When we get notification from the server of a new // transaction, we move any pending transaction over to // the current transaction, then we store the token in it. // if there isn't a pending transaction, then it's either // a TSQL transaction or a distributed transaction. Debug.Assert(null == _currentTransaction, "non-null current transaction with an ENV Change"); _currentTransaction = _pendingTransaction; _pendingTransaction = null; if (null != _currentTransaction) { _currentTransaction.TransactionId = env[ii].newLongValue; // this is defined as a ULongLong in the server and in the TDS Spec. } else { TransactionType transactionType = TransactionType.LocalFromTSQL; _currentTransaction = new SqlInternalTransaction(_connHandler, transactionType, null, env[ii].newLongValue); } if (null != _statistics && !_statisticsIsInTransaction) { _statistics.SafeIncrement(ref _statistics._transactions); } _statisticsIsInTransaction = true; break; case TdsEnums.ENV_COMMITTRAN: // SQLHOT 483 // Must clear the retain id if the server-side transaction ends by anything other // than rollback. goto case TdsEnums.ENV_ROLLBACKTRAN; case TdsEnums.ENV_ROLLBACKTRAN: // When we get notification of a completed transaction // we null out the current transaction. if (null != _currentTransaction) { #if DEBUG // Check null for case where Begin and Rollback obtained in the same message. if (SqlInternalTransaction.NullTransactionId != _currentTransaction.TransactionId) { Debug.Assert(_currentTransaction.TransactionId != env[ii].newLongValue, "transaction id's are not equal!"); } #endif if (TdsEnums.ENV_COMMITTRAN == env[ii].type) { _currentTransaction.Completed(TransactionState.Committed); } else if (TdsEnums.ENV_ROLLBACKTRAN == env[ii].type) { // Hold onto transaction id if distributed tran is rolled back. This must // be sent to the server on subsequent executions even though the transaction // is considered to be rolled back. _currentTransaction.Completed(TransactionState.Aborted); } else { _currentTransaction.Completed(TransactionState.Unknown); } _currentTransaction = null; } _statisticsIsInTransaction = false; break; case TdsEnums.ENV_ENLISTDTC: case TdsEnums.ENV_DEFECTDTC: case TdsEnums.ENV_TRANSACTIONENDED: Debug.Assert(false, "Should have thrown if DTC token encountered"); break; default: _connHandler.OnEnvChange(env[ii]); break; } } } break; } case TdsEnums.SQLLOGINACK: { SqlLoginAck ack; if (!TryProcessLoginAck(stateObj, out ack)) { return false; } _connHandler.OnLoginAck(ack); break; } case TdsEnums.SQLFEATUREEXTACK: { if (!TryProcessFeatureExtAck(stateObj)) { return false; } break; } case TdsEnums.SQLSESSIONSTATE: { if (!TryProcessSessionState(stateObj, tokenLength, _connHandler._currentSessionData)) { return false; } break; } case TdsEnums.SQLCOLMETADATA: { if (tokenLength != TdsEnums.VARNULL) { _SqlMetaDataSet metadata; if (!TryProcessMetaData(tokenLength, stateObj, out metadata)) { return false; } stateObj._cleanupMetaData = metadata; } else { if (cmdHandler != null) { stateObj._cleanupMetaData = cmdHandler.MetaData; } } if (null != dataStream) { byte peekedToken; if (!stateObj.TryPeekByte(out peekedToken)) { // temporarily cache next byte return false; } if (!dataStream.TrySetMetaData(stateObj._cleanupMetaData, (TdsEnums.SQLTABNAME == peekedToken || TdsEnums.SQLCOLINFO == peekedToken))) { return false; } } else if (null != bulkCopyHandler) { bulkCopyHandler.SetMetaData(stateObj._cleanupMetaData); } break; } case TdsEnums.SQLROW: case TdsEnums.SQLNBCROW: { Debug.Assert(stateObj._cleanupMetaData != null, "Reading a row, but the metadata is null"); if (token == TdsEnums.SQLNBCROW) { if (!stateObj.TryStartNewRow(isNullCompressed: true, nullBitmapColumnsCount: stateObj._cleanupMetaData.Length)) { return false; } } else { if (!stateObj.TryStartNewRow(isNullCompressed: false)) { return false; } } if (null != bulkCopyHandler) { if (!TryProcessRow(stateObj._cleanupMetaData, bulkCopyHandler.CreateRowBuffer(), bulkCopyHandler.CreateIndexMap(), stateObj)) { return false; } } else if (RunBehavior.ReturnImmediately != (RunBehavior.ReturnImmediately & runBehavior)) { if (!TrySkipRow(stateObj._cleanupMetaData, stateObj)) { // skip rows return false; } } else { dataReady = true; } if (_statistics != null) { _statistics.WaitForDoneAfterRow = true; } break; } case TdsEnums.SQLRETURNSTATUS: int status; if (!stateObj.TryReadInt32(out status)) { return false; } if (cmdHandler != null) { cmdHandler.OnReturnStatus(status); } break; case TdsEnums.SQLRETURNVALUE: { SqlReturnValue returnValue; if (!TryProcessReturnValue(tokenLength, stateObj, out returnValue)) { return false; } if (cmdHandler != null) { cmdHandler.OnReturnValue(returnValue); } break; } case TdsEnums.SQLSSPI: { // token length is length of SSPI data - call ProcessSSPI with it Debug.Assert(stateObj._syncOverAsync, "ProcessSSPI does not support retry, do not attempt asynchronously"); stateObj._syncOverAsync = true; ProcessSSPI(tokenLength); break; } case TdsEnums.SQLTABNAME: { { if (!stateObj.TrySkipBytes(tokenLength)) { return false; } } break; } default: Debug.Assert(false, "Unhandled token: " + token.ToString(CultureInfo.InvariantCulture)); break; } Debug.Assert(stateObj._pendingData || !dataReady, "dataReady is set, but there is no pending data"); } // Loop while data pending & runbehavior not return immediately, OR // if in attention case, loop while no more pending data & attention has not yet been // received. while ((stateObj._pendingData && (RunBehavior.ReturnImmediately != (RunBehavior.ReturnImmediately & runBehavior))) || (!stateObj._pendingData && stateObj._attentionSent && !stateObj._attentionReceived)); #if DEBUG if ((stateObj._pendingData) && (!dataReady)) { byte token; if (!stateObj.TryPeekByte(out token)) { return false; } Debug.Assert(IsValidTdsToken(token), string.Format("DataReady is false, but next token is not valid: {0,-2:X2}", token)); } #endif if (!stateObj._pendingData) { if (null != CurrentTransaction) { CurrentTransaction.Activate(); } } // if we recieved an attention (but this thread didn't send it) then // we throw an Operation Cancelled error if (stateObj._attentionReceived) { // Dev11 #344723: SqlClient stress hang System_Data!Tcp::ReadSync via a call to SqlDataReader::Close // Spin until SendAttention has cleared _attentionSending, this prevents a race condition between receiving the attention ACK and setting _attentionSent SpinWait.SpinUntil(() => !stateObj._attentionSending); Debug.Assert(stateObj._attentionSent, "Attention ACK has been received without attention sent"); if (stateObj._attentionSent) { // Reset attention state. stateObj._attentionSent = false; stateObj._attentionReceived = false; if (RunBehavior.Clean != (RunBehavior.Clean & runBehavior) && !stateObj._internalTimeout) { // Add attention error to collection - if not RunBehavior.Clean! stateObj.AddError(new SqlError(0, 0, TdsEnums.MIN_ERROR_CLASS, _server, SQLMessage.OperationCancelled(), "", 0)); } } } if (stateObj.HasErrorOrWarning) { ThrowExceptionAndWarning(stateObj); } return true; }
private bool TryProcessDone(SqlCommand cmd, SqlDataReader reader, ref RunBehavior run, TdsParserStateObject stateObj) { ushort curCmd; ushort status; int count; // Can't retry TryProcessDone stateObj._syncOverAsync = true; // status // command // rowcount (valid only if DONE_COUNT bit is set) if (!stateObj.TryReadUInt16(out status)) { return false; } if (!stateObj.TryReadUInt16(out curCmd)) { return false; } long longCount; if (!stateObj.TryReadInt64(out longCount)) { return false; } count = (int)longCount; // We get a done token with the attention bit set if (TdsEnums.DONE_ATTN == (status & TdsEnums.DONE_ATTN)) { Debug.Assert(TdsEnums.DONE_MORE != (status & TdsEnums.DONE_MORE), "Not expecting DONE_MORE when receiving DONE_ATTN"); Debug.Assert(stateObj._attentionSent, "Received attention done without sending one!"); stateObj._attentionReceived = true; Debug.Assert(stateObj._inBytesUsed == stateObj._inBytesRead && stateObj._inBytesPacket == 0, "DONE_ATTN received with more data left on wire"); } if ((null != cmd) && (TdsEnums.DONE_COUNT == (status & TdsEnums.DONE_COUNT))) { if (curCmd != TdsEnums.SELECT) { cmd.InternalRecordsAffected = count; } // Skip the bogus DONE counts sent by the server if (stateObj._receivedColMetaData || (curCmd != TdsEnums.SELECT)) { cmd.OnStatementCompleted(count); } } stateObj._receivedColMetaData = false; // Surface exception for DONE_ERROR in the case we did not receive an error token // in the stream, but an error occurred. In these cases, we throw a general server error. The // situations where this can occur are: an invalid buffer received from client, login error // and the server refused our connection, and the case where we are trying to log in but // the server has reached its max connection limit. Bottom line, we need to throw general // error in the cases where we did not receive a error token along with the DONE_ERROR. if ((TdsEnums.DONE_ERROR == (TdsEnums.DONE_ERROR & status)) && stateObj.ErrorCount == 0 && stateObj._errorTokenReceived == false && (RunBehavior.Clean != (RunBehavior.Clean & run))) { stateObj.AddError(new SqlError(0, 0, TdsEnums.MIN_ERROR_CLASS, _server, SQLMessage.SevereError(), "", 0)); if (null != reader) { if (!reader.IsInitialized) { run = RunBehavior.UntilDone; } } } // Similar to above, only with a more severe error. In this case, if we received // the done_srverror, this exception will be added to the collection regardless. // The server will always break the connection in this case. if ((TdsEnums.DONE_SRVERROR == (TdsEnums.DONE_SRVERROR & status)) && (RunBehavior.Clean != (RunBehavior.Clean & run))) { stateObj.AddError(new SqlError(0, 0, TdsEnums.FATAL_ERROR_CLASS, _server, SQLMessage.SevereError(), "", 0)); if (null != reader) { if (!reader.IsInitialized) { run = RunBehavior.UntilDone; } } } ProcessSqlStatistics(curCmd, status, count); // stop if the DONE_MORE bit isn't set (see above for attention handling) if (TdsEnums.DONE_MORE != (status & TdsEnums.DONE_MORE)) { stateObj._errorTokenReceived = false; if (stateObj._inBytesUsed >= stateObj._inBytesRead) { stateObj._pendingData = false; } } // _pendingData set by e.g. 'TdsExecuteSQLBatch' // _hasOpenResult always set to true by 'WriteMarsHeader' // if (!stateObj._pendingData && stateObj._hasOpenResult) { /* Debug.Assert(!((sqlTransaction != null && _distributedTransaction != null) || (_userStartedLocalTransaction != null && _distributedTransaction != null)) , "ProcessDone - have both distributed and local transactions not null!"); */ // WebData 112722 stateObj.DecrementOpenResultCount(); } return true; }
internal void CheckResetConnection(TdsParserStateObject stateObj) { if (_fResetConnection && !stateObj._fResetConnectionSent) { Debug.Assert(stateObj._outputPacketNumber == 1 || stateObj._outputPacketNumber == 2, "In ResetConnection logic unexpectedly!"); try { if (_fMARS && !stateObj._fResetEventOwned) { // If using Async & MARS and we do not own ResetEvent - grab it. We need to not grab lock here // for case where multiple packets are sent to server from one execute. stateObj._fResetEventOwned = _resetConnectionEvent.WaitOne(stateObj.GetTimeoutRemaining()); if (stateObj._fResetEventOwned) { if (stateObj.TimeoutHasExpired) { // We didn't timeout on the WaitOne, but we timed out by the time we decremented stateObj._timeRemaining. stateObj._fResetEventOwned = !_resetConnectionEvent.Set(); stateObj.TimeoutTime = 0; } } if (!stateObj._fResetEventOwned) { // We timed out waiting for ResetEvent. Throw timeout exception and reset // the buffer. Nothing else to do since we did not actually send anything // to the server. stateObj.ResetBuffer(); Debug.Assert(_connHandler != null, "SqlConnectionInternalTds handler can not be null at this point."); stateObj.AddError(new SqlError(TdsEnums.TIMEOUT_EXPIRED, (byte)0x00, TdsEnums.MIN_ERROR_CLASS, _server, _connHandler.TimeoutErrorInternal.GetErrorMessage(), "", 0, TdsEnums.SNI_WAIT_TIMEOUT)); Debug.Assert(_connHandler._parserLock.ThreadMayHaveLock(), "Thread is writing without taking the connection lock"); ThrowExceptionAndWarning(stateObj, callerHasConnectionLock: true); } } if (_fResetConnection) { // Check again to see if we need to send reset. Debug.Assert(!stateObj._fResetConnectionSent, "Unexpected state for sending reset connection"); { // if we are reseting, set bit in header by or'ing with other value stateObj._outBuff[1] = (Byte)(stateObj._outBuff[1] | TdsEnums.ST_RESET_CONNECTION); } if (!_fMARS) { _fResetConnection = false; // If not MARS, can turn off flag now. } else { stateObj._fResetConnectionSent = true; // Otherwise set flag so we don't resend on multiple packet execute. } } else if (_fMARS && stateObj._fResetEventOwned) { Debug.Assert(!stateObj._fResetConnectionSent, "Unexpected state on WritePacket ResetConnection"); // Otherwise if Yukon and we grabbed the event, free it. Another execute grabbed the event and // took care of sending the reset. stateObj._fResetEventOwned = !_resetConnectionEvent.Set(); Debug.Assert(!stateObj._fResetEventOwned, "Invalid AutoResetEvent state!"); } } catch (Exception) { if (_fMARS && stateObj._fResetEventOwned) { // If exception thrown, and we are on Yukon and own the event, release it! stateObj._fResetConnectionSent = false; stateObj._fResetEventOwned = !_resetConnectionEvent.Set(); Debug.Assert(!stateObj._fResetEventOwned, "Invalid AutoResetEvent state!"); } throw; } } #if DEBUG else { Debug.Assert(!_fResetConnection || (_fResetConnection && stateObj._fResetConnectionSent && stateObj._fResetEventOwned), "Unexpected state on else ResetConnection block in WritePacket"); } #endif }