Increase stack space

Use the mi_call() function to increase stack space for recursive UDRs.

The DataBlade® API provides the mi_call() function to dynamically manage stack space. This function performs the following tasks:
  • It checks the amount of unused stack space and allocates additional stack segments if necessary.
  • It executes the specified UDR.

Keep in mind that mi_call() does not know the size of the routine arguments. When mi_call() creates a new stack and copies an argument onto this new stack, the function uses the size of the MI_DATUM data type for the argument. If the data type of the routine argument is larger than MI_DATUM, mi_call() does not copy all the argument bytes.

For example, consider a UDR that includes an mi_double argument.

On UNIX™ or Linux™, an mi_double_precision argument takes the space of two long int values. Therefore, the mi_call() function pushes only half of the argument onto the new stack. Any arguments after the mi_double_precision might get garbled, and the last one might be truncated.

When you design UDRs that require the use of mi_call(), make sure you use the correct passing mechanism for the argument data type. Pass all data types larger than MI_DATUM by reference. Examples of large data types are floating-point types (such as mi_real and mi_double_precision) and data type structures.

The following example illustrates stack-space allocation with mi_call():
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "mi.h"

mi_integer factorial(mi_integer value)
{
   mi_integer callstatus=0,
            retval=0;

   if ( value < 0 )
      return -1;
   else if ( value == 1 || value == 0 )
      return 1;
   else if ( value > 30 )
      mi_db_error_raise(NULL, MI_EXCEPTION,
   "factorial: input value too big for result.");

   callstatus = mi_call(&retval, factorial, 1, value-1);
   switch( callstatus )
      {
      case MI_TOOMANY:
         mi_db_error_raise(NULL, MI_EXCEPTION,
            "factorial: too many parameters.");

      case MI_CONTINUE:
         return (value * factorial(value-1));

      case MI_NOMEM:
         mi_db_error_raise(NULL, MI_EXCEPTION,
            "factorial: not enough memory");

      case MI_DONE:
         /* At the end of the factorial recursion, the
          * function still needs to calculate:
          *      value * factorial(value-1)
          */
         retval *= value;
         break;
      }

   return retval;
}

This code sample implements a factorial function. If the mi_call() function determines that there is sufficient stack space, the code recursively calls the handle_row() function to process the row value. The return value of the mi_call() function indicates whether mi_call() has allocated additional thread-stack memory, as follows.

The mi_call() return value Description Action
MI_CONTINUE The thread stack currently has room for another invocation of factorial(). The mi_call() function does not need to allocate a new thread stack.

The code fragment explicitly calls factorial() on the value-1 value.

MI_DONE The thread stack currently does not have room for another invocation of factorial(). The mi_call() function allocates a new thread stack, copies the arguments onto this stack, and invokes the factorial() function on the value-1 value, returning its value in callstatus.
The code fragment does not need to explicitly call factorial() on the value-1 value. The mi_call() function did the work of invoking the routine; however, mi_call() completed only the following portion of the calculation:
factorial(value-1)
To complete the factorial, the function needs to complete the following calculation:
value * factorial(value-1)

The other mi_call() return values (MI_NOMEM and MI_TOOMANY) indicate error conditions. For these return values, the function uses the mi_db_error_raise() function to raise a database server exception and provide an error message.

The following CREATE FUNCTION registers the factorial() function:
CREATE FUNCTION factorial (INTEGER)
   RETURNS INTEGER
   EXTERNAL NAME
      "$INFORMIXDIR/extend/misc/fact_ius.bld"
   LANGUAGE C;
The following EXECUTE FUNCTION invokes the factorial() function:
EXECUTE FUNCTION factorial(5);