Rethrowing a T-SQL Error - sql-server-2008

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

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;

How to throw a specific Exception in Julia

I am doing test driven development in Julia.
The test expects a certain exception to be thrown.
How do I throw the expected exception?
I'm looping through a string and counting the occurrence of specific letters.
Any letter than 'A','C','G',or'T' should result in an exception
Running Julia version 1.2.0.
I have tried these alternatives:
throw(DomainError())
throw(DomainError)
throw("DomainError")
I expected those to work based on this resource:
https://scls.gitbooks.io/ljthw/content/_chapters/11-ex8.html
Here is a link to the problem I am trying to solve:
https://exercism.io/my/solutions/781af1c1f9e2448cac57c0707aced90f
(Heads up: That link may be unique to my login)
My code:
function count_nucleotides(strand::AbstractString)
Counts = Dict()
Counts['A'] = 0
Counts['C'] = 0
Counts['G'] = 0
Counts['T'] = 0
for ch in strand
# println(ch)
if ch=='A'
Counts['A'] += 1
# Counts['A'] = Counts['A'] + 1
elseif ch=='C'
Counts['C'] += 1
elseif ch=='G'
Counts['G'] += 1
elseif ch=='T'
Counts['T'] += 1
else
throw(DomainError())
end
end
return Counts
end
The test:
#testset "strand with invalid nucleotides" begin
#test_throws DomainError count_nucleotides("AGXXACT")
end
My error report, see the lines with: Expected and Thrown.
strand with invalid nucleotides: Test Failed at /Users/username/Exercism/julia/nucleotide-count/runtests.jl:18
Expression: count_nucleotides("AGXXACT")
Expected: DomainError
Thrown: MethodError
Stacktrace:
[1] top-level scope at /Users/shane/Exercism/julia/nucleotide-count/runtests.jl:18
[2] top-level scope at /Users/juliainstall/buildbot/worker/package_macos64/build/usr/share/julia/stdlib/v1.2/Test/src/Test.jl:1113
[3] top-level scope at /Users/username/Exercism/julia/nucleotide-count/runtests.jl:18
Test Summary: | Fail Total
strand with invalid nucleotides | 1 1
ERROR: LoadError: Some tests did not pass: 0 passed, 1 failed, 0 errored, 0 broken.
The MethodError comes from the call to DomainError -- there are no zero-argument constructor for this exception type. From the docs:
help?> DomainError
DomainError(val)
DomainError(val, msg)
The argument val to a function or constructor is outside the valid domain.
So there are one constructor which takes the value that was out of the domain, and one that, in addition, takes an extra message string. You could e.g. do
throw(DomainError(ch))
or
throw(DomainError(ch, "this character is bad"))

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???

Ada data exception infinite loop

When executing my user-created get procedure, in the exception block, my program handles my user-defined exceptions fine but goes into an infinite loop when a data exception occurs.
PROCEDURE Get (File: IN Ada.Text_IO.File_Type; Item : OUT Rational) IS
N: Integer;
D: Integer;
Dummy: Character; -- dummy character to hold the "/"
BEGIN -- Get
LOOP
BEGIN
Ada.Integer_Text_IO.Get(File => File, Item => N);
Ada.Text_IO.Get (File => File, Item => Dummy);
Ada.Integer_Text_IO.Get(File => File, Item => D);
Item := N/D;
if Dummy /= '/' then
raise BadFormat;
end if;
EXIT;
EXCEPTION
when ZeroDenominator =>
Ada.Text_IO.Put_Line("No Zeroes in Denominator, Please enter data again.");
when BadFormat =>
Ada.Text_IO.Put_Line("Incorrect Formatting, Correct Format is Numerator/Denominator.");
when ADA.IO_EXCEPTIONS.DATA_ERROR =>
Ada.Text_IO.Put_Line("Data Error Occurred, re-enter data like this: ");
Ada.Text_IO.Put_Line("Numerator/Denominator");
END;
END LOOP;
END Get;
This is the output I get:
Data Error Occurred, re-enter data like this:
Numerator/Denominator
Data Error Occurred, re-enter data like this:
Numerator/Denominator
Data Error Occurred, re-enter data like this:
Numerator/Denominator
Data Error Occurred, re-enter data like this:
Numerator/Denominator
(infinitely repeats)
Does anyone have any advice on this error? thanks

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

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.