Fail a procedure when error occurs in sql server - sql-server

I would like my procedure to fail with a critical error when a certain error occurs. I have the below try catch block as part of it.
`
BEGIN TRY
BEGIN
some other sql ops
END
END TRY
BEGIN CATCH
Declare #MESSAGE NVARCHAR(400) = N'Warning : some message'
THROW 52000, #MESSAGE, 1;
END CATCH`
Although the error is being captured as per the expectation and the process not going any further, the procedure is not ending with a critical failure instead says completed successfully. I need it to do so for my wrapper process to identify critical failure and perform relevant actions. I tried raise error and also with different severity so far but no luck.

Related

Position of TRY/CATCH bloc for SQL Server stored procedure

I would like to know the best position of a TRY/CATCH for a T-SQL procedure and why
In the statement:
CREATE PROCEDURE procedure_name
AS
BEGIN
-- Code
BEGIN TRY
sql_statement
END TRY
BEGIN CATCH
-- Handle errors
END CATCH
--Code
END
or in the call:
BEGIN TRY
EXEC procedure_name
END TRY
BEGIN CATCH
-- Handle errors
END CATCH
I would go for the first option.
BEGIN TRY
sql_statement
END TRY
BEGIN CATCH
-- Handle errors
END CATCH
The reason is you would want to catch the errors at the source and then take some appropriate actions.
In second option you are letting the error bubble up and there you would not have access to all the Exact error information returned by the error functions inside the catch block.
For example the ERROR_LINE() function will return the line number of the calling procedure where the it is calling the procedure containing the actual sql code, you would want to know the error line number where the actual exception was thrown, this information is only available in the catch block of the procedure being called.
Moral of the story is try to catch exceptions as close to the source as possible.

SQL Server TRY...CATCH Is Not Catching An Error

BEGIN TRY
EXEC N'EXEC sp_testlinkedserver N''[MyLinkedServer]'';';
END TRY
BEGIN CATCH
SELECT 'LinkedServerDown' AS Result
RETURN
END CATCH
SELECT TOP(1) FirstName FROM [MyLinkedServer].TestDatabase.dbo.Customer
My first experience with using a TRY...CATCH in SQL Server does not have me impressed so far.
I've stopped the SQL Service on my linked server to attempt to test a situation where our linked server is down, inaccessible, etc.
Instead of catching any error, this code just throws the "Login timeout expired" and "network-related or instance-specific error has occurred..." error and ceases execution of the rest of the code.
Is my SQL TRY...CATCH block not set up correctly?
As per the MSDN, what sp_testlinkedserver do is
Tests the connection to a linked server. If the test is unsuccessful
the procedure raises an exception with the reason of the failure.
So when you compile your code (SP), sp_testlinkedserver checks for connection. But you can defer this and capture it by using dynamic SQL.
Like this -
BEGIN TRY
EXEC sp_executesql N'EXEC sp_testlinkedserver [192.168.51.81];';
END TRY
BEGIN CATCH
SELECT 'LinkedServerDown' AS Result
END CATCH
From MSDN
Errors Unaffected by a TRY…CATCH Construct
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.
You need to create your end testlinkedserver stored procedure. This will also capture login time out errors.
exec dbo.USP_testlinkedserver 'myServerNameHere'
The definition is mentioned below:
CREATE PROCEDURE USP_testlinkedserver
#ServerName sysname
AS
BEGIN
SET NOCOUNT ON;
DECLARE #statement NVARCHAR(MAX), #errorMessage NVARCHAR(MAX)
SET #statement = N'SELECT * FROM OPENQUERY('+QUOTENAME(#ServerName,'[')+', ''SELECT 1'')'
BEGIN TRY
-- run the query
EXEC sp_executesql #stmt = #statement;
END TRY
BEGIN CATCH
-- show custom message
SET #errorMessage=QUOTENAME(#ServerName,'[') + ' linked server is not available. ' + ERROR_MESSAGE()
Raiserror(#errorMessage,16,1)
END CATCH;
END

What happens when I return from inside a SQL cursor?

I have the following code inside a cursor in a stored procedure I am working with:
SELECT #err_code = ##error
If #err_code <> 0
BEGIN
ROLLBACK TRAN
Select return_status = 'FAIL',
return_msg = 'Insert Into Errs Warnings Failed !!'
return 16
END
Before this error checking there is some table inserts (still inside the cursor). Recently I encountered an error where the table we were inserting into didn't match the insert statement that was coded (Column name or number of supplied values does not match table definition). I would have thought that this error checking code would have caught this and killed the procedure, but for some reason all it did was print an error message to the log and continue running. Column name or number of supplied values does not match table definition was the error message.
So I'm wondering if this has to do with the return being inside of a cursor. Is is possible that in this scenario all the return 16 would have done was exit the cursor and continue to execute the rest of the stored procedure? Or should it have terminated the procedure entirely? I'm using MS SQL Server 2008.
Thank you!
The issue with this ##ERROR function is , its scope is very limited. It will only be populated if an error occurs in the statement that was executed just before calling ##ERROR function.
If any other statement is executed before or called after that error occurred the ##ERROR will return NULL.
Also to anticipate where error may occur, and capture its value intime and stop the execution there, its a bit of a pain and error prone.
The best way of error handling in sql server would be try..catch block.
BEGIN TRY
BEGIN TRANSACTION
-- your code here
COMMIT TRANSACTION --<-- this will only be executed if nothing
-- goes wrong in prior statements
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION;
Select return_status = 'FAIL',
return_msg = 'Insert Into Errs Warnings Failed !!'
END CATCH
Using Try...Catch block also gives you access to Error functions which can only be used in CATCH BLOCK and can help you to get detailed information about the error occurred in the Try..Block.
error functions like ERROR_MESSAGE(), ERROR_LINE() , ERROR_STATE(), ERROR_PROCEDURE() etc

Weird behavior of Try-Catch in a Stored Procedure

I have a stored procedure where I need to cast to a type, but do not know if the cast will succeed. In an imperative language, I would use some sort of TryCast pattern. I figured that this would be equivalent in T-SQL:
begin try
select cast(#someValue as SomeType)
end try begin catch end catch
On the surface, it does appear to be equivalent. If #SomeTypeVar is uninitialized and the cast fails, I get NULL to work with; the correct value if the cast succeeds.
I used this same code in a stored procedure, but that yields an error: Msg 2812, Level 16, State 62, Line 20. The current transaction cannot be committed and cannot support operations that write to the log file. Roll back the transaction. Some research led me to other questions on Stack Overflow and this table of times when try-catch fails in T-SQL:
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.
These errors are returned to the level
that ran the batch, stored procedure, or trigger.
At first, I thought I fell into the statement-level recompilation bucket (as my error level is 16) until I tried to bisect the problem. The minimal reproduction is as follows:
create procedure failsInTransactions
as
begin
begin try
select cast(#someValue as SomeType)
end try begin catch end catch
end
and the calling code:
begin tran
exec failsInTransactions
commit
This yields the error I discussed above. However, I remembered that if a stored procedure doesn't have any parameters, you can call it without exec. This:
begin tran
failsInTransactions
commit
succeeds with Command(s) completed successfully. Further experimentation led me to another error with level 16:
begin try
select 1/0
end try begin catch end catch
which works in both cases, producing no rows of output.
I have two questions:
Why is there different behavior calling the procedure with and without exec?
Why does another error of the same error level proceed after the catch?
The EXECUTE keyword is optional only if it is the first statement in the batch. It is not related to parameters and is required in all other contexts. Microsoft inherited this odd behavior from the Sybase code base as well as other many lax T-SQL parsing rules. I suggest you follow a strict T-SQL coding style to avoid gotchas.
The code below runs without error because it is not executing a proc at all. Since there are no semicolon statement terminators, the stored procedure name becomes part of the BEGIN TRAN statement and is interpreted as a transaction name.
begin tran
failsInTransactions
commit
You will get the expected syntax error during compilation if you add statement terminators and this will lead you down the path to specify EXEC.
begin tran;
EXEC failsInTransactions;
commit;
Be aware that not using statement terminators is deprecated so I suggest you get in the habit of specifying them. See https://www.dbdelta.com/always-use-semicolon-statement-terminators/.

Why the procedure is not going into the catch block

I have a stored procedure which is selecting the entry from the Entity table which doesn't exists in our database.
In the code below if I execute the stored procedure, it's not going into catch block while for every error in try block it should go to the catch block automatically.
I am not able to understand the reason
Create PROCEDURE AddUpdateEntity
(#Name VARCHAR(20),
#Age SMALLINT)
AS
BEGIN TRY
SELECT NAME, Age FROM Entity WHERE NAME = #name AND Age = #Age
END TRY
BEGIN CATCH
SELECT ERROR_NUMBER() StatusCode, ERROR_MESSAGE() [Message]
END CATCH
GO
/* Command to execute the SP */
EXEC AddUpdateEntity 'Sandeep',20
This execute statement is showing the error "Invalid Entity Object" but not calling the catch block.
The stored procedure has crashed and is showing the message
Msg 208, Level 16, State 1, Procedure AddUpdateEntity, Line 10
Nom d'objet 'Entity' non valide.
As per the MSDN (follow link http://msdn.microsoft.com/en-us/library/ms175976.aspx)
Errors Unaffected by a TRY…CATCH Construct
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.
These errors are returned to the level that ran the batch, stored procedure, or trigger.
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.
Hope the above description will solve your problem.
It will never enter the CATCH block simply because no rows are returned from a query. A sql query which returns no rows isn't an error; it's expected behavior.
As others have pointed out, the error you're seeing appears to be a compile-time error, because the Entity table does not exist.
You could try using dynamic sql with sp_executesql to force the SP to check for the tables existence only at run-time.
BEGIN TRY
DECLARE #sqlStr NVARCHAR(4000),
#sqlParams NVARCHAR(400);
SET #sqlStr = N'SELECT Name, Age FROM Entity WHERE Name=#Name AND Age=#Age';
SET #sqlParams = '#Name VARCHAR(20), #Age SMALLINT';
sp_executesql #sqlStr, #sqlParams, #Name = #Name, #Age = #Age
END TRY
BEGIN CATCH
SELECT ERROR_NUMBER() StatusCode, ERROR_MESSAGE() [Message]
END CATCH

Resources