4

So I recently started with transactions and error handling in SQL Server in college and I've run into this issue which my teachers haven't been able to explain. When I try and execute this block:

Begin Transaction TRA
  Begin Try
    Alter Table history
    Add Primary Key (employee_id,date_beg)
  End Try
  Begin catch
    If @@trancount>0
    Select ERROR_NUMBER() Error, ERROR_MESSAGE() Mensaje
  End Catch
Commit

I get the following error messages:

Msg 8111, Level 16, State 1, Line 2
Cannot define PRIMARY KEY constraint on nullable column in table 'history'.

Msg 1750, Level 16, State 0, Line 2
Could not create constraint. See previous errors.

So it seems the code doesn't catch the error as it's supposed to.

Would really appreciate it if you could help me!

2
  • 1
    Have you looked at any documentation on the Try ... Catch? This MSDN page states that not all errors are caught... Commented Sep 19, 2015 at 20:54
  • 2
    @ChipsLetten . . . This is a level 16 error and it is run-time, not compile-time. So, it seems like it should be caught. Commented Sep 19, 2015 at 21:46

2 Answers 2

4

According to the documentation on TRY...CATCH, I think that your CATCH block should have caught the error.

These are the documented reasons why an error will not be caught in a TRY...CATCH block, none of which appear to apply to your case:

TRY…CATCH constructs do not trap the following conditions:

  • Warnings or informational messages that have a severity of 10 or lower.
  • Errors that have a severity of 20 or higher that stop the SQL Server Database Engine task processing for the session. If an error occurs that has severity of 20 or higher and the database connection is not disrupted, TRY…CATCH will handle the error.
  • Attentions, such as client-interrupt requests or broken client connections.
  • When the session is ended by a system administrator by using the KILL statement.

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.

Considering the last 2 reasons in particular, this is not a compilation error, nor is it a deferred name resolution error, because your table name resolves just fine. This really is a runtime error, and should have been caught.

And yet, even though it shouldn't be applicable here, you'll find that the following information provides a working solution for your case:

If an error occurs during compilation or statement-level recompilation at a lower execution level (for example, when executing sp_executesql or a user-defined stored procedure) inside the TRY block, the error occurs at a lower level than the TRY…CATCH construct and will be handled by the associated CATCH block.

Applying the above information, you'll find that you can catch the error by changing your script to either of the following 2 options:

Option 1: sp_executesql

Begin Transaction TRA
  Begin Try
    exec sp_executesql 'Alter Table history Add Primary Key (employee_id,date_beg)';
  End Try
  Begin catch
    If @@trancount>0
    Select ERROR_NUMBER() Error, ERROR_MESSAGE() Mensaje
  End Catch
Commit

Option 2: wrapping alter statement in a separate stored procedure

create procedure WrapAlterStatementInSP as
Alter Table history Add Primary Key (employee_id,date_beg);
go

Begin Transaction TRA
  Begin Try
    exec WrapAlterStatementInSP;
  End Try
  Begin catch
    If @@trancount>0
    Select ERROR_NUMBER() Error, ERROR_MESSAGE() Mensaje
  End Catch
Commit

Hopefully this helps. But I do feel that there is either a bug in the current behavior, or missing information in the documentation.

Sign up to request clarification or add additional context in comments.

3 Comments

How can it not be a deferred name issue? The op is creating a new object (a primary key constraint) that did not exist when the procedure was compiled. technet.microsoft.com/en-us/library/ms190686(v=sql.105).aspx
@Rachel: A deferred name resolution error would occur if the history table didn't exist. OP's error is a result of executing the alter statement, not of a statement-level recompilation/deferred name resolution error.
The constraint is an object that doesn't exist though until it is created. I've had many a fun time having to force batch things like this just to force them in, just as the answer is suggesting. So your method I feel is 100% valid, its just your thoughts that it's a bug I find suspect. You should however get the magic beanz because you suggested a solution, all I did was describe the problem. Upvoted accordingly.
0

As per the BOL, Try Catch will not work when:

an error that occurs during statement-level recompilation will not prevent the batch from compiling, but it will terminate the batch as soon as recompilation for the statement fails. For example, if a batch has two statements and the second statement references a table that does not exist, deferred name resolution causes the batch to compile successfully and start execution without binding the missing table to the query plan until that statement is recompiled. The batch stops running when it gets to the statement that references the missing table and returns an error. This type of error will not be handled by a TRY…CATCH construct at the same level of execution at which the error occurred.

Methinx this is the issue here.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.