Case wise Exception Handling in Oracle - oracle

How can I achieve below scenario using Oracle PL/SQL?
MY_STORED_PROC
BEGIN
UPDATE1;
IF UPDATE1 fails RAISE EXCP1
ELSE COMMIT;
UPDATE2;
IF UPDATE2 fails RAISE EXCP2
ELSE COMMIT;
EXCEPTION
WHEN EXCP1
INSERT LOG ('UPDATE1 Failed')
WHEN EXCP2
INSERT LOG ('UPDATE2 Failed')
END;

That is quite simple:
BEGIN
BEGIN
UPDATE1;
COMMIT;
EXCEPTION
WHEN OTHERS THEN
RAISE EXCP1;
END;
BEGIN
UPDATE2;
COMMIT;
EXCEPTION
WHEN OTHERS THEN
RAISE EXCP2;
END;
EXCEPTION
WHEN EXCP1 THEN
INSERT LOG ('UPDATE1 Failed');
WHEN EXCP2 THEN
INSERT LOG ('UPDATE2 Failed');
END;
You can use PRAGMA EXCEPTION_INIT to define your own exceptions to be raised.

Related

Oracle Pl/SQL Exception Flow

Given a simple SP like;
CREATE OR REPLACE PROCEDURE TEST1
AS
BEGIN
EXECUTE IMMEDIATE 'truncate table missingtable';
dbms_output.put_line('here');
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -942 THEN
RAISE;
END IF;
END;
I never get to the output statement, I thought control returned to the same block, which is the only block.. and yes, missingtable reports a -942 if I try to truncate it.
it's a logic problem, in fact the exception happens but you coded to raise an exception only if the return code is different of 942 which is the the error happening.
if you want to continue to the dbms_output in you first block you need an inner exception
CREATE OR REPLACE PROCEDURE TEST1
AS
BEGIN
BEGIN
EXECUTE IMMEDIATE 'truncate table missingtable';
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -942 THEN
RAISE;
ELSE
NULL;
END IF;
END;
dbms_output.put_line('here');
END;
/

Oracle: Savepoints within execute immediate and ORA-01086

Does anybody know, why i get an ORA-01086: savepoint 'SPX' never established in this session or is invalid in the following Code?
begin
rollback; --clear all Transactions
execute immediate 'begin
savepoint SPX;
raise no_data_found;
end;';
exception when no_data_found then
rollback to savepoint SPX;
end;
It is working if i don't use execute immediate:
begin
rollback; --clear all Transactions
begin
savepoint SPX;
raise no_data_found;
end;
exception when no_data_found then
rollback to savepoint SPX;
end;
So is this an expected behaviour or is this something like a bug?
I'm using Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production
Update:
the following Example is also working, it uses dynamic SQL combined with Savepoints:
begin
rollback; --clear all Transactions
execute immediate 'begin
savepoint SPX;
end;';
rollback to savepoint SPX;
end;
The problem is that before the exception propagates to the outer block, an implicit ROLLBACK occurs, which removes the savepoint that the block had created.
Here is an example that reproduces the problem but using static SQL:
begin
rollback; --clear all Transactions
savepoint SPX;
rollback;
raise no_data_found;
exception when no_data_found then
rollback to savepoint SPX;
end;
ORA-01086: savepoint 'SPX' never established in this session or is invalid ORA-06512: at line 7
Unfortunately you're stuck with this behaviour; dynamic SQL must be run within its own context, and if it raises an exception (and doesn't handle it), a ROLLBACK will be issued.
You will need to create the savepoint before running the dynamic SQL; alternatively, you can suppress the ORA-01086 error:
begin
rollback; --clear all Transactions
execute immediate 'begin
savepoint SPX;
raise no_data_found;
end;';
exception when no_data_found then
begin
rollback to savepoint SPX;
exception when others then
if sqlcode != -1086 /*savepoint never established*/ then
raise;
end if;
end;
end;
In the Oracle Doc on the SAVEPOINT statement ist says:
The SQL SAVEPOINT statement can be embedded as static SQL in PL/SQL.
So I think this can be considered as an expected behaviour.

Oracle uses exception handler from first block in later blocks

I'm running into a behavior where I'm trying to use case-specific exception handlers for several Oracle PL/SQL blocks in a Flyway script and Oracle, apparently contradicting its documented scoping for exception handlers, sends all exceptions to the exception handler for the first block. For example, in this code:
begin
begin
execute immediate '
create table "test" (
"id" number not null,
"name" varchar2(100) not null,
constraint "test_pk" primary key ("id")
)
';
exception
when others then
if sqlcode != -955 then raise; end if;
end;
begin
execute immediate 'fail to create index "test_name_idx" on "test" ("name")';
exception
when others then
if sqlcode != -6512 then raise; end if;
end;
end;
the ORA-06512 exception is not caught, and the exception raised is tagged as from line 13.
Wrapping the blocks in more blocks doesn't help.
What is going on here? How do I stop this from happening?
This seems to be a bug, which has (so far) been reproduced in 11.2.0.4, 12.1.0.2 and 12.2.0.1. It doesn't seem to require DDL, or any real action in the first sub-block (though just doing null; as a placeholder doesn't trigger it, possibly because the compiler removes it), but it does seem to need the if inside both exception handlers:
begin
begin
dbms_output.put_line('Dummy message');
exception
when others then
dbms_output.put_line('In first exception handler');
if 1=1 then
raise;
end if;
end;
begin
execute immediate 'invalid';
exception
when others then
dbms_output.put_line('In second exception handler');
if 1=1 then
raise;
end if;
end;
end;
/
Dummy message
In second exception handler
ORA-00900: invalid SQL statement
ORA-06512: at line 8
ORA-06512: at line 13
As with your example the exception is thrown by line 13 so should be reported as (re-)raised at line 18; but it's instead it's reported as raised from line 8, which doesn't make sense. (The at line 13 message is only shown in 12.2; in 11.2 and 12.1 it only reports the first ORA-06512, which is rather more confusing. At least in 12 2 you have some clue where the problem really is.)
From the debugs you can see it doesn't actually use the first exception handler, and it does go into the second one. It 'only' seems to be reporting against the wrong line number, rather than executing the wrong code.
It appears that doing real work inside the if, immediately before the raise somehow fixes things - in either exception handling section; this adds a message in the first, which can't be reached:
begin
begin
dbms_output.put_line('Dummy message');
exception
when others then
dbms_output.put_line('In first exception handler');
if 1=1 then
dbms_output.put_line('This avoids the bug somehow');
raise;
end if;
end;
begin
execute immediate 'invalid';
exception
when others then
dbms_output.put_line('In second exception handler');
if 1=1 then
raise;
end if;
end;
end;
/
Dummy message
In second exception handler
ORA-00900: invalid SQL statement
ORA-06512: at line 19
ORA-06512: at line 14
and this in the second:
begin
begin
dbms_output.put_line('Dummy message');
exception
when others then
dbms_output.put_line('In first exception handler');
if 1=1 then
raise;
end if;
end;
begin
execute immediate 'invalid';
exception
when others then
dbms_output.put_line('In second exception handler');
if 1=1 then
dbms_output.put_line('This avoids the bug somehow');
raise;
end if;
end;
end;
/
Dummy message
In second exception handler
ORA-00900: invalid SQL statement
ORA-06512: at line 19
ORA-06512: at line 13
In both cases the reported line number is now correct. Somehow.
It doesn't have to be a dbms_output call, anything seems to work, such as a dummy procedure call or query, even an extra sub-block (e.g. begin execute immediate 'select * from dual'; end;, even though the query isn't executed because there's no into...). Again just using null; doesn't work though.
This is a bit ugly but gives you a way to stop it from happening at least, sort of.
It's clearly weird and unexpected and inconsistent behaviour, and has been around for a while, so it should probably be raised as a service request through My Oracle Support. I can't see any existing reports but I didn't look very hard so there might be one lurking somewhere.

Catch ORA exceptions in PL/SQL

I wrote a package to add records in a country table that has a reference key pointing to a "regions" table using region_id.So,if I try to add a "region_id" in my countries table and if that value does not exist in my regions table,I should throw the exception and catch.
My package code is:
CREATE PACKAGE BODY cus7 AS
v_error_code NUMBER;
region_exists pls_integer;
procedure addi6 (c_cntry_id in out countries.country_id%type,
c_cntr_name in countries.country_name%type,
c_rgn_id in countries.region_id%type)
is
begin
begin
select 1 into region_exists
from regions r
where r.region_id = c_rgn_id;
exception
when no_data_found then
region_exists := 0;
DBMS_OUTPUT.PUT_LINE('Region not present');
end;
if region_exists = 1 then
insert into countries(country_id, country_name,region_id)
values (c_cntry_id, c_cntr_name, c_rgn_id);
DBMS_OUTPUT.PUT_LINE('Inserted');
END IF;
EXCEPTION
WHEN dup_val_on_index
THEN
c_cntry_id := null;
DBMS_OUTPUT.PUT_LINE('Already present');
end addi6;
END cus7;
/
Now,in my procedure,everything is working fine,except when I do an add like this:
DECLARE
outputValue CHAR(2) := 'KO';
begin
cus7.addi6(outputValue,'KOREA',5);
end;
/
apart from getting my own message which is "Region not found",I am also getting ORA-01403-No data found.
My question is if there is a way to catch this ORA exception or avoid display?
Tx in advance
Yes, all you have to do is add WHEN OTHERS to your exception block in order to catch all of the other possible ORA exceptions.
EXCEPTION
WHEN dup_val_on_index
THEN
c_cntry_id := null;
DBMS_OUTPUT.PUT_LINE('Already present')
WHEN OTHERS
THEN
DBMS_OUTPUT.PUT_LINE('Another Error');
-- other logic here
Here is an extensive documentation regarding error handling in PL/SQL.

ignore a user defined exception in oracle

In pl/sql I have some inner begin, end blocks and "Exception Others" blocks.
When I throw my user defined exception from an inner block I just want to catch this exception in the last "Exception userdef" block, not in the inner "Exception Others" blocks.
Any idea?
It sounds like you have something like this:
BEGIN
BEGIN
BEGIN
DO_SOMETHING; -- raises USERDEF_EXCEPTION
EXCEPTION
WHEN OTHERS THEN
DIE_HORRIBLY;
END;
EXCEPTION
WHEN OTHERS THEN
DIE_EVEN_MORE_HORRIBLY;
END;
EXCEPTION
WHEN USERDEF_EXCEPTION THEN
DO_SOMETHING_REASONABLE;
WHEN OTHERS THEN
DIE_INCREDIBLY_HORRIBLY;
END;
and you want to DO_SOMETHING_REASONABLE rather than DIE_HORRIBLY or DIE_EVEN_MORE_HORRIBLY. Sorry - you can't do that without providing a handler in the inner blocks for your exception. You'll have to do something like:
BEGIN
BEGIN
BEGIN
DO_SOMETHING; -- raises USERDEF_EXCEPTION
EXCEPTION
WHEN USERDEF_EXCEPTION THEN
RAISE;
WHEN OTHERS THEN
DIE_HORRIBLY;
END;
EXCEPTION
WHEN USERDEF_EXCEPTION THEN
RAISE;
WHEN OTHERS THEN
DIE_EVEN_MORE_HORRIBLY;
END;
EXCEPTION
WHEN USERDEF_EXCEPTION THEN
DO_SOMETHING_REASONABLE;
WHEN OTHERS THEN
DIE_INCREDIBLY_HORRIBLY;
END;
Share and enjoy.
/* package */
CREATE OR REPLACE PACKAGE exceptions_pkg AS
user_defined_exception EXCEPTION;
END exceptions_pkg;
/* block */
DECLARE
l_var1 NUMBER;
BEGIN
DBMS_OUTPUT.PUT_LINE('one');
DECLARE
l_var2 NUMBER;
BEGIN
DBMS_OUTPUT.PUT_LINE('two');
IF 1 < 2 THEN
RAISE exceptions_pkg.user_defined_exception;
END IF;
DBMS_OUTPUT.PUT_LINE('three');
END;
DBMS_OUTPUT.PUT_LINE('four');
EXCEPTION
WHEN exceptions_pkg.user_defined_exception THEN
DBMS_OUTPUT.PUT_LINE('five');
END;
-- anonymous block completed
/*
one
two
five
*/

Resources