informix
Informix DataBlade API Programmer's Manual
Handling Exceptions and Events

Handling Database Server Exceptions

When the database server or a user-defined routine raises a database server exception, the DataBlade API invokes any callbacks that are registered for the exception. This section provides information about exception handling in DataBlade API modules:

What Is a Database Server Exception?

A database server exception is an unexpected condition that occurs within the database server. A database server exception can occur in any of the following tasks:

When the database server encounters a database server exception, it raises the MI_Exception event.

Warnings and Errors

The MI_Exception event indicates which of the following status conditions has caused the database server exception:

The following list describes the most common DataBlade API library errors:

Potential exceptions other than these types of common invalid arguments are mentioned in the Return Values section of the individual function descriptions in Chapter 15, DataBlade API Function Descriptions.

An error descriptor for an MI_Exception event indicates the status condition of the event with one of the following exception levels.

Status Condition Exception Level Description
Warning MI_MESSAGE Raised when the database server generates a warning or an informational message. The database server passes a warning back to the client application; it is up to the client to display the warning message.
Runtime error
(failure)
MI_EXCEPTION Raised when the database server generates a runtime error.

The mi_error_level() function returns the exception level from an error descriptor for the MI_Exception event.

Status Variables

To identify the particular cause of an exception, the database server sets the following status variables:

SQLSTATE Status Value

SQLSTATE is a five-character string that the database server sets after it executes each DataBlade API function. The value of SQLSTATE indicates the status of the function execution.

The SQLSTATE status variable is compliant with ANSI and X/Open standards.

This five-character code consists of a two-character class code and a three-character subclass code. In Figure 10-6, "IX" is the class code and "000" is the subclass code. The SQLSTATE value "IX000" indicates that an Informix-specific error has occurred.

Figure 10-6
The Structure of the SQLSTATE Code with the Value "IX000"

SQLSTATE can contain only digits and capital letters. The class code is unique but the subclass code is not. The meaning of the subclass code depends on the associated class code. The initial character of the class code indicates the source of the exception, as Figure 10-7 summarizes.

Figure 10-7
Initial SQLSTATE Class-Code Values

Initial Class-Code Value Source of Exception Code Notes
0 to 4
A to H
X/Open and ANSI/ISO The associated subclass codes also begin in the range 0 to 4 or A to H.
5 to 9 Defined by the implementation Subclass codes are also defined by the implementation.
I to Z Dynamic Server,
a DataBlade module,
a C UDR,
a client LIBMI application
Any of the Informix-specific error messages (those that the X/Open or ANSI/ISO reserved range does not support) have an initial class-code value of "I" (SQLSTATE value of "IX000"). If a UDR returns an error message that this routine has defined, the initial class-code value is "U" (SQLSTATE value of "U0001" ). Other SQLSTATE class-code values can be defined by the implementation.

After the database server executes a DataBlade API function, it sets SQLSTATE to indicate one of the status conditions, as Figure 10-8 shows.

Figure 10-8
Status Conditions In SQLSTATE

Status
Condition
SQLSTATE Value
Class Code Subclass Code
Success "00" "000"
Success, but no rows found "02" "000"
Success, but warnings generated "01" For ANSI and X/Open warnings:
For Informix-specific warnings:
For literal warnings that DataBlade API modules raise: "U01" For custom warnings that DataBlade API modules define: other subclass values, as defined in the syserrors system catalog table
Failure, runtime error generated For ANSI and X/Open errors: > "02" For Informix-specific errors: "IX" For literal errors that DataBlade API modules raise: "U0" For custom errors that DataBlade API modules define: other class codes, as defined in the syserrors system catalog table Error-specific value

For a list of reserved ANSI and X/Open values for SQLSTATE, see the description of the GET DIAGNOSTICS statement in the Informix Guide to SQL: Syntax. For more information on DataBlade API literal exceptions ("U0001" and "01U01"), see Passing Literal Messages. For more information on DataBlade API custom exceptions, see Raising Custom Messages.

Identifying Warnings With SQLSTATE

When the database server executes a DataBlade API function successfully, but encounters a warning condition, it:

Identifying Runtime Errors With SQLSTATE

When an SQL statement results in a runtime error, the database server:

The actual class and subclass codes of SQLSTATE identify the particular error. For Informix-specific errors (SQLSTATE is "IX000"), you can also check the value of the SQLCODE variable to identify the an error.

Tip: The database server sets SQLSTATE to "02000" (class = "02") when a SELECT or FETCH statement encounters NOT FOUND (or END OF DATA). However, the NOT FOUND condition does not cause a database server exception. Therefore, you do not use SQLSTATE to detect this condition from within a callback of a DataBlade API module. Instead, your DataBlade API module can check for the MI_NO_MORE_RESULTS return code from the mi_get_result() function. For more information, see Retrieving Query Data. SQLCODE Status Value

Each SQLSTATE value also has an associated Informix-specific status code. The database server saves this Informix-specific status code in the SQLCODE status variable. The SQLCODE variable is an integer that indicates whether the SQL statement succeeded or failed.

When the database server executes an SQL statement, the database server automatically updates the SQLCODE variable. After an SQL statement executes, the SQLCODE variable can indicate one of the status conditions that Figure 10-9 shows.

Figure 10-9
Status Conditions In SQLCODE

Status Condition SQLCODE Value
Success 0
Success, but no rows found 100
Success, but warnings generated not available directly from SQLCODE
Failure, runtime error generated < 0

Identifying Warnings With SQLCODE

When the database server executes an SQL statement successfully, but encounters a warning condition, it:

To identify warnings, examine the value of SQLSTATE. The SQLCODE value does not indicate the cause of a warning. For more information, see Identifying Warnings With SQLSTATE.

Identifying Runtime Errors With SQLCODE

When an SQL statement results in a runtime error, the database server:

The actual number in SQLCODE identifies the particular Informix runtime error. The Informix Error Messages manual lists these Informix-specific error codes and their corrective actions. You can also use the command-line finderr utility to obtain information about an Informix error. For more information about finderr, see the Informix Error Messages manual.

Tip: The database server sets SQLCODE to 100 when a SELECT or FETCH statement encounters NOT FOUND (or END OF DATA). However, the NOT FOUND condition does not cause a database server exception. Therefore, you do not use SQLCODE to detect this condition from within a callback of a DataBlade API module. Instead, your DataBlade API module can check for the MI_NO_MORE_RESULTS return code from the mi_get_result() function. For more information, see Retrieving Query Data.

Providing Exception Handling

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:

A database server exception only triggers an exception callback 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 user-defined routine or a client LIBMI application.

Exceptions In a C UDR

If the 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.)

For example, in Figure 10-10, the return statement never executes when a runtime error occurs in the SQL statement that mi_exec() executes.

Figure 10-10
A C UDR
With Default Exception Handling

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. The calling module might have a registered callback (or some other method) to handle the exception.

To provide event handling for database server exceptions within a UDR, perform the following tasks:

Handling Errors From DataBlade API Functions

The function descriptions in Chapter 15, DataBlade API Function Descriptions, contain a section entitled 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.

Most DataBlade API functions throw an MI_Exception event when they encounter a database server exception. Therefore, you can get control after a database server exception occurs in a DataBlade API function by registering a callback that handles MI_Exception. 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, if possible, correct the condition. Uncorrected error conditions might lead to worse failures later in processing. For conditions that cannot be corrected, the calling code might want to provide an informational message to notify the user what has occurred. The calling code can use the mi_db_error_raise() function to perform the following tasks:

Registering an Exception Callback

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 10-11 contains the same mi_exec() call as Figure 10-10 on page 10-38. However, this UDR, has_exception_handling(), registers the excpt_callback() function as an exception callback.

Figure 10-11
A C UDR
with Exception Handling

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 10-12 on page 10-44.

For the excpt_callback() function to be invoked on database exceptions that occur on the current connection, you must specify the connection descriptor of the current connection when you register excpt_callback(). Therefore, in Figure 10-11, mi_register_callback() passes the conn connection descriptor, which the mi_open() call has obtained, when it registers excpt_callback().

However, 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. Once you save this descriptor, you can reuse it in subsequent invocations of the UDR.

The following C UDR, 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:

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 called error to the exception callback. For more information, see Returning Error Information to the Caller.

Determining How To Handle the Exception

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 Figure 10-3 on page 10-20 lists.

Handling an Exception in the Callback

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.)

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 10-12 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.

Figure 10-12
A Simple Exception Callback

The excpt_callback() function in Figure 10-12 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 10-11 on page 10-41 defines), the DataBlade API takes the following steps when the mi_exec() function executes:

  1. Execute the excpt_callback() callback when mi_exec() throws an exception.
  2. Return control to the first statement in has_exception_handling() after mi_exec().

As a result, execution of the has_exception_handling() routine returns from the mi_exec() call with a return value of MI_ERROR.

Important: Because excpt_callback() returns MI_CB_EXC_HANDLED, the database server assumes that the MI_Exception event does not require any further handling. However, excpt_callback() does not actually take any exception-handling steps; it contains only a return statement to return an MI_CB_EXC_HANDLED status. In an actual DataBlade API module, you would probably want to add code to excpt_callback() that provides exception handling. Continuing With Exception Handling

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.) The actions of the callback provide supplemental exception handling.

If the excpt_callback() function in Figure 10-12 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:

  1. Execute the excpt_callback() function when the mi_exec() call throws an exception.
  2. Abort the mi_exec() call in has_exception_handling().
  3. Return control back to the calling module that called has_exception_handling().

If has_exception_handling() was a user-defined routine 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 Creating a Callback Function.

Tip: The MI_Exception event might overlap with the MI_EVENT_END_STMT and MI_EVENT_END_XACT events because an exception always causes either a statement or a transaction to abort. Design the corresponding callbacks with this relationship in mind.

Exceptions In a Client LIBMI Application

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:

The function descriptions in Chapter 15, DataBlade API Function Descriptions, contain a section entitled Return Values. This section lists the possible return values for the associated 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:

For more information about how to write a callback function for a client LIBMI application, see Creating a Callback Function.

Returning Error Information to the Caller

The fourth argument of a callback function is a pointer to 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 10-4 on page 10-22).

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 10-13 shows a sample user-defined error structure called DB_ERROR_BUF.

Figure 10-13
A Sample
User-Defined Error Structure

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_STATEMENT, 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_STATEMENT memory duration:

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 10-4 on page 10-22.

The following func1() UDR registers a callback called excpt_callback2(), which puts error information in the DB_ERROR_BUF user-defined structure (which Figure 10-13 on page 10-48 defines):

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 10-4 on page 10-22). The following code implements the excpt_callback2() callback function:

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 do 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 limited cases. For more information, contact your Informix representative.

The following code fragment from a client LIBMI application registers a callback called clntexcpt_callback2() that puts error information in the DB_ERROR_BUF user-defined structure (which Figure 10-13 on page 10-48 defines).

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.

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.

Associating with the Connection

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

The following code fragment from a client LIBMI application binds the DB_ERROR_BUF user-defined structure (Figure 10-13 on page 10-48) to a connection:

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:

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).

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:

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 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:

This callback also uses the DPRINTF macro to send trace messages to an output file. For more information on tracing, see Using Tracing.

Raising an Exception

If a DataBlade API module detects an error, it can use the mi_db_error_raise() function to raise an exception.

In a C UDR, the mi_db_error_raise() function raises an exception to the database server.

In a client LIBMI application, the mi_db_error_raise() function sends the exception over to the database server.

When the mi_db_error_raise() function raises an exception, the database server handles this exception in the same way it would if a database server exception occurred in a DataBlade API function. If the DataBlade API module has registered an exception callback, this call to mi_db_error_raise() invokes the exception callback. If no exception callback has been registered, the DataBlade API uses the default behavior for the handling of exceptions.

Specifying the Connection

The first argument to the mi_db_error_raise() function is a connection descriptor. This connection descriptor can be either a NULL-valued pointer or a pointer to a valid connection. Which values are valid depend on whether the calling module is a UDR or a client LIBMI application.

In a C UDR, you can specify the connection descriptor to mi_db_error_raise() as either of the following values:

Raising an Exception on the Parent Connection

When you specify a NULL-valued connection descriptor to the mi_db_error_raise() function, this function raises the exception against the parent connection, which is the connection on which the C UDR was invoked. This connection might be a client connection or a UDR-owned connection that was passed to mi_exec(), mi_exec_prepared_statement(), or mi_routine_exec().

If the raised exception has an MI_EXCEPTION exception level, the database server aborts both the UDR and the current SQL expression. For both exception levels (MI_EXCEPTION and MI_MESSAGE), the database server passes the event message to the module on the parent connection and returns control to this module. If the UDR needs control of the exception, it must call mi_db_error_raise() with a pointer to the current connection descriptor. For more information, see Raising an Exception on the Current Connection.

The following example shows how the MI_EXCEPTION exception level causes the my_function() UDR to abort when mi_db_error_raise() specifies a NULL-valued pointer as its connection descriptor:

Execution returns to the code that called my_function(). If this code has an exception callback, this callback determines how the exception handling continues.

Raising an Exception on the Current Connection

When you specify a valid connection descriptor to the mi_db_error_raise() function, this function raises the exception against the specified connection. The DataBlade API invokes any callbacks that are registered for the MI_Exception event on this same connection. If a registered callback returns the MI_CB_EXC_HANDLED status, control returns to the UDR. (For more information, see Determining How To Handle the Exception.

When the my_function() routine registers a callback, the callback can catch the exception with an MI_EXCEPTION exception level, as the following example shows:

For a sample implementation of the excpt_callback() function, see Figure 10-12 on page 10-44.

In a client LIBMI application, you must specify a valid connection descriptor to the mi_db_error_raise() function. For an exception callback to be invoked when the mi_db_error_raise() function raises an exception, specify the same connection descriptor as the one on which the callback was registered.

For example, in the following code fragment, the call to mi_db_error_raise() causes the excpt_callback() function to be invoked when an MI_Exception event occurs:

Both mi_register_callback(), which registers the callback for the MI_Exception event, and mi_db_error_raise(), which raises the MI_Exception event, specify conn1 as their connection descriptor.

Specifying the Message

The message that mi_db_error_raise() passes to an exception callback can be either of the following types:

Passing Literal Messages

To raise an exception whose message text you provide, the mi_db_error_raise() function requires the following information:

When you pass the MI_MESSAGE or MI_EXCEPTION message type to the mi_db_error_raise() function, the function raises an MI_Exception event whose error descriptor contains the following information.

Error Descriptor Field Warning Runtime Error
Exception level
(2nd argument)
MI_MESSAGE MI_EXCEPTION
SQLSTATE value "01U01" "U0001"
Message text
(3rd argument)
Specified warning text Specified error text

If any exception callback is registered for the same connection, the DataBlade API sends this error descriptor to the callback when the MI_Exception event is raised.

If the C UDR (or any if its calling routines) has not registered an exception callback when the MI_Exception event is raised, the DataBlade API performs the default exception handling, which depends on the exception level of the exception:

If the client LIBMI application has not registered an exception callback when the MI_Exception event is raised, the client LIBMI calls the system-default callback, which provides the following information:

For more information on the actions of the system-default callback, see Using Default Behavior.

Raising Custom Messages

The mi_db_error_raise() function can raise exceptions with custom messages, which DataBlade modules and user-defined routines can store in the syserrors system catalog table. The syserrors table maps these messages to five-character SQLSTATE values.

To raise an exception whose message text is stored in syserrors, you provide the following information to the mi_db_error_raise() function:

When you pass the MI_SQL message type to the mi_db_error_raise() function, the function raises an MI_Exception event whose error descriptor contains the following information

Error Descriptor Field Warning Runtime Error
Exception level MI_MESSAGE (If the SQLSTATE value has a class code of "01") MI_EXCEPTION (If the SQLSTATE value has a class code of "02" or greater)
SQLSTATE value (3rd argument) Specified warning value: "01Xxx" Specified error value
"XXxxx"
(class code of "02" or greater)
Message text Associated warning text from syserrors table Associated error text from syserrors table
:

If any exception callback is registered for the same connection, the DataBlade API sends this error descriptor to the callback when the MI_Exception event is raised.

For example, assume that the following predefined error message is under an SQLSTATE value of "03I01" in the syserrors table:

The following call to mi_db_error_raise() sends this predefined error message to a registered (and enabled) callback that handles the MI_Exception event:

The exception level for this exception would be MI_EXCEPTION, because any SQLSTATE value whose class code is greater than "02" is considered to represent a runtime error. If no such callback was registered (or enabled), the database server would take its default exception-handling behavior.

If the SQLSTATE value had a class code of "01", mi_db_error_raise() would raise a warning instead of an error. (For more information on SQLSTATE values, see SQLSTATE Status Value.) The following mi_db_error_raise() call raises an MI_Exception event whose exception level is MI_MESSAGE:

When this exception is raised, execution continues at the next line after this call to mi_db_error_raise().

Tip: Both of the preceding mi_db_error_raise() examples specify NULL as the last argument because neither of their syserrors messages contains parameter markers. For more information on parameter markers, see Specifying Parameter Markers.

The following sections provide information on how to perform the following tasks:

Searching For Custom Messages

When the mi_db_error_raise() function initiates a search of the syserrors table, it requests the message in which all components of the locale (language, territory, code set, and optional modifier) are the same in the current processing locale and the locale column of syserrors.

Tip: For more information on the columns of the syserrors system catalog table, see the chapter on the system catalog tables in the "Informix Guide to SQL: Reference." For more information on SQLSTATE, see SQLSTATE Status Value.

For DataBlade API modules that use the default locale, the current processing locale is U.S. English (en_us). (The name of the default code set depends upon the platform you use. Refer to the Informix Guide to GLS Functionality for more information on default code sets.) When the current processing locale is U.S. English, mi_db_error_raise() looks only for messages that use the U.S. English locale.

For DataBlade API modules that use nondefault locales, the current processing locale is one of the following locales:

For more information on the client, database, server, and server-processing locale, see the Informix Guide to GLS Functionality.

A GLS locale name has the format ll_tt.codeset@modf, where ll is the name of the language, tt is the name of the territory, codeset is the name of the code set, and modf is the 4-character name of the optional locale modifier. (For more information on locale names, refer to the Informix Guide to GLS Functionality.) The ll_tt.codeset@modf format is the standard GLS locale name. Locale names can take other forms. However, mi_db_error_raise() first attempts to convert the names of the current processing locale and the syserrors locale into this standard GLS format.

The mi_db_error_raise() function then performs a string comparison on these two locale names. The function attempts to match a value in the locale column of syserrors with the GLS name of the current processing locale as follows:

  1. Convert the current processing locale and syserrors locale names into standard GLS names, if possible.
  2. If mi_db_error_raise() cannot map the current processing locale name to a standard name, it cannot perform the match.

  3. Match the current processing locale name with an entire locale name, if possible.
  4. Locate a row in syserrors whose locale column has a value that matches the full ll_tt.codeset@modf locale name.

  5. Match the current processing locale name with only the language and territory part of a locale name, if possible.
  6. Locate a row in syserrors whose locale column starts with the value ll_tt (only language and territory names match).

  7. Match the current processing locale name with only the language part of a locale name, if possible.
  8. Locate a row in syserrors whose locale column starts with the value ll (only language name matches).

  9. Match the current processing locale name with the default locale (U.S. English), if it is available.
  10. Locate a row in syserrors whose locale column matches the standard GLS name of the default locale.

When mi_db_error_raise() finds a matching locale name for the specified SQLSTATE value, it then verifies that the code set of the locale name from syserrors is compatible with the code set of the current processing locale. A compatible code set is one that is either the same as or can be converted to the current processing code set. If the two code sets are not compatible, mi_db_error_raise() continues to search the syserrors table for rows that match the specified SQLSTATE value. Once mi_db_error_raise() finds a matching row, it obtains the text from the corresponding message column of syserrors.

For example, suppose the current processing locale is the French Canadian locale, fr_ca.8859-1, and you issue the following call to mi_db_error_raise():

The mi_db_error_raise() function performs the following search process to locate entries in syserrors:

  1. Is there a row with the sqlstate column of "08001" and a locale value that matches "fr_ca.8859-1"?
  2. Is there a row with the sqlstate column of "08001" and a locale value that starts with "fr_ca"?
  3. Is there a row with the sqlstate column of "08001" and a locale value that starts with "fr"?
  4. Is there a row with the sqlstate column of "08001" and a locale value that starts with "en_us"?

Suppose mi_db_error_raise() finds a row in syserrors with an sqlstate value of "08001" and a locale of "fr_ca.1250". The function obtains the associated text from the message column of this row if it can find valid code-set conversion files between the ISO8859-1 code set (8859-1) and the Microsoft 1250 code set (1250).

For C UDRs, these code-set conversion files must exist on the server computer.

For client LIBMI applications, these code-set conversion files must exist on the client computer.

Specifying Parameter Markers

The custom message in the syserrors system catalog table can contain parameter markers. These parameter markers are sequences of characters enclosed by a single percent sign on each end (for example, %TOKEN%). A parameter marker is treated as a variable for which the mi_db_error_raise() function can supply a value.

For messages with parameter markers, mi_db_error_raise() can handle a variable-length parameter list, as follows:

The mi_db_error_raise() function requires a parameter pair for each parameter marker in the message. A parameter pair has the following values:

Parameter pairs do not have to be in the same order as the markers in the string.

Important: Terminate the parameter list arguments with a NULL pointer. If a NULL pointer does not terminate the list, the results are unpredictable.

The first member of the parameter pair has the following syntax:

The mi_db_error_raise() function supports following format_character values.

Format Character Meaning
d Integer
f,g,G,e,E Double (by reference)
T Text (mi_lvarchar type, that is, a pointer to mi_lvarchar)
t Length followed by string (two separate parameters)
s Null-terminated C string

For example, suppose that the following message is under an SQLSTATE value of "2AM10" in the syserrors table:

This message contains the following parameter markers: LINE, TOKEN, and CMD. The following call to mi_db_error_raise() assigns the string "selecl" to the TOKEN parameter, 500 to the LINE parameter, and the text of the query to the CMD parameter in the message text:

The string "TOKEN%s" indicates that the value that replaces the parameter marker %TOKEN% in the message string is to be formatted as a string (%s). The next member of the parameter pair, the string "selecl", is the value to format.

This mi_db_error_raise() call sends the following message to an exception callback:

The mi_db_error_raise() function assumes that any message text or message parameter strings that you supply are in the current processing locale.

Adding Custom Messages

You can store custom status codes and their associated messages in the syserrors system catalog table. To add custom messages, follow these steps:

  1. Determine the SQLSTATE code for the message you want to add
  2. Insert a row into the syserrors system catalog table for the new message
Choosing an SQLSTATE Code

The syserrors system catalog table holds custom messages for DataBlade modules and user-defined routines. A unique SQLSTATE value identifies each row in the syserrors system catalog table. Therefore, to store a custom message in syserrors, you assign it an SQLSTATE value.

You must ensure that this SQLSTATE value is unique within syserrors. When you choose an SQLSTATE value, keep the following restrictions in mind:

You can use the following query to determine the current list of SQLSTATE message strings in syserrors:

The locale column is used for the internationalization of error and warning messages. For more information, see Searching For Custom Messages.

Adding New Messages

To create a new message, insert a row into the syserrors system catalog table. By default, all users can view this table, but only users with the DBA privilege can modify it. For more information on columns of the syserrors system catalog table, see the Informix Guide to SQL: Reference.

For example, the following INSERT statement inserts a new message into syserrors whose SQLSTATE value is "03I01":

Enter message text in the language of the target locale, with the characters in the locale code set. By convention, do not include any newline characters in the message. Make sure you also update the locale column of syserrors with the name of the target locale of the message text. For information on locale names, see the Informix Guide to GLS Functionality.

Do not allow any code-set conversion to take place when you insert the message text. If the code sets of the client and database locales differ, temporarily set both the CLIENT_LOCALE and DB_LOCALE environment variables in the client environment to the name of the database locale. This workaround prevents the client application from performing code-set conversion.

If you specify any parameters in the message text, include only ASCII characters in the parameters names. Following this convention means that the parameter name can be the same for all locales. All code sets include the ASCII characters.


Informix DataBlade API Programmer's Manual, Version 9.2
Copyright © 1999, Informix Software, Inc. All rights reserved