raise_application_error reuse of the error - oracle

I was wondering if theres a way in which I can decide that from "this day on" a user-defined error can act as a resource I can use over and over.
For example->
Raise_Application_Error (-20343, 'The balance is too low.');
So basically if I can use -20343 as key word(error code) and use again in a different procedure instead of raising it again and again..,
Is that possible?

Well, you'll have to raise it, somehow, Oracle can't know what you want to do if balance is too low.
Maybe you could create your own table of exceptions, e.g.
SQL> select * from my_exception;
ERR_CODE ERR_NAM ERR_MESSAGE
---------- ------- ------------------------------
-20343 bal_low Balance is too low
-20344 name_s Name can not begin with an "S"
Function accepts error code and returns message:
SQL> create or replace function f_myerr (par_err_code in my_exception.err_code%type)
2 return my_exception.err_message%type
3 is
4 retval my_exception.err_message%type;
5 begin
6 select err_message
7 into retval
8 from my_exception
9 where err_code = par_err_code;
10 return retval;
11 exception
12 when no_data_found then
13 return 'Exception does not exist';
14 end;
15 /
Function created.
This piece of code simulates "balance too low" error:
SQL> declare
2 l_balance number;
3 bal_low exception;
4 begin
5 select sal into l_balance
6 from emp
7 where ename = 'JONES';
8
9 if l_balance < 5000 then
10 raise bal_low;
11 end if;
12
13 exception
14 when bal_low then
15 raise_application_error(-20343, f_myerr(-20343));
16 end;
17 /
declare
*
ERROR at line 1:
ORA-20343: Balance is too low
ORA-06512: at line 15
SQL>
Another PL/SQL procedure might also find out that balance is too low, but you'd have to repeat such a code again, I'm afraid.
You can modify that code (i.e. table, function) to better suit your needs, but - that's what I understood for what you said so far.

Related

Is no-data-found exception not raised implicitly?

question 1:
I noticed the no-data-found exception not raised implicitly, do we need to raise them manually/explicitly?
Question 2:
I writing a SP package with 4 procedures in it and i'm writing below procedures on a single table.
the exception handling is repetitive in each procedure. is there any guidance/coding standard to keep it in a single place procedure#5 or function and call it there.
Select
Update
Insert
Delete
I guess if I keep it in a function it would raise the exception and return to the called procedure and continue to run ? but when we raise a exception the program should stop , right?
You have run into the only exception in PL/SQL exception handling - ORA-1403: no data found is suppressed when it is generated by a function called in a SQL context.
No data found exceptions work fine in PL/SQL anonymous blocks:
SQL> declare
2 v_number number;
3 begin
4 select 1 into v_number from dual where 1=0;
5 end;
6 /
declare
*
ERROR at line 1:
ORA-01403: no data found
ORA-06512: at line 4
And no data found also works as expected with a function called in a PL/SQL statement:
SQL> create or replace function exception_no_data_found return number is
2 v_number number;
3 begin
4 select 1 into v_number from dual where 1=0;
5 return v_number;
6 end;
7 /
Function created.
SQL> declare
2 v_number number;
3 begin
4 v_number := exception_no_data_found;
5 end;
6 /
declare
*
ERROR at line 1:
ORA-01403: no data found
ORA-06512: at "JHELLER.EXCEPTION_NO_DATA_FOUND", line 4
ORA-06512: at line 4
But when calling the function in a SQL context, the exception disappears:
SQL> select exception_no_data_found from dual;
EXCEPTION_NO_DATA_FOUND
-----------------------
I think the reason for this is that SQL internally uses the no data found exception to let the system know there are no more rows, so it's not always an exception. (This behavior is probably a bug but has been around for so long that it'll never change.)
To raise that exception in this situation, you must manually catch and raise a different exception like this:
SQL> create or replace function exception_no_data_found return number is
2 v_number number;
3 begin
4 select 1 into v_number from dual where 1=0;
5 return v_number;
6 exception when no_data_found then
7 raise_application_error(-20000, 'No data found.');
8 end;
9 /
Function created.
SQL> select exception_no_data_found from dual;
select exception_no_data_found from dual
*
ERROR at line 1:
ORA-20000: No data found.
ORA-06512: at "JHELLER.EXCEPTION_NO_DATA_FOUND", line 7
But the above situation is by far the weirdest part about Oracle exception handling. Other than this scenario, in general you are almost always better off by not doing any exception handling. Just let the program break; the exceptions will propagate up, and the application will get an exception that includes the full error message and the exact line number of the error. In practice, that information is almost always enough to debug any problems. Only add custom exception handling when you know for sure that you need to gather extra data.

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.

Exclude Extra Error Information from RAISE_APPLICATION_ERROR

I have raised an error message in a PL/SQL trigger and it works fine except that it returns more than the error I specify to the application. In addition to the custom error I get information about the line that the error happened on and the trigger.
For example it will get
ORA-20111: There is a custom error here
ORA-06512: at "{schema_name}.{trigger_name}", LINE 2
ORA-04088: error during execution of trigger {schema_name}.{trigger_name}<br>
.Operation canceled.
What I actually want is:
ORA-20111: There is a custom error here
How do I remove the extra information from the error message before it is returned to my application? My testing code below...
CREATE OR REPLACE TRIGGER CUSTOM_ERROR
BEFORE INSERT OR UPDATE ON {SCHEMA_NAME}.{TABLE_NAME} FOR EACH ROW
BEGIN
RAISE_APPLICATION_ERROR(-20111, 'There is a custom error here');
END;
You could use RAISE_APPLICATION_ERROR.
For example,
SQL> DECLARE
2 custom_err EXCEPTION;
3 PRAGMA EXCEPTION_INIT( custom_err, -20099 );
4 BEGIN
5 raise_application_error( -20099, 'This is a custom error' );
6 EXCEPTION
7 WHEN custom_err THEN
8 dbms_output.put_line( sqlerrm );
9 END;
10 /
ORA-20099: This is a custom error
PL/SQL procedure successfully completed.
SQL>
PRAGMA EXCEPTION_INIT is to give a custom error number, which could vary in a range from -20001 to -20999
However, you need to take care of the already-defined exceptions from being raised.
For example,
SQL> DECLARE
2 custom_err EXCEPTION;
3 PRAGMA EXCEPTION_INIT( custom_err, -20099 );
4 v number;
5 BEGIN
6 SELECT empno INTO v FROM emp WHERE empno = 1234;
7 raise_application_error( -20099, 'This is a custom error' );
8 EXCEPTION
9 WHEN custom_err THEN
10 dbms_output.put_line( sqlerrm );
11 END;
12 /
DECLARE
*
ERROR at line 1:
ORA-01403: no data found
ORA-06512: at line 6
SQL>
There is a NO_DATA_FOUND exception thrown. You could suppress that, however, it will leave the error stack with additional messages.
SQL> DECLARE
2 custom_err EXCEPTION;
3 PRAGMA EXCEPTION_INIT( custom_err, -20099 );
4 v number;
5 BEGIN
6 SELECT empno INTO v FROM emp WHERE empno = 1234;
7 raise_application_error( -20099, 'This is a custom error' );
8 EXCEPTION
9 WHEN no_data_found THEN
10 raise_application_error( -20099, 'This is a custom error' );
11 WHEN custom_err THEN
12 dbms_output.put_line( sqlerrm );
13 END;
14 /
DECLARE
*
ERROR at line 1:
ORA-20099: This is a custom error
ORA-06512: at line 10
SQL>
I would always want to log the errors as verbose as possible. The raise_application_error is good for a custom message to be displayed on the application, however, I would log all the errors in the program.
Please have a look at:
dbms_utility.format_error_stack
dbms_utility.format_error_backtrace

ORA-00932: inconsistent datatypes: expected - got -

I have been using Oracle(10g.2) as a PHP programmer for almost 3 years, but when I gave an assignment, I have tried to use the ref cursors and collection types for the first time. And I
've searched the web, when I faced with problems, and this ora-00932 error really overwhelmed me. I need help from an old hand.
Here is what I've been tackling with,
I want to select rows from a table and put them in a ref cursor, and then with using record type, gather them within an associative array. And again from this associative array, make a ref cursor. Don't ask me why, I am writing such a complicated code, because I need it for more complex assignment. I might be sound confusing to you, thus let me show you my codes.
I have 2 types defined under the types tab in Toad. One of them is an object type:
CREATE OR REPLACE
TYPE R_TYPE AS OBJECT(sqn number,firstname VARCHAR2(30), lastname VARCHAR2(30));
Other one is collection type which is using the object type created above:
CREATE OR REPLACE
TYPE tr_type AS TABLE OF r_type;
Then I create a package:
CREATE OR REPLACE PACKAGE MYPACK_PKG IS
TYPE MY_REF_CURSOR IS REF CURSOR;
PROCEDURE MY_PROC(r_cursor OUT MY_REF_CURSOR);
END MYPACK_PKG;
Package Body:
CREATE OR REPLACE PACKAGE BODY MYPACK_PKG AS
PROCEDURE MY_PROC(r_cursor OUT MY_REF_CURSOR) AS
rcur MYPACK_PKG.MY_REF_CURSOR;
sql_stmt VARCHAR2(1000);
l_rarray tr_type := tr_type();
l_rec r_type;
BEGIN
sql_stmt := 'SELECT 1,e.first_name,e.last_name FROM hr.employees e ';
OPEN rcur FOR sql_stmt;
LOOP
fetch rcur into l_rec;
exit when rcur%notfound;
l_rarray := tr_type( l_rec );
END LOOP;
CLOSE rcur;
--OPEN r_cursor FOR SELECT * FROM TABLE(cast(l_rarray as tr_type) );
END MY_PROC;
END MYPACK_PKG;
I commented out the last line where I open ref cursor. Because it's causing another error when I run the procedure in Toad's SQL Editor, and it is the second question that I will ask.
And lastly I run the code in Toad:
variable r refcursor
declare
r_out MYPACK_PKG.MY_REF_CURSOR;
begin
MYPACK_PKG.MY_PROC(r_out);
:r := r_out;
end;
print :r
There I get the ora-00932 error.
The way you are using the REF CURSOR is uncommon. This would be the standard way of using them:
SQL> CREATE OR REPLACE PACKAGE BODY MYPACK_PKG AS
2 PROCEDURE MY_PROC(r_cursor OUT MY_REF_CURSOR) AS
3 BEGIN
4 OPEN r_cursor FOR SELECT e.empno,e.ENAME,null FROM scott.emp e;
5 END MY_PROC;
6 END MYPACK_PKG;
7 /
Corps de package crÚÚ.
SQL> VARIABLE r REFCURSOR
SQL> BEGIN
2 MYPACK_PKG.MY_PROC(:r);
3 END;
4 /
ProcÚdure PL/SQL terminÚe avec succÞs.
SQL> PRINT :r
EMPNO ENAME N
---------- ---------- -
7369 SMITH
7499 ALLEN
7521 WARD
7566 JONES
7654 MARTIN
[...]
14 ligne(s) sÚlectionnÚe(s).
I'm not sure what you are trying to accomplish here, you're fetching the ref cursor inside the procedure and then returning another ref cursor that will have the same data. I don't think it's necessary to fetch the cursor at all in the procedure. Let the calling app do the fetching (here the fetching is done by the print).
Update: why are you getting the unhelpful error message?
You're using a cursor opened dynamically and I think that's part of the reason you are getting the unhelpful error message. If we use fixed SQL the error message is different:
SQL> CREATE OR REPLACE PACKAGE BODY MYPACK_PKG AS
2 PROCEDURE MY_PROC(r_cursor OUT MY_REF_CURSOR) AS
3 TYPE type_rec IS RECORD (qn number,
4 firstname VARCHAR2(30),
5 lastname VARCHAR2(30));
6 lt_record type_rec; /* Record type */
7 lt_object r_type; /* SQL Object type */
8 BEGIN
9 OPEN r_cursor FOR SELECT e.empno,e.ENAME,null FROM scott.emp e;
10 FETCH r_cursor INTO lt_record; /* This will work */
11 FETCH r_cursor INTO lt_object; /* This won't work in 10.2 */
12 END MY_PROC;
13 END MYPACK_PKG;
14 /
Package body created
SQL> VARIABLE r REFCURSOR
SQL> BEGIN
2 MYPACK_PKG.MY_PROC(:r);
3 END;
4 /
BEGIN
*
ERREUR Ó la ligne 1 :
ORA-06504: PL/SQL: Return types of Result Set variables or query do not match
ORA-06512: at "APPS.MYPACK_PKG", line 11
ORA-06512: at line 2
I outlined that currently in 10.2 you can fetch a cursor into a PLSQL record but not in a SQL Object.
Update: regarding the PLS-00306: wrong number or types of arguments
l_rarray is a NESTED TABLE, it needs to be initialized and then extended to be able to store elements. For example:
SQL> CREATE OR REPLACE PACKAGE BODY MYPACK_PKG AS
2 PROCEDURE MY_PROC(r_cursor OUT MY_REF_CURSOR) AS
3 lr_array tr_type := tr_type(); /* SQL Array */
4 BEGIN
5 FOR cc IN (SELECT e.empno, e.ENAME, NULL lastname
6 FROM scott.emp e) LOOP
7 lr_array.extend;
8 lr_array(lr_array.count) := r_type(cc.empno,
9 cc.ename,
10 cc.lastname);
11 /* Here you can do additional procedural work on lr_array */
12 END LOOP;
13 /* then return the result set */
14 OPEN r_cursor FOR SELECT * FROM TABLE (lr_array);
15 END MY_PROC;
16 END MYPACK_PKG;
17 /
Corps de package crÚÚ.
SQL> print r
SQN FIRSTNAME LASTNAME
---------- ------------------------------ -----------
7369 SMITH
7499 ALLEN
7521 WARD
[...]
14 ligne(s) sÚlectionnÚe(s).
For further reading you can browse the documentation for PL/SQL collections and records.

How to determine row/value throwing error in PL/SQL statement?

(Oracle PL/SQL)
If I have a simple SQL statement that is throwing an error, ie:
DECLARE
v_sql_errm varchar2(2048);
BEGIN
UPDATE my_table SET my_column = do_something(my_column)
WHERE my_column IS NOT NULL;
EXCEPTION
when others then
-- How can I obtain the row/value causing the error (unknown)?
v_sql_errm := SQLERRM;
insert into log_error (msg) values ('Error updating value (unknown): '||
v_sql_errm);
END;
Is there any way within the exception block to determine the row/value on which the query is encountering an error? I would like to be able to log it so that I can then go in and modify/correct the specific data value causing the error.
This can be done using DML error logging, if you are on 10gR2 or later.
An example:
SQL> create table my_table (my_column)
2 as
3 select level from dual connect by level <= 9
4 /
Tabel is aangemaakt.
SQL> create function do_something
2 ( p_my_column in my_table.my_column%type
3 ) return my_table.my_column%type
4 is
5 begin
6 return 10 + p_my_column;
7 end;
8 /
Functie is aangemaakt.
SQL> alter table my_table add check (my_column not in (12,14))
2 /
Tabel is gewijzigd.
SQL> exec dbms_errlog.create_error_log('my_table')
PL/SQL-procedure is geslaagd.
This creates an error logging table called err$_my_table. This table is filled by adding a log errors clause to your update statement:
SQL> begin
2 update my_table
3 set my_column = do_something(my_column)
4 where my_column is not null
5 log errors reject limit unlimited
6 ;
7 end;
8 /
PL/SQL-procedure is geslaagd.
SQL> select * from err$_my_table
2 /
ORA_ERR_NUMBER$
--------------------------------------
ORA_ERR_MESG$
--------------------------------------------------------------------
ORA_ERR_ROWID$
--------------------------------------------------------------------
OR
--
ORA_ERR_TAG$
--------------------------------------------------------------------
MY_COLUMN
--------------------------------------------------------------------
2290
ORA-02290: check constraint (RWK.SYS_C00110133) violated
AAGY/aAAQAABevcAAB
U
12
2290
ORA-02290: check constraint (RWK.SYS_C00110133) violated
AAGY/aAAQAABevcAAD
U
14
2 rijen zijn geselecteerd.
Prior to 10gR2, you can use the SAVE EXCEPTIONS clause: http://rwijk.blogspot.com/2007/11/save-exceptions.html
A solution using the SAVE EXCEPTIONS clause:
SQL> create table my_table (my_column)
2 as
3 select level from dual connect by level <= 9
4 /
Table created.
SQL> create function do_something
2 ( p_my_column in my_table.my_column%type
3 ) return my_table.my_column%type
4 is
5 begin
6 return 10 + p_my_column;
7 end;
8 /
Function created.
SQL> alter table my_table add check (my_column not in (12,14))
2 /
Table altered.
SQL> declare
2 e_forall_error exception;
3 pragma exception_init(e_forall_error,-24381)
4 ;
5 type t_my_columns is table of my_table.my_column%type;
6 a_my_columns t_my_columns := t_my_columns()
7 ;
8 begin
9 select my_column
10 bulk collect into a_my_columns
11 from my_table
12 ;
13 forall i in 1..a_my_columns.count save exceptions
14 update my_table
15 set my_column = do_something(a_my_columns(i))
16 where my_column = a_my_columns(i)
17 ;
18 exception
19 when e_forall_error then
20 for i in 1..sql%bulk_exceptions.count
21 loop
22 dbms_output.put_line(a_my_columns(sql%bulk_exceptions(i).error_index));
23 end loop;
24 end;
25 /
2
4
PL/SQL procedure successfully completed.
For very large data sets, you probably don't want to blow up your PGA memory, so be sure to use the LIMIT clause in that case.
try outputting your error and see if it gives you the information you are looking for. For example:
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.PUT_LINE(SQLERRM);
END;
For more detailing information regarding how execution arrived at the line in question, you could try displaying the output returned by these functions:
DBMS_UTILITY.format_error_stack:
Format the current error stack. This can be used in exception handlers to look at the full error stack.
DBMS_UTILITY.format_error_backtrace:
Format the backtrace from the point of the current error to the exception handler where the error has been caught. NULL string is returned if no error is currently being handled.
Try this (not tested):
DECLARE
cursor c1 is
select key_column, my_column
from my_table
WHERE my_column IS NOT NULL
ORDER BY key_column;
my_table_rec my_table%ROWTYPE;
BEGIN
FOR my_table_rec in c1
LOOP
UPDATE my_table SET my_column = do_something(my_column)
WHERE key_column = my_table_rec.key_column;
END LOOP;
EXCEPTION
when others then
insert into log_error (msg) values ('Error updating key_column: ' || my_table_rec.key_column || ', my_column: ' || my_table_rec.my_column);
END;
PL/SQL defines 2 global variables to refer to errors:
SQLERRM : SQL error Message
SQLERRNO: SQL error Number
This is readable in the EXCEPTION block in your PL/SQL.
DECLARE
x number;
BEGIN
SELECT 5/0 INTO x FROM DUAL;
EXCEPTION
WHEN OTHERS THEN:
dbms_output.put_line('Error Message: '||SQLERRM);
dbms_output.put_line('Error Number: '||SQLERRNO);
END;

Resources