If I have an inner stored procedure, that always rolls back without an error.
CREATE PROCEDURE [inner] AS BEGIN
SET XACT_ABORT, NOCOUNT ON;
BEGIN TRY
BEGIN TRANSACTION;
IF (1 + 1 = 2) BEGIN
ROLLBACK TRANSACTION;
END ELSE BEGIN
COMMIT TRANSACTION;
END
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 BEGIN
ROLLBACK TRANSACTION;
END
END CATCH
END
and an outer SP that calls the inner,
CREATE PROCEDURE [outer] AS BEGIN
SET XACT_ABORT, NOCOUNT ON;
BEGIN TRY
BEGIN TRANSACTION;
EXEC [inner];
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 BEGIN
ROLLBACK TRANSACTION;
END
;THROW;
END CATCH
END
and, then I call the outer SP,
EXEC [outer];
I get this error
Msg 266 Level 16 State 2 Line 0
Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements.
Previous count = 1, current count = 0.
Now, initially this is not what I was expecting.
What I think is happening,
axioms:
BEGIN TRANSACTIONalways increments@@TRANCOUNTby 1;COMMIT TRANSACTIONalways decrements@@TRANCOUNTby 1;ROLLBACK TRANSACTIONalways resets@@TRANCOUNTto 0;- There are no nested transactions!
- When
@@TRANCOUNTchanges to any value, other than 0, nothing else happens.
so, step by step,
- [outer] ->
BEGIN TRANSACTIONsets@@TRANCOUNTto1. - [outer] -> calls [inner] with
@@TRANCOUNT1. - [inner] ->
ROLLBACK TRANSACTIONresets@@TRANCOUNTto0. - [inner] -> returns to [outer] with
@@TRANCOUNT0. - SQL Server detects a mismatch in
@@TRANCOUNTbefore and after the call to [inner] and, therefore raises the error above.
So, my questions are,
Is my understanding correct and where is the canonical documentation of this?
What is the best way to write an Stored Procedure that may want to rollback its own changes, so that it can be called directly in its own batch or from within another transaction?