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
Related
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.
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.
Trigger code -
CREATE OR REPLACE TRIGGER tib_ms_merchant_group
BEFORE INSERT
ON ms_merchant_group FOR EACH ROW DECLARE integrity_error EXCEPTION;
PRAGMA EXCEPTION_INIT (integrity_error, -20001);
errno INTEGER;
errmsg CHAR(200);
dummy INTEGER;
FOUND BOOLEAN;
BEGIN
-- Errors handling
EXCEPTION
WHEN integrity_error THEN
raise_application_error(errno, errmsg);
END;
Error -
"PLS-00103: Encountered the symbol "EXCEPTION" when expecting one of the following:
:= . ( # % ;
The symbol ";" was substituted for "EXCEPTION" to continue."
Probably you should declare some content after the BEGIN directive to make the function do something actual? This is (probably) demanded by the SQL syntax. That is why EXCEPTION cant be put straight after BEGIN
As #sanfor said, you need to have something between the BEGIN-EXCEPTION block. I could compile the trigger -
SQL> CREATE
2 OR
3 replace TRIGGER tib_ms_merchant_group BEFORE
4 INSERT
5 ON emp FOR EACH ROW DECLARE integrity_error EXCEPTION;
6
7 PRAGMA EXCEPTION_INIT (integrity_error, -20001);
8 errno INTEGER;
9 errmsg CHAR(200);
10 dummy INTEGER;
11 FOUND BOOLEAN;
12 BEGIN
13 NULL;
14 EXCEPTION
15 WHEN integrity_error THEN
16 raise_application_error(errno, errmsg);
17 END;
18 /
Trigger created.
SQL> sho err
No errors.
SQL>
Just replace the table name and add the required trigger logic after BEGIN.
I have below code to_date('1311313', 'yymmdd') which actually throws exception with saying invalid month. Which is can manage as
exception
when others then
sop('date format is wrong');
Here the problem is everything will get caught which I do not want to do as if some other error will occur then also it will pass the message date format is wrong. I also do not want to create a user defined exception. Just want to know which exception is being thrwon out so that I can use in my code like below
exception
when name_of_exception then
sop('date format is wrong');
The Internally Defined Exceptions section of the Oracle Database PL/SQL Language Reference says:
An internally defined exception does not have a name unless either PL/SQL gives it one (see "Predefined Exceptions") or you give it one.
You code throws the exception ORA-01830:
SQL> select to_date('1311313', 'yymmdd') from dual
*
ERROR at line 1:
ORA-01830: date format picture ends before converting entire input string
Since it is not one of the Predefined Exceptions, you must give it a name yourself:
declare
ex_date_format exception;
pragma exception_init(ex_date_format, -1830);
v_date date;
begin
select to_date('1311313', 'yymmdd')
into v_date
from dual;
exception
when ex_date_format then
sop('date format is wrong');
end;
/
There are at least two approaches to handle different exceptions raised during an attempt to convert character literal to a value of DATE data type:
Define as many exception names and associate them with Oracle error codes, using exception_init pragma, as many exceptions to_date() function is able to raise.
Create a stand alone, or part of a package, wrap-up function for to_date() function, with one when others exception handler.
Personally I lean toward the second one.
SQL> create or replace package util1 as
2 function to_date1(
3 p_char_literal in varchar2,
4 p_date_format in varchar2
5 ) return date;
6 end;
7 /
Package created
SQL> create or replace package body util1 as
2
3 function to_date1(
4 p_char_literal in varchar2,
5 p_date_format in varchar2
6 ) return date is
7 begin -- in this situation it'll be safe to use `when others`.
8 return to_date(p_char_literal, p_date_format);
9 exception
10 when others then
11 raise_application_error(-20001, 'Not a valid date');
12 end;
13
14 end;
15 /
Package body created
Now, there is only one exception to handle, -20001 Not a valid date, and your PL/SQl block might look like this:
SQL> set serveroutput on;
-- [1] otherwise, for '1311313' the ORA-01830 exception would be raised
SQL> declare
2 not_a_valid_date exception;
3 pragma exception_init(not_a_valid_date, -20001);
4 l_res date;
5 begin
6 l_res := util1.to_date1('1311313', 'yymmdd');
7 exception
8 when not_a_valid_date then
9 dbms_output.put_line(sqlerrm);
10 -- or other handler sop('date format is wrong');
11 end;
12 /
ORA-20001: Not a valid date
-- [2] otherwise, for '000000' the ORA-01843(not a valid month)
-- exception would be raised
SQL> declare
2 not_a_valid_date exception;
3 pragma exception_init(not_a_valid_date, -20001);
4 l_res date;
5 begin
6 l_res := util1.to_date1('000000', 'yymmdd');
7 exception
8 when not_a_valid_date then
9 dbms_output.put_line(sqlerrm);
10 -- or other handler sop('date format is wrong');
11 end;
12 /
ORA-20001: Not a valid date
Is there oracle table that stores user defined exceptions?
No.
User-defined exceptions are, like other variables, defined in PL/SQL blocks and have whatever scope the PL/SQL variable would have. So, for example
DECLARE
my_exception EXCEPTION;
BEGIN
RAISE my_exception;
EXCEPTION
WHEN my_exception
THEN
dbms_output.put_line( 'Caught my_exception' );
END;
will create the user-defined exception my_exception but the exception will only exist for the scope of the anonymous PL/SQL block. You can define exceptions in packages so that they can be referenced by multiple PL/SQL blocks. And you can use the exception_init pragma to associate user-defined exceptions with particular error codes
SQL> ed
Wrote file afiedt.buf
1 DECLARE
2 my_exception EXCEPTION;
3 pragma exception_init( my_exception, -20001 );
4 BEGIN
5 RAISE my_exception;
6* END;
SQL> /
DECLARE
*
ERROR at line 1:
ORA-20001:
ORA-06512: at line 5
Or you can use the raise_application_error function either alone or in concert with user-defined exception variables
SQL> ed
Wrote file afiedt.buf
1 DECLARE
2 my_exception EXCEPTION;
3 pragma exception_init( my_exception, -20001 );
4 BEGIN
5 RAISE_APPLICATION_ERROR( -20001, 'This is my error text' );
6 EXCEPTION
7 WHEN my_exception
8 THEN
9 dbms_output.put_line( 'Caught my_exception' );
10 dbms_output.put_line( sqlerrm );
11* END;
SQL> /
Caught my_exception
ORA-20001: This is my error text
PL/SQL procedure successfully completed.
There is no data dictionary table to store user-defined exceptions because there are so many (potentially conflicting) ways that your code might define those exceptions. From a good exception management standpoint, you would generally want to come up with a consistent way of defining your application-specific exceptions and sticking to that approach. Personally, I like a package that defines all my exceptions and associates them with error codes
SQL> ed
Wrote file afiedt.buf
1 create or replace package error_pkg
2 as
3 invalid_name exception;
4 pragma exception_init( invalid_name, -20001 );
5 invalid_address exception;
6 pragma exception_init( invalid_address, -20002 );
7* end;
SQL> /
Package created.
Assuming all my user-defined exceptions are all defined like this, I can go to the package definition for all my definitions.