Having some confusion in exception types - oracle

We can pass error message, error code to front end using USER_DEFINED EXCEPTION or PREDEFINED EXCEPTION (others, SQLERRM, SQLCODE) using OUT PARAMETER MODE, then why we use RAISE_APPLICATION_ERROR procedure?
What is the difference between RAISE_APPLICATION_ERROR and pragma EXCEPTION_INIT?
I have Googled these questions, but can't get a clear answer - that is why posted here.

See this similar question. The answer to both of those is that RAISE_APPLICATION_ERROR is the canonical way to define custom error messages to be displayed in the client application. Yes, you could also pass custom messages back using an OUT parameter, or DBMS_OUTPUT, or something similar, but those are not standardized - you'll have to add code on both the server and client side to handle them.
DECLARE
my_exception EXCEPTION;
PRAGMA EXCEPTION_INIT(my_exception, -20001);
BEGIN
raise my_exception; -- message is default "ORA-20001: "
END;
/
BEGIN
raise_application_error(-20001, 'My custom error message'); -- message is "ORA-20001: My custom error message"
END;
/
This is often useful when you want to give a more helpful, descriptive error message.
DECLARE
age NUMBER;
BEGIN
age := 'hello';
EXCEPTION when VALUE_ERROR then
raise_application_error(-20001, 'Age must be a number.');
END;
/
So instead of a generic "PL/SQL: numeric or value error: character to number conversion error", we can tell the user exactly what the problem is: "Age must be a number".

A user-defined exception doesn't provide any message to the calling application. It'll just get a generic 'ORA-06510: PL/SQL: unhandled user-defined exception'. raise_application_error lets you pass a message describing the actual problem.
declare
out_of_stock exception;
begin
raise out_of_stock;
end;
/
ERROR at line 1:
ORA-06510: PL/SQL: unhandled user-defined exception
ORA-06512: at line 4
begin
raise_application_error(-20000, 'Cheese is out of stock');
end;
/
ERROR at line 1:
ORA-20000: Cheese is out of stock
ORA-06512: at line 2
pragma exception_init(exception_name, error_code) lets you associate your own user-defined exception with a system error code. This can be useful from a programming perspective, but ultimately it doesn't add any value for the caller:
declare
invalid_day_of_month exception;
pragma exception_init(invalid_day_of_month, -1847);
d date;
begin
d := to_date('99-JAN-2020','DD-MON-YYYY');
exception
-- Pointless exception handler - just to demo raising a user-defined exception
when invalid_day_of_month then raise;
end;
/
ERROR at line 2:
ORA-01847: day of month must be between 1 and last day of month
ORA-06512: at line 10
ORA-06512: at line 7
Passing success/failure status via OUT parameters is usually a bad idea because the procedure will appear to have completed successfully, and the caller has to read the status to see whether it really succeeded or not.

Related

PL/SQL VALUE_ERROR exception not raised where expected

I am sure my question has a simple theoretical answer but I cannot find it.
I have a procedure which accepts as parameter a NUMBER. It also have the VALUE_ERROR and OTHERS exceptions:
create or replace procedure test( p1 number) is
begin
null;
exception
when VALUE_ERROR then
RAISE_APPLICATION_ERROR(-20001, 'value_error');
when others then
RAISE_APPLICATION_ERROR(-20000, 'others');
end;
I am executing the procedure with a VARCHAR2 paramter:
execute test('a');
... an I expect that the error message displayed to be
ORA-20001 value_error
but, unfortunately, I got:
Error report - ORA-06502: PL/SQL: numeric or value error: character to
number conversion error ORA-06512: at line 1
06502. 00000 - "PL/SQL: numeric or value error%s"
*Cause: An arithmetic, numeric, string, conversion, or constraint error
occurred. For example, this error occurs if an attempt is made to
assign the value NULL to a variable declared NOT NULL, or if an
attempt is made to assign an integer larger than 99 to a variable
declared NUMBER(2).
*Action: Change the data, how it is manipulated, or how it is declared so
that values do not violate constraints.
Can anyone explain this, or share a link where it is explained why I do not receive expected error message?
Thank you very much,
As Nicholas mentioned you don't get your message because the exception is thrown not inside the procedure but before executing it.
Let's look at an example:
create or replace procedure test( p1 number) is
begin
null;
exception
when VALUE_ERROR then
RAISE_APPLICATION_ERROR(-20001, 'PROC value_error');
when others then
RAISE_APPLICATION_ERROR(-20000, 'PROC others');
end;
/
begin
test('a');
exception
when VALUE_ERROR then
RAISE_APPLICATION_ERROR(-20001, 'OUT value_error');
when others then
RAISE_APPLICATION_ERROR(-20000, 'OUT others');
end;
What happens here is that you're calling a procedure that requires number as parameter so Oracle tries conversion during execution of anonymous block. You can't see the message from the procedure because before entering the procedure the conversion exception is thrown.
Now let's see what happens if we change the procedure:
create or replace procedure test( p1 varchar2) is
param number;
begin
param := p1;
exception
when VALUE_ERROR then
RAISE_APPLICATION_ERROR(-20001, 'PROC value_error');
when others then
RAISE_APPLICATION_ERROR(-20000, 'PROC others');
end;
/
begin
test('a');
exception
when VALUE_ERROR then
RAISE_APPLICATION_ERROR(-20001, 'OUT value_error');
when others then
RAISE_APPLICATION_ERROR(-20000, 'OUT others');
end;
Or just:
begin
test('a');
end;
to see the error thrown in the procedure.
Now the procedure requires number within its body; when execution reaches that point it throws the conversion error, from within the procedure itself.

in oracle what is the deference between raising exception and reraising

in oracle what is the deference between raising exception and reraising exception 'with simple example please' and what is the deference between exception and normal if statement that print error if it happens?
One big difference between raising an exception vs. re-raising an exception is that with a RAISE; the original error message is preserved, e.g. (this is a contrived example, hopefully you get the idea):
create table t (x varchar2(1));
begin
insert into t values ('aa');
exception
when others then
dbms_output.put_line(sqlerrm);
raise;
end;
ORA-12899: value too large for column "XXSPS"."T"."X" (actual: 2, maximum: 1)
vs.
declare
value_too_large exception;
pragma exception_init (value_too_large, -12899);
begin
insert into t values ('aa');
exception
when value_too_large then
raise value_too_large;
end;
ORA-12899: value too large for column (actual: , maximum: )
In the 2nd case, we raise the "value_too_large" exception which has no reference back to the original exception that had the column name or length information.
When you are inside an exception handler in an exception section, you can re-raise the exception that “got you there” by issuing an unqualified RAISE statement as follows:
RAISE;
Since you do not specify an exception, the PL/SQL runtime engine re-raises the current exception (whose error number would be returned by a call to the SQLCODE function).
Here is an example of using raise in this way:
EXCEPTION
WHEN OTHERS
THEN
send_error_to_pipe (SQLCODE);
RAISE;
END;

Pragma Exception init with constant

I store the error codes my stored procedures may throw (by calling raise_application_error) as constants. One of my procedures has to catch the error another one throws, and I am trying to do this with pragma exception_init.
It seems that it only accepts numeric literals (as it explicitly says so in the error message I get), but it would be great to be able not to hardwire the error codes I otherwise store in constants. Is there some workaround for this, so that I could achieve the same as if I wrote:
pragma exception_init(myException, pkg_constants.error_not_null);
You can't use the package constant, or any variable, in the pragma.
What you could do is define the exception itself and its associated pragma in your package:
create package pkg_constants as
MyException exception;
error_not_null constant pls_integer := -20001;
pragma exception_init(myException, -20001);
end pkg_constants;
/
You still have to repeat the error number, but at least only in the same file. You can then refer to that package-defined exception in a handler without having to redeclare it:
exception
when pkg_constants.MyException then
...
For example:
set serveroutput on
begin
raise_application_error(pkg_constants.error_not_null, 'Threw it');
exception
when pkg_constants.MyException then
dbms_output.put_line('Caught it');
raise;
end;
/
Error report -
ORA-20001: Threw it
ORA-06512: at line 6
Caught it

Checking for Error during execution of a procedure in oracle

create or replace procedure proc_advertisement(CustomerID in Number,
NewspaperID in number,
StaffID in Number,
OrderDate in date,
PublishDate in date,
Type in varchar,
Status in varchar,
Units in number) is
begin
insert into PMS.Advertisement(CustomerID, NewspaperID, StaffID, OrderDate, PublishDate,
Type, Status, Units)
values(CustomerID,NewspaperID, StaffID, OrderDate, PublishDate,
Type, Status, Units);
dbms_output.put_line('Advertisement Order Placed Successfully');
end;
How to check for if any error has occurred during the execution of the procedure and if any error has occurred then I wish to display an error message.
First of all, Oracle itself will raise an error message if any error occurs while running the procedure - for example:
ORA-02291: integrity constraint (EMP.MGR_FK) violated - parent key not Found
You can handle errors explicitly by writing an exception handler, but unless you do this well you are quite likely to just obfuscate the problem. For example you could simply add this (just before the END of your procedure:
EXCEPTION
WHEN OTHERS THEN
RAISE_APPLICATION_ERROR(-20001,'An error occured');
But now your user won't know what kind of error, whereas before they could infer that it was that the specified Manager did not exist. You could show the original error also like this:
EXCEPTION
WHEN OTHERS THEN
RAISE_APPLICATION_ERROR(-20001,'An error occured: '||SQLERRM);
if that adds any value. Or you could just show a generic error and then write the value of SQLERRM to a log table.
You can also handle particular exceptions: for example
PROCEDURE ... IS
e_invalid_fk EXCEPTION;
PRAGMA EXCEPTION_INIT(e_invalid_fk,-2291);
BEGIN
...
EXCEPTION
WHEN e_invalid_fk THEN
IF SQLERRM LIKE '%(EMP.MGR_FK)%' THEN
raise_application_error(-20001,'Invalid manager specified');
ELSE
RAISE;
END IF;
END;
Note the RAISE: if any part of your exception handler doesn't issue either a RAISE or a RAISE_APPLICATION_ERROR then you are effectively sweeping the exception under the carpet - the user will think the procedure worked.
By the way, DBMS_OUTPUT.PUT_LINE is great for trying things out and debugging, in SQL Plus or an IDE, but it has no place in real code as users and applications that call the procedure will never see the output it produces.

Providing a more meaningful message when an error is raised in PL/SQL

Suppose I have a PL/SQL function that selects one value from a table. If the query returns no records, I wish for the NO_DATA_FOUND error to propagate (so that the calling code can catch it), but with a more meaningful error message when SQLERRM is called.
Here is an example of what I am trying to accomplish:
FUNCTION fetch_customer_id(customer_name VARCHAR2) RETURN NUMBER;
customer_id NUMBER;
BEGIN
SELECT customer_id
INTO customer_id
FROM CUSTOMERS
WHERE customer_name = fetch_customer_id.customer_name;
RETURN customer_id;
EXCEPTION
WHEN NO_DATA_FOUND THEN
meaningful_error_message := 'Customer named ' || customer_name || ' does not exist';
RAISE;
END;
Is there a way to associate meaningful_error_message with the NO_DATA_FOUND error?
Update: It has been suggested that I use RAISE_APPLICATION_ERROR to raise a custom error code when NO_DATA_FOUND is encountered. The purpose of this question was to determine if this technique could be avoided so that the calling code can catch NO_DATA_FOUND errors rather than a custom error code. Catching NO_DATA_FOUND seems more semantically correct, but I could be wrong.
Use RAISE_APPLICATION_ERROR (-20001, 'your message');
This will return an error number -20001, and your message instead of the NO_DATA_FOUND message. Oracle has reserved the error numbers between -20001 and -210000 for user use in their applications, so you won't be hiding another Oracle error by using these numbers.
EDIT: RAISE_APPLICATION_ERROR is specifically designed to allow you to create your own error messages. So Oracle does not have another method of allowing dynamic error messages. To further refine this you can define your own exception in the package where you define your procedure. Add the following:
CUSTOMER_NO_DATA_FOUND EXCEPTION;
EXCEPTION_INIT (CUSTOMER_NO_DATA_FOUND, -20001);
In your procedure code, you do the RAISE_APPLICATION_ERROR, and the client code can do a
WHEN CUSTOMER_NO_DATA_FOUND THEN which looks better, and they still have the error message captured in SQLERRM.
As suggested by Thomas you can use RAISE_APPLICATION_ERROR. If you also want to keep the NO_DATA_FOUND error on the error stack you can add TRUE as a third parameter to the function:
DECLARE
l NUMBER;
BEGIN
SELECT NULL INTO l FROM dual WHERE 1 = 2;
EXCEPTION
WHEN no_data_found THEN
raise_application_error(-20001, 'Meaningful Message', TRUE);
END;
ORA-20001: Meaningful Message
ORA-06512: at line 8
ORA-01403: no data found (*)
The line tagged (*) is the original error message.

Resources