How to skip exception in nested block - oracle

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.

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

Similar to finally Block (JAVA) in Oracle PL/SQL Block

As in Java there is finally block which executed in all conditions.
Is there any similar function in Oracle PL/SQL which will be executed whenever procedure completes its execution even a return statement is used?
There is no equivalent of FINALLY but you can simulate it using nested PL/SQL blocks;
DECLARE
-- Your variables.
return_early BOOLEAN := FALSE;
BEGIN
-- Do something
DECLARE
-- Local variables in "try" block
BEGIN
-- Equivalent of "try" block
-- Do something that may raise an exception
IF some_condition THEN
return_early := TRUE;
-- you could also use "GOTO end_try;" rather than surrounding the
-- following statements in an "ELSE" statement
ELSE
-- Do something else that may raise an exception
END IF;
EXCEPTION
WHEN your_exception THEN
-- Equivalent of "catch" block
END;
<<end_try>>
-- Handle "finally" here, after end of nested block.
-- Note: you can only see variables declared in this outer block
-- not variables local to the nested PL/SQL block.
IF return_early THEN
RETURN;
END IF;
-- Continue and do more stuff.
END;
/
Another thread on Stackoverflow can help out here : Exception handling in pl/sql
Where Tony says :
"You can created nested blocks:"
create or replace procedure Trial
is
Begin
begin
---Block A--
EXCEPTION
when others then
insert into error_log values('error');
end;
begin
--Block B ----
end;
end;
The solutions provided in other answers does not implement exact try-catch-finally logic. E.g. the finally should be executed even if exception is reraised and even if in did not handled at all. In other words, the finally block must be called always.
The most similar behavior to Java's finally is the following. Unfortunately, here we have to wrap finally block into a procedure. We have no choice in PL/SQL.
declare
begin
declare
EXPECTED_EXCEPTION_RECOVERABLE exception;
EXPECTED_EXCEPTION_FATAL exception;
EXPECTED_EXCEPTION_UNHANDLED exception;
procedure finally is
begin
dbms_output.put_line('FINALLY section executed');
end;
begin
dbms_output.put_line('Trying dangerous section...');
/* uncomment to try different behavior */
--raise EXPECTED_EXCEPTION_RECOVERABLE;
--raise EXPECTED_EXCEPTION_FATAL;
--raise EXPECTED_EXCEPTION_UNHANDLED;
dbms_output.put_line('Dangerous section is executed successfully');
FINALLY();
exception
when EXPECTED_EXCEPTION_RECOVERABLE then
dbms_output.put_line('Recoverable exception handled.');
FINALLY();
when EXPECTED_EXCEPTION_FATAL then
dbms_output.put_line('Fatal exception handled and will be reraised');
FINALLY();
RAISE;
when OTHERS then
dbms_output.put_line('Unhandled exception is just reraised after finally section');
FINALLY();
RAISE;
end;
end;
/
It seems, this solution looks enough readable;
You can create a custom Exception and then Raise it at the end of your other exceptions, this custom exception must be on an outside block:
BEGIN
BEGIN
--DO SOME CODE HERE
EXCEPTION
WHEN NO_DATA_FOUND THEN
--HANDLE EXCEPTION
RAISE CUSTOM_EXCEPTION;
END;
EXCEPTION
WHEN CUSTOM_EXCEPTION THEN
--HANDLE THE FINALLY CODE HERE
END;

Can we raise an exception within an exception?

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;

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