Problem with logging error from within CATCH block

  • I have a code where I execute a transaction and I need to log an error if it occurs. I execute my transaction in TRY block, and catch an error in CATCH block.

    Here is a sample of my code:

    First, I create a table to insert some values in a transaction:

    if object_id('t','u') is not null

    drop table t

    go

    create table t

    (c1 int)

    go

    -- Next, I create an error log table:

    if object_id('error_log', 'u') is not null

    drop table error_log

    go

    create table error_log

    (err_num int,

    err_message varchar(100)

    )

    go

    --And here is my code where I try to log my error into error_log table:

    declare

    @error_number int,

    @error_message varchar(100)

    set xact_abort on

    begin try

    begin transaction

    -- here I intentionally enter number that's larger than int:

    insert into t values (67896789867);

    insert into t values (5);

    if xact_state() = 1 begin

    commit transaction

    end

    end try

    begin catch

    select @error_number = error_number(),

    @error_message = error_message()

    print @error_number

    print @error_message

    insert into error_log

    select @error_number, @error_message

    rollback transaction

    end catch

    Upon execution of this batch I get an error:

    "Msg 3930, Level 16, State 1, Line 21

    The current transaction cannot be committed and cannot support operations that write to the log file. Roll back the transaction."

    If I comment lines which insert into error_log table, I don't receive an error. But the problem is that I DO need to log errors.

    Any suggestions please ?

  • The rollback is affecting the insert into the error_log as well as the insert into t. At a minimum the rollback should be before the insert into error_log statement. Normally IF @@TRANCOUNT > 0 is wrapped around the rollback as well. This may not solve everything but it should help:

    IF @@TRANCOUNT > 0

    BEGIN

    ROLLBACK TRANSACTION

    END

    insert into error_log

    select @error_number, @error_message

  • Another thing you may consider. If you want to capture the data causing the error, you can insert that data into a table variable defined for that function, then you can roll back the transaction then use the data in the tale variable to update an error log or error audit table with the offending data for further analysis.

    The reason the works is that table variables are not affected by transactions.

  • This works fine.

    declare

    @error_number int,

    @error_message varchar(100)

    begin try

    begin transaction

    -- here I intentionally enter number that's larger than int:

    insert into t values (67896789867);

    insert into t values (5);

    commit transaction

    end try

    begin catch

    select @error_number = error_number(),

    @error_message = error_message()

    print @error_number

    print @error_message

    rollback transaction

    insert into error_log

    select @error_number, @error_message

    end catch

    SELECT * FROM T

    SELECT * FROM ERROR_LOG

  • Thanks, that's good solution, you moved insert ouside of transaction. This is what I needed.

  • Please be aware the ERROR_MESSAGE() function returns a nvarchar(4000). So you will run the risk of truncating some of the messages using the variable declared as varchar(100).

  • Adam Gojdas (11/5/2009)


    Please be aware the ERROR_MESSAGE() function returns a nvarchar(4000). So you will run the risk of truncating some of the messages using the variable declared as varchar(100).

    It's NVARCHAR(2048) on my SQL Servers :laugh:

  • SQL Guy-482125 (11/4/2009)


    Thanks, that's good solution, you moved insert ouside of transaction. This is what I needed.

    You might like to change the ROLLBACK TRANSACTION to:

    IF XACT_STATE() != 0 BEGIN ROLLBACK TRANSACTION END;

    The ROLLBACK TRANSACTION on its own would generate its own error if the original error terminated the active transaction. In that case, no error details would be logged, which is probably not what you want.

    edit: changed <> to != in the code block due to dodgy site formatting :rolleyes:

  • Paul White (11/6/2009)


    Adam Gojdas (11/5/2009)


    Please be aware the ERROR_MESSAGE() function returns a nvarchar(4000). So you will run the risk of truncating some of the messages using the variable declared as varchar(100).

    It's NVARCHAR(2048) on my SQL Servers :laugh:

    Thanks for pointing that out Paul. Seems there is a discrepancy. The BOL I have installed for 2005 states: nvarchar(4000). But after searching the msdn website's BOL I see it lists both 2005 and 2008: nvarchar(2048).

    I have to wonder what's up with that?

  • Paul White (11/6/2009)


    SQL Guy-482125 (11/4/2009)


    Thanks, that's good solution, you moved insert ouside of transaction. This is what I needed.

    You might like to change the ROLLBACK TRANSACTION to:

    IF XACT_STATE() != 0 BEGIN ROLLBACK TRANSACTION END;

    The ROLLBACK TRANSACTION on its own would generate its own error if the original error terminated the active transaction. In that case, no error details would be logged, which is probably not what you want.

    edit: changed <> to != in the code block due to dodgy site formatting :rolleyes:

    I also like to check transactions with that method in my stored procedures. But I sometimes find when running scripts to test code with transactions I have to do the following to rollback all since I always have my test scripts in transactions for the testing.

    WHILE (XACT_STATE() <> 0) BEGIN

    ROLLBACK TRANSACTION;

    END;

  • Adam Gojdas (11/7/2009)


    WHILE (XACT_STATE() <> 0) BEGIN

    ROLLBACK TRANSACTION;

    END;

    Hey Adam,

    Not trying to be 'smart' here...but ROLLBACK always rolls back completely, through any number of 'nested' transactions.

    You might be thinking of the way COMMIT TRANSACTION works, or possibly be confusing ROLLBACK with ROLLBACK to a savepoint.

    Paul

  • Paul White (11/7/2009)


    Adam Gojdas (11/7/2009)


    WHILE (XACT_STATE() <> 0) BEGIN

    ROLLBACK TRANSACTION;

    END;

    Hey Adam,

    Not trying to be 'smart' here...but ROLLBACK always rolls back completely, through any number of 'nested' transactions.

    You might be thinking of the way COMMIT TRANSACTION works, or possibly be confusing ROLLBACK with ROLLBACK to a savepoint.

    Paul

    Yes, I know what you are talking about and had always thought that same exact thing. But for some reason with my scripts and the the way the code was when I would be running and rerunning from SSMS I would get a message that there was still a transaction left. I then put it into the while loop and didn't have an issue. I know it sounds pretty odd and probably should have stated this with my initial reply. I will have to see if I can gather an example of this situation...or else it may have all been in my head.:w00t:

  • Makes you wonder if the connection had IMPLICIT_TRANSACTIONS set on. Well, never mind, I'm sure there was a non-paranormal explanation 🙂

Viewing 13 posts - 1 through 13 (of 13 total)

You must be logged in to reply to this topic. Login to reply