IBM Informix

Documentation Notes for IBM Informix Object Interface for C++ Programmer's
Guide

2.90.xC3 1 July 2005

Contents

Documentation Notes
Purpose of these documentation notes
Corrections and Changes to the Document

Documentation Notes

Purpose of these documentation notes

These notes give information that has not yet been added to the IBM Informix Object Interface for C++ Programmer's Guide. It also describes errors in that document that have not yet been corrected.

Corrections and Changes to the Document

This section describes the known corrections and changes that need to be made to the IBM Informix Object Interface for C++ Programmer's Guide.

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 and INFORMIX-Universal Server apply equally to Informix Dynamic Server.

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 and later allows these objects to be created, using the following methods.

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();
}
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();
    }

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.

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.4):

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 )

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;
          }
       ...

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.

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.

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("1234567890123456789012345678901234567890123456789
                  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 >>>>>>>>>>>>>>

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.84, 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().

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;
}

Assumptions About Your Locale

The examples in the IBM 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.

(C) Copyright IBM Corp. 1994, 2005