PL/SQL exception handling: do nothing (ignore exception) - oracle

This is a question I am asked very frequently. Since I couldn't find any exact duplicate on stackoverflow, I thought I'd post it as a reference.
Question:
In PL/SQL, I know how to catch exceptions and execute code when they are caught, and how to propagate them to the calling block.
For example, in the following procedure, the NO_DATA_FOUND exception is handled directly, while all other exceptions are raised to the calling block:
CREATE OR REPLACE PROCEDURE MY_PROCEDURE()
IS
BEGIN
do_stuff();
EXCEPTION
WHEN NO_DATA_FOUND THEN
-- Do something
handle_exception();
WHEN OTHERS THEN
-- Propagate exception
RAISE;
END;
But what command should I use to ignore one or all raised exceptions and return execution control back to the calling block?

While I agree that 99% of the time it is bad practice to silently ignore exceptions without at least logging them somewhere, there are specific situations where this is perfectly acceptable.
In these situations, NULL is your friend:
[...]
EXCEPTION
WHEN OTHERS THEN
NULL;
END;
Two typical situations where ignoring exceptions might be desirable are:
1) Your code contains a statement which you know will fail occasionally and you don't want this fact to interrupt your program flow.
In this case, you should enclose you statement in a nested block, as the following example shows:
CREATE OR REPLACE PROCEDURE MY_PROCEDURE()
IS
l_empoyee_name EMPLOYEES.EMPLOYEE_NAME%TYPE;
BEGIN
-- Catch potential NO_DATA_FOUND exception and continue
BEGIN
SELECT EMPLOYEE_NAME
INTO l_empoyee_name
FROM EMPLOYEES
WHERE EMPLOYEE_ID = 12345;
EXCEPTION
WHEN NO_DATA_FOUND THEN
NULL;
WHEN OTHERS THEN
RAISE;
END;
do_stuff();
EXCEPTION
WHEN OTHERS THEN
-- Propagate exception
RAISE;
END;
Note that PL/SQL generally does not allow for the On Error Resume Next type of exception handling known from Visual Basic, where all exceptions are ignored and the program continues to run as if nothing happened (see On error resume next type of error handling in PL/SQL oracle). You need to explicitly enclose potentially failing statements in a nested block.
2) Your procedure is so unimportant that ignoring all exceptions it throws will not affect your main program logic. (However, this is very rarely the case and can often result in a debugging nightmare in the long run)
BEGIN
do_stuff();
EXCEPTION
WHEN OTHERS THEN
-- Ignore all exceptions and return control to calling block
NULL;
END;

Another scenario when it does make sense to silently ignore exception:
When you call a script that is expected to create an object if it does not exist, and you do not have a create-or-replace syntax for that object.
PLSQL objects have a create-or-replace syntax, but tables and indexes do not.
Then we can put such scripts in a block and ignore the raised exception.

Related

Should Rollback be issued before raise application error?

This is a portion of my procedure. The Rollback did not happen when the procedure failed.
1. Should the Rollback be issued before raise application error?
2. In that case will backtrace work with raise application error. It is tracked in log table . However want to know if raise application error will issue correctly backtrace if rollbacked.
EXCEPTION
WHEN OTHERS THEN
/*Log that some other error occured and backtrace*/
eps_datareduction_run_log ( 5, 'CREDIT', current_timestamp, 'FAILED', 'CREDIT: Error Number:'||sqlcode||'Error Code:'||substr(sqlerrm, 1, 200)||'-backtrace:'||dbms_utility.format_error_backtrace, sysdate, user, null, null );
lv_err_msg := 'Unexcpected Error '||SUBSTR(sqlerrm,1,255);
raise_application_error(-20000,lv_err_msg || '- backtrace - ' || dbms_utility.format_error_backtrace);
ROLLBACK;
Rollback should be issued first, then do all logical operation you want to do in Log tables and then Raise. Raise is kind of the end part of program, which technically tells user that this specific error has occurred and program have ended.
See if there's a COMMIT inside eps_datareduction_run_log? That could be the reason why rollback didn't occur. It's still a good idea to rollback before any other actions in exception handler and use autonomous_transaction pragma in your logging subroutines.

Are following statements executed in any case if the first one throws an exception?

When an exception happens at the first select statement. Will the second select statement and the function then be executed in any case? Or will all following statements be skipped ?
BEGIN
SELECT ...
SELECT ...
procedure_that_performs_select();
EXCEPTION
WHEN NO_DATA_FOUND THEN ...
END
In PLSQL, the occurrence of an exception stops program exectuion at the point of the exception and jumps to the EXCEPTION block, if any, for handling, else raises the exception to the client.
You can see this behavior in a test block.
Here we will print before and after the exception, and can observe the next statement after the exception does not get printed:
BEGIN
DBMS_OUTPUT.PUT_LINE('About to throw an exception');
RAISE_APPLICATION_ERROR(-20001,'Exception');
DBMS_OUTPUT.PUT_LINE('Done throwing exception');
EXCEPTION WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Handling the exception');
END;
/
Result:
About to throw an exception
Handling the exception
Note: in the example you provided, the EXCEPTION block only handles NO DATA FOUND exceptions, so other types of exceptions will be raised instead of running through the exception handler. But in any case, things will stop processing at the point of the exception.
Once the control goes to an exception block, it doesn't go back to the begin or declare section of the pl/sql block.
Following the same, if there is an error in your first select statement, the exception block will be executed and the respective handler would be used. In case, you have neither mentioned the respective handler nor a WHEN OTHERS, the control will go to the calling environment (either any procedure or interface/ IDE).
In case, you still wish to run the second select statement, you could write another pl/sql block in exception handler.

What is the purpose of RAISE_APPLICATION_ERROR?

I understand that RAISE_APPLICATION_ERROR associates a message error which only has a meaning to the user. But can't the user just write a similar exception the following way?
DECLARE
e_negative EXCEPTION;
BEGIN
IF v_sid < 0 THEN
RAISE e_negative;
...
EXCEPTION
WHEN e_negative THEN
DBMS_OUTPUT.PUT_LINE ('An id cannot be negative');
raise_application_error does more than print an error message to the console (like dbms_output.put_line does).
First, it's an actual error - it fails the statement, terminates the current block's execution, and propagates to outer blocks (similar to throw in Java or raise in Python).
Second, it actually returns this error regardless of the console. dbms_output's messages may be turned off or ignored, depending on the client. Raising an application error allows you to return the details of the failure, regardless of the client.

Determine Type of PL/SQL Exception

In an OTHERS exception block I'd like to display the type of exception.
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE( -- I want to display the type of exception here -- );
END;
I would prefer a robust exception block rather than a trivial EXCEPTION WHEN OTHERS THEN without any specific purpose. Always remember, you catch exception and then DO something about it. Else, you need to RE-RAISE it to give the handle back to the caller.
In your exception block, add the following :
DBMS_UTILITY. FORMAT_ERROR_STACK;
DBMS_UTILITY.FORMAT_ERROR_BACKTRACE
This will give you the error stack and the backtrace procedure will let you know the exactnline number of error. Since EXCEPTION takes the handle from the caller, thus the caller never comes to know about the error raised. Include RAISE statement to re-raise the error and give it back to the caller. Better have an error logging table to have the errors logged for future research.
And regarding when others, please read my article, http://lalitkumarb.wordpress.com/2014/05/02/when-others-then-null-a-bug/

What will this give as output

declare
v number;
begin
select 1
into v
from dual
where 1=2;
exception
when others then
dbms_output.put_line('A');
when no_data_found then
dbms_output.put_line('B');
end;
On executing this I am getting an error but i had read it somewhere that in exception WHEN OTHERS, if placed first will always get executed suppressing other type of exceptions . I was asked this question in an interview and I as above . can somebody please confirm
You will get the following error in 11.2G:
Error at line 1
ORA-06550: line 11, column 3:
PLS-00370: OTHERS handler must be last among the exception handlers of a block
ORA-06550: line 0, column 0:
PL/SQL: Compilation unit analysis terminated
You should always have OTHER as the last exception handling block
The WHEN OTHERS clause is used to trap all remaining exceptions that have not been handled by your Named System Exceptions and Named Programmer-Defined Exceptions.
If you have only the WHEN OTHERS block in your code, then "PL/SQL procedure successfully completed." will be the message. Since the OTHER will never be encountered. A when others is almost always a BUG unless it is immediately followed by a RAISE. The point of an exception block is to catch exceptional conditions you are EXPECTING, handle them gracefully and continue. You should only catch the exceptions you are expecting and can do something about. Let the others propagate out so you can detect them (so you see them)
My suggestion would be "IGNORE WHEN OTHERS TOTALLY"
Executing this code block will raise the exception PLS-00370 (documented here):
PLS-00370: OTHERS handler must be last among the exception handlers of a block
Cause: One or more exception handlers appear after an OTHERS handler. However, the OTHERS handler must be the last handler in a
block or subprogram because it acts as the handler for all exceptions
not named specifically.
Action: Move the OTHERS handler so that it follows all specific exception handlers.
you just have to leave the OTHERS exception in the last place .
For instance:
BEGIN
something
EXCEPTION
THEN WHEN DUP_VAL_ON_INDEX
do something
WHEN OTHERS THEN
last exception do something
END;

Resources