Search code examples
sql-servertry-catchxact-abort

set xact_abort on and try catch together


I have a try catch block in my SP with just a insert statement in the try. The catch checks the error code to see if it is PK violation, if it is then do an update. But sometimes I get and error:

The current transaction cannot be committed and cannot support operations that write to the log file. Roll back the transaction. Uncommittable transaction is detected at the end of the batch. The transaction is rolled back.

So I added xact_abort on, but then I keep getting:

Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements.

And I found this Set-XACT_ABORT-ON-and-TryCatch-block-in-Sql-Server-2005, which if true, will my catch code not run if there is a error in my try block with xact_abort on?


Solution

  • It is not true, at least with SQL Server 2008, that SET XACT_ABORT ON will cause an error to skip the CATCH block:

    Here is the code I tried using the Northwind database

    SET XACT_ABORT OFF
    BEGIN TRY
        SELECT 1,  @@TRANCOUNT
    BEGIN TRAN
        UPDATE [dbo].[Categories]
        SET Description='BLAH'
        WHERE [CategoryID]=2
        SELECT 2,  @@TRANCOUNT
    
        SELECT 1/0 as whoops
    
    
    COMMIT
        SELECT 3,  @@TRANCOUNT
    
    END TRY
    BEGIN CATCH
        SELECT 'In Catch. Error occured', 4,  @@TRANCOUNT
        
         IF (XACT_STATE()) = 0
        BEGIN
            SELECT
                N'There is no transaction'
           
        END;
    
    
         IF (XACT_STATE()) = -1
        BEGIN
            SELECT
                N'The transaction is in an uncommittable state.' +
                'Rolling back transaction.'
            ROLLBACK TRANSACTION;
        END;
    
        -- Test whether the transaction is committable.
        IF (XACT_STATE()) = 1
        BEGIN
            SELECT
                N'The transaction is committable.' +
                'Committing transaction.'
            COMMIT TRANSACTION;   
        END;
    
    END CATCH
    

    This will, obviously, force an error when it hits the SELECT 1/0 statement. With SET XACT_ABORT OFF, when the CATCH block is reached, the value returned by the XACT_STATE() function is 1, causing the code to run which COMMITs the transaction. When SET XACT_ABORT is on, the value returned, in the CATCH block is -1 so the code which ROLLs back the transaction is executed.

    This is based on:

    http://msdn.microsoft.com/en-us/library/ms175976.aspx