raise_application_error exception handling in subprocedures - sql

I have multiple stored procedures calling multiple stored procedures in my database. To give a small example, I've constructed a fictionalised version of a few of them below. In my example, a Java program calls calculate_bill, which calls calculate_commission, which calls update_record.
I'm hoping to get some advice on how best to propagate the error messages up the stack to the calling Java layer, so the user gets a precise error message corresponding to wherever the error has occurred.
I'm really quite stuck on this. I've played around in my example with raise_application_error and just continually shuffling it up the stack. Is the way I'm doing it below remotely correct? Or is one raise_application_error in the relevant procedure enough, with no pragma exception init etc needed?
To give an idea of what I mean, in the example below, if a user entered a number which corresponded to a record which couldn't be updated because it didn't exist, I'd like them to get the message:
"Error calculating bill. Error calculating commission. No record exists to be updated" or something to that effect.
So two questions:
What is the best practice, most efficient, most tidy way to pass error messages up the stack for the end user in the application layer?
Does anyone have any suggestions on tidier output from the code, i.e. the best way to concatenate these errors to make them more meaningful? I'm really open to any suggestions on how to make this work best as I have absolutely no prior experience in this.
Example:
(Error in code):
-20000 : Error in top level procedure
-20001 : Error in middle level procedure
-20002 : Error in bottom level procedure
Java code:
try {
// call calculate_bill
exception (SQLException ex)
// output oracle code and relevant message.
Oracle code:
create or replace procedure calculate_bill(in_num NUMBER)
is
error_calculating_commission EXCEPTION;
error_updating_record EXCEPTION;
PRAGMA EXCEPTION_INIT (error_calculating_commission, -20001);
PRAGMA EXCEPTION_INIT (error_updating_record , -20002);
begin
if in_num > 2 then
calculate_commission(in_num);
else
raise_application_error(-20000, 'Error calculating bill. ' || 'Record number doesn''t exist.', false);
end if;
exception
when error_calculating_commission then
raise_application_error(SQLCODE, 'Error calculating bill. ' || SQLERRM, false);
when error_updating_record then
raise_application_error(SQLCODE, 'Error calculating bill. ' || SQLERRM, false);
when others then
raise_application_error(-20000, 'Unknown error encountered calculating bill.', false);
end;
create or replace procedure calculate_commission(in_num NUMBER)
is
begin
if in_num < 30 then
raise_application_error(-20001, 'Number too small to calculate commission.', false);
elsif in_num >= 30 and < 40 then
declare
error_storing_record EXCEPTION;
PRAGMA EXCEPTION_INIT (error_storing_record , -20002);
begin
update_record(in_num);
exception
when error_storing_record then
raise_application_error(SQLCODE, 'Error calculating commission. ' || SQLERRM, false);
when others then
raise_application_error(-20001, 'Unknown error encountered calculating commission.', false);
else
raise_application_error(-20001, 'Number too large to calculate commission', false);
end if;
end;
create or replace procedure update_record(in_num NUMBER)
is
begin
//some SQL query with a where clause, where in_num equals something
exception
when no_data_found then
raise_application_error(-20002, 'No record exists to be updated', false);
when others then
raise_application_error(-20002, 'Unknown error encountered updating record.', false);
end if;
end;
Note: I know this example is a little contrived. I was just trying to keep it brief.

The way I would implement this is to use RAISE_APPLICATION_ERROR where the error actually originates, then leave it unhandled in the other layers (or, if you want to do logging in the database, catch it in the OTHERS section, then re-raise using RAISE rather than RAISE_APPLICATION_ERROR.
You're OTHERS sections are still perpetuating the problem raised in your previous question: when an unknown error occurs you're replacing it with a generic, unhelpful message. To paraphrase Tom Kyte, "a WHEN OTHERS that does not end in RAISE is a bug`.
With no logging in the database, I would re-write the provided code like this:
create or replace procedure calculate_bill(in_num NUMBER)
is
begin
if in_num > 2 then
calculate_commission(in_num);
else
raise_application_error(-20000, 'Error calculating bill. ' || 'Record number doesn''t exist.', false);
end if;
end;
create or replace procedure calculate_commission(in_num NUMBER)
is
begin
if in_num < 30 then
raise_application_error(-20001, 'Number too small to calculate commission.', false);
elsif in_num >= 30 and in_num < 40 then
update_record(in_num);
else
raise_application_error(-20001, 'Number too large to calculate commission', false);
end if;
end;
create or replace procedure update_record(in_num NUMBER)
is
v_stub number;
begin
select 1 into v_stub from dual where 1 = 0;
exception
when no_data_found then
raise_application_error(-20002, 'No record exists to be updated', false);
end;
Below is sample input and the stack traces they created:
exec calculate_bill(0)
ORA-20000: Error calculating bill. Record number doesn't exist.
ORA-06512: at "SCHEMANAME.CALCULATE_BILL", line 7
ORA-06512: at line 1
exec calculate_bill(10)
ORA-20001: Number too small to calculate commission.
ORA-06512: at "SCHEMANAME.CALCULATE_COMMISSION", line 5
ORA-06512: at "SCHEMANAME.CALCULATE_BILL", line 5
ORA-06512: at line 1
exec calculate_bill(35)
ORA-20002: No record exists to be updated
ORA-06512: at "SCHEMANAME.UPDATE_RECORD", line 8
ORA-06512: at "SCHEMANAME.CALCULATE_COMMISSION", line 7
ORA-06512: at "SCHEMANAME.CALCULATE_BILL", line 5
ORA-06512: at line 1
exec calculate_bill(100)
ORA-20001: Number too large to calculate commission
ORA-06512: at "SCHEMANAME.CALCULATE_COMMISSION", line 9
ORA-06512: at "SCHEMANAME.CALCULATE_BILL", line 5
ORA-06512: at line 1
If you were to add error logging to these procedures, it would be a simple matter of adding an OTHERS clause to the EXCEPTION sections:
WHEN OTHERS THEN
my_logging (SQLCODE, SQLERRM, DBMS_UTILITY.format_error_backtrace ());
RAISE;
The new exception created by your RAISE_APPLICATION_ERROR calls will be handled transparently by the RAISE.

Related

Error While generating pascal's triangle in plSQL

well I'm pretty new to plSQL and I can't seem to figure out some errors I'm getting while trying to generate a pascal's triangle
Here's the code:
SET SERVEROUTPUT ON;
DECLARE
N NUMBER;
I NUMBER;
J NUMBER;
K NUMBER;
L NUMBER;
T NUMBER;
S NUMBER;
BEGIN
DBMS_OUTPUT.PUT_LINE('ENTER THE LIMIT');
N:=&N;
I:=0;
T:=N-2;
S:=0;
L:=0;
WHILE (I<N) LOOP
J:=0;
WHILE (J<T) LOOP
DBMS_OUTPUT.PUT(' ');
J:=J+1;
END LOOP;
K:=0;
WHILE (K<=S) LOOP
IF (K<=I) THEN
DBMS_OUTPUT.PUT(K+1);
L:=K;
ELSE
L:=L-1;
DBMS_OUTPUT.PUT(L+1);
END IF;
K:=K+1;
END LOOP;
DBMS_OUTPUT.PUT_LINE('');
T:=T-1;
S:=S+2;
END LOOP;
END;
The errors are
Error report -
ORA-20000: ORU-10027: buffer overflow, limit of 1000000 bytes
ORA-06512: at "SYS.DBMS_OUTPUT", line 32
ORA-06512: at "SYS.DBMS_OUTPUT", line 97
ORA-06512: at line 29
20000. 00000 - "%s"
*Cause: The stored procedure 'raise_application_error'
was called which causes this error to be generated.
*Action: Correct the problem as described in the error message or contact
the application administrator or DBA for more information.
Neither I nor N are ever updated, so you have an infinite loop. Other than that, the error is indicating that the DBMS_OUTPUT buffer is being exceeded. It's default buffer size is 1,000,000 bytes, though you can adjust that with an appropriate DBMS_OUTPUT.ENABLE call or SET SERVEROUTPUT SIZE statement.
The problem you're having has to do with SQL*Plus, the Oracle SQL client, rather than PL/SQL, the programming language. Change the line which says SET SERVEROUTPUT ON to
SET SERVEROUTPUT ON SIZE UNLIMITED
This error may, however, indicate you've got an infinite loop, so be careful. Note that I never appears to be incremented and N is never decremented, so the loop started at WHILE (I<N) LOOP may not terminate.
Best of luck.

how do I get PL PGSQL error code in a function?

I am writing a PL/PGSQL function and it produces an error. I want to print the error with RAISE NOTICE statement but I don't know how to get the error code? What variable holds the last error?
This is my sample code:
IF FOUND
THEN
BEGIN
insert into app.company(dateinserted,name) values(now(),company_name) returning comnpany_id;
return company_id;
EXCEPTION
WHEN OTHERS THEN
RAISE NOTICE 'Insert failed with...';
return -2;
END;
ELSE
RETURN -1;
END IF;
This code will return company_id if insert was successful and print the error if it fails.
...
EXCEPTION
WHEN OTHERS THEN
RAISE NOTICE 'Insert failed with error code %', SQLSTATE;
...
Read more in the documentation.
include SQLERRM to get an error message.

ORA-21700: object does not exist or is marked for delete

I have package with Period and TableOfPeriod types:
TYPE Period
IS RECORD
( StartPeriod Date,
EndPeriod Date
);
TYPE TableOfPeriod
IS TABLE OF Period;
and in this package I have three simple function:
FUNCTION Temp1
RETURN TableOfPeriod IS
returnedValue TableOfPeriod := TableOfPeriod();
BEGIN
returnedValue.extend(1);
returnedValue(1).StartPeriod := sysdate-100;
returnedValue(1).EndPeriod := sysdate;
RETURN returnedValue;
END Temp1;
FUNCTION CalculateFine
return VARCHAR2
IS
freewillLockTableRP TableOfPeriod:=TableOfPeriod();
compulsoryLockTableRP TableOfPeriod:=TableOfPeriod();
BEGIN
--for testing
compulsoryLockTableRP:=Temp1();
FOR i IN compulsoryLockTableRP.FIRST..compulsoryLockTableRP.LAST LOOP
IF(((compulsoryLockTableRP(i).EndPeriod - compulsoryLockTableRP(i).StartPeriod)>1)) THEN
BEGIN
-- RAISE_APPLICATION_ERROR(-20001, 'Hello world');
SELECT T111.StartPeriod StartPeriod,
T111.EndPeriod EndPeriod
bulk collect into freewillLockTableFull
FROM TABLE(DistributeDaysByPeriods(compulsoryLockTableRP, 5)) T111;
END;
END IF;
END LOOP;
/*SELECT T111.StartPeriod StartPeriod,
T111.EndPeriod EndPeriod
-- BULK COLLECT INTO compulsoryLockTableRP
bulk collect into freewillLockTableFull
FROM TABLE(DistributeDaysByPeriods(compulsoryLockTableRP, 5)) T111;*/
--===========
--SOME OTHER PROCESSING
RETURN 'Ok '
|| '#r';
EXCEPTION
WHEN No_Data_Found THEN return 'No data found#g';
-- WHEN OTHERS THEN RETURN SQLERRM;
END CalculateFine;
When I execute this function, I have next error:
"ORA-21700: object does not exist or is marked for delete ORA-06512:
at "MyPackageName", line 1181 ORA-06512: at line 1
21700. 00000 - "object does not exist or is marked for delete""
where 1181 line is a line with Select statement of CalculateFine function. Can anybody tell me, whats wrong and how I can solve this problem?
Check if you have in the same folder where your package is located a file with the same name but with an extention *.~sql (e.g.: your_file_name.~sql). I had the same error but after I have deleted an *.~sql file I could compile my package without ORA errors.

exception handling not outputting to dbms

Here is my sql code for a function that calculates the area of a circle. I am having trouble getting a dbms_output to show the user defined exception.
I begin by declaring an exception underzero. then i raise it if the user inputs a number less than or equal to 0. In the exception i have dbms_output then return the result. It shows in the query result that the value is null, however the output isn't showing.
I have already set serveroutput on and set verify off. I don't know why it wont output anything. If i run that line alone it outputs to the dbms output window but not when the exception is raised.
create or replace function circle_area
(p_radius number)
return number
is
c_Pi Constant number := acos(-1);
v_result number(10, 2);
underzero exception;
begin
if p_radius <=0 then
raise underzero;
else
v_result := c_pi * p_radius * p_radius;
return v_result;
end if;
exception
when underzero then
dbms_output.put_line('enter number greater than 0');
return v_result;
when others then
dbms_output.put_line('Exception Location: Anonymous Block');
dbms_output.put_line(sqlcode || ': ' || sqlerrm);
return v_result;
end circle_area;
to call the function:
select circle_area(-2) from dual;
I'm not sure that I understand the problem. The code you posted will generate output to dbms_output if serveroutput is enabled...
SQL> set serveroutput on;
SQL> create or replace function circle_area
2 (p_radius number)
3 return number
4 is
5 c_Pi Constant number := acos(-1);
6 v_result number(10, 2);
7 underzero exception;
8 begin
9 if p_radius <=0 then
10 raise underzero;
11 else
12 v_result := c_pi * p_radius * p_radius;
13 return v_result;
14 end if;
15
16 exception
17 when underzero then
18 dbms_output.put_line('enter number greater than 0');
19 return v_result;
20 when others then
21 dbms_output.put_line('Exception Location: Anonymous Block');
22 dbms_output.put_line(sqlcode || ': ' || sqlerrm);
23 return v_result;
24 end circle_area;
25 /
Function created.
SQL> select circle_area(-2) from dual;
CIRCLE_AREA(-2)
---------------
enter number greater than 0
I'm assuming that this is part of a homework assignment and not a real problem that you're facing. In reality, you'd never write code whose primary purpose was to write to dbms_output and you would never, ever have an exception handler that only wrote to dbms_output.

User-defined function with IN VARCHAR parameter in PL/SQL Oracle [closed]

This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 9 years ago.
I need some help. It is the first time when I am asking for help here, so please excuse me for my bad english. I am so mad, because I can not figure out how to fix this error. I work in Oracle 10g. The output is:
Function created.
No errors.
Function created.
Enter value for v_numes: 'SECTIA1' --SECTIA1 is the name of section(department) from sectii table
Sectia exista in baza de date SECTIA1
Numele sectiei este: SECTIA1 --the name of the department(v_numes)
DECLARE
*
ERROR at line 1:
ORA-06502: PL/SQL: numeric or value error: character to number conversion error
ORA-06512: at "SYSTEM.FUNCTION0", line18
ORA-01403: no data found
ORA-06512: at line 17
Here it is my code:
set serveroutput on
set verify off
CREATE OR REPLACE FUNCTION function0(p_section_name VARCHAR) RETURN NUMBER IS
v_procent1 CONSTANT REAL:=0.01;
v_cods sectii.cods%TYPE;--the table is named sectii, which means sections
v_ok1 NUMBER:=1;
BEGIN
DBMS_OUTPUT.PUT_LINE('Numele sectiei este: '||p_section_name);
SELECT cods
INTO v_cods
FROM sectii
WHERE den=UPPER(p_section_name);
DBMS_OUTPUT.PUT_LINE('Codul sectiei pentru '||p_section_name||' este '||v_cods);
RETURN (v_ok1);
EXCEPTION
WHEN TOO_MANY_ROWS THEN
RETURN 'Prea multe sectii cu acest nume.';
WHEN NO_DATA_FOUND THEN
RETURN 'Nici o sectie cu acest nume.';
WHEN OTHERS THEN
RAISE_APPLICATION_ERROR(-20011,'Unknown Exception in function0');
END function0;
/
show errors;
/
DECLARE
v_numes VARCHAR2(255);
v_numesu VARCHAR2(255);
v_numesl VARCHAR2(255);
v_boool NUMBER:=1;
e_invalid_section EXCEPTION;
BEGIN
v_numes:=&v_numes;
IF SQL%NOTFOUND THEN
RAISE e_invalid_section;
ELSE
DBMS_OUTPUT.PUT_LINE('Sectia exista in baza de date '|| v_numes);
END IF;
v_numesu:=UPPER(v_numes);
v_numesl:=LOWER(v_numes);
IF v_numes=v_numesl OR v_numes=v_numesu THEN
DBMS_OUTPUT.PUT_LINE('FUNCTION0 '||function0(v_numes));
END IF;
--v_numes:=TO_CHAR(v_numes);
--DBMS_OUTPUT.PUT_LINE('RETURN FUNCTIE0: '||v_boool);
EXCEPTION
WHEN e_invalid_section THEN --exceptie definita de utilizator
DBMS_OUTPUT.PUT_LINE('Nu exista aceasta sectie in tabelul sectii.');
END;
/
The declaration of FUNCTION0, states that the function returns a number:
CREATE OR REPLACE FUNCTION function0(p_section_name VARCHAR) RETURN NUMBER IS
The return statements in your exception blocks return characters...
EXCEPTION
WHEN TOO_MANY_ROWS THEN
RETURN 'Prea multe sectii cu acest nume.';
WHEN NO_DATA_FOUND THEN
RETURN 'Nici o sectie cu acest nume.';
One thing to note, your error was extremely specific:
ERROR at line 1:
ORA-06502: PL/SQL: numeric or value error: character to number conversion error
ORA-06512: at "SYSTEM.FUNCTION0", line18
ORA-01403: no data found
ORA-06512: at line 17
This means you're converting a character to a number at line 18 and a NO_DATA_FOUND exception was raised at line 17...
To answer all of your comments in one place, you're getting a NO_DATA_FOUND exception because of the WHERE clause, as I've already said.
WHERE den = UPPER(p_section_name)
Run this SELECT statement:
select * from cods where den = upper(den)
It will not return any rows because it is not true. If you're uppercasing the parameter p_section_name you also need to uppercase the column den. You need to search case-insensitively, see Case insensitive searching in Oracle for more details.