e), these functions return the column value by reference. The MI_DATUM structure contains a pointer to the value.

For a list of the text and binary representations of built-in, opaque, and distinct data types, see Table 55. For more information on the passing mechanism for an MI_DATUM value, see Contents of an MI_DATUM Structure.

Important:
The difference in behavior of mi_value( ) and mi_value_by_name( ) between C UDRs and client LIBMI applications means that row-retrieval code is not completely portable between these two types of DataBlade API modules. When you move your DataBlade API code from one of these uses to another, you must change the row-retrieval code to use the appropriate passing mechanism for column values that mi_value( ) or mi_value_by_name( ) returns.
Column Values Passed Back to a C UDR (Server)

Within a C UDR, the value buffer that mi_value( ) or mi_value_by_name( ) fills can contain either of the following values:

Tip:
The value buffer also contains a pointer to the column value if the return status of mi_value( ) or mi_value_by_name( ) is MI_ROW_VALUE or MI_COLLECTION_VALUE. For more information, see Obtaining Row Values and Obtaining Collection Values.

Therefore, within your C UDR, you cannot assume what the MI_DATUM value contains without checking its data type (or length).

Windows Only

If the mi_value( ) or mi_value_by_name( ) function passes back a value smaller than the size of an MI_DATUM structure, the DataBlade API cast promotes the smaller value to the size of MI_DATUM. (For more information, see MI_DATUM in a C UDR (Server)). If you now want to pass a pointer in the MI_DATUM structure, you might have problems on Windows if you passed the address of the MI_DATUM value, as the following pseudocode shows:

MI_DATUM datum;
mi_integer length;
mi_char bool;
mi_short small;
mi_int large;
void *pointer;

switch ( mi_value( ..., &datum, &length ) )
   {
   ....
   } /* end switch */
/* Assume that 'datum' contains a BOOLEAN value 
 * (which uses only one byte of the MI_DATUM storage space). 
 * Pass the address of the actual data to another function.
 * YOU CANNOT ALWAYS DO THIS! 
 *         my_func( &datum, length ); 
 * This address might point to the wrong byte! */

The preceding code fragment works if datum always contains a pointer to a column value or contains data the size of MI_DATUM. It might not work on some computer architectures, however, for data that is smaller than MI_DATUM (such as mi_boolean).

To convert the MI_DATUM value into a pointer to the data value, you must be sure that the address points to the starting position of the cast-promoted data. The following code fragment determines what the MI_DATUM value in datum contains and then correctly copies the value and obtains its address, based on the length of datum:

MI_ROW_DESC *row_desc;
MI_ROW *row;
MI_DATUM datum;
mi_integer length;
mi_boolean *bool;
mi_smallint *small_int;
mi_integer *full_int;
mi_date *date_val;
mi_string *col_type_name;
void *ptr_to_value;
...
switch ( mi_value(row, i, col_id, &datum, &length) )
   {
   ...
   case MI_NORMAL_VALUE:
      col_type_name =
         mi_type_typename(
            mi_column_typedesc(row_desc, i));

/* To obtain the datum value and its address, first check
 * if the value is passed by value. If not, assume that
 * the value is passed by reference.
 */
      switch( length )
         {
      /*  Case 1: Assume that a length of one byte means
       *          that 'datum' contains a BOOLEAN value.
       */
         case 1:
            bool = (mi_boolean) datum;
            ptr_to_value = &bool;
            break;

      /*  Case 2: Assume that a length of two bytes means
       *          that 'datum' contains a SMALLINT value
       */
         case 2:
            small_int = (mi_smallint) datum;
            ptr_to_value = &small_int;
            break;

      /*  Case 4: Assume that a length of four bytes means
       *          that 'datum' contains an INTEGER or DATE value
       */
         case 4:
            if ( stcopy(col_type_name, "date") == 0 )
               {
               date_val = (mi_date) datum;
               ptr_to_value = &date_val;
               }
            else /* data type is INTEGER */
               {
               full_int = (mi_integer) datum;
               ptr_to_value = &full_int;
               }
            break;

      /*  Default Case: Assume that any for any other lengths,
       *                'datum' contains a pointer to the value.
       */
         default:
            ptr_to_value = &datum;
            break;
   } /* end switch */

my_func( ptr_to_value );

The preceding code fragment handles only built-in data types that are passed by value. It was not written to handle all possible user-defined types (such as small fixed-length opaque types) because these do not have unique lengths.

End of Windows Only

In a C UDR, if the data type of the column value can fit into an MI_DATUM structure, the value buffer contains the actual column value. If you know the data types of the column values, the value buffers you declare to hold the column values must be declared as pointers to their data type. For example, the code fragment declares the value buffer that holds a SMALLINT value can be declared as follows:

mi_integer *small_int_ptr;

After the call to mi_value( ), the C UDR must cast the contents of the value buffer from a pointer variable (as small_int_ptr is declared) to the actual data type. For the SMALLINT value, the code can perform the following cast to create a copy of the column value:

small_int = (mi_smallint) small_int_ptr;

This cast is necessary only for column values whose data types are passed by value because the MI_DATUM structure contains the actual column value, not a pointer to the value.

You can use the mi_type_byvalue( ) function to determine the passing mechanism of the column value that mi_value( ) passes back, as the following code fragment shows:

row_desc = mi_get_row_desc_without_row(conn);
...
switch ( mi_value(row, i, col_id, &datum, &length) )
   {
   ...
   case MI_NORMAL_VALUE:

      if ( mi_type_byname(mi_column_typedesc(row_desc, i))
            == MI_TRUE )
         {
         /* handle pass-by-value data types */;

The mi_type_byvalue( ) function helps to determine if a one-, two-, or four-byte value is actually passed by value. You can use this function to determine the passing mechanism of a fixed-length opaque data type.

Column Values Passed Back to a Client LIBMI Application

The mi_value( ) and mi_value_by_name( ) functions pass back by reference column values for all data types; therefore, the returned MI_DATUM structure always contains a pointer to the actual value, never the value itself. Even column values that can fit into an MI_DATUM structure are passed by reference. For example, a SMALLINT value could have the same value-buffer declaration as it would in a C UDR, as follows:

mi_integer *small_int_ptr;

Unlike a C UDR, however, the column value in the value buffer does not require a cast to create a copy:

small_int = *small_int_ptr;
Accessing Smart Large Objects

In a database, smart large objects are in columns with the data type CLOB or BLOB. A smart-large-object column contains an LO handle that describes the smart large object, including the location of its data in an sbspace. This LO handle does not contain the actual smart-large-object data.

When a query retrieves a smart large object (a BLOB or CLOB column), the mi_value( ) and mi_value_by_name( ) functions return the MI_NORMAL_VALUE value status. For a BLOB or CLOB column, the MI_DATUM structure that these functions pass back contains the LO handle for the smart large object. The control mode of the query data determines whether this LO handle is in text or binary representation, as follows.

Query Control Mode
Contents of Value Buffer
Text representation
Character string that contains the hexadecimal dump of the LO-handle structure
Binary representation
Pointer to an LO-handle structure (MI_LO_HANDLE *)

When query data is in binary representation, the mi_value( ) and mi_value_by_name( ) functions pass back the LO handle by reference. Regardless of whether you obtain a smart large object in a C UDR or a client LIBMI application, the MI_DATUM structure that these functions pass back contains a pointer to an LO handle (MI_LO_HANDLE *).

To make a copy of the LO handle within your DataBlade API module, you can copy the contents of the value buffer, as follows:

MI_LO_HANDLE *blob_col, my_LO_hdl;
...
switch ( mi_value(row, i, col_id, &blob_col, &length) )
   {
   ...
   case MI_NORMAL_VALUE:

      my_LO_hdl = *blob_col;

To obtain the smart-large-object data, use the binary representation of the LO handle with the functions of the smart-large-object interface. The smart-large-object interface allows you to access smart-large-object data through its LO handle. You access the smart-large-object data with read, write, and seek operations similar to an operating-system file.

The following code fragment implements the get_smart_large_object( ) function, which reads smart-large-object data in 4,000-byte chunks:

#define BUFSIZE 4000;

mi_integer get_smart_large_object(conn, LO_hndl)
   MI_CONNECTION *conn;
   MI_LO_HANDLE *LO_hndl;
{
   MI_LO_FD LO_fd;
   mi_char read_buf[BUFSIZE];

/* Open the selected smart large object */
LO_fd = mi_lo_open(conn, LO_hndl, MI_LO_RDONLY);
if ( LO_fd == MI_ERROR )
   /* handle error */
   return (-1);
else
   {
   while ( mi_lo_read(conn, LO_fd, read_buf, BUFSIZE) 
         != MI_ERROR )
      {
      /* perform processing on smart-large-object data */
      ...
      }
   mi_lo_close(conn, LO_fd);
   return ( 0 );
   }
}

For a description of the smart-large-object interface, see Using Smart Large Objects.

Obtaining NULL Values

The mi_value( ) and mi_value_by_name( ) functions return the MI_NULL_VALUE value status for a column that contains the SQL NULL value. These functions return MI_NULL_VALUE for columns of any data type. When the mi_value( ) or mi_value_by_name( ) function returns MI_NULL_VALUE, the contents of the MI_DATUM structure that these functions pass back depends on whether the control mode for the query data is text or binary representation, as the following table shows.

Control Mode Contents of Value Buffer
(From mi_value( ) or mi_value_by_name( ))
Text representation No valid value
Binary representation The internal representation of the SQL NULL value for the data type

Obtaining Row Values

The mi_value( ) and mi_value_by_name( ) functions return the MI_ROW_VALUE value status for a column that meets either of the following conditions:

When the mi_value( ) or mi_value_by_name( ) function returns MI_ROW_VALUE, the MI_DATUM structure that these functions pass back contains a pointer to the row structure, regardless of whether the query data is in binary or text representation. You can extract the individual values from the row structure by passing the returned MI_ROW pointer to mi_value( ) or mi_value_by_name( ) for each value you need to retrieve.

Obtaining Row Types

The mi_value( ) and mi_value_by_name( ) functions can return the MI_ROW_VALUE value status for a column with a row data type: unnamed or named. The contents of the MI_DATUM structure that these functions pass back is a pointer to a row structure that contains the fields of the row type. The format of the field values depends on whether the control mode for the query data is text or binary representation, as the following table shows.

Control Mode Contents of Fields Within Row Structure
Text representation Null-terminated strings
Binary representation Internal formats of field values

For a list of the text and binary representations of data types, see Table 55.

You can extract the individual field value from the row type by passing the returned MI_ROW pointer to mi_value( ) or mi_value_by_name( ) for each field value you need to retrieve.

The get_data( ) function calls the get_row_data( ) function for an mi_value( ) return value of MI_ROW_VALUE (see the example on page Example: The get_data( ) Function). This function takes the pointer to a row structure as an argument and uses mi_value( ) on it to obtain field values in text representation.

mi_integer get_row_data(row)
   MI_ROW *row;
{
   mi_integer numflds, fldlen;
   MI_ROW_DESC *rowdesc;
   mi_integer i;
   char *fldname, *fldval;
   mi_boolean is_nested_row;

/* Get row descriptor */
   rowdesc = mi_get_row_desc(row);

/* Get number of fields in row type */
   numflds = mi_column_count(rowdesc);

/* Display the field names of the row type */
   for ( i=0; i < numflds; i++ )
      {
      fldname = mi_column_name(rowdesc, i);
      DPRINTF("trc_class, 11, ("%s\t", fldname));
      }
   DPRINTF("trc_class", 11, ("\n"));

/* Get field values for each field of row type */
   for ( i=0, i < numflds; i++ )
      {
      is_nested_row = MI_FALSE;
      switch( mi_value(row, i, &fldval, &fldlen) )
         {
         case MI_ERROR:
            ...

         case MI_NULL_VALUE:
            fldval = "NULL";
            break;

         case MI_NORMAL_VALUE:
            break;

         case MI_ROW_VALUE:
         /* have nested row type - make recursive call */
            is_nested_row = MI_TRUE;
            get_row_data((MI_ROW *)fldval);
            break;

         default:
            ...

         }
      if ( is_nested_row == MI_FALSE )
         DPRINTF("trc_class", 11, ("%s\t", fldval));
      }
   return (0);
}
Obtaining Jagged Rows

When all the rows that a query retrieves are not the same type and length, the rows are called jagged rows. Jagged rows occur as a result of a query that uses the following syntax to request all the rows in a supertable and all its subtables:

SELECT correlation_variable 
FROM table_name correlation_variable;

In the preceding query, table_name represents a supertable in an inheritance hierarchy. Suppose you create the following schema in which the table parent has one column, child has two columns, and grandchild has three columns:

CREATE TABLE parent OF TYPE parent_t (num1 INTEGER);
INSERT INTO parent VALUES (10);

CREATE TABLE child OF TYPE child_t (num2 SMALLFLOAT) 
   UNDER parent;
INSERT INTO child VALUES (20, 3.5);

CREATE TABLE grandchild OF TYPE grandchild_t (name TEXT)
   UNDER child;
INSERT INTO grandchild VALUES (30, 7.8, 'gundrun');

The following SELECT statement queries the parent supertable:

SELECT p FROM parent p;

This query returns the following three jagged rows:

p (parent_t)
num1

10
p (child_t)
num1

20
num2

3.5E+00
p (grandchild_t)
num1

30
num2

7.8E+00
name

gundrun

The DataBlade API indicates that a query returned a jagged row as follows:

The format of the columns depends on whether the control mode for the query data is text or binary representation, as the following table shows.

Control Mode Contents of Elements within Row Structure
Text representation Null-terminated strings
Binary representation Internal formats of column values

For a list of the text and binary representations of data types, see Table 55.

To retrieve jagged rows
  1. Use the mi_get_row_desc( ) function to get a row descriptor for each row structure that mi_value( ) or mi_value_by_name( ) obtains.
  2. Use the mi_column_count( ) function with the row descriptor to get a column count for each row that mi_next_row( ) retrieves.
  3. Retrieve the individual components of the row within an inner column-value loop.

Obtaining Collection Values

For a collection, the value that the mi_value( ) and mi_value_by_name( ) functions return depends on whether the control mode for the query data is text or binary representation, as the following table shows.

Return Value Control Mode Contents of Value Buffer
MI_NORMAL_VALUE Text representation Null-terminated string that contains the text representation of the collection
MI_COLLECTION_VALUE Binary representation A pointer to a collection structure (MI_COLLECTION)

In a DataBlade API module, a collection can be created in either of the following ways:

A Collection in Text Representation

When the control mode of the query data is text representation, the mi_value( ) or mi_value_by_name( ) function returns a value status of MI_NORMAL_VALUE for a collection column. The value buffer contains the text representation of the column.

For example, suppose that a query selects the set_col column, which is defined as Figure 44 shows.

Figure 44. A Sample Collection Column
CREATE TABLE table1
(
....
set_col SET(INTEGER NOT NULL),
...
)

If the set_col column contains a SET collection with the values of 3, 5, and 7, the value buffer contains the following string after mi_value( ) or mi_value_by_name( ) executes:

"SET{3          ,5          ,7          }"

For a description of collection text representation, see Collection Text Representation.

A Collection in Binary Representation

When the control mode of the query data is in binary representation, the mi_value( ) or mi_value_by_name( ) function returns a value status of MI_COLLECTION_VALUE for a collection column. The value buffer contains a pointer to the collection structure for the collection. You can extract the individual elements from the collection structure with the DataBlade API collection functions, as follows:

For more information on the use of the DataBlade API collection functions, see Collections.

For the collection column that Figure 44 defines, the following code fragment handles the MI_COLLECTION_VALUE value status that mi_value( ) or mi_value_by_name( ) returns for a collection column in binary representation:

switch( mi_value(row, i, &colval, &collen) )
   {
   ...
   case MI_COLLECTION_VALUE:
      if ( (colldesc = mi_collection_open(conn,
            (MI_COLLECTION *)colval) != NULL )
         {
         while ( mi_collection_fetch(conn, colldesc, 
               MI_CURSOR_NEXT, 0, (MI_DATUM *)&elmtval,
               &elmtlen) != MI_END_OF_DATA )
            {
            int_val = (mi_integer)elmtval;
            DPRINTF("trc_class", 11, 
               ("Element value=%d\n", int_val));
            }
         }
      break;

Example: The get_data( ) Function

The get_data( ) function retrieves data from a query that mi_exec( ) sends to the database server. This example makes the following assumptions:

The code for the get_data( ) function follows:

/*
 * FUNCTION: get_data( )
 * PURPOSE: Gets rows that a query returns.
 *
 * CALLED BY: get_results( ) (See page Example: The get_results( ) Function.)
 */
#include "mi.h"

mi_integer get_data( MI_CONNECTION *conn )
{
   MI_ROW          *row = NULL;
   MI_ROW_DESC     *rowdesc;
   mi_integer      error;
   mi_integer      numcols;
   mi_integer      i;
   mi_string       *colname;
   mi_integer      collen;
   mi_string       *colval;
   mi_integer      is_nested_row;

 
/* Get the row descriptor for the current statement */
   rowdesc = mi_get_row_desc_without_row(conn);

/* Get the number of columns in the row */

   numcols = mi_column_count(rowdesc);

/* Obtain the column names from the row desriptor */
   i = 0;
   while( i < numcols )
      {
      colname = mi_column_name(rowdesc, i);
      DPRINTF("trc_class", 11, ("  %s\t", colname));

      i++;
      }
   DPRINTF("trc_class", 11,("\n\n"));

/* For each retrieved row: */
   while ( NULL != (row = mi_next_row(conn, &error)) )
      {
      /* For each column */
      for ( i = 0; i < numcols; i++ )
         {
         is_nested_row = MI_FALSE;

         /* Initialize column value and length */
         colval = NULL;
         collen = 0;

         /* Put the column value in colval */
         switch( mi_value(row, i, &colval, &collen) )
            {
            case MI_ERROR:
               mi_db_error_raise(conn, MI_EXCEPTION,
            "\nCannot get column value (mi_value)\n" );

            case MI_NULL_VALUE:
               colval = "NULL";
               break;

            case MI_NORMAL_VALUE:
               break;

            case MI_ROW_VALUE:
               is_nested_row = MI_TRUE;
               get_rowtype_data((MI_ROW *)colval);
               break;

            default:
               mi_db_error_raise(conn, MI_EXCEPTION,
                  "\nUnknown value (mi_value)\n" );
               return( -1 );
            }     /* end switch */

         if ( is_nested_row )
            {
            /* process row type */
            }
         else
            {
            /* Print the column value */
            DPRINTF("trc_class", 11, ("  %s\t", colval));
            }

         } /* end for */

      DPRINTF("trc_class", 11, ("\n"));
      } /* end while */

   if ( MI_ERROR == error )
      {
      DPRINTF("trc_class", 11, ("\nReached last row\n"));
      }

   DPRINTF("trc_class", 11, ("\n"));

   return(1);
}

The get_data( ) function calls mi_get_row_desc_without_row( ) to obtain the row descriptor and mi_column_count( ) to obtain the number of columns. It then calls mi_column_name( ) in a for loop to obtain and print the names of the columns in the row descriptor.

Server Only

The get_data( ) function assumes it is called from within a C UDR. The function uses the DPRINTF statement, which is part of the DataBlade API tracing feature and is available only to C UDRs. The first DPRINTF statement sends the name of each retrieved column to a trace file when the trace level is 11 or higher. Another DPRINTF statement sends the column value to the trace file. For more information on tracing, see Using Tracing.

End of Server Only
Client Only

For the get_data( ) function to execute in a client LIBMI application, it would need to replace the DPRINTF statement with a client-side output function such as printf( ) or fprintf( ). The following code fragment uses the fprintf( ) function to display the names of retrieved columns:

while( i < numcols )
  {
    colname = mi_column_name(rowdesc, i);
    fprintf(stderr, "%s\t", colname);

    i++;
  }
fprintf(stderr, "\n\n");

All occurrences of DPRINTF would need to be replaced by appropriate client-side output functions.

End of Client Only

In the outer loop, mi_next_row( ) obtains every row, and in the inner loop, mi_value( ) obtains every value in the row. The pointer returned in the value buffer is not valid after the next call to mi_value( ). If the data were needed for later use, you would need to copy the data in the value buffer into a previously defined variable.

The get_data( ) function retrieves column data that is in text representation. The return values of the mi_value( ) function handle the text representations as follows:

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