Can we raise an exception within an exception? - oracle

Can we raise an exception within an exception, like RAISE my_exception within an WHEN OTHERS exception?
Additionally, I am trying to understand the output in the following 2 scenarios:
(Note that the only difference is the ordering of the exceptions)
Scenario 1
DECLARE
lc_exception Exception;
var1 number;
BEGIN
select 1
into var1
from dual
where 1=2;
EXCEPTION
when lc_exception then
dbms_output.put_line('This is my exception');
when no_data_found then
raise lc_exception;
when others then
dbms_output.put_line('There could be some other exception');
END;
Scenario 2
DECLARE
lc_exception Exception;
var1 number;
BEGIN
select 1
into var1
from dual
where 1=2;
EXCEPTION
when no_data_found then
raise lc_exception;
when lc_exception then
dbms_output.put_line('This is my exception');
when others then
dbms_output.put_line('There could be some other exception');
END;

Both scenarios have the similar error stack:
ORA-06510: PL/SQL: unhandled user-defined exception
ORA-06512: at line 20
ORA-01403: no data found
The only difference is the exact location (line 20 vs. 18) where the user defined exception is raised.
The select matches no rows thus no_data_found is raised by Oracle - ORA-01403: no data found in the error stack.
The exception is caught by the block's exception handler block.
In the exception handler an user defined exception lc_exception is raised.
Because the program has no other exception handlers the user defined exception is leaked to host environment (in my case that was sqlplus client) - ORA-06510: PL/SQL: unhandled user-defined exception and program terminates.
The exception propagation is similar in both scenarios because in both cases the same initial exception no_data_found is raised and there is only one exception handling block. Therefore the order of no_data_found and lc_exception handlers doesn't matter.
More detailed explanation about exception handling/propagation is found from: PL/SQL Error Handling in Oracle documentation.

You just need an exception handler to catch your local exception. If you raise an exception in an exception handler, it cannot be caught in the same exception hander.
DECLARE
lc_exception EXCEPTION;
var1 number;
BEGIN
BEGIN
SELECT 1
INTO var1
FROM dual
WHERE 1=2;
EXCEPTION -- this is where the exception will be raised
WHEN no_data_found THEN
raise lc_exception;
END;
dbms_output.put_line( 'This will never be executed.' );
EXCEPTION -- this is where the exception will be caught
WHEN lc_exception THEN
dbms_output.put_line( 'This is my exception' );
WHEN others THEN
dbms_output.put_line( 'There could be some other exception' );
END;
However, you may want to consider a slightly different method.
DECLARE
lc_exception EXCEPTION;
PRAGMA EXCEPTION_INIT( lc_exception, -20222 );
var1 NUMBER;
BEGIN
BEGIN
SELECT 1
INTO var1
FROM dual
WHERE 1=2;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE_APPLICATION_ERROR( -20222, 'This is my exception' );
END;
DBMS_OUTPUT.PUT_LINE( 'This will never be executed.' );
EXCEPTION
WHEN lc_exception THEN
DBMS_OUTPUT.PUT_LINE( SQLERRM );
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE( 'Some other exception: ' || SQLERRM );
END;

Related

Cannot handle raised exception in PL/SQL

I trying to handle exception if no rows found. In result I see an following error: PL/SQL: unhandled user-defined exception. A user-defined exception was raised by PL/SQL code, but not handled. What I doing wrong?
DECLARE
my_exception EXCEPTION;
result1 VARCHAR2(50);
result2 NUMBER;
BEGIN
SELECT brand, year INTO result1, result2 FROM cars WHERE car_id=200;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE my_exception; WHEN my_exception THEN
DBMS_OUTPUT.PUT_LINE('Exception has been thrown');
END;
You are successfully trapping the NO_DATA_FOUND exception, but within that exception handler you are then raising a new user-defined exception of your own (RAISE my_exception).
When you raise from an exception handler like this, it falls back to the parent block and looks for an exception handler there... but you have no parent block, and so on until it reaches the outermost block. If it isn't caught, it will finally throw the "unhandled user-defined exception" to the client.
If you want to continue execution and do nothing but emit that dbms_output message, then remove the "RAISE my_exception" line and everything to do with that exception. Just put the dbms_output in the handler for NO_DATA_FOUND.
If however you really do want to use a user defined exception, then nest the entire thing in a wrapping parent block and handle it there:
DECLARE
my_exception exception;
BEGIN
DECLARE
result1 VARCHAR2(50);
result2 NUMBER;
BEGIN
SELECT brand, year INTO result1, result2 FROM cars WHERE car_id=200;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE my_exception;
END;
EXCEPTION
WHEN my_exception THEN
dbms_output.put_line("my_exception raised")
END;
The exception handler in the outer block has already been called when the NO_DATA_FOUND exception was raised. If you raise another exception inside the exception handling block then it will not be caught at the same level; instead you either need to:
Wrap the RAISE statement inside its own PL/SQL anonymous block and catch the exception inside the exception handler of that block:
DECLARE
my_exception EXCEPTION;
result1 VARCHAR2(50);
result2 NUMBER;
BEGIN
SELECT brand, year INTO result1, result2 FROM cars WHERE car_id=200;
EXCEPTION
WHEN NO_DATA_FOUND THEN
BEGIN
RAISE my_exception;
EXCEPTION
WHEN my_exception THEN
DBMS_OUTPUT.PUT_LINE('Exception has been thrown');
END;
END;
/
Wrap the block raising the exception in an outer block and catch the exception there:
DECLARE
my_exception EXCEPTION;
BEGIN
DECLARE
result1 VARCHAR2(50);
result2 NUMBER;
BEGIN
SELECT brand, year INTO result1, result2 FROM cars WHERE car_id=200;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE my_exception;
END;
EXCEPTION
WHEN my_exception THEN
DBMS_OUTPUT.PUT_LINE('Exception has been thrown');
END;
/
Both of which output:
Exception has been thrown
fiddle

How to skip exception in nested block

I have 2 nested blocks in my program. If any error occurs in the 1st nested block then program will not execute further, and it will go to exception section and exit from the overall program.
But I do not want to be exit from my program. My program need to be executed for the 2nd nested block and further also, even an exception is raised in 1st nested block.
If I have program like this:
DECLARE
var_out VARCHAR2(10):= 'OUTER';
BEGIN
<<INNER1>>
DECLARE
var_in1 NUMBER:='INNER 1';
BEGIN
DBMS_OUTPUT.PUT_LINE(var_in1);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SQLERRM);
END;
<<INNER2>>
DECLARE
var_in2 VARCHAR2(10):='INNER 2';
BEGIN
DBMS_OUTPUT.PUT_LINE(var_in2);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SQLERRM);
END;
DBMS_OUTPUT.PUT_LINE(var_out);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SQLERRM);
END;
in my program inner1 block will throw value error exception so the overall program will not be executed.
How do I execute inner2 and outer block, even exception raised in inner1 block?
Because your exception in INNER1 occurs within the declaration section, it is beyond the scope of the block's exception handler. You need to wrap the whole INNER1 block in another block with its own exception handler.
declare
var_out varchar2(10):= 'OUTER';
begin
<<INNER1>>
begin
declare
var_in1 number:='INNER 1';
begin
dbms_output.put_line(var_in1);
exception
when others then
dbms_output.put_line('Error in innermost INNER1');
end;
exception
when others then
dbms_output.put_line('Error in INNER1 wrapper');
end;
<<INNER2>>
declare
var_in2 varchar2(10):='INNER 2';
begin
dbms_output.put_line(var_in2);
exception
when others then
dbms_output.put_line('Error in INNER2');
end;
dbms_output.put_line(var_out);
exception
when others then
dbms_output.put_line('Error at top level');
end;
Output:
Error in INNER1 wrapper
INNER 2
OUTER
Probably both OTHERS handlers are not needed - the above is just to illustrate the structure.

PL/SQL Handle Not Predefined Exceptions

How do you handle PL/SQL errors that are not predefined?
Predefined exceptions are the following:
ACCESS_INTO_NULL
CASE_NOT_FOUND
COLLECTION_IS_NULL
CURSOR_ALREADY_OPENED
DUP_VAL_ON_INDEX
INVALID_CURSOR
INVALID_NUMBER
NO_DATA_FOUND
PROGRAM_ERROR
ROWTYPE_MISMATCH
STORAGE_ERROR
SUBSCRIPT_BEYOND_COUNT
SUBSCRIPT_OUTSIDE_LIMIT
SYS_INVALID_ROWID
TOO_MANY_ROWS
VALUE_ERROR
ZERO_DIVIDE
You could use a WHEN OTHERS exception handler that looks at the SQLCODE. But you're generally better off defining an exception that you can handle. That is going to lead to cleaner code, it lets you map your exception name to a number just once, and makes user-defined exceptions look just like predefined exceptions.
declare
column_already_indexed exception;
pragma exception_init( column_already_indexed, -1408 );
begin
call_procedure;
exception
when column_already_indexed
then
null;
-- Do something with the exception
end;
You can handle errors outside of the predefined with the following:
BEGIN
buggyprocedure; --Call to a procedure that can throw exceptions
EXCEPTION
WHEN NO_DATA_FOUND THEN --Catch predefined exception
NULL; --Ignore no_data_found
WHEN OTHERS THEN --Catch all other exceptions
IF SQLCODE = -1408 THEN --Catch error -1408
NULL; --Ignore this exception
ELSEIF SQLCODE = -955 THEN --Catch error -955
anotherprocedure; --Call a different procedure
ELSE
RAISE; --Re-raise exception that we are not handling
END IF;
END;
You basically catch all other exceptions, then use if and else statements to check if the SQLCODE returned is one you are expecting and want to handle.

Oracle nested blocks and exception handling

DECLARE
string_of_5_chars VARCHAR2(5);
BEGIN
BEGIN
string_of_5_chars := 'Steven';
EXCEPTION
WHEN value_error THEN
RAISE no_data_found;
WHEN no_data_found THEN
dbms_output.Put_line ('Inner block');
END;
EXCEPTION
WHEN no_data_found THEN
dbms_output.Put_line ('Outer block');
END;
Answer says that the output will be 'Outer block' , Can somebody explain why the inner block would not be executed ? What is the precedence of exceptions in oracle
DECLARE
string_of_5_chars VARCHAR2(5);
BEGIN
BEGIN
string_of_5_chars := 'Steven'; -- Varchar has a size of 5 defined above. So it will throw a value_error(due to size constraints) exception.
EXCEPTION
WHEN value_error THEN -- This exception block will handle the error thrown above.
RAISE no_data_found; -- It raises a no_data _found exception which by rule has to be handled in the outer exception block. So it goes to the outer exception block.
WHEN no_data_found THEN
dbms_output.Put_line ('Inner block');
END;
EXCEPTION
WHEN no_data_found THEN
dbms_output.Put_line ('Outer block'); -- Exception is handled here which causes it to print 'Outer Block'
END;
Read here for more information about nested exception blocks.
You should consider the exception block's WHEN clauses as being similar to a regular CASE statement. The first WHEN that matches the condition executes, and the following WHEN clauses in that exception handler are skipped.
Therefore the second WHEN clause in the inner exception block is not in the code execution path at all, and the outer exception block catches the no_data_found error raised by the first WHEN clause of the nested exception.
Exception propagation in this scenario is explained here: http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/errors.htm#LNPLS00706
As string_of_5_chars := 'Steven'; raises an value_error, the corresponding Exception block is entered.
Inside the Exception block the no_data_found Exception is raised. Due to the raising-part this exception is then handled by the exception-handling of the outer block.
For more information check http://docs.oracle.com/cd/B19306_01/appdev.102/b14261/raise_statement.htm

ignore a user defined exception in oracle

In pl/sql I have some inner begin, end blocks and "Exception Others" blocks.
When I throw my user defined exception from an inner block I just want to catch this exception in the last "Exception userdef" block, not in the inner "Exception Others" blocks.
Any idea?
It sounds like you have something like this:
BEGIN
BEGIN
BEGIN
DO_SOMETHING; -- raises USERDEF_EXCEPTION
EXCEPTION
WHEN OTHERS THEN
DIE_HORRIBLY;
END;
EXCEPTION
WHEN OTHERS THEN
DIE_EVEN_MORE_HORRIBLY;
END;
EXCEPTION
WHEN USERDEF_EXCEPTION THEN
DO_SOMETHING_REASONABLE;
WHEN OTHERS THEN
DIE_INCREDIBLY_HORRIBLY;
END;
and you want to DO_SOMETHING_REASONABLE rather than DIE_HORRIBLY or DIE_EVEN_MORE_HORRIBLY. Sorry - you can't do that without providing a handler in the inner blocks for your exception. You'll have to do something like:
BEGIN
BEGIN
BEGIN
DO_SOMETHING; -- raises USERDEF_EXCEPTION
EXCEPTION
WHEN USERDEF_EXCEPTION THEN
RAISE;
WHEN OTHERS THEN
DIE_HORRIBLY;
END;
EXCEPTION
WHEN USERDEF_EXCEPTION THEN
RAISE;
WHEN OTHERS THEN
DIE_EVEN_MORE_HORRIBLY;
END;
EXCEPTION
WHEN USERDEF_EXCEPTION THEN
DO_SOMETHING_REASONABLE;
WHEN OTHERS THEN
DIE_INCREDIBLY_HORRIBLY;
END;
Share and enjoy.
/* package */
CREATE OR REPLACE PACKAGE exceptions_pkg AS
user_defined_exception EXCEPTION;
END exceptions_pkg;
/* block */
DECLARE
l_var1 NUMBER;
BEGIN
DBMS_OUTPUT.PUT_LINE('one');
DECLARE
l_var2 NUMBER;
BEGIN
DBMS_OUTPUT.PUT_LINE('two');
IF 1 < 2 THEN
RAISE exceptions_pkg.user_defined_exception;
END IF;
DBMS_OUTPUT.PUT_LINE('three');
END;
DBMS_OUTPUT.PUT_LINE('four');
EXCEPTION
WHEN exceptions_pkg.user_defined_exception THEN
DBMS_OUTPUT.PUT_LINE('five');
END;
-- anonymous block completed
/*
one
two
five
*/

Resources