How to handle general exceptions in PL/SQL - oracle

I looked at Google and here but couldn't find an answer for this.
I don't want to treat all exceptions in Oracle.
What I want is just get the return code of the last statement and check if it is 0 (zero). If it is, the command was executed ok. If not, there was some error and then I want the procedure to exit and raise a message saying only that there was an error and the command that caused that error.
Thanks in advance!

If I'm interpreting your question correctly, it sounds like you want to ignore all exceptions and manually check the SQLCODE after every statement. If my understanding is correct, I'm afraid there's not really a way to do that in PL/SQL. When errors occur the system WILL raise an exception - there's no way around it. What you CAN do is to record a description of where the program is in its execution and print that from a generic exception handler, similar to the following:
PROCEDURE SOME_PROCEDURE IS
strCheckpoint VARCHAR2(2000);
rowSome_table SOME_TABLE%ROWTYPE;
BEGIN
strCheckpoint := 'SELECT FROM SOME_TABLE';
SELECT *
INTO rowSome_table
FROM SOME_TABLE
WHERE SOME_COLUMN = 42;
strCheckpoint := 'UPDATE SOME_OTHER_TABLE';
UPDATE SOME_OTHER_TABLE
SET A_COLUMN = rowSome_table.A_COLUMN
WHERE KEY_COLUMN = rowSome_table.KEY_COLUMN;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Something happened at checkpoint ''' ||
strCheckpoint ||
''' SQLCODE=' || SQLCODE ||
' SQLERRM=' || SQLERRM);
RETURN;
END; -- SOME_PROCEDURE
Share and enjoy.

Not sure I got your problem right but does this look like you need ?
SQL> create function get_number return number
2 is
3 begin
4 return 1;
5 end;
6 /
SQL> begin
2 if get_number != 0 then
3 raise_application_error(-20000,'Error raised !');
4 end if;
5 end;
6 /
begin
*
Error in line 1:
ORA-20000: Error raised !
ORA-06512: на line 3
or
SQL> declare
2 user_def_exc exception;
3 begin
4 if get_number!=0 then
5 raise user_def_exc;
6 end if;
7 exception
8 when user_def_exc then
9 dbms_output.put_line('We ave got a problem');
10 end;
11 /
We ave got a problem

You can use the following in SQLPLus:
SQL>whenever sqlerror exit 1
This will terminate the execution of your script upon an error with exit value 1

Related

migrate raiserror from SYBASE ASE to ORACLE

I am migrating stored procedures to pl/sql blocks, and I have little knowledge in error handling in oracle and nothing in sybase can you help me.
example: sql SYBASE
DELETE table_1
WHERE N=0
SELECT #myrowcount = ##rowcount, #myerror = ##error, #mystat = ##sqlstatus
if (#myerror <> 0)
begin
raiserror 40900 "Error: When Generating Exception List #table_1 (error= %1!)", #mystat
select #cod_err= 1
return #cod_err
end
Edit: sql oracle i dont know if this is right
begin
DELETE table_1
WHERE N=0
EXCEPTION WHEN OTHERS THEN
SWV_error := SQLCODE;
end;
v_mi_error := SWV_error;
if v_mi_error != 0 then
RAISE_APPLICATION_ERROR(-40900,'Error: When Generating Exception List table_1');
return;
end if;
Which error do you expect for delete? It'll either delete some rows, or won't. If table (or column) doesn't exist, code wouldn't even compile so you wouldn't reach runtime error.
Anyway: in Oracle, it looks like this:
begin
delete table_1 where n = 0;
exception
when others then
raise_application_error(-20000, 'Error: ' || sqlerrm);
end;
/
others is exception which handles various things; you don't really care which error it is, as could be any error. Oracle reserved error codes from -20000 to -20999 for us, developers so you have to pick one of these (which means that -40900 won't work).
sqlerrm is error message (its description). If you want, you can get its code via sqlcode.
Example which shows how it actually works (not with delete, though) is query that fetches employee name for non-existent employee number. That raises predefined no_data_found error (whose code is -01403) so you could handle it, directly, or - as my previous example shows - use others.
SQL> declare
2 l_name varchar2(10);
3 begin
4 select ename
5 into l_name
6 from emp
7 where empno = -1;
8 exception
9 when others then
10 raise_application_error(-20000, 'Error: ' || sqlcode ||': '|| sqlerrm);
11 end;
12 /
declare
*
ERROR at line 1:
ORA-20000: Error: 100: ORA-01403: no data found
ORA-06512: at line 10
SQL>

Duplicate error message when error handling

I have this procedure:
CREATE OR REPLACE PROCEDURE procA (paramA VARCHAR2, paramB VARCHAR2, output_value OUT VARCHAR2)
IS
BEGIN
IF paramA IS NOT NULL THEN
BEGIN --1
SELECT columA
INTO output_value
FROM tableA
WHERE columnB = paramA;
EXCEPTION
WHEN NO_DATA_FOUND THEN
-- 2
SELECT columB
INTO output_value
FROM tableB
WHERE columnC = paramB;
END;
ELSE
SELECT columB
INTO output_value
FROM tableB
WHERE columnC = paramB;
END IF;
EXCEPTION
WHEN OTHERS THEN
RAISE_APPLICATION_ERROR (-20008, 'Error: ' || SQLCODE || ' ' || SUBSTR (SQLERRM, 1, 200));
END procA;
I run this call:
DECLARE
output VARCHAR2 (10);
BEGIN
procA ('valueA', 'valueB', output);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE (SQLERRM); --3
END;
With these parameters the first SELECT statement gets a NO_DATA_FOUND (1). And handled with the that EXCEPTION. Then, I run the second SELECT statement (2) and I get again, a NO_DATA_FOUND exception.
Why do I get a duplicate NO_DATA_FOUND message in (3), like this:
ORA-20008: Error: 100 ORA-01403: no data found
ORA-01403: no data found
The first exception is handled, I expected that the last error handling will return just one message.
You aren't seeing a single error being duplicated; you're seeing two errors, which happen to be the same.
If you add a format_error_stack call to your final exception hander you can see that both of the thrown exceptions are in that stack:
...
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(DBMS_UTILITY.FORMAT_ERROR_STACK);
RAISE_APPLICATION_ERROR (-20008, 'Error: ' || SQLCODE || ' ' || SUBSTR (SQLERRM, 1, 200));
END procA;
/
The call from your anonymous block then shows:
ORA-01403: no data found
ORA-06512: at "<schema>.PROCA", line 13
ORA-01403: no data found
ORA-06512: at "<schema>.PROCA", line 6
ORA-20008: Error: 100 ORA-01403: no data found
ORA-01403: no data found
db<>fiddle
It might be a bit clearer if you vary the errors, e.g.:
declare
l_dummy varchar2(1);
begin
begin
select 'x' into l_dummy from all_users;
exception when too_many_rows then
select 'x' into l_dummy from all_users where 1=0;
end;
exception when others then
dbms_output.put_line(dbms_utility.format_error_stack);
dbms_output.put_line('caught <' || sqlerrm || '>');
end;
/
which produces:
ORA-01403: no data found
ORA-06512: at line 7
ORA-01422: exact fetch returns more than requested number of rows
ORA-06512: at line 5
caught <ORA-01403: no data found
ORA-01422: exact fetch returns more than requested number of rows>
db<>fiddle
The SQLERRM call combines both error messages, though the documentation doesn't seem to say that will happen. Oracle support note 2438311.1 seems to suggest it shouldn't, but it does all of the db<>fiddle versions and in 12.2.0.1 and 19.11 at least.
But, what if I just want the last one?
If you're on Oracle 12c or later, you can use UTL_CALL_STACK.ERROR_MSG(1) to get the first error from the stack, instead of using SQLERRM.
...
EXCEPTION
WHEN OTHERS THEN
RAISE_APPLICATION_ERROR (-20008,
'Error: ' || SQLCODE || ' ' || SUBSTR (UTL_CALL_STACK.ERROR_MSG(1), 1, 200));
END procA;
/
which will now just show:
ORA-20008: Error: 100 no data found
db<>fiddle (and another one for my earlier example.)
You can read more about that function in the documentation.
There is a difference between handling and catching an exception.
You're using RAISE_APPLICATION_ERROR, which throws the exception to the caller although you've caught it once and since you catch it two times - you have two errors.

Catching multiple exception at once in oracle

Please let me know whether it is possible to catch multiple exception at same time in oracle. Not like 1 user defined and 1 is oracle default .I need to catch multiple user defined exception at same time . Kindly let me know how to do .
Thank you !
Certainly, there is - if I understood the question correctly. It is called WHEN OTHERS. Though, people usually misuse it, especially when they use
exception
when others then
null;
end;
as it successfully hides any errors that might appear. WHEN OTHERS is OK during development process, but might be really bad in production, especially if it doesn't contain raise.
Yes, you can do what you want from "When Others" as indicated by #Littlefoot or bulk processing errors (not covered here). But additionally you can have an OR condition exception name clause on the WHEN . It's not very useful as generally requires more code the 2 separate WHEN condition, but it is valid syntax. The following demonstrates various error definition methods and exception processing.
create table except_demo ( id integer, col1 varchar2(20));
insert into except_demo (id, col1)
select 5,'OK' from dual union all
select 6,'Too Many' from dual union all
select 6,'Still' from dual;
select id, count(*) from except_demo group by id;
create or replace procedure catcher(which_in integer, select_in boolean default False)
is
e_user_1 exception;
e_user_2 exception;
invalid_which_range exception;
appErr_inactive_acct exception;
sample_ora_error exception;
pragma exception_init (sample_ora_error, -00060);
rae exception;
rae_num constant integer := -20100;
pragma exception_init (rae, -20100);
col1_value except_demo.col1%type;
begin
dbms_output.put( 'Enter catcher(' || which_in || ') Result=>');
if which_in > 8
then raise invalid_which_range;
end if ;
if select_in
then
select col1
into col1_value
from except_demo
where id = which_in;
dbms_output.put_line('Select Successful 1 row selected.');
else
case which_in
when 1 then raise e_user_1;
when 2 then raise e_user_2;
when 3 then raise appErr_inactive_acct;
when 4 then raise sample_ora_error;
else raise_application_error(rae_num, 'Which_In=' || which_in || ' invalid. Please specify number 1-7 only');
end case;
end if;
exception
when e_user_1
then dbms_output.put_line('Error e_user_1'); -- user error
when e_user_2
then dbms_output.put_line('Error e_user_2');
when no_data_found or too_many_rows
then dbms_output.put_line('Select except_demo where id=' || which_in ||'; Returned 0 or more than 1 row. Must return exactly 1.' ); -- oracle predefined error
when sample_ora_error
then dbms_output.put_line('Ora Error:: ' || sqlerrm ); -- oracle error NOT predefined
when appErr_inactive_acct
then dbms_output.put_line('Error Account id ' || which_in || ' is inactive.'); -- user error
when rae
then dbms_output.put_line(sqlerrm);
end catcher;
declare
do_select boolean;
begin
for i in 1..9
loop
do_select := (i between 5 and 7);
catcher(i,do_select);
end loop;
exception
when others
then
dbms_output.put_line('Error returned from catcher=>' || sqlerrm);
raise;
end ;
drop procedure catcher;
drop table except_demo;
In a live environment the dbms_output statement would be replaced writing the message and other information to a exception log table and NOT dbms_output.
I have a very minor disagreement with Littlefoot. I firmly believe that what ever is written in development, whether intended or not, will run in production. Too often it is the unintended that gets you into trouble. Therefore the example of a misused WHEN OTHERS is invalid even in development.

Control Loop Statement from Exception Block Oracle

I want to know if there is any way in oracle to redirect the control from exception block to the current begin/end block.
Following is the code snippet. i variable loops through the table data sets, I want to update table i; if there is any exception go to the exec1 exception block,do the stuff and then try updating table i again.
If control goes to the exec2 exception then continue to the next iteration.
I want help as after performing steps in exec1 exception, how can I try updating table(i) again; so that if it fails again it is handled in exec1 exception block.
FOR i in c_tables
LOOP
BEGIN
---label---
Update (i);
DBMS_OUTPUT.PUT_LINE ('ROWS UPDATED IS: ' || SQL%ROWCOUNT);
EXCEPTION
WHEN exec1 THEN
BEGIN
do_stuff();
goto label;
EXCEPTION WHEN exec2 THEN
do_stuff();
continue;
END;
END;
END LOOP;
I tried to simulate what you have, in Scott's schema.
It seems that you'd be good if you moved the label in front of the BEGIN, not after it. Have a look:
SQL> DECLARE
2 l_deptno_dflt NUMBER := 50;
3 BEGIN
4 FOR i IN (SELECT distinct deptno FROM emp)
5 LOOP
6 <<this_is_label>> -- put label here ...
7 BEGIN
8 -- <<this_is_label>> -- ... not here
9 UPDATE emp
10 SET deptno = l_deptno_dflt
11 WHERE deptno = i.deptno;
12
13 DBMS_OUTPUT.put_line ('rows updated is ' || SQL%ROWCOUNT);
14 EXCEPTION
15 WHEN OTHERS
16 THEN
17 DBMS_OUTPUT.put_line ('this is exec1');
18
19 BEGIN
20 l_deptno_dflt := 40;
21 goto this_is_label;
22 END;
23 END;
24 END LOOP;
25 END;
26 /
this is exec1
rows updated is 6
rows updated is 5
rows updated is 3
PL/SQL procedure successfully completed.
SQL>
If it is where you initially put it, it raises the mentioned PLS-00375 error.

How to pass an error details in PL/SQL exception propagation without a package state?

Currently I'm using the following pattern to record error details in PL/SQL exception propagation. Please see the commented code below for the details. I'm happy with it as the error handling code doesn't clutter the whole code base and all the details why an error was triggered can be recorded.
Unfortunately there is an unwanted side-effect of package state introduced by v_error variable.
How I can pass an error detail information in PL/SQL exception propagation without introducing a package state ? (I want to eliminate package state to make deployment easier.)
Using different exceptions like rule_2_failure_ex and rule_3_failure_ex is not the solution I'm looking for as
there is no need to handle the error conditions differently
for troubleshooting it is very important to be able to record arbitrary information
(I'm already using a logging so the error condition information is available, but it's not in "the right place".)
I'm looking for Oracle 11g solution but 12c solution (if different than in 11g) is also welcome as one day I might end working with 12c too (personally I don't care about 10g).
-- this is a simplied example to address to question only
create or replace package so50 is
procedure run(p_num in number);
end;
/
show errors
create or replace package body so50 is
processing_failure_ex exception;
-- package state that I'd like to eliminate
v_error varchar2(32767);
-- in reality the processing and details are more complex
procedure p3(p_num in number) is
begin
if p_num = 3
then
-- it's important to be able to record arbitrary information at this point
v_error := 'Failed to process rule 3: (p_num = ' || p_num || ')';
raise processing_failure_ex;
end if;
end;
-- the comments on p3 apply
procedure p2(p_num in number) is
begin
if p_num = 2
then
v_error := 'Failed to process rule 2: (p_num = ' || p_num || ')';
raise processing_failure_ex;
end if;
end;
procedure p1(p_num in number) is
begin
p2(p_num);
p3(p_num);
exception
when others then
v_error := v_error
|| ' Additional details of failure.';
raise;
end;
procedure run(p_num in number) is
begin
v_error := null;
begin
p1(p_num);
exception
when processing_failure_ex then
-- in reality an error recovery will be tried first and only then
-- the error will be forwarded to a monitoring framework that will
-- raise an alert for human action
dbms_output.put_line('Error details: ' || v_error);
raise;
end;
exception
when others then
-- out of the scope of the question
raise;
end;
end;
/
show errors
You could use raise_application_error with an error code tied to your exception:
create or replace package body so50 is
processing_failure_ex exception;
pragma exception_init(processing_failure_ex, -20999);
And raise with the message you want:
raise_application_error(-20999,
'Failed to process rule 3: (p_num = ' || p_num || ')', true);
When you want to store the whole stack you can use dbms_utility.format_error_stack:
dbms_output.put_line('Error details:');
dbms_output.put_line(dbms_utility.format_error_stack);
So removing v_error altogether:
create or replace package so50 is
procedure run(p_num in number);
end;
/
create or replace package body so50 is
processing_failure_ex exception;
pragma exception_init(processing_failure_ex, -20999);
-- in reality the processing and details are more complex
procedure p3(p_num in number) is
begin
if p_num = 3
then
-- it's important to be able to record arbitrary information at this point
raise_application_error(-20999,
'Failed to process rule 3: (p_num = ' || p_num || ')', true);
end if;
end;
-- the comments on p3 apply
procedure p2(p_num in number) is
begin
if p_num = 2
then
raise_application_error(-20999,
'Failed to process rule 2: (p_num = ' || p_num || ')', true);
end if;
end;
procedure p1(p_num in number) is
begin
p2(p_num);
p3(p_num);
exception
when others then
raise_application_error(-20999,
'Additional details of failure', true);
end;
procedure run(p_num in number) is
begin
begin
p1(p_num);
exception
when processing_failure_ex then
-- in reality an error recovery will be tried first and only then
-- the error will be forwarded to a monitoring framework that will
-- raise an alert for human action
dbms_output.put_line('Error details:');
dbms_output.put_line(dbms_utility.format_error_stack);
raise;
end;
exception
when others then
-- out of the scope of the question
raise;
end;
end;
/
Calling that gets:
SQL> set serveroutput on
SQL> exec so50.run(1);
PL/SQL procedure successfully completed.
SQL> exec so50.run(2);
ORA-20999: Additional details of failure
ORA-06512: at "STACKOVERFLOW.SO50", line 42
ORA-20999: Failed to process rule 2: (p_num = 2)
ORA-06512: at "STACKOVERFLOW.SO50", line 64
ORA-06512: at line 1
Error details:
ORA-20999: Additional details of failure
ORA-06512: at "STACKOVERFLOW.SO50", line 42
ORA-20999: Failed to process rule 2: (p_num = 2)
SQL> exec so50.run(3);
ORA-20999: Additional details of failure
ORA-06512: at "STACKOVERFLOW.SO50", line 42
ORA-20999: Failed to process rule 3: (p_num = 3)
ORA-06512: at "STACKOVERFLOW.SO50", line 64
ORA-06512: at line 1
Error details:
ORA-20999: Additional details of failure
ORA-06512: at "STACKOVERFLOW.SO50", line 42
ORA-20999: Failed to process rule 3: (p_num = 3)
In both cases the stack trace before the 'Error details:' is coming from the final out-of-scope raise; if that was temporarily squashed (just for a demo, not suggesting you really squash it!) you'd just see:
SQL> exec so50.run(3);
PL/SQL procedure successfully completed.
Error details:
ORA-20999: Additional details of failure
ORA-06512: at "STACKOVERFLOW.SO50", line 42
ORA-20999: Failed to process rule 3: (p_num = 3)
You can use different exception numbers for the various procedures and scenarios of course, I've just used a common one to simplify things for now. They only need to be named (tied with a name via a pragma) if you want to catch them by name. And if you do you could have all the exceptions defined in one place.
My current solution based on #AlexPoole answer:
Exception Package
-- encapsulates the uglyness to keep calling code clean
create or replace package so50_ex is
-- exception type and error code reserved for this purpose only
general_ex exception;
general_ex_code constant number := -20999;
pragma exception_init(general_ex, -20999);
procedure raise(p_msg in varchar2, p_ex_code in number default -20999);
function full_error_stack return varchar2;
end;
/
show errors
create or replace package body so50_ex is
procedure raise(p_msg in varchar2, p_ex_code in number default -20999) is
begin
raise_application_error(p_ex_code,
substrb(p_msg, 1, 2048),
true);
end;
function full_error_stack return varchar2 as
-- will always fit to buffer as format_error_stack returns 2000 bytes at
-- maximum
v_stack varchar2(32767) := dbms_utility.format_error_stack;
begin
-- might not fit to buffer as format_error_backtrace return length is not
-- limited
v_stack := v_stack ||
substrb(dbms_utility.format_error_backtrace, 1, 30767);
return v_stack;
end;
end;
/
show errors
Usage Example
create or replace package so50 is
-- a user can always have his own exceptions
processing_failure_ex exception;
processing_failure_ex_code constant number := -20100;
pragma exception_init(processing_failure_ex, -20100);
procedure run(p_num in number);
end;
/
show errors
create or replace package body so50 is
procedure p3(p_num in number) is
begin
if p_num = 3
then
-- use specific exception
so50_ex.raise('Failed to process rule 3: (p_num = ' || p_num || ')',
processing_failure_ex_code);
end if;
end;
procedure p2(p_num in number) is
begin
if p_num = 2
then
-- use default exception
so50_ex.raise('Failed to process rule 2: (p_num = ' || p_num || ')');
end if;
end;
procedure p1(p_num in number) is
begin
p2(p_num);
p3(p_num);
exception
when processing_failure_ex then
dbms_output.put_line('ERROR RECOVERED SUCCESFULLY.');
dbms_output.put_line('DETAILS:');
dbms_output.put_line(so50_ex.full_error_stack);
when others then
so50_ex.raise('Additional details of failure.');
end;
procedure run(p_num in number) is
begin
p1(p_num);
exception
when others then
dbms_output.put_line('EXCEPTION: ' || so50_ex.full_error_stack);
raise;
end;
end;
/
show errors

Resources