Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OCIErrorGet and multiple error handling for OCI_ERROR

For OCIErrorGet() it is documented that it might return multiple errors, which I use to extract with the following method for OCI_SUCCESS_WITH_INFO, but currently not for OCI_ERROR:

void check_error( sword status )
{
    switch( status ) {
    case OCI_SUCCESS:
        break;

    case OCI_SUCCESS_WITH_INFO:
        {
           ub4 recordno = 1;
           while( status != OCI_NO_DATA ) {
              sb4 errcode = 0;
              text errbuf[ 1024 ];
              status = ::OCIErrorGet( m_err, recordno, (text*)NULL, &errcode, errbuf, sizeof( errbuf ), OCI_HTYPE_ERROR );
              if( status == OCI_SUCCESS ) {
                 std::cout << "oracle info: " << (const char*)errbuf << std::endl;
              }
              else {
                 assert( status == OCI_NO_DATA );
              }
              ++recordno;
           }
        }
        break;

    case OCI_ERROR:
        {
           sb4 errcode = 0;
           text errbuf[ 1024 ];
           // note here: no check of returned value from OCIErrorCode(), no loop!
           ::OCIErrorGet( m_err, 1, (text*)NULL, &errcode, errbuf, sizeof( errbuf ), OCI_HTYPE_ERROR );
           throw my_oracle_error( errcode, (const char*)errbuf );
        }
        break;
    default:
        throw "something else";
    }
}

(of course the real code is a bit different, but the important part is shown above).

In case of OCI_ERROR (marked with a comment in the above code), my questions are:

  • Do I need a similar loop or does Oracle guarantee/document that only a single error can be returned in that case?
  • Do I need to check the returned value from OCIErrorGet() in that case?
  • If multiple errors could be returned, which errcode should I use for the exception that I throw?

Preferably the answers should link to the documentation from Oracle.

like image 699
Daniel Frey Avatar asked Sep 11 '13 06:09

Daniel Frey


1 Answers

Oracle guarantees that only a single error will be returned per call of OCIErrorGet() (note the singular):

Returns an error message in the buffer provided and an Oracle Database error code.
...
Multiple diagnostic records can be retrieved by calling OCIErrorGet() repeatedly until there are no more records (OCI_NO_DATA is returned). OCIErrorGet() returns at most a single diagnostic record.

Whether you need a similar loop or nor depends on what (PL/)SQL code you're calling. Simple SQL statements will generally only return one error code; for e.g.:

SQL> select 1/0 from dual;
select 1/0 from dual
        *
ERROR at line 1:
ORA-01476: divisor is equal to zero

However, if PL/SQL is involved more may be returned:

SQL> begin
  2     raise_application_error(-20000, 'error');
  3  end;
  4  /
begin
*
ERROR at line 1:
ORA-20000: error
ORA-06512: at line 2

Here the actual error that you're interested in would be ORA-20000. Oracle's exception propagation works from internal block to external block so, assuming you're not dealing with compilation errors, the original cause for the error will be the first exception. If you do catch and re-raise an exception this changes. The example Oracle gives in the docs is:

SQL> begin
  2    dbms_output.put_line(1/0);  -- handled
  3  exception when zero_divide then
  4    dbms_output.put_line(1/0 || ' is undefined');  -- not handled
  5  end;
  6  /
begin
*
ERROR at line 1:
ORA-01476: divisor is equal to zero
ORA-06512: at line 4
ORA-01476: divisor is equal to zero

DBMS_OUTPUT.PUT_LINE is a procedure hence the same exception appearing twice; note that it's still the first exception that you're interested in.

To answer your questions:

You don't need a similar loop; you only should use one if you want to get more than one error code.

If multiple errors are returned you should probably throw the first error code because of the method in which Oracle propagates exceptions; this is essentially a judgement call you need to make though. It's unclear from the docs whether OCIErrorGet() returns the most recent or the earliest exception first; you might need to throw the last exception.

like image 103
Ben Avatar answered Oct 15 '22 19:10

Ben