Pragma Exception init with constant - oracle

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

Related

Having some confusion in exception types

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.

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;

What PRAGMA statements would be used inside an EXCEPTION block?

I was playing around with Oracle exceptions and tried to do some preprocessing between EXCEPTION and the WHERE statements. That is,
EXCEPTION
some_operation_here();
WHEN yaddayadda THEN
...
PL/SQL Developer said that that wasn't kosher – oh well – but its error message intrigued me: it was expecting either WHEN or PRAGMA. I am not thoroughly familiar with all the PRAGMA directives, but it doesn't seem like any of them are applicable in an exception block unless for some reason you waited till this point to bind an error code to an exception you had declared earlier. Why would you ever need to use a PRAGMA directive here?
A little experimentation tells me that you can, in fact put a PRAGMA in the exception block, but I can't see much use to it. The following executes successfully, but the raised error triggers the OTHER section, not the section for the newly defined exception (e.g. it returns "Old exception"). It would appear that this is an undocumented feature.
DECLARE
v NUMBER;
new_divide_zero EXCEPTION;
BEGIN
v := 1 / 0;
EXCEPTION
PRAGMA EXCEPTION_INIT (new_divide_zero, -1476);
WHEN new_divide_zero THEN
DBMS_OUTPUT.put_line ('New exception');
WHEN OTHERS THEN
DBMS_OUTPUT.put_line ('Old exception');
END;
Similarly, I tried putting a AUTONOMOUS_TRANSACTION in the exception block, but it too appears to have no effect (in this case, both rows are inserted).
CREATE TABLE test_results (result VARCHAR2 (2000));
BEGIN
DECLARE
v NUMBER;
new_divide_zero EXCEPTION;
BEGIN
insert into test_results values ('Test Value');
v := 1 / 0;
EXCEPTION
PRAGMA AUTONOMOUS_TRANSACTION;
WHEN OTHERS THEN
INSERT INTO test_results
VALUES ('Old exception');
Commit;
END;
ROLLBACK;
END;
The Oracle documentation (12g, which is the version i tested this on) does not mention using PRAGMA in the exception block, so it's definitely undocumented. On the other hand, it doesn't seem to be much of a feature, as it doesn't appear to actually do anything...
WHEN { exception_name [ OR exception_name ]... | OTHERS } THEN
statement [ statement ]...
It is syntax error. To catch exception you must use following syntax:
declare
yaddayadda exception;
begin
...
exception
WHEN yaddayadda THEN
some_operation_here();
end;
You need to use pragma exception_Init to assign you custom exception with sql error code.
For example:
declre
l_id number;
e_resource_busy EXCEPTION;
PRAGMA EXCEPTION_INIT(e_resource_busy, -54);
begin
select id
into l_id
from job_table
where status = 'RUNNING'
for update nowait;
exception
WHEN e_resource_busy THEN
-- row is locked
some_operation_here();
end;

In PL/SQL (Oracle), can exceptions propagate across function/procedure calls?

as it happens in enclosed blocks
If an anonymous block calls a function that raises exceptions, those are neither shown on console nor trapped in enclosing blocks...
What's more, after being caught by handler in the function, lines after function call in the anonymous block are executed normally!
The called procedure is:
CREATE OR REPLACE PROCEDURE qt(pno number, qty OUT number)
IS
BEGIN
select sum(qty_on_hand) into qty from products where productno=pno;
END;
The calling block is:
DECLARE
qty number;
BEGIN
qt(&pno, qty);
dbms_output.put_line('qty is: '||qty);
END;
In case of invalid product number, no error is shown; why?
If the exception is caught by the handler in the function and not re-raised then no exception will be triggered in the caller. This is correct behaviour. If you want the exception to be visible to the caller you must re-raise it in the function using the RAISE command:
FUNCTION fun ...
...
EXCEPTION
WHEN some_exception THEN
...
RAISE;
END;
Alternatively you can raise a different exception e.g.
EXCEPTION
WHEN some_exception THEN
...
RAISE_APPLICATION_ERROR(-20001,'My error message');
END;
In your specific example, I think that no exception is being raised at all. You say "in the case of an invalid product number", by which I assume you mean a product number that does not exist. That sounds like you expect your query to throw NO_DATA_FOUND, but since it is using an aggregate function without a GROUP BY, it will actually return a single row containing NULL if there are no matching rows.
Just to add to Tony's answer.
You may not know what type of exception a calling function may throw. In this case you can do:
EXCEPTION
WHEN exception_that_may_occur_in_my_function THEN
...
RAISE_APPLICATION_ERROR(-20001,'My error message');
WHEN others THEN -- Any exception that can come from a function I'm calling
RAISE;
END;

Is it a bad practice to use global exceptions in PL/SQL?

Is it a bad practice to do what the code below does? Will bad things happen to me for writing it?
Edit: this is just an example. I would not use dbms_output for any real error reporting.
CREATE OR REPLACE PACKAGE my_package
AS
PROCEDURE master;
END;
/
CREATE OR REPLACE PACKAGE BODY my_package
AS
my_global_interrupt EXCEPTION;
PROCEDURE my_private_procedure
IS
BEGIN
-- in case some flag is raised, raise exception to stop process and prepare for resume
RAISE my_global_interrupt;
END;
PROCEDURE master
IS
BEGIN
my_private_procedure;
EXCEPTION
WHEN my_global_interrupt THEN
dbms_output.put_line('global interrupt, ');
-- prepare to resume
END;
END;
/
On the contrary globally defined user exceptions is good practice. Consider the following skeleton of a package body.
create or replace package body my_pkg
as
my_x1 exception;
my_x2 exception;
my_x3 exception;
PROCEDURE p1 is
begin
...
exception
when no_data_found then raise my_x1;
end p1;
PROCEDURE p2 is
begin
...
exception
when no_data_found then raise my_x2;
end p2;
PROCEDURE p3 is
begin
...
exception
when no_data_found then raise my_x3;
end p3;
PROCEDURE master is
begin
p1;
p2;
p3;
exception
when my_x1 then do_this;
when my_x2 then do_that;
when my_x3 then do_the_other;
end master;
end my_pkg;
/
The use of globally declared exceptions makes exception handling in the master procedure easier.
Also, bear in mind that sometimes we want to propagate the exception beyond the package, to say a program which calls our publicly declared procedure. We can do that by defining our exceptions in the package spec. This means other proecdures can reference them...
SQL> begin
2 my_pkg.master;
3 exception
4 when my_pkg.my_public_x1
5 then dbms_output.put_line('oh no!');
6 end;
7 /
oh no!
PL/SQL procedure successfully completed.
SQL>
We can also associate such exceptions with specific error numbers, so that they are recognisable even if the calling procedure doesn't explicitly handled them.
SQL> exec my_pkg.master
BEGIN my_pkg.master; END;
*
ERROR at line 1:
ORA-20999:
ORA-06512: at "APC.MY_PKG", line 32
ORA-06512: at line 1
SQL>
That's (slightly) more helpful than the generic ORA-06510 error.
Looks reasonable enough to me, provided you're happy that after the interrupt condition it's OK to resume processing. If you are going to log the interrupt in some way, it's probably better to insert a row into a log table using an autonomous transaction. You won't see anything from DBMS_OUTPUT until the whole procedure finishes. Then you'll see all the DBMS_OUTPUT at once.

Resources