By default, the DataBlade API aborts the current statement when the statement generates a database runtime error and continues execution when the statement generates a database warning. (For more information, see Using Default Behavior.)
To override the default exception handling, you must take the following actions:
To handle an MI_Exception event, you can write either of the following types of callback functions:
Within a callback, the DataBlade API function mi_error_level( ) returns the exception level for the database server exception. You can also use mi_error_sql_state( ), mi_error_sqlcode( ), and mi_errmsg( ) to get more details about the database server exception from its error descriptor. For more information, see Accessing an Error Descriptor.
Use the mi_register_callback( ) function to register callback functions. After you register a callback that handles the MI_Exception event, the DataBlade API invokes this callback instead of performing its default exception handling for the event.
A database server exception triggers an exception callback only if the DataBlade API module has registered (and enabled) a callback that handles the MI_Exception event. The way that your DataBlade API module handles a database server exception depends on whether the DataBlade API module is a UDR or a client LIBMI application.
If a C UDR has not registered an exception callback on the current connection, the DataBlade API takes a default action based on the exception level of the MI_Exception event. For more information, see Default Behavior in a C UDR (Server).
For example, in Figure 59, the return statement never executes when a runtime error occurs in the SQL statement that mi_exec( ) executes.
mi_integer no_exception_handling(flag) mi_integer flag; { MI_CONNECTION *conn; conn = mi_open(NULL, NULL, NULL); mi_exec(conn, "bad SQL statement", MI_QUERY_NORMAL); /* Not reached; this function aborts on an exception. */ ... return 1; }
When an exception with an MI_EXCEPTION exception level occurs, the DataBlade API aborts the mi_exec( ) call and the no_exception_handling( ) routine. The database server returns control to the calling module.
To provide event handling for database server exceptions within a UDR, perform the following tasks:
Function descriptions in the IBM Informix: DataBlade API Function Reference contain a section titled "Return Values." This section lists the possible return values for the associated DataBlade API function. However, whether the calling code actually receives a return value depends on whether the DataBlade API function throws an MI_Exception event when it encounters a runtime error. The DataBlade API functions can be divided into the following subsets based on their response to a database server exception:
Most DataBlade API functions throw an MI_Exception event when they encounter a database server exception. For these functions, you can register an exception callback to gain control after a database server exception occurs. Whether the calling code receives a return value from the DataBlade API function depends on how the registered callback handles the MI_Exception event.
The DataBlade API functions that do not throw an MI_Exception event when they encounter a database server exception include the following functions:
When one of the preceding DataBlade API functions encounters an exception, the function does not cause any callbacks registered for the MI_Exception event to be invoked. Instead, these functions return one of the following values to the calling code to indicate failure:
The calling code must check the return value of the DataBlade API function and take the appropriate actions. Uncorrected error conditions might lead to worse failures later in processing. For conditions that cannot be corrected, the calling code can provide an informational message to notify the user about what has occurred. The calling code can use the mi_db_error_raise( ) function to perform the following tasks:
When the database server or a UDR raises a database server exception, the database server invokes any callbacks that handle the MI_Exception event and that the UDR has registered (and enabled) on the current connection. Use the mi_register_callback( ) function to register such a callback. For general information about mi_register_callback( ), see Registering a Callback.
The code fragment in Figure 60 contains the same mi_exec( ) call as Figure 59. However, this UDR, has_exception_handling( ), registers the excpt_callback( ) function as an exception callback.
#include <mi.h> mi_integer has_exception_handling(flag) mi_integer flag; { static MI_CALLBACK_STATUS MI_PROC_CALLBACK excpt_callback( ); MI_CONNECTION *conn = NULL; MI_CALLBACK_HANDLE *cback_hndl; /* Obtain the connection descriptor */ conn = mi_open(NULL, NULL, NULL); /* Register the 'excpt_callback' function as an ** exception callback */ cback_hndl = mi_register_callback(conn, MI_Exception, excpt_callback, NULL, NULL); /* Generate a syntax error that excpt_callback( ) will ** catch */ ret = mi_exec(conn, "bad SQL statement", MI_QUERY_NORMAL); if ( ret == MI_ERROR ) /* handle exception */ ... }
When the database server exception occurs in the SQL statement that mi_exec( ) executes, mi_exec( ) returns MI_ERROR and the if statement handles the exception. For a sample implementation of the excpt_callback( ) callback function, see Figure 62.
For the excpt_callback( ) function to be invoked for database exceptions that occur on the current connection, you must specify the connection descriptor of the current connection when you register excpt_callback( ). In Figure 60, mi_register_callback( ) passes the conn connection descriptor, which the mi_open( ) call has obtained, when it registers excpt_callback( ).
The mi_open( ) function can be resource intensive. If your UDR is likely to be executed many times in the context of a single SQL statement, you might want to cache the connection descriptor from the initial mi_open( ) call in an MI_FPARAM structure. After you save this descriptor, you can reuse it in subsequent invocations of the UDR.
The C UDR in Figure 61, has_exception_handling2( ), saves the connection descriptor in its MI_FPARAM structure the first time it is called and obtains the saved connection descriptor on subsequent calls.
mi_integer has_exception_handling2(flag, fparam) mi_integer flag; MI_FPARAM *fparam; { MI_CONNECTION *conn = NULL; MI_CALLBACK_HANDLE *cback_hndl; DB_ERROR_BUF error; /* Obtain the connection descriptor from MI_FPARAM */ conn = (MI_CONNECTION *)mi_fp_funcstate(fparam); if ( conn == NULL ) /* first time routine is called */ { /* Obtain the connection descriptor */ conn = mi_open(NULL, NULL, NULL); /* Register the 'excpt_callback2( ) function as an ** exception callback on this connection */ cback_hndl = mi_register_callback(conn, MI_Exception, excpt_callback2, (void *)&error, NULL); /* Save connection descriptor in MI_FPARAM */ mi_fp_setfuncstate(fparam, (void *)conn); } ... }
For more information on the MI_FPARAM structure and the user state, see Saving a User State.
In the preceding code fragment, the has_exception_handling2( ) routine registers the excpt_callback2( ) function as its exception callback. This callback uses a user-provided buffer to store event information. As its fourth argument, the mi_register_callback( ) call passes a user-defined buffer named error to the exception callback. For more information, see Returning Error Information to the Caller.
The return value of an exception callback tells the database server how to continue handling a database server exception once the callback completes. An exception callback for a UDR must return one of the MI_CALLBACK_STATUS values that Table 67 lists.
To indicate that the callback function executes instead of the default exception handling, an exception callback function returns the MI_CB_EXC_HANDLED status. This return status tells the DataBlade API that the actions of the callback have completely handled the exception.
An exception callback function returns the MI_CB_EXC_HANDLED status to indicate that the callback has completely handled the exception. That is, the actions of the callback have provided the exception handling. When the DataBlade API receives the MI_CB_EXC_HANDLED return status, it does not perform its default exception handling. It assumes that the callback has executed instead of the default exception handling. (For more information, see Default Behavior in a C UDR (Server).)
When a callback returns MI_CB_EXC_HANDLED, the DataBlade API does not propagate the exception up the calling sequence. Therefore, a client application that has executed an SQL expression that contains a UDR does not receive an error from the execution of the UDR (unless the callback uses a user-provided error buffer). If the SQL expression contains no other exceptions, the client application would have an SQLSTATE value of 00000 (success).
Figure 62 shows the excpt_callback( ) exception callback, which is written to handle the MI_Exception event. It returns MI_CB_EXC_HANDLED to indicate that no further exception handling is required.
static MI_CALLBACK_STATUS MI_PROC_CALLBACK excpt_callback(event_type, conn, event_data, user_data) MI_EVENT_TYPE event_type; MI_CONNECTION *conn; void *event_data; void *user_data; { /* claim to have handled the exception */ return MI_CB_EXC_HANDLED; }
The excpt_callback( ) function in Figure 62 returns MI_CB_EXC_HANDLED, which prevents the DataBlade API from taking any further exception-handling steps, such as invoking other callbacks that handle MI_Exception or aborting the current statement. This callback executes instead of the default exception handling.
For the has_exception_handling( ) routine (which Figure 60 defines), the DataBlade API takes the following steps when the mi_exec( ) function executes:
To indicate that the callback function executes in addition to the default exception handling, an exception callback function returns the MI_CB_CONTINUE return status. This return status tells the DataBlade API that the actions of the callback have not completely handled the exception and that the DataBlade API should continue with its default exception handling. (For more information, see Default Behavior in a C UDR (Server).) The actions of the callback provide supplemental exception handling.
If the excpt_callback( ) function in Figure 62 had returned MI_CB_CONTINUE instead of MI_CB_EXC_HANDLED, the database server would handle the exception in the has_exception_handling( ) routine as follows:
If has_exception_handling( ) was a UDR in an SQL statement, the database server would abort the SQL statement and return control to the client application. The client application would be expected to handle the runtime error for the end user.
However, if has_exception_handling( ) was called by another C UDR that had registered an exception callback, the database server would have executed this callback and continued with the exception handling as the return status of this callback indicated (MI_CB_CONTINUE or MI_CB_EXC_HANDLED). If this callback also returned MI_CB_CONTINUE, the database server would continue up the calling sequence, looking for a registered callback that handled the MI_Exception event. If the database server reached the top-most level in the calling sequence (the UDR within an SQL statement) without locating an exception callback that returned MI_CB_EXC_HANDLED, the database server would abort the UDR and return control to the client application.
For more information on how to write a callback function for a UDR, see Callback Functions.
If the client LIBMI application has not registered a callback that handles the MI_Exception event on the current connection, the client LIBMI calls the system-default callback. (For more information, see Default Behavior in Client LIBMI Applications.)
To provide event handling for database server exceptions within a client LIBMI application, register a callback that handles the MI_Exception event in the client LIBMI application. The DataBlade API invokes any exception callback that the application has registered (and enabled) on the current connection when either of the following actions occurs:
Function descriptions in the IBM Informix: DataBlade API Function Reference contain a section titled "Return Values." This section lists the possible return values for the DataBlade API function. In a C UDR, DataBlade API function calls might or might not return a value, depending on whether the DataBlade API function throws an MI_Exception event when it encounters a runtime error. However, DataBlade API function calls in a client LIBMI application always indicate failure because client-side callbacks always return to the DataBlade API function that generated the error.
On failure, DataBlade API functions return one of the following values to a client LIBMI application:
The client LIBMI application can check for these error values and take any appropriate actions.
The client LIBMI application registers callbacks with the mi_register_callback( ) function. You must provide a valid connection descriptor to mi_register_callback( ) for all valid event types. For more information, see Registering a Callback.
For example, the following mi_register_callback( ) call registers the clntexcpt_callback( ) function to handle MI_Exception events:
int main (argc, arcv) int argc; char *argv; { MI_CONNECTION *client_conn; MI_CALLBACK_HANDLE *client_cback; mi_integer ret; /* Open a connection to the database server */ client_conn = mi_open(argv[1], NULL, NULL); /* Register the exception callback */ client_cback = mi_register_callback(client_conn, MI_Exception, (MI_VOID *)clntexcpt_callback, NULL, NULL); if ( client_cback == NULL ) /* do something appropriate */ ... ret = mi_exec(client_conn, "bad SQL statement", MI_QUERY_NORMAL); if ( ret == MI_ERROR ) /* perform error recovery */ ... }
For more information about how to write a callback function for a client LIBMI application, see Callback Functions.
Home | [ Top of Page | Previous Page | Next Page | Contents | Index ]