Home | Previous Page | Next Page   Database Access > Executing User-Defined Routines > Calling UDRs with the Fastpath Interface >

Executing the Routine

The mi_routine_exec( ) function can execute any UDR that is registered in the open database. Therefore, any UDR that you can use in an SQL statement, you can directly execute with mi_routine_exec( ). Once you obtain a function descriptor for a registered UDR or cast function, the mi_routine_exec( ) function sends it to the routine manager for execution. You can use the function descriptor in repeated calls to mi_routine_exec( ). This executed routine runs in the virtual processor (VP) that was defined for it. This VP is not necessarily the VP in which the calling UDR runs.

Important:
You cannot use the Fastpath interface to execute iterator functions or SPL functions with the WITH RESUME keywords in their RETURN statement. However, to simulate iterator functionality, you can call the same UDR repeatedly, passing it the same MI_FPARAM structure. Each invocation of the UDR can return one value. For more information on iterator functions, see Writing an Iterator Function.

The mi_routine_exec( ) function takes the following steps:

  1. Passes the argument values in its argument list to the UDR
  2. Returns any return value from the user-defined function to the calling module

Passing in Argument Values

When you call mi_routine_exec( ), you provide argument values for the UDR that Fastpath is to execute. Keep the following points in mind when you create the argument list for mi_routine_exec( ):

The mi_routine_exec( ) function dispatches the UDR through the routine manager. Therefore, each UDR gets a call to mi_call( ) because the routine manager checks for sufficient space before it executes the UDR.

Receiving the Return Value

When the mi_routine_exec( ) function executes a user-defined function, it returns as an MI_DATUM value the return value of the user-defined function that it has executed. This MI_DATUM value contains a value appropriate for the passing mechanism for the data type. Most data types are to be passed by reference from the UDR. For a list of data types that can be passed by value, see Table 13.

You can then use C casting to convert this MI_DATUM value to the appropriate data type. You can obtain information about the return type (such as its data type) from the MI_FPARAM structure.

If the user-defined function returned an SQL NULL value, mi_routine_exec( ) returns a NULL-valued pointer and sets the status argument to MI_OK.

For examples of how to receive a UDR return value from mi_routine_exec( ), see Sample mi_routine_exec( ) Calls. For more information on MI_DATUM values, see The MI_DATUM Data Type.

Sample mi_routine_exec( ) Calls

The code fragment in Figure 54 uses the mi_routine_exec( ) function to execute the numeric_func( ) routine that has INTEGER parameters.

Figure 54. Executing the numeric_func( ) Function with INTEGER Arguments
MI_CONNECTION *conn;
MI_FUNC_DESC *fdesc;
MI_DATUM ret_val;
mi_integer int_ret;
mi_integer error;
...
/* fdesc obtains from code in Figure 51 */
ret_val = mi_routine_exec(conn, fdesc, &error, 1, 2);
if ( ret_val == NULL )
   {
   if ( error == MI_OK )
      /* numeric_func( ) returned an SQL NULL value */
      ...
   else /* error in mi_routine_exec( ) */
      {
      mi_db_error_raise(NULL, MI_EXCEPTION, 
         "mi_routine_exec( ) failed");
      return MI_ERROR;
      }
   }
else /* cast MI_DATUM in ret_val to INTEGER */
   {
   int_ret = (mi_integer) MI_DATUM;
   ...

In Figure 54, the mi_routine_exec( ) function uses the fdesc function descriptor that the mi_routine_get( ) function obtained for the numeric_func( ) function that accepts INTEGER arguments and returns an INTEGER value (Figure 51). The last two arguments to mi_routine_exec( ) are the integer argument values for numeric_func( ). The ret_val variable is an MI_DATUM value for the mi_integer data type that the numeric_func( ) function returns.

Figure 54 also tests the return status of mi_routine_exec( ) to check for the return value from numeric_func( ). If the return value (ret_val) is a NULL-valued pointer, the code then determines which of the following results this NULL value indicates:

Finally, the code fragment in Figure 54 casts the MI_DATUM value that mi_routine_exec( ) has returned to an mi_integer value. The MI_DATUM structure contains the actual return value because the routine manager can pass integer values by value (they can fit into an MI_DATUM structure).

Suppose the call to mi_routine_get( ) had been calling the version of numeric_func( ) that accepted a FLOAT argument and returned a FLOAT value, as follows:

fdesc = mi_routine_get(conn, 0, 
   "function numeric_func(float)");

The call to mi_routine_exec( ) to execute this version of numeric_func( ) would require that the argument and the return value be passed by reference, because the FLOAT data type cannot be stored directly in an MI_DATUM structure.

Figure 55 shows a sample call to mi_routine_exec( ) to execute the numeric_func(FLOAT) user-defined function. The argument list to mi_routine_exec( ) passes the FLOAT value by reference and the returned FLOAT value is returned by reference.

Figure 55. Executing the numeric_func( ) Function with INTEGER Arguments
MI_CONNECTION *conn;
MI_FUNC_DESC *fdesc;
MI_DATUM ret_val;
mi_double_precision double_arg1, double_arg2, double_ret;
mi_integer error;
...
fdesc = mi_routine_get(conn, 0, "function numeric_func(float)");
ret_val = mi_routine_exec(conn, fdesc, &error, &double_arg1,
      &double_arg2);
if ( ret_val == NULL )
   {
   if ( error == MI_OK )
      /* numeric_func( ) returned an SQL NULL value */
      ...
   else /* error in mi_routine_exec( ) */
      {
      mi_db_error_raise(NULL, MI_EXCEPTION, 
         "mi_routine_exec( ) failed");
      return MI_ERROR;
      }
   }
else /* cast MI_DATUM in ret_val to FLOAT */
   {
   double_ret = (mi_double_precision *)MI_DATUM;
   ...

The following call to mi_routine_exec( ) executes the DECIMAL-to-mytype cast function for which Figure 52 obtained the function descriptor, fdesc2:

ret_val = mi_routine_exec(conn, fdesc2, &error, dec_val);
mytype_val = (mytype *)ret_val;

The dec_val argument is an mi_decimal variable that contains the DECIMAL source data type to cast to the mytype target data type with the dec_to_mt( ) cast function. The ret_val variable is an MI_DATUM value of the mytype data type that contains the casted DECIMAL value.

Executing a Built-in Cast Function

The execution of a built-in cast (system or MI_SYSTEM_CAST) function is different from the execution of a user-defined cast function. A user-defined cast function takes only one argument, the data value to be converted, but a built-in cast function takes three arguments:

The FPARAM information for the value argument comes from the source type for the cast. The length and precision fields are both integers so their precision is always 0. Below is an example of a system cast.

                                    if (cast_status == MI_SYSTEM_CAST)
                       {
                              mi_integer     precision
=
mi_type_precision(tdTarget);
                              mi_integer        length =
mi_type_maxlength(tdTarget);
                  mi_integer scale = mi_type_scale(tdTarget);
                                          fd_fparam =
mi_fparam_get(conn,fd);
                      ....
                     .....
                     mi_setretlen(fd_fparam,0,length);
                     mi_setretprec(fd_fparam,0,precision);
                     mi_setretscale(fd_fparam,0,scale);
                    retValue = mi_routine_exec(conn, fd, &error,
 value,
NULL,NULL);

Reusing a Function Descriptor

Looking up a UDR and creating its function descriptor can be an expensive operation. If you have multiple UDR invocations calling the same UDR through Fastpath, you can cache the function descriptor to make it available for reuse within either of the following scopes:

When you reuse the function descriptor, you save the overhead of looking up the UDR and allocating a function descriptor each time you reexecute the same UDR.

Function Descriptors Within an SQL Command

When you pass a public connection descriptor to one of the Fastpath look-up functions (see Table 65), the look-up function allocates the function descriptor with a PER_COMMAND memory duration. Therefore, the function descriptor remains allocated for the duration of the SQL command that invokes the UDR.

To cache the function descriptor, save its address in the MI_FPARAM structure of the UDR. The MI_FPARAM structure has a PER_COMMAND duration. Therefore, storing the function-descriptor address in the user state of MI_FPARAM guarantees that all UDR invocations within the SQL command can access it. You can also cache the connection description in MI_FPARAM. When the SQL command completes, the function descriptor, the connection descriptor, and the MI_FPARAM structure are deallocated.

The following code fragment for the non_optimal( ) UDR opens a connection and obtains the function descriptor for the user-defined function equal( ) for each invocation of non_optimal( ):

mi_integer non_optimal(arg1, arg2, fparam)
   mi_integer arg1, arg2;
   MI_FPARAM *fparam;
{
   MI_CONNECTION *conn=NULL;
   MI_FUNC_DESC *func_desc=NULL;
   MI_DATUM func_result;
   mi_integer func_error;
   mi_string *func_sig="equal(int, int)";

/* Open a connection */
if ( (conn = mi_open(NULL, NULL, NULL)) 
      == (MI_CONNECTION *)NULL )
   {
   mi_db_error_raise(NULL, MI_EXCEPTION, 
      "mi_open( ) call failed");
   return MI_ERROR;
   }

/* Get the function descriptor for equal( ) */
if ( (func_desc = mi_routine_get(conn, 0, func_sig)) 
      == (MI_FUNC_DESC *)NULL )
   {
   mi_db_error_raise(NULL, MI_EXCEPTION, 
      "mi_routine_get( ) call failed");
   return MI_ERROR;
   }

/* Execute the equal( ) user-defined function */
func_result = mi_routine_exec(conn, func_desc, &func_error,
   arg1, arg2);
if ( func_error == MI_ERROR )
   {
   mi_db_error_raise(NULL, MI_EXCEPTION,
      "mi_routine_exec( ) call failed");
   return MI_ERROR;
   }
...

When you cache the function descriptor in the user state of MI_FPARAM, you do not have to repeatedly call mi_routine_get( ) for each invocation of the equal( ) function. Instead, you can call mi_routine_get( ) only in the first invocation of equal( ). The following code fragment opens a connection and gets the function descriptor in the first invocation of the user-defined function equal( ) only. It caches these descriptors into the user_state structure, whose address it stores in the user-state pointer of MI_FPARAM, for subsequent invocations of equal( ):

typedef struct user_state
   {
   MI_CONNECTION *conn;
   MI_FUNC_DESC *func_desc;
   };

mi_integer optimal(arg1, arg2, fparam)
   mi_integer arg1, arg2;
   MI_FPARAM *fparam;
{
   MI_CONNECTION *local_conn = NULL;
   MI_FUNC_DESC *local_fdesc = NULL;
   mi_string *func_sig="equal(int, int)";
   user_state *func_state;

   /* Obtain the connection descriptor from MI_FPARAM */
   func_state = (user_state *)mi_fp_funcstate(fparam);
   if ( func_state == NULL ) /* first time UDR is called */
      {
      /* Allocate a user_state structure */
      func_state = 
         (user_state *)mi_dalloc(sizeof(user_state),
         PER_COMMAND);

      /* Obtain the connection descriptor */
      if ( (local_conn = mi_open(NULL, NULL, NULL))
            == (MI_CONNECTION *)NULL )
         {
         mi_db_error_raise(NULL, MI_EXCEPTION, 
            "mi_open( ) call failed");
         return MI_ERROR;
         }

      /* Obtain the function descriptor for equal( ) */
      if ( (local_fdesc = 
            mi_routine_get(local_conn, 0, func_sig)) 
            == (MI_FUNC_DESC *)NULL )
         {
         mi_db_error_raise(NULL, MI_EXCEPTION,
            "mi_routine_get( ) call failed");
         return MI_ERROR;
         }

      /* Cache the connection descriptor and function
       * descriptor in MI_FPARAM
       */
      func_state->conn = local_conn;
      func_state->func_desc = local_fdesc;

      /* Save the user state in MI_FPARAM */
      mi_fp_setfuncstate(fparam, (void *)func_state);
      }

   /* Execute the equal( ) user-defined function */
   func_result = mi_routine_exec(func_state->conn,
      func_state->func_desc, &func_error, arg1, arg2);
   if ( func_error == MI_ERROR )
      {
      mi_db_error_raise(NULL, MI_EXCEPTION,
         "mi_routine_exec( ) call failed");
      return MI_ERROR;
      }
...
Function Descriptors Within a Session (Server)

When you pass a session-duration connection descriptor to one of the Fastpath look-up functions (see Table 65), the look-up function allocates a function descriptor with a PER_SESSION memory duration, called a session-duration function descriptor. The session-duration function descriptor remains allocated until the session ends. In this case, all UDRs within the session can access the cached function descriptor.

Warning:
The session-duration connection descriptor and session-duration function descriptor are advanced features of the DataBlade API. They can adversely affect your UDR if you use them incorrectly. In addition, session-duration function descriptors require named memory to store the pointers to function descriptors. Without named memory, UDRs cannot share these pointers. Named memory is also an advanced feature of the DataBlade API. Use a session-duration function descriptor only when a regular function descriptor cannot perform the task you need done.

The following table summarizes the memory operations for a session-duration function descriptor in a C UDR.

Memory Duration Memory Operation Function Name
PER_SESSION Constructor mi_cast_get( ), mi_func_desc_by_typeid( ), mi_routine_get( ), mi_routine_get_by_typeid( ), mi_td_cast_get( )

When passed a session-duration connection descriptor instead of a public connection descriptor

Destructor mi_routine_end( )

When the session ends

Caching a Session-Duration Function Descriptor

To cache the function descriptor, save its address in PER_SESSION named memory. This location guarantees that all UDRs within the session can access the function descriptor. A UDR can create a session-duration function descriptor and cache it as follows:

The following code fragment uses the mi_routine_get( ) function to obtain a session-duration function descriptor for the func1( ) UDR:

MI_CONNECTION *sess_conn;
MI_FUNC_DESC **fdesc;
mi_integer status;

/* Obtain a session-duration connection descriptor */
sess_conn = mi_get_session_connection( );

/* Allocate a PER_SESSION named-memory block named
 * 'funcptr_blk'. Assign address of this block to fdesc
 * pointer.
 */
if ( (status = mi_named_alloc((sizeof)(MI_FUNC_DESC *),
      "funcptr_blk", PER_SESSION, (void **)&fdesc)) 
      != MI_OK )
   {
   /* Unable to allocate named-memory block. Handle error */
   }

/* Obtain the session-duration function descriptor for
 * func1( ). Store function descriptor in named-memory block.
 */
if ( (*fdesc = mi_routine_get(sess_conn, 0, 
      "function func1(int, char)") == (MI_FUNC_DESC *)NULL)
   {
   /* Unable to obtain function descriptor for func1( ) UDR. 
    * Handle error.
    */
   }

The preceding code fragment uses the mi_get_session_connection( ) function to obtain the session-duration connection descriptor, sess_conn. It then passes sess_conn to the mi_routine_get( ) function to obtain a session-duration function descriptor for func1( ). The address of this session-duration function descriptor is stored in a PER_SESSION named-memory block named funcptr_blk. All UDRs that need to access the func1( ) function descriptor can obtain it from funcptr_blk. For more information, see Reusing the Session-Duration Function Descriptor.

Important:
Do not store the address of a session-duration function descriptor in the MI_FPARAM structure of the UDR. Neither should you allocate PER_SESSION memory with mi_dalloc( ) and store the address of this memory in MI_FPARAM. Both these methods cause the address of the session-duration function descriptor to be lost because the MI_FPARAM structure gets freed when the UDR instance completes. However, you can optimize the named-memory look-up by caching the address of the named-memory block in MI_FPARAM. This method requires only one call to mi_named_get( ) for each instance of the UDR. The first UDR invocation that needs the information must allocate the named-memory block and populate the named memory.
Reusing the Session-Duration Function Descriptor

To reuse the session-duration function descriptor in another UDR, you:

The following code fragment shows how other UDRs can access the fdesc function descriptor until the session ends:

MI_CONNECTION *sess_conn;
MI_FUNC_DESC **fdesc;
mi_integer status, error;
mi_integer i = 55;
mi_char ch = 'c';
MI_DATUM *value;

/* Obtain a public and session-duration connection 
 * descriptor 
 */
conn = mi_open(NULL, NULL, NULL);
sess_conn = mi_get_session_connection( );

/* Obtain the address of the 'funcptr_blk' named-memory block,
 * which contains the session-duration function descriptor
 */
if ( (status = mi_named_get("funcptr_blk", PER_SESSION, 
      (void **)&fdesc)) != MI_OK )
   {
   /* Handle error */
   ...
   }

/* Execute the UDR associated with the 'fdesc' 
 * session-duration function descriptor
 */
value = mi_routine_exec(conn, *fdesc, &error, i, c);

Once you obtain the session-duration function descriptor, the mi_routine_exec( ) function can execute the associated UDR. You can specify either a public connection descriptor or a session-duration connection descriptor to mi_routine_exec( ). If the UDR that the function descriptor references is dropped while the session-duration function descriptor is still in use, the database server generates an error when you try to execute the routine.

Deallocating a Session-Duration Function Descriptor

The session-duration function descriptor has a PER_SESSION memory duration. The function descriptor remains active until either of the following events occurs:

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