Home | Previous Page | Next Page   Database Access > Handling Exceptions and Events > Database Server Exceptions >

Returning Error Information to the Caller

The fourth argument of a callback function is a pointer to callback user data. The user data is a C variable or structure that contains application-specific information that a callback can use. You pass the user data to a callback when you register the callback with the mi_register_callback( ) function. The fourth argument of mi_register_callback( ) provides a pointer to the user data (see Figure 57).

One of the most common uses of user data is a user-defined error structure. When a callback handles exceptions, DataBlade API functions return either MI_ERROR or NULL on failure. This information is often not specific enough for the calling code to determine the cause of the error. You can create a user-defined error structure to pass more specific error information back to the calling code, as follows:

  1. The calling code defines and allocates a user-defined error structure.
  2. The callback function populates the user-defined structure with error information.

Defining a User-Defined Error Structure

The calling code can define a user-defined error structure to hold error information. This user-defined structure can be a single C variable or a structure with several pieces of error information. It can contain as much error information as the calling code requires.

Figure 63 shows a sample user-defined error structure named DB_ERROR_BUF.

Figure 63. A Sample User-Defined Error Structure
#define MSG_SIZE 256
typedef struct error_buf_
{
   mi_integer error_type;
   mi_integer error_level;
   mi_string sqlstate[6];
   mi_string error_msg[MSG_SIZE];
} DB_ERROR_BUF;

The DB_ERROR_BUF structure holds the following error information.

Error Field
Description
error_type
The event type for the event

For exception handling, this event type should always be MI_Exception.

error_level
The exception level (or error level) for the event

For exception handling, this field holds the exception level: MI_MESSAGE or MI_EXCEPTION.

sqlstate
The value of the SQLSTATE variable, which indicates the cause of the exception
error_msg
The text of the error message, up to a limit of MSG_SIZE bytes

The calling code must allocate memory for the user-defined error structure. You can use the DataBlade API memory-allocation functions such as mi_alloc( ) and mi_dalloc( ). When you allocate the user-defined error structure, you must associate a memory duration with this structure that you declare that is appropriate to its usage. For example, if the user-defined error structure is to be associated with a registered callback, you must allocate the structure with a memory duration of PER_STMT_EXEC so that this memory is still allocated when the callback executes.

The following mi_dalloc( ) call allocates a DB_ERROR_BUF buffer with a PER_STMT_EXEC memory duration:

mi_dalloc(sizeof(DB_ERROR_BUF), PER_STMT_EXEC);

Implementing the Callback

The calling code can use one of the following ways to make a user-defined error structure available to a callback:

Associating with a Callback

To associate a user-defined error structure with the registered callback, specify the address of the structure as the fourth argument of mi_register_callback( ) function. The call to mi_register_callback( ) initializes the fourth parameter of the exception callback with a pointer to the user-defined structure. For more information, see Figure 57.

Server Only

The following func1( ) UDR registers a callback named excpt_callback2( ), which puts error information in the DB_ERROR_BUF user-defined structure (which Figure 63 defines):

void func1(flag)
   mi_integer flag;
{
   MI_CONNECTION *conn;
   MI_CALLBACK_HANDLE *cback_hndl;
   DB_ERROR_BUF error;

   /* Initialize information in the error buffer */
   error.sqlstate[0] = '\0';
   strcpy(error.error_msg, 
      "func3: initialized error buffer.");

   /* Obtain connection descriptor */
   conn = mi_open(NULL, NULL, NULL);
   if ( conn == NULL )
      mi_db_error_raise(NULL, MI_EXCEPTION, 
         "func1: mi_open( ) call failed!");

   /* Register the exception callback */
   cback_hndl = mi_register_callback(conn, MI_Exception,
         excpt_callback2, (void *)&error), NULL):

   /* Execute SQL statement */
      mi_exec(conn, "bad SQL statement", MI_QUERY_NORMAL);

   /* Execution does not reach this point if the
    * excpt_callback2( ) callback returns MI_CB_CONTINUE.
    */

The call to mi_register_callback( ) specifies the address of the user-defined structure as its fourth argument. This structure is, in turn, passed in as the third argument of the excpt_callback2( ) callback (see Figure 57). The following code implements the excpt_callback2( ) callback function:

MI_CALLBACK_STATUS
excpt_callback2(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;

   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_callback2 called with wrong event type ",
          "%d", event_type);
      }
   else /* event_type is MI_Exception */
      {
      mi_error_sql_state((MI_ERROR_DESC *)event_info,
         user_info->sqlstate, 6);
      mi_errmsg((MI_ERROR_DESC *)event_info,
         user_info->error_msg, MSG_SIZE-1);
      }

   return MI_CB_EXC_HANDLED;
}

Important:
Make sure that you allocate the user-defined error structure with a memory duration that is compatible with the callback that uses it. Memory durations longer than PER_COMMAND exist for use with end-of-statement, end-of-transaction, and end-of-session callbacks. However, these longer memory durations should be used only in special cases. For more information, see Choosing the Memory Duration.
End of Server Only
Client Only

The following code fragment from a client LIBMI application registers a callback named clntexcpt_callback2( ), which puts error information in the DB_ERROR_BUF user-defined structure (which Figure 63 defines).

int main (argc, argv)
   int argc;
   char **argv;
{
   MI_CONNECTION *conn = NULL;
   char stmt[300];
   MI_CALLBACK_HANDLE callback_hdnl;
   DB_ERROR_BUF error_buff;
   mi_integer ret;

   /* Open a connection to the database server */
   conn = mi_open(argv[1], NULL, NULL);
   if ( conn == NULL )
      /* do something appropriate */

   /* Register the exception callback, with the user-defined
    * error structure as the fourth argument to
    * mi_register_callback( )
    */
   callback_hndl = mi_register_callback(conn, MI_Exception,
      (MI_VOID *)clntexcpt_callback2;
      (MI_VOID *)&error_buff, NULL);
   if ( callback_hndl == NULL )
      /* do something appropriate */
   ...
   /* Execute the SQL statement that 'stmt' contains */
   ret = send_statement(conn, stmt);
   /* If an exception occurred during the execution of the
    * SQL statement, the exception callback initialized the
    * 'error_buff' structure. Obtain error information from
    * 'error_buff'.
    */
   if ( ret == MI_ERROR )
      {
      if ( error_buff.error_type == MI_Exception )
         {
         if ( error_buf.error_level == MI_EXCEPTION )
            {
            fprintf(stderr, "MI_Exception: level = %d",
               error_buff.error_level);
            fprintf(stderr, "SQLSTATE='%s'\n",
               error_buff.sqlstate);
            fprintf(stderr, "message = '%s'\n",
               error_buff.error_msg);
            /* discontinue processing */
            }
         else /* error_level is MI_WARNING */
            {
            sprintf(warn_msg, "WARNING: %s\n", 
               error_buf.error_msg);
            display_msg(warn_msg);
            }
         }
      /* do something appropriate */
   ...
}

The call to mi_register_callback( ) specifies the address of the user-defined structure as its fourth argument. This structure is, in turn, passed in as the fourth argument of the clntexcpt_callback2( ) callback. The following code implements the clntexcpt_callback2( ) callback function.

void clntexcpt_callback2(event_type, conn, event_info,
      error_info)
   MI_EVENT_TYPE event_type;
   MI_CONNECTION *conn;
   void *event_info;
   void *error_info; /* user-defined error buffer here */
{
   DB_ERROR_BUF *error_buf = (DB_ERROR_BUF *)error_info;

   /* Fill user-defined structure with error information */
   error_buf->error_type = event_type;
   if ( event_type == MI_Exception )
      {
      error_buf->error_level = mi_error_level(event_info);
      mi_error_sql_state(event_info, error_buf->sqlstate, 6);
      mi_errmsg(event_info, error_buf->error_msg, MSG_SIZE-1);
      }
   else
      fprintf(stderr,
         "Warning! clntexcpt_callback( ) fired for event ",
         "%d", event_type);
   return;
}

The clntexcpt_callback( ) function is an example of an exception callback for a client LIBMI application. This callback returns void because the client LIBMI does not interpret the MI_CALLBACK_STATUS return value, as does the database server for UDRs.

End of Client Only
Associating with the Connection

To associate a user-defined error structure with the connection, you:

Important:
You can associate a user-defined error structure with a connection only if a valid connection exists and this connection does not change between the point at which the callback is registered and the point at which the exception event occurs. If you cannot guarantee that these two conditions exist, associate the user-defined error structure with the registered callback (page Associating with a Callback).
Client Only

The following code fragment from a client LIBMI application binds the DB_ERROR_BUF user-defined structure (Figure 63) to a connection:

int main (argc, argv)
   int argc;
   char **argv;
{
   MI_CONNECTION *conn = NULL;
   MI_CALLBACK_HANDLE *cback_hndl;
   char query[300];
   mi_integer ret;
   DB_ERROR_BUF error_buff;

   conn = mi_open(argv[1], NULL, NULL);
   if ( conn == NULL )
      /* do something appropriate */
      ...
   cback_hndl = mi_register_callback(conn, MI_Exception,
      (MI_VOID)clntexcpt_callback2, NULL, NULL);
   ret = mi_set_connection_user_data(conn,
      (MI_VOID)&error_buff);
   if ( ret == MI_ERROR )
      /* do something appropriate */
      ...
   ret = send_command(conn, query);
   if ( ret == MI_ERROR)
      {
      fprintf(stderr, "MI_Exception: level = %d",
         error_buff.error_level);
      fprintf(stderr, "SQLSTATE='%s'\n",
         error_buff.sqlstate);
      fprintf(stderr, "message = '%s'\n",
         error_buff.error_msg);
      }
   /* do something appropriate */
   ...
}

The call to mi_register_callback( ) does not specify the address of the user-defined structure as its fourth argument because this structure is associated with the connection. The following code implements the clntexcpt_callback( ) callback function, which uses the mi_get_connection_user_data( ) function to obtain the user-defined structure:

void clntexcpt_callback2(event_type, conn, event_info,
      user_data)
   MI_EVENT_TYPE event_type;
   MI_CONNECTION *conn; /* user-defined error buffer here */
   void *event_info;
   void *user_data;
{
   DB_ERROR_BUF *error_buf

   mi_get_connection_user_data(conn, (void **)&error_buf)
   error_buf->error_type = event_type;
   if ( event_type == MI_Exception )
      {
      error_buf->error_level = mi_error_level(event_info);
      mi_error_sql_state(event_info, error_buf->sqlstate, 6);
      mi_errmsg(event_info, error_buf->error_msg, MSG_SIZE-1);
      }
   else
      fprintf(stderr,
         "Warning! clntexcpt_callback2( ) fired for event "
         "%d", event_type);
   return;
}

In the preceding code fragment, the italicized portion is the only difference between this client LIBMI application and the one that registers a user-defined variable with the callback (in Associating with a Callback).

End of Client Only
Home | [ Top of Page | Previous Page | Next Page | Contents | Index ]