Enterprise Edition Home | Express Edition Home | Previous Page | Next Page   Database Access > Executing SQL Statements > Executing SQL Statements >

Executing Prepared SQL Statements

A prepared statement is an SQL statement that is parsed and ready for execution. For these statements, you prepare the statement once and execute it as many times as needed. The DataBlade API provides the following functions to execute a prepared SQL statement.

DataBlade API Function Step in Prepared-Statement Execution
mi_prepare( ) Prepares a text representation of the SQL statement to execute
mi_statement_command_name( ), mi_get_statement_row_desc( ), or input-parameter accessor function (Table 57) Obtains information about the prepared statement
mi_exec_prepared_statement( ) or mi_open_prepared_statement( ) Sends the prepared statement to the database server for execution
mi_drop_prepared_statement( ) Releases prepared-statement resources

Preparing an SQL Statement

To turn a statement string for an SQL statement into a format that the database server can execute, use the mi_prepare( ) statement. The mi_prepare( ) function performs the following tasks to create a prepared statement:

Tip:
The mi_prepare( ) function performs the same basic task for a DataBlade API module as the SQL PREPARE statement does for an IBM Informix ESQL/C application.
Assembling a Prepared Statement

The mi_prepare( ) function passes the SQL statement to the database server as a statement string. For the mi_prepare( ) function, a statement string can contain either of the following formats of an SQL statement:

Assembling Unparameterized Statements

If you know all the statement information before the statement is prepared, you assemble an unparameterized statement as the statement string. Pass the SQL statement as a string (or a variable that contains a string) to the mi_prepare( ) function. For example, Figure 34 prepares an unparameterized SELECT statement that obtains column values from the customer table.

Figure 34. Preparing an Unparameterized Statement
stmt_desc = mi_prepare(conn, 
   "SELECT * FROM customer;", NULL)

For more information, see Assembling a Statement String.

Assembling Parameterized Statements

If some column or expression value is provided when the statement actually executes, you assemble the parameterized statement as the statement string. Specify input parameters in the statement text representation of an SQL statement. For a description of an input parameter, see Prepared Statements and Input Parameters.

You indicate the presence of an input parameter with a question mark (?) anywhere within a statement where an expression is valid. You cannot list a program-variable name in the text of an SQL statement because the database server knows nothing about variables declared in the DataBlade API module. You cannot use an input parameter to represent an identifier such as a database name, a table name, or a column name.

For example, Figure 35 shows an INSERT statement that uses input parameters as placeholders for two column values in the customer table.

Figure 35. Preparing a Statement That Contains Input Parameters
insrt_stdesc = mi_prepare(conn, 
   "INSERT INTO customer (customer_num, company) \
   VALUES (?,?);", NULL

In Figure 35, the first input parameter is defined for the value of the customer_num column and the second for the value of the company column.

Before the prepared statement executes, your DataBlade API module must assign a value to the input parameter. You pass these input-parameter values as arguments to the mi_exec_prepared_statement( ) or mi_open_prepared_statement( ) function. For more information, see Assigning Values to Input Parameters.

Assigning an Optional Name

You can obtain access to a prepared statement through its statement descriptor. However, other SQL statements that need to reference the prepared statement cannot use a statement descriptor. Therefore, you can assign an optional string name to a prepared SQL statement. Specify a name as the third argument of the mi_prepare( ) function.

Server Only

The last argument to mi_prepare( ) specifies the cursor name for the prepared statement. Assigning a cursor name is useful for a statement that includes an update cursor so that an UPDATE or DELETE statement that contains the following clause can reference the cursor in this clause:

WHERE CURRENT OF cursor_name

You can specify an update cursor in the syntax of the SELECT statement that you prepare, as the following versions of the SELECT statement show:

SELECT customer_num, company FROM customer
WHERE customer_num = 104 FOR UPDATE OF company;

SELECT customer_num, company FROM customer
WHERE customer_num = 104;

For more information on the FOR UPDATE keywords of SELECT with databases that are ANSI compliant and not ANSI compliant, see Defining a Cursor Mode.

End of Server Only

The following code fragment uses the mi_prepare( ) statement to assign a name to a cursor and an UPDATE WHERE CURRENT OF statement to update the fifth row in this cursor:

/* Prepare the FOR UPDATE statement */
if ( (stmt1 = mi_prepare(conn, 
            "select * from tab1 for update;",
            "curs1")) == NULL )
   return MI_ERROR;

/* Open the cursor */
if ( mi_open_prepared_statement(stmt1, MI_BINARY,
      MI_QUERY_BINARY, num_params, values, lengths, nulls,
      types, NULL, 0, NULL) != MI_OK )
   return MI_ERROR;

/* Fetch the 5th row */
if ( mi_fetch_statement(stmt1, MI_CURSOR_NEXT, 0, 5) 
      != MI_OK )
   return MI_ERROR;

/* Get values from 5th row */
if ( mi_get_result(conn) != MI_ROWS 
      || mi_next_row(conn, &res) == NULL )
   return MI_ERROR;

/* Update 5th row */
if ( mi_exec("update tab1 set int_col = int_col + 2 \
            where current of curs1;", NULL) != MI_OK )
   return MI_ERROR;

/* Clean up */
if ( mi_close_statement(stmt1) != MI_OK )
   return MI_ERROR;
if ( mi_drop_prepared_statement(stmt1) != MI_OK )
   return MI_ERROR;

The mi_open_prepared_statement( ) function also provides the ability to name the cursor. However, if you specify a cursor name in mi_prepare( ), make sure that you pass a NULL-valued pointer as the cursor name to mi_open_prepared_statement( ). Conversely, if you want to specify the cursor name in mi_open_prepared_statement( ), use a NULL-valued pointer as the cursor name in mi_prepare( ). If you specify a cursor name in both mi_prepare( ) and mi_open_prepared_statement( ), the DataBlade API uses the cursor name that mi_open_prepared_statement( ) provides.

If your prepared statement does not fetch rows, pass a NULL-valued pointer as the third argument to mi_prepare( ).

Client Only

The last argument to mi_prepare( ) specifies the statement name for the prepared statement. The cursor_name argument of mi_open_prepared_statement( ) specifies the cursor name for the prepared statement. If you do not need to assign a statement name, pass a NULL-valued pointer as the last argument to mi_prepare( ).

End of Client Only
Returning a Statement Descriptor

The mi_prepare( ) function sends the contents of an SQL statement string to the database server, which parses the statement and returns it in an optimized executable format. The function returns a pointer to an explicit statement descriptor (usually called just a statement descriptor). A statement descriptor, MI_STATEMENT, is a DataBlade API structure that contains the information about a prepared SQL statement, including the executable format of the SQL statement.

The following table summarizes the memory operations for a statement descriptor.

Memory Duration Memory Operation Function Name
Not allocated from memory-duration pools Constructor mi_prepare( )
Destructor mi_drop_prepared_statement( ),
mi_close_statement( )

A statement descriptor can be identified in either of the following ways:

Table 56 lists the DataBlade API accessor functions for an explicit statement descriptor.

Table 56. Accessor Functions for an Explicit Statement Descriptor
Statement-Descriptor Information DataBlade API Accessor Function
The name of the SQL statement that was prepared mi_statement_command_name( )
Information about any input parameters in the prepared statement The input-parameter accessor functions (Table 57)
A row descriptor for the columns in the prepared statement mi_get_statement_row_desc( )

From the row descriptor, you can use the row-descriptor accessor functions to obtain information about a particular column (see Table 22).

Important:
To DataBlade API modules, the statement descriptor (MI_STATEMENT) is an opaque C structure. Do not access the internal fields of this structure directly. The internal structure of the MI_STATEMENT may change in future releases. Therefore, to create portable code, always use these accessor functions to obtain prepared-statement information.

You pass a statement descriptor to the other DataBlade API functions that handle prepared statements, including mi_exec_prepared_statement( ), mi_open_prepared_statement( ), mi_fetch_statement( ), mi_close_statement( ), and mi_drop_prepared_statement( ).

Obtaining Input-Parameter Information

From a statement descriptor, you can obtain information about an input parameter once an SQL statement has been prepared. An input parameter indicates a value that is provided when the prepared statement executes. Table 57 lists the DataBlade API accessor functions that obtain input-parameter information from the statement descriptor.

Table 57. Input-Parameter Information in the Statement Descriptor
Column Information DataBlade API
Accessor Function
The number of input parameters in the prepared statement mi_parameter_count( )
The precision (total number of digits) of the column associated with an input parameter mi_parameter_precision( )
The scale of a column that is associated with the input parameter mi_parameter_scale( )
Whether the column associated with each input parameter was defined with the NOT NULL constraint mi_parameter_nullable( )
The type identifier of the column that is associated with the input parameter mi_parameter_type_id( )
The type name of the column that is associated with the input parameter mi_parameter_type_name( )

Important:
To DataBlade API modules, the input-parameter information in the statement descriptor (MI_STATEMENT) is part of an opaque C data structure. Do not access the internal fields of this structure directly. The internal structure of the MI_STATEMENT structure may change in future releases. Therefore, to create portable code, always use these accessor functions to obtain input-parameter information.

Input-parameter information is available only for the INSERT and UPDATE statements. Support for the UPDATE statement includes the following forms of UPDATE:

If you attempt to request input-parameter information for other SQL statements, the input-parameter functions in Table 57 raise an exception.

The statement descriptor stores input-parameter information in several parallel arrays.

Input-Parameter Array Contents
Parameter-type ID array Each element is a pointer to a type identifier (MI_TYPEID) that indicates the data type of the input parameter.
Parameter-type name array Each element is a pointer to the string name of the data type for each input parameter.
Parameter-scale array Each element is the scale of the column associated with the input parameter.
Parameter-precision array Each element is the precision of the column associated with the input parameter.
Parameter-nullable array Each element is either MI_FALSE or MI_TRUE:
  • MI_FALSE indicates that the input parameter is associated with a column that cannot contain SQL NULL values.
  • MI_TRUE indicates that the input parameter is associated with a column that can contain SQL NULL values.

All of the input-parameter arrays in the statement descriptor have zero-based indexes. Within the statement descriptor, each input parameter in the prepared statement has a parameter identifier, which is the zero-based position of the input parameter within the input-parameter arrays. When you need information about an input parameter, specify its parameter identifier to one of the statement-descriptor accessor functions in Table 57.

Figure 36 shows how the information at index position 1 of these arrays holds the input-parameter information for the second input parameter of a prepared statement.

Figure 36. Input-Parameter Arrays in the Statement Descriptor
begin figure description - This figure is described in the surrounding text. - end figure description

To access information for the nth input parameter, provide an index value of n-1 to the appropriate accessor function in Table 57. The following calls to the mi_parameter_type_id( ) and mi_parameter_nullable( ) functions obtain from the statement descriptor that stmt_desc identifies the type identifier (param_type) and whether the column is nullable (param_nullable) for the second input parameter:

MI_STATEMENT *stmt_desc;
MI_TYPEID *param_type;
mi_integer param_nullable;
...
param_type = mi_parameter_type_id(stmt_desc, 1);
param_nullable = mi_parameter_nullable(stmt_desc, 1);

To obtain the number of input parameters in the prepared statement (which is also the number of elements in the input-parameter arrays), use the mi_parameter_count( ) function.

Sending the Prepared Statement

For a prepared statement to be executed, you must send it to the database server with one of the following DataBlade API functions.

DataBlade API Function When To Use
mi_exec_prepared_statement( ) If the prepared statement does not return rows

If the prepared statement does return rows but you only need to access these rows sequentially (with an implicit cursor)

mi_open_prepared_statement( ) If the prepared statement does return rows and you need to perform one of the following tasks:
  • Access these rows with a scroll, update, or hold cursor (instead of a read-only sequential cursor)
  • Control how many rows the database server puts into the cursor at one time

Both these functions support the following parameters.

Parameter Description
stmt_desc Is a pointer to a statement descriptor for the prepared statement

The mi_prepare( ) function generates this statement descriptor.

control flag Determines whether any query rows are in binary or text representation
params_are_binary Indicates whether the input-parameter values are in binary or text representation
n_params Is the number of input-parameter values in the input-parameter-value arrays
Input-parameter-value arrays:
  • values
  • types
  • lengths
  • nulls
Arrays that contain the following information for each input-parameter value:
  • Value
  • Data type
  • Length (for varying-length data types)
  • Whether the input-parameter value is an SQL NULL value

For more information, see Assigning Values to Input Parameters.

retlen The number of column values that are in each retrieved row
rettypes An array that contains the data types of any returned column values

Once the database server executes the prepared statement, the statement becomes the current statement. The database server sends back the statement results, including whether the current statement was successful. Obtain the status of the current statement with the mi_get_result( ) function. For more information, see Processing Statement Results.

Tip:
The return value that the mi_exec_prepared_statement( ) or mi_open_prepared_statement( ) function returns does not indicate the success of the current statement. It indicates if mi_exec_prepared_statement( ) or mi_open_prepared_statement( ) was able to successfully send the prepared statement to the database server.
Statements with mi_exec_prepared_statement( )

The mi_exec_prepared_statement( ) function is for the execution of prepared statements, both queries and other valid SQL statements. In a DataBlade API module, use the following DataBlade API functions to execute a prepared SQL statement with mi_exec_prepared_statement( ).

DataBlade API Function Step in Prepared-Statement Execution
mi_prepare( ) Prepares the statement string for execution
mi_statement_command_name( ), mi_get_statement_row_desc( ), or input-parameter accessor function (Table 57) Obtains information about the prepared statement (optional)
mi_exec_prepared_statement( ) Sends the prepared statement to the database server for execution and opens any cursor required
mi_drop_prepared_statement( ) Releases prepared-statement resources

The mi_exec_prepared_statement( ) function performs the following tasks for the prepared SQL statement:

Tip:
If the implicit cursor that mi_exec_prepared_statement( ) creates does not adequately meet the needs of your DataBlade API module, you can use the mi_open_prepared_statement( ) function to define other types of cursors. For more information, see Defining an Explicit Cursor.

When the mi_exec_prepared_statement( ) function successfully fetches the query rows into the cursor, the cursor position points to the first row of the cursor, and the mi_get_result( ) function returns a status of MI_ROWS to indicate that the cursor contains rows.

You can access these rows one at a time with the mi_next_row( ) function. Each access obtains the row to which the cursor position points. After each access to the cursor, the cursor position moves to the next row. For more information, see Retrieving Query Data.

The following variation of the send_statement( ) function (page Example: The send_statement( ) Function) uses mi_exec_prepared_statement( ) instead of mi_exec( ) to send an SQL statement to the database server:

mi_integer send_statement2(conn, stmt)
   MI_CONNECTION *conn;
   mi_string *stmt;
{
   mi_integer count;
   MI_STATEMENT *stmt_desc;

   /* Prepare the statement */
   if ( (stmt_desc = mi_prepare(conn, stmt, NULL)) == NULL )
      mi_db_error_raise(conn, MI_EXCEPTION,
         "mi_prepared failed\n");

   /* Send the basic statement, specifying that query
    * be sent in its text representation
    */
   if ( mi_exec_prepared_statement(stmt_desc, 0, MI_FALSE,
         0, NULL, NULL, NULL, 0, NULL) == MI_ERROR )
      mi_db_error_raise(conn, MI_EXCEPTION,
         "mi_exec_prepared_statement failed\n");

   /* Get the results of the current statement */
   count = get_results(conn);

   /* Release statement resources */
   if ( mi_drop_prepared_statement(stmt_desc) == MI_ERROR )
      mi_db_error_raise(conn, MI_EXCEPTION,
         "mi_drop_prepared_statement failed\n");
   if ( mi_query_finish(conn) == MI_ERROR )
      mi_db_error_raise(conn, MI_EXCEPTION,
         "mi_query_finish failed\n");

   return ( count );
}

The mi_exec_prepared_statement( ) function allocates type descriptors for each of the data types of the input parameters. If the calls to mi_exec_prepared_statement( ) are in a loop in which these data types do not vary between loop iterations, mi_exec_prepared_statement( ) can reuse the type descriptors, as follows:

This method saves on the number of type descriptors that mi_exec_prepared_statement( ) must allocate, thereby reducing memory usage.

In Figure 37, mi_exec_prepared_statement( ) in the initial pass of the for loop specifies the INTEGER data type for the single input parameter in an INSERT statement. For subsequent passes of the for loop, mi_exec_prepared_statement( ) receives a NULL-valued pointer for its types array. When it receives this NULL-valued pointer, mi_exec_prepared_statement( ) reuses the type descriptor that it has already created.

Figure 37. Reusing Type Descriptors in Repeated Calls to mi_exec_prepared_statement( )
mi_string *types[1] = {"int"};
mi_string **types_exec;
...
sprintf(command, "insert into tabA values(?, %d);", j);
if ( (stmt_desc = mi_prepare(conn, command, NULL)) == NULL )
   {
   return -1;
   }

types_exec = types;
for (j=0; j < numLoop; j++)
   {
   values[0] = (MI_DATUM) j;

   if ( (ret = mi_exec_prepared_statement(stmt_desc, 
         MI_BINARY, 1, 1, values, lengths, nulls,
         types_exec, 0, NULL)) )
      {
      return -2;
      }

   if ( (ret = mi_get_result(conn)) == MI_ERROR )
      return -4;

   if ( ret == MI_DML || MI_DDL )
      row_count += mi_result_row_count(conn);

   types_exec = NULL; /* reuse data types from 1st pass */
   }
Statements with mi_open_prepared_statement( )

The mi_open_prepared_statement( ) function is for the execution of queries. In a DataBlade API module, use the following DataBlade API functions to execute a prepared SQL statement with mi_open_prepared_statement( ).

DataBlade API Function Step in Execution of Prepared Statement
mi_prepare( ) Prepares the statement string for execution
mi_statement_command_name( ), mi_get_statement_row_desc( ), or input-parameter accessor function (Table 57) Obtains information about the prepared statement (optional)
mi_open_prepared_statement( ) Sends the prepared statement to the database server for execution and open the cursor
mi_fetch_statement( ) Retrieves any data that the query returns
mi_close_statement( ), mi_drop_prepared_statement( ) Releases prepared-statement resources

The mi_open_prepared_statement( ) function performs the following tasks for the prepared SQL statement:

Tip:
The mi_open_prepared_statement( ) function performs the same basic task for a DataBlade API module as the SQL OPEN statement does for an IBM Informix ESQL/C application.

The main difference between mi_exec_prepared_statement( ) and mi_open_prepared_statement( ) is that the latter allows more flexibility in the definition of the cursor used for the query rows. With mi_open_prepared_statement( ), you can define an explicit cursor. In particular, mi_open_prepared_statement( ) allows you to specify:

The mi_open_prepared_statement( ) function allocates type descriptors for each of the data types of the input parameters. If the calls to mi_open_prepared_statement( ) are in a loop in which these data types do not vary between loop iterations, mi_open_prepared_statement( ) can reuse the type descriptors, as follows:

This method saves on the number of type descriptors that mi_open_prepared_statement( ) must allocate, thereby reducing memory usage. For sample code in which the mi_exec_prepared_statement( ) function reuses type descriptors, see Figure 37.

Defining an Explicit Cursor

The control flag of mi_open_prepared_statement( ) allows you to define an explicit cursor to hold the rows that the prepared query returns. You can choose the following cursor characteristics when you define the cursor:

Defining a Cursor Type

The mi_open_prepared_statement( ) function supports the following types of cursors for holding query rows.

Cursor Type
Description
Sequential cursor
Enables you to move through the rows of the cursor in the forward direction only

You can pass only once through the rows.

Scroll cursor
Enables you to move through the rows of the cursor in the forward and backward directions

You can move back in the rows without having to reopen the cursor; however, the database server stores the data for a scroll cursor in a temporary table. The data can become stale; that is, the data in the cursor is consistent with the data in the database when the cursor is filled, but if the data in the database changes, the data in the cursor does not reflect these changes.

Table 58 shows the control-flag values that determine cursor type and cursor mode.

Defining a Cursor Mode

You can specify one of the following cursor modes for the cursor with the control-flag bit mask.

Cursor Mode Description SELECT Statement
Update Enables you to read and modify the data within the cursor SELECT...FOR UPDATE
Read-only Enables you to read the data within the cursor; does not allow you to update or delete any row it fetches SELECT...FOR READ ONLY

When you execute a prepared SELECT statement with no FOR UPDATE or FOR READ ONLY clause, the cursor mode you need depends on whether your database is ANSI-compliant, as follows:

For more information on the FOR UPDATE and FOR READ ONLY clauses, see the description of the SELECT statement in the IBM Informix: Guide to SQL Syntax.

By default, both the sequential and scroll cursor types have a cursor mode of update (also called read/write). Table 58 shows the cursor types and cursor modes, with the required bit-mask values for the control flag.

Table 58. Control-Flag Values for Cursor Type and Mode
Cursor Control-Flag Value
Update sequential cursor None (default)
Read-only sequential cursor MI_SEND_READ
Update scroll cursor MI_SEND_SCROLL
Read-only scroll cursor MI_SEND_READ + MI_SEND_SCROLL
Defining a Cursor Lifespan

You can define the lifespan of the cursor with the control-flag bit mask. By default, the database server closes all cursors at the end of a transaction. If your DataBlade API module requires uninterrupted access to a set of rows across transaction boundaries, you can define a hold cursor. A hold cursor can be either a sequential or a scroll cursor.

To define a hold cursor, you specify the MI_SEND_HOLD constant in the control-flag bit mask of the mi_open_prepared_statement( ) function, as the following table shows.

Cursor Control-Flag Value
Update sequential cursor with hold MI_SEND_HOLD
Read-only sequential cursor with hold MI_SEND_READ + MI_SEND_HOLD
Update scroll cursor with hold MI_SEND_SCROLL + MI_SEND_HOLD
Read-only scroll cursor with hold MI_SEND_READ + MI_SEND_SCROLL + MI_SEND_HOLD
Fetching Rows Into a Cursor

When mi_open_prepared_statement( ) successfully opens a cursor, the cursor is empty, with the cursor position pointing to the first location of the cursor, and the mi_get_result( ) function returns a status of MI_NO_MORE_RESULTS to indicate that the cursor does not contain rows.

Figure 38 shows the state of the explicit cursor that contains one integer column after mi_open_prepared_statement( ) executes.

Figure 38. Row Cursor After mi_open_prepared_statement( )
begin figure description - This figure is described in the surrounding text. - end figure description

To populate the open cursor, use the mi_fetch_statement( ) function, which fetches the specified number of retrieved rows from the database server into the cursor. You can perform a fetch operation on an update or a read-only cursor. To fetch rows into a cursor, you must specify the following information to mi_fetch_statement( ):

The mi_fetch_statement( ) function requests the specified number of retrieved rows from the database server and copies them into the cursor, which is associated with the specified statement descriptor. When mi_fetch_statement( ) completes successfully, the following items are true:

With mi_fetch_statement( ), you can request rows at different locations based on the type of cursor that mi_open_prepared_statement( ) has defined. To specify location, mi_fetch_statement( ) has an action argument of type MI_CURSOR_ACTION, which supports the cursor-action constants in the following table.

Cursor-Action Flag Description Type of Cursor
MI_CURSOR_NEXT Fetches the next num_rows rows, starting at the current retrieved row on the database server Sequential

Scroll

MI_CURSOR_PRIOR Fetches the previous num_rows rows, starting at the current retrieved row Scroll
MI_CURSOR_FIRST Fetches the first num_rows rows Sequential

Scroll

MI_CURSOR_LAST Fetches the last num_rows rows Sequential

Scroll

MI_CURSOR_ABSOLUTE Moves jump rows into the retrieved rows and fetches num_rows rows Sequential (as long as the jump argument does not move the cursor position backward)

Scroll

MI_CURSOR_RELATIVE Moves jump rows from the current retrieved row and fetch num_rows rows Sequential (as long as the jump argument is a positive number)

Scroll

Figure 39 shows the state of a row cursor that Figure 38 defines after the following mi_fetch_statement( ) executes:

mi_fetch_statement(stmt_desc, MI_CURSOR_NEXT, 0, 0);

Figure 39. Fetching All Retrieved Rows Into a Cursor
begin figure description - This figure is described in the surrounding text. - end figure description

Once the rows reside in the cursor, your DataBlade API module can access these rows one at a time with the mi_next_row( ) function. For more information, see Retrieving Query Data.

If you specify a non-zero value for the num_rows argument, mi_fetch_statement( ) fetches the requested number of rows into the cursor. Specify a non-zero value for num_rows if your DataBlade API module needs to handle rows in smaller groups. In this case, you retrieve num_rows number of query rows from the cursor with mi_next_row( ). When mi_next_row( ) indicates that no more rows are in the cursor, you must determine whether to fetch any remaining rows from the database server into the cursor, as follows:

The mi_fetch_statement( ) for Figure 39 specified a value of zero (0) as the number of rows to fetch, which tells the function to fetch all retrieved rows. Figure 40 shows the state of the row cursor that Figure 38 defines when the mi_fetch_statement( ) function specifies a num_rows argument of three instead of zero, as follows:

mi_fetch_statement(stmt_desc, MI_CURSOR_NEXT, 0, 3);

Figure 40. Fetching First Three Rows into a Cursor
begin figure description - This figure is described in the surrounding text. - end figure description
Server Only

The following code fragment uses the mi_open_prepared_statement( ) function to assign an input-parameter value, execute a SELECT statement, and retrieve the query rows:

mi_string *cmd = 
   "select order_num from orders \
    where customer_num = ?;";
MI_STATEMENT *stmt;
...
if ( (stmt = mi_prepare(conn, cmd, NULL)) == NULL )
   mi_db_error_raise(NULL, MI_EXCEPTION, 
      "mi_prepare( ) failed");

values[0] = 104;
types[0] = "integer";
lengths[0] = 0;
nulls[0] = MI_FALSE;

/* Open the read-only cursor to hold the query rows */
if ( mi_open_prepared_statement(stmt, MI_SEND_READ,
      MI_TRUE, 1, values, lengths, nulls, types, 
      "cust_select", retlen, rettypes) 
      != MI_OK )
   mi_db_error_raise(NULL, MI_EXCEPTION,
      "mi_open_prepared_statement( ) failed");

/* Fetch the retrieved rows into the cursor */
if ( mi_fetch_statement(stmt, MI_CURSOR_NEXT, 0, 3) != MI_OK )
   mi_db_error_raise(NULL, MI_EXCEPTION, 
      "mi_fetch_statement( ) failed");

if ( mi_get_result(conn) != MI_ROWS )
   mi_db_error_raise(NULL, MI_EXCEPTION,
   "mi_get_result( ) failed or found nonquery statement");

/* Retrieve the query rows from the cursor */
if ( !(get_data(conn)) )
   mi_db_error_raise(NULL, MI_EXCEPTION, 
      "get_data( ) failed");

/* Close the cursor */
if ( mi_close_statement(stmt) == MI_ERROR )
   mi_db_error_raise(NULL, MI_EXCEPTION,
      "mi_close_statement( ) failed");

/* Release resources */
if ( mi_drop_prepared_statement(stmt) == MI_ERROR )
   mi_db_error_raise(NULL, MI_EXCEPTION, 
      "mi_drop_prepared_statement( ) failed");
if ( mi_close(conn) == MI_ERROR )
   mi_db_error_raise(NULL, MI_EXCEPTION,
      "mi_close( ) failed");

This code fragment sends its input-parameter value in binary representation. The code fragment is part of a C UDR because it passes the INTEGER input-parameter value by value. For more information, see Assigning Values to Input Parameters.

End of Server Only
Assigning Values to Input Parameters

For a parameterized SQL statement, your DataBlade API module must perform the following steps:

  1. Specify input parameters in the text of the SQL statement.
  2. Send the SQL statement to the database server for parsing.
  3. Provide input-parameter values when the SQL statement executes.

The mi_prepare( ) statement performs these first two steps for a parameterized statement. For more information, see Preparing an SQL Statement.

The mi_exec_prepared_statement( ) and mi_open_prepared_statement( ) functions assign values to input parameters when they send a parameterized SQL to the database server for execution. To provide a value for an input parameter, you pass information in several parallel arrays:

These input-parameter value arrays are similar to the input-parameter arrays in the statement descriptor (see Figure 36). They have an element for each input parameter in the prepared statement. However, they are unlike the input-parameter arrays in the statement descriptor in the following ways:

All of the input-parameter-value arrays have zero-based indexes. Figure 41 shows how the information at index position 1 of these arrays holds the input-parameter-value information for the second input parameter of a prepared statement.

Figure 41. Arrays for Initialization of Input Parameters
begin figure description - This figure is described in the surrounding text. - end figure description

You specify the number of input-parameter values in the input-parameter value arrays with the nparams argument of mi_exec_prepared_statement( ) or mi_open_prepared_statement( ).

The following sections provide additional information about each of the input-parameter-value arrays.

Parameter-Value Array

The parameter-value array, values, is the fifth argument of the mi_exec_prepared_statement( ) and mi_open_prepared_statement( ) functions. Each element of the parameter-value array is a pointer to an MI_DATUM structure that holds the value for each input parameter. The format of this value depends on:

For more information, see Contents of an MI_DATUM Structure.

For the prepared INSERT statement in Figure 35, the code fragment in Figure 42 assigns values to the input parameters for the customer_num and company columns. These values are in text representation because the params_are_binary argument of mi_exec_prepared_statement( ) is MI_FALSE.

Figure 42. Executing a Statement That Contains Text-Representation Input Parameters
/* Initialize input parameter for customer_num column */
values[0] = "0"; /* value of '0' for SERIAL customer_num */
lengths[0] = 0; /* SERIAL is built-in type: no length */
types[0] = "serial";
nulls[0] = MI_FALSE;

/* Initialize input parameter for company column */
stcopy("Trievers Inc.", strng);
values[1] = strng;
lengths[1] = stleng(strng); /* CHAR types need length! */
types[1] = "char(20)";
nulls[1] = MI_FALSE;

/* Send INSERT statement to database server for execution along 
 * with the input-parameter-value arrays
 */
mi_exec_prepared_statement(insrt_stdesc, 0, MI_FALSE, 2
   values, lengths, nulls, types, 0, NULL);
Server Only

The following code fragment initializes the input parameters to the same values but it assigns these values in binary representation instead of text representation:

/* Initialize input parameter for customer_num column */
values[0] = 0; /* value of 0 for SERIAL customer_num */
lengths[0] = 0; /* SERIAL is built-in type: no length */
types[0] = "serial";
nulls[0] = MI_FALSE;

/* Initialize input parameter for company column */
values[1] = mi_string_to_lvarchar("Trievers Inc.");
lengths[1] = 0; /* CHAR types need length! */
types[1] = "char(20)";
nulls[1] = MI_FALSE;

/* Send INSERT statement to database server for execution along
 * with the input-parameter-value arrays
 */
mi_exec_prepared_statement(insrt_stdesc, 0, MI_TRUE, 2
   values, lengths, nulls, types, 0, NULL);

In the preceding code fragment, the first element of the values array is assigned an integer value. Because this code executes in a C UDR, the integer value in the MI_DATUM structure of this array must be passed by value.

End of Server Only
Client Only

In a client LIBMI application, all values in MI_DATUM structure must be passed by reference. Therefore, to assign values to input parameters within a client LIBMI application, you must assign all values in the values array as pointers to the actual values.

The preceding code fragment assumes that it executes within a C UDR because it passes the value for the first input parameter (an INTEGER column by value). In a client LIBMI application, you cannot use the pass-by-value mechanism. Therefore, the assignment to the first input parameter must pass a pointer to the integer value, as follows:

col1 = 0;
values[0] = &col1;
End of Client Only
Parameter-Value Length Array

The parameter-value length array, lengths, is the sixth argument of the mi_exec_prepared_statement( ) and mi_open_prepared_statement( ) functions. Each element of the parameter-value length array is the integer length (in bytes) of the data type for the input-parameter value.

The meaning of the values in the lengths array depends on the control mode of the input-parameter values, as follows:

Important:
Even though there are some cases in which the database server does not read the length of the input-parameter value, it is recommended that you always specify lengths to maintain consistency of code.
Parameter-Value Null Array

The parameter-value null array, nulls, is the seventh argument of the mi_exec_prepared_statement( ) and mi_open_prepared_statement( ) functions. Each element of the parameter-value null array is either:

Parameter-Value Type Array

The parameter-value type array, types, is the eighth argument of the mi_exec_prepared_statement( ) and mi_open_prepared_statement( ) functions. Each element of the parameter-value type array is a pointer to a string that identifies the data type of the input-parameter value. The type names must match those that the mi_type_typename( ) function would generate for the data type.

If the prepared statement has input parameters and is not an INSERT statement, you must use the types array to supply the data types of the input parameters. Otherwise, you can pass in a NULL-valued pointer as the types argument.

Determining Control Mode for Query Data

The mi_exec_prepared_statement( ) and mi_open_prepared_statement( ) functions specify the control mode for the data of a prepared query in their bit-mask control argument. To determine the control mode, set the control argument as the following table shows.

Control Mode
Control Argument
Text representation
Zero (default)
Binary representation
MI_BINARY

For mi_exec_prepared_statement( ), MI_BINARY is the only valid control-flag constant for the control argument. Therefore, a default value of zero (0) as the control argument indicates text representation of the data. The following mi_exec_prepared_statement( ) call specifies a control mode of binary representation:

mi_open_prepared_statement(stmt_desc, MI_BINARY, ...);

For mi_open_prepared_statement( ), the control argument indicates the cursor characteristics in addition to the control mode. To specify a text representation, omit the MI_BINARY control-flag constant from the control argument. Including MI_BINARY in the control argument indicates that results are to be returned in binary representation. The following mi_open_prepared_statement( ) call specifies an update scroll cursor and a control mode of binary representation:

mi_open_prepared_statement(
   stmt_desc, 
   MI_BINARY + MI_SEND_SCROLL, 
   ...);

For information on how to specify cursor characteristics to mi_open_prepared_statement( ), see Defining an Explicit Cursor. For more information on the control mode, see Control Modes for Query Data.

Releasing Prepared-Statement Resources

When your DataBlade API module no longer needs a prepared statement, you can release the resources that it uses with the following DataBlade API functions.

Prepared-Statement Resource DataBlade API Function
Explicit cursor mi_close_statement( )
Statement descriptor (including any cursor) mi_drop_prepared_statement( )
Closing a Statement Cursor

For prepared queries (SQL statements that return rows), the statement descriptor has a cursor associated with it. The scope of this cursor is from the time it is opened, with mi_exec_prepared_statement( ) or mi_open_prepared_statement( ), until one of the following events occurs:

To conserve resources, use the mi_close_statement( ) function to explicitly close an explicit cursor once your DataBlade API module no longer needs it. The mi_close_statement( ) function is the destructor function for an explicit cursor that is associated with a statement descriptor. That is, it frees the cursor that the mi_open_prepared_statement( ) function opens. Until you drop the prepared statement with mi_drop_prepared_statement( ), you can still reopen an explicit cursor with another call to mi_open_prepared_statement( ).

Tip:
The mi_close_statement( ) function performs the same basic task for a DataBlade API module as the SQL CLOSE statement does for an IBM Informix ESQL/C application.

The mi_close_statement( ) function is not the destructor function for an implicit cursor that is associated with a statement descriptor. That is, it does not free the cursor that the mi_exec_prepared_statement( ) function opens. To close an implicit cursor, use the mi_drop_prepared_statement( ) function.

Dropping a Prepared Statement

A statement descriptor describes a prepared statement. However, this DataBlade API structure is not allocated from the memory-duration pools. Instead, its scope is from the time it is created with mi_prepare( ) until whichever of the following events occurs first:

To conserve resources, use the mi_drop_prepared_statement( ) function to explicitly deallocate the statement descriptor once your DataBlade API module no longer needs it. The mi_drop_prepared_statement( ) function is the destructor function for a statement descriptor. It frees the statement descriptor and any resources (such as an implicit or explicit cursor) that are associated with the statement descriptor. These resources include the prepared statement and any associated row cursor. Once you drop a prepared statement, you must reprepare it before it can be executed again.

Enterprise Edition Home | Express Edition Home | [ Top of Page | Previous Page | Next Page | Contents | Index ]