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;
Related
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;
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;
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
I need to handle the ORA-01400 error (cannot insert NULL into ("SCHEMA"."TABLE_NAME"."COLUMN_NAME") ) using a exception handle.
ORACLE Predefine a few Exceptions like (ACCESS_INTO_NULL, ZERO_DIVIDE and so on), but apparently does not define an Exception for the ORA-01400 error, how do I handle this particular error code?
I need something like this (other suggestions are accepted).
....
...
INSERT INTO MY_TABLE (CODE, NAME) VALUES (aCode,aName);
COMMIT;
EXCEPTION
WHEN NULL_VALUES THEN /* i don't know this value , exist?*/
Do_MyStuff();
WHEN OTHERS THEN
raise_application_error(SQLCODE,MY_OWN_FORMAT_EXCEPTION(SQLCODE,SQLERRM),TRUE);
END;
The pre-defined PL/SQL exceptions are special to Oracle. You really can't mess with those. When you want to have a set of predefined exceptions of your own you can't declare them "globally" like the standard ones. Instead, create an exceptions package which has all of the exception declarations and use that in your application code.
Example:
CREATE OR REPLACE PACKAGE my_exceptions
AS
insert_null_into_notnull EXCEPTION;
PRAGMA EXCEPTION_INIT(insert_null_into_notnull, -1400);
update_null_to_notnull EXCEPTION;
PRAGMA EXCEPTION_INIT(update_null_to_notnull, -1407);
END my_exceptions;
/
Now use the exception defined in the package
CREATE OR REPLACE PROCEDURE use_an_exception AS
BEGIN
-- application specific code ...
NULL;
EXCEPTION
WHEN my_exceptions.insert_null_into_notnull THEN
-- application specific handling for ORA-01400: cannot insert NULL into (%s)
RAISE;
END;
/
Source: http://www.orafaq.com/wiki/Exception
you can define your own exceptions, like variables (they will have the same scope as other variables so you can define package exception, etc...):
SQL> DECLARE
2 NULL_VALUES EXCEPTION;
3 PRAGMA EXCEPTION_INIT(NULL_VALUES, -1400);
4 BEGIN
5 INSERT INTO t VALUES (NULL);
6 EXCEPTION
7 WHEN null_values THEN
8 dbms_output.put_line('null value not authorized');
9 END;
10 /
null value not authorized
PL/SQL procedure successfully completed
You can handle exception by its code like this:
....
...
INSERT INTO MY_TABLE (CODE, NAME) VALUES (aCode,aName);
COMMIT;
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE = -1400 THEN
Do_MyStuff();
ELSE
raise_application_error(SQLCODE,MY_OWN_FORMAT_EXCEPTION(SQLCODE,SQLERRM),TRUE);
END IF;
END;
INSERT INTO MY_TABLE (CODE, NAME) VALUES (aCode,aName);
COMMIT;
EXCEPTION
WHEN NULL_VALUES /* i don't know this value , exist?*/
emesg := SQLERRM;
dbms_output.put_line(emesg);
WHEN OTHERS THEN
emesg := SQLERRM;
dbms_output.put_line(emesg);
END;
SQLERRM shows the sql error message
http://www.psoug.org/reference/exception_handling.html
Frequently I found myself doing some functions to insert/delete/update in one or more tables and I've seen some expected exceptions been taken care of, like no_data_found, dupl_val_on_index, etc. For an insert like this:
create or replace FUNCTION "INSERT_PRODUCTS" (
a_supplier_id IN FORNECEDOR.ID_FORNECEDOR%TYPE,
a_prodArray IN OUT PRODTABLE
)
RETURN NUMBER IS
v_error_code NUMBER;
v_error_message VARCHAR2(255);
v_result NUMBER:= 0;
v_prod_id PRODUTO.ID_PROD%TYPE;
v_supplier FORNECEDOR%ROWTYPE;
v_prodInserted PROD_OBJ;
newList prodtable := prodtable();
BEGIN
SELECT FORNEC_OBJ(ID_FORNECEDOR,NOME_FORNECEDOR,MORADA,ARMAZEM,EMAIL,TLF,TLM,FAX) into v_supplier from fornecedor where id_fornecedor = a_supplier_id;
FOR i IN a_prodArray.FIRST .. a_prodArray.LAST LOOP
INSERT INTO PRODUTO (PRODUTO.ID_PROD,PRODUTO.NOME_PROD,PRODUTO.PREC_COMPRA_PROD,PRODUTO.IVA_PROD,PRODUTO.PREC_VENDA_PROD,PRODUTO.QTD_STOCK_PROD,PRODUTO.QTD_STOCK_MIN_PROD)
VALUES (S_PRODUTO.nextval,a_prodArray(i).NOME_PROD,a_prodArray(i).PREC_COMPRA_PROD,a_prodArray(i).IVA_PROD,NULL,NULL,NULL);
/* If the above insert didn't failed, we can insert in weak entity PROD_FORNECIDO. */
SELECT ID_PROD into v_prod_id from PRODUTO where NOME_PROD = a_prodArray(i).NOME_PROD;
INSERT INTO PROD_FORNECIDO VALUES (a_supplier_id, v_prod_id,a_prodArray(i).PREC_COMPRA_PROD);
SELECT PROD_OBJ(ID_PROD,NOME_PROD,PREC_COMPRA_PROD,PREC_VENDA_PROD,QTD_STOCK_PROD,QTD_STOCK_MIN_PROD,IVA_PROD) into v_prodInserted from PRODUTO where ID_PROD= v_prod_id;
a_prodarray(i).ID_PROD := v_prod_id;
END LOOP;
INSERT INTO FORNECPRODS VALUES (a_supplier_id,v_supplier, a_prodarray);
v_result:= 1;
RETURN v_result;
COMMIT;
Exception
When no_data_found then
v_error_code := 0;
v_error_message:= 'Insert Products: One of selects returned nothing';
Insert Into errors Values (v_error_code,v_error_message, systimestamp);
RETURN v_result;
When others Then
ROLLBACK;
v_error_code := SQLCODE;
v_error_message:=substr(SQLERRM,1,50);
Insert Into errors Values (v_error_code,'Error inserting products list',systimestamp);
RETURN v_result;
END;
I would like to customize more of my exceptions or do an exception block for each select/insert. Is that possible or correct?
If so, could please show me some code with important exceptions being throwed by this function?
If you just want to substitute your own error message, there is RAISE_APPLICATION_ERROR...
When no_data_found then
RAISE_APPLICATION_ERROR(-20000
, 'Insert Products: One of selects returned nothing';
, true);
The third parameter returns the original error as well as your custom one.
Oracle also gives us the option to define our exceptions. This can be useful if we want to pass the exception to a calling program...
Declare
no_product_found exception;
Begin
....
When no_data_found then
raise no_product_found;
This would be most effective if we defined the NO_PRODUCT_FOUND exception in a package specification where it could be referenced by external program units.
In addition, Oracle provides the INIT_EXCEPTION pragma which allows us to associate Oracle error numbers with our custom exceptions. Unfortunately we cannot overload error numbers which Oracle has already defined (for instance, we cannot create our own exceptions for ORA-1403 which is already covered by the NO_DATA_FOUND exception). Find out more.
In the exception section; you can raise application error or return 0 with error code explanation. It is about your choice.
If you want to log your errors in the exception section (or in main section), write your own logging procedure with AUTONOMOUS TRANSACTION. so, your logging mechanism is not affected by your main transaction's COMMIT or ROLLBACK. (see: http://www.dba-oracle.com/t_autonomous_transaction.htm)
Another logging mechanism (DML Error Log) in Oracle 10gR2 (and above) is LOG ERRORS clause (see: http://www.oracle-base.com/articles/10g/DmlErrorLogging_10gR2.php).