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
Related
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;
This try catches the exception:
try die X::AdHoc;
say "Got to the end";
The output shows that the program continues:
Got to the end
If I attempt it with shell and a command that doesn't exit with 0, the try doesn't catch it:
try shell('/usr/bin/false');
say "Got to the end";
The output doesn't look like an exception:
The spawned command '/usr/bin/false' exited unsuccessfully (exit code: 1)
in block <unit> at ... line ...
What's going on that this makes it through the try?
The answer is really provided by Jonathan Worthington:
https://irclog.perlgeek.de/perl6-dev/2017-04-04#i_14372945
In short, shell() returns a Proc object. The moment that object is sunk, it will throw the exception that it has internally if running the program failed.
$ 6 'dd shell("/usr/bin/false")'
Proc.new(in => IO::Pipe, out => IO::Pipe, err => IO::Pipe, exitcode => 1, signal => 0, command => ["/usr/bin/false"])
So, what you need to do is catch the Proc object in a variable, to prevent it from being sunk:
$ 6 'my $result = shell("/usr/bin/false"); say "Got to the end"'
Got to the end
And then you can use $result.exitcode to see whether it was successful or not.
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.
In a ruby script that sends information to the TWILIO API, I have a string of characters of characters that their (Twilio's) API outputs. I then have the console output it so I can save it as a variable and reuse it later:
#client = Twilio::REST:Client.new account_sid, auth_token
call = #client.account.calls.create({:from => 'incoming', :to => 'outgoing', :url => 'url', :method => 'GET'})
puts call.sid
This part is functional, but now when I rename the variable (// #incoming_Cid=call.sid //) as to input it into a MySQL database, I bump into an issue. (The 34 character ID has numbers and letters, so I define the datatype as VARCHAR).
begin
dbh = DBI.connect("DBI:Mysql:db_name:localhost",
"user", "pass")
dbh.do ("INSERT INTO calls (column_name)" #//Select the column to insert
"VALUES (incoming_Cid)") #Insert the 34 character string.
dbh.commit
puts "Customer SID has been recorded"
rescue
puts "A database occurred"
puts "Error code: #{e.err}"
puts "Error message: #{e.errstr}"
ensure
dbh.disconnect if dbh
end
Right here at the dbh.do ("INSERT INTO calls " line if I put the incoming_Cid variable in the VALUES() instead of seeing a 34-char-string, like CA9321a83241035b4c3d3e7a4f7aa6970d, I literally see 'incoming_Cid' appear in the database when I execute select * in calls.
How can I resolve this issue?
You need to use string interpolation: "VALUES (#{#incoming_Cid})"
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