PL/SQL check SQL excution if it went OK or not? - oracle

I have a few SQL (Select/Update/Insert) syntax that I will run inside PL/SQL one after another
is there any way to check if each syntax completed correctly and if there is some error it will not halt the whole PL/SQL, it will just return "OK" or "Not OK" to a variable so I can use it with IF?
UPDATE
I came up with this function, but it dose not seems to work, it returns 0 all time!
create or replace
FUNCTION EXECUTE_SQL(
V_SQL IN VARCHAR2 )
RETURN NUMBER
AS
V_RESULTS NUMBER := 1;
BEGIN
BEGIN
EXECUTE IMMEDIATE V_SQL;
EXCEPTION
WHEN OTHERS THEN
-- the following line is just for debugging!
dbms_output.put_line(SQLERRM);
V_RESULTS:= 0;
END;
RETURN V_RESULTS;
END EXECUTE_SQL;
what is wrong wit it (if any)!
cheers

if sql%rowcount > 0 then
-- insert or update statement affected sql%rowcount rows
end if;
As for the correct syntax: if the syntax is wrong, it won't even compile. If there's a data consistency error (such as divide by 0 error, or primary key violation) an exception will be thrown. Such exception can be caught in exception handlers
In the exception handler, you can then check sqlerrm for more details:
begin
update t set x = ...
exception when others then
dbms_output.put_line(SQLERRM);
end;
There are also a few predefined exceptions that you can check on:
begin
update t set x = ...
exception
when DUP_VAL_ON_INDEX
-- primary key or unique key violation
when OTHERS
-- other kind of exception
end;

If the syntax is not correct the entire block will be invalid, so you'll not be able to run it.
If you want to run all statements, despite that one can raise an exception, you can:
BEGIN
BEGIN
statement1;
EXCEPTION
when exception1 then
some commands or null;
when exception2 then
some commands or null;
END;
BEGIN
statement2;
EXCEPTION
when exception3 then
some commands or null;
when exception4 then
some commands or null;
END;
etc.
END;

Write show errors
begin
update t set x = ...
exception
when DUP_VAL_ON_INDEX
-- primary key or unique key violation
when OTHERS
-- other kind of exception
end;
/
show errors
It will show errors if any.

Related

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.

Similar to finally Block (JAVA) in Oracle PL/SQL Block

As in Java there is finally block which executed in all conditions.
Is there any similar function in Oracle PL/SQL which will be executed whenever procedure completes its execution even a return statement is used?
There is no equivalent of FINALLY but you can simulate it using nested PL/SQL blocks;
DECLARE
-- Your variables.
return_early BOOLEAN := FALSE;
BEGIN
-- Do something
DECLARE
-- Local variables in "try" block
BEGIN
-- Equivalent of "try" block
-- Do something that may raise an exception
IF some_condition THEN
return_early := TRUE;
-- you could also use "GOTO end_try;" rather than surrounding the
-- following statements in an "ELSE" statement
ELSE
-- Do something else that may raise an exception
END IF;
EXCEPTION
WHEN your_exception THEN
-- Equivalent of "catch" block
END;
<<end_try>>
-- Handle "finally" here, after end of nested block.
-- Note: you can only see variables declared in this outer block
-- not variables local to the nested PL/SQL block.
IF return_early THEN
RETURN;
END IF;
-- Continue and do more stuff.
END;
/
Another thread on Stackoverflow can help out here : Exception handling in pl/sql
Where Tony says :
"You can created nested blocks:"
create or replace procedure Trial
is
Begin
begin
---Block A--
EXCEPTION
when others then
insert into error_log values('error');
end;
begin
--Block B ----
end;
end;
The solutions provided in other answers does not implement exact try-catch-finally logic. E.g. the finally should be executed even if exception is reraised and even if in did not handled at all. In other words, the finally block must be called always.
The most similar behavior to Java's finally is the following. Unfortunately, here we have to wrap finally block into a procedure. We have no choice in PL/SQL.
declare
begin
declare
EXPECTED_EXCEPTION_RECOVERABLE exception;
EXPECTED_EXCEPTION_FATAL exception;
EXPECTED_EXCEPTION_UNHANDLED exception;
procedure finally is
begin
dbms_output.put_line('FINALLY section executed');
end;
begin
dbms_output.put_line('Trying dangerous section...');
/* uncomment to try different behavior */
--raise EXPECTED_EXCEPTION_RECOVERABLE;
--raise EXPECTED_EXCEPTION_FATAL;
--raise EXPECTED_EXCEPTION_UNHANDLED;
dbms_output.put_line('Dangerous section is executed successfully');
FINALLY();
exception
when EXPECTED_EXCEPTION_RECOVERABLE then
dbms_output.put_line('Recoverable exception handled.');
FINALLY();
when EXPECTED_EXCEPTION_FATAL then
dbms_output.put_line('Fatal exception handled and will be reraised');
FINALLY();
RAISE;
when OTHERS then
dbms_output.put_line('Unhandled exception is just reraised after finally section');
FINALLY();
RAISE;
end;
end;
/
It seems, this solution looks enough readable;
You can create a custom Exception and then Raise it at the end of your other exceptions, this custom exception must be on an outside block:
BEGIN
BEGIN
--DO SOME CODE HERE
EXCEPTION
WHEN NO_DATA_FOUND THEN
--HANDLE EXCEPTION
RAISE CUSTOM_EXCEPTION;
END;
EXCEPTION
WHEN CUSTOM_EXCEPTION THEN
--HANDLE THE FINALLY CODE HERE
END;

unable to call exception block in oracle

I have trying to execute below pl sql block in my oracle developer edition.I have made calls to functions and procedures and it works fines.But i am not able to call exception in case my query does not get executed.I have been trying to get a wrong query exectued by passing a string value instead of int value.So it throws error but also i need to get exception block to executed in case of such error.Block 2 should through exception as i am passing string value.But exception block does not get call,Any help?? Below is my block
DECLARE
DBCID INT := 102;
CNT INT;
BEGIN
SELECT DEVOPS_ISDBCEXECUTED(DBCID, 'DDL') INTO CNT FROM DUAL;
IF (CNT = 0) THEN
BEGIN
DEVOPS_DBCINSERT (DBCID,'DDL','hsolanki','Prj1','Item1','avarne');
BEGIN
DECLARE W_CNT int;
BEGIN
SELECT COUNT(*) INTO W_CNT FROM HS WHERE NAM = 'DK'; //block 1
IF (W_CNT = 0) THEN
INSERT INTO HS
(NAM, AGE)
VALUES ('Dk',8);
END IF;
END;
END;
BEGIN
DECLARE W_CNT int;
BEGIN
SELECT COUNT(*) INTO W_CNT FROM HS WHERE NAM = 'Ab';
IF (W_CNT = 0) THEN
INSERT INTO HS
(NAM, AGE) //block 2
VALUES ('Ab',s);
END IF;
END;
END;
DEVOPS_DBCUPDATE(DBCID, NULL,'SUCCESS');
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE ('ERROR OCCURED : ' || sqlerrm);
DEVOPS_DBCUPDATE (DBCID,sqlerrm,'Failed');
rollback;
END;
END IF;
END;
Your exception handling block is within the 'IF (CNT = 0) THEN .. END IF' block. If you pass a string value, most probably the exception was thrown at the first function call ( SELECT DEVOPS_ISDBCEXECUTED...), which is not 'protected' by an excpetion handler. You would need to move the exception handler to the outermost block, e.g.:
DECLARE
DBCID INT := 102;
CNT INT;
BEGIN
....
EXCEPTION
WHEN OTHERS THEN
...
END;
So we know ...
DEVOPS_DBCUPDATE is a procedure which updates a table
... and ...
IN exception i am calling DEVOPS_DBCUPDATE ... my table is not getting updated
... and ...
i dont know what is pragma autonomous_transaction
Putting these clues altogether we can see that the rollback in the EXCEPTION block will wipe out the change to the table executed by the preceding call to DEVOPS_DBCUPDATE(), so it only seems as though the EXCEPTION block is not being executed ( a check on whether the DBMS_OUTPUT message is displayed would confirm that it is being called).
Anyway the solution is to make DEVOPS_DBCUPDATE() run in a nested transaction, so the change is applied regardless of what happens in the wider transaction. We do this with the autonomous_transaction pragma.
Obviously I don't know the exact structure of your code, but it will look something like this:
create or replace procedure DEVOPS_DBCUPDATE( ... ) is
pragma autonomous_transaction;
begin
update your_table
set ....
commit;
end;
The COMMIT in the procedure will persist the change to your table but will not save anything in the outer transaction. So the rollback in the EXCEPTION block would still reverse the inserts into the HS table.
Autonomous transactions are very useful when employed properly, but it is easy to misuse them. This scenario - persistent logging in the event of exception or rollback - is their main use case. But generally, use with caution: it's easy to abuse autonomous transactions and end up with a corrupted database. There's more information in the Oracle documentation.
An error raise in the DECLARE section is not handled by that block's EXCEPTION section. For this reason it is often safer to initialise variables after the BEGIN i.e.
DECLARE
DBCID INT;
CNT INT;
BEGIN
DBCID := 'xxx';
...
EXCEPTION
WHEN OTHERS THEN
... -- The assignment will be caught here
END;

Not able to continue loop after exception in Oracle stored procedure

I am trying to return list of number from a stored procedure. My stored procedure gets stopped in for loop when exception occur. I have added exception clause and control is going inside the exception clause to continue the loop but still no luck.
How to continue the loop when exception occur? Thanks.
CREATE OR REPLACE PROCEDURE getNumber(l_list IN CUSTOMLIST, l_output OUT NUMLIST)
IS
n_num varchar2(5);
BEGIN
l_output := NUMLIST();
FOR i IN l_list.FIRST .. l_list.LAST LOOP
l_output.EXTEND(l_list.LAST);
BEGIN
SELECT NUM into n_num
FROM sometable WHERE some condition;
l_output(i) := n_num;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('TRUE');
CONTINUE;
END;
END LOOP;
END;
Maybe you should handle other exceptions too.
Try with adding
WHEN OTHERS THEN
[statements]
The code I posted in question is working when I restart the SQL developer. Something was wrong at SQL developer level when I copy paste the EXCEPTION clause. But when I type the EXCEPTION clause it's started showing be no_data_found option and that one I selected and it worked.

Saving a PL/SQL exception and raising it later?

I have a PL/SQL procedure (in an Oracle 12c database) that tries to insert some data. If that fails, it should check a table to see if it can find some info there to help solve the problem. If it finds the information everything is fine, if not it should reraise the error.
This is my code:
BEGIN
-- Try to insert some data.
INSERT INTO table VALUES x;
EXCEPTION
WHEN OTHERS THEN
BEGIN
-- Check a table to fins some info to help solve the problem.
-- If we find a row here, we can fix it.
-- If not, we should reraise the error.
SELECT * INTO y FROM table WHERE a = b;
-- Do some more stuff here to fix the problem.
EXCEPTION
WHEN NO_DATA_FOUND THEN
-- We could not find anything in the table,
-- so we could not handle the situation.
-- Reraise the error.
RAISE;
END;
END;
The problem here is that the RAISE; statement raises the latest exception, which is the NO_DATA_FOUND that the SELECT statement threw. The original exception from the INSERT is further down in the stack, but not at the top.
Can I somehow "save" the error from the INSERT and reraise it? Or can I run a SELECT INTO that does not throw an error if it finds nothing? My goal here is to reraise the original INSERT exception without having any traces of the NO_DATA_FOUND exception on it.
EDIT: See comments as to why this is not a duplicate.
Pull the raise statement out of the block trying to fix the problem. In case you need to reraise contingent to failure of the fixing, set a flag in the inner exception handler and only execute raise when that flag is true.
As code:
DECLARE
b_reraise BOOLEAN := FALSE;
BEGIN
-- Try to insert some data.
INSERT INTO table VALUES x;
EXCEPTION
WHEN OTHERS THEN
BEGIN
-- Check a table to fins some info to help solve the problem.
-- If we find a row here, we can fix it.
-- If not, we should reraise the error.
SELECT * INTO y FROM table WHERE a = b;
-- Do some more stuff here to fix the problem.
EXCEPTION
WHEN NO_DATA_FOUND THEN
-- We could not find anything in the table,
-- so we could not handle the situation.
-- Reraise the error.
b_reraise := TRUE;
END;
IF b_reraise THEN
RAISE;
END IF;
END;
May be something like this:
DECLARE
l_sqlerrm VARCHAR2(4000);
l_sqlerrc NUMBER;
l_exc EXCEPTION;
BEGIN
-- some code with errors
EXCEPTION
WHEN OTHERS THEN
l_sqlerrm := SQLERRM;
l_sqlerrc := SQLCODE;
-- loggin
INSERT INTO my_log (code, text) VALUES (l_sqlerrc, l_sqlerrm);
COMMIT;
-- some your code "Check a table to fins some info to help solve the problem"
-- some code to SELECT code INTO l_sqlerrc FROM my_log
PRAGMA exception_init(l_exc, l_sqlerrc);
RAISE l_exc;
END;

Resources