Testing exception handling in Oracle 11g - oracle

I have this oracle code
FUNCTION get_enc_val(p_in IN VARCHAR2,
p_key IN VARCHAR2
)
RETURN raw
IS
p_title_procedure_name VARCHAR2(100) := 'get_enc_val';
l_enc_val RAW(2000);
l_mod PLS_INTEGER := DBMS_CRYPTO.ENCRYPT_AES128 + DBMS_CRYPTO.CHAIN_CBC + DBMS_CRYPTO.PAD_PKCS5;
v_key VARCHAR2(16);
encryption_ex Exception;
BEGIN
v_key := RPAD(SUBSTR(TRIM(p_key), 1, 16), 16, '0');
l_enc_val := DBMS_CRYPTO.encrypt(UTL_RAW.cast_to_raw(p_in), l_mod, UTL_RAW.cast_to_raw(v_key));
RAISE encryption_ex;
RETURN l_enc_val;
EXCEPTION
WHEN OTHERS THEN
service_func.log_error(p_title_package_name || '.' || p_title_procedure_name, 'Proc', NULL, SYSDATE, SQLERRM, p_in || '~' || p_key);
RETURN 'Encryption_ERROR';
END;
When I run this I get ORA-06510 Unhandled user-defined exception while it should really return the string 'Encryption_ERROR'.What gives? It goes to the Exception block, because I see the result of log_error function. Question is, isn't the Exception block supposed to HANDLE ANY exception?
I am a bit confused.

The problem is that your second RETURN statement is returning a VARCHAR2 while your function is declared to return a RAW. You could fix that by calling UTL_RAW.CAST_TO_RAW, i.e.
EXCEPTION
WHEN OTHERS THEN
service_func.log_error(p_title_package_name || '.' ||
p_title_procedure_name,
'Proc',
NULL,
SYSDATE,
SQLERRM,
p_in || '~' || p_key);
RETURN utl_raw.cast_to_raw( 'Encryption_ERROR' );
END;
If I declare two functions, one that returns a hard-coded string and one that returns a RAW, you'll see the difference (I'm removing the DBMS_CRYPTO calls and the LOG_ERROR call). If I declare a function that returns a RAW, you get a result back (though a human would have to convert the raw back into a string to make sense of the result)
SQL> ed
Wrote file afiedt.buf
1 create or replace function throw_exception
2 return raw
3 is
4 my_exception exception;
5 begin
6 raise my_exception;
7 exception
8 when others then
9 return utl_raw.cast_to_raw( 'Foo' );
10* end;
SQL> /
Function created.
SQL> select throw_exception
2 from dual;
THROW_EXCEPTION
-----------------------------------------------------------------------------
466F6F
If I just return a string, I'll get the same exception you were getting
SQL> ed
Wrote file afiedt.buf
1 create or replace function throw_exception2
2 return raw
3 is
4 my_exception exception;
5 begin
6 raise my_exception;
7 exception
8 when others then
9 return 'Foo';
10* end;
SQL> /
Function created.
SQL> select throw_exception2
2 from dual;
select throw_exception2
*
ERROR at line 1:
ORA-06502: PL/SQL: numeric or value error: hex to raw conversion error
ORA-06512: at "SCOTT.THROW_EXCEPTION2", line 9
ORA-06510: PL/SQL: unhandled user-defined exception
Of course, the other option would be to declare that the function returns a VARCHAR2. But I would much rather have hashes and encrypted data in a RAW than a VARCHAR2 so that you never have to worry about things like character set conversion issues mangling the data.

Since you are sure that service_func.log_error(...); is not raising the error, the error must be thrown by the calling procedure/function.
And here's something that raises the same error.
create or replace function abc return raw is
begin
return 'Encryption_ERROR';
end;
/
declare
r raw(50);
begin
r := abc;
end;
/

Related

Handling exception 1) when defining Oracle function 2) When Calling Oracle function

My Oracle function below(code1) have no exception handling.
Therefore if it is called(code2) with 0, error shows.
--Code 1
CREATE OR REPLACE FUNCTION TEST2
(P1 IN VARCHAR2)
RETURN NUMBER AS V_VALUE NUMBER;
BEGIN
SELECT(
SELECT 1/TO_NUMBER(P1)
FROM DUAL
)
INTO V_VALUE
FROM DUAL;
RETURN V_VALUE;
END;
/
--Code2
SELECT TEST2('0') FROM DUAL;
Please, help to add exception handling for each 1) 2) case as below.
case 1) when defining function, how to modify code1
for function to return -1 if a system exception,
including dividing by zero happen?
case 2) Without adding exception in my Oracle funtion,
how to modify code2 for the reslut to be -1 if a system excetion, including dividing zero happens in the function?
I think you are making this more complicated than it needs to be. I did not compile this code below, but should provide as an example. Regarding exceptions, it is OK to handle divide-by-zero, but hiding all other exception types is very, very bad design. Also, if I pass in test2(-1), then the result will be a valid value of -1. Are you assured your input parameter is always positive. Regardless, here is a solution which checks for a 0 parameter, and avoids the division problem. A better solution is to define TEST1 P1 as a NUMBER to begin with, and let the caller format it as needed. If not, I could pass in something like TEST2('fsfd') and get an exception.
CREATE OR REPLACE FUNCTION TEST2(P1
IN VARCHAR2)
RETURN NUMBER;
D_Result NUMBER : = -1;
BEGIN
IF P1 <> 0 THEN
D_result := 1/TO_NUMBER(P1);
END IF;
RETURN D_Result;
END
If you really want to throw a divide error, you can catch is like this:
DECLARE
result NUMBER;
BEGIN
result := test2(0);
EXCEPTION
WHEN OTHERS THEN
result := -1;
END;
#OldProgrammer shows how to prevent the exception from occurring, which is the best choice. However, if you want to allow the exception to occur and catch it in the function you could use:
CREATE OR REPLACE FUNCTION TEST2(P1 IN VARCHAR2)
RETURN NUMBER
AS
V_VALUE NUMBER;
BEGIN
BEGIN
V_VALUE := 1 / TO_NUMBER(P1);
EXCEPTION
WHEN OTHERS THEN
V_VALUE := -1;
END;
RETURN V_VALUE;
END TEST2;
or you could simplify this to
CREATE OR REPLACE FUNCTION TEST2(P1 IN VARCHAR2)
RETURN NUMBER
AS
BEGIN
RETURN 1 / TO_NUMBER(P1);
EXCEPTION
WHEN OTHERS THEN
RETURN -1;
END TEST2;

Oracle: detect exception in type construtor during bulk collect

Environment: Oracle 11g
I have a type ty1 with some arguments like s1, s2.
If I use it like this:
SELECT ty1(s1,s2) BULK COLLECT INTO l_collection_of_ty1 FROM ...
I get a collection of ty1.
Now if there is an exception raised inside one of the constructor calls of ty1 the corresponding element of my collection is set to NULL, but the overall SELECT works (no exception, collection returned).
My question, can I detect this right after the SELECT without having to loop over the collection? Is there maybe even a way to access the original error message in a similar way as SQL%BULK_EXCEPTION does for DML?
One workaround I thought of is to not use the constructor during BULK COLLECT, but read collections of s1 and s2, then construct the TYPE in my own loop where I can handle the exception, but that is much more code and I would prefer it if Oracle has some build in way.
here is a testcase that demonstrates that all exceptions are thrown through a bulk-select, but NO_DATA_FOUND is dropped.
-- remember to retreive dbms_output
SET SERVEROUTPUT ON
CREATE OR REPLACE FUNCTION p1 (v1 PLS_INTEGER)
RETURN NUMBER
AS
BEGIN
CASE v1
WHEN 1
THEN
RAISE ACCESS_INTO_NULL;
WHEN 2
THEN
RAISE CASE_NOT_FOUND;
WHEN 3
THEN
RAISE COLLECTION_IS_NULL;
WHEN 4
THEN
RAISE CURSOR_ALREADY_OPEN;
WHEN 5
THEN
RAISE DUP_VAL_ON_INDEX;
WHEN 6
THEN
RAISE INVALID_CURSOR;
WHEN 7
THEN
RAISE INVALID_NUMBER;
WHEN 8
THEN
RAISE LOGIN_DENIED;
WHEN 9
THEN
RAISE NO_DATA_FOUND;
WHEN 10
THEN
RAISE NOT_LOGGED_ON;
WHEN 11
THEN
RAISE PROGRAM_ERROR;
WHEN 12
THEN
RAISE ROWTYPE_MISMATCH;
WHEN 13
THEN
RAISE SELF_IS_NULL;
WHEN 14
THEN
RAISE STORAGE_ERROR;
WHEN 15
THEN
RAISE SUBSCRIPT_BEYOND_COUNT;
WHEN 16
THEN
RAISE SUBSCRIPT_OUTSIDE_LIMIT;
WHEN 17
THEN
RAISE SYS_INVALID_ROWID;
WHEN 18
THEN
RAISE TIMEOUT_ON_RESOURCE;
WHEN 19
THEN
RAISE TOO_MANY_ROWS;
WHEN 20
THEN
RAISE VALUE_ERROR;
WHEN 21
THEN
RAISE ZERO_DIVIDE;
ELSE
RETURN v1;
END CASE;
END;
/
DECLARE
TYPE type1 IS TABLE OF NUMBER;
col1 type1;
BEGIN
FOR ii IN 1 .. 22
LOOP
BEGIN
SELECT p1 (ii)
BULK COLLECT INTO col1
FROM DUAL;
IF col1 (1) IS NULL
THEN
DBMS_OUTPUT.put_line (TO_CHAR (ii, '00') || ': NULL');
ELSE
DBMS_OUTPUT.put_line (TO_CHAR (ii, '00') || ': ' || col1 (1));
END IF;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line (
TO_CHAR (ii, '00')
|| ': exception '
|| SQLCODE);
END;
END LOOP;
END;
/
I wrote a small test case for you and the exception has to be swallowed inside ty1 and not raised, because otherwise the select would not finish successfully:
create or replace function p1 (v1 number) return number
as
begin
if v1 = 1 then
return 1;
elsif v1 = 2 then
raise_application_error(-20010,'err 2');
else
raise_application_error(-20010,'err 3');
end if;
end;
/
declare
type type1 is table of number;
col1 type1;
begin
select p1(level) bulk collect into col1 from dual connect by level <=3;
end;
/
Result:
Error report -
ORA-20010: err 2
So my suggestion to you is - if you want to stay close to your solution - that at the place where you handle the exception in ty1, you write the exceptions to a table. You can then access this table to find the exceptions and don't need to loop through the whole collection. But honestly, what's wrong with looping in PL/SQL over a collection, it's all in memory? HTH
I see only two options.
An exception is consumed inside constructor.
The another constructor is in use.
Example:
create or replace type ty1 as object (p1 number
, constructor function ty1 (p1 varchar2)
return self as result);
create or replace type body ty1
is
constructor function ty1 (p1 varchar2)
return self as result is
x number;
begin
raise_application_error(-20000,'Always Exception');
return;
end;
end;
Test 1 no exception:
declare
type l_collection_of_ty1 is table of ty1;
a varchar2(4000);
x l_collection_of_ty1;
begin
--test1 select ty1(level) bulk collect into x from dual connect by level < 10;
-- no exceptions
--test2 select ty1(level||1) bulk collect into x from dual connect by level < 10;
-- exceptions
--test3 select ty1(null) bulk collect into x from dual connect by level < 10;
-- exceptions
end;
In Test1 db is using Attribute-Value Constructor. It is generate by default.
In Test2 db is using user defined constructor.
In Test3 db is not able to find out which constructor should be used.()

PL/SQL value_error exception not getting caught

This is my Department table:
create table Department(
DeptNo int primary key,
DeptName varchar2(21) not null,
DeptLocation varchar2(13) not null
);
I am trying to insert value for DeptName column with length more than accepted i.e. 21, which means I should get "VALUE_ERROR" exception.
The plsql code I am running is:
begin
insert into department values(1, 'Some random department name', 'SomeLocation');
exception
when value_error then
dbms_output.put_line('Cannot store the value!');
end;
As I am trying to catch the exception, it is not getting caught. I am getting the error:
ORA-12899: value too large for column "SQL_ZIRHWMLFPCEAKPGYGJJZLSIFI"."DEPARTMENT"."DEPTNAME" (actual: 27, maximum: 21) ORA-06512: at line 2
ORA-06512: at "SYS.DBMS_SQL", line 1721
But if I change my exception from "value_error" to "others"
begin
insert into department values(1, 'Some random department name', 'SomeLocation');
exception
when others then
dbms_output.put_line('Cannot store the value!');
end;
then I get the expected output
Cannot store the value!
Where could I have gone wrong? Please let me know. Thanks!
PS: I am running all the code on livesql.oracle.com
Ideally, what you are using is correct and it should had worked as desired. But the exception VALUE_ERROR behaves differently in somecases. See the below illustrative example.
As per the documentation value_error comes when there is an
arithmetic, conversion, truncation, or size-constraint error occurs.
For example, when your program selects a column value into a character
variable, if the value is longer than the declared length of the
variable, PL/SQL aborts the assignment and raises VALUE_ERROR. In
procedural statements, VALUE_ERROR is raised if the conversion of a
character string into a number fails.
The last line says, In procedural statements, VALUE_ERROR is raised if the conversion of a character string into a number fails., but when i run this in a block it rasied INVALID_NUMBER exception.
SQL> declare
2 n number;
3 begin
4 select to_number('a')
5 into n
6 from dual
7 ;
8 exception
9 when value_error
10 then
11 dbms_output.put_line ('Value Error');
12 when invalid_number
13 then
14 dbms_output.put_line ('Invalid Number');
15 end;
16 /
Invalid Number
PL/SQL procedure successfully completed.
I expected that it would raise the VALUE_ERROR but it didn't. So it might be the case that Oracle was not able to raise value_error in your case and when you used WHEN OTHERS it was caught.
Edit:
Ok. So is it possible somehow to catch value_error exception by giving
the value longer than the declared length of the variable?
Explicit Raise of System Exception : Not very elegant but you can do it as below.
declare
var int;
var1 varchar2(21);
var2 varchar2(13);
begin
var1:='Some random department name';
var2:= 'SomeLocation'
If var1 > 21 then
RAISE VALUE_ERROR;
END IF;
If var2 > 13 then
RAISE VALUE_ERROR;
END IF;
insert into department values(1, var1, var2);
exception
when value_error then
dbms_output.put_line('Cannot store the value!');
end;
VALUE_ERROR was not the exception raised when you ran your code, if you want, you can defined an EXCEPTION and catch it, see below code for sample,
DECLARE
ORA_12899 EXCEPTION;
PRAGMA EXCEPTION_INIT(ORA_12899, -12899);
begin
insert into department values(1, 'Some random department name', 'SomeLocation');
exception
when ORA_12899 then
dbms_output.put_line('Cannot store the value!');
end;
/
Below code will have a VALUE_ERROR;
DECLARE
ORA_12899 EXCEPTION;
PRAGMA EXCEPTION_INIT(ORA_12899, -12899);
v_dept VARCHAR2(20);
begin
v_dept := 'Some random department name';
insert into department values(1, v_dept, 'SomeLocation');
exception
when ORA_12899 then
dbms_output.put_line('Cannot store the value!');
end;
/

SQL: exception handling in a function

I have encountered a rather mysterious problem today. As I executed my SQL function f_interestrate()( which should raise a from me defined exception when one of the parameters is equal to 0 ) with the following parameters:
SELECT GENERAL_FUNCTIONS.F_INTERESTRATE(2500000, 0.10, 0) FROM dual;
Gave me the following error:
ORA-06503: PL/SQL: Function returned without value
ORA-06512: at "NOAHBASE.GENERAL_FUNCTIONS", line 73
06503. 00000 - "PL/SQL: Function returned without value"
*Cause: A call to PL/SQL function completed, but no RETURN statement was executed.
*Action: Rewrite PL/SQL function, making sure that it always returns
a value of a proper type.
But as you may see in the following code sample the function should instead raise the form me defined exception ex_invalid_devisor. Not forget to mention that this function is nested inside a package.
FUNCTION f_interestrate(pn_principal NUMBER, pn_interest NUMBER, pn_years NUMBER) RETURN NUMBER IS
vn_interestrate NUMBER;
ex_invalid_devisor EXCEPTION;
BEGIN
IF pn_principal = 0 OR
pn_interest = 0 OR
pn_years = 0 THEN
RAISE ex_invalid_devisor;
ELSE
vn_interestrate := ((pn_interest/pn_principal)-1)/pn_years;
RETURN vn_interestrate;
END IF;
EXCEPTION
WHEN ex_invalid_devisor THEN
DBMS_OUTPUT.PUT_LINE('Devisor must be bigger then 0');
END;
Am I doing anything wrong?
This is how you should handle ... I added 'return -1' in your code. HTH.
create or replace FUNCTION f_interestrate(pn_principal NUMBER, pn_interest NUMBER, pn_years NUMBER) RETURN NUMBER IS
vn_interestrate NUMBER;
ex_invalid_devisor EXCEPTION;
BEGIN
IF pn_principal = 0 OR
pn_interest = 0 OR
pn_years = 0 THEN
RAISE ex_invalid_devisor;
ELSE
vn_interestrate := ((pn_interest/pn_principal)-1)/pn_years;
RETURN vn_interestrate;
END IF;
EXCEPTION
WHEN ex_invalid_devisor THEN
DBMS_OUTPUT.PUT_LINE('Devisor must be bigger then 0');
return -1;
END;
SQL> select F_INTERESTRATE(2500000, 0.10, 0) FROM dual;
F_INTERESTRATE(2500000,0.10,0)
------------------------------
-1
As PL/SQL already has a perfectly good zero_divide exception, I'd be tempted to just write the function as:
create or replace function f_interestrate
( pn_principal number
, pn_interest number
, pn_years number )
return number
as
begin
return ((pn_interest / pn_principal) - 1) / pn_years;
end;
then you'll get the default failure message:
SQL> select f_interestrate(2500000, 0.10, 0) from dual;
select f_interestrate(2500000, 0.10, 0) from dual
*
ERROR at line 1:
ORA-01476: divisor is equal to zero
ORA-06512: at "XXX.F_INTERESTRATE", line 8
Or if you really need some customised handling,
create or replace function f_interestrate
( pn_principal number
, pn_interest number
, pn_years number )
return number
as
begin
return ((pn_interest / pn_principal) - 1) / pn_years;
exception
when zero_divide then
[[[ do something here ]]]
end;
I notice you also raise your custom ex_invalid_devisor exception when pn_interest is zero, even though this isn't used as a divisor, so perhaps there is some subtle logic I am missing here.
(Edit: thinking about it, if pn_interest is zero then maybe you just need to return pn_principal regardless.)

What exact exception to be caugth while calling TO_DATE in pl/sql code

I have below code to_date('1311313', 'yymmdd') which actually throws exception with saying invalid month. Which is can manage as
exception
when others then
sop('date format is wrong');
Here the problem is everything will get caught which I do not want to do as if some other error will occur then also it will pass the message date format is wrong. I also do not want to create a user defined exception. Just want to know which exception is being thrwon out so that I can use in my code like below
exception
when name_of_exception then
sop('date format is wrong');
The Internally Defined Exceptions section of the Oracle Database PL/SQL Language Reference says:
An internally defined exception does not have a name unless either PL/SQL gives it one (see "Predefined Exceptions") or you give it one.
You code throws the exception ORA-01830:
SQL> select to_date('1311313', 'yymmdd') from dual
*
ERROR at line 1:
ORA-01830: date format picture ends before converting entire input string
Since it is not one of the Predefined Exceptions, you must give it a name yourself:
declare
ex_date_format exception;
pragma exception_init(ex_date_format, -1830);
v_date date;
begin
select to_date('1311313', 'yymmdd')
into v_date
from dual;
exception
when ex_date_format then
sop('date format is wrong');
end;
/
There are at least two approaches to handle different exceptions raised during an attempt to convert character literal to a value of DATE data type:
Define as many exception names and associate them with Oracle error codes, using exception_init pragma, as many exceptions to_date() function is able to raise.
Create a stand alone, or part of a package, wrap-up function for to_date() function, with one when others exception handler.
Personally I lean toward the second one.
SQL> create or replace package util1 as
2 function to_date1(
3 p_char_literal in varchar2,
4 p_date_format in varchar2
5 ) return date;
6 end;
7 /
Package created
SQL> create or replace package body util1 as
2
3 function to_date1(
4 p_char_literal in varchar2,
5 p_date_format in varchar2
6 ) return date is
7 begin -- in this situation it'll be safe to use `when others`.
8 return to_date(p_char_literal, p_date_format);
9 exception
10 when others then
11 raise_application_error(-20001, 'Not a valid date');
12 end;
13
14 end;
15 /
Package body created
Now, there is only one exception to handle, -20001 Not a valid date, and your PL/SQl block might look like this:
SQL> set serveroutput on;
-- [1] otherwise, for '1311313' the ORA-01830 exception would be raised
SQL> declare
2 not_a_valid_date exception;
3 pragma exception_init(not_a_valid_date, -20001);
4 l_res date;
5 begin
6 l_res := util1.to_date1('1311313', 'yymmdd');
7 exception
8 when not_a_valid_date then
9 dbms_output.put_line(sqlerrm);
10 -- or other handler sop('date format is wrong');
11 end;
12 /
ORA-20001: Not a valid date
-- [2] otherwise, for '000000' the ORA-01843(not a valid month)
-- exception would be raised
SQL> declare
2 not_a_valid_date exception;
3 pragma exception_init(not_a_valid_date, -20001);
4 l_res date;
5 begin
6 l_res := util1.to_date1('000000', 'yymmdd');
7 exception
8 when not_a_valid_date then
9 dbms_output.put_line(sqlerrm);
10 -- or other handler sop('date format is wrong');
11 end;
12 /
ORA-20001: Not a valid date

Resources