If i have an oracle pl/sql procedure, I define an exception block like this:
exception
WHEN OTHERS THEN
errMsg := substr(SQLERRM, 1, 100);
dbms_output.put_line ('--> Error ALIAS1: ' || errMsg);
end;
Will this be executed no matter what kind of exception is raised?
WHEN OTHERS is a catch-all for any PL/SQL exception that isn't handled explicitly by its own WHEN clause.
It isn't normally a good idea to use it, for that reason; it's easy to accidentally trap and lose an important message about something bad happening. You should catch and handle the specific exceptions you expect to encounter. If you do want to record or log any other exceptions you should include a RAISE in the exception handler so everything else gets propagated normally.
...
exception
when others then
errMsg := substr(SQLERRM, 1, 100);
dbms_output.put_line ('--> Error ALIAS1: ' || errMsg);
raise;
end;
Read more about exceptions generally here, and about OTHERS here.
Related
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.
How do you handle PL/SQL errors that are not predefined?
Predefined exceptions are the following:
ACCESS_INTO_NULL
CASE_NOT_FOUND
COLLECTION_IS_NULL
CURSOR_ALREADY_OPENED
DUP_VAL_ON_INDEX
INVALID_CURSOR
INVALID_NUMBER
NO_DATA_FOUND
PROGRAM_ERROR
ROWTYPE_MISMATCH
STORAGE_ERROR
SUBSCRIPT_BEYOND_COUNT
SUBSCRIPT_OUTSIDE_LIMIT
SYS_INVALID_ROWID
TOO_MANY_ROWS
VALUE_ERROR
ZERO_DIVIDE
You could use a WHEN OTHERS exception handler that looks at the SQLCODE. But you're generally better off defining an exception that you can handle. That is going to lead to cleaner code, it lets you map your exception name to a number just once, and makes user-defined exceptions look just like predefined exceptions.
declare
column_already_indexed exception;
pragma exception_init( column_already_indexed, -1408 );
begin
call_procedure;
exception
when column_already_indexed
then
null;
-- Do something with the exception
end;
You can handle errors outside of the predefined with the following:
BEGIN
buggyprocedure; --Call to a procedure that can throw exceptions
EXCEPTION
WHEN NO_DATA_FOUND THEN --Catch predefined exception
NULL; --Ignore no_data_found
WHEN OTHERS THEN --Catch all other exceptions
IF SQLCODE = -1408 THEN --Catch error -1408
NULL; --Ignore this exception
ELSEIF SQLCODE = -955 THEN --Catch error -955
anotherprocedure; --Call a different procedure
ELSE
RAISE; --Re-raise exception that we are not handling
END IF;
END;
You basically catch all other exceptions, then use if and else statements to check if the SQLCODE returned is one you are expecting and want to handle.
In my application oracle procedure is currently called through unix script, when there is error in exception block we are handling something like this :-
PROC_LOGS('<PROC_NAME>', <TABLE_NAME>, 'Exception','Exception occured - '||SQLERRM);
PROC_LOG does nothing but inserts entry into log table but we need now to catch that error in unix also (return some value except 0) from where is it called so remaining process can be terminated best on that, what is the best way to do this?
CREATE OR REPLACE....
...
DECLARE
....
BEGIN
..
...
EXCEPTION
WHEN OTHERS THEN
PROC_LOGS('<PROC_NAME>', <TABLE_NAME>, 'EXCEPTION','EXCEPTION OCCURED - '||SQLERRM);
END;
Unix script part from where I m calling procedure
sqlplus <<-!
$US/$P#$I
set serveroutput on
#$SQL/execute_proc.sql $1 $2
execue_proc contains something like this :-
define IN_1 = '&1';
define IN_2 = '&2';
spool $SQL/test_&&IN
declare
P_IN_TABLE_NAME varchar2(250) := '&&IN_TABLE_NAME';
P_IN_REGION varchar2(250) := '&&IN_REGION';
begin
PROC_UPDATE_CHARGE_FACT(P_IN_TABLE_NAME,P_IN_REGION);
end;
/
spool off
You need to raise the exception, like this:
EXCEPTION
WHEN OTHERS THEN
PROC_LOGS('<PROC_NAME>', <TABLE_NAME>, 'EXCEPTION','EXCEPTION OCCURED - '||SQLERRM);
RAISE;
END;
or use RAISE_APPLICATION_ERROR:
EXCEPTION
WHEN OTHERS THEN
PROC_LOGS('<PROC_NAME>', <TABLE_NAME>, 'EXCEPTION','EXCEPTION OCCURED - '||SQLERRM);
RAISE_APPLICATION_ERROR (-20002, 'An unexpected exception occurred.');
END;
You are presumably calling SQL*Plus, and SQL*Plus has a configuration item that should help.
http://docs.oracle.com/cd/E11882_01/server.112/e16604/ch_twelve052.htm#BACHCFEF
Since you probably execute the procedure from an anonymous block, the error should propagate to SQL*Plus and "WHENEVER SQLERROR EXIT SQL.SQLCODE" will return the relevant error code to the unix environment.
One way could be to have proc_log() to also write to a log file and then have the unix script to check the log file.
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.
In our Stored procedures we have the following code towards the very end.
<<SQL_ERROR>>
V_SYS_ERROR_MSG := SUBSTR(SQLERRM, 1, 252);
DBMS_OUTPUT.PUT_LINE('ERROR IN EXECUTION IN PROCEDURE');
DBMS_OUTPUT.PUT_LINE('THE ERROR CODE IS ' || V_SYS_ERROR || '- ' ||
V_SYS_ERROR_MSG);
we have statements like following which call the error block.
IF V_SYS_ERROR <> 0 THEN
GOTO SQL_ERROR;
the DBMS output statements come even when there is no error. How can we avoid this?
I am not recommending this GOTO approach: as others have already said, exceptions are the correct way to handle errors in PL/SQL. But to address your specific question, you could do this:
BEGIN
IF V_SYS_ERROR <> 0 THEN
GOTO SQL_ERROR;
END IF;
GOTO PROC_END;
<<SQL_ERROR>>
V_SYS_ERROR_MSG := SUBSTR(SQLERRM, 1, 252);
DBMS_OUTPUT.PUT_LINE('ERROR IN EXECUTION IN PROCEDURE');
DBMS_OUTPUT.PUT_LINE('THE ERROR CODE IS ' || V_SYS_ERROR || '- ' ||
V_SYS_ERROR_MSG);
<<PROC_END>>
NULL;
END;
Of course, this still involves changing the code, so if you are doing that why not do it properly anyway? i.e.
DECLARE
SQL_ERROR EXCEPTION;
BEGIN
IF V_SYS_ERROR <> 0 THEN
RAISE SQL_ERROR;
END IF;
EXCEPTION
WHEN SQL_ERROR THEN
V_SYS_ERROR_MSG := SUBSTR(SQLERRM, 1, 252);
DBMS_OUTPUT.PUT_LINE('ERROR IN EXECUTION IN PROCEDURE');
DBMS_OUTPUT.PUT_LINE('THE ERROR CODE IS ' || V_SYS_ERROR || '- ' ||
V_SYS_ERROR_MSG);
RAISE;
END;
By the way, DBMS_OUTPUT.PUT_LINE is not suitable for output of error messages in a production application system. Only use it for debugging during development.
you should avoid GOTO statements, they are messy as you have noticed. PL/SQL comes with error handling, you should use the EXCEPTION synthax to deal with errors:
BEGIN
<code goes here>
EXCEPTION
WHEN <exception> THEN
<deal_with_it>
WHEN OTHERS THEN
<log_error>
RAISE;
END;
I don't think you'll get the results you want without using an exception handler. From the PL/SQL User's Guide:
SQLERRM with no argument is useful
only in an exception handler. Outside
a handler, SQLERRM with no argument
always returns the normal, successful
completion message.
So first of all, it is possible that an error is happening but you are seeing a message saying "normal, successful completion" because that is what SQLERRM will always return in this context.
Assuming that's not the case, it sounds like you are simply allowing control to flow from the "normal" code into the "error handler". If the error handler is at the very end of the procedure, then a simple fix would be to add a RETURN statement just before the <> label.
A better fix would be to move the error-handler code to a separate procedure and call that procedure instead of using a GOTO.