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