Home | Previous Page | Next Page   Database Access > Executing User-Defined Routines > Accessing MI_FPARAM Routine-State Information >

Saving a User State

The routine manager provides information about arguments and return values of a UDR in the MI_FPARAM structure that is associated with a UDR. In addition, you can store the address of private-state information, called user-state information, in a special field of the MI_FPARAM structure.

The database server passes the same MI_FPARAM structure to every invocation of the UDR within the same routine sequence. When your user-state information is part of the MI_FPARAM structure, your UDR can access this information across all the invocations within the same routine sequence. Your routine can use this private area of the MI_FPARAM structure to cache information that preserves its own state.

Tip:
For more information about user state and the routine sequence, see Creating the Routine Sequence.

The MI_FPARAM structure can hold a user-state pointer that points to this private state information. The user-state pointer references a thread-private place holder that allows a UDR to associate a user-defined state information with a routine sequence. Table 63 shows that the DataBlade API provides the following accessor functions to access the user state of a UDR.

Table 63. User-State Information in the MI_FPARAM Structure
User-State Information DataBlade API
Accessor Function
Obtain the user-state pointer from the MI_FPARAM structure of a UDR. mi_fp_funcstate( )
Set the user-state pointer in the MI_FPARAM structure of a UDR. mi_fp_setfuncstate( )

User-state information is useful for a UDR in the following cases:

Warning:
Avoid the use of static and global variables in a UDR. If a UDR uses variables with these scopes, it is an ill-behaved routine. You must execute an ill-behaved UDR in a separate virtual-processor class, called a user-defined VP. For more information, see Using Virtual Processors.
To save user-state information in the first invocation of a UDR
  1. Use the mi_fp_funcstate( ) function to retrieve the user-state pointer from the MI_FPARAM structure.

    Once the UDR has the user-state pointer, it can obtain state information from the private storage area on subsequent invocations.

  2. Check for a NULL-valued user-state pointer.

    On the first invocation of your UDR, the user-state pointer is a NULL-valued pointer. If the user-state pointer is a NULL-valued pointer, allocate a private user-defined buffer or structure for the user-state information.

    When you allocate memory for the user-state information, you must protect this memory so that it is not reclaimed while it is still in use. Define a memory duration of PER_COMMAND for this memory with a DataBlade API memory-allocation function such as mi_dalloc( ) or mi_switch_mem_duration( ). For more information, see Choosing the Memory Duration.

  3. Put the private data in the user-defined buffer or structure to initialize the user state.
  4. If the UDR has just allocated the private user-state buffer, use the mi_fp_setfuncstate( ) function to store the address of this user-defined buffer or structure as a user-state pointer in the MI_FPARAM structure.

    You save the user-state pointer in the MI_FPARAM structure so that later UDR invocations of the routine sequence can access the routine-state information.

To obtain user-state information in subsequent invocations of the UDR
  1. Use the mi_fp_funcstate( ) function to retrieve the user-state pointer from the MI_FPARAM structure.
  2. If the user-state pointer is not a NULL-valued pointer, cast the pointer to the data type of your user-state information.

    Once the user-state pointer points to the correct data type, you can access the user-state information.

The MI_FPARAM structure is associated with the routine sequence. Therefore, for a UDR in a query that executes in parallel, each thread has its own routine sequence and therefore its own MI_FPARAM structure. The first invocation of the routine by each thread would have to perform any initialization. Only UDRs that are declared as parallelizable can be executed in parallel queries. The database server always executes an SQL statement that contains a nonparallelizable UDR serially.

Tip:
By default, the CREATE FUNCTION statement registers a UDR as non-parallelizable. To declare a user-defined function as parallelizable, specify the PARALLELIZABLE routine modifier in the CREATE FUNCTION or CREATE PROCEDURE statement. For more information, see Creating Parallelizable UDRs.

The MI_FPARAM structure has a memory duration of PER_COMMAND. The database server reinitializes the user-state information that mi_fp_funcstate( ) references to NULL at the end of the SQL command (for example, at the end of the subquery execution for each outer row from an outer query).

The code example in Figure 48 implements the rowcount( ) function. This function uses the MI_FPARAM structure to hold a count of the number of rows in a query.

Figure 48. Using the MI_FPARAM Structure to Hold Private-State Information
/* The rowcount( ) function maintains the row count with a variable that
 * is stored as user-state information in the MI_FPARAM structure 
 */
mi_integer rowcount (fparam_ptr)
   MI_FPARAM *fparam_ptr;
{
   mi_integer *count = NULL;

   /* obtain the current user-state pointer from the MI_FPARAM structure */
   count = (mi_integer *)mi_fp_funcstate(fparam_ptr);

   /* if the user-state pointer is NULL, this is the first 
    * invocation of the function
    */
   if ( count == NULL )
      {
      /* allocate memory for the user-state information */
      count = (mi_integer *)mi_dalloc(sizeof(mi_integer), PER_COMMAND);

      /* save user-state pointer in the MI_FPARAM structure */
      mi_fp_setfuncstate(fparam_ptr, (void *)count);

      /* initialize the row counter */
      *count = 0;
      }

   /* increment the row counter */
   (*count)++;
   return (*count);
}

The rowcount( ) function uses the mi_fp_funcstate( ) function to obtain the user-state pointer from the MI_FPARAM structure. If this pointer is NULL, rowcount( ) allocates memory for the count variable and uses the mi_fp_setfuncstate( ) function to store this pointer as the user-state pointer in the MI_FPARAM structure. It uses the mi_dalloc( ) function to allocate this memory with a duration of PER_COMMAND so that the database server does not deallocate the memory after the first invocation of the function.

Tip:
The rowcount( ) function in Figure 48 shows how to use the MI_FPARAM structure to hold private user-state information. This method removes the need for global or static variables, which can make a C UDR ill-behaved. Figure 74 shows the bad_rowcount( ) function, which incorrectly implements a row counter with a static variable.

For the rowcount( ) function to be used in an SQL statement, it must be registered in the database. The following CREATE FUNCTION statement registers the rowcount( ) function for use in SQL statements:

CREATE FUNCTION rowcount( ) RETURNS INTEGER
   EXTERNAL NAME '/usr/lib/db_funcs/count.so(rowcount)'
   LANGUAGE C;

The CREATE FUNCTION statement must omit the MI_FPARAM argument; therefore the registered rowcount( ) function has no arguments. Suppose that the following query uses the rowcount( ) function:

SELECT rowcount( ) from employee;

The query calls the rowcount( ) function for each row in the employee table. Because the rowcount( ) function uses the MI_FPARAM structure to hold its state information (the count variable), each query has its own private count variable. Separate queries do not interfere with one another as they might with static and global variables.

Tip:
You could also implement the rowcount( ) function as a user-defined aggregate function. User-defined aggregates do not use the MI_FPARAM structure to hold state information. For more information, see Writing an Aggregate Function.
Home | [ Top of Page | Previous Page | Next Page | Contents | Index ]