So I finally had an opportunity to use Try...Catch in the real world, and I found the examples to be a little lacking around executing a stored proc.  Hopefully this will help.  If not, it will at least help me remeber when I forget it in a week.

In the old days we would trap for errors something like this.

declare @rc int
declare @msg varchar(3000)

exec @rc=usp_some_proc

if @rc <> 0 or @@Error <> 0
begin

   set @msg = 'Error in usp_some_proc'
   goto ERRORPROC

end

goto ENDPROC

ERRORPROC:

BEGIN
   RAISERROR
(@msg, 16, 1)

ENDPROC:

Essentially we had to trap for errors in each proc individually, which was quite tedious.

In SQL Server 2005 we get a new error handling setup much like .NET languages using the Try...Catch construct.  Much has been made of this feature and how to use it and the other new error handling functions, so I won't repeat that here.  Essentially what I want to make note of is the ability of T-SQL to bubble up the errors to the calling procedure.  As I mentioned, this is _lightly_ documented.  Imagine the following

BEGIN TRY

   exec usp_some_proc1

   exec usp_some_proc2

   exec usp_some_proc3

END TRY

BEGIN CATCH

PRINT 'This error has been caught...'

PRINT 'Msg ' + cast(ERROR_NUMBER() as varchar) + ', Level ' + cast(ERROR_SEVERITY() as varchar) + ', State ' + cast(ERROR_STATE() as varchar) + ', Line ' + cast(ERROR_LINE() as varchar)

PRINT ERROR_MESSAGE()

END CATCH

Let's say all three procs catch and raise errors like the old method above.  When the error is thrown in child proc, the outer script will catch the error and drop to the catch block, thus bubbling it up the error, with little toil and trouble to you.  One interesting thing that is missing.  It is not possible to re-throw the error already thrown by the child.  You have to log another error or print out the results (unless I'm missing something...).  And finally, yeah, what about Finally?  Here's hoping we get that last piece of that construct in a service pack soon.  It's a really nice feature in .NET that I wish we had in T-SQL.

If you want further proof of bubbling, take a look at this example.  Thanks to Chip Downs for providing it.

/*******************************************************************

Test out all of the different error level types...

0 - 10 are warnings

11 - 19 are trapable errors

20 - 25 are terminal errors

*******************************************************************/

 

IF EXISTS (SELECT distinct 1 FROM sysobjects where name = 'Test_Error_Levels')

DROP PROCEDURE Test_Error_Levels

GO

 

CREATE PROCEDURE Test_Error_Levels

@ErrorLevel INTEGER

AS

DECLARE @ErrorMessage as varchar(100)

If @ErrorLevel between 0 and 10

SET @ErrorMessage = 'This message is a warning and is only for informational purposes'

If @ErrorLevel between 11 and 19

SET @ErrorMessage = 'This is a trapable error'

If @ErrorLevel >= 20

SET @ErrorMessage = 'This is a terminal error for test purposes and is being logged... (can only be raised by an admin).'

If @ErrorLevel >= 20

RAISERROR (@ErrorMessage, @ErrorLevel, 1) WITH LOG

ELSE

RAISERROR (@ErrorMessage, @ErrorLevel, 1)

GO

/*

Test out warnings

Warning: Null value is eliminated by an aggregate or other SET operation

*/

DECLARE @caught AS BIT

SET @caught = 0

BEGIN TRY

EXEC Test_Error_Levels 5

PRINT 'This is the line following the warning.'

END TRY

BEGIN CATCH

PRINT 'This error has been caught...'

PRINT 'Msg ' + cast(ERROR_NUMBER() as varchar) + ', Level ' + cast(ERROR_SEVERITY() as varchar) + ', State ' + cast(ERROR_STATE() as varchar) + ', Line ' + cast(ERROR_LINE() as varchar)

PRINT ERROR_MESSAGE()

SET @caught = 1

END CATCH

IF @caught = 0

PRINT 'This error was not caught...'

GO

--Test out errors

BEGIN TRY

EXEC Test_Error_Levels 13

PRINT 'This is the line following the error.'

END TRY

BEGIN CATCH

PRINT 'This error has been caught...'

PRINT 'Msg ' + cast(ERROR_NUMBER() as varchar) + ', Level ' + cast(ERROR_SEVERITY() as varchar) + ', State ' + cast(ERROR_STATE() as varchar) + ', Line ' + cast(ERROR_LINE() as varchar)

PRINT ERROR_MESSAGE()

END CATCH

GO

--Test out terminal errors (Connection will be broken)

BEGIN TRY

EXEC Test_Error_Levels 24

END TRY

BEGIN CATCH

PRINT 'This error has been caught...'

PRINT 'Msg ' + cast(ERROR_NUMBER() as varchar) + ', Level ' + cast(ERROR_SEVERITY() as varchar) + ', State ' + cast(ERROR_STATE() as varchar) + ', Line ' + cast(ERROR_LINE() as varchar)

PRINT ERROR_MESSAGE()

END CATCH

GO

 

So there it is.  Hopefully this helps someone out there who is finding it hard to determine how this works from the existing documentation.

Jon