Home | Previous Page | Next Page   Database Access > Handling Exceptions and Events > State-Transition Events >

Providing State-Transition Handling

The database server throws a state-transition event when it changes its processing state.

To handle a transition in the processing state
  1. Write a state-transition callback.

    Within a state-transition callback, use the mi_transition_type( ) function on the transition descriptor to determine the state-transition type (begin, normal end, or abort end) that caused the event. The processing required is typically different for each transition type.

  2. Register the state-transition callback in the DataBlade API module that needs the state-transition handling.

    To provide required DataBlade processing at a state-transition point, your DataBlade API module must register state-transition callbacks with the mi_register_callback( ) function.

The way that your DataBlade API module handles a state-transition event depends on whether the DataBlade API module is a C UDR or a client LIBMI application.

State Transitions in a C UDR (Server)

In a C UDR, the following state-transition events might occur:

For these state-transition events, the mi_transition_type( ) function returns a state-transaction type of normal end (MI_NORMAL_END) or abort end (MI_ABORT_END). In a UDR, state-transition callbacks are always called at the end of a transaction (normal or aborted), never at the beginning.

Managing Memory Allocations

If your code allocates memory for user data that the callback function needs, this memory must have a duration long enough to persist until the execution of the callback. Otherwise, the callback cannot access the user data. This user data might include information that the callback function needs to handle the event or to notify users of the cause of the event.

The following table shows the memory durations associated with callback and event types.

Callback or Event Type Memory Duration to Use
End-of-statement PER_STMT_EXEC
End-of-transaction PER_TRANSACTION
End-of-session PER_SESSION
MI_EVENT_POST_XACT PER_TRANSACTION
MI_EVENT_SAVEPOINT PER_TRANSACTION
MI_EVENT_COMMIT_ABORT PER_TRANSACTION

At the end of the memory duration associated with the callback, the database server deallocates the memory as part of its final cleanup.

For more information on memory durations, see Choosing the Memory Duration.

Managing the Transaction

The transaction system of the database server only guarantees transaction semantics on all objects that are internal to the database. However, transactions might also include operations on external objects. A UDR might perform such operations, such as creating a temporary file or sending a message.

For transactions that consist of operations on both internal and external objects, you can use one of the following types of callbacks to commit or to undo (if possible) the operations on the external objects, based on the transaction status:

To enable these callbacks to roll back a transaction, the DataBlade API allows end-of-statement and end-of-transaction callbacks to raise an exception and register their own exception callbacks.

The database server calls an end-of-statement or end-of-transaction callback before it attempts to commit the transaction. When called before a commit, these callbacks receive a transition descriptor that has a transition state of MI_NORMAL_END. However, if either of these callbacks encounters an error during its execution, you probably do not want to allow the transaction to commit.

To cause an event to be aborted or rolled back, you can raise an exception from a state-transition callback by calling the mi_db_error_raise( ) function. When a state-transition callback raises an exception, the DataBlade API takes the following actions:

  1. Terminates further processing of the end-of-statement or end-of-transaction callback
  2. Terminates the current transaction and changes the transition state from MI_NORMAL_END (commit) to MI_ABORT_END (rollback)
  3. Invokes any end-of-statement or end-of-transaction callbacks again, this time with the MI_ABORT_END transition state
  4. Invokes any exception callback that the end-of-statement or end-of-transaction callback has registered to handle the exception

An end-of-transaction callback executes within a C UDR when the MI_EVENT_END_XACT event occurs. The following code implements an end-of-transaction callback named endxact_callback( ), which inserts a row into a database table, tran_state, to indicate the state of the current transaction:

MI_CALLBACK_STATUS MI_PROC_CALLBACK
endxact_callback(event_type, conn, event_data, user_data)
   MI_EVENT_TYPE event_type;
   MI_CONNECTION *conn;
   void *event_data;
   void *user_data;
{
   MI_CONNECTION *cb_conn;
   cb_conn = mi_open(NULL, NULL, NULL);
   (void) mi_register_callback(cb_conn, MI_Exception,
      eox_excpt_callback( ), NULL, NULL);
   if ( event_type == MI_EVENT_END_XACT )
      {
      mi_integer change_type;

      change_type = mi_transition(event_data);
      switch ( change_type )
         {
         case MI_NORMAL_END:
            ret = mi_exec(cb_conn,"insert \
               into tran_state \
               values(\"Transaction Committed.\")\;",
               0);
            if ( ret == MI_ERROR )
               mi_db_error_raise(cb_conn, MI_EXCEPTION,
                  "Unable to save transaction \
                  state: Commit.");
            break;
         case MI_ABORT_END:
            ret = mi_exec(cb_conn,"insert \
               into tran_state \
               values(\"Transaction Aborted.\")\;",
               0);
            if ( ret == MI_ERROR )
               make_log_entry(log_file, 
                  "Unable to save transaction state: \
                  Roll Back.");
            break;

         default:
            mi_exec(cb_conn,"insert into tran_state \
               values(\"Unhandled Transaction \
                  Event.\")\;", 0);
            break;
         }
      }
   else
      {
      mi_exec(cb_conn, "insert into tran_state \
         values(\"Unhandled Event.\");", 0);
      break;
      }

   mi_close(cb_conn);
   return MI_CB_CONTINUE;
}

MI_CALLBACK_STATUS MI_PROC_CALLBACK
eox_excpt_callback(event_type, conn, event_data, user_data)
   MI_EVENT_TYPE event_type;
   MI_CONNECTION *conn;
   void *event_data;
   void *user_data;
{
   ... Perform clean-up tasks ...
   return MI_CB_CONTINUE;
}

The database server invokes the endxact_callback( ) callback with a transition state of MI_NORMAL_END just before it commits the transaction. The endxact_callback( ) function executes as follows:

  1. It executes the MI_NORMAL_END case in the switch statement.
  2. If mi_exec( ) in the MI_NORMAL_END case fails, mi_exec( ) returns MI_ERROR, as does any DataBlade API function except mi_db_error_raise( ).
  3. The condition in the if statement evaluates to TRUE and mi_db_error_raise( ) executes, which raises an exception.
  4. The DataBlade API invokes the registered exception callback, eox_excpt_callback( ).
  5. Because eox_excpt_callback( ) returns a status of MI_CB_CONTINUE, the database server aborts the transaction.

    If eox_excpt_callback( ) had instead returned MI_CB_EXC_HANDLED, execution would continue at the next statement of the endxact_callback( ) callback:

    mi_close(cb_conn);

    The endxact_callback( ) function would then return MI_CB_CONTINUE, which would cause the database server to commit current transaction.

  6. Before the database server aborts the transaction, it invokes endxact_callback( ) with a transition state of MI_ABORT_END. This second invocation of endxact_callback( ) proceeds as follows:
    1. It executes the MI_ABORT_END case in the switch statement.
    2. It calls mi_exec( ) to execute the INSERT statement.
    3. If mi_exec( ) fails, the database server invokes the registered exception callback eox_excpt_callback( ). The mi_exec( ) function does not return MI_ERROR. Because eox_excpt_callback( ) returns MI_CB_CONTINUE, control does not return to endxact_callback( ).

If endxact_callback( ) had not registered its own exception callback, then when mi_exec( ) in the MI_NORMAL_END case fails, execution would proceed as follows:

  1. The mi_exec( ) function returns MI_ERROR.
  2. The condition in the if statement evaluates to TRUE and mi_db_error_raise( ) executes, which raises an exception.
  3. When mi_db_error_raise( ) throws an exception, the database server aborts the transaction.
  4. Before the database server aborts the transaction, it invokes endxact_callback( ) with a transition state of MI_ABORT_END. This second invocation of endxact_callback( ) proceeds as follows:
    1. Execute the MI_ABORT_END case in the switch statement.
    2. Call mi_exec( ) to execute the INSERT statement.
    3. If mi_exec( ) fails, it returns MI_ERROR.
    4. The condition in the if statement evaluates to TRUE and the user-defined make_log_entry( ) function makes a log entry in a text file.

Unlike some other types of callbacks, an end-of-transaction callback can register callbacks of its own. The preceding end-of-transaction callback registers the excpt_callback( ) to handle database server exceptions that might arise from the INSERT statements that mi_exec( ) executes. For a sample implementation of the excpt_callback( ) callback, see Figure 62.

State Transitions in a Client LIBMI Application

In a client LIBMI application, the only state-transition event that might occur is MI_Xact_State_Change. The MI_Xact_State_Change event occurs only within a client application.

In a client LIBMI application, a state-change callback is invoked for the following state-transition types:

Home | [ Top of Page | Previous Page | Next Page | Contents | Index ]