Increase stack space
Use the mi_call() function to increase stack space for recursive UDRs.
- 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.
#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:
To
complete the factorial, the function needs to complete the following
calculation:
|
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.
CREATE FUNCTION factorial (INTEGER)
RETURNS INTEGER
EXTERNAL NAME
"$INFORMIXDIR/extend/misc/fact_ius.bld"
LANGUAGE C;
EXECUTE FUNCTION factorial(5);