### DOCUMENTATION NOTES FOR

## IBM INFORMIX-OBJECT INTERFACE FOR C++

Date: 06/04/2002

Version: 2.5

### Table of Contents

  I. Overview
  II. Database Server Name Changes
  III. Row and Collection Type Value Objects
    A. Creating Row Type Value Objects Without An Open Connection
    B. Creating Collection Type Value Objects Without An Open Connection
  IV. Operations On Large Objects With A Checked-Out Connection
  V. New Constructors For ITINT8
  VI. New ITFactoryList::GetInitState() Function
    A. SQL Strings Passed To Object Interface For C++ Member Functions
  VII. Use Of ITValue::Printable With Null Value Objects
  VIII. Accessing Smart Large Objects In Nondefault SBSpaces
  IX. Use Of ITString &Trim(const char *pmc)
  X. Assumptions About Yout Locale
  XI. Binary Parameter Support

## I. Overview

The purpose of these documentation notes is to make you aware of features not described in the IBM INFORMIX-Object Interface for C++ Programmer's Guide, Version 2.5, which is the latest version of the guide.

## II. Database Server Name Changes

Since publication of the IBM INFORMIX-Object Interface for C++ Programmer's Guide, Informix has simplified its database server product offerings. References in the guide to `INFORMIX`-OnLine Dynamic Server apply equally to Informix Dynamic Server. References to `INFORMIX`-Universal Server apply equally to Informix Dynamic Server 2000, Version 9.2.

## III. Row and Collection Type Value Objects

In previous versions of Object Interface for `C++`, it was not possible to create row or collection type value objects without an open connection. Object Interface for `C++`, Version 2.70 allows these objects to be created, using the following methods.

### A. Creating Row Type Value Objects Without An Open Connection

The process consists of two steps:

1. Create the ITTypeInfo object for the row type.

2. Instantiate the row type value object using the ITFactoryList::DatumToValue() method and pass to it an ITMVDesc structure whose members are populated appropriately.

The row type object returned this way is a null row, which can be modified using ITRow::FromPrintable(). Because the row type object has been created without an open connection, the underlying data of the row type value object cannot be modified with ITDatum::SetData() or retrieved with ITDatum::Data() (where ITDatum is an interface exposed by a row type value object). However, the remaining ITRow methods are not affected.

The following example illustrates how to create a row type value object without an open connection:

    #include <iostream.h>
    #include <it.h>
    int
    main()
    {
                 ITConnection conn;
                 ITMVDesc desc;
                 ITTypeInfo colType(conn,"integer", 4,-1,-1,-1,1);
                 ITTypeInfo *ptrcolType = &colType;
                 ITString colName = "int_val";
                 ITTypeInfo newti(conn,"row(int_val integer)", 1,
                      &ptrcolType, &colName, NULL );
                 desc.vf_origtypeinfo = (ITTypeInfo *) &newti;
                 desc.vf_connection = &conn;
                 desc.vf_typedesc = NULL;
                 desc.vf_preservedata = NULL;
                 desc.vf_outerunknown = NULL;
                 desc.vf_datalength = newti.Size();
                 desc.vf_libmivaluetype = MI_NULL_VALUE;
                 desc.vf_data = NULL;
                 ITValue *val = ITFactoryList::DatumToValue (desc);
                 val->FromPrintable("row(1)");
                 cout << val->Printable() << endl;
                 val->Release();
    }

### B. Creating Collection Type Value Objects Without An Open Connection

You can create collection type value objects without an open connection using a process similar to creating row types. As with row types, ITDatum::Data() and ITDatum::SetData() cannot be used to retrieve or modify values from a collection type created without an open connection.

The following example illustrates how to create a collection type value object without an open connection:

    	#include <iostream.h>
    	#include <it.h>
    	int
    	main()
    	{
                 ITConnection conn;
                 ITMVDesc desc;
                 ITTypeInfo memberType(conn,"integer", 4,-1,-1,-1,1);
                 ITTypeInfo newti( conn, "set(integer not null)",
                 "set", memberType, NULL );
                 desc.vf_origtypeinfo = (ITTypeInfo *) &newti;
                 desc.vf_connection = &conn;
                 desc.vf_typedesc = NULL;
                 desc.vf_preservedata = NULL;
                 desc.vf_outerunknown = NULL;
                 desc.vf_datalength = newti.Size();
                 desc.vf_libmivaluetype = MI_NULL_VALUE;
                 desc.vf_data = NULL;
                 ITValue *val = ITFactoryList::DatumToValue (desc);
                 val->FromPrintable("set{1}");
                 cout << val->Printable() << endl;
                 val->Release();
    	}

## IV. Operations On Large Objects With A Checked-Out Connection

Operations can now be performed on large objects within a fetched row even though the connection is still checked out (locked). A connection is checked out after the ITQuery::ExecForIteration() method returns multiple rows in the result set. It remains checked out until either the last row in the result set has been fetched with ITQuery::NextRow() or the query processing has been terminated by calling ITQuery::Finish(). While a connection is checked out, no other query can be executed on that connection.

## V. New Constructors For ITINT8

In previous Object Interface for `C++` releases, there was the single ITInt8 constructor (described on page 5-21 of the `INFORMIX`-Object Interface for C++ Programmer's Guide, Version 2.5):

        ITInt8();

In Version 2.70, new constructors allow you to create objects using each of the built-in numeric types as initialization arguments. This eliminates the need to explicitly assign a numeric type that is not an int8 (for example, int) to an ITInt8 object before comparing it with an ITInt8 object.

Following are the new constructors:

    	ITInt8( const int );
    	ITInt8( const long );
    	ITInt8( const float );
    	ITInt8( const double );
    	ITInt8( const mi_int8 );
    	ITInt8( const ITString & );
    	#ifdef IT_COMPILER_HAS_LONG_LONG
    	ITInt8( const IT_LONG_LONG );
    	#endif

The last constructor is compiler-dependent and is defined only if the C++ compiler supports the data type LONG_LONG.

In previous releases, to initialize an ITInt8 object, the application had to assign a value to an ITInt8 object using the assignment operator (=), as follows:

    	int i = 100;
    	ITInt8 i8;
    	i8 = i;
    	if ( i8 == (ITInt8)i )

With Version 2.70, the assignment can be replaced by an ITInt8 constructor call:

    	int i = 100;
    	ITInt8 i8(i); // or ITInt8 i8(100);
    	if ( i8 == (ITInt8)i )

## VI. New ITFactoryList::GetInitState() Function

Under some circumstances, the Object Interface for `C++` library might be loaded into memory but not properly initialized. For example, if the environment variable CLIENT_LOCALE is set to an invalid locale, the `GLS` library will not properly initialize, and thus the Object Interface for `C++` library will also not properly initialize.

To allow Object Interface for `C++` application programs to verify that initialization succeeded, several new members have been added to the ITFactoryList class (defined in the public header file $INFORMIXDIR/incl/c++/itcppop.h):

    	class IT_EXPORTCLASS ITFactoryList
    	{
    	    ...

    	public:

    	    // These are the built-in factory list initialization state
    	    values
    	    enum InitState { NOTINIT, INITING, INITED };

    	    // This function can be used to determine if the built-in
    	    factory
    	    // list initialized properly. It returns
    	    ITFactoryList::NOTINIT
    	    // if initialization failed.
    	    static InitState GetInitState();

    	    ...

    	};

The user application should call GetInitState() prior to using any Object Interface for `C++` classes or interfaces to determine if Object Interface for `C++` initialized properly, as follows:

    	   main(int, char *)
    	   {
    	      // check that Object Interface for C++ DLL initialized ok
    	      if (ITFactoryList::GetInitState() == ITFactoryList::NOTINIT)
    	      {
    	         cout << "Error: Object Interface for C++ DLL not
    	         initialized" <<
    			 endl;
    	         cout << "Error: exiting program" << endl;
    	         return -1;

    	      }

    	   ...

### A. SQL Strings Passed To Object Interface For C++ Member Functions

The following Object Interface for `C++` member functions take as an argument an ITString object, which must be a single valid `SQL` statement:

ITCursor::Prepare()

ITQuery::ExecForStatus()

ITQuery::ExecOneRow

ITQuery::ExecForIteration

ITQuery::ExecToSet

ITStatement::Prepare()

The string may not be multiple `SQL` statements separated with semicolon (;) statement delimiters.

## VII. Use Of ITValue::Printable With Null Value Objects

An Object Interface for `C++` value object can encapsulate a datum fetched from a database or a datum that is to be inserted into a database. A value object exists only in the client application, and the datum encapsulated by it can be written to the database using prepared statements encapsulated by ITStatement objects or, if an updatable cursor is used, by ITCursor::UpdateCurrent.

After it fetches a row from a database in which there are columns containing `SQL` NULL entries (that is, with no data), ITValue::Printable called on a value object matching a NULL column will return the string "null." The string "null" is informational only.

Likewise, after ITValue::SetNull is called to set a value object to null (where the term "null" means `SQL` NULL: that is, no data), calls to ITValue::Printable return the string "null" for that value object to indicate that the value object contains no data.

In the special case where an Object Interface for `C++` application program actually inserted the valid data string "null" into a value object (for example, by calling ITValue::FromPrintable("null") or by fetching it from a database), the application could still distinguish between a null value object and a value object containing the valid data "null" by calling the function ITValue::IsNull on the value object. ITValue::IsNull will return true if the value object is null and false if the value object contains the valid data "null." Calling ITValue::IsNull is the preferred way to determine if a value object contains no data and should be used instead of ITValue::Printable.

## VIII. Accessing Smart Large Objects In Nondefault SBSpaces

One way to access smart large objects in nondefault spaces is to call the client-side DataBlade `API` functions that create, initialize and set the column-level characteristics of a large object specification structure and then pass a pointer to this structure (MI_LO_SPEC *LO_spec) to the overloaded Object Interface for `C++` function.

        ITBool CreateLO( MI_LO_SPEC *LO_spec,
                         int flags=IT_LO_WRONLY | IT_LO_APPEND) ;

A better way is to introduce a new C++ class to encapsulate a large object specification structure and possibly modify the existing ITLargeObjectManager class to support passing the column-level storage characteristics of smart large objects as encapsulated C++ objects for use by ITLargeObjectManager::CreateLO.

Here is a description of the short-term solution. Prior to calling CreateLO, the following DataBlade `API` call will set the fields of LO_spec to the column-level storage characteristics of column dolmdolm1.testdata, which is the CLOB column:

           res = mi_lo_colinfo_by_name(miconn,
                                       (const char *)"dolmdolm1.testdata",
                                       LO_spec);

Among the attributes for column testdata is the sbspace location specified by the PUT clause when table dolmdolm1 is created. The smart large object location attribute will be used by CreateLO (which calls DataBlade API function mi_lo_create) when it creates the smart large object.

Here is the complete, modified test case with the new solution:

    >>>>>>>>>>>> Begin modified test case with new solution >>>>>>>>>>>>

    #include <stdlib.h>
    #include <iostream.h>
    #include <it.h>

    int
    main(int argc, const char *argv[])
    {
        ITDBInfo dbinfo;
        ITConnection conn;
        char buf[1024];
        int i;
        ITString types[2];
        ITString sqlcmd;

        types[0] = "VARCHAR";
        types[1] = "VARCHAR";

        cout << " INFORMIXSERVER   : ";
        cin.getline(buf, sizeof(buf));
        if (!dbinfo.SetSystem(buf)){
    	cout << "Could not set system " << endl;
    	return (1);
        }
        cout << " DATABASE : ";
        cin.getline(buf, sizeof(buf));
        if (!dbinfo.SetDatabase(buf)){
    	cout << "Could not set database " << endl;
    	return (1);
        }
        cout << " USER     : ";
        cin.getline(buf, sizeof(buf));
        if (!dbinfo.SetUser(buf)){
    	cout << "Could not set user " << endl;
    	return (1);
        }
        cout << " PASSWORD : ";
        cin.getline(buf, sizeof(buf));
        if (!dbinfo.SetPassword(buf)){
    	cout << "Could not set password " << endl;
    	return (1);
        }

        if (!conn.Open(dbinfo) || conn.Error()) {

        if (!conn.Open() || conn.Error()) {
    	cout << "Could not open database " << endl;
    	return (1);
        }

        cout << "Start Transaction ..." << endl;
        if (!conn.SetTransaction(ITConnection::Begin)) {
    	cout << "Could not start transaction " << endl;
    	return (1);
        }

       ITStatement stmt(conn);
        cout << " SBLOBSPACE : ";
        cin.getline(buf, sizeof(buf));
        sqlcmd = "create table dolmdolm1 (";
        sqlcmd.Append("uid integer primary key,");
        sqlcmd.Append("testdata CLOB)");
        sqlcmd.Append(" PUT testdata in (");
        sqlcmd.Append(buf);
        sqlcmd.Append(") lock mode row;");
        cout << sqlcmd << endl;
        if (stmt.Error()) {
    	cout << "Could not create statement " << endl;
    	return (1);
        }
        if (!stmt.Prepare(sqlcmd)) {
    	cout << "Could not prepare create statement " << endl;
    	return (1);
        }
        if (!stmt.Exec())  {
    	cout << "Could not execute create statement " << endl;
    	return (1);
        }
        if (!stmt.Drop()) {
    	cout << "Could not drop create statement " << endl;
    	return (1);
        }
        cout << "Please monitor your sblobspaces, [return to continue]";
        cin.getline(buf, sizeof(buf));

        /************* begin new solution code **************************/

        MI_LO_SPEC *LO_spec = NULL;
        MI_CONNECTION *miconn = NULL;
        mi_integer res;
        ITLargeObjectManager lo(conn);

        miconn = conn.GetConn();
        if (miconn != NULL)
        {
           res = mi_lo_spec_init(miconn, &LO_spec);
           if (res == MI_ERROR)
           {
              cout << "stmt_test: mi_lo_spec_init failed!" << endl;
              return (1);
           }
           res = mi_lo_colinfo_by_name(miconn,
                                       (const char *)"dolmdolm1.testdata",
                                       LO_spec);
           if (res != MI_ERROR)
           {
               cout << endl << "Create a large object. Please wait ..." <<
               endl;

               ITBool status = false;
               status = lo.CreateLO(LO_spec, IT_LO_WRONLY | IT_LO_APPEND);
               if (status = true)
               {
                  for (i = 0; i < 1000; i++)
                      lo.Write("123456789012345678901234567890123456789012
                      3456789
                      012345678901234567890123456789012345678901234567890"
                      ,100);
               }
               else
    	   {
                  cout << "stmt_test: CreateLO w/non-default sbspace
                  failed!" <<
                  endl;
                  return (1);
               }
           }
           else
           {
              cout << "stmt_test: mi_lo_colinfo_by_name failed!" << endl;
              return (1);
           }
        }
        else
        {
           cout << "stmt_test: conn.GetConn returned NULL!" << endl;
           return (1);
        }
        /************* end new solution code **************************/

        cout << "The default sblobspace has changed" << endl;
        cout << "Please monitor your sblobspaces, [return to continue]";
        cin.getline(buf, sizeof(buf));

        cout << endl << "inserting row into dolmdolm1" << endl;
        if (!stmt.Prepare("insert into dolmdolm1 values (?,?);",2,types))
        {
    	cout << "Could not prepare insert cursor " << endl;
    	return (1);
        }
        ITValue *param;
        param = stmt.Param(0);
        param->FromPrintable("0");
        param->Release();
        param = stmt.Param(1);
        param->FromPrintable(lo.HandleText());
        param->Release();
        if (!stmt.Exec())  {
    	cout << "Could not execute insert statement " << endl;
    	return (1);
        }
        if (!stmt.Drop()) {
    	cout << "Could not drop insert statement " << endl;
    	return (1);
        }
        cout << endl;
        cout << "Please monitor your sblobspaces." << endl;
        cout << "The large object is still stored within the default
        sblobspace." << endl;
        cout << "[return to continue]";
        cin.getline(buf, sizeof(buf));

       /*
        cout << "Rollback Transaction ..." << endl;
        if (!conn.SetTransaction(ITConnection::Abort)) {
    	cout << "Could not rollback transaction " << endl;
    	return (1);
        }
       */
        cout << "Commit Transaction ..." << endl;
        if (!conn.SetTransaction(ITConnection::Commit)) {
    	cout << "Could not commit transaction " << endl;
    	return (1);
        }
        conn.Close();
        cout << endl;
        return 0;
    }

    >>>>>>>>>>>> End modified test case with new solution >>>>>>>>>>>>>>

## IX. Use Of ITString &Trim(const char *pmc)

The member function ITString::Trim(const char *c) is specified to trim the single, rightmost occurrence of the character (not string) starting at 'c' within the string encapsulated by an ITString object. The INFORMIX-Object Interface for C++ Programmer's Guide, Version 2.5, indicates on page 5-37 that the rightmost occurrence of the pmc (pointer to multibyte character) string is trimmed.

Trim() encapsulates searching for the rightmost character (which could be a multibyte byte character in a given locale) of the encapsulated string and truncation of that character. The search is performed by calling ITLocale::MRScan, which in turn encapsulates calling the `GLS` API function ifx_gl_mbsrchr().

## X. Assumptions About Yout Locale

The examples in the INFORMIX-Object Interface for C++ Programmer's Guide are written with the assumption that you are using the default locale en_us.8859-1 on `UNIX`, or the default locale en_us.1252 on Windows. These locales support U.S. English format conventions for dates, times, and currency. In addition, the en_us.8859-1 locale supports the ISO 8859-1 code set. On Windows, you must use codeset conversion files to and from en_us.1252 and ISO 8859-1. However, you can change the client locale on Windows to en_us.8859-1 so no codeset conversion is needed to support ISO 8859-1.

## XI. Binary Parameter Support

Informix Object Interface for C++ now supports passing binary data as parameters in prepared `SQL` DML statements DELETE, INSERT, UPDATE, and SELECT. `SQL` DELETE, INSERT, and UPDATE statements with parameters can be prepared and executed using class ITStatement and `SQL` SELECT statements with parameters can be executed using class ITCursor.

Previously, Object Interface for `C++` supported binding of parameters using only the text representation of the data as set by ITValue::FromPrintable,even though the client-side DataBlade `API` (on which Object Interface for `C++` relies to perform all database connectivity and `SQL` requests) supports binding parameters in both binary and text mode. This restriction is now removed.

Because binary support was missing, user applications had to define custom value interfaces derived from ITValue or ITDatum in which the FromPrintable method is implemented in order to support passing values of user-defined types (UDTs) in text mode. In Informix `SQL`, UDTs are implemented as `SQL` types OPAQUE and DISTINCT. Although providing custom value object interfaces for UDTs is highly desirable in a well-behaved Object Interface for `C++` application, support for passing binary parameters is a viable alternative that requires much less effort of the application programmer and may completely satisfy the application needs in some cases.

No changes were made to the Object Interface for `C++` interface to support binary parameters. Rather, existing member functions were reimplemented and existing applications will continue to work without changes. User applications can set parameters either using ITValue::FromPrintable, as before, or by setting parameters directly from binary data.

Here is an example of how Informix Object Interface for C++ can be used to set a parameter to binary data in a prepared INSERT statement. The example uses the table CUSTOMER in the demonstration database STORES7:

    #include <it.h>
    #include <iostream.h>

    int main()
    {
        ITDBInfo db("stores7");
        ITConnection conn(db);

        conn.Open();

        if( conn.Error() )
        {
            cout << "Couldn't open connection" << endl;
            return -1;
        }

        ITQuery query( conn );

        ITRow *row;

        // Create the value object encapsulating the datum of SQL type CHAR(15)
        // by fetching a row from the database and calling ITRow::Column()

        if( !(row = query.ExecOneRow( "select lname from customer;" )) )
        {
            cout << "Couldn't select from table customer" << endl;
            return -1;
        }

        ITValue *col = row->Column( 0 );

        if( !col )
        {
            cout << "couldn't instantiate lname column value" << endl;
            return -1;
        }
        row->Release();

        ITDatum *datum;

        col->QueryInterface( ITDatumIID, (void **)&datum );
        if( !datum )
        {
            cout << "couldn't get lname column datum" << endl;
            return -1;
        }
        col->Release();

        // Prepare SQL INSERT statement, set the parameter to the value object that
        // encapsulates lname column value and execute INSERT
        ITStatement stmt( conn );

        if( !stmt.Prepare( "insert into customer (lname) values ( ? );" ) )
        {
            cout << "Could not prepare insert into table customer" << endl;
            return -1;
        }
        if( !stmt.SetParam( 0, datum ) )
        {
            cout << "Could not set statement parameter" << endl;
            return -1;
        }
        if( !stmt.Exec() )
        {
            cout << "Could not execute the statement" << endl;
            return -1;
        }

        return 0;

    }

© Copyright International Business Machines Corporation 2002. All rights reserved.
US Government User Restricted Rights—Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp.