Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Try and Catch on TSQL - catch not catching

I have a stored procedure that seems not to be logging its errors correctly.

The code is erroring, but the catch block doesn't seem to be coming into effect.

The try block is fairly long - but the erroring section is simple and comes rightat the end, so I've precis'd that.

BEGIN TRY 
insert into tbl_X
select * from #temp_tbl_Y

RETURN 1
END TRY

BEGIN CATCH
    Insert Into ExtractsErrorLog
    SELECT 
    getdate() as ErrorDate 
    ,object_name(@@procid) as ProcedureName
    ,ERROR_NUMBER() as ErrorNumber
    ,ERROR_LINE() as ErrorLine
    ,ERROR_MESSAGE() as ErrorMessage
    ;
DECLARE @errormessage as varchar(max);
DECLARE @errorseverity as int;
DECLARE @errorstate as int;

set @errormessage = ERROR_MESSAGE();
set @errorseverity = ERROR_SEVERITY();
set @errorstate = ERROR_STATE();

 RAISERROR (@errormessage,
            @errorseverity,
            @errorstate
               );


END CATCH;

The error the proc is failing on is our old friend "Column name or number of supplied values does not match table definition." I've fixed that error - It was a dumb lazy mistake - but I'm baffled why my error logging process didn't seem to be working - no row is being inserted into my ExtractsErrorLog table.

like image 782
DanBennett Avatar asked Dec 27 '22 08:12

DanBennett


1 Answers

TSQL's TRY...CATCH does not catch that error. This error falls into the "compilation/recompilation" type errors that are not handled by the CATCH block "within the same level of execution".

From MSDN:

The following types of errors are not handled by a CATCH block when they occur at the same level of execution as the TRY…CATCH construct:

  • Compile errors, such as syntax errors, that prevent a batch from running.

  • Errors that occur during statement-level recompilation, such as object name resolution errors that occur after compilation because of deferred name resolution

...

You can use TRY…CATCH to handle errors that occur during compilation or statement-level recompilation by executing the error-generating code in a separate batch within the TRY block. For example, you do this by placing the code in a stored procedure or by executing a dynamic Transact-SQL statement using sp_executesql. This allows TRY…CATCH to catch the error at a higher level of execution than the error occurrence. For example, the following code shows a stored procedure that generates an object name resolution error. The batch that contains the TRY…CATCH construct is executing at a higher level than the stored procedure; and the error, which occurs at a lower level, is caught.

I ran into similar issues with a script creating a transaction inside a TRY...CATCH that would ROLLBACK the transaction if it failed. A statement inside the transaction was throwing that same error and caused the transaction to never be closed, as the CATCH was never entered.

As mentioned in the MSDN article, one alternative is to create a stored procedure out of your INSERT statement and then call that inside your try/catch. If the sproc is wrong, you'll catch the compilation error while trying to create it. If the table definition later changes to invalidate the sproc, then the TRY...CATCH will catch the exception for you.

If you want it to all live in one script, you can make it a temporary stored procedure, but you will then need to handle the compilation errors while you are creating the sprocs. It's not pretty, but it will work:

-- Creating error sproc to re-use code
CREATE PROCEDURE #HandleError AS
    Insert Into ExtractsErrorLog
    SELECT  GETDATE() as ErrorDate 
            ,object_name(@@procid) as ProcedureName
            ,ERROR_NUMBER() as ErrorNumber
            ,ERROR_LINE() as ErrorLine
            ,ERROR_MESSAGE() as ErrorMessage;

    DECLARE @errormessage as varchar(max);
    DECLARE @errorseverity as int;
    DECLARE @errorstate as int;

    set @errormessage = ERROR_MESSAGE();
    set @errorseverity = ERROR_SEVERITY();
    set @errorstate = ERROR_STATE();

    RAISERROR ( @errormessage,
                @errorseverity,
                @errorstate);
GO

-- Create a stored procedure of our INSERT and catch any compilation errors
CREATE PROCEDURE #TEST AS
    insert into tbl_X
    select * from #temp_tbl_Y
GO
IF (@@ERROR <> 0) BEGIN
    exec #HandleError
    -- If there was an error creating the sprocs, don't continue to the next batch
    RETURN
END

-- If compilation succeeded, then run the sproc
BEGIN TRY 
    exec #TEST
    RETURN
END TRY
BEGIN CATCH
    exec #HandleError
END CATCH;
like image 76
Jon Senchyna Avatar answered Jan 11 '23 18:01

Jon Senchyna