Oracle: Savepoints within execute immediate and ORA-01086 - oracle

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.

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;
/

Case wise Exception Handling in 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.

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.

declare function and return varchar oracle

I am trying to declare a function in pl/sql and return a variable content from it.
CREATE OR REPLACE FUNCTION executeTransaction
RETURN VARCHAR2 as
pragma autonomous_transaction;
myLog VARCHAR(1200) :='myLog';
BEGIN
savepoint my_savepoint;
begin
insert into datalogger(MPRN_FK,K0213,K0214) values(465,'2142342','423423');
myLog := 'transaction completed with rows insert ' || SQL%ROWCOUNT;
rollback to my_savepoint;
end;
insert into myTbl(col) values(myLog);
RETURN myLog;
END executeTransaction;
The function compiles but when I execute it like
select executeTransaction from dual;
I am getting
ORA-06519: active autonomous transaction detected and rolled back
ORA-06512: at "ECO_AFMS_GAS_CUST.GET_ALLITEMS", line 14
06519. 00000 - "active autonomous transaction detected and rolled back"
*Cause: Before returning from an autonomous PL/SQL block, all autonomous
transactions started within the block must be completed (either
committed or rolled back). If not, the active autonomous
transaction is implicitly rolled back and this error is raised.
*Action: Ensure that before returning from an autonomous PL/SQL block,
any active autonomous transactions are explicitly committed
or rolled back.
and the variable content is not returned? I know that I must either commit or rollback but in this case I rollback so it should work?
Since you are using pragma autonomous_transaction, you need to add COMMIT at the end of your function. As the error states
If not, the active autonomous transaction is implicitly rolled back and this error is raised.
CREATE OR REPLACE FUNCTION executeTransaction
RETURN VARCHAR2 as
pragma autonomous_transaction;
myLog VARCHAR(1200) :='myLog';
BEGIN
savepoint my_savepoint;
begin
insert into datalogger(MPRN_FK,K0213,K0214) values(465,'2142342','423423');
myLog := 'transaction completed with rows insert ' || SQL%ROWCOUNT;
rollback to my_savepoint;
end;
insert into myTbl(col) values(myLog);
commit; --add commit
RETURN myLog;
END executeTransaction;

Exit execution when error occurs PL/SQL

I would like to know, how can I exit the execution when an error occurs. In Microsoft SQL Server there is a RETURN clause, which does the work. But I would like to know similar functionality in Oracle. I am using Oracle Sql Developer. Here is the script I am using:
First block throws error due to Unique Key Violation, even though it throws error the execution goes to next block and executes the insert statement. I want to end the execution or exit at first block of code itself.
Please help me to write the code.
First anonymous PL/SQL block:
set serveroutput on;
BEGIN
insert into test values(1);
insert into test values(1);
COMMIT;
dbms_output.put_line('PRINT SOMETHING 1');
EXCEPTION
WHEN OTHERS THEN
if sqlcode <> 0
then
dbms_output.put_line(SQLCODE || ' ' || SQLERRM);
RAISE;
end if;
return;
END;
/
Second anonymous PL/SQL block:
set serveroutput on;
BEGIN
insert into test values(6);
COMMIT;
dbms_output.put_line('PRINT SOMETHING');
EXCEPTION
WHEN OTHERS THEN
if sqlcode <> 0
then
dbms_output.put_line(SQLCODE || ' ' || SQLERRM);
RAISE;
end if;
return;
END;
/
If you create a stored procedure, you have more control and can exit whenever you like with a return statement.
So create a stored proc:
create or replace procedure myProc as
begin
dbms_ouput.put_line('i am here');
return;
dbms_ouput.put_line('and not here');
end;
Then in sqlplus or developer:
exec myProc();
You can nest the blocks into a single 'program unit'.
In this way an exception in the first block will stop the whole program unit from executing, rather than just being limited in scope to the first block.
set serveroutput on;
BEGIN
BEGIN
insert into test values(1);
insert into test values(1);
COMMIT;
dbms_output.put_line('PRINT SOMETHING 1');
EXCEPTION
WHEN OTHERS THEN
if sqlcode <> 0
then
dbms_output.put_line(SQLCODE || ' ' || SQLERRM);
RAISE;
end if;
return;
END;
BEGIN
insert into test values(6);
COMMIT;
dbms_output.put_line('PRINT SOMETHING');
EXCEPTION
WHEN OTHERS THEN
if sqlcode <> 0
then
dbms_output.put_line(SQLCODE || ' ' || SQLERRM);
RAISE;
end if;
return;
END;
END;
/
You should be able to use "exit" - see the Oracle documentation here: http://docs.oracle.com/cd/B19306_01/server.102/b14357/ch12023.htm
Note that this will end your SqlPlus session, but I don't know of another way of doing it aside from using a single block or stored procedure.
Another useful statement is:
WHENEVER SQLERROR EXIT SQL.SQLCODE
Oracle documentation: http://docs.oracle.com/cd/B19306_01/server.102/b14357/ch12052.htm
Thanks for your valuable comments.
JoshL, i tried using EXIT but i am ending up with error. Please correct my code( I am new to PL/SQL). "WHENEVER SQLERROR EXIT" is good to use but my issue is that I use these sql scriptsd in InstallShield, so InstallShield installers does not recognize these statements and throws error.
set serveroutput on;
BEGIN
insert into test values(1);
insert into test values(1);
COMMIT;
dbms_output.put_line('PRINT SOMETHING 1');
EXCEPTION
WHEN OTHERS THEN
if sqlcode <> 0
then
dbms_output.put_line(SQLCODE || ' ' || SQLERRM);
RAISE;
exit;
end if;
END;
/
The EXIT command is only for use within a loop in PL/SQL. The EXIT command leaves the loop at that point. If you use the EXIT command outside a loop in PL/SQL the compiler throws an error.
The EXIT command in SQLPlus exits the SQLPlus session.
This is confusing, because they are two different Oracle products. SQL*Plus can run PL/SQL and the EXIT statement is a valid statement in both products, but with different contexts.

Resources