SQL Server TRY...CATCH Is Not Catching An Error - sql-server

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

Related

Fail a procedure when error occurs in 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.

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

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

Exception flow in a stored procedure calling another stored procedure

I've got a stored procedure that calls another stored procedure. SP2 has a try/transaction/catch/rollback/raiserror. If SP2 raises an error, will it bubble up through SP1 to the caller, or do I have to nest the call to SP2 in a try/catch as well? If the latter is the case, how can I assure that I'm not killing the "stack trace" of the error from SP2 when raising/exiting SP1?
-- will this bubble any error from SP2 and exit SP1 as well?
EXEC dbo.storedProc2 #someParameter = #someValue
--Or do I need to do this?
BEGIN TRY
EXEC dbo.storedProc2 #someParameter = #someValue
END TRY
BEGIN CATCH
-- this is what I normally do in a simple catch/raise scenario:
-- will it kill the error stack?
DECLARE #ErrMsg VARCHAR(4000), #ErrSeverity INT, #ErrState INT, #ErrLine INT
SELECT #ErrMsg = ERROR_MESSAGE() + ' Line %d',
#ErrSeverity = ERROR_SEVERITY(),
#ErrState = ERROR_STATE(),
#ErrLine = ERROR_LINE()
RAISERROR(#ErrMsg, #ErrSeverity, #ErrState, #ErrLine)
END CATCH
You will need to have an outer TRY/CATCH block.
If you only have the error trapping in SP2, when SP2 errors it will abort.
It will then pass the error code back to SP1, but SP1 will continue to execute under most circumstances (exception would be a fatal error with severity 20-25).
There's no real inherent error trapping without using TRY/CATCH (or checking ##Error if you are old-school) - the code will continue to execute, for better or worse.
You should trap and deal with errors WITHIN dbo.storedProc2
Then return error codes using the return n method
SP1 can then deal with that error(s) in whatever way is required.

SQL Server error logging from a Stored Procedure

Our application is Windows Service (native .EXE written in C++) that calls stored procedures in SQL Server. In most cases errors in stored procedures (in 90% of the cases these errors mean something was wrong in our business logic) are re-thrown as exception and caught by our service. They are then logged in Application Event Log on the computer where our service is running.
However, I now have a need to log some of the errors on the SQL Server itself within a stored procedure.
Following the paradigm we use for our service I think I can use xp_logevent to save error information in the event log.
Is this a recommended approach to log SQL Server errors?
FWIW I use SQL Server 2008
The How To
You can always use RAISEERROR() WITH LOG. Logs to both Windows Application log and the SQL error log.Please note that severity level is key here. There are some limitations and security considerations, but you get some other features also.
More details in BOL:
http://msdn.microsoft.com/en-us/library/ms178592.aspx
The Should you
My opinion is that you shouldn't log anything to SQL error log unless it's generated by SQL server itself. Multiple reasons:
If your IT or DBA uses log analyzer or any other tool, it may trip an alarm on an application issue, instead of the server issue (this is what they are trying to catch).
I never found parsing error logs enjoyable from within SQL server, and I'm not particularly in love with SSMS's way of doing it.
My suggestion
Use a generic logging stored procedure writing to some error log table. A very nice patter is
BEGIN TRY
...do your stuff
END TRY
BEGIN CATCH
get the ERROR_LINE(), ERROR_MESSAGE() and friends
execute generic logging procedure
END CATCH
As a bonus, you can use SSSB within the logging procedure to make it async and not impede the main logic flow
Here is a useful way I have found to keep track of SQL Server errors. First, create a table to store the errors:
CREATE TABLE utiliity.dbo.ProcedureLog
(
LogDate DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
DatabaseID INT,
ObjectID INT,
ProcedureName NVARCHAR(400),
ErrorLine INT,
ErrorMessage NVARCHAR(MAX),
AdditionalInfo NVARCHAR(MAX)
);
GO
CREATE CLUSTERED INDEX cx_LogDate ON dbo.utiliity.dbo.ProcedureLog(LogDate);
GO
Then create a stored procedure to call when the error occurs:
CREATE PROCEDURE sp_CallProcedureLog
#ObjectID INT,
#DatabaseID INT = NULL,
#AdditionalInfo NVARCHAR(MAX) = NULL
AS
BEGIN
SET NOCOUNT ON;
DECLARE
#ProcedureName NVARCHAR(400);
SELECT
#DatabaseID = COALESCE(#DatabaseID, DB_ID()),
#ProcedureName = COALESCE
(
QUOTENAME(DB_NAME(#DatabaseID)) + '.'
+ QUOTENAME(OBJECT_SCHEMA_NAME(#ObjectID, #DatabaseID))
+ '.' + QUOTENAME(OBJECT_NAME(#ObjectID, #DatabaseID)),
ERROR_PROCEDURE()
);
INSERT utiliity.dbo.ProcedureLog
(
DatabaseID,
ObjectID,
ProcedureName,
ErrorLine,
ErrorMessage,
AdditionalInfo
)
SELECT
#DatabaseID,
#ObjectID,
#ProcedureName,
ERROR_LINE(),
ERROR_MESSAGE(),
#AdditionalInfo;
END
GO
Finally, in your stored procedures where you want to record the errors:
BEGIN TRY
... execute SQL commands here
END TRY
BEGIN CATCH
DECLARE #msg NVARCHAR(MAX);
SET #msg = 'Something went horribly wrong. Error number = ' + ERROR_NUMBER();
EXEC utiliity.dbo.sp_CallProcedureLog
#ObjectID = ##PROCID,
#AdditionalInfo = #msg;
DECLARE #ErrorMessage NVARCHAR(MAX);
DECLARE #ErrorSeverity INT;
DECLARE #ErrorState INT;
SELECT
#ErrorMessage = ERROR_MESSAGE(),
#ErrorSeverity = ERROR_SEVERITY(),
#ErrorState = ERROR_STATE();
-- Use RAISERROR inside the CATCH block to return error
-- information about the original error that caused
-- execution to jump to the CATCH block.
RAISERROR (#ErrorMessage, -- Message text.
#ErrorSeverity, -- Severity.
#ErrorState -- State.
);
END CATCH
Here are my sources: http://www.mssqltips.com/sqlservertip/2003/simple-process-to-track-and-log-sql-server-stored-procedure-use/ and http://msdn.microsoft.com/en-us/library/ms178592(SQL.105).aspx. HTH.
You can call xp_logevent to log messages in the event log. But for logging exceptions it is better to use the RAISERROR () WITH LOG statement.
If you are concerned about performance you can pass the message through a SQL Server Service Broker queue and have an activation procedure log the messages in the eventlog.
The downside is that whoever has to find out the errors now needs permissions to get into the event log.
If you go with this, make sure your log has more size than the default 512K. Also set it to overwrite events as needed.
Also, the event log is not as fast as your SQL Server database so you may want to run a load test to figure out if it slows your application down.

Resources