After the query parser has used routine resolution to determine which UDR to invoke, the query executor calls the routine manager to handle the UDR execution. The routine manager performs the following steps to execute the C UDR:
The following sections briefly describe each of these steps. For a general discussion of the routine manager, see the IBM Informix: User-Defined Routines and Data Types Developer's Guide.
When you compile a C UDR, you store its object code in a shared-object file. (For more information, see Compiling a C UDR.) For a UDR to execute, its object code must reside in memory so that a virtual processor (VP) can execute it. The database server uses virtual processors to service client-application SQL requests. A thread is a database server task that a VP schedules for processing.
When the routine manager reaches the first occurrence of the UDR in the SQL statement, the routine manager determines whether its shared-object file is currently loaded into the memory space of the appropriate VP class. If the file is not yet loaded, the routine manager dynamically loads its code and data sections into the data segment for all virtual processors of the VP class. The routine manager obtains the pathname of the shared-object file from the externalname column of the row in the sysprocedures system catalog for the UDR. This loading occurs for both explicit UDR calls and implicit calls (such as operator functions and opaque-type support functions).
Figure 65 shows a schematic representation of what VPs look like after the routine manager loads a shared-object file.
In Figure 65, assume that the func1( ), func2( ) and func3( ) functions are registered as user-defined functions with the CREATE FUNCTION statement and linked into the source1.so UNIX or Linux shared-object file. The client application calls the func1( ) user-defined function within a SELECT statement. The routine manager loads the source1.so file into memory, if this file is not yet loaded. For subsequent references to these UDRs, the routine manager can skip the shared-object load.
The routine manager sends an entry to the message log file about the status of the shared-object load, as follows:
For example, when the routine manager loads the source1.so shared-object file, the message log file would contain messages of the form:
12:28:45 Loading Module </usr/udrs/source1.so> 12:28:45 The C Language Module </usr/udrs/source1.so> loaded
Check the message log file for these messages to ensure that the correct shared-object file is loaded into the virtual processors.
You can monitor the loaded shared-object files with the -g dll option of onstat. This option lists the shared-object files that are currently loaded into the database server.
For information on when the shared-object file is unloaded, see Unloading a Shared-Object File. For information on how to create a shared-object file, see Creating a Shared-Object File. For general information about loading a shared-object file, see the IBM Informix: User-Defined Routines and Data Types Developer's Guide.
A routine sequence is the context in which the UDR executes. Generally, each routine instance (whether implicit or explicit) creates a single, independent routine sequence. For example, suppose you have the following query:
SELECT a_func(x) FROM table1 WHERE a_func(y) > 7;
When this query executes in serial, it contains two routine instances of a_func( ): one in the select list and the second in the WHERE clause. Therefore, this query has two routine sequences.
However, when a query with a parallelizable UDR (one that is registered with the PARALLELIZABLE routine modifier) executes in parallel, each routine instance might have more than one routine sequence. For more information, see Executing the Parallelizable UDR.
For each routine sequence, the routine manager creates a routine-state space, called an MI_FPARAM structure, that contains routine-state information from the routine sequence, including the following information:
The MI_FPARAM structure does not contain the actual argument values.
The routine manager allocates an MI_FPARAM structure when it initializes the routine sequence. This structure persists across all routine invocations in that routine sequence because the MI_FPARAM structure has a memory duration of PER_COMMAND. The routine manager passes an MI_FPARAM structure as the last argument to a UDR. (For more information, see MI_FPARAM Argument.) To obtain routine-state information, a C UDR invocation can access its MI_FPARAM structure. (For more information, see Accessing MI_FPARAM Routine-State Information.)
When the routine manager pushes arguments onto the thread stack, it pushes them as MI_DATUM values. The routine manager takes the following factors into account:
The routine manager pushes MI_DATUM values onto the thread stack before it invokes the routine. The MI_DATUM structures contain the data in its internal database format. The size of the MI_DATUM data type determines whether the routine manager passes a particular argument by value or by reference, as follows:
If the argument value has a data type whose size is greater than the size of the MI_DATUM data type, the routine manager passes the argument by reference because it cannot fit the actual value onto the stack. Instead, the MI_DATUM structure that the routine manager pushes onto the stack contains a pointer to the value. The routine manager allocates the memory for these pass-by-reference arguments with a PER_ROUTINE duration.
If the argument value is a data type whose size is less than or equal to the size of the MI_DATUM data type, the routine manager passes the argument by value because it can fit the actual value onto the stack.
Table 13 lists the data types that the routine manager passes by value. All arguments whose data type is listed in this figure are passed by value unless the argument is an OUT parameter. OUT parameters are never passed by value; they are always passed by reference. The routine manager passes by reference any argument whose data type is not listed in Table 13.
For information on how to code routine parameters, see Defining Routine Parameters. For information on how the routine manager passes return values out of a UDR, see Returning the Value.
C compilers that accept Kernighan-&-Ritchie (K&R) syntax promote all arguments to the int data type when they are passed to a routine. The size of this int data type is native for the computer architecture. ANSI C compilers permit arguments to be shorter than the native computer architecture size of an int. However, the routine manager uses K&R calling conventions when it pushes an MI_DATUM value onto the thread stack.
The routine manager cast promotes arguments with passed-by-value data types whose sizes are smaller than the size of the MI_DATUM data type to the size of MI_DATUM. When you obtain the smaller passed-by-value data type from the MI_DATUM structure, you should reverse the cast promotion to assure that your value is correct. For more information, see MI_DATUM in a C UDR (Server).
If you pass an argument smaller than an MI_DATUM structure, it is recommended that you pass a small "by-value" SQL type as an mi_integer value.
After the routine manager creates a routine sequence and pushes the arguments onto the stack, it invokes the UDR. It then manages the execution of the UDR associated with this routine sequence. The number of times that the UDR is invoked depends on the following factors:
If an argument to the UDR is the SQL NULL value and the UDR does not handle NULL values (it was not registered with the HANDLESNULLS routine modifier), the routine manager does not invoke the UDR.
An iterator function has several iterations. It executes once to initialize the iterations, once for each iteration, and once to release iteration resources. For more information, see Writing an Iterator Function.
A C UDR executes in one or more virtual processors (VPs). VPs are grouped by the kind of task they perform into VP classes. The presence of the CLASS routine modifier in the UDR registration determines in which VP class the UDR executes, as follows:
For more information on how VPs execute C UDRs, see Using Virtual Processors.
For execution of a user-defined function, the routine manager returns any resulting value to the query executor when execution is complete. When the routine manager returns the value from a user-defined function, it passes this value as an MI_DATUM value. As with routine arguments, the passing mechanism that the routine manager uses depends on the size of the return-value data type, as follows:
If the return value has a data type whose size is greater than the size of the MI_DATUM data type, the routine manager passes the return value by reference because it cannot fit the actual value onto the stack. The routine manager allocates the memory for these pass-by-reference return values with a PER_ROUTINE duration.
If the return value is a data type whose size is less than or equal to the size of the MI_DATUM data type, the routine manager passes the return value back by value because it can fit the actual value onto the stack. Table 13 lists the data types that the routine manager passes by value.
The routine manager determines information about the return value (such as whether it is an SQL NULL value) from the MI_FPARAM structure of the UDR. For information on how to code routine return values, see Defining a Return Value.
At the end of the routine instance, the routine manager releases the associated routine sequence. At this time, it also deallocates the MI_FPARAM structure.
Home | [ Top of Page | Previous Page | Next Page | Contents | Index ]