Handling multiple exceptions

The database server can generate multiple exceptions for a single SQL statement.

A single statement might generate multiple exceptions when any of the following conditions have occurred:
  • Multiple warnings occur.
  • Multiple details are associated with a single error occurrence.

    For example, a DROP TABLE statement might set both the SQLCODE value and the ISAM error value. Similarly, nested UDRs might generate errors at many levels.

The database server normally calls a registered exception callback once for each exception message. If a single error causes multiple exceptions, you must use the following DataBlade® API functions in the callback to process multiple messages in a single invocation.
DataBlade API function Description
mi_error_desc_next() Obtains the next error descriptor from the current exception list

The list of exceptions that the current statement generates is called the current exception list.

mi_error_desc_finish() Completes the processing of the current exception list

A callback can use this function to prevent its being called again for any more exceptions currently associated with the current statement.

A callback is not called again for any messages that have already been processed. The database server presents exceptions from highest message level to lowest message level. Therefore, a UDR or SQL message occurs first, followed by any ISAM message.

The smart-large-object functions (mi_lo_*) raise an MI_Exception event if they encounter a database server exception. However, the smart-large-object error is the second message that the database server returns. Therefore, an exception callback needs to include the following steps to obtain an exception from an mi_lo_* function:
  1. Call mi_error_sqlcode() to get the high-level SQLCODE value.
  2. Call mi_error_desc_next() to get the next error descriptor.
  3. Call mi_error_sqlcode() again to get the detailed smart-large-object error (and ISAM error code).
The following callback function, excpt_callback3(), is a modified version of the excp_callback2() callback that handles multiple exceptions:
MI_CALLBACK_STATUS excpt_callback3(event_type, conn,
      event_info, user_data)
   MI_EVENT_TYPE event_type;
   MI_CONNECTION *conn;
   void *event_info;
   void *user_data; /* user-defined error buffer gets
                    * passed here
                    */
{
   DB_ERROR_BUF *user_info;
   mi_integer state_type;
   mi_string *msg;

   mi_integer i=0;
   /* Pointer to multiple error messages */
   MI_ERROR_DESC *err_desc=NULL;

   user_info = ((DB_ERROR_BUF *)user_data);

   user_info->error_type = event_type;
   if ( event_type != MI_Exception )
      {
      user_info->sqlstate[0] = '\0';
      sprintf(user_info->error_msg,
         "excpt_callback3 called with wrong event type ",
          "%d", event_type);

      /* Send trace message for default trace class */
      DPRINTF("__myErrors__", 1, ("<<<<>>>> mesg=%s",
         user_info->error_msg));
      return MI_CB_CONTINUE;
      }

   err_desc = (MI_ERROR_DESC *)event_info;
   i++;
   mi_error_sql_state(err_desc, user_info->sqlcode, 6);
   mi_errmsg(err_desc, user_info->error_msg, MSG_SIZE-1);

   DPRINTF("__myErrors__", 1, 
      ("<<<<>>>> mesg %d: sqlcode=%s, mesg=%s", i,
      user_info->sqlcode, user_info->error_msg));

/* Overwrites previous error. Another approach would be to
 * allocate enough 'user_info' space to store all errors
 */
   if ( (err_desc=
         mi_error_desc_next((MI_ERROR_DESC *)event_info)) 
         != NULL )
      {
      i++;
      mi_error_sql_state(err_desc, user_info->sqlcode, 6);
      mi_errmsg(err_desc, user_info->error_msg, 
         MSG_SIZE-1);

      DPRINTF("__myErrors__", 1, 
         ("<<<<>>>> mesg %d: sqlcode=%s, mesg=%s", i,
         user_info->sqlcode, user_info->error_msg));
      }
   return MI_CB_CONTINUE;
}

This callback also uses the DPRINTF macro to send trace messages to an output file.