Skip to Main Content

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question, arnab.

Asked: September 27, 2010 - 2:25 pm UTC

Last updated: September 27, 2010 - 4:08 pm UTC

Version: 9.1

Viewed 1000+ times

You Asked

--------------- not Done --------------------------------------------
create
or
replace
procedure p2(a in number,b out varchar2)
is
l number;
stop_run exception;
begin
begin
l:=a/0;
exception
when others then
raise stop_run;
end;
exception
when stop_run then
b:='p2 stop_run'||substr(SQLERRM ,1,200);
dbms_output.put_line('p2 '||b);
--raise;
when others then
b:='p2 others '||substr(SQLERRM ,1,200);
dbms_output.put_line('p2 '||b);
--raise;
end;

create
or
replace
procedure p1
is
b varchar2(1000);
begin
p2(1,b);
dbms_output.put_line('p1 1 '||b);
exception
when others then
dbms_output.put_line('p1 2'||b);
raise;
end;


17:23:46 SQL> exec p1;
p2 p2 stop_runUser-Defined Exception
p1 1 p2 stop_runUser-Defined Exception

PL/SQL procedure successfully completed.


create
or
replace
procedure p2(a in number,b out varchar2)
is
l number;
stop_run exception;
begin
begin
l:=a/0;
exception
when others then
raise stop_run;
end;
exception
when stop_run then
b:='p2 stop_run'||substr(SQLERRM ,1,200);
dbms_output.put_line('p2 '||b);
raise;
when others then
b:='p2 others '||substr(SQLERRM ,1,200);
dbms_output.put_line('p2 '||b);
raise;
end;


17:26:11 SQL> exec p1;
p2 p2 stop_runUser-Defined Exception
p1 2
BEGIN p1; END;

*
ERROR at line 1:
ORA-06510: PL/SQL: unhandled user-defined exception
ORA-01476: divisor is equal to zero
ORA-06512: at "MAITYA.P1", line 10
ORA-06512: at line 1



why is the difference in output variable values above?

and Tom said...

I hate your code.

http://asktom.oracle.com/pls/asktom/asktom.search?p_string=%22i+hate+your+code%22


Your first block is basically the logical equivalent of "when others then NULL". In short, it is the worst programming practice ever dreamed of.

You have a when others that calls stop_run. That is bad - that is ugly, that hides information, that makes debugging this code impossible. That is a really bad practice. Then you compound the error by catching stop_run (which is now when others IN DISGUISE) and not re-raiseing it. So your code silently SUCCEEDS, it is as if the error never happened. P1 is clueless that anything was wrong and just pretended everything was hunky dory.


Your second block of code makes the same mistake as the first - but this time instead of compounding the error - you just turn the real exception into a user defined one and raise it - allow it to propagate - so that p1 actually has a clue that there was an error that could not be accounted for (this is good - you WANT that to be the case, you want errors you cannot deal with to unwind your stack and propagate all of the way back up).



Let me put it very very simply:

you will catch only exceptions that are not exceptions. That means - the number of times you will code "when others" in your life will be TINY, SMALL, hardly ever (I'll let you know in a second when you may use that). You will only catch exceptions you are expecting (meaning they are not really exceptions!)

For example, you might have a lookup table, and you are normally expecting to find a value in the lookup table - but if you don't - it is "ok" (your CODE, your LOGIC dictates whether this is OK or not - it is not always OK, it can be OK). You would code that:

...
begin
select value into l_value from lookup where key = l_key;
exception
when no_data_found then
... maybe you set l_value to some default value here...
end;
.....


Now, if that 'fails' with no_data_found - you'll catch it, fix it, ignore it. If it fails with "ora-1555 snapshot too old", you won't catch it, you won't ignore it - it will cause your application to fail (you hit something unimaginable to you - it should fail). If it raises "too_many_rows" - you won't catch it, you won't ignore it - it will cause your application to fail (it should - your primary key just returned two records!! that is bad, something is really wrong, your data is botched and needs to be fixed).


The only time you will use when others is at the very top level of code. The bit of code that a CLIENT submits to the data base might look like this:

begin
   p1;
exception
   when others
   then
       use_autonomous_transaction_to_log_error( .... ); -- log it
       use_another_function_to_turn_it_into_something_useful_for_user( ... );
       raise_application_error( number_output_from_prev_function, 
                                string_output_from_prev_function );
end;



that is - you will LOG the error using an autonomous transaction (that is a function that can insert and commit without committing the already existing transaction - if any) and then might call another function that your entire team uses to map error codes into something "user friendly" to display on the screen and then raises that error (your own error code here now) to the application.


Stop catching exceptions you cannot deal with. I do not understand why people do that - it is just about the WORST programming practice *ever*

Is this answer out of date? If it is, please let us know via a Comment

More to Explore

PL/SQL demos

Check out more PL/SQL tutorials on our LiveSQL tool.

PL/SQL docs

PL/SQL reference manual from the Oracle documentation library