T-SQL is error handling totally turned off in a "BEGIN CATCH" block? - sql-server-2008

I just want to confirm what I'm seeing. Below is a small proof of concept Stored Proc I wrote that illustrates an issue I was having in a much bigger one.
ALTER PROCEDURE TestErrorHandling
#Param1 varchar(1) = ''
AS
BEGIN
/*
Unit test:
DECLARE #returnStatus nvarchar(15);
Exec #returnStatus = TestErrorHandling
print #returnStatus
*/
BEGIN TRY
print 'Start'
IF #Param1 = '' raiserror( '#Param1 is missing', 18, 1 );
print 'Should not see this'
END TRY
BEGIN CATCH
print error_message()
print error_state()
print error_number()
INSERT INTO [FlightOrderUploadFailedLog]
(EvtTyp)
VALUES ('max size of EvtType is only varchar(15) so this should cause truncation')
print 'after insert '
print error_message()
print error_state()
print error_number()
return -- added in second version (after original post)
END CATCH
END
GO
Output:
Start
#Param1 is missing
1
50000
Msg 8152, Level 16, State 4, Procedure TestErrorHandling, Line 30
String or binary data would be truncated.
The statement has been terminated.
after insert
#Param1 is missing
1
50000
-8
The surprising thing to me is that the program doesn't blow up on the "string or binary data would be truncated". In other words, the code after that still runs. Also, the error_message is not changed by a SQL error inside the catch block.
So the question is - what is the best practice for handling unexpected errors in the "BEGIN CATCH" section? Should I have another nested TRY/CATCH?
NOTE: This was my original question, but I thought to put this question there would get it off topic and confuse the issue. After this one gets answered, I'll go back and update that one: T-SQL Clear Errors
Part 2 - added later:
DECLARE #returnStatus nvarchar(15);
Exec #returnStatus = TestErrorHandling
print #returnStatus
This returns a -8. Where the heck does -8 come from?
I'm also experiementing with adding a "return" vs a "return 0". When BizTalk calls the stored proc, I want him to think it completed successfully, so as not to do the 3 retries every 5 minutes.
Added: I think this is mostly the answer I'm looking for:
SQL try-catch statement not handling error (SQL Server 2008)
But it does not discuss the best practice question I asked in this question.
UPDATE: Here is program to demo Allan's response:
ALTER PROCEDURE TestErrorHandling2
#Param1 varchar(1) = ''
AS
BEGIN
/*
Unit test:
DECLARE #returnStatus nvarchar(15);
Exec #returnStatus = TestErrorHandling2
print #returnStatus
This is proof of concept on error handling.
See question: https://stackoverflow.com/questions/20245900/t-sql-is-error-handling-totally-turned-off-in-a-begin-catch-block
Testing to see if Truncation error stops or not.
*/
print 'Start'
INSERT INTO [FlightOrderUploadFailedLog]
(EvtTyp)
VALUES ('max size of EvtType is only varchar(15) so this should cause truncation')
print 'after insert #1'
print 'Message=' + IsNull(error_message(),'null')
print 'State=' + IsNull(convert(varchar(4),error_state()),'null')
print 'ErrorNumber=' + IsNull(convert(varchar(8),error_number()),'null')
BEGIN TRY
print 'Start - Begin Try'
INSERT INTO [FlightOrderUploadFailedLog]
(EvtTyp)
VALUES ('max size of EvtType is only varchar(15) so this should cause truncation')
print 'after insert #2 '
print 'Message=' + IsNull(error_message(),'null')
print 'State=' + IsNull(convert(varchar(4),error_state()),'null')
print 'ErrorNumber=' + IsNull(convert(varchar(8),error_number()),'null')
print 'The End'
END TRY
BEGIN CATCH
print 'Catch'
print 'Message=' + error_message()
print 'State=' + convert(varchar(4),error_state())
print 'ErrorNumber=' + convert(varchar(8),error_number())
return -- error has been theoretically handled by writing it to a database
END CATCH
END
GO
Result:
Start
Msg 8152, Level 16, State 4, Procedure TestErrorHandling2, Line 21
String or binary data would be truncated.
The statement has been terminated.
after insert #1
Message=null
State=null
ErrorNumber=null
Start - Begin Try
(0 row(s) affected)
Catch
Message=String or binary data would be truncated.
State=4
ErrorNumber=8152
-6

Well, let me try for what I can.
"Best practice" - I hate that phrase because 9 times out of 10 it's purely subjective because somebody somewhere read something.
So my subjective answer is that seeing as you can nest try/catch and put a try/catch within the catch block - then I'd consider it good error handling to put a try/catch within the catch - IF what you have can throw an error and is severe enough that you wish to handle it yourself.
So IMO - best practice is yes, nest try/catch for better error handling.
Secondly - the reason your "catch" doesn't blow up is that the truncating error is not a batch terminating error. It's error level isn't high enough, so subsequent statements will be executed.
Simply try it out with a print statement:
PRINT 'something'
--do your insert here
PRINT 'somethingelse'
Then you'll see you should get both print statements.
You can even suppress truncate errors if you wanted to by changing ANSI_WARNINGS to OFF. Not that I would recommend, but well ... :)
If you had a try/catch within your catch, that should catch your truncate error because the severity is enough to trigger catch.

Related

restoring the exception environment

Can anyone explain the concept of restoring the exception environment simply and smoothly.
It is said that when we use the exception handler in the try...endtry statement،When the program reaches the endtry, it restores the exception environment, but if it suddenly encounters a break command, for example, this recovery does not take place.
And the program even after exiting the try.......endtry command thinks that it is in the previous exception environment, and if another error occurs, it returns to the previous try......endtry command.
Like the following code snippet:
program testBadInput3;
#include( "stdlib.hhf" )
static
input: int32;
begin testBadInput3;
// This forever loop repeats
//until the user enters
// a good integer and the
break
//statement below
// exits the loop.
forever
try
stdout.put( "Enter an integer
value: " );
stdin.get( input );
stdout.put( "The first input
value was: ", input, nl );
break;
exception( ex.ValueOutOfRange
)
stdout.put( "The value was too
large, re-enter." nl );
exception( ex.ConversionError
)
stdout.put( "The input
contained illegal characters,
re-enter." nl );
endtry;
endfor;
// Note that the following
code //is outside the loop and
there
// is no try..endtry statement
//protecting this code.
stdout.put( "Enter another
number: " );
stdin.get( input );
stdout.put( "The new number
is:
", input, nl );
end testBadInput3;

rescue how to raise further or forget an exception

How do I raise an exception further in eiffel? I have 3 cases
I want to retry
a_feature
local
l_retries_count: INTEGER
do
some_potential_failing_feature
rescue
if l_retries_count <= 3 then
l_retries_count := l_retries_count + 1
retry
end
end
I want to do close db connection and ignore exception
a_feature
do
some_potential_failing_feature
rescue
db_connection.close
end
I want to do close db connection and send an email to an admin and ignore
a_feature
do
some_potential_failing_feature
rescue
db_connection.close
send_email_to_admin
end
I want to close db_connection and raise the exception further, I'll put all the cases I can think about into the above code
a_feature
local
l_retries_count: INTEGER
do
some_potential_failing_feature
rescue
if l_retries_count <= 3 then
l_retries_count := l_retries_count + 1
log_error ("Error, retrying for " + l_retries_count.out + "th time")
retry
else
db_connection.close
send_email_to_admin
-- raise the_created_exception_from_some_potential_failing_feature -- how do I do that?
end
end
You can try one of the following:
{EXCEPTION_MANAGER}.last_exception.original.raise
{EXCEPTION_MANAGER}.last_exception.cause.raise
{EXCEPTION_MANAGER}.last_exception.raise
The first one goes through the chain of exceptions ignoring those triggered as a result of failed routines. It might be the one you are looking for.
The next two retrieve either the cause of the current exception or the current exception itself, though it might be not the one you are looking for because it depends on the context of the exception and the nested calls.

I am getting raised TASKING_ERROR in Ada

I am a new to Ada, I just got an assignment to do in Ada task program and when I am trying to run a program in Ada and I am getting "raised TASKING_ERROR" while I am running in an online compiler here https://www.tutorialspoint.com/compile_ada_online.php.
with Ada.Text_IO;
use Ada.Text_IO;
procedure ConTasks is
task type SimpleTask (Message: Character; HowMany:Positive) is
entry Start_Running;
end SimpleTask;
task body SimpleTask is
begin
for i in 1 .. HowMany loop
delay 0.1;
Ada.Text_IO.Put_Line("Hello from task "&Message);
end loop;
end SimpleTask;
task_a : SimpleTask('A', 5);
task_b : SimpleTask('B', 7);
task_c : SimpleTask('C', 4);
begin
task_a.Start_Running;
task_b.Start_Running;
task_c.Start_Running;
end ConTasks;
But when I am running the same program in MinGW I am getting below error
C:\MinGW\bin>gcc -c Ada\ConTasks.ada
gcc: warning: Ada\ConTasks.ada: linker input file unused because linking not done
In the online compiler, I an getting the right output but in the end, it's showing below error.
Hello from task B
Hello from task A
.
.
Hello from task B
raised TASKING_ERROR
with Ada.Text_IO;
use Ada.Text_IO;
procedure ConTasks is
task type SimpleTask (Message: Character; HowMany:Positive);
task body SimpleTask is
begin
for i in 1 .. HowMany loop
delay 0.1;
Ada.Text_IO.Put_Line("Hello from task "&Message);
end loop;
end SimpleTask;
task_a : SimpleTask('A', 5);
task_b : SimpleTask('B', 7);
task_c : SimpleTask('C', 4);
begin
null;
end ConTasks;
#Rudra Lad's answer is almost right, the problem with it is that by enclosing the loop with the accept the entire thing is done inside the rendezvous, meaning the Task and the calling-task are both involved, so you're not taking advantage of any parallelism/concurrency.
with Ada.Text_IO;
use Ada.Text_IO;
procedure ConTasks is
task type SimpleTask (Message: Character; HowMany:Positive) is
entry Start_Running;
end SimpleTask;
task body SimpleTask is
begin
accept Start_Running do
null;
end Start_Running;
for i in 1 .. HowMany loop
delay 0.1;
Ada.Text_IO.Put_Line("Hello from task "&Message);
end loop;
end SimpleTask;
task_a : SimpleTask('A', 5);
task_b : SimpleTask('B', 7);
task_c : SimpleTask('C', 4);
begin
task_a.Start_Running;
task_b.Start_Running;
task_c.Start_Running;
end ConTasks;
just to add to Jim's answer, you can use the entry, if you want control over when individual task should start.
This will give you more control over your task. Since, the tasks without any entries start executing as soon as you execute the binary.
with Ada.Text_IO;
use Ada.Text_IO;
procedure ConTasks is
task type SimpleTask (Message: Character; HowMany:Positive) is
entry Start_Running;
end SimpleTask;
task body SimpleTask is
begin
accept Start_Running do
for i in 1 .. HowMany loop
delay 0.1;
Ada.Text_IO.Put_Line("Hello from task "&Message);
end loop;
end Start_Running;
end SimpleTask;
task_a : SimpleTask('A', 5);
task_b : SimpleTask('B', 7);
task_c : SimpleTask('C', 4);
begin
task_a.Start_Running;
delay 2.0;
task_b.Start_Running;
delay 2.0;
task_c.Start_Running;
end ConTasks;

Delphi 2010 : UniDAC vs Indy-MultiThread safety handleing method

I am doing develop Indy based application.
Server has several Indy TCP Server components.
So It works under multi-threads and handles mysql db.
I have faced one problem.
That is about the exceptions of MySQL DB in threads.
When serveral threads attack to same db table, then It says me like follows
UniQuery_Mgr: Duplicate field name 'id'
UniQuery_Mgr: Field 'grp_id' not found //of course grp_id field is really existed.
Assertion failure (C:\Program Files (x86)\unidac539src\Source\CRVio.pas, line 255)
Commands out of sync; You can't run this command now
ReceiveHeader: Net packets out of order: received[0], expected[1]
UniQuery_Mgr: Cannot perform this operation on a closed dataset
How to do I ? UniQuery_Mgr is TUniQuery component.
and my query handling code is normally like this
Code 1
sql := 'SELECT * FROM data_writed;';//for example
UniQuery_Mgr.SQL.Clear;
UniQuery_Mgr.SQL.Add(sql);
UniQuery_Mgr.ExecSQL;
Code 2
try
sql := 'SELECT * FROM gamegrp_mgr;';
UniQuery_Mgr.SQL.Clear;
UniQuery_Mgr.SQL.Add(sql);
UniQuery_Mgr.ExecSQL;
if UniQuery_Mgr.RecordCount > 0 then
begin
MAX_GAME_GROUP_COUNT := UniQuery_Mgr.RecordCount + 1;
UniQuery_Mgr.First;
i := 1;
while not UniQuery_Mgr.Eof do
begin
Game_Group_ID[i] := UniQuery_Mgr.FieldByName('grp_id').AsInteger;
Game_Game_ID[i] := UniQuery_Mgr.FieldByName('game_id').AsInteger;
UniQuery_Mgr.Next;
Inc(i);
end;
end;
except
on E : Exception do
begin
EGAMEMSG := Format('GAME group read error: <%s> # %s',[ E.ToString, DateTimeToStr(now)]);
Exit;
end;
end;
Code 3
try
sql := 'UPDATE data_writed SET write_gamegrp = ' + QuotedStr('0') + ';';
UniQuery_Mgr.SQL.Clear;
UniQuery_Mgr.SQL.Add(sql);
UniQuery_Mgr.ExecSQL;
except
on E : Exception do
begin
EGAMEMSG := Format('data updating error: <%s> # %s',[ E.ToString, DateTimeToStr(now)]);
Exit;
end;
end;
My handling DB components is bad ? Other thread-safe method is existed???

Rethrowing a T-SQL Error

In the following routine that I found here:
ALTER PROCEDURE [dbo].[usp_RethrowError]
AS -- Return if there is no error information to retrieve.
IF ERROR_NUMBER() IS NULL
RETURN;
DECLARE #ErrorMessage NVARCHAR(4000),
#OriginalErrorNumber INT,
#RethrownErrorNumber INT,
#ErrorSeverity INT,
#ErrorState INT,
#ErrorLine INT,
#ErrorProcedure NVARCHAR(200);
-- Assign variables to error-handling functions that
-- capture information for RAISERROR.
SELECT
#OriginalErrorNumber = ERROR_NUMBER()
,#ErrorSeverity = ERROR_SEVERITY()
,#ErrorSeverity = ERROR_SEVERITY()
,#ErrorState = ERROR_STATE()
,#ErrorLine = ERROR_LINE()
,#ErrorProcedure = ISNULL(ERROR_PROCEDURE(),'-');
--Severity levels from 0 through 18 can be specified by any user.
--Severity levels from 19 through 25 can only be specified by members of the sysadmin fixed server role or users with ALTER TRACE permissions
IF #OriginalErrorNumber < 19
SET #RethrownErrorNumber = #OriginalErrorNumber
ELSE
SET #RethrownErrorNumber = 18
-- Building the message string that will contain original
-- error information.
SELECT
#ErrorMessage = N'Error %d, Level %d, State %d, Procedure %s, Line %d, ' + 'Message: ' + ERROR_MESSAGE();
-- Raise an error: msg_str parameter of RAISERROR will contain
-- the original error information.
RAISERROR (#ErrorMessage,
#ErrorSeverity,
#ErrorState,
#RethrownErrorNumber, -- parameter: original error number or 18, if the original was >=19.
#ErrorSeverity, -- parameter: original error severity.
#ErrorState, -- parameter: original error state.
#ErrorProcedure, -- parameter: original error procedure name.
#ErrorLine -- parameter: original error line number.
);
Can someone explain the following line:
SELECT
#ErrorMessage = N'Error %d, Level %d, State %d, Procedure %s, Line %d, ' + 'Message: ' + ERROR_MESSAGE();
I realize that the occurences of % are placeholders for a signed integers (%d) and a strinf (%s), but I don't understand which variables are mapped to these placeholders. They do not seem to map to the parameters specified in the RAISERROR call:
RAISERROR (#ErrorMessage,
#ErrorSeverity,
#ErrorState,
#RethrownErrorNumber, -- parameter: original error number or 18, if the original was >=19.
#ErrorSeverity, -- parameter: original error severity.
#ErrorState, -- parameter: original error state.
#ErrorProcedure, -- parameter: original error procedure name.
#ErrorLine -- parameter: original error line number.
);
I made two small changes to the subroutine, one to lower the severity if > 19 and the other to use the original State rather than always passing 1.
If you guys don't shoot this routine down with my minor changes down too badly, I will add logging teh error info to a user table just prior to rethrowing.
To call:
DECLARE #Zero INT
SET #Zero = 0
BEGIN TRY
SELECT 5 / #Zero
END TRY
BEGIN CATCH
PRINT 'We have an error...'
EXEC usp_RethrowError
END CATCH
Follow up questions:
1) The link above mentions that this routine would not work for deadlocks. Any reason why?
2) I added the "IF #OriginalErrorNumber < 19" part. I am not too concerned that if an error >18 occurs that the error will be rethrown will a severity of 18. In any event, I plab to abort and the original severity will be logged. Is there anything else in this routine that I need to be worried about?
Hope this will help!
You are on the right track, ErrorMessage is the pattern string that is consumed by RAISERROR. Looking at the grammar structure of RAISERROR will clear up the confusion:
RAISERROR ( { msg_id | msg_str | #local_variable }
{ ,severity ,state }
[ ,argument [ ,...n ] ] )
[ WITH option [ ,...n ] ]
The first three required arguments are the message pattern string (msg_str), severity, and state. These are followed by the optional arguments which will replace the substitution parameters in the msg_str.
So the code lets:
msg_str be ErrorMessage
severity be ErrorSeverity
state be ErrorState
arguments be RethrownErrorNumber, ErrorSeverity, ErrorState, ErrorProcedure, ErrorLine
Reference http://msdn.microsoft.com/en-us/library/ms178592.aspx