Skip to Main Content
  • Questions
  • WHEN OTHERS and raise/ raise_application_error

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question.

Asked: January 06, 2011 - 1:43 pm UTC

Last updated: July 16, 2013 - 12:18 pm UTC

Version: 11.1.0

Viewed 10K+ times! This question is

You Asked



Dear Sir,

We have a pro*c program which calls a PL/SQL procedure (say t1).

Procedure "t1" should return the error code and message so that pro*c program can take action.

We need to use the "WHEN OTHERS" also in addition to other validations in exception section.

We also want to use "raise/ raise_application_error" due to use of "WHEN OTHERS".

We are losing the error code and message variable values if use "raise/ raise_application_error". Is there any way to keep the values?

Below is sample code and run details.


Thanks and keep up the good work.
Rakesh


--start sample code

--Database version is Oracle 11g R1
--PROCEDURE t1 without raise/ raise_application_error
create or replace PROCEDURE t1(
p_environment_in IN VARCHAR2,
p_return_val_inout IN OUT NOCOPY NUMBER,
p_return_msg_inout IN OUT NOCOPY VARCHAR2 )
IS
v_environment_in varchar2(1);
BEGIN

--Process Logic
--process_logic;

--Test scenario for fail
v_environment_in := 'XX';

EXCEPTION
WHEN OTHERS THEN
p_return_val_inout := SQLCODE;
p_return_msg_inout := DBMS_UTILITY.FORMAT_ERROR_STACK()||DBMS_UTILITY.FORMAT_ERROR_BACKTRACE();
dbms_output.put_line(chr(10) || '-' || p_return_val_inout || '-' || p_return_msg_inout );
END t1;
/
show errors




--Test code for running procedure t1
VARIABLE ret_cd NUMBER
VARIABLE ret_msg VARCHAR2(4000)
EXECUTE :ret_cd := 0
EXECUTE :ret_msg := ''
PRINT ret_cd
PRINT ret_msg
EXECUTE t1('D',:ret_cd,:ret_msg);
PRINT ret_cd
PRINT ret_msg



/*
--1st

SQL> VARIABLE ret_cd NUMBER
SQL> VARIABLE ret_msg VARCHAR2(4000)
SQL> EXECUTE :ret_cd := 0

PL/SQL procedure successfully completed.

SQL> EXECUTE :ret_msg := ''

PL/SQL procedure successfully completed.

SQL> PRINT ret_cd

RET_CD
----------
0

SQL> PRINT ret_msg

RET_MSG
----------------------------------------------------------------------------------------------------


SQL> EXECUTE t1('D',:ret_cd,:ret_msg);

PL/SQL procedure successfully completed.

SQL> PRINT ret_cd

RET_CD
----------
-6502

SQL> PRINT ret_msg

RET_MSG
----------------------------------------------------------------------------------------------------
ORA-06502: PL/SQL: numeric or value error: character string buffer too small
ORA-06512: at "OPS$XXXXX.T1", line 13


SQL> l 13
13* v_environment_in := 'XX';
SQL>

*/

--PROCEDURE t1 with raise_application_error
create or replace PROCEDURE t1(
p_environment_in IN VARCHAR2,
p_return_val_inout IN OUT NOCOPY NUMBER,
p_return_msg_inout IN OUT NOCOPY VARCHAR2 )
IS
v_environment_in varchar2(1);
BEGIN

--Process Logic
--process_logic;

--Test scenario for fail
v_environment_in := 'XX';

EXCEPTION
WHEN OTHERS THEN
p_return_val_inout := SQLCODE;
p_return_msg_inout := DBMS_UTILITY.FORMAT_ERROR_STACK()||DBMS_UTILITY.FORMAT_ERROR_BACKTRACE();
dbms_output.put_line(chr(10) || '-' || p_return_val_inout || '-' || p_return_msg_inout );

--raise;
raise_application_error(-20000, 'Error!');

END t1;
/
show errors


/*
--2nd run
SQL> VARIABLE ret_cd NUMBER
SQL> VARIABLE ret_msg VARCHAR2(4000)
SQL> EXECUTE :ret_cd := 0

PL/SQL procedure successfully completed.

SQL> EXECUTE :ret_msg := ''

PL/SQL procedure successfully completed.

SQL> PRINT ret_cd

RET_CD
----------
0

SQL> PRINT ret_msg

RET_MSG
----------------------------------------------------------------------------------------------------


SQL> EXECUTE t1('D',:ret_cd,:ret_msg);
BEGIN t1('D',:ret_cd,:ret_msg); END;

*
ERROR at line 1:
ORA-20000: Error!
ORA-06512: at "OPS$XXXXX.T1", line 22
ORA-06512: at line 1


SQL> PRINT ret_cd

RET_CD
----------
0

SQL> PRINT ret_msg

RET_MSG
----------------------------------------------------------------------------------------------------


SQL> l 22
22* raise_application_error(-20000, 'Error!');
SQL>

*/



--end sample code




and Tom said...

... Procedure "t1" should return the error code and message so that pro*c program can take action.
...

no, you don't. Return codes are so 1980's.

code like this:


WHEN OTHERS THEN 
  do_whatever_error_logging_you_want_here_using_an_autonomous_transaction;
  raise_application_error( my_error_code,  my_error_message ); 
END t1; 
/ 



Pro*C will get the error code as the SQLCODE and the error message in the sql error message variable in the SQLCA.


Lose the parameters
  p_return_val_inout IN OUT NOCOPY NUMBER, 
  p_return_msg_inout IN OUT NOCOPY VARCHAR2 ) 

you don't need nor want them.


Your user written Pro*c code will find it hard to ignore a database error message and far far too easy to ignore a return code.

Rating

  (6 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

A reader, January 06, 2011 - 3:29 pm UTC

Dear Sir,

Thanks for the advice.

This gives me the error details of the raise_application_error line and not actual line of error (v_environment_in := 'XX'; --here).

Also, since we are using DBMS_UTILITY.FORMAT_ERROR_STACK() & DBMS_UTILITY.FORMAT_ERROR_BACKTRACE to get the complete error details, we will check to see if Pro*C gives the same.

Thanks and keep up the good work.
Rakesh


--start sample code

/*
create or replace PROCEDURE t2(
p_environment_in IN VARCHAR2
)
IS
v_environment_in varchar2(1);
BEGIN

--Process Logic
--process_logic;

--Test scenario for fail
v_environment_in := 'XX';

EXCEPTION
WHEN OTHERS THEN
raise_application_error(-20000, 'Error!');

END t2;
/
show errors





$ cat catch_err.pc
#include <stdio.h>
#include <sqlca.h>

extern void sqlglm( char*, size_t*, size_t* );
extern void sqlgls( char*, size_t*, size_t* );
void exit(int);

void sql_error(msg)
char *msg;
{
size_t clen, fc;
char cbuf[128];

clen = sizeof (cbuf);
sqlgls((char *)cbuf, (size_t *)&clen, (size_t *)&fc);

printf("\n%s\n", msg);
printf("Error Statement obtained using sqlgls is--\n%s\n", cbuf);

sqlglm((char *)cbuf, (size_t *) &clen, (size_t *) &clen);
/*
sqlglm((char *)cbuf, 128 , 128 );
*/


printf("Full Error Message obtained using sqlglm is ========");
printf ("\n%.*s\n", clen, cbuf);

EXEC SQL WHENEVER SQLERROR CONTINUE;
EXEC SQL ROLLBACK WORK RELEASE;
exit(0);
}

void main()
{
EXEC SQL BEGIN DECLARE SECTION;
char *uid = "ops$xxxxx/xxxxx";
EXEC SQL END DECLARE SECTION;

EXEC SQL WHENEVER SQLERROR do sql_error("Oracle error");

EXEC SQL CONNECT :uid;

EXEC SQL EXECUTE
begin
ops$xxxxx.t2('D');
end;
END-EXEC;

EXEC SQL WHENEVER SQLERROR CONTINUE;
EXEC SQL ROLLBACK WORK RELEASE;
exit(1);
}
$



$ ./catch_err

Oracle error
Error Statement obtained using sqlgls is--
begin ops$xxxxx . t2 ( 'D' ) ; end ; À
Full Error Message obtained using sqlglm is ========
ORA-20000: Error!
ORA-06512: at "OPS$xxxxx.T2", line 16
ORA-06512: at line 1

$



*/



--end sample code

Rakesh, January 08, 2011 - 9:56 pm UTC

Dear Sir,

Can you provide your views on the following mentioned above?

"
This gives me the error details of the raise_application_error line and not actual line of error
(v_environment_in := 'XX'; --here).

"
Thanks and keep up the good work.
Rakesh

Tom Kyte
January 10, 2011 - 7:43 am UTC

put whatever you want into your_error_message.

you were sending:


p_return_msg_inout := DBMS_UTILITY.FORMAT_ERROR_STACK()||DBMS_UTILITY.FORMAT_ERROR_BACKTRACE();


before, use that as your input to raise application error.

Excellent! Opinion requested for running a procedure from UNIX shell script

A reader, January 10, 2011 - 10:25 pm UTC

Dear Sir, 

Thanks for the excellent advice.

This gives me the correct error details of the actual line of error (v_environment_in := 'XX'; --here).

/*
--start sample code


--Database version is Oracle 11g R1
--PROCEDURE t2 with raise_application_error

create or replace PROCEDURE t2(
    p_environment_in   IN  VARCHAR2
)
IS
v_environment_in varchar2(1);
v_err_code       number;
v_err_message    varchar2(4000);
BEGIN
  
  --Process Logic
  --process_logic;

  --Test scenario for fail
  v_environment_in := 'XX';

EXCEPTION
WHEN OTHERS THEN

  v_err_code    := SQLCODE;
  v_err_message := DBMS_UTILITY.FORMAT_ERROR_STACK()||DBMS_UTILITY.FORMAT_ERROR_BACKTRACE();  
  dbms_output.put_line(chr(10) || '-' || v_err_code || '-' || v_err_message );
  raise_application_error(-20000, 'Error! '||v_err_message);

END t2;
/
show errors


--run details of the Pro*C program

$ cd $BE
$ ./catch_err

Oracle error
Error Statement obtained using sqlgls is--
begin ops$xxxxx . t2 ( 'D' ) ; end ;                                                                                            À
Full Error Message obtained using sqlglm is ========
ORA-20000: Error! ORA-06502: PL/SQL: numeric or value error: character string buffer too small
ORA-06512: at "OPS$xxxxx.T2", line 14
ORA-06512: at "OPS$xxxxx.T2", line 22
ORA-06512: at line 1

$

--Verifying the error details
SQL> l 14
 14*   v_environment_in := 'XX';
SQL> l 22
 22*   raise_application_error(-20000, 'Error! '||v_err_message);
SQL> 


--end sample code
*/


On similar line in addition to the original question, we need to run the procedure from the UNIX shell script.


Can you please provide your opinion on the following approach to do error handling?




/*
--start sample code
--Database version is Oracle 11g R1
--PROCEDURE t2 with raise_application_error

create or replace PROCEDURE t2(
    p_environment_in   IN  VARCHAR2
)
IS
v_environment_in varchar2(1);
v_err_code       number;
v_err_message    varchar2(4000);
BEGIN
  
  --Process Logic
  --process_logic;

  --Test scenario for fail
  v_environment_in := 'XX';

EXCEPTION
WHEN OTHERS THEN

  v_err_code    := SQLCODE;
  v_err_message := DBMS_UTILITY.FORMAT_ERROR_STACK()||DBMS_UTILITY.FORMAT_ERROR_BACKTRACE();  

  dbms_output.put_line( 'STRT');
  dbms_output.put_line(SQLCODE);
  dbms_output.put_line( 'STRTSTCK');
  uxpsubm.p_Log_Errors ( 'Error_Stack...' || Chr(10) || DBMS_UTILITY.FORMAT_ERROR_STACK() );
  dbms_output.put_line( 'STRTBTRC');
  uxpsubm.p_Log_Errors ( 'Error_Backtrace...' || Chr(10) || DBMS_UTILITY.FORMAT_ERROR_BACKTRACE() );
  dbms_output.put_line( 'ENDD');

  raise_application_error(-20000, 'Error! '||v_err_message);

END t2;
/
show errors


--UNIX shell script calls a sql program catch_error.sql for running a procedure
$ cat catch_error.sh
#!/bin/ksh
#

if [ ${UIPW:-0} = 0 ];
then
    UIPW="/"
    fi
    a='D'
    echo $a
    export a
    #
    #       run sqlplus to print the report
    #
    echo "Starting sql script...."
    MSG=`sqlplus -s $UIPW @catch_error.sql $a | col -b |rmnl | sed -n '/STRT/,/ENDD/p'`
    echo $?
    echo "Completed sql script...."
    R_CD=`echo $MSG| awk '{print $2}'`
    echo $R_CD
    echo $MSG
    if [[ $R_CD -ne 0 ]]
    then
        echo "`date` Failure"
        exit 1
    else
        echo "`date` Success"
    fi

    exit $?

--SQL program catch_error.sql for running a procedure
$ cat catch_error.sql
--set timing on verify on termout on feedback on timing on
set heading off
whenever sqlerror exit 1
whenever oserror exit 2

set verify off

column a format a10


variable a varchar2(10);

begin

  select '&&1' into :a from dual;

end;
/

set serveroutput on size 100000

EXECUTE ops$xxxxx.t2(:a);


pause
exit

$


--run details of the UNIX Shell script program


$ sh catch_error.sh
D
Starting sql script....
0
Completed sql script....
-6502
STRT -6502 STRTSTCK Error_Stack... ORA-06502: PL/SQL: numeric or value error: character string buffer too small STRTBTRC Error_Backtrace... ORA-06512: at "OPS$xxxxx.T2", line 14 ENDD
Mon Jan 10 22:49:33 EST 2011 Failure


--end sample code
*/

Thanks
Rakesh

Tom Kyte
January 11, 2011 - 5:58 am UTC

that works - using sqlplus like that - however you'd be much much much better off using a scripting language that actually lets you connect to the database and do stuff - perl or whatnot.

dbms_utility.FORMAT_ERROR_BACKTRACE in pro*C instead of in SQL

DVN, April 02, 2013 - 3:38 am UTC

Hi Tom,

We have a common Pro*C module which prints the error based on sqlca.sqlcode value returned from a procedure/package.We have around 5000 oracle packages/procedures in our schema and 1 million lines of PL/SQL code.All DMLs in procedures/packages don't have dbms_utility.FORMAT_ERROR_BACKTRACE or DBMS_UTILITY.FORMAT_ERROR_STACK so many times we had spent long time which query or procedure failed when too many rows/STR bind value/no data found error came etc

Is there an easy way using the pro*C common module or using V$<tables> to get the last error details that gives me which procedure at what line failed.Also it will be very good if it gives me the values that are passed to the procedure/package?

Please let me know your valuable inputs
Tom Kyte
April 22, 2013 - 1:13 pm UTC

I'm confused - the default error message you get would have the complete call stack in it.


unless someone catches an exception and makes it "non-exceptional".

give me a for example - tiny procedure that fails, tiny proc that runs it and cannot find out where it failed just by recording the error message it gets back?

Example of Scenario Asked for Trace Back

DVN, July 02, 2013 - 7:53 pm UTC

Hi Tom, Thanks for your reply and I just saw it..my problem is like this..we use pro*c so first we call a stored procedure from one Pro*C function and get the data set..we use that data set to get additional data from other transaction related tables and/or to update the other tables.For doing this we again call another set of stored procedure. During this process if we get any error then our application calls a generic function which writes error into the log file but it doesn't have the base data that got using first function to store in some table.

We have more than 100 of Pro*C Servers which use this generic function to print the error in the log and error can come from any of the procedure so what I need is tracing back mechanism which gives me the set of queries that got ran and parameters passed to it so that I can get the required transaction reference along with error in a application log table.

Right now I can write only error but it will not contain transaction reference as it is not supplied to the generic Pro*C error function

Is there a way to get this transaction data that is used?
Tom Kyte
July 16, 2013 - 12:18 pm UTC

you would have to track that. once you've made the call to the database and it was successful - we lose all of that metadata, we don't care about it - it isn't anything we'd store.

YOU will have to log this level of detail if this is what you want.

Used Global Variables

DVN, November 06, 2013 - 2:53 am UTC

Hi Tom..actually resolved the issue using C Global Variables.Fortunately we have common workflow module and error logging used by all components so was able to use Global Variables and able to capture error into database

More to Explore

PL/SQL demos

Check out more PL/SQL tutorials on our LiveSQL tool.

PL/SQL docs

PL/SQL reference manual from the Oracle documentation library