# Oracle Call Interface Template Library 1.0.5 (OTL), Pro*OTL / Pre-Pro*C preprocessor 1.0.0 (PPC)

Sergei Kuchin, email: skuchin@ispwest.com, skuchin@gmail.comgmail

    Copyright (C) Sergei Kuchin, 1996, 1997,1998
    Permission to use, copy, modify and redistribute this
    document for any purpose is hereby granted without fee,
    provided that the above copyright notice appear in all
    copies.

## Table of Contents

- 1. Introduction
- 2. Getting started with OTL
  - 2.1. Examples
    - 2.1.1. Example 1 (with ordinary, non-template host variables)
    - 2.1.2. Example 2 (with template instantiated host variables)
    - 2.1.3. Example 3 (with the extended "parse" function)
    - 2.1.4. Example 4 (with Oracle LONG columns)
    - 2.1.5. Example 5 (with the otl_select_stream class)
    - 2.1.6. Example 6 (with the otl_out_stream class)
    - 2.1.7. Example 7 (with the otl_inout_stream class)
    - 2.1.8. Example 8 (with the otl_stream class)
    - 2.1.9. Example 9 (with PL/SQL block)
    - 2.1.10. Example 10 (with printf/scanf functions)
    - 2.1.11. Example 11 (with Prosto*C)
  - 2.2. Comparison with the other C++/database libraries
    - 2.2.1. OTL vs. SQLObjects (by Intelligent Objects)
    - 2.2.2. OTL vs. DBTools.h++ (by Rogue Wave)
  - 3. Library structure
    - 3.1. Host variable and array template classes
      - 3.1.1. Specialized host variable classes
      - 3.1.2. Specialized host array classes
    - 3.2. Oracle Call Interface "wrapper"
      - Class otl_exception
      - Class otl_object
      - Class otl_connect
      - Class otl_column_desc
      - Class otl_cursor
      - Class otl_select_cursor
      - Class otl_dynamic_variable
      - Class otl_err_info
    - 3.3. OTL stream interface
      - Class otl_select_stream
      - Class otl_out_stream
      - Class otl_inout_stream
      - Class otl_stream
      - Stream bind variable declarations
    - 3.4. Prosto*C
  - 4. Pro*OTL / Pre-Pro*C preprocessor (PPC)
    - 4.1. Getting started with PPC
      - Example in Pro*C
      - Example in C++
    - 4.2. Directives
      - 4.2.1. #sql-select
      - 4.2.2. #sql-out-stm
      - 4.2.3. #sql-plsql
      - 4.2.4. #sql-init-module
      - 4.2.5. #sql-init-main
      - 4.2.6. #sql-str-type
    - 4.3. Command line parameters
  - 5. Acknowledgements
  - 6. Bibliography
  - Appendix A. OTL class hierarchy
  - Appendix B. Error message list
  - Appendix C. OTL source code (otl.h)
  - Appendix D. Pro*OTL / Pre-Pro*C preprocessor's source code (ppc.C or ppc.cpp)
  - Appendix E. How to install the OTL library and Pro*OTL/Pre-Pro*C preprocessor
  - Appendix F. Modules, generated by PPC for the example from Chapter 4.
    - Pro*C module (ppc_test.pc)
    - C++ module (ppc_test.C)
    - Interface header file (ppc_test.h)
    - Command line for the example from Chapter 4.

  ## 1. Introduction

  This document provides information on the Oracle Call Interface Template Library (OTL). OTL is a new kind of C++ libraries, similar to the Standard Template Library. This kind of libraries is easy to use, since the user needs only to include C++ header files which contain template classes and functions. There is no need to link additional object libraries into C++ applications. The code, instantiated from template classes and inline functions, is efficient and reliable in terms of runtime performance and C++ strict type checking.

  OTL comprises of a set of template classes. The templates allow the user to create scalar host variables and host arrays, then dynamically bind the variables and arrays with SQL statements or PL/SQL blocks. OTL has a number of non-template classes which encapsulate the Oracle Call Interface (OCI) functions and provide transparent programming interface to them.

  OTL provides an optional exception handling mechanism, given in the form of the otl_exception class. This mechanism takes advantage of C++ exceptions compared to coding database applications in plain C. The user does not need to check out return codes after each function call. The code, instantiated from the OTL templates and inline functions, is much nicer and cleaner in comparison with the code generated by the Pro*C precompiler.

  In OTL, a concept of SQL streams is introduced. The SQL programming interface becomes unified and homogeneous.

  OTL has a simplified set of functions, called Prosto*C. It provides basic functions, such as connect/disconnect, printf/scanf, commit/rollback, etc. The word "Prosto*C" is originated in the author's native language: "prosto" means "simple". The idea here is to simplify the interface as much as possible, without losing the functionality.

  OTL is a database access function library. Also, this document describes the Pro*OTL / Pre-Pro*C preprocessor (PPC). PPC is a preprocessor which takes a directive file on input and generates OTL and Pro*C code on output. Besides, PPC produces a header file with prototypes of the generated functions and data structures, used by the functions. The generated functions are the "executable" form of the directives. The directives are more declarative and much more compact than the corresponding functions. The generated header file can be included in both C++ and plain C modules.

  OTL, as a library, and PPC, as a preprocessor, are intended to boost productivity of Oracle database developers who work with C++ as well as Pro*C. PPC is a pathway from traditional Pro*C to more advanced C++ database APIs.

  OTL and PPC compile with the following 32-bit C++ compilers:

  - IBM AIX, C++ (xlC), 1.x and higher
  - SunOS/Solaris, Sun C++, 4.x
  - Unix, GNU C++ (g++), 2.7.x
  - Windows 95, NT, Visual C++, 4.x, 32-bit

  The author is hoping to get feedback from potential users of OTL and that the OTL source code is clean enough to be ported across the 32-bit platforms, different from the mentioned above.

  Besides, the author's goal is to eventually find a sponsor to make this product commercial, in order to enhance, maintaion and support it on the regular basis.

  Despite the common opinion that Freeware products are not that good and badly supported, the author believes that OTL & PPC have production quality and can be used successfully.

  The OTL source code resides in Appendix C, PPC -- in Appendix D. The whole page may be downloaded, in order to get the source code. Examples may be clipped from the text, copied to separate files and used. Comments and questions would be appreciated very much. Email to skuchin@gmail.comskuchin@ispwest.com.

  ## 2. Getting started with OTL

  ## 2.1. Examples

  Let's assume you want to create a table, fill it out with a hundred records and then select some of them. This may be accomplished by the following code.

  ### 2.1.1. Example 1 (with ordinary, non-template host variables)

  #### Source code

      #include <iostream>
      using namespace std;

      #include <stdio.h>
      #include <otl.h>

      otl_connect db; // connect object

      const int BUF_SIZE=50; // host array size
      const int STR_SIZE=31; // string size

      void insert()
       // insert rows into table
      {
       float f1[BUF_SIZE];
        // float host array f1 without indicators
       char f2[BUF_SIZE][STR_SIZE];
        // string host array f2 without indicators
       otl_cursor o(db); // create cursor
       int n=0;

       o.parse("insert into test_tab values(:f1,:f2)");
         // parse sql statement
       o.bind_float(":f1",f1); // bind f1
       o.bind_cstring(":f2",(char*)f2,STR_SIZE); // bind f2

       for(int i=1;i<=100;++i){
        ++n;
        f1[n-1]=i; // fill out host array f1
        sprintf(f2[n-1],"Name%d",i); // fill out host array f2
        if(n==BUF_SIZE){ // execute the sql statement when buffer gets
                         // full
         o.exec(n);
         n=0;
        }
       }
       if(n>0) o.exec(n);
       db.commit(); // commit transaction
      } /* insert */

      void select()
      {
       float f1[BUF_SIZE];
         // float host array f1 without indicators
       char f2[BUF_SIZE][STR_SIZE];
         // string host array f2 without indicators
       int f;
         // host variable f without indicator

       otl_select_cursor i(db,BUF_SIZE);
         // create specialized select cursor

       i.parse("select * from test_tab where f1>=:f and f1<=:f*2");
       // parse select statement
       i.bind_float(1,f1); // bind f1 to column 1
       i.bind_cstring(2,(char*)f2,STR_SIZE); // bind f2 to column 2
       i.bind_int(":f",&f); // bind f

       f=8; // assign 8 to f

       while(i.next()){ // while not end-of-data
        int k=i.cur_row; // index of current row in host arrays
        cout<<"f1="<<f1[k]<<", f2="<<f2[k]<<endl;
       }

      } /* select */

      int main()
      {
       try{

        db.rlogon("scott/tiger"); // connect to Oracle

        otl_cursor::direct_exec
         (
          db,
          "drop table test_tab",
          otl_exception::disabled // disable OTL exceptions
         ); // drop table

        otl_cursor::direct_exec
         (
          db,
          "create table test_tab(f1 number, f2 varchar2(30))"
          );  // create table

        insert(); // insert records into table
        select(); // select records from table

       }

       catch(otl_exception& p){
        cerr<<p.msg<<endl; // print out error message
       }

       db.logoff(); //disconnect from Oracle

       return 0;
      } /* main */

  #### Output

      f1=8, f2=Name8
      f1=9, f2=Name9
      f1=10, f2=Name10
      f1=11, f2=Name11
      f1=12, f2=Name12
      f1=13, f2=Name13
      f1=14, f2=Name14
      f1=15, f2=Name15
      f1=16, f2=Name16

  ### 2.1.2. Example 2 (with template instantiated host variables)

  #### Source code

      #include <iostream.h>
      #include <stdio.h>
      #include <otl.h>

      otl_connect db; // connect object

      const int BUF_SIZE=50; // host array size
      const int STR_SIZE=31; // string size

      void insert()
      // insert rows into table
      {
       otl_float_array<BUF_SIZE> f1; // float host array f1

       otl_cstring_array<BUF_SIZE,STR_SIZE> f2; // C-string host array f2
       otl_cursor o(db); // create cursor
       int n=0;

       o.parse("insert into test_tab values(:f1,:f2)");
         // parse sql statement
       o.bind(":f1",f1); // bind f1
       o.bind(":f2",f2); // bind f2

       for(int i=1;i<=100;++i){
        ++n;
        f1.v[n-1]=i; // fill out host array f1
        sprintf(f2.v[n-1],"Name%d",i); // fill out host array f2
        if(n==BUF_SIZE){ // execute sql statement when buffer gets
                         // full
         o.exec(n);
         n=0;
        }
       }
       if(n>0) o.exec(n);
       db.commit(); // commit transaction
      } /* insert */

      void select()
      {
       otl_float_array<BUF_SIZE> f1; // float host array f1
       otl_cstring_array<BUF_SIZE,STR_SIZE> f2; // string host array f2
       otl_int f; // host variable f
       otl_select_cursor i(db,BUF_SIZE);
         // create specialized select cursor

       i.parse("select * from test_tab where f1>=:f and f1<=:f*2");
         // parse select statement

       i.bind(1,f1); // bind f1 to column 1
       i.bind(2,f2); // bind f2 to column 2
       i.bind(":f",f); // bind f. f is input variable

       f.v=8; // assign 8 to f

       while(i.next()){ // while not end-of-data
        int k=i.cur_row; // index of current row in host arrays
        cout<<"f1="<<f1.v[k]<<", f2="<<f2.v[k]<<endl;
       }

      } /* select */

      int main()
      {

       try{

        db.rlogon("scott/tiger"); // connect to Oracle

        otl_cursor::direct_exec
         (
          db,
          "drop table test_tab",
          otl_exception::disabled // disable OTL exceptions
         ); // drop table

        otl_cursor::direct_exec
         (
          db,
          "create table test_tab(f1 number, f2 varchar2(30))"
          );  // create table

        insert(); // insert records into table
        select(); // select records from table

       }

       catch(otl_exception& p){ // intercept OTL exceptions
        cerr<<p.msg<<endl; // print out error message
       }

       db.logoff(); // disconnect from Oracle

       return 0;

      } /* main */

  #### Output

      f1=8, f2=Name8
      f1=9, f2=Name9
      f1=10, f2=Name10
      f1=11, f2=Name11
      f1=12, f2=Name12
      f1=13, f2=Name13
      f1=14, f2=Name14
      f1=15, f2=Name15
      f1=16, f2=Name16

  ### 2.1.3. Example 3 (with the extended "parse" function)

  #### Source code

      #include <iostream.h>
      #include <stdio.h>
      #include <otl.h>

      otl_connect db; // connect object

      const int BUF_SIZE=50; // host array size
      const int STR_SIZE=31; // string size

      void insert()
      // insert rows into table
      {
       otl_float_array<BUF_SIZE> f1(":f1"); // float host array f1

       otl_cstring_array<BUF_SIZE,STR_SIZE> f2(":f2"); // C-string host array f2
       otl_cursor o(db); // create cursor
       int n=0;

       o.eparse("insert into test_tab values(:f1,:f2)",&f1,&f2,0);
         // parse sql statement and bind variables f1 and f2 with the
         // statement. the variable list is NULL terminated

       for(int i=1;i<=100;++i){
        ++n;
        f1.v[n-1]=i; // fill out host array f1
        sprintf(f2.v[n-1],"Name%d",i); // fill out host array f2
        if(n==BUF_SIZE){ // execute sql statement when buffer gets
                         // full
         o.exec(n);
         n=0;
        }
       }
       if(n>0) o.exec(n);
       db.commit(); // commit transaction
      } /* insert */

      void select()
      {
       otl_float_array<BUF_SIZE> f1; // float host array f1
       otl_cstring_array<BUF_SIZE,STR_SIZE> f2; // string host array f2
       otl_int f(":f"); // host variable f
       otl_select_cursor i(db,BUF_SIZE);
         // create specialized select cursor

       i.eparse("select * from test_tab where f1>=:f and f1<=:f*2",&f1,&f2,&f,0);
         // parse select statement, bind input variable f and output columns
         // f1, f2 with the statement. f1 is treated as column 1 in the
         // select list, f2 -- as column 2. The variable list is NULL terminated

       f.v=8; // assign 8 to f

       while(i.next()){ // while not end-of-data
        int k=i.cur_row; // index of current row in host arrays
        cout<<"f1="<<f1.v[k]<<", f2="<<f2.v[k]<<endl;
       }

      } /* select */

      int main()
      {

       try{

        db.rlogon("scott/tiger"); // connect to Oracle

        otl_cursor::direct_exec
         (
          db,
          "drop table test_tab",
          otl_exception::disabled // disable OTL exceptions
         ); // drop table

        otl_cursor::direct_exec
         (
          db,
          "create table test_tab(f1 number, f2 varchar2(30))"
          );  // create table

        insert(); // insert records into table
        select(); // select records from table

       }

       catch(otl_exception& p){ // intercept OTL exceptions
        cerr<<p.msg<<endl; // print out error message
       }

       db.logoff(); // disconnect from Oracle

       return 0;

      } /* main */

  #### Output

      f1=8, f2=Name8
      f1=9, f2=Name9
      f1=10, f2=Name10
      f1=11, f2=Name11
      f1=12, f2=Name12
      f1=13, f2=Name13
      f1=14, f2=Name14
      f1=15, f2=Name15
      f1=16, f2=Name16

  ### 2.1.4. Example 4 (with Oracle LONG columns)

  #### Source code

      #include <iostream.h>
      #include <stdio.h>
      #include <otl.h>

      otl_connect db; // connect object

      const int BUF_SIZE=50; // host array size
      const int LONG_STR_SIZE=2000; // LONG string size

      void insert()
      // insert rows into table
      {
       otl_float f1(":f1"); // float host variable f1
       otl_long_varchar<LONG_STR_SIZE> f2(":f2");
         // long varchar host variable f2

       otl_cursor o(db); // create cursor

       o.eparse("insert into test_tab values(:f1,:f2)",&f1,&f2,0);
         // parse sql statement and bind variables f1 and f2 with the
         // statement. the variable list is NULL terminated

       for(int i=1;i<=9;++i){
        f1.v=i; // assign host variable f1
        f2.set_len(5); // set the long_varchar string length
        f2[0]='N'; f2[1]='a';
        f2[2]='m'; f2[3]='e';
        f2[4]='0'+i;
        o.exec();
        db.commit(); // commit transaction
       }
      } /* insert */

      void select()
      {
       otl_float f1; // float f1
       otl_long_varchar<LONG_STR_SIZE> f2;
       otl_int f(":f"); // host variable f

       otl_select_cursor i(db);
         // create specialized select cursor

       i.eparse("select * from test_tab where f1>=:f and f1<=:f*2",&f1,&f2,&f,0);
         // parse select statement, bind input variable f and output columns
         // f1, f2 with the statement. f1 is treated as column 1 in the
         // select list, f2 -- as column 2. The variable list is NULL terminated

       f.v=4; // assign 4 to f

       while(i.next()){ // while not end-of-data
        cout<<"f1="<<f1.v<<", f2=";
        for(int j=0;j<f2.len();++j)
         cout<<f2[j]; // print out the long_varchar string
        cout<<endl;
       }

      } /* select */

      int main()
      {

       try{

        db.rlogon("scott/tiger"); // connect to Oracle

        otl_cursor::direct_exec
         (
          db,
          "drop table test_tab",
          otl_exception::disabled // disable OTL exceptions
         ); // drop table

        otl_cursor::direct_exec
         (
          db,
          "create table test_tab(f1 number, f2 long)"
          );  // create table

        insert(); // insert records into table
        select(); // select records from table

       }

       catch(otl_exception& p){ // intercept OTL exceptions
        cerr<<p.msg<<endl; // print out error message
       }

       db.logoff(); // disconnect from Oracle

       return 0;

      } /* main */

  #### Output

      f1=4, f2=Name4
      f1=5, f2=Name5
      f1=6, f2=Name6
      f1=7, f2=Name7
      f1=8, f2=Name8

  ### 2.1.5. Example 5 (with the otl_select_stream class)

  #### Source code

      #include <iostream.h>
      #include <stdio.h>
      #include <otl.h>

      otl_connect db; // connect object

      const int BUF_SIZE=50; // host array size
      const int STR_SIZE=31; // string size

      void insert()
      // insert rows into table
      {
       otl_float_array<BUF_SIZE> f1(":f1"); // float host array f1

       otl_cstring_array<BUF_SIZE,STR_SIZE> f2(":f2"); // C-string host array f2
       otl_cursor o(db); // create cursor
       int n=0;

       o.eparse("insert into test_tab values(:f1,:f2)",&f1,&f2,0);
         // parse sql statement and bind variables f1 and f2 with the
         // statement. the variable list is NULL terminated

       for(int i=1;i<=100;++i){
        ++n;
        f1.v[n-1]=i; // fill out host array f1
        sprintf(f2.v[n-1],"Name%d",i); // fill out host array f2
        if(n==BUF_SIZE){ // execute sql statement when buffer gets
                         // full
         o.exec(n);
         n=0;
        }
       }
       if(n>0) o.exec(n);
       db.commit(); // commit transaction
      } /* insert */

      void select()
      {
       otl_int f(":f"); // host variable f
       otl_select_stream i(db,
      		     "select * from test_tab where f1>=:f and f1<=:f*2",
      		     BUF_SIZE, // size of the buffer attached to the
      			       // stream
      		     &f, // input variable :f2
      		     0 // NULL terminator of the variable list
      		    );
         // create select stream

       int f1;
       char f2[STR_SIZE];

       f.v=8; // assign 8 to f

       i.rewind(); // rewind the stream: re-execute the statement and fetch
      	     // first portion of rows.

       while(!i.eof()){ // while not end-of-data
        i>>f1>>f2;
        cout<<"f1="<<f1<<", f2="<<f2<<endl;
       }

       f.v=4; // assign 4 to f

       i.rewind(); // rewind the stream: re-execute the statement and fetch
      	     // first portion of rows.

       while(!i.eof()){ // while not end-of-data
        i>>f1>>f2;
        cout<<"f1="<<f1<<", f2="<<f2<<endl;
       }

      } /* select */

      int main()
      {

       try{

        db.rlogon("scott/tiger"); // connect to Oracle

        otl_cursor::direct_exec
         (
          db,
          "drop table test_tab",
          otl_exception::disabled // disable OTL exceptions
         ); // drop table

        otl_cursor::direct_exec
         (
          db,
          "create table test_tab(f1 number, f2 varchar2(30))"
          );  // create table

        insert(); // insert records into table
        select(); // select records from table

       }

       catch(otl_exception& p){ // intercept OTL exceptions
        cerr<<p.msg<<endl; // print out error message
       }

       db.logoff(); // disconnect from Oracle

       return 0;

      } /* main */

  #### Output

      f1=8, f2=Name8
      f1=9, f2=Name9
      f1=10, f2=Name10
      f1=11, f2=Name11
      f1=12, f2=Name12
      f1=13, f2=Name13
      f1=14, f2=Name14
      f1=15, f2=Name15
      f1=16, f2=Name16
      f1=4, f2=Name4
      f1=5, f2=Name5
      f1=6, f2=Name6
      f1=7, f2=Name7
      f1=8, f2=Name8

  ### 2.1.6. Example 6 (with the otl_out_stream class)

  #### Source code

      #include <iostream.h>
      #include <stdio.h>
      #include <otl.h>

      otl_connect db; // connect object

      const int BUF_SIZE=50; // host array size
      const int STR_SIZE=31; // string size

      void insert()
      // insert rows into table
      {
       otl_float_array<BUF_SIZE> f1(":f1"); // float host array f1
       otl_cstring_array<BUF_SIZE,STR_SIZE> f2(":f2"); // C-string host array f2

       otl_out_stream o(db, // connect object
      		  "insert into test_tab values(:f1,:f2)", // SQL statement
      		  &f1, // bind variable :f1
      		  &f2, // bind variable :f2
      		  0 // end of variable list
      		 );

       char tmp[31];

       for(int i=1;i<=100;++i){
        sprintf(tmp,"Name%d",i);
        o<<(float)i<<tmp;
       }
      } /* insert */

      void select()
      {
       otl_int f(":f"); // host variable f
       otl_select_stream i(db,
      		     "select * from test_tab where f1>=:f and f1<=:f*2",
      		     BUF_SIZE, // size of the buffer attached to the
      			       // stream
      		     &f, // input variable :f2
      		     0 // NULL terminator of the variable list
      		    );
         // create select stream

       int f1;
       char f2[STR_SIZE];

       f.v=8; // assign 8 to f

       i.rewind(); // rewind the stream: re-execute the statement and fetch
      	     // first portion of rows.

       while(!i.eof()){ // while not end-of-data
        i>>f1>>f2;
        cout<<"f1="<<f1<<", f2="<<f2<<endl;
       }

       f.v=4; // assign 4 to f

       i.rewind(); // rewind the stream: re-execute the statement and fetch
      	     // first portion of rows.

       while(!i.eof()){ // while not end-of-data
        i>>f1>>f2;
        cout<<"f1="<<f1<<", f2="<<f2<<endl;
       }

      } /* select */

      int main()
      {

       try{

        db.rlogon("scott/tiger"); // connect to Oracle

        otl_cursor::direct_exec
         (
          db,
          "drop table test_tab",
          otl_exception::disabled // disable OTL exceptions
         ); // drop table

        otl_cursor::direct_exec
         (
          db,
          "create table test_tab(f1 number, f2 varchar2(30))"
          );  // create table

        insert(); // insert records into table
        select(); // select records from table

       }

       catch(otl_exception& p){ // intercept OTL exceptions
        cerr<<p.msg<<endl; // print out error message
       }

       db.logoff(); // disconnect from Oracle

       return 0;

      } /* main */

  #### Output

      f1=8, f2=Name8
      f1=9, f2=Name9
      f1=10, f2=Name10
      f1=11, f2=Name11
      f1=12, f2=Name12
      f1=13, f2=Name13
      f1=14, f2=Name14
      f1=15, f2=Name15
      f1=16, f2=Name16
      f1=4, f2=Name4
      f1=5, f2=Name5
      f1=6, f2=Name6
      f1=7, f2=Name7
      f1=8, f2=Name8

  ### 2.1.7. Example 7 (with the otl_inout_stream class)

  #### Source code

      #include <iostream.h>
      #include <stdio.h>
      #include <otl.h>

      otl_connect db; // connect object

      void insert()
      // insert rows into table
      {
       otl_inout_stream o(50, // buffer size
      		    "insert into test_tab values(:f1<float>,:f2<char[31]>)",
      		       // SQL statement
      		    db // connect object
      		   );
       char tmp[32];

       for(int i=1;i<=100;++i){
        sprintf(tmp,"Name%d",i);
        o<<(float)i<<tmp;
       }
      } /* insert */

      void select()
      {
       otl_select_stream i(50, // buffer size
      		     "select * from test_tab where f1>=:f<int> and f1<=:f*2",
      		        // SELECT statement
      		     db // connect object
      		    );
         // create select stream

       int f1;
       char f2[31];

       i<<8; // assigning :f = 8
         // SELECT automatically executes when all input variables are
         // assigned. First portion of out rows is fetched to the buffer

       while(!i.eof()){ // while not end-of-data
        i>>f1>>f2;
        cout<<"f1="<<f1<<", f2="<<f2<<endl;
       }

       i<<4; // assigning :f = 4
         // SELECT automatically re-executes when all input variables are
         // assigned. First portion of out rows is fetched to the buffer

       while(!i.eof()){ // while not end-of-data
        i>>f1>>f2;
        cout<<"f1="<<f1<<", f2="<<f2<<endl;
       }

      } /* select */

      int main()
      {

       try{

        db.rlogon("scott/tiger"); // connect to Oracle

        otl_cursor::direct_exec
         (
          db,
          "drop table test_tab",
          otl_exception::disabled // disable OTL exceptions
         ); // drop table

        otl_cursor::direct_exec
         (
          db,
          "create table test_tab(f1 number, f2 varchar2(30))"
          );  // create table

        insert(); // insert records into table
        select(); // select records from table

       }

       catch(otl_exception& p){ // intercept OTL exceptions
        cerr<<p.msg<<endl; // print out error message
       }

       db.logoff(); // disconnect from Oracle

       return 0;

      } /* main */

  #### Output

      f1=8, f2=Name8
      f1=9, f2=Name9
      f1=10, f2=Name10
      f1=11, f2=Name11
      f1=12, f2=Name12
      f1=13, f2=Name13
      f1=14, f2=Name14
      f1=15, f2=Name15
      f1=16, f2=Name16
      f1=4, f2=Name4
      f1=5, f2=Name5
      f1=6, f2=Name6
      f1=7, f2=Name7
      f1=8, f2=Name8

  ### 2.1.8. Example 8 (with the otl_stream class)

  #### Source code

      #include <iostream.h>
      #include <stdio.h>
      #include <otl.h>

      otl_connect db; // connect object

      void insert()
      // insert rows into table
      {
       otl_stream o(50, // buffer size
      	      "insert into test_tab values(:f1<float>,:f2<char[31]>)",
      	         // SQL statement
      	      db // connect object
      	     );
       char tmp[32];

       for(int i=1;i<=100;++i){
        sprintf(tmp,"Name%d",i);
        o<<(float)i<<tmp;
       }
      } /* insert */

      void select()
      {
       otl_stream i(50, // buffer size
      	      "select * from test_tab where f1>=:f<int> and f1<=:f*2",
      	         // SELECT statement
      	      db // connect object
      	     );
         // create select stream

       int f1;
       char f2[31];

       i<<8; // assigning :f = 8
         // SELECT automatically executes when all input variables are
         // assigned. First portion of out rows is fetched to the buffer

       while(!i.eof()){ // while not end-of-data
        i>>f1>>f2;
        cout<<"f1="<<f1<<", f2="<<f2<<endl;
       }

       i<<4; // assigning :f = 4
         // SELECT automatically re-executes when all input variables are
         // assigned. First portion of out rows is fetched to the buffer

       while(!i.eof()){ // while not end-of-data
        i>>f1>>f2;
        cout<<"f1="<<f1<<", f2="<<f2<<endl;
       }

      } /* select */

      int main()
      {

       try{

        db.rlogon("scott/tiger"); // connect to Oracle

        otl_cursor::direct_exec
         (
          db,
          "drop table test_tab",
          otl_exception::disabled // disable OTL exceptions
         ); // drop table

        otl_cursor::direct_exec
         (
          db,
          "create table test_tab(f1 number, f2 varchar2(30))"
          );  // create table

        insert(); // insert records into table
        select(); // select records from table

       }

       catch(otl_exception& p){ // intercept OTL exceptions
        cerr<<p.msg<<endl; // print out error message
       }

       db.logoff(); // disconnect from Oracle

       return 0;

      } /* main */

  #### Output

      f1=8, f2=Name8
      f1=9, f2=Name9
      f1=10, f2=Name10
      f1=11, f2=Name11
      f1=12, f2=Name12
      f1=13, f2=Name13
      f1=14, f2=Name14
      f1=15, f2=Name15
      f1=16, f2=Name16
      f1=4, f2=Name4
      f1=5, f2=Name5
      f1=6, f2=Name6
      f1=7, f2=Name7
      f1=8, f2=Name8

  ### 2.1.9. Example 9 (with PL/SQL block)

  #### Source code

      #include <iostream.h>
      #include <stdio.h>
      #include <otl.h>

      otl_connect db; // connect object

      void plsql()
      // invoking PL/SQL block
      {
       otl_stream o(5, // buffer size
      	      "begin "
      	      " :A<int,inout> := :A+1; "
      	      " :B<char[31],out> := :C<char[31],in>; "
      	      "end;",
      	         // PL/SQL block
      	      db // connect object
      	     );
       o<<1<<"Test String1"; // assigning :A = 1, :C = "Test String1"
       o<<2<<"Test String2"; // assigning :A = 2, :C = "Test String2"
       o<<3<<"Test String3"; // assigning :A = 3, :C = "Test String3"

       o.flush(); // executing PL/SQL block 3 times

       int a;
       char b[32];

       while(!o.eof()){ // not end-of-data
        o>>a>>b;
        cout<<"A="<<a<<", B="<<b<<endl;
       }

      } /* plsql */

      int main()
      {
       try{
        db.rlogon("scott/tiger"); // connect to Oracle
        plsql(); // invoking PL/SQL block
       }
       catch(otl_exception& p){ // intercept OTL exceptions
        cerr<<p.msg<<endl; // print out error message
       }
       db.logoff(); // disconnect from Oracle
       return 0;
      } /* main */

  #### Output

      A=2, B=Test String1
      A=3, B=Test String2
      A=4, B=Test String3

  ### 2.1.10. Example 10 (with printf/scanf functions)

  #### Source code

      #include <iostream.h>
      #include <stdio.h>
      #include <otl.h>

      otl_connect db; // connect object

      void insert()
      // insert rows into table
      {
       otl_stream o(50, // buffer size
      	      "insert into test_tab values(:f1<int>,:f2<char[31]>)",
      	         // SQL statement
      	      db // connect object
      	     );
       char tmp[32];

       for(int i=1;i<=100;++i){
        sprintf(tmp,"Name%d",i);
        o.printf("%d %s",i,tmp); // write one row into stream
       }
      } /* insert */

      void select()
      {
       otl_stream i(50, // buffer size
      	      "select * from test_tab where f1>=:f<int> and f1<=:f*2",
      	         // SELECT statement
      	      db // connect object
      	     );
         // create select stream

       int f1;
       char f2[31];

       i<<8; // assigning :f = 8
         // SELECT automatically executes when all input variables are
         // assigned. First portion of out rows is fetched to the buffer

       while(!i.eof()){ // while not end-of-data
        i.scanf("%d %s",&f1,f2); // read one row from stream
        cout<<"f1="<<f1<<", f2="<<f2<<endl;
       }

       i<<4; // assigning :f = 4
         // SELECT automatically re-executes when all input variables are
         // assigned. First portion of out rows is fetched to the buffer

       while(!i.eof()){ // while not end-of-data
        i.scanf("%d %s",&f1,f2); // read one row from stream
        cout<<"f1="<<f1<<", f2="<<f2<<endl;
       }

      } /* select */

      int main()
      {

       try{

        db.rlogon("scott/tiger"); // connect to Oracle

        otl_cursor::direct_exec
         (
          db,
          "drop table test_tab",
          otl_exception::disabled // disable OTL exceptions
         ); // drop table

        otl_cursor::direct_exec
         (
          db,
          "create table test_tab(f1 number, f2 varchar2(30))"
          );  // create table

        insert(); // insert records into table
        select(); // select records from table

       }

       catch(otl_exception& p){ // intercept OTL exceptions
        cerr<<p.msg<<endl; // print out error message
       }

       db.logoff(); // disconnect from Oracle

       return 0;

      } /* main */

  #### Output

      f1=8, f2=Name8
      f1=9, f2=Name9
      f1=10, f2=Name10
      f1=11, f2=Name11
      f1=12, f2=Name12
      f1=13, f2=Name13
      f1=14, f2=Name14
      f1=15, f2=Name15
      f1=16, f2=Name16
      f1=4, f2=Name4
      f1=5, f2=Name5
      f1=6, f2=Name6
      f1=7, f2=Name7
      f1=8, f2=Name8

  ### 2.1.11. Example 11 (with Prosto*C)

  #### Source code

      #include <iostream.h>
      #include <stdlib.h>
      #include <stdio.h>
      #include <otl.h>

      otl_connect* db=0; // pointer to connect object

      void my_handler(char* msg, int code)
      // my error handler
      {
       cout<<msg<<endl;
       if(db)db->rollback(); // on error, roll back transaction if already
                             // connected
       exit(1); // exit from the program
      }

      void insert()
      // insert rows into table
      {

       otl_stream* o=otl_stream_open // open OTL stream
        (
         db, // connect object
         "insert into test_tab values(:f1<int>,:f2<char[31]>)", // SQL statement
         50 // buffer size
        );

       char tmp[32];

       for(int i=1;i<=100;++i){
        sprintf(tmp,"Name%d",i);
        otl_printf(o,"%d %s",i,tmp); // write one row into the stream
       }

       otl_stream_close(o); // Close OTL stream

      } /* insert */

      void select()
      {
       otl_stream* i=otl_stream_open // Open OTL stream
        (
         db, // connect object
         "select * from test_tab where f1>=:f<int> and f1<=:f*2",
           // SELECT statement
         50 // buffer size
        );  // create select stream

       int f1;
       char f2[31];

       otl_printf(i,"%d",8); // assigning :f = 8
         // SELECT automatically executes when all input variables are
         // assigned. First portion of out rows is fetched to the buffer

       while(!otl_eof(i)){ // while not end-of-data
        otl_scanf(i,"%d %s",&f1,f2); // fetch a row from the stream
        cout<<"f1="<<f1<<", f2="<<f2<<endl;
       }

       otl_printf(i,"%d",4); // assigning :f = 4
         // SELECT automatically executes when all input variables are
         // assigned. First portion of out rows is fetched to the buffer

       while(!otl_eof(i)){ // while not end-of-data
        otl_scanf(i,"%d %s",&f1,f2); // fetch a row from the stream
        cout<<"f1="<<f1<<", f2="<<f2<<endl;
       }

       otl_stream_close(i); // Close OTL stream

      } /* select */

      int main()
      {

      // connect to Oracle
       db=otl_logon("scott/tiger",
      	      my_handler // attaching error handler to the connect object
      	     );

        otl_exec(
      	   db,
      	   "drop table test_tab",
      	   1 // ignore error
      	  ); // drop table

        otl_exec(
      	   db,
      	   "create table  test_tab(f1 number, f2 varchar2(30))"
      	  );  // create table

        insert(); // insert records into table
        select(); // select records from table

       otl_logoff(db); // disconnect from Oracle
       return 0;

      } /* main */

  #### Output

      f1=8, f2=Name8
      f1=9, f2=Name9
      f1=10, f2=Name10
      f1=11, f2=Name11
      f1=12, f2=Name12
      f1=13, f2=Name13
      f1=14, f2=Name14
      f1=15, f2=Name15
      f1=16, f2=Name16
      f1=4, f2=Name4
      f1=5, f2=Name5
      f1=6, f2=Name6
      f1=7, f2=Name7
      f1=8, f2=Name8

  ### 2.2. Comparison with the other C++/database libraries

  In this section, OTL is compared with the other C++/database libraries, namely:

  - SQLObjects (by Intelligent Objects)
  - DBTools.h++ (by Rogue Wave)

  SQLObjects and DBTools.h++ are both commercial, multi-platform, multi-database C++ database access libraries. It is hard to perform good comparison between them and OTL, since they provide similar functionality but belong to the different category. Nevertheless, OTL has advantage of being well suited for a range of Oracle applications and platforms. It is portable and easy-to-maintain within this range.

  Multi-database C++ libraries lose the sense of "closeness" with the database. They, most often, do not have so called "native access" to the database. All their multi-database functionality is based upon the monster called ODBC. Oracle has many advantages compared to the other databases, such as Sybase, Informix, etc.

  It is not such a bad idea to harness most of the power Oracle provides:

  - PL/SQL functions/procedures/packages
  - Other Oracle SQL extentions
  - Oracle Array Interface

  Typical problem of accessing Oracle via ODBC is that no ODBC driver provides transparent access to PL/SQL, especially with OUT or IN OUT parameters.

  Often, it is critical to have fast interface. If the interface is fast, then, most probably, it is not portable accross database servers from different vendors. If the interface is portable and unified, then it is awfully slow.

  Considering the fact that a big segment of the database market is covered by Oracle, it makes a lot of sense to develop an interface unified for Oracle across most of the platforms on which Oracle runs.

  Let's consider a typical C++ interface to a database. It provides a set of functions like:

  - Connect/Disconnect
  - Open/Close cursor
  - Parse/Execute SQL statement
  - Bind host variables
  - Fetch rows from a SELECT statement
  - Get and process database errors

  Usually, such a layer of code is called access library. Some vendors go far beyond that and provide factories, based upon such access libraries. The factory is a GUI based front end which generates the access library calls. Does it really improve the developer's productivity? Sometimes, yes.

  In contrast, OTL provides a multilayer class hierarchy and allows the database programmer to decide which layer is more appropriate for each case. The main advantage of OTL is that it provides the code layers both as low as the Oracle Call Interface and as high as abstract and unified SQL stream interface.

  Therefore, OTL, in a sense, fills out the gap between the access layer of code and the factory. The database programmer does not have to learn tons of new front ends. He has to deals only with the same C++ code and the database.

  It is not hard to imagine that it is rather easy to build up a factory on top of OTL, as access library. The code, generated by the factory would occupy less memory and would be more readable than the code, generated into the calls of a low level access library. The Pro*OTL / Pre-Pro*C preprocessor may be the first step toward such a factory.

  #### 2.2.1. OTL vs. SQLObjects (by Intelligent Objects)

  #### Sample Program in SQLObjects

      Step 1 char szName[31]="";
      Step 2 char szAddress[31]="";

      Step 3 SQLObject *pDb=new SQLSybase();
                // Instantiate SQLObject object

      Step 4 pDb->sqlLogin();
               // Log into the database

      Step 5 pDb->sqlExec("SELECT name, address FROM customer");
              // execute the SQL command

      Step 6 pDb->sqlBind(1,bindZSTRING,31,szName);
      Step 7 pDb->sqlBind(2,bindZSTRING,31,szAddress);
              // Binds the host variables to the column results

      Step 8 while(pDb->sqlFetch() == TRUE)
               {
                 cout << szName << " " << szAddress << "\n";
               }
              // Fetches the records from the data source

      Step 9 pDb->sqlDisconnect();
              // Disconnects from the data source

      Step 10 delete pDb; // Deletes the object

  #### The Same Sample Program in OTL streams

  "As high as abstract and unified SQL stream interface":

      Step 1 char szName[31];
      Step 2 char szAddress[31];

      Step 3 otl_stream  s(20, // stream buffer size (in rows)
                           "SELECT name, address FROM customer",
      		        // SELECT statement
      		     db // connect object
                          ;

      Step 4,5,6,7
        // no explicit binding, no explicit execution

      Step 8 while(!s.eof())
               {
      	   s >> szName >> szAddress; // fetch one row
                 cout << szName << " " << szAddress << endl;
               }
              // Fetches the records from the data source

      Step 9, 10
         // Automatic destructor will do the job

  #### The Same Sample Program in OTL lower code layer

  "As low as the Oracle Call Interface interface":

      Step 1 char szName[31];
      Step 2 char szAddress[31];

      Step 3 otl_select_cursor s(db); // db -- connect object
                // Instantiate Cursor object

      Step 4 // Everything is done in the constructor

      Step 5 s.parse("SELECT name, address FROM customer");
              // Parse the SQL command

      Step 6 s.bind_cstring(1,szName,30);
      Step 7 s.bind_cstring(2,sAddress,30);
              // Binds the host variables to the column results

      Step 8 while(s.next())
               {
                 cout << szName << " " << szAddress << endl;
               }
              // Fetches the records from the data source

      Step 9, 10
              // Automatic constructor will do the job

  #### 2.2.2. OTL vs. DBTools.h++ (by Rogue Wave)

  #### Sample Program in DBTools.h++

      // This example establishes a connection to a SYBASE database, creates
      // a table on the server, and uses the reader to read back the values.
      // The values are then stored on a Memory Table and accessed row by
      // row.  This example can run on both WINDOWS and SUN/SOLARIS or
      // SUNOS.

      //#define UNIX   // To run examples on Unix SunOS/Solaris

      #define WINDOWS         // To run example on WINDOWS
      #include <rw/db/db.h>
      #include <rw/db/dbmgr.h>

      #ifdef WINDOWS
      #include <windows.h>
      #endif

      void write(const RWCString&  w)
      {
      #ifdef WINDOWS
        MessageBox(0,w.data(),"DBTOOLS_BOX",MB_OK);
      #elif defined(UNIX)
        cout << w.data() << endl;
      #endif
      }

      void errorHandler(const RWDBStatus& s)
      {
        char buf[1040];
        sprintf(buf,"Message:  %s ",s.message().data());
        write(RWCString(buf));
      }

      #ifdef WINDOWS
      #pragma argsused
      int PASCAL WinMain(HINSTANCE , HINSTANCE, LPSTR, int )
      {
        RWCString serverType("bdbsdld.dll");
      #elif defined(UNIX)
      int main()
      {
        RWCString serverType("SYBASE");
      #endif

        // Set the Error Handler
        RWDBManager::setErrorHandler(errorHandler);
        // Establish Connection
        RWDBDatabase adb = RWDBManager::database(serverType,
                     "SYBASE100", // to the database.
                     "henri",
                     "meli12",
                     "SUPPORT");
        if(!adb.isValid())
          write("Hmmm !!!  Cannot Connect. Exit...");
        else {
         RWDBConnection conn=adb.connection();
         if(adb.table("TABLE1").exists())
           adb.table("TABLE1").drop();

        RWDBSchema mySchema;
        mySchema.appendColumn("id_num",RWDBValue::Int);
        mySchema.appendColumn("id_type", RWDBValue::String, 15);
        mySchema.appendColumn("input_date", RWDBValue::DateTime);
        mySchema.appendColumn("price", RWDBValue::Float);
        adb.createTable("TABLE1",mySchema, conn);

        RWDBTable tab=adb.table("TABLE1");
        if(tab.exists())
          write("Table 'TABLE1' successfully created.");

        // Insert about 10 Values
        int numberOfEntries=5;
        char buffer[1040];
        RWDBDateTime date;
        float price=1.5f;

        RWDBInserter ins = tab.inserter();
        for(int i=0; i<numberOfEntries; i++)
        {
           sprintf(buffer,"Item%d",i);
           date.addDays(1);
           ins << i << RWCString(buffer) << date << << i*price;
           ins.execute(conn);
        }

        // Select all of items
        RWDBSelector sel = adb.selector();
        sel << tab;
        RWDBResult  res = sel.execute(conn);
        RWDBReader  rdr=res.table().reader(conn);
        RWDBMemTable memTab(rdr,numberOfEntries);
        for(i=0;i<memTab.entries(); i++)
        {
          sprintf(buffer,"ROW[%d]  ID_NUMBER %d  ID_TYPE %s \n
           INPUT_DATE %s INPUT_PRICE %f\n", i,
                (memTab[i][0]).asInt(),
                (memTab[i][1]).asString().data(),
                (memTab[i][2]).asString().data(),
                (memTab[i][3]).asFloat());
          write(RWCString(buffer));
        }
        return 0;
       }
       return 0;
      }

  #### The Same Sample Program in OTL

      // This example establishes a connection to an Oracle database, creates
      // a table on the server, and reads the values back.

      #include <stdio.h>
      #include <iostream.h>
      #include <otl.h>

      otl_connect db; // connect object
      void write(const char* w)
      {
       cout << w << endl; // Write a message
      }

      int main()
      {
       try{
        db.rlogon("scott/tiger"); // connect to Oracle
        otl_cursor::direct_exec
         (
          db,
          "drop table TABLE1",
          otl_exception::disabled // disable OTL exceptions
         ); // drop table
        otl_cursor::direct_exec
         (
          db,
          "create table TABLE1"
          "( "
          " id_num     number(8),"
          " id_type    varchar2(15),"
          " input_date date,"
          " price      number"
          ")"
          );  // create table
        write("Table 'TABLE1' successfully created.");

        // Insert about 10 Values
        {
         int numberOfEntries=5;
         char buffer[1040];
         int date=0;
         float price=1.5f;

         otl_stream ins(50, // buffer size (in rows)
      		  "insert into TABLE1 values("
      		  " :id_num<int>,"
      		  " :id_type<char[64]>,"
      		  " sysdate+:input_date<int>,"
      		  " :price<float>"
      		  ")",
      		  db // connect object
      		 );
         for(int i=0; i<numberOfEntries; i++){
          sprintf(buffer,"Item%d",i);
          ++date;
          ins << i << buffer << date << (float) i*price;
            // insert one row into the table
         }

        }

        // Select all of items
        {
         char buffer[1040];
         int count=0;
         int id_num;
         char id_type[64];
         char input_date[32];
         float price;

         otl_stream sel(10, // buffer size (in rows)
      		  "select * TABLE1"
      		  db // connect object
      		 );

         while(!sel.eof()){
          sel >> id_num >> id_type >> input_date >> price;
            // get one row
          sprintf(buffer,
      	    "ROW[%d]  ID_NUMBER %d  ID_TYPE %s \n"
      	    "INPUT_DATE %s INPUT_PRICE %f\n",
      	    ++count,
      	    id_num,
      	    id_type,
      	    input_date,
      	    price
      	   );
         }
        }

       }
       catch(otl_exception& p){ // intercept OTL exceptions
        write(p.msg);
       }

       db.logoff(); // disconnect from Oracle
       return 0;
      }

  ## 3. Library structure

  OTL falls into two parts: template classes to create host variables and arrays and non-template classes to provide programming interface to the Oracle Call Interface.

  ### 3.1. Host variable and array template classes

  Two types of host objects are distinguished: scalar variables and arrays. OTL has two generic template classes (otl_variable and otl_array) from which the following specialized classes are derived:

  - Numerical data types
    - otl_double, otl_double_array
    - otl_signed_char, otl_signed_char_array
    - otl_short_int, otl_short_int_array
    - otl_int, otl_int_array
    - otl_long_int, otl_long_int_array
    - otl_unsigned, otl_unsigned_array
  - String data types
    - otl_ctring, otl_cstring_array
    - otl_varchar2, otl_varchar2_array
    - otl_long, otl_long_array
    - otl_varchar, otl_varchar_array
    - otl_varraw, otl_varraw_array
    - otl_raw, otl_raw_array
    - otl_char, otl_char_array
    - otl_charz, otl_charz_array
  - Data types for Oracle LONG and LONG RAW columns
    - otl_long_varchar
    - otl_long_varraw
  - Oracle internal data types (DATE, ROWID, VARNUM and NUMBER)
    - otl_date, otl_date_array
    - otl_rowid, otl_rowid_array
    - otl_varnum, otl_varnum_array;
    - otl_number, otl_number_array

  The otl_variable and otl_array classes define the following kinds of buffers which are necessary for handling host variables:

  - value buffer (data member v)
  - indicator buffer (data member ind)
  - returned length buffer (data member rlen)
  - returned code buffer (data member rcode)

  These data members are defined to be public, so the user has access to them and may freely assign and change their values.

  The otl_variable and otl_array classes have a common parent (otl_generic_variable) which contains information about the buffer addresses, dimensions and data type codes. When a host variable or array is constructed from an instantiated template, constructors of the corresponding template classes initialize the data members of the otl_generic_variable class. otl_cursor has a couple of the bind functions which have the second parameter of the otl_generic_variable type. Any template instantiated variables or arrays may be substituted as actual parameters into those bind finctions.

  ### Class otl_generic_variable

      class otl_generic_variable{
      public:

  - Default constructor

        otl_generic_variable();

  - Destructor

        virtual ~otl_generic_variable();

  - Assigning a name to the variable

        void copy_name(const char* aname);

  - Assigning a position (number) to the select list item (column)

        void copy_pos(const int apos);

  - For input variable/array
    - Set variable's value as NULL

          virtual void set_null(int ndx=0);

    - Set variable's buffer length

          virtual void set_len(int len, int ndx=0);

    - Set variable's value as NOT NULL

          virtual void set_not_null(int ndx=0);

  - For output variable/array
    - Check if variable's value is NULL

          virtual int is_null(int ndx=0);

    - Check if variable's value has been fetched OK

          virtual int is_success(int ndx=0);

    - Check if variable's value is truncated

          virtual int is_truncated(int ndx=0);

    - Get pointer to variable's buffer

          virtual void* val(int ndx=0);

  - Only for output arrays, defined in SELECT statement
    - Check if during fetch conversion error occurred

          virtual int is_invalid_conversion(int ndx=0);

    - Check if during fetch real overflow occurred

          virtual int is_real_overflow(int ndx=0);

    - Check if variable has unsupported data type

          virtual int is_unsupported_datatype(int ndx=0);

    protected:

- pointer to buffer

      ub1* p_v;

- pointer to indicator variable/array

      sb2* p_ind;

- pointer to column's returned length

      ub2* p_rlen;

- poinetr column's returned code

      ub2* p_rcode;

- external data type's code of the host variable/array

      int ftype;

- array element/variable size

      int elem_size;

- host array size (=1 in case of scalar host variable)

      int array_size;

- variable name;

      char* name;

- select list item position

      int pos;

    };

### Class otl_variable

This is the OTL template variable class. It is the base class for constructing specialized host variable classes.

    template <class T, int atype>
    class otl_variable: public otl_generic_variable{
    public:

- Default constructor

      otl_variable();

- Construct variable by the name of aname, e.g. ":F1"

      otl_variable(const char* aname);

- Construct variable/column, with its further use in a select list in the column_num position.

       otl_variable(const int column_num);

- host varibale of data type T

      T v;

- indicator

      sb2 ind;

- returned length

      ub2 rlen;

- returned code

      ub2 rcode;

    };

### Class otl_array

This is the OTL template host array class. It is the base class for constructing specialized template array classes.

    template <class T, int atype, short size>
    class otl_array: public otl_generic_variable{
    public:

- Default constructor

      otl_array();

- Construct array by the name of aname, e.g. ":F1"

      otl_array(const char* aname);

- Construct array-column, with its further use in a select list in the column_num position.

      otl_array(const int column_num);

- host array

      T v[size];

- indicator array

      sb2 ind[size];

- returned length array

      ub2 rlen[size];

- returned code array

      ub2 rcode[size];

    };

### 3.1.1. Specialized host variable classes

- Numerical data types
  - otl_double, example: otl_double f1,f2;
  - otl_float, example: otl_float f1,f2;
  - otl_signed_char, example: otl_signed_char f1,f2;
  - otl_short_int, example: otl_short_int f1,f2;
  - otl_long_int, example: otl_long_int f1,f2;
  - otl_unsigned, example: otl_unsigned f1,f2;

- String data types
  - otl_cstring example: otl_ctring<STR_SIZE> f1,f2;
  - otl_varchar2 example: otl_varchar2<STR_SIZE> f1,f2;
  - otl_long example: otl_long<STR_SIZE> f1,f2;
  - otl_varchar, example: otl_varchar<STR_SIZE> f1,f2;
  - otl_varraw, example: otl_varraw<STR_SIZE> f1,f2;
  - otl_raw, example: otl_raw<STR_SIZE> f1,f2;
  - otl_char, example: otl_char<STR_SIZE> f1,f2;
  - otl_charz, example: otl_charz<STR_SIZE> f1,f2;

- Data types for Oracle LONG and LONG RAW columns

  These two classes are used for reading and writing objects of the Oracle LONG and LONG RAW data types. The classes define operator[] to access elements of the data array, the set_len() function to set up string length on input and the len() function to get string length on output.

  - otl_long_varchar, example: otl_long_varchar<STR_SIZE> f1,f2;
  - otl_long_varraw, example: otl_long_varraw<STR_SIZE> f1,f2;

- Oracle internal data types (DATE, ROWID, VARNUM, NUMBER)
  - otl_date, example: otl_date f1,f2;
  - otl_rowid, example: otl_rowid f1,f2;
  - otl_varnum, example: otl_varnum f1,f2;
  - otl_number, example: otl_number f1,f2;

### 3.1.2. Specialized host array classes

- Numerical data types
  - otl_double_array, example: otl_double_array<ARR_SIZE> f1,f2;
  - otl_float_array, example: otl_float_array<ARR_SIZE> f1,f2;
  - otl_signed_char_array, example: otl_signed_char_array<ARR_SIZE> f1,f2;
  - otl_short_int_array, example: otl_short_int_array<ARR_SIZE> f1,f2;
  - otl_int_array, example: otl_int_array<ARR_SIZE> f1,f2;
  - otl_long_int_array, example: otl_long_int_array<ARR_SIZE> f1,f2;
  - otl_unsigned_array, example: otl_unsigned_array<ARR_SIZE> f1,f2;

- String data types
  - otl_ctring_array, example: otl_ctring_array<ARR_SIZE,STR_SIZE> f1,f2;
  - otl_varchar2_array, example: otl_varchar2_array<ARR_SIZE,STR_SIZE> f1,f2;
  - otl_long_array, example: otl_long_array<ARR_SIZE,STR_SIZE> f1,f2;
  - otl_varchar_array, example: otl_varchar_array<ARR_SIZE,STR_SIZE> f1,f2;
  - otl_varraw_array, example: otl_varraw_array<ARR_SIZE,STR_SIZE> f1,f2;
  - otl_raw_array, example: otl_raw_array<ARR_SIZE,STR_SIZE> f1,f2;
  - otl_char_array, example: otl_char_array<ARR_SIZE,STR_SIZE> f1,f2;
  - otl_charz_array, example: otl_charz_array<ARR_SIZE,STR_SIZE> f1,f2;

- Oracle internal data types (DATE, ROWID, VARNUM, NUMBER)
  - otl_date_array, example: otl_date_array<ARR_SIZE> f1,f2;
  - otl_rowid_array, example: otl_rowid_array<ARR_SIZE> f1,f2;
  - otl_varnum_array, example: otl_varnum_array<ARR_SIZE> f1,f2;
  - otl_number_array, example: otl_number_array<ARR_SIZE> f1,f2;

## 3.2. Oracle Call Interface "wrapper"

### Class otl_exception

This is the OTL exception class. Exceptions of this type are raised by the library functions (default mode), unless it is prohibited explicitly in the otl_connect or otl_cursor class constructors. In case of disabled exceptions OTL functions return codes and it is the user's responsibility to check out the codes and handle errors. The main advantage of using this exception handling mechanism is that exceptions can be processed in one catch block, instead of checking return codes from every library function call.

    class otl_exception{
    public:

- This "enum" defines two constants which are used in constructors of the otl_connect, otl_cursor and otl_select_cursor classes.

      enum{ disabled, enabled };

- Create exception from LDA

      otl_exception(Lda_Def& lda);

- Create exception from amsg and acode

      otl_exception(const char* amsg,const int acode);

- Copy constructor

      otl_exception(const otl_exception& p);

- Default constructor

       otl_exception();

- error message buffer

      unsigned char msg[1000];

- error code

      int code;

    };

### Class otl_object

This class is a parent of the otl_cursor and otl_connect classes. Its children inherit the following two properties:

- "connected" flag
- "exception enabled" flag

    class otl_object{
    public:

- Default constructor

      otl_object();

- Destructor

      virtual ~otl_object();

- "connected" flag

      int connected;

    protected:

- "exception enabled" flag

      int ex_enabled;

    };

### Class otl_connect

This class encapsulates the Oracle Call Interface functions which have Logon Descriptor Area as their first parameter. In other words, otl_connect is the class for creating "connect" objects.

    class otl_connect: public otl_object{
    public:

- Create "connect" object. Exceptions are allowed to raise by default

      otl_connect(int exception_enabled=otl_exception::enabled);

- Create "connect" object and connect to Oracle using the "connect_str" connect string; by default, exceptions are allowed to raise

       otl_connect(const char* connect_str,
      	     int exception_enabled=otl_exception::enabled
      	    );

- Destructor

      ~otl_connect();

- Concurrent logon; OCI application is allowed to have more than one concurrent logon. Returns 1 on success, 0 on failure.

      int rlogon(const char* connect_str);

- Get valid LDA from Pro*C. Returns 1 on success, 0 on failure.

      int sqllda(void);

- Exclusive logon. there may be only one logon of this type performed in OCI application (for more details see OCI doc.). Returns 1 on success, 0 on failure.

      int logon(const char* connect_str);

- Disconnect from / log off Oracle. Returns 1 on success, 0 on failure.

      int logoff(void);

- Commit current transaction. Returns 1 on success, 0 on failure.

      int commit(void);

- Roll back current transaction. Returns 1 on success, 0 on failure.

      int rollback(void);

- Break current OCI call. Returns 1 on success, 0 on failure.

      int obreak(void);

- Set auto commit mode on. Returns 1 on success, 0 on failure.

      int auto_commit_on(void);

- Set auto commit mode off. Returns 1 on success, 0 on failure.

      int auto_commit_off(void);

- Logon Descriptor Area

      Lda_Def lda;

- reference to "V7 return code"

      ub2& rc;

    };

### Class otl_column_desc

This class is data structure which contains a select item (column) descriptive information. The information may be obtained by the otl_cursor::describe_column function call (see the otl_cursor class).

    class otl_column_desc{
    public:

- field name

      sb1  name[241];

- field name length

      sb4  nlen;

- field size as the field is represented inside ORACLE

      sb4  dbsize;

- internal datatype code

      sb2  dbtype;

- numeric field scale: NUMBER(scale,precision)

      sb2  scale;

- numeric field precision

      sb2  prec;

- maximum display size of the field

      sb4  dsize;

- NULLs are allowed

      sb2  nullok;

    };

### Class otl_cursor

This class is a general-purpose cursor class.

    class otl_cursor: public otl_object{
    public:

- Create "cursor" object. by default, exceptions are allowed to raise.

      otl_cursor(int exception_enabled=otl_exception::enabled);

- Create "cursor" object and open cursor via "connect"

      otl_cursor(otl_connect& connect, // reference to "connect" object
      	   int exception_enabled=otl_exception::enabled
      	  );

- Close cursor (if opened) and destruct object

      ~otl_cursor();

- Open cursor via "connect". Returns 1 on success, 0 on failure

      int open(otl_connect& connect);

- Close cursor. Returns 1 on success, 0 on failure

      int close(void);

- Cancel a query after desired number of rows have been fetched. Returns 1 on success, 0 on failure

      int cancel(void);

- Set rollback options for non-fatal Oracle errors. For more info see the OCI manual, the "oopt" function. Returns 1 on success, 0 on failure

      int option(int rbopt, int waitopt);

- Fetch a portion of a LONG or LONG RAW column. For more info see the OCI manual, the "oflng" function. Returns 1 on success, 0 on failure

      int fetch_long(int column_num, // column number: 1,2,...
      	       void* buf, // pointer to buffer
      	       sb4 bufl, // buffer size
      	       int dtype, // buffer data type, see ext* "enum"
      	       ub4* retl, // returned length
      	       sb4 offset // offset
      	      );

- Parse sql statement. Returns 1 on success, 0 on failure

      int parse(const char* sqlstm);

- Parse sql statement; bind variable and select list items; variable list needs to be terminated with 0 pointer; Returns 1 on success, 0 on failure. if variable list contains variables which are SELECT statement output columns and if the variables don't have "column_num" defined, then the parse function enumerates the variables as follows:
  - eparse("select...",&f1,&f2,&f3,0);
    - f1 -- column 1
    - f2 -- column 2
    - f3 -- column 3

      int eparse(const char* sqlstm,...);

- Execute statement iters times. Returns 1 on success, 0 on failure

      int exec(short iters=1);

- Combined operation: Parse+Bind+Execute. Parse sqlstm. Bind variables. Execute statement iters times. Returns 1 on success, 0 on failure

      int exec(const char* sqlstm, // SQL statement
      	 short iters,  // number of iterations
      	 ... // NULL terminated host variable list
              );

- Fetch iters number of rows. Returns 1 on success, 0 on failure

      int fetch(short iters=1);

- Combined operation -- execute statement + fetch iters number of rows. Returns 1 on success, 0 on failure

      int exfet(short iters=1);

- Functions to bind placeholders
  - Bind host variable/array (instantiated template) to placeholder. Returns 1 on success, 0 on failure

        int bind(const char* name,
                    // placeholder name: ":F1", ":F2"
        	 otl_generic_variable& v
        	    // reference to host variable/array
                );

  - Bind "ordinary" host variable/array to placeholder. Returns 1 on success, 0 on failure

        int bind(const char* name, // placeholder name: ":f1", ":f2"
        	 void* buf, // pointer to host variable/array
        	 int   elem_size, //array element/ variable size in bytes
        	 int   ftype, // Oracle external data type code
        	 sb2*  indp=0 // pointer to indicator variable/array
                );

  - Bind template host variable/array with already defined name. Returns 1 on success, 0 on failure

        int bind(otl_generic_variable& v);

- Functions to bind select list items (columns)
  - Bind host variable/array (instantiated template) to column. Returns 1 on success, 0 on failure

        int bind(int column_num, // column number: 1,2,...
        	 otl_generic_variable& v // reference to variable/array
                );

  - bind "ordinary" host variable/array to column. Returns 1 on success, 0 on failure

        int bind(int column_num, // column number: 1,2,...
        	 void* buf, // pointer to host variable/array
        	 int   elem_size, // array element/variable size in bytes
        	 int   ftype, // Oracle external data type code
        	 sb2*  indp=0, // pointer to indicator array/varibale
        	 ub2*  rlen=0, // pointer to returned length array/variable
        	 ub2*  rcode=0 // pointer to returned code array variable
        	);

- Specialized function to bind columns
  - Bind C-style (null terminated) string variable/array to column. Returns 1 on success, 0 on failure

        int bind_cstring(int column_num, // column number: 1,2,...
                         char* buf, // pointer to C-string variable/array
                         int elem_size, // array element/variable size
                         sb2*  indp=0, // pointer to indicator array/variable
                         ub2*  rlen=0, // pointer to returned length array/variable
                         ub2*  rcode=0 // pointer to returned code array/variable
                        );

  - Bind int variable/array to column. Returns 1 on success, 0 on failure

        int bind_int(int column_num, // column number: 1,2,...
        	     int* buf, // pointer to int variable/array
        	     sb2*  indp=0, // pointer to indicator array/variable
        	     ub2*  rlen=0, // pointer to returned length array/variable
        	     ub2*  rcode=0 // pointer to returned code array/variable
        	    );

  - Bind short int variable/array to column. Returns 1 on success, 0 on failure

        int bind_short(int column_num,// column number: 1,2,...
        	       short* buf, // pointer to short int variable/array
        	       sb2*  indp=0, // pointer to indicator array/variable
        	       ub2*  rlen=0, // pointer to returned length array/variable
        	       ub2*  rcode=0 // pointer to returned code array/variable
        	      );

  - Bind long int variable/array to column. Returns 1 on success, 0 on failure

        int bind_long_int(int column_num, // column number: 1,2,...
        		  long* buf, // pointer to long int variable/array
        		  sb2*  indp=0, // pointer to indicator array/variable
        		  ub2*  rlen=0, // pointer to returned length array/variable
        		  ub2*  rcode=0 // pointer to returned code array/variable
        		 );

  - Bind float variable/array to column. Returns 1 on success, 0 on failure

        int bind_float(int column_num,// column number: 1,2,...
        	       float* buf, // pointer to float variable/array
        	       sb2*  indp=0, // pointer to indicator array/variable
        	       ub2*  rlen=0, // pointer to returned length array/variable
        	       ub2*  rcode=0 // pointer to returned code array/variable
        	     );

  - Bind double variable/array to column. Returns 1 on success, 0 on failure

        int bind_double(int column_num,// column number: 1,2,...
        	        double* buf, // pointer to double variable/array
        	        sb2*  indp=0, // pointer to indicator array/variable
        	        ub2*  rlen=0, // pointer to returned length array/variable
        	        ub2*  rcode=0 // pointer to returned code array/variable
        	       );

- Specialized functions to bind placeholders
  - Bind C-style (null terminated) string variable/array to column. Returns 1 on success, 0 on failure

        int bind_cstring(const char* name, // placeholder name: ":F1", ":F2"
        		 char* buf, // pointer to C-string variable/array
        		 int elem_size, // array element/variable size
        		 sb2*  indp=0 // pointer to indicator array/variable
        	        );

  - Bind int variable/array to placeholder. Returns 1 on success, 0 on failure

        int bind_int(const char* name, // placeholder name: ":F1", ":F2"
        	     int* buf, // pointer to int variable/array
        	     sb2*  indp=0 // pointer to indicator array/variable
        	    );

  - Bind short int variable/array to placeholder. Returns 1 on success, 0 on failure

        int bind_short(const char* name, // placeholder name: ":F1", ":F2"
        	       short* buf, // pointer to short int variable/array
        	       sb2*  indp=0 // pointer to indicator array/variable
        	      );

  - Bind long int variable/array to placeholder. Returns 1 on success, 0 on failure

        int bind_long_int(const char* name, // placeholder name: ":F1", ":F2"
        		  long* buf, // pointer to long int variable/array
        		  sb2*  indp=0 // pointer to indicator array/variable
        		 );

  - Bind float variable/array to placeholder. Returns 1 on success, 0 on failure

        int bind_float(const char* name, // placeholder name: ":F1", ":F2"
        	       float* buf, // pointer to float variable/array
        	       sb2*  indp=0 // pointer to indicator array/variable
        	      );

  - Bind double variable/array to placeholder. Returns 1 on success, 0 on failure

        int bind_double(const char* name, // placeholder name: ":F1", ":F2"
        	        double* buf, // pointer to double variable/array
        	        sb2*  indp=0 // pointer to indicator array/variable
        	       );

- Static (in class) function to immediately execute a constant SQL statement. Returns 1 on success, 0 on failure

      static int direct_exec(otl_connect& db, // connect object
      		       const char* stm, // statement
      		       int exception_enabled=otl_exception::enabled
      		        // exception_enabled flag
      		     );

- Check "end-of-file" condition when fetching rows from select statement. Returns 1 when "EOF", 0 otherwise

      int eof(void);

- Describe select item (column)

      int describe_column(otl_column_desc& col_desc,
                               // column descriptor structure
      		    int column_num
                               // column number: 1,2,...
      		   );

- "End-of-description" condition check

      int end_of_desc(void);

- Parse statement and bind variables

      int parse(const char* sqlstm, // SQL statement
      	  otl_generic_variable** v // pointer to variable list
      	 );

- Cursor Descriptor Area

      Cda_Def cda;

- convenient references to some of the CDA fields
  - reference to "rows processed count"

        ub4& rpc;

  - reference to "OCI function code"

        ub2& ft;

  - reference to "V7 return code"

        ub2& rc;

  - reference to "parse error offset"

        ub2& peo;

    };

### Class otl_select_cursor

This class is a cursor class, specialized for SELECT statements.

    class otl_select_cursor : public otl_cursor{
    public:

- General constructor

      otl_select_cursor(otl_connect& db, // connect object
      	          short arr_size=1, // attached host array size
      		  int exception_enabled=otl_exception::enabled
      		       // exception enabled flag
      		 );

- Fetch first row. rows are fetched in batches. "cur_row" points to current row in host array attached to select statement. cur_row==-1 if no rows have been fetched. Returns 1 on success, 0 on failure.

      int first(void);

- Fetch next row. "cur_row" points to current row. cur_row==-1 after fetch sequence is complete. Returns 1 on success, 0 on failure.

      int next(void);

- index of current row in host array

      int cur_row;

- number of rows in the buffer after most recent fetch

      int cur_size;

- row count -- total number of rows fetched

      int row_count;

- size of host array attached to select statement

      int array_size;

    };

### Class otl_dynamic_variable

This class is used in the otl_select_stream class to dynamically allocate a list of output columns of SELECT statement and any other automatically created bind variables (e.g. in a SQL stream).

    class otl_dynamic_variable: public otl_generic_variable{
    public:

- Dynamically construct a select list item (column)

      otl_dynamic_variable(
      	  const int column_num, // column number/position in select
      				// list
      	  const ftype, // external data type of the column
      	  const elem_size, // host array element/variable size
      	  const short array_size=1 // array size
      	 );

- Dynamically construct host variable/array

      otl_dynamic_variable(
      	      const char *aname, // varable/array name
      	      const ftype, // external data type
      	      const elem_size, // variable/array element size
      	      const short array_size=1 // array size
      	     );

- Default constructor

       otl_dynamic_variable();

- Destructor

      ~otl_dynamic_variable();

- Allocate memory and initialize variable/array

      void init(const int ftype, // external data types
                const int elem_size, // array element/variable size
      	  const short array_size // array size
      	 );

- return dynamic variable's external data type

      int get_ftype(void);

- return dynamic variable's elem_size

      int get_elem_size(void);

    };

### Class otl_err_info

This is the OTL error info class. It is intended for using in case of manual error handling. The class allows to get more detailed error information about the current Oracle error.

    class otl_err_info: public otl_exception{
    public:

- Default constructor

      otl_err_info();

- Create error info object from otl_connect object

      otl_err_info(otl_connect& connect);

- Create error info object from otl_cursor object

      otl_err_info(otl_cursor& cur);

- Read error info into existing error info descriptor

       void get_info(otl_connect& connect);
       void get_info(otl_cursor& cur);

    };

## 3.3. OTL stream interface

In OTL, SQL streams are introduced. The idea here is to combine streams and SQL. Such a combination provides new quality and simplicity in programming interface to SQL. The Oracle Array Interface naturally transforms into buffered stream operations.

The SQL streams are intended for SQL or PL/SQL statements which have input and/or output bind variables. Any statement can be treated as a functional element with input/output parameters. There are functions to put objects into a stream, that is, to assign values to input variables. Also, there are functions to get objects from the stream, that is, to get values from output variables.

                   +--> I1    I2 ... In
                   |    |     |      |
                   |    V     V      V
                   |  +------------------+
                   +--| SQL statement or |
                      | PL/SQL block     |
                      +-+-----+------+---+
                        |     |      |
                        V     V      V
                        O1    O2 ... Ok

When values of all input variables of the functional element are filled out then the element is executed. Resulting values are assigned to the output variables right after the execution. Sets of input and output variables are allowed to overlap (see the picture).

Logically, a SQL stream is a structured stream with input and output rows. The format of the input row is defined by a set of output variables of the stream. Similarly, the output row is defined by input variables of the stream. When objects are written into the stream, values are actually assigned to the input variables. Likewise, when objects are read from the stream, values are read from the output variables of the stream.

SQL streams are similar to buffered files. A SQL statement or PL/SQL block is opened as an ordinary buffered file. The logic of the SQL stream operations remains the same as the file operations with the only exception -- the SQL stream has separate input and output buffers which may overlap.

The SQL stream in C++ has a flush function for flushing its input buffer when the buffer gets full and a collection of >> and << operators for reading and writing objects of different data types. The most important advantage of the SQL streams is their unified interface to SQL statements and PL/ SQL blocks of any kind. This mean that application developers need to remember just a few syntactical constructs and function names which they already got familiar with when they started working with C++ streams.

Inside the SQL stream there is a small parser for parsing declarations of bind variables and their data types. There is no need to declare C/C++ host variables and bind them with placeholders by special bind function calls. All necessary buffers are created dynamically inside the stream. The stream just needs to be opened for reading input values and writing output values.

The OTL stream interface requires use of the OTL exceptions. This means that potentially any OTL stream operation can throw an exception of the otl_exception type. In order intercept the exception and prevent the program from aborting, wrap up the OTL stream code with the corresponding try & catch block.

For more detail on the stream class hierarchy, see Appendix A.

### Class otl_select_stream

This is the OTL select stream class. The user does not need to manually attach output columns to SELECT statement because the statement is automatically parsed and the output columns are allocated in the class constructor.

This class may issue the following otl_exceptions:

- Incompatible data types in stream operation, code=32000
- Not all input variables have been initialized, code=32007
- No input variables have beed defined in SELECT statement, code=32004

    class otl_select_stream: public otl_select_cursor{
    public:

- General conctructor. SELECT statement is parsed, all input host variables and output columns are automatically bound.

      otl_select_stream(otl_connect& db, // connect object
      		  const char* sqlstm, // SELECT statement
      		  const short arr_size, // output host arrays size
      		  ... // NULL terminated list of pointers to input host
      		      // variables.

      	        );

- General conctructor. SELECT statement is parsed, all input host variables and output columns are automatically bound. The difference between this constructor and the constuctor above is that this constuctor takes a pointer to an array of pointer to the host variable/array list, instead of taking them from stack, as the above constuctor does. This allows the user to dynamically create host variables, say, in a loop, and collect pointers to the variables into an array.

      otl_select_stream(
      		  otl_connect& db, // connect object
      		  const char* sqlstm, // SELECT statement
      		  otl_p_generic_variable* avp,
       		     // Pointer to NULL terminated list of
       		     // pointers to input hots
      		     // variables
      		  const short arr_size=1 // output host arrays size
      		 );

- General conctructor. SELECT statement is parsed, all input host variables and output columns are automatically allocated and bound. This constructor allows the user to use extended place-holder declarations.

      otl_select_stream(
      		  const short arr_size, // output host arrays size
      		  const char* sqlstm, // SELECT statement
      		  otl_connect& db // connect object
      		 );

- Destructor

      ~otl_select_stream();

- Rewind stream, SQL statement is re-executed. Input host variables of SELECT statement may be re-assigned before calling this function.

      void rewind(void);

- Test if NULL has been fetched during last stream operation

      int is_null(void);

- Test if "end-of-file" has been reached. Returns 1 when "end-of-file".

      int eof(void);

- Read objects from stream

      otl_select_stream& operator>>(char& c);
      otl_select_stream& operator>>(unsigned char& c);
      otl_select_stream& operator>>(char* s);
      otl_select_stream& operator>>(unsigned char* s);
      otl_select_stream& operator>>(int& n);
      otl_select_stream& operator>>(unsigned& u);
      otl_select_stream& operator>>(short& sh);
      otl_select_stream& operator>>(long int& l);
      otl_select_stream& operator>>(float& f);
      otl_select_stream& operator>>(double& d);

- Write input values to the stream (initialize input variables)

      otl_select_stream& operator<<(const char c);
      otl_select_stream& operator<<(const unsigned char c);
      otl_select_stream& operator<<(const char* s);
      otl_select_stream& operator<<(const unsigned char* s);
      otl_select_stream& operator<<(const int n);
      otl_select_stream& operator<<(const unsigned u);
      otl_select_stream& operator<<(const short sh);
      otl_select_stream& operator<<(const long int l);
      otl_select_stream& operator<<(const float f);
      otl_select_stream& operator<<(const double d);

- Get info on SELECT list items

      int select_list_len(void);
      int column_ftype(int ndx=0);
      int column_size(int ndx=0);

- Get column's internal info

      otl_column_desc* sl_desc; // column descriptor array

- Set flag "delete input host variables"

      void set_delete_var(const int should_delete=0);

- Special constructor. It parses SELECT statement and gets SELECT list information. This constructor can be used when the user does not really want to fetch rows via this stream but wants to get information on the SELECT list (output columns).

      otl_select_stream(const char* sqlstm, // SELECT statement
      		  otl_connect& db, // connect object
      		  const char* dummy_par // dummy parameter (needed for
      			                // making the function
      				        // prototype unique
      		 );

    };

### Class otl_out_stream

This is the OTL output class. This class is used for the following SQL statements:

- DELETE
- UPDATE
- INSERT
- PL/SQL block with input host variables only

This class may issue the following otl_exceptions:

- Incompatible data types in stream operation, code=32000
- Row must be full for flushing output stream, code=32004

    class otl_out_stream: public otl_cursor{
    public:

- Default constructor

      otl_out_stream(otl_connect& db):otl_cursor(db){}

- General conctructor. SQL statement is parsed, all ouput host variables are automatically bound.

      otl_out_stream(otl_connect& db, // connect object
      	       const char* sqlstm, // SQL statement
      	       ... // NULL terminated list of pointers to input hots
      	           // variables.
      	      );

- General conctructor. SQL statement is parsed, all ouput host variables are automatically bound.

      otl_out_stream(otl_connect& db, // connect object
      	       const char* sqlstm, // SQL statement
      	       otl_p_generic_variable* avp
      		  // Pointer to NULL terminated list of pointers
      		  // to input hots variables.
      	      );

- General conctructor. SQL statement is parsed, all host variables are automatically allocated and bound. This constructor allows the user to use extended place-holder declarations.

      otl_out_stream(
      	       short arr_size, // host array size
      	       const char* sqlstm, // SQL statement
      	       otl_connect& db // connect object
      	      );

- Destructor

      ~otl_out_stream();

- Write objects into stream

      otl_out_stream& operator<<(const char c);
      otl_out_stream& operator<<(const unsigned char c);
      otl_out_stream& operator<<(const char* s);
      otl_out_stream& operator<<(const unsigned char* s);
      otl_out_stream& operator<<(const int n);
      otl_out_stream& operator<<(const unsigned u);
      otl_out_stream& operator<<(const short sh);
      otl_out_stream& operator<<(const long int l);
      otl_out_stream& operator<<(const float f);
      otl_out_stream& operator<<(const double d);
      otl_out_stream& operator<<(const otl_null n);
        // write Oracle NULL into stream

- Flush stream buffer. SQL statement is executed as many times as rows have been entered into the stream buffer.

      virtual void flush(void);

- Clean up stream buffer without flushing it.

      virtual void clean(void);

- Set "auto-commit" flag. When the buffer is flushed, current transaction is automatically commited, if the flag is set. When otl_out_stream is opened, by default, "auto-commit" flag is set, and when the stream buffer is flushed, current transaction commits. In order to unset the flag, use this function.

      void set_commit(int auto_commit=0);

- Set flag "delete host variables". This flag is used, when, for example, the user allocate host variables in dynamic memory and wants the stream destuctor to automatically deallocate the occupied memory.

      void set_delete_var(const int should_delete=0);

    };

### Class otl_inout_stream

This is the OTL input/output stream class. It is used primarily for PL/SQL blocks with input and output parameters. Though, this stream class can be used for SQL statements and PL/SQL blocks with input or output parameters only.

This class may issue the following otl_exceptions:

- Incompatible data types in stream operation, code=32000
- Row must be full for flushing output stream, code=32001

    class otl_inout_stream: public otl_out_stream{
    public:

- General conctructor. SQL statement is parsed, all host input and output hostvariables are automatically allocated and bound.This constructor allows the user to use extended place-holder declarations.

      otl_inout_stream(
      		 short arr_size, // host array size
      		 const char* sqlstm, // SQL statement
      		 otl_connect& db // connect object
      		);

- Destructor

      ~otl_inout_stream();

- Test if all data has been already read from the stream

      int eof(void);

- Flush stream's output buffer. It actually means to execute the SQL statement as many times as rows entered to the output buffer. The stream is automatically flushed when the buffer gets full.

      void flush(void);

- Clean up buffer without flushing it.

      void clean(void);

- Rewind stream

      void rewind(void);

- Test if NULL was fetched from the stream

      int is_null(void);

- Read objects from stream

      otl_inout_stream& operator>>(char& c);
      otl_inout_stream& operator>>(unsigned char& c);
      otl_inout_stream& operator>>(char* s);
      otl_inout_stream& operator>>(unsigned char* s);
      otl_inout_stream& operator>>(int& n);
      otl_inout_stream& operator>>(unsigned& u);
      otl_inout_stream& operator>>(short& sh);
      otl_inout_stream& operator>>(long int& l);
      otl_inout_stream& operator>>(float& f);
      otl_inout_stream& operator>>(double& d);

    };

### Class otl_stream

This is the OTL stream class. It is a general-purpose and most advanced stream class, unified for streams of all types. This class may issue the following otl_exceptions:

- Incompatible data types in stream operation, code=32000
- Row must be full for flushing output stream, code=32001
- Not all input variables have been initialized, code=32004
- No input variables have been defined in SQL statement, code=32004

    class otl_stream{
    public:

- General conctructor. SQL statement is parsed, all host input and output host variables are automatically allocated and bound. This constructor allows the user to use extended place-holder declarations.

      otl_stream(
      	   short arr_size, // host array size
      	   const char* sqlstm, // SQL statement
      	   otl_connect& db // connect object
      	  );

- Default constructor

      otl_stream();

- Desctructor

      ~otl_stream();

- Test if all data has been already read from the stream

      int eof(void);

- Flush stream's output buffer. It actually means to execute the SQL statement as many times as rows entered to the output buffer. The stream is automatically flushed when the buffer gets full.

      void flush(void);

- Clean up buffer without flushing it.

      void clean(void);

- Rewind stream

      void rewind(void);

- Test if NULL was fetched from the stream

      int is_null(void);

- Set "auto-commit" flag. When the buffer is flushed, current transaction is automatically commited, if the flag is set. By default, the flag is set. In order to prevent current transaction from "auto-commit", uset the flag using this function.

      void set_commit(int auto_commit=0);

- Open stream

      void open(
      	  short arr_size, // host array size
      	  const char* sqlstm, // SQL statement
      	  otl_connect& db // connect object
      	 );

- Close stream

      void close(void);

- Test if the stream was opened okay

      int good(void);

- Read objects from stream

      otl_stream& operator>>(char& c);
      otl_stream& operator>>(unsigned char& c);
      otl_stream& operator>>(char* s);
      otl_stream& operator>>(unsigned char* s);
      otl_stream& operator>>(int& n);
      otl_stream& operator>>(unsigned& u);
      otl_stream& operator>>(short& sh);
      otl_stream& operator>>(long int& l);
      otl_stream& operator>>(float& f);
      otl_stream& operator>>(double& d);

- Write objects into stream

      otl_stream& operator<<(const char c);
      otl_stream& operator<<(const unsigned char c);
      otl_stream& operator<<(const char* s);
      otl_stream& operator<<(const unsigned char* s);
      otl_stream& operator<<(const int n);
      otl_stream& operator<<(const unsigned u);
      otl_stream& operator<<(const short sh);
      otl_stream& operator<<(const long int l);
      otl_stream& operator<<(const float f);
      otl_stream& operator<<(const double d);
      otl_stream& operator<<(const otl_null n);
        // write Oracle NULL into stream

- C-style printf/scanf functions

      void printf(const char* fmt,...);
      void scanf(const char* fmt,...);

  The following format specifiers are supported:

  - %d -- int
  - %u -- unsigned
  - %ld -- long int
  - %f -- float
  - %lf -- double
  - %c -- char
  - %s -- string
  - %N -- specifier for writing NULL into streams

    };

### Stream bind variable declarations

This section explains in detail how to declare bind variables (or extended place-holders) in the SQL streams.

A SQL statement or PL/SQL block may have placeholders which are usually connected with the corresponding bind variables in the program. In Pro*C the user needs to declare such variables directly in the program. OTL provides the same functionality in another way. There is a small parser which parses a SQL statament or PL/SQL block declaration and allocates corresponding bind variables dynamically inside the stream.

The following data types for extneded place-holder declarations are available:

- int
- unsigned
- short
- long -- (long integer)
- float
- double
- char[length]

For PL/SQL blocks, special qualifiers are introduced to distinguish between input and output variables:

- in -- input variable
- out -- output variable
- inout -- input/output variable

  #### Examples

  Here is a number of examples:

       begin :rc<int,out> := my_func( :salary<float,in>,
                                      :ID<int,inout>,
      				:name<char[32],out>
                                    ); end;

  Invoke the my_func function; return the function result into the :rc variable; the function has three parameters: salary (input), ID (iput/output), name (output)

         select * from tab1 where f1 > :f1<double>

  Select all columns from the tab1 table where f1 is greater than :f1

         insert into tab1 values( :f1<double>, :f2<char[32]>, :f3<int> )

  Insert row { :f1(double), :f2(string), :f3(integer) } into the tab1 table.

  ## 3.4. Prosto*C

  The name Prosto*C is originated in the author's native language -- "prosto" means "simple". Prosto*C is supposed to provide a simplified set of procedures for interfacing with SQL or PL/SQL. In Prosto*C, the mechanism of handling errors is slightly different from the otl_exception mechanism. Each connect object is supplied with the error handler -- a procedure, which is invoked each time when an error occurs (see also 2.1.11.).

  Prosto*C provides a set of functions which is very similar to the C "stdio" interface: scanf(), printf(), etc.

  Here is the list of Prosto*C functions:

  - Typedef which defines Prosto*C error handler prototype

        typedef void (*otl_error_handler)(char* err_msg,int err_code);

  - Connect to Oracle using the "connect" string and attach the "handler" function to the connect object. The function returns a pointer to the corresponding connect object

        otl_connect* otl_logon(char* connect,otl_error_handler handler=0);

  - "Pro*C" connect. Primary connection is done in a Pro*C module using EXEC SQL CONNECT...; Attach the "handler" function to the connect object. The function returns a pointer to the corresponding connect object.

        otl_connect* otl_proC_logon(otl_error_handler handler=0);

  - Disconnect from Oracle. "db" -- connect object. Returns 1 on success, 0 -- on failure.

        int otl_logoff(otl_connect* db);

  - Commit transaction. "db" -- connect object.

        void otl_commit(otl_connect* db);

  - Roll back transaction. "db" -- connect object.

        void otl_rollback(otl_connect* db);

  - Execute constant SQL statement. Returns 1 on success, 0 -- on failure

        int otl_exec(otl_connect* db,char* stm,int ignore_error=0);

    - db -- connect object
    - stm -- SQL statement
    - ignore_error -- "ignore error" flag. If the flag is set up, then the error handler function is not called.
  - Open OTL stream. Returns pointer to stream on success, 0 -- on failure.

        otl_stream* otl_stream_open(otl_connect* db, char* stm, short bufsize=1);

    - db -- connect object
    - stm -- SQL statement
    - bufsize -- size of the buffer, attached to the stream
  - Close OTL stream

        void otl_stream_close(otl_stream* f);

  - Check out the "EOF" condition on the "f" stream

        int otl_eof(otl_stream* f);

  - Check out if Oracle NULL has been fetched from the stream

         int otl_is_null(otl_stream* f);

  - Set "auto-commit" flag. When the buffer is flushed, current transaction is automatically commited, if the flag is set

         void otl_set_commit(otl_stream* f,int auto_commit=1);

  - Flush stream buffer. SQL statement is executed as many times as the rows have been entered into the stream buffer

        void otl_flush(otl_stream* f);

  - C-style printf/scanf functions

        void otl_printf(otl_stream* f, const char* fmt,...);
        void otl_scanf(otl_stream* f, const char* fmt,...);

    The following format specifiers are supported:

    - %d -- int
    - %u -- unsigned
    - %ld -- long int
    - %f -- float
    - %lf -- double
    - %c -- char
    - %s -- string
    - %N -- specifier for writing NULL into streams

  ## 4. Pro*OTL / Pre-Pro*C preprocessor (PPC)

  PPC is a preprocessor which reads a directive file on input and can generate both Pro*C and OTL code on output. When the preprocessor starts up, it connects to the database, parses directives and then generates the output code. The output code consists of a Pro*C file, a C++ file with OTL function calls and a header file with the prototypes of the functions, generated by PPC.

  The directives fall into three main categories:

  - "SELECT" directive
  - "Output" directive for
    - INSERT
    - UPDATE
    - DELETE
    - PL/SQLblocks with input parameters only
  - "Arbitrary PL/SQL block" directive

  ### 4.1. Getting started with PPC

  Let's consider similar examples as described in Chapter 2: examples 8 and 9. In the "scott/tiger" user, create the following table, using SQLPlus:

        create table test_tab(f1 number, f2 varchar2(30));

  Let's assume that the example comprises of two modules: main and auxiliary. The main module has the main function which connects to the database and call functions from the auxiliary module. Source code of the auxiliary module is generated by the PPC preprocessor from the directive file. The directive file is unified for both Pro*C and C++, but the output for Pro*C and C++ is different (see examples below). The interface functions are the same and can be used both in Pro*C and C++.

  Here is the source code of the directive file (ppc_test.ppc):

      /*
        ppc_test.ppc - directive file;
        ppc_test.h   - generated header file with interface functions and
                       data structures;
        ppc_test.C   - generated C++ module with OTL function calls;
        ppc_test.pc  - generated Pro*C module;
      */
      #include <ppc_test.h> /* generated header file */

      /* PPC standard prolog for an auxiliary (not main) module */
      #sql-init-module
      #sql-str-type <CSTR,1>
      /* type equivalence directive */

      /*
         SELECT statement directive. "Sel" is the directive label. 50 is the
         internal host arrays size.
      */
      #sql-select <Sel,50>
      SELECT
       *
      FROM
       TEST_TAB
      WHERE
       F1>=:F<int> AND F1<=:F*2
      ORDER BY
        F1
      ##
      /* ## is a directive terminator */

      /*
         "Output" statement directive. "Ins" is the directive label. 50 is the
         internal host arrays size.
      */
      #sql-out-stm <Ins,50>
      INSERT INTO TEST_TAB
      (
       F1,
       F2
      )
       VALUES
      (
       :F1<float>,
       :F2<char[31]>
      )
      ##

      /*
         "Arbitrary PL/SQL block" directive. "PL" is the directive label. 1
         is a dummy parameter which does not matter in the current release
         of PPC. Put 1 for compatibility with the future versions.

         :A is IN/OUT parameter;
         :B is OUT parameter;
         :C is IN parameter;
      */
      #sql-plsql <PL,1>
      BEGIN
       :A<int,inout> := :A+1;
       :B<char[31],out> := :C<char[31],in>;
      END;
      ##

      #ifdef __cplusplus

      /*
        Function for C++. In the main C++ module, the user needs to call
        this function, in order to pass over a pointer to the actual
        database connect object into the C++ module, generated by PPC.

        This function can be eliminated if only Pro*C is used.
      */

      void assign_db(otl_connect* adb)
      {
       db=adb; // db  is a static (in the module) pointer
               // to the database connect object
      }

      /*
        Function for C++. In the main C++ module, the user needs to call
        this function just before disconnecting from the database, in order
        to close the static "hot" cursor in this file.

        This function can be eliminated if only Pro*C is used.
      */
      void close_hotcur(void)
      {
       hotcur.close(); // close static hot cursor
      }
      #endif

  Here is the header file (ppc_test.h), generated from the directive file by PPC:

      #ifndef __PPC_TEST_H
      #define __PPC_TEST_H

      #ifdef __cplusplus
      extern "C"{
      #endif

      /*
        C-structure, corresponding to the "Sel" statement. The SELECT list
        has been automatically extracted from the database dictionary and
        the structure generated. The structure is a container for one output
        row of the "Sel" statement.
      */

      struct struct_Sel{

        double F1;    /*  F1 number */
        short F1_IND; /* F1's indicator */

        char F2[31]; /* F2 varchar2(30) */
        short F2_IND;/* F2's indicator */

      };
      typedef struct struct_Sel Sel; /* typedef declaration */

      void Sel_open(
          int F /* F is the integer input host variable :F */
      ); /* Open the "Sel" statement */

      void Sel_close(void); /* Close the "Sel" statement */

      int Sel_get(Sel* out);
        /* Get one row and put it into the "Sel" structure */

      void Ins_open(int auto_commit);
      /* Open the "Ins" statement. "auto_commit" is the auto-commit flag. If
         the flag is set then:

           - commit transaction right after the internal
             host arrays get full and the "Ins" statement is executed;
           - commit transaction right after the "Ins" statement is closed;
      */

      void Ins_put(
          float F1, /* input float host variable :F1 */
          char* F2  /* input char[31] host variable :f2 */
      );
      /* Put one row into the "Ins" statement's internal buffer. The
         statement is automatically executed when the buffer gets full.
      */

      void Ins_flush(void);
      /* "Flush" internal buffer, no matter how full it is. This means that
         the "Ins" statement executes as many times as rows the buffer
         contains. If the "auto_commit" flag was set, then the current
         transaction commits.
      */

      void Ins_close(void);
      /* Close the "Ins" statement: call the Ins_flush() function, then
         deallocate all internal resources and quit.
      */

      void PL_exec(
          int* A, /* IN/OUT integer parameter :A */
          char* B,/* OUT char[31] parameter :B */
          char* C /* IN char[31] parameter :C
      ); /* Execute the "PL" PL/SQL block  */

      #ifdef __cplusplus
      }
      #endif

      #endif

  For more information on this example, see Appendix F.

  #### Example in Pro*C

  In this section, source code of the Pro*C main module (ppc_main.pc) is given. It needs to be preprocessed by Pro*C, compiled by C and linked with the ppc_test.pc module (see the above example).

  ##### Source code

      #include <ppc_test.h>
      #include <stdio.h>
      EXEC SQL INCLUDE SQLCA;

      typedef char CSTR[80];

      EXEC SQL BEGIN DECLARE SECTION;
        EXEC SQL TYPE CSTR IS STRING(80);
        CSTR UserId;
      EXEC SQL END DECLARE SECTION;

      /* Define error handler */
      void sqlerror(void)
      {
       EXEC SQL WHENEVER SQLERROR CONTINUE;
       fprintf(stderr,"\n%s\n",sqlca.sqlerrm.sqlerrmc);
       EXEC SQL ROLLBACK RELEASE;
       exit(1);
      }
      EXEC SQL WHENEVER SQLERROR DO sqlerror();

      void Insert()
      /* insert rows into table */
      {int i;
       Ins_open(1); /* open "Ins" statement with "auto_commit" flag set */
       for(i=0;i<100;++i){
        char F2[32];
        sprintf(F2,"Name%d",i);
        Ins_put(i,F2); /* write one row into the database */
       }
       Ins_close(); /*  close statement */
      } /* insert */

      void Select()
      /* select rows from table */
      {
       Sel p; /* one row containter */

       Sel_open(8);
      /* open "Sel" statement with the :F input parameter = 8 */

      /* fetch rows from the table */
       while(!Sel_get(&p)){ /* not end-of-data */
        printf("f1=%g, f2=%s\n",p.F1,p.F2);
       }
       Sel_close(); /* close the statement */

      /* re-open "Sel" statement with :F = 4 */
       Sel_open(4);

      /* fetch rows from the table */
       while(!Sel_get(&p)){ /* not end-of-data */
        printf("f1=%g, f2=%s\n",p.F1,p.F2);
       }
       Sel_close(); /* close the statement */

      } /* select */

      void plsql()
      /* execute the "PL" PL/SQL block */
      {
       int a;
       char b[31];
       char c[31];

      /* assigning :A = 1, :C = "Test String1" */
       a=1;
       strcpy(c,"Test String1");

       PL_exec(&a,b,c); /* execute the block */
       printf("A=%d, B=%s\n",a,b); /* print out results */

      /* assigning :A = 2, :C = "Test String2" */
       a=2;
       strcpy(c,"Test String2");

       PL_exec(&a,b,c); /* execute the block */
       printf("A=%d, B=%s\n",a,b); /* print out results */

      /* assigning :A = 3, :C = "Test String3" */
       a=3;
       strcpy(c,"Test String3");

       PL_exec(&a,b,c); /* execute the block */
       printf("A=%d, B=%s\n",a,b); /* print out results */

      } /* plsql */

      int main()
      {
       strcpy(UserId,"scott/tiger");
       EXEC SQL CONNECT :UserId; /* connect to Oracle as scott/tiger */

       EXEC SQL TRUNCATE TABLE TEST_TAB; /* truncate table */
       Insert(); /* insert rows */
       Select(); /* select rows */
       plsql(); /* execute PL/SQL block */

       EXEC SQL COMMIT WORK; /* disconnect from Oracle */
      } /* main */

  ##### Output

      f1=8, f2=Name8
      f1=9, f2=Name9
      f1=10, f2=Name10
      f1=11, f2=Name11
      f1=12, f2=Name12
      f1=13, f2=Name13
      f1=14, f2=Name14
      f1=15, f2=Name15
      f1=16, f2=Name16
      f1=4, f2=Name4
      f1=5, f2=Name5
      f1=6, f2=Name6
      f1=7, f2=Name7
      f1=8, f2=Name8
      A=1, B=Test String1
      A=2, B=Test String2
      A=3, B=Test String3

  #### Example in C++

  In this section, source code of the C++ main module (ppc_main.C) is given. It needs to be compiled by C++ and linked with the ppc_test.C module (see the above example).

  ##### Source code

      #include <ppc_test.h>
      #include <stdio.h>
      #include <iostream.h>
      #include <otl.h>

      otl_connect db; // connect object

      void Insert()
      // insert rows into table
      {
       Ins_open(1);
      // open "Ins" statement with "auto_commit" flag set

       for(int i=0;i<100;++i){
        char F2[32];
        sprintf(F2,"Name%d",i);
        Ins_put(i,F2); // write one row into the database
       }
       Ins_close(); // close statement
      } /* insert */

      void Select()
      // select rows from table
      {
       Sel p; // one row container

       Sel_open(8); // open "Sel" statement with the :F input parameter = 8

      // fetch rows from the table
       while(!Sel_get(&p)){ // not end-of-data
        printf("f1=%g, f2=%s\n",p.F1,p.F2);
       }
       Sel_close(); // close the statement

       Sel_open(4); // re-open the statement with :F = 4

      // fetch rows from the table
       while(!Sel_get(&p)){ // not end-of-data
        printf("f1=%g, f2=%s\n",p.F1,p.F2);
       }
       Sel_close(); // close the statement

      } /* select */

      void plsql()
      // execute the "PL" PL/SQL block
      {
       int a;
       char b[31];
       char c[31];

      // assigning :A = 1, :C = "Test String1"
       a=1;
       strcpy(c,"Test String1");

       PL_exec(&a,b,c); // execute the block
       printf("A=%d, B=%s\n",a,b); // print out results

      // assigning :A = 2, :C = "Test String2"
       a=2;
       strcpy(c,"Test String2");

       PL_exec(&a,b,c); // execute the block
       printf("A=%d, B=%s\n",a,b); // print out results

      // assigning :A = 3, :C = "Test String3"
       a=3;
       strcpy(c,"Test String3");

       PL_exec(&a,b,c); // execute the block
       printf("A=%d, B=%s\n",a,b); // print out results

      } /* plsql */

      // define prototypes of the assign_db and close_hotcur functions
      // from the ppc_test.C module.
      extern "C" void assign_db(otl_connect* db);
      extern "C" void close_hotcur(void);

      int main()
      {
       try{
        db.rlogon("scott/tiger"); // connect to Oracle as scott/tiger
        assign_db(&db); // initialize internal pointer to the actual connect
                        // object in the ppc_test.C module

        otl_cursor::direct_exec(db,"truncate table test_tab");
           // truncate table

        Insert(); // insert rows
        Select(); // select rows
        plsql();  // execute PL/SQL block

       }catch(otl_exception& p){ // intercept OTL exceptions
        cout<<p.msg<<endl; // print out error message
       }
       close_hotcur(); // close the hot cursor from ppc_test.C
       db.logoff(); // disconnect from Oracle
       return 0;
      } /* main */

  ##### Output

      f1=8, f2=Name8
      f1=9, f2=Name9
      f1=10, f2=Name10
      f1=11, f2=Name11
      f1=12, f2=Name12
      f1=13, f2=Name13
      f1=14, f2=Name14
      f1=15, f2=Name15
      f1=16, f2=Name16
      f1=4, f2=Name4
      f1=5, f2=Name5
      f1=6, f2=Name6
      f1=7, f2=Name7
      f1=8, f2=Name8
      A=1, B=Test String1
      A=2, B=Test String2
      A=3, B=Test String3

  ##### Notes

  It is possible to encapsulate all database functionality in separate Pro*C and PPC modules and use the modules without using OTL. The user can define Pro*C connect and disconnect functions separately in a Pro*C file, together with the other "handmade" Pro*C procedures. Additionally, a few directive files can be defined to automatically generate Pro*C code. Then, all this stuff can be "objectified" (encapsulated) in C++ classes and the classes can be used in the C++ main module.

  The user needs to keep in mind the technique of invoking plain C functions from C++.

  ### 4.2. Directives

  PPC source code files consist of directives which may be mixed with real code in plain C, C++ or Pro*C (non-directive code is not processed and remains intact). The directive starts with # at the beginning of line. Some directives have names, arguments and special terminators, the other -- do not. By format, the directives are similar to the C preprocessor commands. Inside the directive body, extended place-holder declarations are allowed.

  #### 4.2.1. #sql-select

  This is the "SELECT" directive. On output, it generates a set of functions to select rows according to the given SELECT statement. The directive format is as follows:

      #sql-select <Label,BufSize> end-of-line
       ...
       <SELECT Statement>
       ...
      ## end-of-line

  <Label>
    Statement label
  <BufSize>
    Defines the size of internal host arrays, attached to the SELECT statment
  <SELECT Statement>
    SELECT statement body. Can be multi-line.
  ##
    SELECT statement terminator. Starts at the beginning of line

  Example

      #sql-select <Sel,50>
      SELECT
       *
      FROM
       TEST_TAB
      WHERE
       F1>=:F<int> AND F1<=:F*2
      ORDER BY
        F1
      ##

  Generated code

      struct struct_<Label>{
        ...
        <SELECT list items>
        ...
      }; /* One output row container */
      typedef struct struct_<Label> <Label>;

      void <Label>_open( /* open statement */
          ...
          <Input variable list>
          ..
      );
      void <Label>_close(void); /* close statement */
      int <Label>_get(<Label>* out); /* get one rows from the fetch sequence */

  For more detail, see Appendix F and "Getting Started with PPC".

  #### 4.2.2. #sql-out-stm

  This is the "output" directive. It is called "output" similar to the OTL streams. A stream is called output, when the user can write objects into the stream. This directive is used for:

  - INSERT
  - UPDATE
  - DELETE
  - PL/SQLblocks with input parameters only

  On the output, the directive generates a set of functions to write rows to the database, according to the given SQL statement or PL/SQL block. The directive format is as follows:

      #sql-out-stm <Label,BufSize> end-of-line
       ...
       <SQL Statement or PL/SQL block with input
        parameters only>
       ...
      ##

  <Label>
    Statement label
  <BufSize>
    Defines the size of internal host arrays, attached to the statment
  <SQL Statement or PL/SQL block with input parameters only>
    Statement body. Can be multi-line.
  ##
    Statement terminator. Starts at the beginning of line

  Example

      #sql-out-stm <Ins,50>
      INSERT INTO TEST_TAB
      (
       F1,
       F2
      )
       VALUES
      (
       :F1<float>,
       :F2<char[31]>
      )
      ##

  Generated code

      void <Label>_open(int auto_commit); /* open statement */
      void <Label>_put( /* write one row */
        ...
        <Parameter list>
        ...
      );
      void <Label>_flush(void); /* "flush" internal buffer */
      void <Label>_close(void); /* close statement */

  For more detail, see Appendix F and "Getting Started with PPC".

  #### 4.2.3. #sql-plsql

  This is the "arbitrary PL/SQL block" directive. On the output, the directive generates the "exec" function to execute the PL/SQL block, given in the directive. The directive format is as follows:

      #sql-plsql <Label,BufSize> end-of-line
       ...
       <PL/SQL block>
       ...
      ##  end-of-line

  <Label>
    Statement label
  <BufSize>
    Defines the size of internal host arrays, attached to the statment. In the current release of PPC should be always 1.
  <PL/SQL block>
    Statement body. Can be multi-line.
  ##
    Statement terminator. Starts at the beginning of line

  Example

      #sql-plsql <PL,1>
      BEGIN
       :A<int,inout> := :A+1;
       :B<char[31],out> := :C<char[31],in>;
      END;
      ##

  Generated code

      void <Label>_exec( /* execute PL/SQL block */
        ...
        <Parameter list>
        ...
      );

  For more detail, see Appendix F and "Getting Started with PPC".

  #### 4.2.4. #sql-init-module

  This is the "module's standard prolog" directive. On output, the directive generates a piece of code, typical for non-main modules.

      #sql-init-module  end-of-line

  Example

      #sql-init-module

  Generated code

  For more detail, see Appendix F and "Getting Started with PPC".

  #### 4.2.5. #sql-init-main

  This is the "main module's standard prolog" directive. On output, the directive generates a piece of code, typical for main modules.

      #sql-init-main  end-of-line

  Example

      #sql-init-main

  Generated code

  For more detail, see Appendix F and "Getting Started with PPC".

  #### 4.2.6. #sql-str-type

  This is the "string type equivalence" directive. It has effect only in Pro*C. The directive format is as follows:

      #sql-str-type <StringType,Flag>  end-of-line

  <StringType>
    C-string type, defined as a typedef. PPC uses the "StringType" identifier in generating Pro*C internal string variables.
  <Flag>
    if <flag>==1 then the string type equivalence is enforced. if <flag>==0 then the string type equivalence is off.

  Example

      typedef char C_STR[256];
      ...
      #sql-str-type <C_STR,1>
      ...

  Generated code

  None.

  ### 4.3. Command line parameters

  All command line parameter are positional. Parameters inside [] are optional. The format of PPC command line is as follows:

        ppc <connect_string> <input_file> <proc-file> <h-file> <#define>
            [<macro-def-file> [<OTL-module>]]

  1. connect_string
    Database connect sting
  2. input_file
    Input PPC directive file
  3. proc-file
    Output Pro*C file
  4. h-file
    Output interface header file (contains PPC-generated external function prototypes and data structures
  5. #define
    #define for the interface header file
  6. macro-def-file
    File, containing macro definitions to be used with the OTL streams (see the file for more detail)
  7. OTL-module
    Output C++ module which contains OTL function calls

  Examples

        ppc scott/tiger sample.ppc sample.pc sample.h __SAMPLE_H

        ppc scott/tiger sample.ppc sample.pc sample.h __SAMPLE_H dummy.h sample.C

  ## 5. Acknowledgements

  Vladimir Shipunov and Igor Galichin (Siberian Trade Bank, Novosibirsk, Russia) have discussed with me some ideas how to implement basic classes.

  Peter Muth, Hannelore Eisner, Achim Kraiss and other members of the VODAK team in GMD IPSI (Darmstadt, Germany) have given me good knowledge on Object Oriented Databases and I do not regret about the time I spent with them. The knowledge was very useful in the development of the OTL.

  Especially, I would like to thank Prof.Dr. Erich Neuhold who granted me a visiting researcher position in GMD IPSI.

  Sergei Trapeznikov's and my hard work on numerous Oracle projects at Siemens / Empros inspired me to develop PPC. I wish Sergei Trapeznikov all the best in his career at SDT.

  Many thanks to my wife Irina for her patience and understanding that this work is important to me.

  ## 6. Bibliography

  - [1] Programmer's Guide to the Oracle Call Interface.
  - [2] PL/SQL User's Guide and Reference.
  - [3] Oracle 7 Server. Messages and Codes Manual.

  ## Appendix A. OTL class hierarchy

        otl_object
         |
         +------>otl_connect
         |
         +------>otl_cursor
                   |
                   +------>otl_select_cursor
                   |         |
                   |         +------>otl_select_stream
                   |
                   |
                   |                                      otl_stream
                   +------>otl_out_stream
                             |
          		       +------>otl_inout_stream

        otl_generic_variable
         |
         +-->otl_dynamic_variable
         |
         +-->otl_variable
         |     |
         |     +--->otl_cstring
         |	      otl_varchar2
         |	      otl_long
         |	      otl_varchar
         |	      otl_varraw
         |	      otl_raw
         |	      otl_long_raw
         |	      otl_char
         |	      otl_charz
         |	      otl_long_varchar
         |	      otl_long_varraw
         |
         +->otl_array
              |
              +---->otl_date_array
      	      otl_rowid_array
      	      otl_varnum_array
      	      otl_number_array
      	      otl_double_array
      	      otl_float_array
      	      otl_signed_char_array
      	      otl_short_int_array
      	      otl_int_array
      	      otl_long_int_array
      	      otl_unsigned_array
      	      otl_cstring_array
      	      otl_varchar2_array
      	      otl_long_array
      	      otl_varchar_array
      	      otl_varraw_array
      	      otl_raw_array
      	      otl_long_raw_array
      	      otl_char_array
      	      otl_charz_array

        otl_exception
         |
         +---->otl_err_info

  ## Appendix B. Error message list

  A few error codes are defined by OTL. It is necessary because a runtime error can occur during debugging of a program. All the error codes defined can be issued only from member functions of the stream classes. Error reporting and handling is implemented via the normal mechanism of the OTL exceptions.

  The user can catch an exception raised not by an Oracle error but by one of the << or >> operators of the stream classes.

  Code=32000: Incompatible data types in stream operation
    Cause: The data type of a variable used in the current stream operation is not compatible with the declared stream format.
    Action: Check placeholders and their data types declaration.
  Code=32001: Row must be full for flushing output stream
    Cause: Stream is open for output and has a format of output rows. An output row is a tuple of all output variables put together. The current output row is not filled yet but the flush function is invoked. The stream buffer cannot be flushed until the current row of the output buffer is full.
    Action: Fill the row first, then flush the stream.
  Code=32004: Not all input variables have been initialized
    Cause: stream has input variables but not all the variables have been initialized. An attempt to read data from the stream was made.
    Action: Assign all the input variables first.
  Code=32004: No input variables have been defined in SQL statement
    Cause: Stream has no input variables. An attempt to write objects to the stream via one of the << operators was made.
    Action: Do not call the << operators for streams which have no input variables defined.

  ## Appendix C. OTL source code (otl.h)

      //
      // The OCI Template Library 1.0.6.5
      // Copyright (C) Sergei Kuchin, 1996, 1997, 1998
      // Author: Sergei Kuchin
      // This library is free software. Permission to use, copy,
      // modify and redistribute it for any purpose is hereby granted
      // without fee, provided that the above copyright notice appear
      // in all copies.
      //
      #ifndef __OTL_H
      #define __OTL_H

      // Functions are defined as INLINE. Those, who don't like inline
      // functions and prefer to have a separate C++ module with the
      // functions, can define INLINE as empty string and split up otl.h
      // into two files: otl.h and otl.C (otl.cpp).

      #define INLINE inline
      #define OTL_DEBUG_

      // OTL uses the ociapr.h file (OCI standard header file, recommended
      // for ANSI C compilers).  In Unix, OCI header files reside in the
      // $ORACLE_HOME/rdbms/demo directory.

      extern "C"{
      #include <ociapr.h>
      }

      #ifdef OTL_DEBUG
      #include <iostream.h>
      #endif

      #include <string.h>
      #include <ctype.h>
      #include <stdarg.h>

      // Oracle internal data types (see Programmer's Guide to the
      // Oracle Call Interface, chapter 3)

      // enum otl_internal_type{

       const int inVarChar2=1;
       const int inNumber=2;
       const int inLong=8;
       const int inRowId=11;
       const int inDate=12;
       const int inRaw=23;
       const int inLongRaw=24;
       const int inChar=96;
       const int inMslabel=106;

      //};

      // Oracle external data types (see Programmer's Guide to the
      // Oracle Call Interface, chapter 3)

      //enum otl_external_type{

       const int  extVarChar2=inVarChar2;
       const int  extNumber=inNumber;
       const int  extInt=3;
       const int  extFloat=4;
       const int  extCChar=5;
       const int  extVarNum=6;
       const int  extLong=inLong;
       const int  extVarChar=9;
       const int  extRowId=inRowId;
       const int  extDate=inDate;
       const int  extVarRaw=15;
       const int  extRaw=inRaw;
       const int  extLongRaw=inLongRaw;
       const int  extUInt=68;
       const int  extLongVarChar=94;
       const int  extLongVarRaw=95;
       const int  extChar=inChar;
       const int  extCharZ=97;
       const int  extMslabel=inMslabel;

      //};

      // Container data types for some of Oracle external data types defined
      // in Programmer's Guide to the Oracle Call Interface, chapter 3:

      typedef ub1 otl_date_intern[7]; // date Oracle internal format
      typedef ub1 otl_rowid_intern[14]; // rowid in Oracle internal format
      typedef char otl_cchar_rowid[19]; // rowid in text format
      typedef ub1 otl_varnum_intern[22]; // varnum in internal format
      typedef ub1 otl_number_intern[21]; // number in Oracle 21-byte binary
                                         // format

      // Error codes and messages, used in an exception raised in the
      // otl_select_stream class and otl_out_stream_class

      const int otl_error_code_0=32000;
      #define otl_error_msg_0 "Incompatible data types in stream operation"

      const int otl_error_code_1=32004;
      #define otl_error_msg_1 "No input variables have been defined in SQL statement"

      const int otl_error_code_2=32004;
      #define otl_error_msg_2  "Not all input variables have been initialized"

      const int otl_error_code_3=32001;
      #define otl_error_msg_3  "Row must be full for flushing output stream"

      // OTL "Blocked Call" class. It is used for "non-blocking" Oracle
      // connections. The otl_blocked_call exception is raised when the
      // current call of an OCI function is blocked.
      class otl_blocked_call{
      public:
       otl_blocked_call(){};
       ~otl_blocked_call(){};
      };

      const int otl_blocked=3123; // Return code, indicating that the most
      			    // recent Oracle call has been blocked
      const int otl_blocking=3128; // Return code, indicating that the
      			     // current Oracle connection is still blocking

      const int otl_nonblocking=1; // Return code, indicating that the
                                   // current connection is nonblocking

      // OTL exception class. Exceptions of this type are raised by the
      // library functions (default mode), unless it is prohibited
      // explicitly in the otl_connect or otl_cursor class constructors
      // (see below). In case of disabled exceptions OTL functions
      // return codes and it the user's responsibility to check out the
      // codes and handle errors. The main advantage of using this
      // exception handling mechanism is that exceptions can be
      // processed in one catch block, instead of checking return codes
      // from every library function call.

      class otl_exception{

      public:

      // This "enum" defines two constants which are used in
      // constructors of the  otl_connect, otl_cursor and
      // otl_select_cursor classes.

       enum{ disabled, enabled };

       unsigned char msg[1000]; // error message buffer
       unsigned char* stm_text;// sql that caused the error
       int code;                // error code

      // Create exception from LDA
       otl_exception(Lda_Def& lda,const char* sqlstm=0)
       {if(lda.rc==otl_blocked)throw otl_blocked_call();
        init(lda,sqlstm);
       }

      // Create exception from amsg and acode
       otl_exception(const char* amsg,const int acode,const char* sqlstm=0)
       {if(acode==otl_blocked)throw otl_blocked_call();
        init(amsg,acode,sqlstm);
       }

      // Copy constructor
       otl_exception(const otl_exception& p)
         {init((char*)p.msg,p.code,(const char*)p.stm_text);}

      // Default constructor
       otl_exception(){stm_text=0;}

      // Destructor
       ~otl_exception(){delete[] stm_text;stm_text=0;}

      // Init-function
       void init(const char* amsg,const int acode,const char* sqlstm)
       {stm_text=0;
        code=acode;
        if(sqlstm) {
         stm_text=new unsigned char[strlen(sqlstm)+1];
         strcpy((char*)stm_text,sqlstm);
        }
       }

      // Init-function
       void init(const otl_exception& p)
         {init((const char*)p.msg,p.code,(const char*)p.stm_text);}

      protected:

      INLINE void init(Lda_Def& lda,const char* sqlstm=0);
        // get error code and message

      };

      // Parent for otl_cursor and otl_connect classes
      class otl_object{

      public:
       int connected; // "connected" flag

      // Default constructor
       INLINE otl_object();

      // Destructor
       virtual ~otl_object(){};

      protected:
       int ex_enabled; // "exception enabled" flag

      };

      class otl_out_stream;

      // Connect class. Object of this class are used for connecting to
      // Oracle.
      class otl_connect: public otl_object{

      public:

       Lda_Def lda; // Logon Descriptor Area (see OCI doc. for more
                    // details)

       ub2& rc; // reference to "V7 return code"

      // Create "connect" object
      INLINE otl_connect(int exception_enabled=otl_exception::enabled);
        // exceptions are allowed to raise by default

      // Create "connect" object and connect to Oracle using the
      // "connect_str" connect string; by default, exceptions are
      // allowed to raise
      INLINE otl_connect(const char* connect_str,
      		   int exception_enabled=otl_exception::enabled
      		  );

      // Destructor
      INLINE ~otl_connect();

      // Concurrent logon; OCI application is allowed to have more than one
      // concurrent logon. Returns 1 on success, 0 on failure.
      INLINE int rlogon(const char* connect_str // Connect string
      		 );

      // Test if connection is in blocking mode. Returns "otl_blocking" if
      // connection is blocking, "otl_nonblocking" if connection is
      // non-blocking and 0 on failure.
      INLINE int test_blocking(void);

      // Set nonblocking connection mode. Returns 1 on success, 0 on
      // failure.
      INLINE int set_nonblocking(void);

      // Clear nonblocking connection mode. In other words, set blocking
      // connection mode.
      INLINE int clear_nonblocking(void);

      // Get valid LDA from Pro*C. Returns 1 on success, 0 on failure.
      INLINE int sqllda(void);

      // Get valid LDA from X/Open. Returns 1 on success, 0 on failure.
      INLINE int sqlld2(const char* dbname);

      // Exclusive logon. there may be only one logon of this type performed
      // in OCI application (for more details see OCI doc.). Returns 1 on
      // success, 0 on failure.
      INLINE int logon(const char* connect_str);

      // Disconnect from / log off Oracle. Returns 1 on success, 0 on
      // failure.
      INLINE int logoff(void);

      // Commit current transaction. Returns 1 on success, 0 on failure.
      INLINE int commit(void);

      // Roll back current transaction. Returns 1 on success, 0 on failure.
      INLINE int rollback(void);

      // Break current OCI call. Returns 1 on success, 0 on failure.
      INLINE int obreak(void);

      // Set auto commit mode on. Returns 1 on success, 0 on failure.
      INLINE int auto_commit_on(void);

      // Set auto commit mode off. Returns 1 on success, 0 on failure.
      INLINE int auto_commit_off(void);

      // Thread safe initialization. Returns 1 on success, 0 on failure.
      // This function needs to be called only once at the very beginning of
      // the program. This function works in Oracle 7.3 only.
      INLINE static int thread_safe_init(void);

      protected:

       unsigned char hda[512]; // private logon area
       int proc_connect; // connected via Pro*C EXEC SQL CONNECT...;

       friend otl_out_stream;

      public:

       void* handler; // reserved for use in Prosto*C
      };

      // Select list item (column) descriptor
      class otl_column_desc{

      public:
       sb1  name[241]; // field name
       sb4  nlen;      // field name length
       sb4  dbsize;    // field size as the field is presented inside ORACLE
       sb2  dbtype;    // internal datatype code
       sb2  scale;     // numeric field scale: NUMBER(scale,precision)
       sb2  prec;      // numeric field precision
       sb4  dsize;     // maximum display size of the field
       sb2  nullok;    // NULLs are allowed

      };

      class otl_cursor;
      class otl_ref_cursor;
      class otl_select_stream;
      class otl_ref_select_stream;
      class otl_out_stream;
      class otl_inout_stream;

      // OTL generic variable -- parent of the otl_variable, otl_array
      // template classes and otl_dynamic_variable. This class is needed in
      // order to pass over internal info about actual host variables and
      // arrays, instantiated from the templates.

      class otl_generic_variable{
      public:

      // Default constructor
      INLINE otl_generic_variable();

      // Destructor
      INLINE virtual ~otl_generic_variable();

      // Assigning a name to the variable
      INLINE void copy_name(const char* aname);

      // Assigning a position (number) to the select list item (column)
      INLINE void copy_pos(const int apos);

      // For more detail on indicator, returned length and returned code
      // arrays see Programmer's Guide to the Oracle Call Interface

      // For input variable/array:

      INLINE virtual void set_null(int ndx=0);
       // Set variable's value as NULL

      INLINE virtual void set_len(int len, int ndx=0);
       // Set variable's buffer length

      INLINE virtual void set_not_null(int ndx=0);
       // Set variable's value as NOT NULL

      // For output variable/array:

      INLINE virtual int is_null(int ndx=0);
       // Check if variable's value is NULL

      INLINE virtual int is_success(int ndx=0);
       // Check if variable's value has been fetched OK.

      INLINE virtual int is_truncated(int ndx=0);
       // Check if variable's value is truncated

      INLINE virtual void* val(int ndx=0);
       // Get pointer to variable's buffer

      // Only for output arrays, defined in select statement:

      INLINE virtual int is_invalid_conversion(int ndx=0);
       // Check if during fetch conversion error occurred

      INLINE virtual int is_real_overflow(int ndx=0);
       // Check if during fetch real overflow occurred

      INLINE virtual int is_unsupported_datatype(int ndx=0);
       // Check if variable has unsupported data type

      // Used only for PLSQL tables: set/get PLSQL table size
      INLINE virtual void set_tab_len(int new_len);
      INLINE virtual int get_tab_len(void);

      protected:

       friend otl_cursor;
       friend otl_ref_cursor;
       friend otl_select_stream;
       friend otl_ref_select_stream;
       friend otl_out_stream;
       friend otl_inout_stream;

       ub1* p_v; // pointer to buffer
       sb2* p_ind; // pointer to indicator variable/array
       ub2* p_rlen; // pointer to column's returned length
       ub2* p_rcode; // poinetr column's returned code
       int ftype; // external data type's code of the host variable/array
       int elem_size; // array element/variable size
       int array_size; // host array size (=1 in case of scalar host variable)
       char* name; // variable name;
       int pos; // select list item position

      // Used only for PLSQL tables
       ub4 max_tab_len; // maximum length (of PLSQL table)
       ub4 cur_tab_len; // current length (of PLSQL table)

      };

      // pointer to otl_generic_variable
      typedef otl_generic_variable* otl_p_generic_variable;

      // OTL dynamic variable/array class. Used in otl_select_stream to
      // automatically allocate default select list items based on the
      // information from the describe_column function.

      class otl_dynamic_variable: public otl_generic_variable{
      public:

      // Dynamically construct a select list item (column)
      INLINE otl_dynamic_variable(
      	  const int column_num, // column number/position in select
      				// list
      	  const int Aftype, // external data type of the column
      	  const int Aelem_size, // host array element/variable size
      	  const short Aarray_size=1 // array size
      	 );

      // Dynamically construct host variable/array
      INLINE otl_dynamic_variable(
      	      const char *aname, // varable/array name
      	      const int Aftype, // external data type
      	      const int Aelem_size, // variable/array element size
      	      const short Aarray_size=1 // array size
      	     );

      // Default constructor
      INLINE otl_dynamic_variable();

      // Destructor
      INLINE ~otl_dynamic_variable();

      // Allocate memory and initialize variable/array
      INLINE void init(const int Aftype, // external data types
      		 const int Aelem_size, // array element/variable size
      		 const short Aarray_size // array size
      		);

      // return dynamic variable's external data type
      INLINE int get_ftype(void);

      // return dynamic variable's elem_size
      INLINE int get_elem_size(void);

      };

      // OTL (general-purpose) cursor class

      class otl_cursor: public otl_object{

      public:

       Cda_Def cda; // Cursor Descriptor Area

      // convenient references to some of the CDA fields
       ub4& rpc; // reference to "rows processed count"
       ub2& ft; // reference to "OCI function code"
       ub2& rc; // reference to "V7 return code"
       ub2& peo; // reference to "parse error offset"
       char* stm_text; // SQL Statement Text

      // Create "cursor" object. by default, exceptions are allowed to
      // raise.
      INLINE otl_cursor(int exception_enabled=otl_exception::enabled);

      // Create "cursor" object and open cursor via  "connect"
      INLINE otl_cursor(otl_connect& connect, // reference to "connect" object
      		  int exception_enabled=otl_exception::enabled
      		 );

      // Close cursor (if opened) and destruct object
      INLINE ~otl_cursor();

      // Open cursor via "connect". Returns 1 on success, 0 on failure
      INLINE int open(otl_connect& connect);

      // Close cursor. Returns 1 on success, 0 on failure
      INLINE virtual int close(void);

      // Cancel a query after desired number of rows have been
      // fetched. Returns 1 on success, 0 on failure
      INLINE int cancel(void);

      // Set rollback options for non-fatal Oracle errors. For more info see
      // the OCI manual, the "oopt" function. Returns 1 on success, 0 on
      // failure
      INLINE int option(int rbopt, int waitopt);

      // Fetch a portion of a LONG or LONG RAW column. For more info see the
      // OCI manual, the "oflng" function. Returns 1 on success, 0 on failure
      INLINE int fetch_long(int column_num, // column number: 1,2,...
      		      void* buf, // pointer to buffer
      		      sb4 bufl, // buffer size
      		      int dtype, // buffer data type, see ext* "enum"
      		      ub4* retl, // returned length
      		      sb4 offset // offset
      		     );

      // Parse sql statement. Returns 1 on success, 0 on failure
      INLINE int parse(const char* sqlstm);

      // Extended Parse function -- eparse:
      // Parse sql statement;
      // bind variable and select list items;
      // variable list needs to be terminated with 0 pointer;
      // Returns 1 on success, 0 on failure.

      // if variable list contains variables which are SELECT statement's
      // output columns and if the variables don't have "column_num"
      // defined, then the parse function enumerates the variables as follows:
      //
      //      eparse("select...",&f1,&f2,&f3,0);
      //         f1 -- column 1
      //         f2 -- column 2
      //         f3 -- column 3
      //
      INLINE int eparse(const char* sqlstm,...);

      // Execute statement iters times. Returns 1 on success, 0 on failure
      INLINE int exec(short iters=1);

      // Combined operation: Parse+Bind+Execute (Extended Exec function)
      // Parse sqlstm;
      // Bind variables;
      // Execute statement iters times.
      // Returns 1 on success, 0 on failure
      INLINE int eexec(const char* sqlstm, // SQL statement
      		short iters,  // number of iterations
      		... // NULL terminated list of pointers to bind variables
      	       );

      // Fetch iters number of rows. Returns 1 on success, 0 on failure
      INLINE int fetch(short iters=1);

      // Combined operation -- execute statement + fetch iters number of
      // rows. Returns 1 on success, 0 on failure
      INLINE int exfet(short iters=1);

      // --------------- Functions to bind placeholders ------------------

      // Bind host variable/array (instantiated template) to
      // placeholder. Returns 1 on success, 0 on failure
      INLINE virtual int bind(const char* name, // placeholder name: ":F1", ":F2"
      			otl_generic_variable& v
      			  // reference to host variable/array
        	               );

      // Bind host array (instantiated template) with placeholder as PLSQL
      // table. Returns 1 on success, 0 on failure
      INLINE virtual int bind_tab(const char* name, // placeholder name: ":F1", ":F2"
      			    otl_generic_variable& v
      			    // reference to host array
      			   );

      // Bind "ordinary" host variable/array to placeholder. Returns 1 on
      // success, 0 on failure
      INLINE int bind(const char* name, // placeholder name: ":f1", ":f2"
      		void* buf, // pointer to host variable/array
      		int   elem_size, //array element/ variable size in bytes
      		int   ftype, // Oracle external data type code
      		sb2*  indp=0 // pointer to indicator variable/array
      	       );

      // Bind template host variable/array with already defined
      // name. Returns 1 on success, 0 on failure
      INLINE virtual int bind(otl_generic_variable& v);

      // Bind template host array with already defined name of placeholder
      // as PLSQL table. Returns 1 on success, 0 on failure.
      INLINE virtual int bind_tab(otl_generic_variable& v);

      // ------------ Functions to bind select items (columns) -----------------

      // Bind host variable/array (instantiated template) to column. Returns
      // 1 on success, 0 on failure
      INLINE virtual int bind(int column_num, // column number: 1,2,...
      			otl_generic_variable& v // reference to variable/array
      		       );

      // bind "ordinary" host variable/array to column. Returns 1 on
      // success, 0 on failure
      INLINE int bind(int column_num, // column number: 1,2,...
      		void* buf, // pointer to host variable/array
      		int   elem_size, // array element/variable size in bytes
      		int   ftype, // Oracle external data type code
      		sb2*  indp=0, // pointer to indicator array/varibale
      		ub2*  rlen=0, // pointer to returned length array/variable
      		ub2*  rcode=0 // pointer to returned code array variable
      	       );

      // ------------- Specialized bind functions ------------------

      // Functions to bind "ordinary" host variables/arrays of different
      // data types.

      // ------------- Specialized function to bind columns -------------

      // Bind C-style (null terminated) string variable/array to
      // column. Returns 1 on success, 0 on failure
      INLINE int bind_cstring(int column_num, // column number: 1,2,...
                        char* buf, // pointer to C-string variable/array
                        int elem_size, // array element/variable size
                        sb2*  indp=0, // pointer to indicator array/variable
                        ub2*  rlen=0, // pointer to returned length array/variable
                        ub2*  rcode=0 // pointer to returned code array/variable
                       );

      // Bind int variable/array to column. Returns 1 on success, 0 on
      // failure
      INLINE int bind_int(int column_num, // column number: 1,2,...
      		    int* buf, // pointer to int variable/array
      		    sb2*  indp=0, // pointer to indicator array/variable
      		    ub2*  rlen=0, // pointer to returned length array/variable
      		    ub2*  rcode=0 // pointer to returned code array/variable
      		   );

      // Bind short int variable/array to column. Returns 1 on success, 0 on
      // failure
      INLINE int bind_short(int column_num,// column number: 1,2,...
      		      short* buf, // pointer to short int variable/array
      		      sb2*  indp=0, // pointer to indicator array/variable
      		      ub2*  rlen=0, // pointer to returned length array/variable
      		      ub2*  rcode=0 // pointer to returned code array/variable
      		     );

      // Bind long int variable/array to column. Returns 1 on success, 0 on
      // failure
      INLINE int bind_long_int(int column_num, // column number: 1,2,...
      			 long* buf, // pointer to long int variable/array
      			 sb2*  indp=0, // pointer to indicator array/variable
      			 ub2*  rlen=0, // pointer to returned length array/variable
      			 ub2*  rcode=0 // pointer to returned code array/variable
      			);

      // Bind float variable/array to column. Returns 1 on success, 0 on
      // failure
      INLINE int bind_float(int column_num,// column number: 1,2,...
      		      float* buf, // pointer to float variable/array
      		      sb2*  indp=0, // pointer to indicator array/variable
      		      ub2*  rlen=0, // pointer to returned length array/variable
      		      ub2*  rcode=0 // pointer to returned code array/variable
      		    );

      // Bind double variable/array to column. Returns 1 on success, 0 on
      // failure
      INLINE int bind_double(int column_num,// column number: 1,2,...
      		       double* buf, // pointer to double variable/array
      		       sb2*  indp=0, // pointer to indicator array/variable
      		       ub2*  rlen=0, // pointer to returned length array/variable
      		       ub2*  rcode=0 // pointer to returned code array/variable
      		      );

      // ----------- Specialized function to bind placeholders -------------

      // Bind C-style (null terminated) string variable/array to
      // column. Returns 1 on success, 0 on failure
      INLINE int bind_cstring(const char* name, // placeholder name: ":F1", ":F2"
      			char* buf, // pointer to C-string variable/array
      			int elem_size, // array element/variable size
      			sb2*  indp=0 // pointer to indicator array/variable
      		       );

      // Bind int variable/array to placeholder. Returns 1 on success, 0 on
      // failure
      INLINE int bind_int(const char* name, // placeholder name: ":F1", ":F2"
      		    int* buf, // pointer to int variable/array
      		    sb2*  indp=0 // pointer to indicator array/variable
      		   );

      // Bind short int variable/array to placeholder. Returns 1 on success,
      // 0 on failure
      INLINE int bind_short(const char* name, // placeholder name: ":F1", ":F2"
      		      short* buf, // pointer to short int variable/array
      		      sb2*  indp=0 // pointer to indicator array/variable
      		     );

      // Bind long int variable/array to placeholder. Returns 1 on success,
      // 0 on failure
      INLINE int bind_long_int(const char* name, // placeholder name: ":F1", ":F2"
      			 long* buf, // pointer to long int variable/array
      			 sb2*  indp=0 // pointer to indicator array/variable
      			);

      // Bind float variable/array to placeholder. Returns 1 on success, 0
      // on failure
      INLINE int bind_float(const char* name, // placeholder name: ":F1", ":F2"
      		      float* buf, // pointer to float variable/array
      		      sb2*  indp=0 // pointer to indicator array/variable
      		     );

      // Bind double variable/array to placeholder. Returns 1 on success, 0
      // on failure
      INLINE int bind_double(const char* name, // placeholder name: ":F1", ":F2"
      		       double* buf, // pointer to double variable/array
      		       sb2*  indp=0 // pointer to indicator array/variable
      		      );

      // -------------- End of Specialized bind functions ---------------

      //  Static (in class) function to immediately execute a constant SQL
      //  statement. Returns 1 on success, 0 on failure
      INLINE static int direct_exec(otl_connect& db, // connect object
      			      const char* stm, // statement
      			      int exception_enabled=otl_exception::enabled
      			      // exception_enabled flag
      			     );

      // Check "end-of-file" condition when fetching rows from select
      // statement. Returns 1 when "EOF", 0 otherwise
      INLINE int eof(void);

      // describe select item (column)
      INLINE virtual int describe_column(
      		    otl_column_desc& col_desc, // column descriptor structure
      		    int column_num // column number: 1,2,...
      		   );

      // "End-of-description" condition check
      INLINE int end_of_desc(void);

      // Parse statement and bind variables
      INLINE int parse(const char* sqlstm, // SQL statement
      		 otl_generic_variable** v // pointer to variable list
      		);

      protected:

      // Internal stuff

       int vl_len; // variable list length
       otl_p_generic_variable* vl; // variable list
       otl_connect* adb; // pointer to connect object

       INLINE void alloc_var(otl_p_generic_variable* vp);

       // Parse sql statement. Returns 1 on success, 0 on failure
       INLINE int parse(void);

      };

      // Specialized cursor -- OTL select cursor class
      class otl_select_cursor: public otl_cursor{

      public:

       int cur_row; // index of current row in host array
       int cur_size; // number of rows in the buffer after most recent fetch
       int row_count; // row count -- total number of rows fetched
       int array_size; // size of host array attached to select statement

      // General constructor
      INLINE otl_select_cursor(otl_connect& db, // connect object
      			 short arr_size=1, // attached host array size
      			 int exception_enabled=otl_exception::enabled
      			 // exception enabled flag
      			);

      // Default constructor
      INLINE otl_select_cursor(int exception_enabled=otl_exception::enabled);

      // Open cursor
      INLINE int open(otl_connect& db, // connect object
                      short arr_size=1, // attached host array size
                      int exception_enabled=otl_exception::enabled
                                // exception enabled flag
                     );

      // Close cursor
      INLINE int close(void);

      // Fetch first row. rows are fetched in batches. "cur_row" points to
      // current row in host array attached to select statement.
      // cur_row==-1 if no rows have been fetched. Returns 1 on success, 0
      // on failure.
      INLINE int first(void);

      // Fetch next row. "cur_row" points to current row.  cur_row==-1 after
      // fetch sequence is complete. Returns 1 on success, 0 on failure.
      INLINE int next(void);

      };

      const int otl_var_list_size=256;
        // maximum size of OTL select list

      // Specialized cursor -- OTL reference cursor class.  This
      // class implements Cursor References which were introduced in
      // Oracle 7.2
      class otl_ref_cursor: public otl_cursor{

      public:

       int cur_row; // index of current row in host array
       int cur_size; // number of rows in the buffer after most recent fetch
       int row_count; // row count -- total number of rows fetched
       int array_size; // size of host array attached to select statement

      // General constructor
      INLINE otl_ref_cursor(otl_connect& db, // connect object
      		      const char* cur_placeholder_name,
      		        // cursor reference placeholder name
      		      short arr_size=1, // attached host array size
      		      int exception_enabled=otl_exception::enabled
      		      // exception enabled flag
      		     );

      // Default constructor
      INLINE otl_ref_cursor(int exception_enabled=otl_exception::enabled
      		      // exception enabled flag
      		     );

      // Close cursor (if opened) and destruct object
      INLINE ~otl_ref_cursor();

      // Open cursor
      INLINE int open(otl_connect& db, // connect object
                      const char* cur_placeholder_name,
                         // cursor reference placeholder name
                      short arr_size=1, // attached host array size
                      int exception_enabled=otl_exception::enabled
                        // exception enabled flag
                     );

      // Close cursor
      INLINE int close(void);

      // Fetch first row. rows are fetched in batches. "cur_row" points to
      // current row in host array attached to select statement.
      // cur_row==-1 if no rows have been fetched. Returns 1 on success, 0
      // on failure.
      INLINE int first(void);

      // Fetch next row. "cur_row" points to current row.  cur_row==-1 after
      // fetch sequence is complete. Returns 1 on success, 0 on failure.
      INLINE int next(void);

      // Bind host variable/array (instantiated template) to column. Returns
      // 1 on success, 0 on failure
      INLINE int bind(int column_num, // column number: 1,2,...
      		otl_generic_variable& v // reference to variable/array
      	       );

      // Bind template host variable/array with already defined
      // name. Returns 1 on success, 0 on failure
      INLINE int bind(otl_generic_variable& v);

      // Bind host variable/array (instantiated template) to
      // placeholder. Returns 1 on success, 0 on failure
      INLINE int bind(const char* name, // placeholder name: ":F1", ":F2"
      		otl_generic_variable& v // reference to host variable/array
      	       );

      // Describe reference cursor's select item list. Returns 1 on success,
      // 0 on failure.
      INLINE int describe_select(
      	       otl_column_desc* desc, // pointer to array of column
      				      // descriptors
      	       int& desc_len // actual number of columns
      	      );

      protected:

       int rvl_len; // variable list length
       otl_p_generic_variable* rvl; // reference cursor variable (column) list
       int vl_cur_len; // Current size of the vl array
       otl_cursor sel_cur; // cursor, used for the reference cursor
       char cur_placeholder[64]; // Reference cursor placeholder name
      };

      const int otl_max_long_size=32760;
       // max default size of LONG and LONG RAW internal data types in
       // otl_select_stream.

      // OTL Select Stream class. Dynamically allocates default select list
      // items (output columns) by the information from the describe_column
      // function.
      // This class may issue the following otl_exceptions:
      //
      //    Incompatible data types in stream operation, code=32000
      //    Not all input variables have been initialized, code=32004
      //    No input variables have beed defined in SELECT statement, code=32004
      //
      class otl_select_stream: public otl_select_cursor{
      public:

      // General conctructor. SELECT statement is parsed, all input host
      // variables and output columns are automatically bound.
      INLINE otl_select_stream(otl_connect& db, // connect object
      			 const char* sqlstm, // SELECT statement
      			 const short arr_size, // output host arrays size
      			 ... // NULL terminated list of pointers to input host
      			     // variables.
      		        );

      // General conctructor. SELECT statement is parsed, all input host
      // variables and output columns are automatically bound. The
      // difference between this constructor and the constuctor above is
      // that this constuctor takes a pointer to an array of pointer to the
      // host variable/array list, instead of taking them from stack, as the
      // above constuctor does. This allows the user to dynamically create
      // host variables, say, in a loop, and collect pointers to the variables
      // into an array.
      INLINE otl_select_stream(otl_connect& db, // connect object
      			 const char* sqlstm, // SELECT statement
      			 otl_p_generic_variable* avp,
       			   // Pointer to NULL terminated list of
       			   // pointers to input hots
      			   // variables
      			 const short arr_size=1 // output host arrays size
      			 );

      // General conctructor. SELECT statement is parsed, all input host
      // variables and output columns are automatically allocated and
      // bound. This constructor allows the user to use extended
      // place-holder declarations:
      //
      //    :NAME<DATA_TYPE>
      //
      // The following data types are available in the extended
      // declarations:
      //
      //  :VAR<char[<length>]>, e.g. :F1<char[32]>
      //  :VAR<int>
      //  :VAR<short>
      //  :VAR<long>
      //  :VAR<unsigned>
      //  :VAR<float>
      //  :VAR<double>
      //
      INLINE otl_select_stream(
      			 const short arr_size, // output host arrays size
      			 const char* sqlstm, // SELECT statement
      			 otl_connect& db // connect object
      			);

      // Destructor
      INLINE ~otl_select_stream();

      // Rewind stream, SQL statement is re-executed. Input host variables
      // of SELECT statement may be re-assigned before calling this
      // function.
      INLINE void rewind(void);

      // Test if NULL has been fetched during last stream operation
      INLINE int is_null(void);

      // Test if "end-of-file" has been reached. Returns 1 when
      // "end-of-file".
      INLINE int eof(void);

      // Read objects from stream

      INLINE otl_select_stream& operator>>(char& c);
      INLINE otl_select_stream& operator>>(unsigned char& c);
      INLINE otl_select_stream& operator>>(char* s);
      INLINE otl_select_stream& operator>>(unsigned char* s);
      INLINE otl_select_stream& operator>>(int& n);
      INLINE otl_select_stream& operator>>(unsigned& u);
      INLINE otl_select_stream& operator>>(short& sh);
      INLINE otl_select_stream& operator>>(long int& l);
      INLINE otl_select_stream& operator>>(float& f);
      INLINE otl_select_stream& operator>>(double& d);

      // Write input values to the stream
      // (initialize input variables)

      INLINE otl_select_stream& operator<<(const char c);
      INLINE otl_select_stream& operator<<(const unsigned char c);
      INLINE otl_select_stream& operator<<(const char* s);
      INLINE otl_select_stream& operator<<(const unsigned char* s);
      INLINE otl_select_stream& operator<<(const int n);
      INLINE otl_select_stream& operator<<(const unsigned u);
      INLINE otl_select_stream& operator<<(const short sh);
      INLINE otl_select_stream& operator<<(const long int l);
      INLINE otl_select_stream& operator<<(const float f);
      INLINE otl_select_stream& operator<<(const double d);

      // Get info on SELECT list items

      INLINE int select_list_len(void);
      INLINE int column_ftype(int ndx=0);
      INLINE int column_size(int ndx=0);

      // Get column's internal info

       otl_column_desc* sl_desc; // column descriptor array

      // Set flag "delete input host variables"
      INLINE void set_delete_var(const int should_delete=0);

      protected:

       otl_dynamic_variable* sl; // Select list -- output columns.
       int sl_len; // Select list length

       int null_fetched; // "NULL fetched" flag
       int ret_code; // "return code" variable
       int cur_col; // current column index
       int cur_in; // current input variable index
       int executed; // "statement executed" flag

      // Internal stuff

       int should_delete_flag; // "should delete host variables" flag

       INLINE void init(int);
       INLINE void get_next(void);
       INLINE int check_type(int type_code);
       INLINE void look_ahead(void);
       INLINE void bind_all(void);
       INLINE void get_select_list(void);

       INLINE void get_in_next(void);
       INLINE int check_in_type(int type_code,int tsize);

       INLINE void check_in_var(void);
       INLINE void check_if_executed(void);

      public:

      // Special constructor. It parses SELECT statement and gets SELECT
      // list information. This constructor can be used when the user does
      // not really want to fetch rows via this stream but wants to get
      // information on the SELECT list (output columns).

      INLINE otl_select_stream(const char* sqlstm, // SELECT statement
      			 otl_connect& db, // connect object
      			 const char* dummy_par // dummy parameter (needed for
      				               // making the function
      				              // prototype unique
      			);

      };

      class otl_ref_select_stream: public otl_ref_cursor{
      public:

      // General conctructor. PLSQL block is parsed, all input host
      // variables and output columns are automatically bound.
      INLINE otl_ref_select_stream(
      			 otl_connect& db, // connect object
      			 const char* sqlstm, // PLSQL block
      			 const char* cur_placeholder,
      			   // reference cursor placeholder
      			 const short arr_size, // output host arrays size
      			 ... // NULL terminated list of pointers to input host
      			     // variables.
      		        );

      // General conctructor. PLSQL block is parsed, all input host
      // variables and output columns are automatically bound. The
      // difference between this constructor and the constuctor above is
      // that this constuctor takes a pointer to an array of pointer to the
      // host variable/array list, instead of taking them from stack, as the
      // above constuctor does. This allows the user to dynamically create
      // host variables, say, in a loop, and collect pointers to the variables
      // into an array.
      INLINE otl_ref_select_stream(
      			 otl_connect& db, // connect object
      			 const char* sqlstm, // PLSQL block
      			 const char* cur_placeholder,
      			   // reference cursor placeholder
      			 otl_p_generic_variable* avp,
       			   // Pointer to NULL terminated list of
       			   // pointers to input hots
      			   // variables
      			 const short arr_size=1 // output host arrays size
      			 );

      // General conctructor. PLSQL block is parsed, all input host
      // variables and output columns are automatically allocated and
      // bound. This constructor allows the user to use extended
      // place-holder declarations:
      //
      //    :NAME<DATA_TYPE>
      //
      // The following data types are available in the extended
      // declarations:
      //
      //  :VAR<char[<length>]>, e.g. :F1<char[32]>
      //  :VAR<int>
      //  :VAR<short>
      //  :VAR<long>
      //  :VAR<unsigned>
      //  :VAR<float>
      //  :VAR<double>
      //
      INLINE otl_ref_select_stream(
      			 const short arr_size, // output host arrays size
      			 const char* sqlstm, // PLSQL block
      			 const char* cur_placeholder,
      			   // reference cursor placeholder
      			 otl_connect& db // connect object
      			);

      // Destructor
      INLINE ~otl_ref_select_stream();

      // Rewind stream, PLSQL block is re-executed. Input host variables
      // of the block may be re-assigned before calling this
      // function.
      INLINE void rewind(void);

      // Test if NULL has been fetched during last stream operation
      INLINE int is_null(void);

      // Test if "end-of-file" has been reached. Returns 1 when
      // "end-of-file".
      INLINE int eof(void);

      // Read objects from stream

      INLINE otl_ref_select_stream& operator>>(char& c);
      INLINE otl_ref_select_stream& operator>>(unsigned char& c);
      INLINE otl_ref_select_stream& operator>>(char* s);
      INLINE otl_ref_select_stream& operator>>(unsigned char* s);
      INLINE otl_ref_select_stream& operator>>(int& n);
      INLINE otl_ref_select_stream& operator>>(unsigned& u);
      INLINE otl_ref_select_stream& operator>>(short& sh);
      INLINE otl_ref_select_stream& operator>>(long int& l);
      INLINE otl_ref_select_stream& operator>>(float& f);
      INLINE otl_ref_select_stream& operator>>(double& d);

      // Write input values to the stream
      // (initialize input variables)

      INLINE otl_ref_select_stream& operator<<(const char c);
      INLINE otl_ref_select_stream& operator<<(const unsigned char c);
      INLINE otl_ref_select_stream& operator<<(const char* s);
      INLINE otl_ref_select_stream& operator<<(const unsigned char* s);
      INLINE otl_ref_select_stream& operator<<(const int n);
      INLINE otl_ref_select_stream& operator<<(const unsigned u);
      INLINE otl_ref_select_stream& operator<<(const short sh);
      INLINE otl_ref_select_stream& operator<<(const long int l);
      INLINE otl_ref_select_stream& operator<<(const float f);
      INLINE otl_ref_select_stream& operator<<(const double d);

      // Get info on SELECT list items

      INLINE int select_list_len(void);
      INLINE int column_ftype(int ndx=0);
      INLINE int column_size(int ndx=0);

      // Get column's internal info

       otl_column_desc* sl_desc; // column descriptor array

      // Set flag "delete input host variables"
      INLINE void set_delete_var(const int should_delete=0);

      protected:

       otl_dynamic_variable* sl; // Select list -- output columns.
       int sl_len; // Select list length

       int null_fetched; // "NULL fetched" flag
       int ret_code; // "return code" variable
       int cur_col; // current column index
       int cur_in; // current input variable index
       int executed; // "statement executed" flag

      // Internal stuff

       int should_delete_flag; // "should delete host variables" flag

       INLINE void init(int);
       INLINE void get_next(void);
       INLINE int check_type(int type_code);
       INLINE void look_ahead(void);
       INLINE void get_select_list(void);

       INLINE void get_in_next(void);
       INLINE int check_in_type(int type_code,int tsize);

       INLINE void check_in_var(void);
       INLINE void check_if_executed(void);

      };

      // OTL NULL class. This class is used only for writing NULL into OTL
      // stream classes.
      class otl_null{
      public:
       otl_null(){}
       ~otl_null(){}
      };

      // OTL output stream. It should be used for the following SQL statements:
      //
      //   DELETE;
      //   UPDATE;
      //   INSERT;
      //   PL/SQL block with input parameters;
      //
      class otl_out_stream: public otl_cursor{
      public:

      // Default constructor
      INLINE otl_out_stream(otl_connect& db);

      // General conctructor. SQL statement is parsed, all ouput host
      // variables are automatically bound.
      INLINE otl_out_stream(otl_connect& db, // connect object
      		      const char* sqlstm, // SQL statement
      		      ...// NULL terminated list of pointers to input host
      		         // variables.
      		     );

      // General conctructor. SQL statement is parsed, all ouput host
      // variables are automatically bound.
      INLINE otl_out_stream(otl_connect& db, // connect object
      		      const char* sqlstm, // SQL statement
      		      otl_p_generic_variable* avp
      		      // Pointer to NULL terminated list of pointers
      		      // to input hots variables.

      		  );

      // General conctructor. SQL statement is parsed, all host variables
      // are automatically allocated and bound. This constructor allows the
      // user to use extended place-holder declarations:
      //
      //    :NAME<DATA_TYPE>
      //
      // The following data types are available in the extended
      // declarations:
      //
      //  :VAR<char[<length>]>, e.g. :F1<char[32]>
      //  :VAR<int>
      //  :VAR<short>
      //  :VAR<long>
      //  :VAR<unsigned>
      //  :VAR<float>
      //  :VAR<double>
      //
      INLINE otl_out_stream(
      		      short arr_size, // host array size
      		      const char* sqlstm, // SQL statement
      		      otl_connect& db // connect object
      		     );

      // Destructor
      INLINE ~otl_out_stream();

      // Write objects into stream

      INLINE otl_out_stream& operator<<(const char c);
      INLINE otl_out_stream& operator<<(const unsigned char c);
      INLINE otl_out_stream& operator<<(const char* s);
      INLINE otl_out_stream& operator<<(const unsigned char* s);
      INLINE otl_out_stream& operator<<(const int n);
      INLINE otl_out_stream& operator<<(const unsigned u);
      INLINE otl_out_stream& operator<<(const short sh);
      INLINE otl_out_stream& operator<<(const long int l);
      INLINE otl_out_stream& operator<<(const float f);
      INLINE otl_out_stream& operator<<(const double d);
      INLINE otl_out_stream& operator<<(const otl_null n); // write Oracle NULL into stream

      // Flush stream buffer. SQL statement is executed as many times as the
      // rows have been entered into the stream buffer.
      INLINE virtual void flush(void);

      // Clean up stream buffer without flushing it.
      INLINE virtual void clean(void);

      // Set "auto-commit" flag. When the buffer is flushed, current
      // transaction is automatically commited, if the flag is set.
      INLINE void set_commit(int auto_commit=0);

      // Set flag "delete host variables"
      INLINE void set_delete_var(const int should_delete=0);

      protected:

       int auto_commit_flag; // "auto-commit" flag
       int dirty; // "dirty buffer" flag
       int should_delete_flag; // "should delete" flag

       int cur_x; //current variable/array index (X-coordinate)
       int cur_y; // current array element index (Y-coordinate)

       short array_size; // host variables/arrays size
       otl_connect* connect; // connect object

       int in_exception_flag; // "in exception" flag
       int in_destruct_flag; // "in destructor" flag

       INLINE void init(otl_connect& db,int should_delete=0);
       INLINE void get_next(void);
       INLINE int check_type(int type, int tsize);
       INLINE void check_buf(void);

      };

      // OTL input/output stream. It is used primarily for PL/SQL blocks
      // with  input and output parameters. Though, this stream class can be
      // used for SQL statements and PL/SQL blocks with input or output
      // parameters only.
      //
      class otl_inout_stream: public otl_out_stream{
      public:

      // General conctructor. SQL statement is parsed, all host input and
      // output hostvariables are automatically allocated and bound. This
      // constructor allows the user to use extended place-holder
      // declarations:
      //
      //    :NAME<DATA_TYPE,ACCESS_TYPE>
      //
      // The following data types are available in the extended
      // declarations:
      //
      //  :VAR<char[<length>]>, e.g. :F1<char[32]>
      //  :VAR<int>
      //  :VAR<short>
      //  :VAR<long>
      //  :VAR<unsigned>
      //  :VAR<float>
      //  :VAR<double>
      //
      // The following access types are available in the extended
      // declarations:
      //
      //  in    -- input variable
      //  out    -- output variable
      //  inout  -- input/output variable
      //
      INLINE otl_inout_stream(
      			 short arr_size, // host array size
      			 const char* sqlstm, // SQL statement
      			 otl_connect& db // connect object
      			);

      INLINE ~otl_inout_stream();

      // Test if all data has been already read from the stream
      INLINE int eof(void);

      // Flush stream's output buffer. It actually means to execute the SQL
      // statement as many times as rows entered to the output buffer. The
      // stream is automatically flushed when the buffer gets full.
      INLINE void flush(void);

      // Clean up buffer without flushing it.
      INLINE void clean(void);

      // Rewind stream
      INLINE void rewind(void);

      // Test if NULL was fetched from the stream
      INLINE int is_null(void);

      // Read objects from stream

      INLINE otl_inout_stream& operator>>(char& c);
      INLINE otl_inout_stream& operator>>(unsigned char& c);
      INLINE otl_inout_stream& operator>>(char* s);
      INLINE otl_inout_stream& operator>>(unsigned char* s);
      INLINE otl_inout_stream& operator>>(int& n);
      INLINE otl_inout_stream& operator>>(unsigned& u);
      INLINE otl_inout_stream& operator>>(short& sh);
      INLINE otl_inout_stream& operator>>(long int& l);
      INLINE otl_inout_stream& operator>>(float& f);
      INLINE otl_inout_stream& operator>>(double& d);

      protected:

      // Internal stuff

       otl_p_generic_variable*  in_vl; // input variable list
       int iv_len; // input variable list length
       int cur_in_x; // current input array index (input X-coordinate)
       int cur_in_y; // current input array element index (input Y-coordinate)
       int in_y_len; // input variable list length
       int null_fetched; // "NULL fetched" flag
       otl_p_generic_variable*  avl; // list of all variables
       int avl_len; // length of list of all variables

       INLINE void get_in_next(void);
       INLINE int check_in_type(int type_code,int tsize);
       INLINE int is_null_intern(void);

      };

      // OTL stream class. This is a general-purpose stream class, unified
      // for streams of all types.
      class otl_stream{
      public:

      // General conctructor. SQL statement is parsed, all host input and
      // output host variables are automatically allocated and bound. This
      // constructor allows the user to use extended place-holder
      // declarations:
      //
      //    :NAME<DATA_TYPE,ACCESS_TYPE>
      //
      // The following data types are available in the extended
      // declarations:
      //
      //  :VAR<char[<length>]>, e.g. :F1<char[32]>
      //  :VAR<int>
      //  :VAR<short>
      //  :VAR<long>
      //  :VAR<unsigned>
      //  :VAR<float>
      //  :VAR<double>
      //
      // The following access types are available in the extended
      // declarations:
      //
      //  in    -- input variable
      //  out    -- output variable
      //  inout  -- input/output variable
      //
      // Access types need to be defined only for PL/SQL blocks to avoid
      // ambiguity.
      INLINE otl_stream(short arr_size, // host array size
      		  const char* sqlstm, // SQL statement
      		  otl_connect& db, // connect object
      		  const char* ref_cur_placeholder=0
      		   // reference cursor placeholder, e.g. ":cur"
      		 );

      INLINE otl_stream();

      // Desctructor
      INLINE ~otl_stream();

      // Test if all data has been already read from the stream
      INLINE int eof(void);

      // Flush stream's output buffer. It actually means to execute the SQL
      // statement as many times as rows entered to the output buffer. The
      // stream is automatically flushed when the buffer gets full.
      INLINE void flush(void);

      // Clean up buffer without flushing it.
      INLINE void clean(void);

      // Rewind stream
      INLINE void rewind(void);

      // Test if NULL was fetched from the stream
      INLINE int is_null(void);

      // Set "auto-commit" flag. When the buffer is flushed, current
      // transaction is automatically commited, if the flag is set.
      INLINE void set_commit(int auto_commit=0);

      // Open stream
      INLINE void open(short arr_size, // host array size
      		 const char* sqlstm, // SQL statement
      		 otl_connect& db, // connect object
      		 const char* ref_cur_placeholder=0
      		   // reference cursor placeholder, e.g. ":cur"
      		);
      // Close stream
      INLINE void close(void);

      // Test if the stream was opened okay
      INLINE int good(void);

      // Read objects from stream

      INLINE otl_stream& operator>>(char& c);
      INLINE otl_stream& operator>>(unsigned char& c);
      INLINE otl_stream& operator>>(char* s);
      INLINE otl_stream& operator>>(unsigned char* s);
      INLINE otl_stream& operator>>(int& n);
      INLINE otl_stream& operator>>(unsigned& u);
      INLINE otl_stream& operator>>(short& sh);
      INLINE otl_stream& operator>>(long int& l);
      INLINE otl_stream& operator>>(float& f);
      INLINE otl_stream& operator>>(double& d);

      // Write objects into stream

      INLINE otl_stream& operator<<(const char c);
      INLINE otl_stream& operator<<(const unsigned char c);
      INLINE otl_stream& operator<<(const char* s);
      INLINE otl_stream& operator<<(const unsigned char* s);
      INLINE otl_stream& operator<<(const int n);
      INLINE otl_stream& operator<<(const unsigned u);
      INLINE otl_stream& operator<<(const short sh);
      INLINE otl_stream& operator<<(const long int l);
      INLINE otl_stream& operator<<(const float f);
      INLINE otl_stream& operator<<(const double d);
      INLINE otl_stream& operator<<(const otl_null n); // write Oracle NULL into stream

      // C-like printf/scanf functions
      // The following format specifiers are supported:
      //
      //    %d  - int
      //    %u  - unsigned
      //    %ld - long int
      //    %f  - float
      //    %lf - double
      //    %c  - char
      //    %s  - string
      //
      // There is a specifier for writing NULL into stream:
      //
      //    %N  - NULL
      //
      // This couple of functions is intended for those who likes the C
      // style better than C++ streams.

      INLINE void printf(const char* fmt,...);
      INLINE void scanf(const char* fmt,...);

      protected:

       otl_ref_select_stream* ref_ss; // reference (cursor) select stream
       otl_select_stream* ss; // ordinary select stream
       otl_inout_stream* io; // input/ouput stream

      public:

       otl_connect* adb; // pointer to connect object for use in Prosto*C

      // Functions which contain actual implementation of  printf/scanf.

       INLINE void vprintf(char* fmt,va_list argv);
       INLINE void vscanf(char* fmt,va_list argv);

      };

      // OTL error info class. Should be used in case of manual error
      // handling in order to get more detailed error information

      class otl_err_info: public otl_exception{

      public:

      // Default constructor
       otl_err_info(){}

      // Create error info object from otl_connect object
       otl_err_info(otl_connect& connect){init(connect.lda);}

      // Create error info object from otl_cursor object
       otl_err_info(otl_cursor& cur){init(cur.cda);}

      // Read error info into existing error info descriptor
       void get_info(otl_connect& connect){init(connect.lda);}
       void get_info(otl_cursor& cur){init(cur.cda,cur.stm_text);}

      };

      // ========================= Template class section ==========================

      // OTL template variable class. allows to construct specialized
      // template variable classes (see below)

      template <class T, int atype>
      class otl_variable: public otl_generic_variable{

      public:

       T v; // host varibale of data type T

       sb2 ind; // indicator
       ub2 rlen; // returned length
       ub2 rcode; // returned code

      // Default constructor
       otl_variable(){init();}

      // Construct variable by the name of aname, e.g. ":F1"
       otl_variable(const char* aname)
       {
        init();
        copy_name(aname);
       }

      // Construct variable/column, with its further use in a select list in
      // the column_num position.
       otl_variable(const int column_num)
       {
        init();
        copy_pos(column_num);
       }

      // Variables with either name or column_num initialized may be used in
      // the parse functions of OTL cursor classes, which variable lists of
      // parameters.

      // Initialize host variable internals
       void init()
       {
        ind=sizeof(T)>32767?0:(sb2)sizeof(T);
        rcode=0;
        ftype=atype;
        p_v=(ub1*)&v;
        p_ind=&ind;
        p_rlen=&rlen;
        p_rcode=&rcode;
        elem_size=sizeof(T);
        rlen=(ub2)elem_size;
        array_size=1;
        max_tab_len=(ub4)array_size;
        cur_tab_len=(ub4)array_size;
       }

      };

      // OTL template host array class. allows to construct specialized
      // template array classes (see below)

      template <class T, const int atype, const short size>
      class otl_array: public otl_generic_variable{

      public:

       T v[size]; // host array

       sb2 ind[size]; // indicator array
       ub2 rlen[size]; // returned length array
       ub2 rcode[size]; // returned code array

      // Default constructor
       otl_array(){init();}

      // Construct array by the name of aname, e.g. ":F1"
       otl_array(const char* aname)
       {
        init();
        copy_name(aname);
       }

      // Construct array-column, with its further use in a select list in
      // the column_num position.
       otl_array(const int column_num)
       {
        init();
        copy_pos(column_num);
       }

      // Initialize host array internals
       void init(void)
       {
        for(int i=0;i<size;++i){
         ind[i]=sizeof(T);
         rlen[i]=sizeof(T);
         rcode[i]=0;
        }
        ftype=atype;
        p_v=(ub1*)&v[0];
        p_ind=&ind[0];
        p_rlen=&rlen[0];
        p_rcode=&rcode[0];
        elem_size=sizeof(T);
        array_size=size;
        max_tab_len=(ub4)array_size;
        cur_tab_len=(ub4)array_size;
       }

      };

      // OTL DATE class

      typedef otl_variable<otl_date_intern,(const int)extDate> otl_date;

      // OTL ROWID class

      typedef otl_variable<otl_rowid_intern,(const int)extRowId> otl_rowid;

      // OTL VARNUM class

      typedef otl_variable<otl_varnum_intern,(const int)extVarNum> otl_varnum;

      // OTL NUMBER class

      typedef otl_variable<otl_number_intern,(const int)extNumber> otl_number;

      // OTL DATE ARRAY

      template <short size> class otl_date_array:
       public otl_array<otl_date_intern,(const int)extDate,size>{

      public:

       otl_date_array()
        :otl_array<otl_date_intern,(const int)extDate,size>(){}

       otl_date_array(const char* aname)
        :otl_array<otl_date_intern,(const int)extDate,size>(aname){}

       otl_date_array(const int  column_num)
        :otl_array<otl_date_intern,(const int)extDate,size>(column_num){}

      };

      // OTL ROWID ARRAY

      template <short size> class otl_rowid_array:
       public otl_array<otl_rowid_intern,(const int)extRowId,size>{

      public:

       otl_rowid_array()
        :otl_array<otl_rowid_intern,(const int)extRowId,size>(){}

       otl_rowid_array(const char *aname)
        :otl_array<otl_rowid_intern,(const int)extRowId,size>(aname){}

       otl_rowid_array(const int column_num)
        :otl_array<otl_rowid_intern,(const int)extRowId,size>(column_num){}

      };

      // OTL VAR NUM ARRAY

      template <short size> class otl_varnum_array:
       public otl_array<otl_varnum_intern,(const int)extVarNum,size>{

      public:

       otl_varnum_array()
        :otl_array<otl_varnum_intern,(const int)extVarNum,size>(){}

       otl_varnum_array(const char* aname)
        :otl_array<otl_varnum_intern,(const int)extVarNum,size>(aname){}

       otl_varnum_array(const int column_num)
        :otl_array<otl_varnum_intern,(const int)extVarNum,size>(column_num){}

      };

      // OTL NUMBER ARRAY

      template <short size> class otl_number_array:
       public otl_array<otl_number_intern,(const int)extNumber,size>{

      public:

       otl_number_array()
        :otl_array<otl_number_intern,(const int)extNumber,size>(){}

       otl_number_array(const char *aname)
        :otl_array<otl_number_intern,(const int)extNumber,size>(aname){}

       otl_number_array(const int column_num)
        :otl_array<otl_number_intern,(const int)extNumber,size>(column_num){}

      };

      // There is a little use of these data types. As recommended in the
      // OCI documentation, it is better to convert internal data types to
      // strings. These classes are defined for completeness only.

      // ------------------- Numerical data types -------------------------

      // Numerical scalar classes

      // OTL DOUBLE class

      typedef otl_variable<double,(const int)extFloat> otl_double;

      // OTL FLOAT class

      typedef otl_variable<float,(const int)extFloat> otl_float;

      // OTL SIGNED CHAR class

      typedef otl_variable<char,(const int)extInt> otl_signed_char;

      // OTL SHORT INT class

      typedef otl_variable<short,(const int)extInt> otl_short_int;

      // OTL INT class

      typedef otl_variable<int,(const int)extInt> otl_int;

      // OTL LONG INT class

      typedef otl_variable<long int,(const int)extInt> otl_long_int;

      // OTL UNSIGNED INT class
      typedef otl_variable<unsigned int,(const int)extInt> otl_unsigned;

      // Numerical array classes

      // OTL DOUBLE ARRAY

      template <short size> class otl_double_array:
       public otl_array<double,(const int)extFloat,size>{

      public:

       otl_double_array()
        :otl_array<double,(const int)extFloat,size>(){}

       otl_double_array(const char* aname)
        :otl_array<double,(const int)extFloat,size>(aname){}

       otl_double_array(const int column_num)
        :otl_array<double,(const int)extFloat,size>(column_num){}

      };

      // OTL FLOAT ARRAY

      template <short size> class otl_float_array:
       public otl_array<float,(const int)extFloat,size>{

      public:

       otl_float_array()
        :otl_array<float,(const int)extFloat,size>(){}

       otl_float_array(const char* aname)
        :otl_array<float,(const int)extFloat,size>(aname){}

       otl_float_array(const int column_num)
        :otl_array<float,(const int)extFloat,size>(column_num){}
      };

      // OTL SIGNED CHAR ARRAY

      template <short size> class otl_signed_char_array:
       public  otl_array<char,(const int)extInt,size>{

      public:

       otl_signed_char_array()
        :otl_array<char,(const int)extInt,size>(){}

       otl_signed_char_array(const char *aname)
        :otl_array<char,(const int)extInt,size>(aname){}

       otl_signed_char_array(const int column_num)
        :otl_array<char,(const int)extInt,size>(column_num){}

      };

      // OTL SHORT INT ARRAY

      template <short size> class otl_short_int_array:
       public otl_array<short,(const int)extInt,size>{

      public:

       otl_short_int_array()
        :otl_array<short,(const int)extInt,size>(){}

       otl_short_int_array(const char *aname)
        :otl_array<short,(const int)extInt,size>(aname){}

       otl_short_int_array(const int column_num)
        :otl_array<short,(const int)extInt,size>(column_num){}

      };

      // OTL INT ARRAY

      template <short size> class otl_int_array:
       public otl_array<int,(const int)extInt,size>{

      public:

       otl_int_array()
        :otl_array<int,(const int)extInt,size>(){}

       otl_int_array(const char *aname)
        :otl_array<int,(const int)extInt,size>(aname){}

       otl_int_array(const int column_num)
        :otl_array<int,(const int)extInt,size>(column_num){}

      };

      // OTL LONG INT ARRAY

      template <short size> class otl_long_int_array:
       public otl_array<long int,(const int)extInt,size>{

      public:

       otl_long_int_array()
        :otl_array<long int,(const int)extInt,size>(){}

       otl_long_int_array(const char* aname)
        :otl_array<long int,(const int)extInt,size>(aname){}

       otl_long_int_array(const int column_num)
        :otl_array<long int,(const int)extInt,size>(column_num){}

      };

      // OTL UNSIGNED INT ARRAY

      template <short size> class otl_unsigned_array:
       public otl_array<unsigned int,(const int)extInt,size>{

      public:

       otl_unsigned_array()
        :otl_array<unsigned int,(const int)extInt,size>(){}

       otl_unsigned_array(const char *aname)
        :otl_array<unsigned int,(const int)extInt,size>(aname){}

       otl_unsigned_array(const int column_num)
        :otl_array<unsigned int,(const int)extInt,size>(column_num){}

      };

      // ----------------- String data types -------------------------

      // String scalar classes

      // Null terminated string
      // (!!) most useful in C++ programs

      template <int str_size> class otl_cstring:
       public otl_variable<char[str_size],(const int)extCChar>{

      public:

       otl_cstring():
        otl_variable<char[str_size],(const int)extCChar>(){}

       otl_cstring(const int column_num):
        otl_variable<char[str_size],(const int)extCChar>(column_num){}

       otl_cstring(const char* aname):
        otl_variable<char[str_size],(const int)extCChar>(aname){}

      };

      // OTL VARCHAR2 class

      template <int str_size> class otl_varchar2:
       public otl_variable<char[str_size],(const int)extVarChar2>{

      public:

       otl_varchar2()
        :otl_variable<char[str_size],(const int)extVarChar2>(){}

       otl_varchar2(const char *aname)
        :otl_variable<char[str_size],(const int)extVarChar2>(aname){}

       otl_varchar2(const int column_num)
        :otl_variable<char[str_size],(const int)extVarChar2>(column_num){}

      };

      // OTL LONG class

      template <int str_size> class otl_long:
       public otl_variable<char[str_size],(const int)extLong>{

      public:

       otl_long()
        :otl_variable<char[str_size],(const int)extLong>(){}

       otl_long(const char *aname)
        :otl_variable<char[str_size],(const int)extLong>(aname){}

       otl_long(const int column_num)
        :otl_variable<char[str_size],(const int)extLong>(column_num){}

      };

      // OTL VARCHAR class (see the OCI manual, chapter 3)

      template <int str_size> class otl_varchar:
       public otl_variable<char[str_size],(const int)extVarChar>{

      public:

       otl_varchar()
        :otl_variable<char[str_size],(const int)extVarChar>(){}

       otl_varchar(const char *aname)
        :otl_variable<char[str_size],(const int)extVarChar>(aname){}

       otl_varchar(const int column_num)
        :otl_variable<char[str_size],(const int)extVarChar>(column_num){}

      };

      // OTL VARRAW class

      template <int str_size> class otl_varraw:
       public otl_variable<unsigned char[str_size],(const int)extVarRaw>{

      public:

       otl_varraw()
        :otl_variable<unsigned char[str_size],(const int)extVarRaw>(){}

       otl_varraw(const char *aname)
        :otl_variable<unsigned char[str_size],(const int)extVarRaw>(aname){}

       otl_varraw(const int column_num)
        :otl_variable<unsigned char[str_size],(const int)extVarRaw>(column_num){}

      };

      // OTL RAW class

      template <ub1 str_size> class otl_raw:
       public otl_variable<unsigned char[str_size],(const int)extRaw>{

      public:

       otl_raw()
        :otl_variable<unsigned char[str_size],(const int)extRaw>(){}

       otl_raw(const char *aname)
        :otl_variable<unsigned char[str_size],(const int)extRaw>(aname){}

       otl_raw(const int column_num)
        :otl_variable<unsigned char[str_size],(const int)extRaw>(column_num){}

      };

      // OTL LONG RAW class

      template <int str_size> class otl_long_raw:
       public otl_variable<unsigned char[str_size],(const int)extLongRaw>{

      public:

       otl_long_raw()
        :otl_variable<unsigned char[str_size],(const int)extLongRaw>(){}

       otl_long_raw(const char *aname)
        :otl_variable<unsigned char[str_size],(const int)extLongRaw>(aname){}

       otl_long_raw(const int column_num)
        :otl_variable<unsigned char[str_size],(const int)extLongRaw>(column_num){}

      };

      // OTL CHAR class

      template <ub1 str_size> class otl_char:
       public otl_variable<char[str_size],(const int)extChar>{

      public:

       otl_char()
        :otl_variable<char[str_size],(const int)extChar>(){}

       otl_char(const char *aname)
        :otl_variable<char[str_size],(const int)extChar>(aname){}

       otl_char(const int column_num)
        :otl_variable<char[str_size],(const int)extChar>(column_num){}

      };

      // OTL CHARZ class

      template <ub1 str_size> class otl_charz:
       public otl_variable<char[str_size],(const int)extCharZ>{

      public:

       otl_charz()
        :otl_variable<char[str_size],(const int)extCharZ>(){}

       otl_charz(const char *aname)
        :otl_variable<char[str_size],(const int)extCharZ>(aname){}

       otl_charz(const int column_num)
        :otl_variable<char[str_size],(const int)extCharZ>(column_num){}

      };

      // OTL LONG VARCHAR class.

      // It is used for reading and writing objects of the Oracle LONG data
      // type. The class defines:
      //
      //  - operator[] to access elements of the char buffer;
      //  - function set_len() to set up string length on input;
      //  - function len() to get string length on output;
      //
      template <int str_size> class otl_long_varchar:
       public otl_variable<char[str_size],(const int)extLongVarChar>{

      public:

       otl_long_varchar()
        : otl_variable<char[str_size],(const int)extLongVarChar>(),
          real_len(*(sb4*)&v[0]),
          real_p_v((char*)&v[0]+sizeof(sb4))
        {
         real_len=sizeof(v);
        }

       otl_long_varchar(const char *aname)
        : otl_variable<char[str_size],(const int)extLongVarChar>(aname),
          real_len(*(sb4*)&v[0]),
          real_p_v((char*)&v[0]+sizeof(sb4))
        {
         real_len=sizeof(v);
        }

       otl_long_varchar(const int column_num)
        : otl_variable<char[str_size],(const int)extLongVarChar>(column_num),
          real_len(*(sb4*)&v[0]),
          real_p_v((char*)&v[0]+sizeof(sb4))
        {
         real_len=sizeof(v);
        }

       void set_null(int ndx=0){ind=-1;}
       void set_len(int len, int ndx=0){real_len=len;}
       void set_not_null(int ndx=0){ind=0;}

       void* val(int ndx=0){return real_p_v;}
       int  len(void){return real_len;}

       char& operator[](int ndx){return real_p_v[ndx];}

      private:

       char* real_p_v;
       sb4& real_len;

      };

      // OTL LONG VARRAW class

      // It is used for reading and writing objects of the Oracle LONG RAW
      // data type. The class defines:
      //
      //  - operator[] to access elements of the char buffer;
      //  - function set_len() to set up string length on input;
      //  - function len() to get string length on output;
      //

      template <int str_size> class otl_long_varraw:
       public otl_variable<unsigned char[str_size],(const int)extLongVarRaw>{

      public:

       otl_long_varraw()
        : otl_variable<unsigned char[str_size],(const int)extLongVarRaw>(),
          real_len(*(sb4*)&v[0]),
          real_p_v((unsigned char*)&v[0]+sizeof(sb4))
        {
         real_len=sizeof(v);
        }

       otl_long_varraw(const char *aname)
        : otl_variable<unsigned char[str_size],(const int)extLongVarRaw>(aname),
          real_len(*(sb4*)&v[0]),
          real_p_v((unsigned char*)&v[0]+sizeof(sb4))
        {
         real_len=sizeof(v);
        }

       otl_long_varraw(const int column_num)
        : otl_variable<unsigned char[str_size],(const int)extLongVarRaw>(column_num),
          real_len(*(sb4*)&v[0]),
          real_p_v((unsigned char*)&v[0]+sizeof(sb4))
        {
         real_len=sizeof(v);
        }

       void set_null(int ndx=0){ind=-1;}
       void set_len(int len, int ndx=0){real_len=len;}
       void set_not_null(int ndx=0){ind=0;}

       void* val(int ndx=0){return real_p_v;}
       int  len(void){return real_len;}

       unsigned char& operator[](int ndx){return real_p_v[ndx];}

      private:

       unsigned char* real_p_v;
       sb4& real_len;

      };

      // String array classes

      // Null terminated string array
      // (!!) most useful in C++ programs

      template <const short arr_size,const int str_size> class otl_cstring_array:
       public otl_array<char[str_size],(const int)extCChar,arr_size>{

      public:

       otl_cstring_array()
        :otl_array<char[str_size],(const int)extCChar,arr_size>(){}

       otl_cstring_array(const char *aname)
        :otl_array<char[str_size],(const int)extCChar,arr_size>(aname){}

       otl_cstring_array(const int column_num)
        :otl_array<char[str_size],(const int)extCChar,arr_size>(column_num){}

      };

      // OTL VARCHAR2 ARRAY

      template <const short arr_size,const int str_size> class otl_varchar2_array:
       public otl_array<char[str_size],(const int)extVarChar2,arr_size>{

      public:

       otl_varchar2_array()
        :otl_array<char[str_size],(const int)extVarChar2,arr_size>(){}

       otl_varchar2_array(const char *aname)
        :otl_array<char[str_size],(const int)extVarChar2,arr_size>(aname){}

       otl_varchar2_array(const int column_num)
        :otl_array<char[str_size],(const int)extVarChar2,arr_size>(column_num){}

      };

      // OTL LONG ARRAY

      template <const short arr_size,const int str_size> class otl_long_array:
       public otl_array<char[str_size],(const int)extLong,arr_size>{};

      // OTL VARCHAR ARRAY

      template <const short arr_size,const int str_size> class otl_varchar_array:
       public otl_array<char[str_size],(const int)extVarChar,arr_size>{

      public:

       otl_varchar_array()
        :otl_array<char[str_size],(const int)extVarChar,arr_size>(){}

       otl_varchar_array(const char *aname)
        :otl_array<char[str_size],(const int)extVarChar,arr_size>(aname){}

       otl_varchar_array(const int column_num)
        :otl_array<char[str_size],(const int)extVarChar,arr_size>(column_num){}

      };

      // OTL VARRAW ARRAY

      template <const short arr_size,const int str_size> class otl_varraw_array:
       public otl_array<unsigned char[str_size],(const int)extVarRaw,arr_size>{

      public:

       otl_varraw_array()
        :otl_array<unsigned char[str_size],(const int)extVarRaw,arr_size>(){}

       otl_varraw_array(const char *aname)
        :otl_array<unsigned char[str_size],(const int)extVarRaw,arr_size>(aname){}

       otl_varraw_array(const int column_num)
        :otl_array<unsigned char[str_size],(const int)extVarRaw,arr_size>(column_num){}

      };

      // OTL VARRAW ARRAY

      template <const short arr_size,const ub1 str_size> class otl_raw_array:
       public otl_array<unsigned char[str_size],(const int)extVarRaw,arr_size>{

      public:

       otl_raw_array()
        :otl_array<unsigned char[str_size],(const int)extVarRaw,arr_size>(){}

       otl_raw_array(const char *aname)
        :otl_array<unsigned char[str_size],(const int)extVarRaw,arr_size>(aname){}

       otl_raw_array(const int column_num)
        :otl_array<unsigned char[str_size],(const int)extVarRaw,arr_size>(column_num){}

      };

      // OTL LONG RAW ARRAY

      template <const short arr_size,const int str_size> class otl_long_raw_array:
       public otl_array<unsigned char[str_size],(const int)extLongRaw,arr_size>{

      public:

       otl_long_raw_array()
        :otl_array<unsigned char[str_size],(const int)extLongRaw,arr_size>(){}

       otl_long_raw_array(const char *aname)
        :otl_array<unsigned char[str_size],(const int)extLongRaw,arr_size>(aname){}

       otl_long_raw_array(const int column_num)
        :otl_array<unsigned char[str_size],(const int)extLongRaw,arr_size>(column_num){}

      };

      // OTL CHAR ARRAY

      template <const short arr_size,const ub1 str_size> class otl_char_array:
       public otl_array<char[str_size],(const int)extChar,arr_size>{

      public:

       otl_char_array()
        :otl_array<char[str_size],(const int)extChar,arr_size>(){}

       otl_char_array(const char *aname)
        :otl_array<char[str_size],(const int)extChar,arr_size>(aname){}

       otl_char_array(const int column_num)
        :otl_array<char[str_size],(const int)extChar,arr_size>(column_num){}

      };

      // OTL CHARZ ARRAY

      template <const short arr_size,const ub1 str_size> class otl_charz_array:
       public otl_array<char[str_size],(const int)extCharZ,arr_size>{

      public:

       otl_charz_array()
        :otl_array<char[str_size],(const int)extCharZ,arr_size>(){}

       otl_charz_array(const char *aname)
        :otl_array<char[str_size],(const int)extCharZ,arr_size>(aname){}

       otl_charz_array(const int column_num)
        :otl_array<char[str_size],(const int)extCharZ,arr_size>(column_num){}

      };

      // Define variable or array p with the corresponding placeholder's
      // name ":p", e.g:
      //
      //    OTL_VAR(f1,otl_float_array<100>);
      //    OTL_VAR(f,otl_int);
      //
      #define OTL_VAR(p,type) type p(":"#p);

      // ================================ Prosto*C ================================

      // Prosto*C is a simplified interface to SQL. It provides an
      // alternative method of intercepting errors. The user needs to define
      // an error handler function and attach it to the connect object. When
      // error occurs, the handler function is invoked.

      // OTL Error handler function prototype:
      //
      //    err_msg  -- error message
      //    err_code -- error code
      //
      typedef void (*otl_error_handler)(char*,int);

      // Connect to Oracle using the "connect" string and attach the
      // "handler" function to the connect object. The function returns a
      // pointer to the corresponding connect object.
      INLINE otl_connect* otl_logon(char* connect,otl_error_handler handler=0);

      // "Pro*C" connect. Primary connection is done in a Pro*C module using
      // EXEC SQL CONNECT...; Attach the "handler" function to the connect
      // object.  The function returns a pointer to the corresponding
      // connect object.
      INLINE otl_connect* otl_proC_logon(otl_error_handler handler=0);

      // Disconnect from Oracle. "db" -- connect object. Returns 1 on
      // success, 0 -- on failure.
      INLINE int otl_logoff(otl_connect* db);

      // Commit transaction. "db" -- connect object.
      INLINE void otl_commit(otl_connect* db);

      // Roll back transaction. "db" -- connect object.
      INLINE void otl_rollback(otl_connect* db);

      // Execute constant SQL statement:
      //
      //  db  -- connect object
      //  stm -- SQL statement
      //  ignore_error -- "ignore error" flag. If the flag is set up, then
      //  the error handler function is not called.
      //
      // Returns 1 on success, 0 -- on failure.
      INLINE int otl_exec(otl_connect* db,char* stm,int ignore_error=0);

      // Open OTL stream:
      //
      //  db -- connect object
      //  stm -- SQL statement
      //  bufsize -- size of the buffer, attached to the stream
      //
      // Returns pointer to stream on success, 0 -- on failure.
      INLINE otl_stream* otl_stream_open(otl_connect* db,
      				   char* stm,
      				   short bufsize=1,
      				   const char* ref_cur_placeholder=0
      				  );

      // Close OTL stream
      INLINE void otl_stream_close(otl_stream* f);

      // Check out the "EOF" condition on the "f" stream
      INLINE int otl_eof(otl_stream* f);

      // Check out if Oracle NULL has been fetched from the stream
      INLINE int otl_is_null(otl_stream* f);

      // Set "auto-commit" flag. When the buffer is flushed, current
      // transaction is automatically commited, if the flag is set.
      INLINE void otl_set_commit(otl_stream* f,int auto_commit=1);

      // Flush stream buffer. SQL statement is executed as many times as the
      // rows have been entered into the stream buffer.
      INLINE void otl_flush(otl_stream* f);

      // C-like printf/scanf functions
      // The following format specifiers are supported:
      //
      //    %d  - int
      //    %u  - unsigned
      //    %ld - long int
      //    %f  - float
      //    %lf - double
      //    %c  - char
      //    %s  - string
      //
      // There is a specifier for writing NULL into stream:
      //
      //    %N  - NULL
      //
      // This couple of functions is intended for those who likes the C
      // style better than C++ streams.
      INLINE void otl_printf(otl_stream* f,const char* fmt,...);
      INLINE void otl_scanf(otl_stream* f,const char* fmt,...);

      // ======================= Function bodies section ===========================

      INLINE void otl_exception::init(Lda_Def& lda,const char* sqlstm)
       // get error code and message
      {
       int len;
       stm_text=0;
       code=lda.rc;
       oerhms(&lda,lda.rc,msg,sizeof(msg));
       len = strlen((const char*)msg);
       msg[len-1]=0;
       if(sqlstm){
          len = strlen(sqlstm)+1;
          stm_text = new unsigned char[len];
          strcpy((char*)stm_text,sqlstm);
       }
      }

      INLINE otl_object::otl_object()
      {
       connected=0;
       ex_enabled=otl_exception::enabled;
      }

      INLINE otl_connect::otl_connect(int exception_enabled)
       :rc(lda.rc)
      {
       ex_enabled=exception_enabled;
       proc_connect=0;
       handler=0;
       memset(&lda,0,sizeof(lda));
      }

      INLINE otl_connect::otl_connect(
       const char* connect_str,
       int exception_enabled
      ) : rc(lda.rc)
      {
       ex_enabled=exception_enabled;
       proc_connect=0;
       handler=0;
       memset(&lda,0,sizeof(lda));
       rlogon(connect_str);
      }

      INLINE otl_connect::~otl_connect()
      {
       logoff();
      }

      INLINE int otl_connect::test_blocking(void)
      {
       if(!connected)return 0;
       int r=onbtst(&lda);
       if(rc==otl_blocking)return otl_blocking;
       if(rc==0)return otl_nonblocking;
       if(rc!=0 && ex_enabled) throw otl_exception(lda);
       return 0;
      }

      INLINE int otl_connect::set_nonblocking(void)
      {
       if(!connected)return 0;
       int r=onbset(&lda);
       if(r && ex_enabled) throw otl_exception(lda);
       return 1;
      }

      INLINE int otl_connect::clear_nonblocking(void)
      {
       if(!connected)return 0;
       int r=onbclr(&lda);
       if(r && ex_enabled) throw otl_exception(lda);
       return 1;
      }

      INLINE int otl_connect::rlogon(
        const char* connect_str
       )
      {// according to the OCI manual, HDA needs to be initialized
       memset(hda,0,sizeof(hda));
       connected=!olog(&lda,
      		 hda,
      		 (unsigned char*)connect_str,
      		 -1,
      		 0,
      		 -1,
      		 0,
      		 -1,
      		 0 // default, blocking connection mode
      	       );
       if(!connected && ex_enabled) throw otl_exception(lda);// raise exception
       return connected;
      }

      INLINE int otl_connect::sqllda()
      {
       ::sqllda(&lda);
       connected=!rc;
       if(!connected && ex_enabled) throw otl_exception(lda);
       proc_connect=1;
       return connected;
      }

      INLINE int otl_connect::sqlld2(const char* dbname)
      {sb4 clen=-1;
       ::sqlld2(&lda,(unsigned char*)dbname,&clen);
       connected=!rc;
       if(!connected && ex_enabled) throw otl_exception(lda);
       proc_connect=1;
       return connected;
      }

      INLINE int otl_connect::logon(const char* connect_str)
      {
       connected=!olon(
      		 &lda,
      		 (unsigned char*)connect_str,
      		 -1,
      		 0,
      		 -1,
      		 0
                       );
       if(!connected && ex_enabled) throw otl_exception(lda);
       return connected;
      }

      INLINE int otl_connect::logoff()
      {
       if(!connected||proc_connect)return 0;
       int r=ologof(&lda);
       connected=0;
       if(r && ex_enabled) throw otl_exception(lda);
       return !rc;
      }

      INLINE int otl_connect::commit()
      {
       if(!connected)return 0;
       int r=ocom(&lda);
       if(r && ex_enabled) throw otl_exception(lda);
       return !rc;
      }

      INLINE int otl_connect::rollback()
      {
       if(!connected)return 0;
       int r=orol(&lda);
       if(r && ex_enabled) throw otl_exception(lda);
       return !rc;
      }

      INLINE int otl_connect::obreak()
      {
       if(!connected)return 0;
       int r=::obreak(&lda);
       if(r && ex_enabled) throw otl_exception(lda);
       return !rc;
      }

      INLINE int otl_connect::auto_commit_on()
      {
       if(!connected)return 0;
       int r=ocon(&lda);
       if(r && ex_enabled) throw otl_exception(lda);
       return !rc;
      }

      INLINE int otl_connect::auto_commit_off()
      {
       if(!connected)return 0;
       int r=ocof(&lda);
       if(r && ex_enabled) throw otl_exception(lda);
       return !rc;
      }

      INLINE int otl_connect::thread_safe_init(void)
      {
       int r=opinit(1); // Set up thread safe environment.
       return !r;
      }

      INLINE otl_generic_variable::otl_generic_variable()
      {
       name=0;
       pos=0;
       max_tab_len=0;
       cur_tab_len=0;
      }

      INLINE otl_generic_variable::~otl_generic_variable()
      {
      #ifdef OTL_DEBUG
       cout<<"(001)- = "<<(int*)name<<endl;
      #endif

       delete name;
      }

      INLINE void otl_generic_variable::copy_name(const char* aname)
      {
       pos=0;
       if(name==aname)return;
       if(name){
      #ifdef OTL_DEBUG
       cout<<"(002)- = "<<(int*)name<<endl;
      #endif
        delete name;
       }

       name=new char[strlen(aname)+1];
      #ifdef OTL_DEBUG
       cout<<"(069)+ = "<<(int*)name<<endl;
      #endif

       strcpy(name,aname);
      }

      INLINE void otl_generic_variable::copy_pos(const int apos)
      {
       if(name){
      #ifdef OTL_DEBUG
       cout<<"(003)- = "<<(int*)name<<endl;
      #endif

        delete name;
        name=0;
       }
       pos=apos;
      }

      INLINE void otl_generic_variable::set_null(int ndx)
      {
       p_ind[ndx]=-1;
      }

      INLINE void otl_generic_variable::set_len(int len, int ndx)
      {
       p_rlen[ndx]=(short)len;
      }

      INLINE void otl_generic_variable::set_not_null(int ndx)
      {
       p_ind[ndx]=(short)elem_size;
      }

      INLINE int otl_generic_variable::is_null(int ndx)
      {
       return p_ind[ndx]==-1;
      }

      INLINE int otl_generic_variable::is_success(int ndx)
      {
       return p_ind[ndx]==0;
      }

      INLINE int otl_generic_variable::is_truncated(int ndx)
      {
       return p_ind[ndx]==-2||p_ind[ndx]>0;
      }

      INLINE void* otl_generic_variable::val(int ndx)
      {
       return (void*)&p_v[((unsigned)ndx)*elem_size];
      }

      INLINE int otl_generic_variable::is_invalid_conversion(int ndx)
      {
       return p_rcode[ndx]==1454;
      }

      INLINE int otl_generic_variable::is_real_overflow(int ndx)
      {
       return p_rcode[ndx]==1456;
      }

      INLINE int otl_generic_variable::is_unsupported_datatype(int ndx)
      {
       return p_rcode[ndx]==3115;
      }

      INLINE void otl_generic_variable::set_tab_len(int new_len)
      {
       max_tab_len=(ub4)array_size;
       cur_tab_len=(ub4)new_len;
      }

      INLINE int otl_generic_variable::get_tab_len(void)
      {
       return (int)cur_tab_len;
      }

      INLINE otl_dynamic_variable::otl_dynamic_variable(
       const int column_num,
       const Aftype,
       const Aelem_size,
       const short Aarray_size
      )
      {
       copy_pos(column_num);
       init(Aftype,Aelem_size,Aarray_size);
      }

      INLINE otl_dynamic_variable::otl_dynamic_variable(
       const char *aname,
       const Aftype,
       const Aelem_size,
       const short Aarray_size
      )
      {
       copy_name(aname);
       init(Aftype,Aelem_size,Aarray_size);
      }

      INLINE otl_dynamic_variable::otl_dynamic_variable()
      {
       p_v=0;
       p_ind=0;
       p_rlen=0;
       p_rcode=0;
      }

      INLINE otl_dynamic_variable::~otl_dynamic_variable()
      {
      #ifdef OTL_DEBUG
       cout<<"(070)- = "<<(int*)p_v<<endl;
      #endif
       delete p_v;
      #ifdef OTL_DEBUG
       cout<<"(071)- = "<<(int*)p_ind<<endl;
      #endif
       delete p_ind;
      #ifdef OTL_DEBUG
       cout<<"(072)- = "<<(int*)p_rlen<<endl;
      #endif
       delete p_rlen;
      #ifdef OTL_DEBUG
       cout<<"(073)- = "<<(int*)p_rcode<<endl;
      #endif
       delete p_rcode;
      }

      INLINE void otl_dynamic_variable::init(
      	       const int Aftype,
      	       const int Aelem_size,
      	       const short Aarray_size
      	      )
      {
       int i;

       ftype=Aftype;
       elem_size=Aelem_size;
       array_size=Aarray_size;
       p_v=new ub1[elem_size*(unsigned)array_size];
      #ifdef OTL_DEBUG
       cout<<"(074)+ = "<<(int*)p_v<<endl;
      #endif
       p_ind=new sb2[array_size];
       p_rlen=new ub2[array_size];
      #ifdef OTL_DEBUG
       cout<<"(075)+ = "<<(int*)p_rlen<<endl;
      #endif
       p_rcode=new ub2[array_size];
      #ifdef OTL_DEBUG
       cout<<"(076)+ = "<<(int*)p_rcode<<endl;
      #endif

       max_tab_len=(ub4)array_size;
       cur_tab_len=(ub4)array_size;

       memset(p_v,0,elem_size*(unsigned)array_size);
       for(i=0;i<array_size;++i){
        p_ind[i]=(short)elem_size;
        p_rlen[i]=(short)elem_size;
        p_rcode[i]=0;
       }

      }

      INLINE int otl_dynamic_variable::get_ftype(void)
      {
       return ftype;
      }

      INLINE int otl_dynamic_variable::get_elem_size(void)
      {
       return elem_size;
      }

      INLINE otl_cursor::otl_cursor(int exception_enabled)
       : rpc(cda.rpc), ft(cda.ft), rc(cda.rc), peo(cda.peo)
      {
       ex_enabled=exception_enabled;
       vl=0;
       vl_len=0;
       adb=0;
       stm_text=0;
       memset(&cda,0,sizeof(cda));
      }

      INLINE otl_cursor::otl_cursor(
       otl_connect& connect,
       int exception_enabled
      ) : rpc(cda.rpc), ft(cda.ft), rc(cda.rc), peo(cda.peo)
      {
       vl=0;
       vl_len=0;
       adb=&connect;
       stm_text=0;
       ex_enabled=exception_enabled;
       open(connect);
      }

      INLINE otl_cursor::~otl_cursor()
      {
       close();
       delete[] stm_text;
       stm_text=0;
      }

      INLINE void otl_cursor::alloc_var(otl_p_generic_variable* vp)
      {
       otl_p_generic_variable tmp_vl[otl_var_list_size];
       while(*vp){
        ++vl_len;
        tmp_vl[vl_len-1]=*vp;
        ++vp;
       }
       if(vl_len>0){
        vl=new otl_p_generic_variable[vl_len];
      #ifdef OTL_DEBUG
       cout<<"(078)+ = "<<(int*)vl<<endl;
      #endif

        for(int i=0;i<vl_len;++i)
         vl[i]=tmp_vl[i];
       }
      }

      INLINE int otl_cursor::open(otl_connect& connect)
      {
       adb=&connect;
       if(rc!=otl_blocked)memset(&cda,0,sizeof(cda));
       connected=!oopen(&cda,&connect.lda,0,-1,-1,0,-1);
       if(!connected && ex_enabled) throw otl_exception(connect.lda,stm_text);
       return connected;
      }

      INLINE int otl_cursor::close(void)
      {
       if(!connected)return 0;
       if(adb){
        if(!adb->connected){
         connected=0;
         adb=0;
         return 0;
        }
       }
       int r=oclose(&cda);
       connected=0;
       adb=0;
       if(r && ex_enabled) throw otl_exception(cda,stm_text);
       return !r;
      }

      INLINE int otl_cursor::cancel(void)
      {
       if(!connected)return 0;
       int r=ocan(&cda);
       if(r && ex_enabled) throw otl_exception(cda,stm_text);
       return !r;
      }

      INLINE int otl_cursor::option(int rbopt, int waitopt)
      {
       if(!connected)return 0;
       int r=oopt(&cda,rbopt,waitopt);
       if(r && ex_enabled) throw otl_exception(cda,stm_text);
       return !r;
      }

      INLINE int otl_cursor::fetch_long(
       int column_num, // column number: 1,2,...
       void* buf, // pointer to buffer
       sb4 bufl, // buffer size
       int dtype, // buffer data type, see ext* "enum"
       ub4* retl, // returned length
       sb4 offset // offset
      )
      {
       if(!connected)return 0;
       int r=oflng(&cda,column_num,(ub1*)buf,bufl,dtype,retl,offset);
       if(r && ex_enabled) throw otl_exception(cda,stm_text);
       return !r;
      }

      INLINE int otl_cursor::parse(const char* sqlstm)
      {
       if(!connected)return 0;
       if(stm_text){
        delete[] stm_text;
        stm_text=0;
       }
       stm_text = new char[strlen(sqlstm) + 1];
       strcpy(stm_text, sqlstm);
       return(parse());
      }

      INLINE int otl_cursor::parse(void)
      {
       int r=oparse(&cda,(unsigned char*)stm_text,-1,0,0);
       if(r && ex_enabled) throw otl_exception(cda,stm_text);
       return !r;
      }

      INLINE int otl_cursor::parse(const char* sqlstm,
      			     otl_generic_variable** v
      			    )
      {
       if(!connected)return 0;
       if(stm_text){
        delete[] stm_text;
        stm_text=0;
       }
       stm_text = new char[strlen(sqlstm) + 1];
       strcpy(stm_text, sqlstm);
       if(!parse())return 0;
       otl_generic_variable** vp=v;
       int pos=0;
       while(*vp){
        otl_generic_variable* v=*vp;
        if(v->name){
         if(!bind(*v))return 0;
        }else{
         ++pos;
         if(!v->pos)v->pos=pos;
         if(!bind(*v))return 0;
        }
        ++vp;
       }
       return 1;
      }

      INLINE int otl_cursor::eparse(const char* sqlstm,...)
      {
       va_list argv;
       va_start(argv,sqlstm);
       if(!connected)return 0;
       if(stm_text){
        delete[] stm_text;
        stm_text=0;
       }
       stm_text = new char[strlen(sqlstm) + 1];
       strcpy(stm_text, sqlstm);
       if(!parse())return 0;
       int pos=0;
       otl_generic_variable* v;
       do{
        v=(otl_generic_variable*)va_arg(argv,otl_generic_variable*);
        if(v){
         if(v->name){
          if(!bind(*v))return 0;
         }else{
          ++pos;
          if(!v->pos)v->pos=pos;
          if(!bind(*v))return 0;
         }
        }
       }while(v);
       return 1;
      }

      INLINE int otl_cursor::eexec(const char* sqlstm,short iters,...)
      {
       va_list argv;
       va_start(argv,iters);
       if(!connected)return 0;
       if(stm_text){
        delete[] stm_text;
        stm_text=0;
       }
       stm_text = new char[strlen(sqlstm) + 1];
       strcpy(stm_text, sqlstm);
       if(!parse())return 0;
       int pos=0;
       otl_generic_variable* v;
       do{
        v=(otl_generic_variable*)va_arg(argv,otl_generic_variable*);
        if(v){
         if(v->name){
          if(!bind(*v))return 0;
         }else{
          ++pos;
          if(!v->pos)v->pos=pos;
          if(!bind(*v))return 0;
         }
        }
       }while(v);
       return exec(iters);
      }

      INLINE int otl_cursor::exec(short iters)
      {
       if(!connected)return 0;
       int r=oexn(&cda,iters,0);
       if(r && ex_enabled) throw otl_exception(cda,stm_text);
       return !r;
      }

      INLINE int otl_cursor::fetch(short iters)
      {
       if(!connected)return 0;
       int r=ofen(&cda,iters);
       if(r && ex_enabled && rc!=1403) throw otl_exception(cda,stm_text);
       return !r;
      }

      INLINE int otl_cursor::exfet(short iters)
      {
       if(!connected)return 0;
       int r=oexfet(&cda,iters,0,0);
       if(r && ex_enabled && rc!=1403) throw otl_exception(cda,stm_text);
       return !r;
      }

      INLINE int otl_cursor::bind(
       const char* name, // placeholder name: ":F1", ":F2"
       otl_generic_variable& v // reference to host variable/array
      )
      {
       if(!connected)return 0;
       if(rc!=otl_blocked)v.copy_name(name);
       int r=obndrv(&cda,
      	      (unsigned char*) name,
      	      -1,
      	      (ub1*)v.p_v,
      	      v.elem_size,
      	      v.ftype,
      	      -1,
      	      v.p_ind,
      	      0,
      	      -1,
      	      -1
      	      );
       if(r && ex_enabled) throw otl_exception(cda,stm_text);
       return !r;
      }

      INLINE int otl_cursor::bind_tab(
       const char* name, // placeholder name: ":F1", ":F2"
       otl_generic_variable& v // reference to host variable/array
      )
      {
       if(!connected)return 0;
       if(rc!=otl_blocked)v.copy_name(name);
       int r=obndra(&cda,
      	      (unsigned char*)name,
      	      -1,
      	      (ub1*)v.p_v,
      	      v.elem_size,
      	      v.ftype,
      	      -1,
      	      v.p_ind,
      	      v.p_rlen,
      	      v.p_rcode,
      	      v.max_tab_len,
      	      &v.cur_tab_len,
      	      0,
      	      -1,
      	      -1
      	     );
       if(r && ex_enabled) throw otl_exception(cda,stm_text);
       return !r;
      }

      INLINE int otl_cursor::bind(
       const char* name, // placeholder name: ":f1", ":f2"
       void* buf, // pointer to host variable/array
       int   elem_size, //array element/ variable size in bytes
       int   ftype, // Oracle external data type code
       sb2*  indp // pointer to indicator variable/array
      )
      {
       if(!connected)return 0;
       int r=obndrv(&cda,
      	      (unsigned char*) name,
      	      -1,
      	      (ub1*)buf,
      	      elem_size,
      	      ftype,
      	      -1,
      	      indp,
      	      0,
      	      -1,
      	      -1
      	      );
       if(r && ex_enabled) throw otl_exception(cda,stm_text);
       return !r;
      }

      INLINE int otl_cursor::bind(otl_generic_variable& v)
      {
       if(v.name)return bind(v.name,v);
       if(v.pos)return bind(v.pos,v);
       return 0;
      }

      INLINE int otl_cursor::bind_tab(otl_generic_variable& v)
      {
       if(v.name)return bind_tab(v.name,v);
       return 0;
      }

      INLINE int otl_cursor::bind(
       int column_num, // column number: 1,2,...
       otl_generic_variable& v // reference to variable/array
      )
      {
       if(!connected)return 0;
       if(rc!=otl_blocked)v.copy_pos(column_num);
       int r=odefin(&cda,
      	      column_num,
      	      (ub1*)v.p_v,
      	      v.elem_size,
      	      v.ftype,
      	      -1,
      	      v.p_ind,
      	      0,
      	      -1,
      	      -1,
      	      v.p_rlen,
      	      v.p_rcode
      	      );
       if(r && ex_enabled) throw otl_exception(cda,stm_text);
       return !r;
      }

      INLINE int otl_cursor::bind(
       int column_num, // column number: 1,2,...
       void* buf, // pointer to host variable/array
       int   elem_size, // array element/variable size in bytes
       int   ftype, // Oracle external data type code
       sb2*  indp, // pointer to indicator array/varibale
       ub2*  rlen, // pointer to returned length array/variable
       ub2*  rcode // pointer to returned code array variable
      )
      {
       if(!connected)return 0;
       int r=odefin(&cda,
      	      column_num,
      	      (ub1*)buf,
      	      elem_size,
      	      ftype,
      	      -1,
      	      indp,
      	      0,
      	      -1,
      	      -1,
      	      rlen,
      	      rcode
      	      );
       if(r && ex_enabled) throw otl_exception(cda,stm_text);
       return !r;
      }

      INLINE int otl_cursor::bind_cstring(
       int column_num, // column number: 1,2,...
       char* buf, // pointer to C-string variable/array
       int elem_size, // array element/variable size
       sb2*  indp, // pointer to indicator array/variable
       ub2*  rlen, // pointer to returned length array/variable
       ub2*  rcode // pointer to returned code array/variable
      )
      {
       return bind(column_num,
      	     buf,
      	     elem_size,
      	     extCChar,
      	     indp,
      	     rlen,
      	     rcode
                   );
      }

      INLINE int otl_cursor::bind_int(
       int column_num, // column number: 1,2,...
       int* buf, // pointer to int variable/array
       sb2*  indp, // pointer to indicator array/variable
       ub2*  rlen, // pointer to returned length array/variable
       ub2*  rcode // pointer to returned code array/variable
      )
      {
       return bind(column_num,
      	     buf,
      	     sizeof(int),
      	     extInt,
      	     indp,
      	     rlen,
      	     rcode
                   );
      }

      INLINE int otl_cursor::bind_short(
       int column_num,// column number: 1,2,...
       short* buf, // pointer to short int variable/array
       sb2*  indp, // pointer to indicator array/variable
       ub2*  rlen, // pointer to returned length array/variable
       ub2*  rcode // pointer to returned code array/variable
      )
      {
       return bind(column_num,
      	     buf,
      	     sizeof(short),
      	     extInt,
      	     indp,
      	     rlen,
      	     rcode
                   );
      }

      INLINE int otl_cursor::bind_long_int(
       int column_num, // column number: 1,2,...
       long* buf, // pointer to long int variable/array
       sb2*  indp, // pointer to indicator array/variable
       ub2*  rlen, // pointer to returned length array/variable
       ub2*  rcode // pointer to returned code array/variable
      )
      {
       return bind(column_num,
      	     buf,
      	     sizeof(long),
      	     extInt,
      	     indp,
      	     rlen,
      	     rcode
                   );
      }

      INLINE int otl_cursor::bind_float(
       int column_num,// column number: 1,2,...
       float* buf, // pointer to float variable/array
       sb2*  indp, // pointer to indicator array/variable
       ub2*  rlen, // pointer to returned length array/variable
       ub2*  rcode // pointer to returned code array/variable
      )
      {
       return bind(column_num,
      	     buf,
      	     sizeof(float),
      	     extFloat,
      	     indp,
      	     rlen,
      	     rcode
                  );
      }

      INLINE int otl_cursor::bind_double(
       int column_num,// column number: 1,2,...
       double* buf, // pointer to double variable/array
       sb2*  indp, // pointer to indicator array/variable
       ub2*  rlen, // pointer to returned length array/variable
       ub2*  rcode // pointer to returned code array/variable
      )
      {
       return bind(column_num,
      	     buf,
      	     sizeof(double),
      	     extFloat,
      	     indp,
      	     rlen,
      	     rcode
                   );
      }

      INLINE int otl_cursor::bind_cstring(
       const char* name, // placeholder name: ":F1", ":F2"
       char* buf, // pointer to C-string variable/array
       int elem_size, // array element/variable size
       sb2*  indp // pointer to indicator array/variable
      )
      {
       return bind(name,
      	     buf,
      	     elem_size,
      	     extCChar,
      	     indp
                   );
      }

      INLINE int otl_cursor::bind_int(
       const char* name, // placeholder name: ":F1", ":F2"
       int* buf, // pointer to int variable/array
       sb2*  indp // pointer to indicator array/variable
      )
      {
       return bind(name,
      	     buf,
      	     sizeof(int),
      	     extInt,
      	     indp
                   );
      }

      INLINE int otl_cursor::bind_short(
       const char* name, // placeholder name: ":F1", ":F2"
       short* buf, // pointer to short int variable/array
       sb2*  indp // pointer to indicator array/variable
      )
      {
       return bind(name,
      	     buf,
      	     sizeof(short),
      	     extInt,
      	     indp
                   );
      }

      INLINE int otl_cursor::bind_long_int(
       const char* name, // placeholder name: ":F1", ":F2"
       long* buf, // pointer to long int variable/array
       sb2*  indp // pointer to indicator array/variable
      )
      {
       return bind(name,
      	     buf,
      	     sizeof(long),
      	     extInt,
      	     indp
                   );
      }

      INLINE int otl_cursor::bind_float(
       const char* name, // placeholder name: ":F1", ":F2"
       float* buf, // pointer to float variable/array
       sb2*  indp // pointer to indicator array/variable
      )
      {
       return bind(name,
      	     buf,
      	     sizeof(float),
      	     extFloat,
      	     indp
                  );
      }

      INLINE int otl_cursor::bind_double(
       const char* name, // placeholder name: ":F1", ":F2"
       double* buf, // pointer to double variable/array
       sb2*  indp // pointer to indicator array/variable
      )
      {
       return bind(name,
      	     buf,
      	     sizeof(double),
      	     extFloat,
      	     indp
                  );
      }

      INLINE int otl_cursor::direct_exec(
       otl_connect& db, // connect object
       const char* stm, // statement
       int exception_enabled // exception_enabled flag
      )
      {
       otl_cursor cur(db,otl_exception::disabled);
       int r=cur.parse(stm);
       if(cur.rc&&exception_enabled)
        throw otl_exception(cur.cda,stm);
       if(cur.rc&&!exception_enabled)return 0;
       r=cur.exec();
       if(cur.rc&&exception_enabled)throw otl_exception(cur.cda,stm);
       return r;
      }

      INLINE int otl_cursor::describe_column(
       otl_column_desc& col_desc, // column descriptor structure
       int column_num // column number: 1,2,...
      )
      {
       if(!connected)return 0;
       col_desc.nlen=sizeof(col_desc.name);
       int r=odescr(&cda,
      	      column_num,
      	      &col_desc.dbsize,
      	      &col_desc.dbtype,
      	      &col_desc.name[0],
      	      &col_desc.nlen,
      	      &col_desc.dsize,
      	      &col_desc.prec,
      	      &col_desc.scale,
      	      &col_desc.nullok
                    );
       if(rc==0) col_desc.name[col_desc.nlen]='\0';
       if(r && !end_of_desc() && ex_enabled) throw otl_exception(cda,stm_text);
       return !r;
      }

      INLINE int otl_cursor::eof(void)
      {
       return rc==1403;
      }

      INLINE int otl_cursor::end_of_desc(void)
      {
       return rc==1007;
      }

      // ================ Internal otl_ext_hv_decl class  ======================

      class otl_ext_hv_decl{
      public:

       enum var_status{
        in=0,
        out=1,
        io=2,
        def=3
       };

      INLINE otl_ext_hv_decl(char* stm,short arr_size=1);
      INLINE ~otl_ext_hv_decl();

      INLINE char* operator[](int ndx){return hv[ndx];}
      INLINE short v_status(int ndx){return inout[ndx];}
      INLINE int is_id(char c);
      INLINE int name_comp(char* n1,char* n2);
      INLINE void add_var(int &n,char* v,short in_out);

      INLINE void alloc_host_var_list(
      				otl_p_generic_variable* &vl,
      				int& vl_len,
      				const int status=def
      			       );

      INLINE otl_generic_variable* alloc_var(char* s, const int vstat, const int status);

       char* hv[otl_var_list_size];
       short inout[otl_var_list_size];
       short array_size;
       short vst[4];
       short len;
      };

      INLINE int otl_ext_hv_decl::is_id(char c)
      {
       return isalnum(c)||c=='_';
      }

      INLINE int otl_ext_hv_decl::name_comp(char* n1,char* n2)
      {
       while(*n1!=' '&&*n1!='\0'&&*n2!=' '&&*n2!='\0'){
        if(toupper(*n1)!=toupper(*n2))return 0;
        ++n1;
        ++n2;
       }
       if(*n1==' '&&*n2!=' '||*n2==' '&&*n1!=' ')
        return 0;
       return 1;
      }

      INLINE void otl_ext_hv_decl::add_var(int &n,char* v,short in_out)
      {
       for(int i=0;i<n;++i)
        if(name_comp(hv[i],v))
         return;
       hv[n]=new char[strlen(v)+1];
      #ifdef OTL_DEBUG
       cout<<"(078)+ = "<<(int*)hv[n]<<endl;
      #endif
       strcpy(hv[n],v);
       inout[n]=in_out;
       hv[++n]=0;
       inout[n]=def;
      }

      INLINE otl_ext_hv_decl::otl_ext_hv_decl(char* stm, short arr_size)
      {
       array_size=arr_size;

       int i=0;
       short in_str=0;
       char *c=stm;

       hv[i]=0;
       while(*c){
        if(*c=='\''){
         if(!in_str)
          in_str=1;
         else{
          if(c[1]=='\'')
           ++c;
          else
           in_str=0;
         }
        }
        if(*c==':'&&!in_str){
         short in_out=def;
         char var[64];
         char* v=var;
         *v++=*c++;
         while(is_id(*c))
          *v++=*c++;
         while(isspace(*c)&&*c)
          ++c;
         if(*c=='<'){
          *c=' ';
          while(*c!='>'&&*c!=','&&*c){
           *v++=*c;
           *c++=' ';
          }
          if(*c==','){
           *c++=' ';
           if(toupper(*c)=='I'){
            if(toupper(c[2])=='O')
             in_out=io;
            else
             in_out=in;
           }else if(toupper(*c)=='O')
            in_out=out;
           while(*c!='>'&&*c)
            *c++=' ';
          }
          *c=' ';
          *v='\0';
          add_var(i,var,in_out);
         }
        }
        ++c;
       }

       for(int j=0;j<4;++j)vst[j]=0;
       i=0;
       while(hv[i]){
        switch(inout[i]){
        case in:
         ++vst[0];
         break;
        case out:
         ++vst[1];
         break;
        case io:
         ++vst[2];
         break;
        case def:
         ++vst[3];
         break;
        }
        ++i;
       }
       len=(short)i;
      }

      INLINE otl_ext_hv_decl::~otl_ext_hv_decl()
      {
       for(int i=0;hv[i]!=0;++i){
      #ifdef OTL_DEBUG
       cout<<"(008)- = "<<(int*)hv[i]<<endl;
      #endif

        delete hv[i];
       }
      }

      INLINE void otl_ext_hv_decl::alloc_host_var_list(
        otl_p_generic_variable* &vl,
        int& vl_len,
        const int status
      )
      {
       vl_len=0;
       if(!hv[0]){
        vl=0;
        return;
       }
       otl_p_generic_variable tmp_vl[otl_var_list_size];
       int i=0;
       while(hv[i]){
        otl_p_generic_variable vp=alloc_var(hv[i],inout[i],status);
        if(vp){
         ++vl_len;
         tmp_vl[vl_len-1]=vp;
        }
        ++i;
       }
       if(vl_len>0){
        vl=new otl_p_generic_variable[vl_len];
      #ifdef OTL_DEBUG
       cout<<"(079)+ = "<<(int*)vl<<endl;
      #endif

        for(int j=0;j<vl_len;++j)
         vl[j]=tmp_vl[j];
       }
      }

      extern "C" int atoi(const char*);

      INLINE otl_generic_variable* otl_ext_hv_decl::alloc_var(
       char* s,
       const int vstat,
       const int status
      )
      {
       char name[64];
       char type;
       sword size=0;

       char *c=name,*c1=s;
       while(*c1!=' '&&*c1)
        *c++=*c1++;
       *c='\0';
       while(*c1==' '&&*c1)
        ++c1;
       type=(char)toupper(*c1);
       if(type=='C'){
        char tmp[32];
        char *t=tmp;
        while(*c1!='['&&*c1)
          ++c1;
        ++c1;
        while(*c1!=']'&&*c1)
         *t++=*c1++;
        *t='\0';
        size=atoi(tmp);
       }

       if(status==in && (vstat==in||vstat==io))
        ;
       else if(status==out && (vstat==out||vstat==io||vstat==def))
        ;
       else if(status==def)
        ;
       else
        return 0;

       otl_dynamic_variable* v=new otl_dynamic_variable;
      #ifdef OTL_DEBUG
       cout<<"(080)+ = "<<(int*)v<<endl;
      #endif

       v->copy_name(name);
       switch(type){
       case 'C':
        v->init(extCChar,size,array_size);
        break;
       case 'D':
        v->init(extFloat,sizeof(double),array_size);
        break;
       case 'F':
        v->init(extFloat,sizeof(float),array_size);
        break;
       case 'I':
        v->init(extInt,sizeof(int),array_size);
        break;
       case 'U':
        v->init(extInt,sizeof(unsigned),array_size);
        break;
       case 'S':
        v->init(extInt,sizeof(short),array_size);
        break;
       case 'L':
        v->init(extInt,sizeof(long),array_size);
        break;
       default:
      #ifdef OTL_DEBUG
       cout<<"(010)- = "<<(int*)v<<endl;
      #endif
        delete v;
        v=0;
        break;
       }
       return v;
      }

      // ========================================================================

      INLINE otl_select_cursor::otl_select_cursor(
       otl_connect& db, // connect object
       short arr_size, // attached host array size
       int exception_enabled // exception enabled flag
      ):otl_cursor(db,exception_enabled)
      {
       cur_row=-1;
       row_count=0;
       cur_size=0;
       array_size=arr_size;
      }

      INLINE otl_select_cursor::otl_select_cursor(int exception_enabled)
       : otl_cursor(exception_enabled)
      {
      }

      INLINE int otl_select_cursor::open(
        otl_connect& db, // connect object
        short arr_size, // attached host array size
        int exception_enabled // exception enabled flag
      )
      {
       cur_row=-1;
       row_count=0;
       cur_size=0;
       array_size=arr_size;
       ex_enabled=exception_enabled;
       return otl_cursor::open(db);
      }

      // Close cursor
      INLINE int otl_select_cursor::close(void)
      {
       return otl_cursor::close();
      }

      INLINE int otl_select_cursor::first(void)
      {
       cur_row=-1;
       exec();
       fetch((short)array_size);
       row_count=rpc;
       cur_size=row_count;
       if(cur_size!=0)cur_row=0;
       return cur_size!=0;
      }

      INLINE int otl_select_cursor::next(void)
      {
       if(cur_row==-1)return first();
       if(cur_row<cur_size-1)
        ++cur_row;
       else{
        if(eof()){
         cur_row=-1;
         return 0;
        }
        fetch((short)array_size);
        cur_size=rpc-row_count;
        row_count=rpc;
        if(cur_size!=0)cur_row=0;
       }
       return cur_size!=0;
      }

      INLINE otl_ref_cursor::otl_ref_cursor(
        int exception_enabled // exception enabled flag
      ): otl_cursor(exception_enabled),sel_cur(exception_enabled)
      {

      }

      INLINE otl_ref_cursor::otl_ref_cursor(
       otl_connect& db, // connect object
       const char* cur_placeholder_name, // cursor reference placeholder name
       short arr_size, // attached host array size
       int exception_enabled // exception enabled flag
      ):otl_cursor(db,exception_enabled),sel_cur(exception_enabled)
      {
       cur_row=-1;
       row_count=0;
       cur_size=0;
       array_size=arr_size;
       rvl_len=otl_var_list_size;
       vl_cur_len=0;
       rvl=new otl_p_generic_variable[rvl_len];
      #ifdef OTL_DEBUG
       cout<<"(081)+ = "<<(int*)rvl<<endl;
      #endif
       for(int i=0;i<rvl_len;++i)rvl[i]=0;
       strcpy(cur_placeholder,cur_placeholder_name);
      }

      INLINE otl_ref_cursor::~otl_ref_cursor()
      {
       delete[] rvl;
       rvl=0;
      }

      INLINE int otl_ref_cursor::open(
        otl_connect& db, // connect object
        const char* cur_placeholder_name,
           // cursor reference placeholder name
        short arr_size, // attached host array size
        int exception_enabled
        // exception enabled flag
      )
      {
       cur_row=-1;
       row_count=0;
       cur_size=0;
       array_size=arr_size;
       rvl_len=otl_var_list_size;
       vl_cur_len=0;
       rvl=new otl_p_generic_variable[rvl_len];
      #ifdef OTL_DEBUG
       cout<<"(181)+ = "<<(int*)rvl<<endl;
      #endif
       for(int i=0;i<rvl_len;++i)rvl[i]=0;
       strcpy(cur_placeholder,cur_placeholder_name);
       return otl_cursor::open(db);
      }

      INLINE int otl_ref_cursor::close(void)
      {
       delete[] rvl;
       rvl=0;
       sel_cur.close();
       return otl_cursor::close();
      }

      INLINE int otl_ref_cursor::first(void)
      {
       otl_cursor::bind(cur_placeholder,
      		  &sel_cur.cda,
      		  sizeof(sel_cur.cda),
      		  102 // Special "OCI External datatype" code --
      		  // cursor reference
      		 );
       if(cur_row==-2)
        ; // Special case -- calling describe_select() between parse() and first()
       else{
        exec(); // Executing the PLSQL master block
        sel_cur.connected=1;
       }
       cur_row=-1;
       for(int i=0;i<vl_cur_len;++i)
        sel_cur.bind(i+1,*rvl[i]);
       sel_cur.fetch((short)array_size);
       row_count=sel_cur.rpc;
       cur_size=row_count;
       if(cur_size!=0)cur_row=0;
       return cur_size!=0;
      }

      INLINE int otl_ref_cursor::next(void)
      {
       if(cur_row<0)return first();
       if(cur_row<cur_size-1)
        ++cur_row;
       else{
        if(sel_cur.eof()){
         cur_row=-1;
         return 0;
        }
        sel_cur.fetch((short)array_size);
        cur_size=sel_cur.rpc-row_count;
        row_count=sel_cur.rpc;
        if(cur_size!=0)cur_row=0;
       }
       return cur_size!=0;
      }

      INLINE int otl_ref_cursor::bind(
       int column_num, // column number: 1,2,...
       otl_generic_variable& v // reference to variable/array
      )
      {
       if(!connected)return 0;
       ++vl_cur_len;
       rvl[vl_cur_len-1]=&v;
       v.pos=column_num;
       return 1;
      }

      INLINE int otl_ref_cursor::bind(otl_generic_variable& v)
      {
       if(v.pos)
        return bind(v.pos,v);
       else if(v.name)
        return otl_cursor::bind(v);
       return 0;
      }

      INLINE int otl_ref_cursor::bind(
        const char* name, // placeholder name: ":F1", ":F2"
        otl_generic_variable& v // reference to host variable/array
       )
      {
       return otl_cursor::bind(name,v);
      }

      INLINE int otl_ref_cursor::describe_select(
        otl_column_desc* desc, // pointer to array of column
                               // descriptors
        int& desc_len // actual number of columns
       )
      {
       otl_cursor::bind(cur_placeholder,
      		  &sel_cur.cda,
      		  sizeof(sel_cur.cda),
      		  102 // Special "OCI External datatype" code --
      		  // cursor reference
      		 );
       exec(); // Executing the PLSQL master block
       sel_cur.connected=1;
       cur_row=-2; // Special case -- describe_select() before first() or next()
       desc_len=0;
       for(int i=1;sel_cur.describe_column(desc[i-1],i);++i)
        ++desc_len;
       return 1;
      }

      INLINE void otl_select_stream::init(int should_delete)
      {
       sl=0;
       sl_len=0;
       null_fetched=0;
       ret_code=0;
       sl_desc=0;
       should_delete_flag=should_delete;
       executed=0;
       cur_in=0;
       stm_text=0;
      }

      INLINE otl_select_stream::otl_select_stream(
       otl_connect& db, // connect object
       const char* sqlstm, // SELECT statement
       const short arr_size, // output host arrays size
       ... // NULL terminated list of pointers to input host
           // variables.
       ):otl_select_cursor(db,arr_size)
      {
       init(0);

       otl_p_generic_variable tmp_vl[otl_var_list_size];
       va_list argv;
       va_start(argv,arr_size);
       otl_generic_variable* v;

       do{
        v=(otl_generic_variable*)va_arg(argv,otl_generic_variable*);
        if(v){
         ++vl_len;
         tmp_vl[vl_len-1]=v;
        }
       }while(v);

       if(vl_len>0){
        vl=new otl_p_generic_variable[vl_len];
      #ifdef OTL_DEBUG
       cout<<"(082)+ = "<<(int*)vl<<endl;
      #endif

        for(int i=0;i<vl_len;++i)
         vl[i]=tmp_vl[i];
       }
       parse(sqlstm);
       get_select_list();
       bind_all();
       if(vl_len==0){
        rewind();
        null_fetched=0;
       }
      }

      INLINE otl_select_stream::otl_select_stream(
        otl_connect& db, // connect object
        const char* sqlstm, // SELECT statement
        otl_p_generic_variable* avp,
         // Pointer to NULL terminated list of
         // pointers to input hots
         // variables
        const short arr_size // output host arrays size
       ):otl_select_cursor(db,arr_size)
      {
       init(0);
       alloc_var(avp);
       parse(sqlstm);
       get_select_list();
       bind_all();
       if(vl_len==0){
        rewind();
        null_fetched=0;
       }
      }

      INLINE otl_select_stream::otl_select_stream(
       const short arr_size, // output host arrays size
       const char* sqlstm, // SELECT statement
       otl_connect& db // connect object
       ):otl_select_cursor(db,arr_size)
      {
       init(1);

       {
        int len=strlen(sqlstm)+1;
        stm_text=new char[len];
      #ifdef OTL_DEBUG
       cout<<"(083)+ = "<<(int*)stm_text<<endl;
      #endif
        strcpy(stm_text,sqlstm);
        otl_ext_hv_decl hvd(stm_text,1);
        hvd.alloc_host_var_list(vl,vl_len);
       }

       parse();
       get_select_list();
       bind_all();
       if(vl_len==0){
        rewind();
        null_fetched=0;
       }

      }

      INLINE otl_select_stream::otl_select_stream(
       const char* sqlstm, // SELECT statement
       otl_connect& db, // connect object
       const char*  dummy_par
       ):otl_select_cursor(db,1)
      {
       init(0);
       parse(sqlstm);
       get_select_list();
       if(dummy_par); // dummy use of the dummy parameter
      }

      INLINE otl_select_stream::~otl_select_stream()
      {
       delete[] sl;
       if(should_delete_flag)
        for(int i=0;i<vl_len;++i){
      #ifdef OTL_DEBUG
       cout<<"(011)- = "<<(int*)vl[i]<<endl;
      #endif
         delete vl[i];
        }
      #ifdef OTL_DEBUG
       cout<<"(012)- = "<<(int*)vl<<endl;
      #endif
       delete[] vl;
      #ifdef OTL_DEBUG
       cout<<"(013)- = "<<(int*)sl_desc<<endl;
      #endif
       delete[] sl_desc;
      #ifdef OTL_DEBUG
       cout<<"(014)- = "<<(int*)stm_text<<endl;
      #endif
      }

      INLINE void otl_select_stream::rewind(void)
      {
       ret_code=first();
       null_fetched=0;
       cur_col=-1;
       cur_in=0;
       executed=1;
      }

      INLINE int otl_select_stream::is_null(void)
      {
       return null_fetched;
      }

      INLINE int otl_select_stream::eof(void)
      {
       return !ret_code;
      }

      INLINE otl_select_stream& otl_select_stream::operator>>(char& c)
      {
       check_if_executed();
       if(eof())return *this;
       get_next();
       if(check_type(extCChar)&&!eof()){
        c=*(char*)sl[cur_col].val(cur_row);
        look_ahead();
       }
       return *this;
      }

      INLINE otl_select_stream& otl_select_stream::operator>>(unsigned char& c)
      {
       check_if_executed();
       if(eof())return *this;
       get_next();
       if(check_type(extCChar)&&!eof()){
        c=*(unsigned char*)sl[cur_col].val(cur_row);
        look_ahead();
       }
       return *this;
      }

      INLINE otl_select_stream& otl_select_stream::operator>>(char* s)
      {
       check_if_executed();
       if(eof())return *this;
       get_next();
       if(check_type(extCChar)&&!eof()){
        strcpy((char*)s,(char*)sl[cur_col].val(cur_row));
        look_ahead();
       }
       return *this;
      }

      INLINE otl_select_stream& otl_select_stream::operator>>(unsigned char* s)
      {
       check_if_executed();
       if(eof())return *this;
       get_next();
       if(check_type(extCChar)&&!eof()){
        strcpy((char*)s,(char*)sl[cur_col].val(cur_row));
        look_ahead();
       }
       return *this;
      }

      INLINE otl_select_stream& otl_select_stream::operator>>(int& n)
      {
       check_if_executed();
       if(eof())return *this;
       get_next();
       if(check_type(extFloat)&&!eof()){
        n=(int)*(double*)sl[cur_col].val(cur_row);
        look_ahead();
       }
       return *this;
      }

      INLINE otl_select_stream& otl_select_stream::operator>>(unsigned& u)
      {
       check_if_executed();
       if(eof())return *this;
       get_next();
       if(check_type(extFloat)&&!eof()){
        u=(unsigned)*(double*)sl[cur_col].val(cur_row);
        look_ahead();
       }
       return *this;
      }

      INLINE otl_select_stream& otl_select_stream::operator>>(short& sh)
      {
       check_if_executed();
       if(eof())return *this;
       get_next();
       if(check_type(extFloat)&&!eof()){
        sh=(short)*(double*)sl[cur_col].val(cur_row);
        look_ahead();
       }
       return *this;
      }

      INLINE otl_select_stream& otl_select_stream::operator>>(long int& l)
      {
       check_if_executed();
       if(eof())return *this;
       get_next();
       if(check_type(extFloat)&&!eof()){
        l=(long)*(double*)sl[cur_col].val(cur_row);
        look_ahead();
       }
       return *this;
      }

      INLINE otl_select_stream& otl_select_stream::operator>>(float& f)
      {
       check_if_executed();
       if(eof())return *this;
       get_next();
       if(check_type(extFloat)&&!eof()){
        f=(float)*(double*)sl[cur_col].val(cur_row);
        look_ahead();
       }
       return *this;
      }

      INLINE otl_select_stream& otl_select_stream::operator>>(double& d)
      {
       check_if_executed();
       if(eof())return *this;
       get_next();
       if(check_type(extFloat)&&!eof()){
        d=*(double*)sl[cur_col].val(cur_row);
        look_ahead();
       }
       return *this;
      }

      INLINE void otl_select_stream::get_next(void)
      {
       if(cur_col<sl_len-1){
        ++cur_col;
        null_fetched=sl[cur_col].is_null(cur_row);
       }else{
        ret_code=next();
        cur_col=0;
       }
      }

      INLINE int otl_select_stream::check_type(int type_code)
      {
       if(sl[cur_col].get_ftype()!=type_code){
        throw otl_exception(otl_error_msg_0,
      		      otl_error_code_0,
                            stm_text);
        return 0;
       }else
        return 1;
      }

      INLINE void otl_select_stream::look_ahead(void)
      {
       if(cur_col==sl_len-1){
        ret_code=next();
        cur_col=-1;
       }
      }

      INLINE void otl_select_stream::bind_all(void)
      {
       int i;
       for(i=0;i<vl_len;++i)bind(*vl[i]);
       for(i=0;i<sl_len;++i)bind(sl[i]);
      }

      static INLINE int otl_int2ext(int int_type)
      {
       switch(int_type){
       case inVarChar2: return extCChar;
       case inNumber:   return extFloat;
       case inLong:     return extCChar;
       case inRowId:    return extCChar;
       case inDate:     return extCChar;
       case inRaw:      return extCChar;
       case inLongRaw:  return extCChar;
       case inChar:     return extCChar;
       default:
        return -1;
       }
      }

      static INLINE int otl_datatype_size(int ftype,int maxsz,int int_type)
      {
       switch(ftype){
       case extCChar:
        switch(int_type){
        case inRowId:
         return sizeof(otl_cchar_rowid);
        case inDate:
         return 10; // e.g. 19-JAN-64 + '\0'
        case inLong:
        case inLongRaw:
         return otl_max_long_size;
        case inRaw:
         return maxsz*2+1;
        default:
         return maxsz+1;
        }
       case extFloat:
        return sizeof(double);
       case extDate:
        return sizeof(otl_date_intern);
       default:
        return 0;
       }
      }

      static INLINE void otl_map_ftype(const otl_column_desc& desc,
      				 int& ftype,
      				 int& elem_size
      				)
      {
       ftype=otl_int2ext(desc.dbtype);
       elem_size=otl_datatype_size(ftype,desc.dbsize,desc.dbtype);
      }

      INLINE void otl_select_stream::get_select_list(void)
      {
       otl_column_desc sl_desc_tmp[otl_var_list_size];
       int sld_tmp_len=0;
       int ftype,elem_size,i;

       for(i=1; describe_column(sl_desc_tmp[i-1],i); ++i)
        ++sld_tmp_len;
       sl_len=sld_tmp_len;
       if(sl){
        delete[] sl;
        sl=0;
       }
       sl=new otl_dynamic_variable[sl_len];
      #ifdef OTL_DEBUG
       cout<<"(084)+ = "<<(int*)sl<<endl;
      #endif

       for(int j=0;j<sl_len;++j){
        otl_map_ftype(sl_desc_tmp[j],ftype,elem_size);
        sl[j].copy_pos(j+1);
        sl[j].init(ftype,
      	     (short)elem_size,
      	     (short)array_size
      	    );
       }
       if(sl_desc){
        delete[] sl_desc;
        sl_desc=0;
       }
       sl_desc=new otl_column_desc[sl_len];
      #ifdef OTL_DEBUG
       cout<<"(085)+ = "<<(int*)sl_desc<<endl;
      #endif

       memcpy(sl_desc,sl_desc_tmp,sizeof(otl_column_desc)*sl_len);
      }

      INLINE int otl_select_stream::select_list_len(void)
      {
       return sl_len;
      }

      INLINE int otl_select_stream::column_ftype(int ndx)
      {
       return sl[ndx].get_ftype();
      }

      INLINE int otl_select_stream::column_size(int ndx)
      {
       return sl[ndx].get_elem_size();
      }

      INLINE void otl_select_stream::set_delete_var(const int should_delete)
      {
       should_delete_flag=should_delete;
      }

      INLINE void otl_select_stream::get_in_next(void)
      {
       if(cur_in==vl_len-1)
        rewind();
       else{
        ++cur_in;
        executed=0;
       }
      }

      INLINE int otl_select_stream::check_in_type(int type_code,int tsize)
      {
       if(vl[cur_in]->ftype==extCChar&&type_code==extCChar)
        return 1;
       if(vl[cur_in]->ftype!=type_code||vl[cur_in]->elem_size!=tsize){
        throw otl_exception(otl_error_msg_0,
      		      otl_error_code_0,
                            stm_text);
        return 1;
       }else
        return 1;
      }

      INLINE void otl_select_stream::check_in_var(void)
      {
       if(vl_len==0)
        throw otl_exception(otl_error_msg_1,
      		      otl_error_code_1,
                            stm_text);
      }

      INLINE void otl_select_stream::check_if_executed(void)
      {
       if(!executed)
        throw otl_exception(otl_error_msg_2,
      		      otl_error_code_2,
                            stm_text);
      }

      INLINE otl_select_stream& otl_select_stream::operator<<(const char c)
      {
       check_in_var();
       if(check_in_type(extCChar,1)){
        char* tmp=(char*)vl[cur_in]->val();
        tmp[0]=c;
        tmp[1]=0;
       }
       get_in_next();
       return *this;
      }

      INLINE otl_select_stream& otl_select_stream::operator<<(const unsigned char c)
      {
       check_in_var();
       if(check_in_type(extCChar,1)){
        unsigned char* tmp=(unsigned char*)vl[cur_in]->val();
        tmp[0]=c;
        tmp[1]=0;
       }
       get_in_next();
       return *this;
      }

      INLINE otl_select_stream& otl_select_stream::operator<<(const char* s)
      {
       check_in_var();
       if(check_in_type(extCChar,1)){
        strcpy((char*)vl[cur_in]->val(),(char*)s);
       }
       get_in_next();
       return *this;
      }

      INLINE otl_select_stream& otl_select_stream::operator<<(const unsigned char* s)
      {
       check_in_var();
       if(check_in_type(extCChar,1)){
        strcpy((char*)vl[cur_in]->val(),(char*)s);
       }
       get_in_next();
       return *this;
      }

      INLINE otl_select_stream& otl_select_stream::operator<<(const int n)
      {
       check_in_var();
       if(check_in_type(extInt,sizeof(int))){
        *(int*)vl[cur_in]->val()=n;
       }
       get_in_next();
       return *this;
      }

      INLINE otl_select_stream& otl_select_stream::operator<<(const unsigned u)
      {
       check_in_var();
       if(check_in_type(extInt,sizeof(unsigned))){
        *(unsigned*)vl[cur_in]->val()=u;
       }
       get_in_next();
       return *this;
      }

      INLINE otl_select_stream& otl_select_stream::operator<<(const short sh)
      {
       check_in_var();
       if(check_in_type(extInt,sizeof(short))){
        *(short*)vl[cur_in]->val()=sh;
       }
       get_in_next();
       return *this;
      }

      INLINE otl_select_stream& otl_select_stream::operator<<(const long int l)
      {
       check_in_var();
       if(check_in_type(extInt,sizeof(long))){
        *(long*)vl[cur_in]->val()=l;
       }
       get_in_next();
       return *this;
      }

      INLINE otl_select_stream& otl_select_stream::operator<<(const float f)
      {
       check_in_var();
       if(check_in_type(extFloat,sizeof(float))){
        *(float*)vl[cur_in]->val()=f;
       }
       get_in_next();
       return *this;
      }

      INLINE otl_select_stream& otl_select_stream::operator<<(const double f)
      {
       check_in_var();
       if(check_in_type(extFloat,sizeof(double))){
        *(double*)vl[cur_in]->val()=f;
       }
       get_in_next();
       return *this;
      }

      INLINE void otl_ref_select_stream::init(int should_delete)
      {
       sl=0;
       sl_len=0;
       null_fetched=0;
       ret_code=0;
       sl_desc=0;
       should_delete_flag=should_delete;
       executed=0;
       cur_in=0;
       stm_text=0;
      }

      INLINE otl_ref_select_stream::otl_ref_select_stream(
       otl_connect& db, // connect object
       const char* sqlstm, // SELECT statement
       const char* cur_placeholder,
         // reference cursor placeholder
       const short arr_size, // output host arrays size
       ... // NULL terminated list of pointers to input host
           // variables.
       ):otl_ref_cursor(db,cur_placeholder,arr_size)
      {
       init(0);

       otl_p_generic_variable tmp_vl[otl_var_list_size];
       va_list argv;
       va_start(argv,arr_size);
       otl_generic_variable* v;

       do{
        v=(otl_generic_variable*)va_arg(argv,otl_generic_variable*);
        if(v){
         ++vl_len;
         tmp_vl[vl_len-1]=v;
        }
       }while(v);

       if(vl_len>0){
        vl=new otl_p_generic_variable[vl_len];
      #ifdef OTL_DEBUG
       cout<<"(086)+ = "<<(int*)vl<<endl;
      #endif

        for(int i=0;i<vl_len;++i)
         vl[i]=tmp_vl[i];
       }
       parse(sqlstm);
       if(vl_len==0){
        rewind();
        null_fetched=0;
       }
      }

      INLINE otl_ref_select_stream::otl_ref_select_stream(
        otl_connect& db, // connect object
        const char* sqlstm, // SELECT statement
        const char* cur_placeholder,
         // reference cursor placeholder
        otl_p_generic_variable* avp,
         // Pointer to NULL terminated list of
         // pointers to input hots
         // variables
        const short arr_size // output host arrays size
       ):otl_ref_cursor(db,cur_placeholder,arr_size)
      {
       init(0);
       alloc_var(avp);
       parse(sqlstm);
       if(vl_len==0){
        rewind();
        null_fetched=0;
       }
      }

      INLINE otl_ref_select_stream::otl_ref_select_stream(
       const short arr_size, // output host arrays size
       const char* sqlstm, // SELECT statement
       const char* cur_placeholder,
          // reference cursor placeholder
       otl_connect& db // connect object
       ):otl_ref_cursor(db,cur_placeholder,arr_size)
      {
       init(1);

       {
        int len=strlen(sqlstm)+1;
        stm_text=new char[len];
      #ifdef OTL_DEBUG
       cout<<"(087)+ = "<<(int*)stm_text<<endl;
      #endif

        strcpy(stm_text,sqlstm);
        otl_ext_hv_decl hvd(stm_text,1);
        hvd.alloc_host_var_list(vl,vl_len);
       }

       parse();
       if(vl_len==0){
        rewind();
        null_fetched=0;
       }

      }

      INLINE otl_ref_select_stream::~otl_ref_select_stream()
      {
       delete[] sl;
       if(should_delete_flag)
        for(int i=0;i<vl_len;++i){
      #ifdef OTL_DEBUG
       cout<<"(016)- = "<<(int*)vl[i]<<endl;
      #endif
         delete vl[i];
        }
      #ifdef OTL_DEBUG
       cout<<"(017)- = "<<(int*)vl<<endl;
      #endif
       delete[] vl;
      #ifdef OTL_DEBUG
       cout<<"(018)- = "<<(int*)sl_desc<<endl;
      #endif
       delete[] sl_desc;
      #ifdef OTL_DEBUG
       cout<<"(019)- = "<<(int*)stm_text<<endl;
      #endif
      }

      INLINE void otl_ref_select_stream::rewind(void)
      {
       get_select_list();
       ret_code=first();
       null_fetched=0;
       cur_col=-1;
       cur_in=0;
       executed=1;
      }

      INLINE int otl_ref_select_stream::is_null(void)
      {
       return null_fetched;
      }

      INLINE int otl_ref_select_stream::eof(void)
      {
       return !ret_code;
      }

      INLINE otl_ref_select_stream& otl_ref_select_stream::operator>>(char& c)
      {
       check_if_executed();
       if(eof())return *this;
       get_next();
       if(check_type(extCChar)&&!eof()){
        c=*(char*)sl[cur_col].val(cur_row);
        look_ahead();
       }
       return *this;
      }

      INLINE otl_ref_select_stream& otl_ref_select_stream::operator>>(unsigned char& c)
      {
       check_if_executed();
       if(eof())return *this;
       get_next();
       if(check_type(extCChar)&&!eof()){
        c=*(unsigned char*)sl[cur_col].val(cur_row);
        look_ahead();
       }
       return *this;
      }

      INLINE otl_ref_select_stream& otl_ref_select_stream::operator>>(char* s)
      {
       check_if_executed();
       if(eof())return *this;
       get_next();
       if(check_type(extCChar)&&!eof()){
        strcpy((char*)s,(char*)sl[cur_col].val(cur_row));
        look_ahead();
       }
       return *this;
      }

      INLINE otl_ref_select_stream& otl_ref_select_stream::operator>>(unsigned char* s)
      {
       check_if_executed();
       if(eof())return *this;
       get_next();
       if(check_type(extCChar)&&!eof()){
        strcpy((char*)s,(char*)sl[cur_col].val(cur_row));
        look_ahead();
       }
       return *this;
      }

      INLINE otl_ref_select_stream& otl_ref_select_stream::operator>>(int& n)
      {
       check_if_executed();
       if(eof())return *this;
       get_next();
       if(check_type(extFloat)&&!eof()){
        n=(int)*(double*)sl[cur_col].val(cur_row);
        look_ahead();
       }
       return *this;
      }

      INLINE otl_ref_select_stream& otl_ref_select_stream::operator>>(unsigned& u)
      {
       check_if_executed();
       if(eof())return *this;
       get_next();
       if(check_type(extFloat)&&!eof()){
        u=(unsigned)*(double*)sl[cur_col].val(cur_row);
        look_ahead();
       }
       return *this;
      }

      INLINE otl_ref_select_stream& otl_ref_select_stream::operator>>(short& sh)
      {
       check_if_executed();
       if(eof())return *this;
       get_next();
       if(check_type(extFloat)&&!eof()){
        sh=(short)*(double*)sl[cur_col].val(cur_row);
        look_ahead();
       }
       return *this;
      }

      INLINE otl_ref_select_stream& otl_ref_select_stream::operator>>(long int& l)
      {
       check_if_executed();
       if(eof())return *this;
       get_next();
       if(check_type(extFloat)&&!eof()){
        l=(long)*(double*)sl[cur_col].val(cur_row);
        look_ahead();
       }
       return *this;
      }

      INLINE otl_ref_select_stream& otl_ref_select_stream::operator>>(float& f)
      {
       check_if_executed();
       if(eof())return *this;
       get_next();
       if(check_type(extFloat)&&!eof()){
        f=(float)*(double*)sl[cur_col].val(cur_row);
        look_ahead();
       }
       return *this;
      }

      INLINE otl_ref_select_stream& otl_ref_select_stream::operator>>(double& d)
      {
       check_if_executed();
       if(eof())return *this;
       get_next();
       if(check_type(extFloat)&&!eof()){
        d=*(double*)sl[cur_col].val(cur_row);
        look_ahead();
       }
       return *this;
      }

      INLINE void otl_ref_select_stream::get_next(void)
      {
       if(cur_col<sl_len-1){
        ++cur_col;
        null_fetched=sl[cur_col].is_null(cur_row);
       }else{
        ret_code=next();
        cur_col=0;
       }
      }

      INLINE int otl_ref_select_stream::check_type(int type_code)
      {
       if(sl[cur_col].get_ftype()!=type_code){
        throw otl_exception(otl_error_msg_0,
      		      otl_error_code_0,
                            stm_text);
        return 0;
       }else
        return 1;
      }

      INLINE void otl_ref_select_stream::look_ahead(void)
      {
       if(cur_col==sl_len-1){
        ret_code=next();
        cur_col=-1;
       }
      }

      INLINE void otl_ref_select_stream::get_select_list(void)
      {int i;
       otl_column_desc sl_desc_tmp[otl_var_list_size];
       int sld_tmp_len=0;
       int ftype,elem_size;

       otl_cursor::bind(cur_placeholder,
      		  &sel_cur.cda,
      		  sizeof(sel_cur.cda),
      		  102 // Special "OCI External datatype" code --
      		  // cursor reference
      		 );
       for(i=0;i<vl_len;++i)otl_cursor::bind(*vl[i]);
       otl_cursor::exec(); // Executing the PLSQL master block
       sel_cur.connected=1;
       cur_row=-2;
       sld_tmp_len=0;
       for(i=1;sel_cur.describe_column(sl_desc_tmp[i-1],i);++i)
        ++sld_tmp_len;
       sl_len=sld_tmp_len;
       if(sl){
        delete[] sl;
        sl=0;
       }
       sl=new otl_dynamic_variable[sl_len];
      #ifdef OTL_DEBUG
       cout<<"(088)+ = "<<(int*)sl<<endl;
      #endif

       for(int j=0;j<sl_len;++j){
        otl_map_ftype(sl_desc_tmp[j],ftype,elem_size);
        sl[j].copy_pos(j+1);
        sl[j].init(ftype,
      	     (short)elem_size,
      	     (short)array_size
      	    );
       }
       if(sl_desc){
        delete[] sl_desc;
        sl_desc=0;
       }
       sl_desc=new otl_column_desc[sl_len];
      #ifdef OTL_DEBUG
       cout<<"(089)+ = "<<(int*)sl_desc<<endl;
      #endif

       memcpy(sl_desc,sl_desc_tmp,sizeof(otl_column_desc)*sl_len);
       for(i=0;i<sl_len;++i)sel_cur.bind(sl[i]);
      }

      INLINE int otl_ref_select_stream::select_list_len(void)
      {
       return sl_len;
      }

      INLINE int otl_ref_select_stream::column_ftype(int ndx)
      {
       return sl[ndx].get_ftype();
      }

      INLINE int otl_ref_select_stream::column_size(int ndx)
      {
       return sl[ndx].get_elem_size();
      }

      INLINE void otl_ref_select_stream::set_delete_var(const int should_delete)
      {
       should_delete_flag=should_delete;
      }

      INLINE void otl_ref_select_stream::get_in_next(void)
      {
       if(cur_in==vl_len-1)
        rewind();
       else{
        ++cur_in;
        executed=0;
       }
      }

      INLINE int otl_ref_select_stream::check_in_type(int type_code,int tsize)
      {
       if(vl[cur_in]->ftype==extCChar&&type_code==extCChar)
        return 1;
       if(vl[cur_in]->ftype!=type_code||vl[cur_in]->elem_size!=tsize){
        throw otl_exception(otl_error_msg_0,
      		      otl_error_code_0,
                            stm_text);
        return 1;
       }else
        return 1;
      }

      INLINE void otl_ref_select_stream::check_in_var(void)
      {
       if(vl_len==0)
        throw otl_exception(otl_error_msg_1,
      		      otl_error_code_1,
                            stm_text);
      }

      INLINE void otl_ref_select_stream::check_if_executed(void)
      {
       if(!executed)
        throw otl_exception(otl_error_msg_2,
      		      otl_error_code_2,
                            stm_text);
      }

      INLINE otl_ref_select_stream& otl_ref_select_stream::operator<<(const char c)
      {
       check_in_var();
       if(check_in_type(extCChar,1)){
        char* tmp=(char*)vl[cur_in]->val();
        tmp[0]=c;
        tmp[1]=0;
       }
       get_in_next();
       return *this;
      }

      INLINE otl_ref_select_stream& otl_ref_select_stream::operator<<(const unsigned char c)
      {
       check_in_var();
       if(check_in_type(extCChar,1)){
        unsigned char* tmp=(unsigned char*)vl[cur_in]->val();
        tmp[0]=c;
        tmp[1]=0;
       }
       get_in_next();
       return *this;
      }

      INLINE otl_ref_select_stream& otl_ref_select_stream::operator<<(const char* s)
      {
       check_in_var();
       if(check_in_type(extCChar,1)){
        strcpy((char*)vl[cur_in]->val(),(char*)s);
       }
       get_in_next();
       return *this;
      }

      INLINE otl_ref_select_stream& otl_ref_select_stream::operator<<(const unsigned char* s)
      {
       check_in_var();
       if(check_in_type(extCChar,1)){
        strcpy((char*)vl[cur_in]->val(),(char*)s);
       }
       get_in_next();
       return *this;
      }

      INLINE otl_ref_select_stream& otl_ref_select_stream::operator<<(const int n)
      {
       check_in_var();
       if(check_in_type(extInt,sizeof(int))){
        *(int*)vl[cur_in]->val()=n;
       }
       get_in_next();
       return *this;
      }

      INLINE otl_ref_select_stream& otl_ref_select_stream::operator<<(const unsigned u)
      {
       check_in_var();
       if(check_in_type(extInt,sizeof(unsigned))){
        *(unsigned*)vl[cur_in]->val()=u;
       }
       get_in_next();
       return *this;
      }

      INLINE otl_ref_select_stream& otl_ref_select_stream::operator<<(const short sh)
      {
       check_in_var();
       if(check_in_type(extInt,sizeof(short))){
        *(short*)vl[cur_in]->val()=sh;
       }
       get_in_next();
       return *this;
      }

      INLINE otl_ref_select_stream& otl_ref_select_stream::operator<<(const long int l)
      {
       check_in_var();
       if(check_in_type(extInt,sizeof(long))){
        *(long*)vl[cur_in]->val()=l;
       }
       get_in_next();
       return *this;
      }

      INLINE otl_ref_select_stream& otl_ref_select_stream::operator<<(const float f)
      {
       check_in_var();
       if(check_in_type(extFloat,sizeof(float))){
        *(float*)vl[cur_in]->val()=f;
       }
       get_in_next();
       return *this;
      }

      INLINE otl_ref_select_stream& otl_ref_select_stream::operator<<(const double f)
      {
       check_in_var();
       if(check_in_type(extFloat,sizeof(double))){
        *(double*)vl[cur_in]->val()=f;
       }
       get_in_next();
       return *this;
      }

      INLINE void otl_out_stream::init(otl_connect& db,int should_delete)
      {
       dirty=0;
       auto_commit_flag=1;
       should_delete_flag=should_delete;
       cur_x=-1;
       cur_y=0;
       connect=&db;
       in_exception_flag=0;
       in_destruct_flag=0;
       stm_text=0;
      }

      INLINE otl_out_stream::otl_out_stream(otl_connect& db)
       :otl_cursor(db)
      {
       in_exception_flag=0;
       in_destruct_flag=0;
       dirty=0;
       auto_commit_flag=1;
       should_delete_flag=0;
       cur_x=-1;
       cur_y=0;
       connect=0;
       stm_text=0;
      }

      INLINE otl_out_stream::otl_out_stream(
         otl_connect& db,
         const char* sqlstm,
         ...
        ):otl_cursor(db)
      {
       init(db);

       otl_p_generic_variable tmp_vl[otl_var_list_size];
       va_list argv;
       va_start(argv,sqlstm);
       otl_generic_variable* v;

       do{
        v=(otl_generic_variable*)va_arg(argv,otl_generic_variable*);
        if(v){
         ++vl_len;
         tmp_vl[vl_len-1]=v;
        }
       }while(v);

       if(vl_len>0){
        vl=new otl_p_generic_variable[vl_len];
      #ifdef OTL_DEBUG
       cout<<"(090)+ = "<<(int*)vl<<endl;
      #endif

        for(int i=0;i<vl_len;++i)
         vl[i]=tmp_vl[i];
       }

       array_size=(short)vl[0]->array_size;
       parse(sqlstm);
       for(int i=0;i<vl_len;++i)
        bind(*vl[i]);
      }

      INLINE otl_out_stream::otl_out_stream(
        otl_connect& db,
        const char* sqlstm,
        otl_p_generic_variable* avp
        ):otl_cursor(db)
      {
       init(db);
       alloc_var(avp);
       array_size=(short)vl[0]->array_size;
       parse(sqlstm);
       for(int i=0;i<vl_len;++i)
        bind(*vl[i]);
      }

      INLINE otl_out_stream::otl_out_stream(
        short arr_size, // host array size
        const char* sqlstm, // SQL statement
        otl_connect& db // connect object
        ):otl_cursor(db)
      {
       init(db,1);
       array_size=arr_size;

       {
        int len=strlen(sqlstm)+1;
        stm_text=new char[len];
      #ifdef OTL_DEBUG
       cout<<"(091)+ = "<<(int*)stm_text<<endl;
      #endif

        strcpy(stm_text,sqlstm);
        otl_ext_hv_decl hvd(stm_text,arr_size);
        hvd.alloc_host_var_list(vl,vl_len);
       }

       parse();
       for(int i=0;i<vl_len;++i)
        bind(*vl[i]);
      }

      INLINE otl_out_stream::~otl_out_stream()
      {
       in_destruct_flag=1;
       if(dirty&&!in_exception_flag)flush();
       if(should_delete_flag)
        for(int i=0;i<vl_len;++i){
      #ifdef OTL_DEBUG
       cout<<"(020)- = "<<(int*)vl[i]<<endl;
      #endif
         delete vl[i];
        }
      #ifdef OTL_DEBUG
       cout<<"(021)- = "<<(int*)vl<<endl;
      #endif
       delete vl;
      #ifdef OTL_DEBUG
       cout<<"(022)- = "<<(int*)stm_text<<endl;
      #endif
       in_destruct_flag=0;
      }

      INLINE otl_out_stream& otl_out_stream::operator<<(const char c)
      {
       if(vl_len>0){
        get_next();
        if(check_type(extCChar,1)){
         char* tmp=(char*)vl[cur_x]->val(cur_y);
         tmp[0]=c;
         tmp[1]=0;
        }
        check_buf();
       }
       return *this;
      }

      INLINE otl_out_stream& otl_out_stream::operator<<(const unsigned char c)
      {
       if(vl_len>0){
        get_next();
        if(check_type(extCChar,1)){
         unsigned char* tmp=(unsigned char*)vl[cur_x]->val(cur_y);
         tmp[0]=c;
         tmp[1]=0;
        }
        check_buf();
       }
       return *this;
      }

      INLINE otl_out_stream& otl_out_stream::operator<<(const char* s)
      {
       if(vl_len>0){
        get_next();
        if(check_type(extCChar,1)){
         strcpy((char*)vl[cur_x]->val(cur_y),(char*)s);
        }
        check_buf();
       }
       return *this;
      }

      INLINE otl_out_stream& otl_out_stream::operator<<(const unsigned char* s)
      {
       if(vl_len>0){
        get_next();
        if(check_type(extCChar,1)){
         strcpy((char*)vl[cur_x]->val(cur_y),(char*)s);
        }
        check_buf();
       }
       return *this;
      }

      INLINE otl_out_stream& otl_out_stream::operator<<(const int n)
      {
       if(vl_len>0){
        get_next();
        if(check_type(extInt,sizeof(int))){
         *(int*)vl[cur_x]->val(cur_y)=n;
        }
        check_buf();
       }
       return *this;
      }

      INLINE otl_out_stream& otl_out_stream::operator<<(const unsigned u)
      {
       if(vl_len>0){
        get_next();
        if(check_type(extInt,sizeof(unsigned))){
         *(unsigned *)vl[cur_x]->val(cur_y)=u;
        }
        check_buf();
       }
       return *this;
      }

      INLINE otl_out_stream& otl_out_stream::operator<<(const short sh)
      {
       if(vl_len>0){
        get_next();
        if(check_type(extInt,sizeof(short))){
         *(short*)vl[cur_x]->val(cur_y)=sh;
        }
        check_buf();
       }
       return *this;
      }

      INLINE otl_out_stream& otl_out_stream::operator<<(const long int l)
      {
       if(vl_len>0){
        get_next();
        if(check_type(extInt,sizeof(int))){
         *(long*)vl[cur_x]->val(cur_y)=l;
        }
        check_buf();
       }
       return *this;
      }

      INLINE otl_out_stream& otl_out_stream::operator<<(const float f)
      {
       if(vl_len>0){
        get_next();
        if(check_type(extFloat,sizeof(float))){
         *(float*)vl[cur_x]->val(cur_y)=f;
        }
        check_buf();
       }
       return *this;
      }

      INLINE otl_out_stream& otl_out_stream::operator<<(const double d)
      {
       if(vl_len>0){
        get_next();
        if(check_type(extFloat,sizeof(double))){
         *(double*)vl[cur_x]->val(cur_y)=d;
        }
        check_buf();
       }
       return *this;
      }

      INLINE otl_out_stream& otl_out_stream::operator<<(const otl_null n)
      {
       if(vl_len>0){
        get_next();
        vl[cur_x]->set_null(cur_y);
        check_buf();
       }
       return *this;
      }

      INLINE void otl_out_stream::flush(void)
      {
       if(!dirty)return;
       if(rc||adb->rc){
        clean();
        return; // buffer is not flushed in case of error
       }
       if(cur_x!=vl_len-1){
        in_exception_flag=1;
        throw otl_exception(otl_error_msg_3,
      		      otl_error_code_3,
                            stm_text);
       }
       if(in_destruct_flag){
        int save=ex_enabled;
        ex_enabled=0;
        exec((short)(cur_y+1));
        ex_enabled=save;
        if(rc){
         clean();
         in_exception_flag=1;
         throw otl_exception(cda,stm_text);
         return;
        }
        if(auto_commit_flag){
         int save=connect->ex_enabled;
         connect->ex_enabled=0;
         connect->commit();
         connect->ex_enabled=save;
         if(connect->rc){
          clean();
          in_exception_flag=1;
          throw otl_exception(connect->lda,stm_text);
          return;
         }
        }
       }else{
        exec((short)(cur_y+1));
        if(auto_commit_flag)
         connect->commit();
        clean();
       }
      }

      INLINE void otl_out_stream::clean(void)
      {
       if(!dirty)return;
       for(int i=0;i<array_size;++i)
        for(int j=0;j<vl_len;++j)
         vl[j]->set_not_null(i);
       cur_x=-1;
       cur_y=0;
       dirty=0;
      }

      INLINE void otl_out_stream::set_commit(int auto_commit)
      {
       auto_commit_flag=auto_commit;
      }

      INLINE void otl_out_stream::set_delete_var(const int should_delete)
      {
       should_delete_flag=should_delete;
      }

      INLINE void otl_out_stream::get_next(void)
      {
       if(cur_x<vl_len-1)
        ++cur_x;
       else{
        if(cur_y<array_size-1){
         ++cur_y;
         cur_x=0;
        }else{
         flush();
         cur_x=0;
        }
       }
       dirty=1;
      }

      INLINE int otl_out_stream::check_type(int type_code, int tsize)
      {
       if(vl[cur_x]->ftype==extCChar&&type_code==extCChar)
        return 1;
       if(vl[cur_x]->ftype!=type_code||vl[cur_x]->elem_size!=tsize){
        in_exception_flag=1;
        throw otl_exception(otl_error_msg_0,
      		      otl_error_code_0,
                            stm_text);
        return 1;
       }else
        return 1;
      }

      INLINE void otl_out_stream::check_buf(void)
      {
       if(cur_x==vl_len-1 && cur_y==array_size-1)
        flush();
      }

      INLINE otl_inout_stream::otl_inout_stream(
        short arr_size, // host array size
        const char* sqlstm, // SQL statement
        otl_connect& db // connect object
        ):otl_out_stream(db)
      {
       dirty=0;
       auto_commit_flag=1;
       should_delete_flag=0;
       connect=&db;
       in_exception_flag=0;
       stm_text=0;
       array_size=arr_size;

       in_vl=0;
       iv_len=0;
       avl_len=0;
       avl=0;

       {
        int len=strlen(sqlstm)+1;
        stm_text=new char[len];
      #ifdef OTL_DEBUG
       cout<<"(092)+ = "<<(int*)stm_text<<endl;
      #endif

        strcpy(stm_text,sqlstm);
        otl_ext_hv_decl hvd(stm_text,arr_size);
        if(hvd.vst[otl_ext_hv_decl::def]==hvd.len){
         should_delete_flag=1;
         hvd.alloc_host_var_list(vl,vl_len);
        }else{
         for(int i=0;i<hvd.len;++i){
          if(hvd.inout[i]==otl_ext_hv_decl::in)
           ++vl_len;
          else if(hvd.inout[i]==otl_ext_hv_decl::out)
           ++iv_len;
          else if(hvd.inout[i]==otl_ext_hv_decl::io){
           ++vl_len;
           ++iv_len;
          }
         }
         if(vl_len>0){
          vl=new otl_p_generic_variable[vl_len];
      #ifdef OTL_DEBUG
       cout<<"(093)+ = "<<(int*)vl<<endl;
      #endif

         }
         if(iv_len>0){
          in_vl=new otl_p_generic_variable[iv_len];
      #ifdef OTL_DEBUG
       cout<<"(094)+ = "<<(int*)in_vl<<endl;
      #endif
         }
         if(hvd.len>0){
          avl=new otl_p_generic_variable[hvd.len];
      #ifdef OTL_DEBUG
       cout<<"(095)+ = "<<(int*)avl<<endl;
      #endif
         }
         iv_len=0; vl_len=0; avl_len=hvd.len;
         for(int j=0;j<avl_len;++j){
          otl_p_generic_variable v=hvd.alloc_var(hvd[j],hvd.inout[j],otl_ext_hv_decl::def);
          avl[j]=v;
          if(hvd.inout[j]==otl_ext_hv_decl::in){
           ++vl_len;
           vl[vl_len-1]=v;
          }else if(hvd.inout[j]==otl_ext_hv_decl::out){
           ++iv_len;
           in_vl[iv_len-1]=v;
          }else if(hvd.inout[j]==otl_ext_hv_decl::io){
           ++vl_len;
           ++iv_len;
           vl[vl_len-1]=v;
           in_vl[iv_len-1]=v;
          }
         }
        }
       }

       parse();
       for(int i=0;i<vl_len;++i)bind(*vl[i]);
       for(int j=0;j<iv_len;++j)bind(*in_vl[j]);

       rewind();
      }

      INLINE otl_inout_stream::~otl_inout_stream()
      {
       if(!in_exception_flag)
        flush();
       for(int i=0;i<avl_len;++i){
      #ifdef OTL_DEBUG
       cout<<"(023)- = "<<(int*)avl[i]<<endl;
      #endif
        delete avl[i];
       }
      #ifdef OTL_DEBUG
       cout<<"(024)- = "<<(int*)avl<<endl;
      #endif
       delete[] avl;
      #ifdef OTL_DEBUG
       cout<<"(025)- = "<<(int*)in_vl<<endl;
      #endif
       delete[] in_vl;
      }

      INLINE void otl_inout_stream::rewind(void)
      {
       flush();
       cur_in_x=0;
       cur_in_y=0;
       cur_x=-1;
       cur_y=0;
       in_y_len=0;
       null_fetched=0;
       if(vl_len==0){
        exec(array_size);
        in_y_len=array_size;
        cur_in_y=0;
        cur_in_x=0;
       }
      }

      INLINE int otl_inout_stream::eof(void)
      {
       if(iv_len==0)return 1;
       if(in_y_len==0)return 1;
       if(cur_in_y<=in_y_len-1)return 0;
       return 1;
      }

      INLINE void otl_inout_stream::flush(void)
      {
       if(vl_len==0)return;
       in_y_len=cur_y+1;
       cur_in_y=0;
       cur_in_x=0;
       if(!in_exception_flag)
        otl_out_stream::flush();
      }

      INLINE void otl_inout_stream::clean(void)
      {
       if(vl_len==0)return;
       in_y_len=cur_y+1;
       cur_in_y=0;
       cur_in_x=0;
       otl_out_stream::clean();
      }

      INLINE int otl_inout_stream::is_null(void)
      {
       return null_fetched;
      }

      INLINE otl_inout_stream& otl_inout_stream::operator>>(char& c)
      {
       if(eof())return *this;
       if(check_in_type(extCChar,1)){
        c=*(char*)in_vl[cur_in_x]->val(cur_in_y);
        null_fetched=is_null_intern();
       }
       get_in_next();
       return *this;
      }

      INLINE otl_inout_stream& otl_inout_stream::operator>>(unsigned char& c)
      {
       if(eof())return *this;
       if(check_in_type(extCChar,1)){
        c=*(unsigned char*)in_vl[cur_in_x]->val(cur_in_y);
        null_fetched=is_null_intern();
       }
       get_in_next();
       return *this;
      }

      INLINE otl_inout_stream& otl_inout_stream::operator>>(char* s)
      {
       if(eof())return *this;
       if(check_in_type(extCChar,1)){
        strcpy((char*)s,(char*)in_vl[cur_in_x]->val(cur_in_y));
        null_fetched=is_null_intern();
       }
       get_in_next();
       return *this;
      }

      INLINE otl_inout_stream& otl_inout_stream::operator>>(unsigned char* s)
      {
       if(eof())return *this;
       if(check_in_type(extCChar,1)){
        strcpy((char*)s,(char*)in_vl[cur_in_x]->val(cur_in_y));
        null_fetched=is_null_intern();
       }
       get_in_next();
       return *this;
      }

      INLINE otl_inout_stream& otl_inout_stream::operator>>(int& n)
      {
       if(eof())return *this;
       if(check_in_type(extInt,sizeof(int))){
        n=*(int*)in_vl[cur_in_x]->val(cur_in_y);
        null_fetched=is_null_intern();
       }
       get_in_next();
       return *this;
      }

      INLINE otl_inout_stream& otl_inout_stream::operator>>(unsigned& u)
      {
       if(eof())return *this;
       if(check_in_type(extInt,sizeof(unsigned))){
        u=*(unsigned*)in_vl[cur_in_x]->val(cur_in_y);
        null_fetched=is_null_intern();
       }
       get_in_next();
       return *this;
      }

      INLINE otl_inout_stream& otl_inout_stream::operator>>(short& sh)
      {
       if(eof())return *this;
       if(check_in_type(extInt,sizeof(short))){
        sh=*(short*)in_vl[cur_in_x]->val(cur_in_y);
        null_fetched=is_null_intern();
       }
       get_in_next();
       return *this;
      }

      INLINE otl_inout_stream& otl_inout_stream::operator>>(long int& l)
      {
       if(eof())return *this;
       if(check_in_type(extInt,sizeof(long))){
        l=*(long*)in_vl[cur_in_x]->val(cur_in_y);
        null_fetched=is_null_intern();
       }
       get_in_next();
       return *this;
      }

      INLINE otl_inout_stream& otl_inout_stream::operator>>(float& f)
      {
       if(eof())return *this;
       if(check_in_type(extFloat,sizeof(float))){
        f=*(float*)in_vl[cur_in_x]->val(cur_in_y);
        null_fetched=is_null_intern();
       }
       get_in_next();
       return *this;
      }

      INLINE otl_inout_stream& otl_inout_stream::operator>>(double& d)
      {
       if(eof())return *this;
       if(check_in_type(extFloat,sizeof(double))){
        d=*(double*)in_vl[cur_in_x]->val(cur_in_y);
        null_fetched=is_null_intern();
       }
       get_in_next();
       return *this;
      }

      INLINE void otl_inout_stream::get_in_next(void)
      {
       if(iv_len==0)return;
       if(in_y_len==0)return;
       if(cur_in_x<iv_len-1)
         ++cur_in_x;
       else{
        if(cur_in_y<in_y_len-1){
         ++cur_in_y;
         cur_in_x=0;
        }else{
         cur_in_y=0;
         cur_in_x=0;
         in_y_len=0;
        }
       }
      }

      INLINE int otl_inout_stream::check_in_type(int type_code,int tsize)
      {
       if(in_vl[cur_in_x]->ftype==extCChar&&type_code==extCChar)
        return 1;
       if(in_vl[cur_in_x]->ftype!=type_code||in_vl[cur_in_x]->elem_size!=tsize){
        in_exception_flag=1;
        throw otl_exception(otl_error_msg_0,
      		      otl_error_code_0,
                            stm_text);
        return 1;
       }else
        return 1;

      }

      INLINE int otl_inout_stream::is_null_intern(void)
      {
       if(iv_len==0)return 0;
       if(in_y_len==0)return 0;
       if(in_y_len>0)
        return in_vl[cur_in_x]->is_null(cur_in_y);
       return 0;
      }

      INLINE otl_stream::otl_stream(
         short arr_size, // host array size
         const char* sqlstm, // SQL statement
         otl_connect& db, // connect object
         const char* ref_cur_placeholder // reference cursor placeholder, e.g. ":cur"
      )
      {
       io=0; ss=0; ref_ss=0;
       adb=&db;
       open(arr_size,sqlstm,db,ref_cur_placeholder);
      }

      INLINE otl_stream::otl_stream()
      {
       ref_ss=0;
       io=0;
       ss=0;
       adb=0;
      }

      INLINE void otl_stream::open(
         short arr_size, // host array size
         const char* sqlstm, // SQL statement
         otl_connect& db, // connect object
         const char* ref_cur_placeholder // reference cursor placeholder, e.g. ":cur"
      )
      {
       char tmp[7];
       char* c=(char*)sqlstm;

       while(isspace(*c))++c;
       strncpy(tmp,c,6);
       tmp[6]=0;
       c=tmp;
       while(*c){
        *c=(char)toupper(*c);
        ++c;
       }
       adb=&db;
       if(strncmp(tmp,"SELECT",6)==0){
        ss=new otl_select_stream(arr_size,sqlstm,db);
      #ifdef OTL_DEBUG
       cout<<"(096)+ = "<<(int*)ss<<endl;
      #endif

       }
       else if(ref_cur_placeholder!=0){
        ref_ss=new otl_ref_select_stream(arr_size,sqlstm,ref_cur_placeholder,db);
      #ifdef OTL_DEBUG
       cout<<"(097)+ = "<<(int*)ref_ss<<endl;
      #endif
       }else {
        io=new otl_inout_stream(arr_size,sqlstm,db);
      #ifdef OTL_DEBUG
       cout<<"(098)+ = "<<(int*)io<<endl;
      #endif

       }
      }

      INLINE int otl_stream::good(void)
      {
       if(io||ss||ref_ss)
        return 1;
       else
        return 0;
      }

      INLINE void otl_stream::close(void)
      {
      #ifdef OTL_DEBUG
       cout<<"(026)- = "<<(int*)ss<<endl;
      #endif
       delete ss;
      #ifdef OTL_DEBUG
       cout<<"(027)- = "<<(int*)io<<endl;
      #endif
       delete io;
      #ifdef OTL_DEBUG
       cout<<"(026)- = "<<(int*)ref_ss<<endl;
      #endif
       delete ref_ss;
       ss=0; io=0; ref_ss=0;
       adb=0;
      }

      INLINE otl_stream::~otl_stream()
      {
       close();
      }

      INLINE int otl_stream::eof(void)
      {
       if(io)
        return io->eof();
       else if(ss)
        return ss->eof();
       else if(ref_ss)
        return ref_ss->eof();
       else
        return 1;
      }

      INLINE void otl_stream::flush(void)
      {
       if(io)io->flush();
      }

      INLINE void otl_stream::clean(void)
      {
       if(io)io->clean();
      }

      INLINE void otl_stream::rewind(void)
      {
       if(io)
        io->rewind();
       else if(ss)
        ss->rewind();
       else if(ref_ss)
        ref_ss->rewind();
      }

      INLINE int otl_stream::is_null(void)
      {
       if(io)
        return io->is_null();
       else if(ss)
        return ss->is_null();
       else if(ref_ss)
        return ref_ss->is_null();
       else
        return 0;
      }

      INLINE void otl_stream::set_commit(int auto_commit)
      {
       if(io)io->set_commit(auto_commit);
      }

      INLINE otl_stream& otl_stream::operator>>(char& c)
      {
       if(io)
        io->operator>>(c);
       else if(ss)
        ss->operator>>(c);
       else if(ref_ss)
        ref_ss->operator>>(c);
       return *this;
      }

      INLINE otl_stream& otl_stream::operator>>(unsigned char& c)
      {
       if(io)
        io->operator>>(c);
       else if(ss)
        ss->operator>>(c);
       else if(ref_ss)
        ref_ss->operator>>(c);
       return *this;
      }

      INLINE otl_stream& otl_stream::operator>>(char* s)
      {
       if(io)
        io->operator>>(s);
       else if(ss)
        ss->operator>>(s);
       else if(ref_ss)
        ref_ss->operator>>(s);
       return *this;
      }

      INLINE otl_stream& otl_stream::operator>>(unsigned char* s)
      {
       if(io)
        io->operator>>(s);
       else if(ss)
        ss->operator>>(s);
       else if(ref_ss)
        ref_ss->operator>>(s);
       return *this;
      }

      INLINE otl_stream& otl_stream::operator>>(int& n)
      {
       if(io)
        io->operator>>(n);
       else if(ss)
        ss->operator>>(n);
       else if(ref_ss)
        ref_ss->operator>>(n);
       return *this;
      }

      INLINE otl_stream& otl_stream::operator>>(unsigned& u)
      {
       if(io)
        io->operator>>(u);
       else if(ss)
        ss->operator>>(u);
       else if(ref_ss)
        ref_ss->operator>>(u);
       return *this;
      }

      INLINE otl_stream& otl_stream::operator>>(short& sh)
      {
       if(io)
        io->operator>>(sh);
       else if(ss)
        ss->operator>>(sh);
       else if(ref_ss)
        ref_ss->operator>>(sh);
       return *this;
      }

      INLINE otl_stream& otl_stream::operator>>(long int& l)
      {
       if(io)
        io->operator>>(l);
       else if(ss)
        ss->operator>>(l);
       else if(ref_ss)
        ref_ss->operator>>(l);
       return *this;
      }

      INLINE otl_stream& otl_stream::operator>>(float& f)
      {
       if(io)
        io->operator>>(f);
       else if(ss)
        ss->operator>>(f);
       else if(ref_ss)
        ref_ss->operator>>(f);
       return *this;
      }

      INLINE otl_stream& otl_stream::operator>>(double& d)
      {
       if(io)
        io->operator>>(d);
       else if(ss)
        ss->operator>>(d);
       else if(ref_ss)
        ref_ss->operator>>(d);
       return *this;
      }

      INLINE otl_stream& otl_stream::operator<<(const char c)
      {
       if(io)
        io->operator<<(c);
       else if(ss)
        ss->operator<<(c);
       else if(ref_ss)
        ref_ss->operator<<(c);
       return *this;
      }

      INLINE otl_stream& otl_stream::operator<<(const unsigned char c)
      {
       if(io)
        io->operator<<(c);
       else if(ss)
        ss->operator<<(c);
       else if(ref_ss)
        ref_ss->operator<<(c);
       return *this;
      }

      INLINE otl_stream& otl_stream::operator<<(const char* s)
      {
       if(io)
        io->operator<<(s);
       else if(ss)
        ss->operator<<(s);
       else if(ref_ss)
        ref_ss->operator<<(s);
       return *this;
      }

      INLINE otl_stream& otl_stream::operator<<(const unsigned char* s)
      {
       if(io)
        io->operator<<(s);
       else if(ss)
        ss->operator<<(s);
       else if(ref_ss)
        ref_ss->operator<<(s);
       return *this;
      }

      INLINE otl_stream& otl_stream::operator<<(const int n)
      {
       if(io)
        io->operator<<(n);
       else if(ss)
        ss->operator<<(n);
       else if(ref_ss)
        ref_ss->operator<<(n);
       return *this;
      }

      INLINE otl_stream& otl_stream::operator<<(const unsigned u)
      {
       if(io)
        io->operator<<(u);
       else if(ss)
        ss->operator<<(u);
       else if(ref_ss)
        ref_ss->operator<<(u);
       return *this;
      }

      INLINE otl_stream& otl_stream::operator<<(const short sh)
      {
       if(io)
        io->operator<<(sh);
       else if(ss)
        ss->operator<<(sh);
       else if(ref_ss)
        ref_ss->operator<<(sh);
       return *this;
      }

      INLINE otl_stream& otl_stream::operator<<(const long int l)
      {
       if(io)
        io->operator<<(l);
       else if(ss)
        ss->operator<<(l);
       else if(ref_ss)
        ref_ss->operator<<(l);
       return *this;
      }

      INLINE otl_stream& otl_stream::operator<<(const float f)
      {
       if(io)
        io->operator<<(f);
       else if(ss)
        ss->operator<<(f);
       else if(ref_ss)
        ref_ss->operator<<(f);
       return *this;
      }

      INLINE otl_stream& otl_stream::operator<<(const double d)
      {
       if(io)
        io->operator<<(d);
       else if(ss)
        ss->operator<<(d);
       else if(ref_ss)
        ref_ss->operator<<(d);
       return *this;
      }

      INLINE otl_stream& otl_stream::operator<<(const otl_null n)
      {
       if(io)io->operator<<(otl_null());
       return *this;
      }

      INLINE void otl_stream::vprintf(char* fmt,va_list argv)
      {
       char *c=fmt;

       while(*c){
        if(*c=='%'){
         ++c;
         switch(*c){
         case 'N':
          (*this)<<otl_null();
          break;
         case 'f':
          (*this)<<(float)va_arg(argv,double);
          break;
         case 'd':
          (*this)<<(int)va_arg(argv,int);
          break;
         case 'u':
          (*this)<<(unsigned)va_arg(argv,unsigned);
          break;
         case 'l':
          ++c;
          switch(*c){
          case 'd':
           (*this)<<(long)va_arg(argv,long);
           break;
          case 'f':
           (*this)<<(double)va_arg(argv,double);
           break;
          }
          break;
         case 'c':
          (*this)<<(char)va_arg(argv,int);
          break;
         case 's':
          (*this)<<(char*)va_arg(argv,char*);
          break;
         }
        }
        ++c;
       }
      }

      INLINE void otl_stream::vscanf(char* fmt,va_list argv)
      {char *c=fmt;

       while(*c){
        if(*c=='%'){
         ++c;
         switch(*c){
         case 'f':
          (*this)>>*(float*)va_arg(argv,float*);
          break;
         case 'd':
          (*this)>>*(int*)va_arg(argv,int*);
          break;
         case 'u':
          (*this)>>*(unsigned*)va_arg(argv,unsigned*);
          break;
        case 'l':
          ++c;
          switch(*c){
          case 'd':
           (*this)>>*(long*)va_arg(argv,long*);
           break;
         case 'f':
          (*this)>>*(double*)va_arg(argv,double*);
          break;
          }
          break;
         case 'c':
          (*this)>>*(char*)va_arg(argv,char*);
          break;
         case 's':
          (*this)>>(char*)va_arg(argv,char*);
          break;
         }
        }
        ++c;
       }
      }

      INLINE void otl_stream::printf(const char* fmt,...)
      {
       va_list argv;
       va_start(argv,fmt);
       vprintf((char*)fmt,argv);
       va_end(argv);
      }

      INLINE void otl_stream::scanf(const char* fmt,...)
      {
       va_list argv;
       va_start(argv,fmt);
       vscanf((char*)fmt,argv);
      }

      // =========================== Prosto*C functions  ===============================

      INLINE otl_connect* otl_logon(char* connect,otl_error_handler handler)
      {
       otl_connect* db=new otl_connect;
      #ifdef OTL_DEBUG
       cout<<"(099)+ = "<<(int*)db<<endl;
      #endif

       db->handler=handler;
       try{
         db->rlogon(connect);
       }catch(otl_exception& p){
        if(handler)(*handler)((char*)p.msg,p.code);
      #ifdef OTL_DEBUG
       cout<<"(028)- = "<<(int*)db<<endl;
      #endif
        delete db;
        return 0;
       }
       return db;
      }

      INLINE otl_connect* otl_proC_logon(otl_error_handler handler)
      {
       otl_connect* db=new otl_connect;
      #ifdef OTL_DEBUG
       cout<<"(100)+ = "<<(int*)db<<endl;
      #endif

       db->handler=handler;
       try{
         db->sqllda();
       }catch(otl_exception& p){
        if(handler)(*handler)((char*)p.msg,p.code);
      #ifdef OTL_DEBUG
       cout<<"(029)- = "<<(int*)db<<endl;
      #endif
        delete db;
        return 0;
       }
       return db;
      }

      INLINE int otl_logoff(otl_connect* db)
      {
       if(!db)return 0;
       try{
      #ifdef OTL_DEBUG
       cout<<"(030)- = "<<(int*)db<<endl;
      #endif
        delete db;
        return 1;
       }
       catch(otl_exception& p){
        if(db->handler)
         (*(otl_error_handler)db->handler)((char*)p.msg,p.code);
      #ifdef OTL_DEBUG
       cout<<"(031)- = "<<(int*)db<<endl;
      #endif
        delete db;
        return 0;
       }
       // return 1;
      }

      INLINE void otl_commit(otl_connect* db)
      {
       try{
        db->commit();
       }
       catch(otl_exception& p){
        if(db->handler)
         (*(otl_error_handler)db->handler)((char*)p.msg,p.code);
       }
      }

      INLINE void otl_rollback(otl_connect* db)
      {
       try{
        db->rollback();
       }
       catch(otl_exception& p){
        if(db->handler)
         (*(otl_error_handler)db->handler)((char*)p.msg,p.code);
       }
      }

      INLINE int otl_exec(otl_connect* db,char* stm,int ignore_error)
      {
       if(ignore_error){
        return otl_cursor::direct_exec(*db,stm,otl_exception::disabled);
       }else{
        try{
         otl_cursor::direct_exec(*db,stm);
         return 1;
        }
        catch(otl_exception& p){
         if(db->handler)
          (*(otl_error_handler)db->handler)((char*)p.msg,p.code);
         return 0;
        }
        //  return 1;
       }
      }

      INLINE otl_stream* otl_stream_open(otl_connect* db,
      				   char* stm,
      				   short bufsize,
      				   const char* ref_cur_placeholder
      				  )
      {otl_stream* s=0;
       try{
        s=new otl_stream(bufsize,stm,*db,ref_cur_placeholder);
      #ifdef OTL_DEBUG
       cout<<"(101)+ = "<<(int*)s<<endl;
      #endif

        return s;
       }
       catch(otl_exception& p){
        if(db->handler)
         (*(otl_error_handler)db->handler)((char*)p.msg,p.code);
        return 0;
       }
       // return 0;
      }

      INLINE void otl_stream_close(otl_stream* f)
      {otl_connect* db=f->adb;
       try{
      #ifdef OTL_DEBUG
       cout<<"(032)- = "<<(int*)f<<endl;
      #endif
        delete f;
       }
       catch(otl_exception& p){
        if(db->handler)
         (*(otl_error_handler)db->handler)((char*)p.msg,p.code);
       }
      }

      INLINE int otl_eof(otl_stream* f)
      {
       try{
        return f->eof();
       }
       catch(otl_exception& p){
        if(f->adb->handler)
         (*(otl_error_handler)f->adb->handler)((char*)p.msg,p.code);
       }
       return 1;
      }

      INLINE int otl_is_null(otl_stream* f)
      {
       try{
        return f->is_null();
       }
       catch(otl_exception& p){
        if(f->adb->handler)
         (*(otl_error_handler)f->adb->handler)((char*)p.msg,p.code);
       }
       return 0;
      }

      INLINE void otl_set_commit(otl_stream* f,int auto_commit)
      {
       try{
        f->set_commit(auto_commit);
       }
       catch(otl_exception& p){
        if(f->adb->handler)
         (*(otl_error_handler)f->adb->handler)((char*)p.msg,p.code);
       }
      }

      INLINE void otl_flush(otl_stream* f)
      {
       try{
        f->flush();
       }
       catch(otl_exception& p){
        if(f->adb->handler)
         (*(otl_error_handler)f->adb->handler)((char*)p.msg,p.code);
       }
      }

      INLINE void otl_printf(otl_stream* f,const char* fmt,...)
      {
       try{
        va_list argv;
        va_start(argv,fmt);
        f->vprintf((char*)fmt,argv);
        va_end(argv);
       }
       catch(otl_exception& p){
        if(f->adb->handler)
         (*(otl_error_handler)f->adb->handler)((char*)p.msg,p.code);
       }
      }

      INLINE void otl_scanf(otl_stream* f,const char* fmt,...)
      {
       try{
        va_list argv;
        va_start(argv,fmt);
        f->vscanf((char*)fmt,argv);
        va_end(argv);
       }
       catch(otl_exception& p){
        if(f->adb->handler)
         (*(otl_error_handler)f->adb->handler)((char*)p.msg,p.code);
       }
      }

      #endif

  ## Appendix D. Pro*OTL / Pre-Pro*C preprecessor's source code (ppc.C or ppc.cpp)

      //
      // The OCI Template Library 1.0.6,
      // Pro*OTL / Pre-Pro*C preprocessor 1.0.0,
      // Copyright (C) Sergei Kuchin, 1996
      // Author: Sergei Kuchin
      // This is free software. Permission to use, copy, modify and
      // redistribute it for any purpose is hereby granted without fee,
      // provided that the above copyright notice appear in all copies.
      //
      #define text ora_text
      #include <otl.h>
      #undef text
      #include <iostream.h>
      #include <fstream.h>
      #include <stdio.h>
      #include <stdlib.h>
      #include <string.h>
      #include <ctype.h>

      otl_connect db;
      fstream inf,outf,hf,hf1,cm;
      int hf1flag=0;
      int cmflag=0;
      char buf[1024];
      int line=0;

      const int dir_size=6;

      const char* directive[dir_size]={
       "#sql-init-main",     //  0
       "#sql-init-module",   //  1
       "#sql-select",        //  2
       "#sql-out-stm",       //  3
       "#sql-plsql",         //  4
       "#sql-str-type"       //  5
      };

      int select_count=0;

      int stflag=0;
      char sttype[128]="";

      void HelpMessage(void)
      {
       cout<<endl;
       cout<<"Usage: ppc <connect_string> <input_file> <proc-file> <h-file> <#define> [<macro-def> [<OTL-module>]]"<<endl;
       cout<<"Example: ppc scott/tiger sample.ppc sample.pc sample.h __SAMPLE_H"<<endl;
       cout<<endl;
      } /* HelpMessage */

      void GetLine(void)
      {
       inf.getline(buf,sizeof(buf));
       ++line;
      } /* GetLine */

      void Error(char* err)
      {
       cerr<<"Line ("<<line<<"):"<<buf<<endl;
       cerr<<err<<endl;
       db.logoff();
       exit(1);
      } /* Error */

      void SqlInitMain(void)
      {
       outf<<"#include <stdio.h>"<<endl;
       outf<<"#include <string.h>"<<endl;
       outf<<"EXEC SQL INCLUDE SQLCA;"<<endl;
       outf<<""<<endl;
       outf<<"#define sql_code sqlca.sqlcode"<<endl;
       outf<<"#define sql_rpc sqlca.sqlerrd[2]"<<endl;
       outf<<""<<endl;
       outf<<"typedef char CSTR_UserId[256];"<<endl;
       outf<<""<<endl;
       outf<<"EXEC SQL BEGIN DECLARE SECTION;"<<endl;
       outf<<"  EXEC SQL TYPE CSTR_UserId IS STRING(256);"<<endl;
       outf<<"  CSTR_UserId UserId;"<<endl;
       outf<<"EXEC SQL END DECLARE SECTION;"<<endl;
       outf<<""<<endl;
       outf<<"static void sqlerror(void)"<<endl;
       outf<<"{"<<endl;
       outf<<" EXEC SQL WHENEVER SQLERROR CONTINUE;"<<endl;
       outf<<" fprintf(stderr,\"\\n%s\\n\",sqlca.sqlerrm.sqlerrmc);"<<endl;
       outf<<" EXEC SQL ROLLBACK RELEASE;"<<endl;
       outf<<" exit(1);"<<endl;
       outf<<"}"<<endl;
       outf<<endl;
       outf<<"EXEC SQL WHENEVER SQLERROR DO sqlerror();"<<endl;

       if(cmflag){
        cm<<"#include <otl.h>"<<endl;
        cm<<"static otl_connect* db;"<<endl;
        cm<<"static otl_cursor hotcur;"<<endl<<endl;
        cm<<"extern \"C\"{"<<endl<<endl;
       }
      } /* SqlInitMain */

      void SqlInitModule(void)
      {
       outf<<"#include <stdio.h>"<<endl;
       outf<<"#include <string.h>"<<endl;
       outf<<"EXEC SQL INCLUDE SQLCA;"<<endl;
       outf<<""<<endl;
       outf<<"#define sql_code sqlca.sqlcode"<<endl;
       outf<<"#define sql_rpc sqlca.sqlerrd[2]"<<endl;
       outf<<""<<endl;
       outf<<"typedef char CSTR[128];"<<endl;
       outf<<""<<endl;
       outf<<"EXEC SQL BEGIN DECLARE SECTION;"<<endl;
       outf<<"  EXEC SQL TYPE CSTR IS STRING(128);"<<endl;
       outf<<"EXEC SQL END DECLARE SECTION;"<<endl;
       outf<<""<<endl;
       outf<<"static void sqlerror(void)"<<endl;
       outf<<"{"<<endl;
       outf<<" EXEC SQL WHENEVER SQLERROR CONTINUE;"<<endl;
       outf<<" fprintf(stderr,\"\\n%s\\n\",sqlca.sqlerrm.sqlerrmc);"<<endl;
       outf<<" EXEC SQL ROLLBACK RELEASE;"<<endl;
       outf<<" exit(1);"<<endl;
       outf<<"}"<<endl;
       outf<<""<<endl;
       outf<<endl;
       outf<<"EXEC SQL WHENEVER SQLERROR DO sqlerror();"<<endl;

       if(cmflag){
        cm<<"#include <otl.h>"<<endl;
        cm<<"static otl_connect* db;"<<endl;
        cm<<"static otl_cursor hotcur;"<<endl<<endl;
        cm<<"extern \"C\"{"<<endl<<endl;
       }

      } /* SqlInitModule */

      const int stmsize=16000;
      char stm[stmsize];
      char stm1[stmsize];
      char stmName[256];
      int  stmArrSize;

      const int MAX_VAR_ARRAY_SIZE=512;

      class HVDArray{
      public:
       enum VarStatus{
        in=0,
        out=1,
        io=2,
        def=3
       };
       HVDArray(char* stm);
       ~HVDArray();
       char* operator[](int ndx){return hv[ndx];};
       short v_status(int ndx){return inout[ndx];};

       char* hv[MAX_VAR_ARRAY_SIZE];
       short inout[MAX_VAR_ARRAY_SIZE];
       void AddVar(int &n,char* v,short in_out);
      };

      static int isId(char c)
      {
       return isalnum(c)||c=='_';
      }

      static int name_comp(char* n1,char* n2)
      {while(*n1!=' '&&*n1!='\0'&&*n2!=' '&&*n2!='\0'){
        if(toupper(*n1)!=toupper(*n2))return 0;
        ++n1;
        ++n2;
       }
       if(*n1==' '&&*n2!=' '||*n2==' '&&*n1!=' ')
        return 0;
       return 1;
      } /* name_comp */

      void HVDArray::AddVar(int &n,char* v,short in_out)
      {for(int i=0;i<n;++i)
        if(name_comp(hv[i],v))
         return;
       hv[n]=new char[strlen(v)+1];
       strcpy(hv[n],v);
       inout[n]=in_out;
       hv[++n]=0;
       inout[n]=def;
      }

      HVDArray::HVDArray(char* stm)
      {int i=0;
       short InStr=0;
       char *c=stm;

       hv[i]=0;
       while(*c){
        if(*c=='\''){
         if(!InStr)
          InStr=1;
         else{
          if(c[1]=='\'')
           ++c;
          else
           InStr=0;
         }
        }
        if(*c==':'&&!InStr){
         short in_out=in;
         char var[64];
         char* v=var;
         *v++=*c++;
         while(isId(*c))
          *v++=*c++;
         while(isspace(*c))
          ++c;
         if(*c=='<'){
          *c=' ';
          while(*c!='>'&&*c!=','&&*c){
           *v++=*c;
           *c++=' ';
          }
          if(*c==','){
           *c++=' ';
           if(toupper(*c)=='I'){
            if(toupper(c[2])=='O')
             in_out=io;
            else
             in_out=in;
           }else if(toupper(*c)=='O')
            in_out=out;
           while(*c!='>'&&*c)
            *c++=' ';
          }
          *c=' ';
          *v='\0';
          AddVar(i,var,in_out);
         }
        }
        ++c;
       }
      }

      HVDArray::~HVDArray()
      {
       for(int i=0;hv[i]!=0;++i)
        delete hv[i];
      }

      int cstr_arr[MAX_VAR_ARRAY_SIZE];
      int ca_len=0;

      void AddToCStrArr(int size)
      {
       for(int i=0;i<ca_len;++i)
        if(cstr_arr[i]==size)
         return;
       ++ca_len;
       cstr_arr[ca_len-1]=size;
      }

      char* ST(int size,char* Prefix,int type_prefix_on=0)
      {static char C[128];
       if(stflag)
        sprintf(C,"%s",sttype);
       else{
        if(type_prefix_on)
         sprintf(C,"tCSTR_%d_%s",size,Prefix);
        else
         sprintf(C,"CSTR_%d_%s",size,Prefix);
       }
       return C;
      }

      void GetCharDecl(char* s,char* name,int& size,char& type)
      {
       char *c=name,*c1=s;

       while(*c1!=' '&&*c1)
        *c++=*c1++;
       *c='\0';
       while(*c1==' '&&*c1)
        ++c1;
       type=toupper(*c1);
       if(type=='C'){
        char tmp[32];
        char *t=tmp;
        while(*c1!='['&&*c1)
          ++c1;
        ++c1;
        while(*c1!=']'&&*c1)
         *t++=*c1++;
        *t='\0';
        size=atoi(tmp);
       }
      }

      int DeclareHostVar(char* s,short VectSize, char* Prefix,int write)
      {char name[64];
       char type;
       sword size=0;

       GetCharDecl(s,name,size,type);
       switch(type){
       case 'C':
        if(write){
         outf<<"  "<<ST(size,Prefix)<<" "<<(char*)&name[1]<<"_"<<Prefix;
         if(VectSize==1)
          outf<<";"<<endl;
         else
          outf<<"["<<VectSize<<"];"<<endl;
        }
        break;
       case 'D':
        if(write){
         outf<<"  double "<<(char*)&name[1]<<"_"<<Prefix;
         if(VectSize==1)
          outf<<";"<<endl;
         else
          outf<<"["<<VectSize<<"];"<<endl;
        }
        break;
       case 'F':
        if(write){
         outf<<"  float "<<(char*)&name[1]<<"_"<<Prefix;
         if(VectSize==1)
          outf<<";"<<endl;
         else
          outf<<"["<<VectSize<<"];"<<endl;
        }
        break;
       case 'I':
        if(write){
         outf<<"  int "<<(char*)&name[1]<<"_"<<Prefix;
         if(VectSize==1)
          outf<<";"<<endl;
         else
          outf<<"["<<VectSize<<"];"<<endl;
        }
        break;
       case 'U':
        if(write){
         outf<<"  unsigned "<<(char*)&name[1]<<"_"<<Prefix;
         if(VectSize==1)
          outf<<";"<<endl;
         else
          outf<<"["<<VectSize<<"];"<<endl;
        }
        break;
       case 'S':
        if(write){
         outf<<"  short "<<(char*)&name[1]<<"_"<<Prefix;
         if(VectSize==1)
          outf<<";"<<endl;
         else
          outf<<"["<<VectSize<<"];"<<endl;
        }
        break;
       case 'L':
        if(write){
         outf<<"  long "<<(char*)&name[1]<<"_"<<Prefix;
         if(VectSize==1)
          outf<<";"<<endl;
         else
          outf<<"["<<VectSize<<"];"<<endl;
        }
        break;
       default:
        {
         char errmsg[128];
         strcpy(errmsg,"PPC: Invalid variable ==> ");
         strcat(errmsg,s);
         Error(errmsg);
        }
       }
       return size;
      }

      int DeclareTempHostVar(char* s,short VectSize, char* Prefix)
      {char name[64];
       char type;
       sword size=0;

       GetCharDecl(s,name,size,type);
       switch(type){
       case 'C':
        if(VectSize==1)
         cm<<"static otl_cstring<"<<size<<"> *"<<(char*)&name[1]<<"_"<<Prefix<<";"<<endl;
        else
         cm<<"static otl_cstring_array<"<<VectSize<<","<<size<<"> *"<<(char*)&name[1]<<"_"<<Prefix<<";"<<endl;
        break;
       case 'D':
         if(VectSize==1)
          cm<<"static otl_double *"<<(char*)&name[1]<<"_"<<Prefix<<";"<<endl;
         else
          cm<<"static otl_double_array<"<<VectSize<<"> *"<<(char*)&name[1]<<"_"<<Prefix<<";"<<endl;
        break;
       case 'F':
         if(VectSize==1)
          cm<<"static otl_float *"<<(char*)&name[1]<<"_"<<Prefix<<";"<<endl;
         else
          cm<<"static otl_float_array<"<<VectSize<<"> *"<<(char*)&name[1]<<"_"<<Prefix<<";"<<endl;
        break;
       case 'I':
         if(VectSize==1)
          cm<<"static otl_int *"<<(char*)&name[1]<<"_"<<Prefix<<";"<<endl;
         else
          cm<<"static otl_int_array<"<<VectSize<<"> *"<<(char*)&name[1]<<"_"<<Prefix<<";"<<endl;
        break;
       case 'U':
         if(VectSize==1)
          cm<<"static otl_unsigned *"<<(char*)&name[1]<<"_"<<Prefix<<";"<<endl;
         else
          cm<<"static otl_unsigned_array<"<<VectSize<<"> *"<<(char*)&name[1]<<"_"<<Prefix<<";"<<endl;
        break;
       case 'S':
         if(VectSize==1)
          cm<<"static otl_short_int *"<<(char*)&name[1]<<"_"<<Prefix<<";"<<endl;
         else
          cm<<"static otl_short_int_array<"<<VectSize<<"> *"<<(char*)&name[1]<<"_"<<Prefix<<";"<<endl;
        break;
       case 'L':
         if(VectSize==1)
          cm<<"static otl_long_int *"<<(char*)&name[1]<<"_"<<Prefix<<";"<<endl;
         else
          cm<<"static otl_long_int_array<"<<VectSize<<"> *"<<(char*)&name[1]<<"_"<<Prefix<<";"<<endl;
        break;
       default:
        {
         char errmsg[128];
         strcpy(errmsg,"PPC: Invalid variable ==> ");
         strcat(errmsg,s);
         Error(errmsg);
        }
       }
       return size;
      }

      void WriteTempInputVar(char* s, char* Prefix)
      {char name[64];
       char type;
       sword size=0;

       GetCharDecl(s,name,size,type);

       cm<<" (*cur_"<<Prefix<<")<<"<<(char*)&name[1]<<";"<<endl;
      }

      void ReplaceVarName(char* stm, char* outstm, char* Prefix)
      {short InStr=0;
       char *c=stm;
       char *c1=outstm;

       while(*c){
        if(*c=='\''){
         if(!InStr)
          InStr=1;
         else{
          if(c[1]=='\''){
           *c1=*c;
           ++c; ++c1;
          }else
           InStr=0;
         }
        }
        if(*c==':'&&!InStr&&isId(c[1])){
         *c1=*c;
         ++c; ++c1;
         while(isId(*c)){
          *c1=*c;
          ++c; ++c1;
         }
         *c1='_';
         ++c1;
         char *c2=Prefix;
         while(*c2){
          *c1=*c2;
          ++c1; ++c2;
         }
        }
        *c1=*c;
        ++c; ++c1;
       }
       *c1=0;
      }

      void CompressBlank(char* instm, char* outstm)
      {
       char* c=instm;
       char* c1=outstm;
       while(*c){
        *c1++=*c;
        if(*c==' '){
         while(*c==' '&&*c!=0)
          ++c;
        }else
         ++c;
       }
       *c1=0;
      }

      void OpenFuncPrototype(HVDArray& hvd,char* Prefix, fstream& f,char* fname)
      {

       f<<"void "<<Prefix<<fname<<"(";
       if(!hvd[0]){
        f<<"void)";
        return;
       }else
        f<<endl;

       int i=0;
       while(hvd[i]){

        char name[64];
        char type;
        sword size=0;
        char *c=name,*c1=hvd[i];

        while(*c1!=' ')
         *c++=*c1++;
        *c='\0';
        while(*c1==' ')
         ++c1;
        type=toupper(*c1);
        if(type=='C'){
         char tmp[32];
         char *t=tmp;
         while(*c1!='[')
          ++c1;
         ++c1;
         while(*c1!=']')
          *t++=*c1++;
         *t='\0';
         size=atoi(tmp);
        }

        switch(type){
        case 'C':
         f<<"    char* "<<&name[1];
         break;
        case 'D':
         f<<"    double "<<&name[1];
         break;
        case 'F':
         f<<"    float "<<&name[1];
         break;
        case 'I':
         f<<"    int "<<&name[1];
         break;
        case 'U':
         f<<"    unsigned "<<&name[1];
         break;
        case 'S':
         f<<"    short "<<&name[1];
         break;
        case 'L':
         f<<"    long "<<&name[1];
         break;
        default:
         {
          char errmsg[128];
          strcpy(errmsg,"PPC: Invalid variable ==> ");
          strcat(errmsg,hvd[i]);
          Error(errmsg);
         }
        }
        if(hvd[i+1])f<<",";
        f<<endl;

        ++i;
       }
       f<<")";
      }

      void AssignInputVar(HVDArray& hvd,char* Prefix,int ArrSize,int plsql=0)
      {
       if(!hvd[0])return;

       int i=0;
       while(hvd[i]){

        char name[64];
        char type;
        sword size=0;
        char *c=name,*c1=hvd[i];

        while(*c1!=' ')
         *c++=*c1++;
        *c='\0';
        while(*c1==' ')
         ++c1;
        type=toupper(*c1);
        if(type=='C'){
         char tmp[32];
         char *t=tmp;
         while(*c1!='[')
          ++c1;
         ++c1;
         while(*c1!=']')
          *t++=*c1++;
         *t='\0';
         size=atoi(tmp);
        }

        if(plsql){
         if(hvd.inout[i]==HVDArray::in||hvd.inout[i]==HVDArray::io){
          switch(type){
          case 'C':
           outf<<" strcpy("<<&name[1]<<"_"<<Prefix<<","<<&name[1]<<");"<<endl;
           break;
          case 'D':
          case 'F':
          case 'I':
          case 'U':
          case 'S':
          case 'L':
           if(hvd.inout[i]==HVDArray::in)
            outf<<" "<<&name[1]<<"_"<<Prefix<<"="<<&name[1]<<";"<<endl;
           else
            outf<<" "<<&name[1]<<"_"<<Prefix<<"=*"<<&name[1]<<";"<<endl;
           break;
          default:
           {
            char errmsg[128];
            strcpy(errmsg,"PPC: Invalid variable ==> ");
            strcat(errmsg,hvd[i]);
            Error(errmsg);
           }
          }
         }

        }else{

         switch(type){
         case 'C':
          if(ArrSize==1)
           outf<<" strcpy("<<&name[1]<<"_"<<Prefix<<","<<&name[1]<<");"<<endl;
          else
           outf<<" strcpy((char*)&"<<&name[1]<<"_"<<Prefix
      	 <<"[n_"<<Prefix<<"-1],"<<&name[1]<<");"<<endl;
          break;
         case 'D':
         case 'F':
         case 'I':
         case 'U':
         case 'S':
         case 'L':
          if(ArrSize==1)
           outf<<" "<<&name[1]<<"_"<<Prefix<<"="<<&name[1]<<";"<<endl;
          else
           outf<<" "<<&name[1]<<"_"<<Prefix<<"[n_"<<Prefix<<"-1]"
      	 <<"="<<&name[1]<<";"<<endl;
          break;
         default:
          {
           char errmsg[128];
           strcpy(errmsg,"PPC: Invalid variable ==> ");
           strcat(errmsg,hvd[i]);
           Error(errmsg);
          }
         }

        }

        ++i;
       }
       outf<<endl;
      }

      void AssignStackInputVar(HVDArray& hvd,char* Prefix)
      {
       if(!hvd[0])return;

       int i=0;
       while(hvd[i]){

        char name[64];
        char type;
        sword size=0;
        char *c=name,*c1=hvd[i];

        while(*c1!=' ')
         *c++=*c1++;
        *c='\0';
        while(*c1==' ')
         ++c1;
        type=toupper(*c1);
        if(type=='C'){
         char tmp[32];
         char *t=tmp;
         while(*c1!='[')
          ++c1;
         ++c1;
         while(*c1!=']')
          *t++=*c1++;
         *t='\0';
         size=atoi(tmp);
        }

        cm<<" (*str_"<<Prefix<<")<<"<<&name[1]<<";"<<endl;

        ++i;
       }
       cm<<endl;
      }

      void AssignOutputVar(HVDArray& hvd,char* Prefix)
      {
       if(!hvd[0])return;

       outf<<endl;
       int i=0;
       while(hvd[i]){

        char name[64];
        char type;
        sword size=0;
        char *c=name,*c1=hvd[i];

        while(*c1!=' ')
         *c++=*c1++;
        *c='\0';
        while(*c1==' ')
         ++c1;
        type=toupper(*c1);
        if(type=='C'){
         char tmp[32];
         char *t=tmp;
         while(*c1!='[')
          ++c1;
         ++c1;
         while(*c1!=']')
          *t++=*c1++;
         *t='\0';
         size=atoi(tmp);
        }

        if(hvd.inout[i]!=HVDArray::in){
         switch(type){
         case 'C':
          outf<<" strcpy("<<&name[1]<<","<<&name[1]<<"_"<<Prefix<<");"<<endl;
          break;
         case 'D':
         case 'F':
         case 'I':
         case 'U':
         case 'S':
         case 'L':
          outf<<" *"<<&name[1]<<"="<<&name[1]<<"_"<<Prefix<<";"<<endl;
          break;
         default:
          {
           char errmsg[128];
           strcpy(errmsg,"PPC: Invalid variable ==> ");
           strcat(errmsg,hvd[i]);
           Error(errmsg);
          }
         }
        }

        ++i;
       }
       outf<<endl;
      }

      void ReplaceEoln(char* stm,char* outstm)
      {
       char* c=stm;
       char* c1=outstm;

       while(*c){
        if(*c=='\n'){
         if(c[1]==0||c[1]==' ')
          ++c;
         else{
         *c1=' ';
         ++c; ++c1;
         }
        }else{
         *c1=*c;
         ++c; ++c1;
        }
       }
       *c1=0;
      } /* ReplaceEoln */

      int int2ext(int int_type)
      {
       switch(int_type){
       case inVarChar2: return extCChar;
       case inNumber:   return extFloat;
       case inLong:     return extCChar;
       case inRowId:    return extCChar;
       case inDate:     return extCChar;
       case inRaw:      return extCChar;
       case inLongRaw:  return extCChar;
       case inChar:     return extCChar;
       default:
        return -1;
       }
      }

      int datatype_size(int ftype,int maxsz,int int_type)
      {
       switch(ftype){
       case extCChar:
        switch(int_type){
        case inRowId:
         return sizeof(otl_cchar_rowid);
        case inDate:
         return 10; // e.g. 19-JAN-64 + '\0'
        case inLong:
        case inLongRaw:
         return otl_max_long_size;
        case inRaw:
         return maxsz*2+1;
        default:
         return maxsz+1;
        }
       case extFloat:
        return sizeof(double);
       case extDate:
        return sizeof(otl_date_intern);
       default:
        return 0;
       }
      }

      void map_ftype(const otl_column_desc& desc,
      	       int& ftype,
      	       int& elem_size)
      {
       ftype=int2ext(desc.dbtype);
       elem_size=datatype_size(ftype,desc.dbsize,desc.dbtype);
      }

      int isId(char* s)
      {
       while(*s){
        if(!isId(*s))
         return 0;
        ++s;
       }
       return 1;
      } /* isId */

      void generate_select_list_struct(otl_select_stream& selstr,char* Prefix)
      {
       int ftype,elem_size;

       hf<<endl;
       hf<<"struct struct_"<<Prefix<<"{"<<endl<<endl;
       for(int j=0;j<selstr.select_list_len();++j){
        map_ftype(selstr.sl_desc[j],ftype,elem_size);
        int id_flag=isId((char*)selstr.sl_desc[j].name);
        switch(ftype){
        case extCChar:
         if(id_flag){
          hf<<"  char "<<selstr.sl_desc[j].name<<"["<<elem_size<<"];"<<endl;
          hf<<"  short "<<selstr.sl_desc[j].name<<"_IND;"<<endl<<endl;
         }else{
          hf<<"  char F"<<j+1<<"["<<elem_size<<"];/* "<<selstr.sl_desc[j].name<<" */"<<endl;
          hf<<"  short F"<<j+1<<"_IND;"<<endl<<endl;
         }
         break;
        case extFloat:
         if(id_flag){
          hf<<"  double "<<selstr.sl_desc[j].name<<";"<<endl;
          hf<<"  short "<<selstr.sl_desc[j].name<<"_IND;"<<endl<<endl;
         }else{
          hf<<"  double F"<<j+1<<";/* "<<selstr.sl_desc[j].name<<" */"<<endl;
          hf<<"  short F"<<j+1<<"_IND;"<<endl<<endl;
         }
         break;
        }
       }
       hf<<"};"<<endl;
       hf<<"typedef struct struct_"<<Prefix<<" "<<Prefix<<";"<<endl<<endl;
      }

      void GetFuncBody1(otl_select_stream& selstr,char* Prefix)
      {
       int ftype,elem_size,sz,j;

       outf<<endl;
       outf<<"int "<<Prefix<<"_get("<<Prefix<<"* out)"<<endl;
       outf<<"{"<<endl;

       ca_len=0;

       for(j=0;j<selstr.select_list_len();++j){
        map_ftype(selstr.sl_desc[j],ftype,elem_size);
        if(ftype==extCChar){
         sz=elem_size;
         AddToCStrArr(sz);
        }
       }
       if(ca_len>0)outf<<endl;
       for(j=0;j<ca_len;++j){
        if(!stflag)
         outf<<" typedef char tCSTR_"<<cstr_arr[j]<<"_"
             <<Prefix<<"["<<cstr_arr[j]<<"];"<<endl;
       }
       if(ca_len>0)outf<<endl;

       outf<<" EXEC SQL BEGIN DECLARE SECTION;"<<endl;

       if(ca_len>0)outf<<endl;
       for(j=0;j<ca_len;++j){
        if(!stflag)
         outf<<"    EXEC SQL TYPE tCSTR_"<<cstr_arr[j]<<"_"
             <<stmName<<" IS STRING("<<cstr_arr[j]<<");"<<endl;
       }
       if(ca_len>0)outf<<endl;

       for(j=0;j<selstr.select_list_len();++j){
        map_ftype(selstr.sl_desc[j],ftype,elem_size);
        int id_flag=isId((char*)selstr.sl_desc[j].name);
        switch(ftype){
        case extCChar:
         if(id_flag){
          outf<<"    "<<ST(elem_size,Prefix,1)<<" "<<selstr.sl_desc[j].name<<";"<<endl;
          outf<<"    short "<<selstr.sl_desc[j].name<<"_IND;"<<endl<<endl;
         }else{
          outf<<"    "<<ST(elem_size,Prefix,1)<<" F"<<j+1<<";"<<endl;
          outf<<"    short F"<<j+1<<"_IND;"<<endl<<endl;
         }
         break;
        case extFloat:
         if(id_flag){
          outf<<"    double "<<selstr.sl_desc[j].name<<";"<<endl;
          outf<<"    short "<<selstr.sl_desc[j].name<<"_IND;"<<endl<<endl;
         }else{
          outf<<"    double F"<<j+1<<";"<<endl;
          outf<<"    short F"<<j+1<<"_IND;"<<endl<<endl;
         }
         break;
        }
       }
       outf<<endl<<" EXEC SQL END DECLARE SECTION;"<<endl<<endl;

       outf<<" do{"<<endl<<endl;
       outf<<"  EXEC SQL FETCH "<<Prefix<<"_cur INTO"<<endl;
       for(j=0;j<selstr.select_list_len();++j){
        map_ftype(selstr.sl_desc[j],ftype,elem_size);
        int id_flag=isId((char*)selstr.sl_desc[j].name);
         if(id_flag)
          outf<<"    :"<<selstr.sl_desc[j].name<<":"<<selstr.sl_desc[j].name<<"_IND";
         else
          outf<<"    :F"<<j+1<<":F"<<j+1<<"_IND";
         if(j<selstr.select_list_len()-1)
          outf<<",";
         else
          outf<<";";
         outf<<endl;
       }
       outf<<endl;

       for(j=0;j<selstr.select_list_len();++j){
        map_ftype(selstr.sl_desc[j],ftype,elem_size);
        int id_flag=isId((char*)selstr.sl_desc[j].name);
        switch(ftype){
        case extCChar:
         if(id_flag){
          outf<<"  strcpy(out->"<<selstr.sl_desc[j].name<<","
      	<<selstr.sl_desc[j].name<<");"<<endl;
         }else{
          outf<<"  strcpy(out->F"<<j+1<<","
      	<<"F"<<j+1<<");"<<endl;
         }
         break;
        case extFloat:
         if(id_flag){
          outf<<"  out->"<<selstr.sl_desc[j].name
      	<<"="<<selstr.sl_desc[j].name<<";"<<endl;
         }else{
          outf<<"  out->F"<<j+1<<"=F"<<j+1<<";"<<endl;
         }
         break;
        }
        if(id_flag){
         outf<<"  out->"<<selstr.sl_desc[j].name<<"_IND"
             <<"="<<selstr.sl_desc[j].name<<"_IND;"<<endl;
        }else{
         outf<<"  out->F"<<j+1<<"_IND=F"<<j+1<<"_IND;"<<endl;
        }
        outf<<endl;
       }
       outf<<endl;
       outf<<" }while(0);"<<endl<<endl;

       outf<<" return sqlca.sqlcode;"<<endl<<endl;

       outf<<"}"<<endl<<endl;
      }

      void GetTempFuncBody(otl_select_stream& selstr,char* Prefix)
      {
       int ftype,elem_size,sz,j;

       cm<<endl;
       cm<<"int "<<Prefix<<"_get("<<Prefix<<"* out)"<<endl;
       cm<<"{"<<endl;
       cm<<" if(str_"<<Prefix<<"->eof())return 1;"<<endl;
       for(j=0;j<selstr.select_list_len();++j){
        map_ftype(selstr.sl_desc[j],ftype,elem_size);
        int id_flag=isId((char*)selstr.sl_desc[j].name);
        if(id_flag){
         cm<<" (*str_"<<Prefix<<")>>out->"<<selstr.sl_desc[j].name<<";"<<endl;
        }else{
         cm<<" (*str_"<<Prefix<<")>>out->F"<<j+1<<";"<<endl;
        }
       }
       cm<<endl;
       for(j=0;j<selstr.select_list_len();++j){
        map_ftype(selstr.sl_desc[j],ftype,elem_size);
        int id_flag=isId((char*)selstr.sl_desc[j].name);
        if(id_flag){
         cm<<" out->"<<selstr.sl_desc[j].name<<"_IND=str_"
           <<Prefix<<"->is_null()?-1:0;"<<endl;
        }else{
         cm<<" out->F"<<j+1<<"_IND=str_"
           <<Prefix<<"->is_null()?-1:0;"<<endl;
        }
       }
       cm<<endl;
       cm<<" return 0;"<<endl;
       cm<<"}"<<endl<<endl;
      }

      void GetFuncN(otl_select_stream& selstr,char* Prefix,short ArrSize)
      {
       int ftype,elem_size,sz,j;

       outf<<endl;

       ca_len=0;

       for(j=0;j<selstr.select_list_len();++j){
        map_ftype(selstr.sl_desc[j],ftype,elem_size);
        if(ftype==extCChar){
         sz=elem_size;
         AddToCStrArr(sz);
        }
       }
       if(ca_len>0)outf<<endl;
       for(j=0;j<ca_len;++j){
        if(!stflag)
         outf<<"typedef char tCSTR_"<<cstr_arr[j]<<"_"
             <<Prefix<<"["<<cstr_arr[j]<<"];"<<endl;
       }
       if(ca_len>0)outf<<endl;

       outf<<"EXEC SQL BEGIN DECLARE SECTION;"<<endl;

       if(ca_len>0)outf<<endl;
       for(j=0;j<ca_len;++j){
        if(!stflag)
         outf<<"  EXEC SQL TYPE tCSTR_"<<cstr_arr[j]<<"_"
             <<stmName<<" IS STRING("<<cstr_arr[j]<<");"<<endl;
       }
       if(ca_len>0)outf<<endl;
       outf<<endl;
       outf<<"  int row_fetch_count_"<<Prefix<<";"<<endl;
       outf<<"  int cur_row_pos_"<<Prefix<<";"<<endl;
       outf<<"  long rpc_save_"<<Prefix<<";"<<endl;
       outf<<"  int end_of_data_"<<Prefix<<";"<<endl;
       outf<<endl;

       for(j=0;j<selstr.select_list_len();++j){
        map_ftype(selstr.sl_desc[j],ftype,elem_size);
        int id_flag=isId((char*)selstr.sl_desc[j].name);
        switch(ftype){
        case extCChar:
         if(id_flag)
          outf<<"  "<<ST(elem_size,Prefix,1)<<" "<<selstr.sl_desc[j].name<<"_"<<Prefix
      	<<"["<<ArrSize<<"];"<<endl;
         else
          outf<<"  "<<ST(elem_size,Prefix,1)<<" F"<<j+1<<"_"<<Prefix
      	<<"["<<ArrSize<<"];"<<endl;
         break;
        case extFloat:
         if(id_flag)
          outf<<"  double "<<selstr.sl_desc[j].name<<"_"<<Prefix
      	<<"["<<ArrSize<<"];"<<endl;
         else
          outf<<"  double F"<<j+1<<"_"<<Prefix<<"["<<ArrSize<<"];"<<endl;
         break;
        }
        if(id_flag)
         outf<<"  short "<<selstr.sl_desc[j].name<<"_IND_"<<Prefix
             <<"["<<ArrSize<<"];"<<endl;
        else
         outf<<"  short F"<<j+1<<"_IND_"<<Prefix<<"["<<ArrSize<<"];"<<endl;
        outf<<endl;
       }
       outf<<endl<<"EXEC SQL END DECLARE SECTION;"<<endl<<endl;

       outf<<"int "<<Prefix<<"_get("<<Prefix<<"* out)"<<endl;
       outf<<"{"<<endl;
       outf<<" if(count_"<<Prefix<<"==0){"<<endl;
       outf<<"  ++count_"<<Prefix<<";"<<endl;
       outf<<"  row_fetch_count_"<<Prefix<<"=0;"<<endl;
       outf<<"  cur_row_pos_"<<Prefix<<"=0;"<<endl;
       outf<<"  end_of_data_"<<Prefix<<"=0;"<<endl;
       outf<<" }"<<endl;

       outf<<" do{"<<endl<<endl;

       outf<<"   if(row_fetch_count_"<<Prefix<<"==0){"<<endl;
       outf<<"    if(end_of_data_"<<Prefix<<")return 1;"<<endl;
       outf<<"      rpc_save_"<<Prefix<<"=sqlca.sqlerrd[2];"<<endl;
       outf<<"      EXEC SQL WHENEVER NOT FOUND goto "<<Prefix<<"_breakloop;"<<endl;
       outf<<"      EXEC SQL FETCH "<<Prefix<<"_cur INTO"<<endl;
       for(j=0;j<selstr.select_list_len();++j){
        map_ftype(selstr.sl_desc[j],ftype,elem_size);
        int id_flag=isId((char*)selstr.sl_desc[j].name);
         if(id_flag)
          outf<<"        :"<<selstr.sl_desc[j].name<<"_"<<Prefix
      	<<":"<<selstr.sl_desc[j].name<<"_IND_"<<Prefix;
         else
          outf<<"        :F"<<j+1<<"_"<<Prefix<<":F"<<j+1<<"_IND_"<<Prefix;
         if(j<selstr.select_list_len()-1)
          outf<<",";
         else
          outf<<";";
         outf<<endl;
       }
       outf<<"      goto "<<Prefix<<"_skip;"<<endl<<endl;
       outf<<"  "<<Prefix<<"_breakloop:"<<endl<<endl;
       outf<<"      if(sqlca.sqlerrd[2]-rpc_save_"<<Prefix<<"==0)return 1;"<<endl;
       outf<<"      end_of_data_"<<Prefix<<"=1;"<<endl;
       outf<<endl;
       outf<<"  "<<Prefix<<"_skip:"<<endl;
       outf<<endl;
       outf<<"      row_fetch_count_"<<Prefix<<"=sqlca.sqlerrd[2]-rpc_save_"<<Prefix<<";"
           <<endl;
       outf<<"      cur_row_pos_"<<Prefix<<"=0;"<<endl;
       outf<<"   }"<<endl;

       outf<<endl;

       for(j=0;j<selstr.select_list_len();++j){
        map_ftype(selstr.sl_desc[j],ftype,elem_size);
        int id_flag=isId((char*)selstr.sl_desc[j].name);
        switch(ftype){
        case extCChar:
         if(id_flag){
          outf<<"   strcpy(out->"<<selstr.sl_desc[j].name<<",(char*)&"
      	<<selstr.sl_desc[j].name
      	<<"_"<<Prefix
      	<<"[cur_row_pos_"<<Prefix<<"]);"<<endl;
         }else{
          outf<<"   strcpy(out->F"<<j+1<<",(char*)&"
      	<<"F"<<j+1
      	<<"_"<<Prefix
      	<<"[cur_row_pos_"<<Prefix<<"]);"<<endl;
         }
         break;
        case extFloat:
         if(id_flag){
          outf<<"   out->"<<selstr.sl_desc[j].name
      	<<"="<<selstr.sl_desc[j].name
      	<<"_"<<Prefix
      	<<"[cur_row_pos_"<<Prefix<<"];"<<endl;
         }else{
          outf<<"   out->F"<<j+1<<"=F"<<j+1
      	<<"_"<<Prefix
      	<<"[cur_row_pos_"<<Prefix<<"];"<<endl;
         }
         break;
        }
        if(id_flag){
         outf<<"   out->"<<selstr.sl_desc[j].name<<"_IND"
             <<"="<<selstr.sl_desc[j].name<<"_IND"
             <<"_"<<Prefix
             <<"[cur_row_pos_"<<Prefix<<"];"<<endl;
        }else{
         outf<<"   out->F"<<j+1<<"_IND=F"<<j+1<<"_IND"
             <<"_"<<Prefix
             <<"[cur_row_pos_"<<Prefix<<"];"<<endl;
        }
        outf<<endl;
       }
       outf<<endl;
       outf<<"   ++cur_row_pos_"<<Prefix<<";"<<endl;
       outf<<"   --row_fetch_count_"<<Prefix<<";"<<endl;
       outf<<endl;
       outf<<" }while(0);"<<endl<<endl;
       outf<<" return 0;"<<endl<<endl;
       outf<<"}"<<endl<<endl;
      }

      void PrintfStmOffs(char* stm,char* offs)
      {
       char* c=stm;
       while(*c){
        if(*c=='\n'&&c[1]!=0){
         outf<<endl<<offs;
        }else
         outf<<*c;
        ++c;
       }
      }

      void PrintStm(char* stm,int no_new_line=0)
      {
       char* c=stm;
       if(!no_new_line)
        cm<<endl;
       cm<<"    \"";
       while(*c){
        if(*c=='\n'&&c[1]!=0){
         cm<<" \""<<endl<<"    \"";
        }else if(*c=='\n'&&c[1]==0){
         if(!no_new_line)
          cm<<"\","<<endl;
         else
          cm<<"\""<<endl;
        }else
         cm<<*c;
        ++c;
       }
       if(!no_new_line)
        cm<<endl;
      }

      void ExecFuncPrototype(HVDArray& hvd,char* Prefix, fstream& f)
      {

       f<<"void "<<Prefix<<"_exec(";
       if(!hvd[0]){
        f<<"void)";
        return;
       }else
        f<<endl;

       int i=0;
       while(hvd[i]){

        char name[64];
        char type;
        sword size=0;
        char *c=name,*c1=hvd[i];

        while(*c1!=' ')
         *c++=*c1++;
        *c='\0';
        while(*c1==' ')
         ++c1;
        type=toupper(*c1);
        if(type=='C'){
         char tmp[32];
         char *t=tmp;
         while(*c1!='[')
          ++c1;
         ++c1;
         while(*c1!=']')
          *t++=*c1++;
         *t='\0';
         size=atoi(tmp);
        }

        switch(type){
        case 'C':
         f<<"    char* "<<&name[1];
         break;
        case 'D':
         f<<"    double"<<(hvd.inout[i]!=HVDArray::in?"* ":" ")<<&name[1];
         break;
        case 'F':
         f<<"    float"<<(hvd.inout[i]!=HVDArray::in?"* ":" ")<<&name[1];
         break;
        case 'I':
         f<<"    int"<<(hvd.inout[i]!=HVDArray::in?"* ":" ")<<&name[1];
         break;
        case 'U':
         f<<"    unsigned"<<(hvd.inout[i]!=HVDArray::in?"* ":" ")<<&name[1];
         break;
        case 'S':
         f<<"    short"<<(hvd.inout[i]!=HVDArray::in?"* ":" ")<<&name[1];
         break;
        case 'L':
         f<<"    long"<<(hvd.inout[i]!=HVDArray::in?"* ":" ")<<&name[1];
         break;
        default:
         {
          char errmsg[128];
          strcpy(errmsg,"PPC: Invalid variable ==> ");
          strcat(errmsg,hvd[i]);
          Error(errmsg);
         }
        }
        if(hvd[i+1])f<<",";
        f<<endl;

        ++i;
       }
       f<<")";
      }

      void BindHostVarList(HVDArray& hvd, fstream& f)
      {

       int i=0;
       while(hvd[i]){

        char name[64];
        char type;
        sword size=0;
        char *c=name,*c1=hvd[i];

        while(*c1!=' ')
         *c++=*c1++;
        *c='\0';
        while(*c1==' ')
         ++c1;
        type=toupper(*c1);
        if(type=='C'){
         char tmp[32];
         char *t=tmp;
         while(*c1!='[')
          ++c1;
         ++c1;
         while(*c1!=']')
          *t++=*c1++;
         *t='\0';
         size=atoi(tmp);
        }

        switch(type){
        case 'C':
         f<<" hotcur.bind_cstring(\""<<name<<"\","<<&name[1]<<","<<size<<");"<<endl;
         break;
        case 'D':
         f<<" hotcur.bind_double(\""<<name<<"\","<<(hvd.inout[i]==HVDArray::in?"&":"")
          <<&name[1]<<","<<size<<");"<<endl;
         break;
        case 'F':
         f<<" hotcur.bind_float(\""<<name<<"\","<<(hvd.inout[i]==HVDArray::in?"&":"")
          <<&name[1]<<");"<<endl;
         break;
        case 'I':
         f<<" hotcur.bind_int(\""<<name<<"\","<<(hvd.inout[i]==HVDArray::in?"&":"")
          <<&name[1]<<");"<<endl;
         break;
        case 'U':
         f<<" hotcur.bind_unsigned(\""<<name<<"\","<<(hvd.inout[i]==HVDArray::in?"&":"")
          <<&name[1]<<");"<<endl;
         break;
        case 'S':
         f<<" hotcur.bind_short(\""<<name<<"\","<<(hvd.inout[i]==HVDArray::in?"&":"")
          <<&name[1]<<");"<<endl;
         break;
        case 'L':
         f<<" hotcur.bind_long_int(\""<<name<<"\","<<(hvd.inout[i]==HVDArray::in?"&":"")
          <<&name[1]<<");"<<endl;
         break;
        default:
         {
          char errmsg[128];
          strcpy(errmsg,"PPC: Invalid variable ==> ");
          strcat(errmsg,hvd[i]);
          Error(errmsg);
         }
        }
        ++i;
       }
      }

      void CreateDefines(char* Name,int ArrSize,char* Stm)
      {
       char* c=Stm;
       hf1<<"#define "<<Name<<"_stm \" \\"<<endl;
       while(*c){
        if(*c=='\n'&&c[1]!=0)
         hf1<<" \\"<<endl;
        else if(*c!='\n')
         hf1<<*c;
        ++c;
       }
       hf1<<"\""<<endl;
       hf1<<"#define "<<Name<<"_size "<<ArrSize<<endl<<endl;
      }

      static char stm2[16000];

      void GetStm(int select_stm_flag,int plsql_flag=0,int StrTypeDir=0)
      {
       char* c=buf;
       char* n=stmName;
       char tmp[64];
       char* c1=tmp;

       while(*c!='<'&&*c!=0)
        ++c;
       if(*c==0)Error("PPC: < is missing");
       ++c;
       *n=0;
       while(*c!=','&&*c!=0)
        *n++=*c++;
       if(*c==0)Error("PPC: , is missing");
       ++c;
       *n=0;
       while(*c!='>'&&*c!=0)
        *c1++=*c++;
       if(*c==0)Error("PPC: > is missing");
       *c1=0;
       stmArrSize=atoi(tmp);
       stm[0]=0;
       if(StrTypeDir){
        if(stmArrSize==1){
         strcpy(sttype,stmName);
         stflag=1;
        }else{
         strcpy(sttype,"");
         stflag=0;
        }
        return;
       }
       while(!inf.eof()&&strncmp("##",buf,2)!=0){
        GetLine();
        if(strncmp("##",buf,2)!=0){
         strcat(stm,buf);
         strcat(stm,"\n");
        }
       }
       if(inf.eof())Error("PPC: ## is missing, EOF encountered");

       if(hf1flag)
        CreateDefines(stmName,stmArrSize,stm);
       strcpy(stm2,stm);

       // outf<<"<"<<stmName<<","<<stmArrSize<<">"<<endl;
       outf<<"/* ===================== "<<stmName<<" ======================= */"<<endl;
       if(cmflag)
        cm<<"/* ===================== "<<stmName<<" ======================= */"<<endl;
       char stm_text[16000];
       int va_len=0;
       strcpy(stm_text,stm);
       HVDArray hvd(stm_text);
       while(hvd[va_len])++va_len;

       ca_len=0;
       for(int i=0;i<va_len;++i){
        //  outf<<"Var["<<i<<"]="<<hvd[i];
        int sz=DeclareHostVar(hvd[i],stmArrSize,stmName,0);
        //  outf<<", Sz="<<sz<<endl;
        if(sz>0)AddToCStrArr(sz);
       }
       if(va_len>0){
        if(ca_len>0)outf<<endl;
        for(int j=0;j<ca_len;++j){
         if(!stflag)
          outf<<"typedef char CSTR_"<<cstr_arr[j]<<"_"
      	<<stmName<<"["<<cstr_arr[j]<<"];"<<endl;
        }
        if(ca_len>0)outf<<endl;
        outf<<"EXEC SQL BEGIN DECLARE SECTION;"<<endl<<endl;
        for(int l=0;l<ca_len;++l){
         if(!stflag)
          outf<<"  EXEC SQL TYPE CSTR_"<<cstr_arr[l]<<"_"
      	<<stmName<<" IS STRING("<<cstr_arr[l]<<");"<<endl;
        }
        if(ca_len>0)outf<<endl;
        for(int k=0;k<va_len;++k){
         DeclareHostVar(hvd[k],select_stm_flag?1:stmArrSize,stmName,1);
         //   if(cmflag&&!plsql_flag&&!select_stm_flag)
         //    DeclareTempHostVar(hvd[k],select_stm_flag?1:stmArrSize,stmName);
        }
        outf<<"  int n_"<<stmName<<";"<<endl;
        outf<<endl<<"EXEC SQL END DECLARE SECTION;"<<endl<<endl;
       }
       CompressBlank(stm_text,stm);
       strcpy(stm1,stm);
       ReplaceVarName(stm,stm_text,stmName);
       strcpy(stm,stm_text);

       if(select_stm_flag){

        outf<<"EXEC SQL DECLARE "<<stmName<<"_cur CURSOR FOR"<<endl;
        outf<<stm<<endl;
        outf<<"  ;"<<endl<<endl;

        outf<<"long gl_rpc_save_"<<stmName<<";"<<endl;
        outf<<"long count_"<<stmName<<";"<<endl<<endl;
        OpenFuncPrototype(hvd,stmName,outf,"_open");
        outf<<endl;
        outf<<"{"<<endl;
        outf<<" count_"<<stmName<<"=0;"<<endl;
        AssignInputVar(hvd,stmName,1);
        outf<<" gl_rpc_save_"<<stmName<<"=sqlca.sqlerrd[2];"<<endl;
        outf<<" EXEC SQL OPEN "<<stmName<<"_cur;"<<endl;
        outf<<"}"<<endl<<endl;

        outf<<"void "<<stmName<<"_close(void)"<<endl;
        outf<<"{"<<endl;
        outf<<" EXEC SQL CLOSE "<<stmName<<"_cur;"<<endl;
        outf<<" sqlca.sqlerrd[2]=gl_rpc_save_"<<stmName<<";"<<endl;
        outf<<"}"<<endl<<endl;

        ReplaceEoln(stm,stm_text);
        strcpy(stm,stm_text);

        //  outf<<"Stm='"<<stm<<"'"<<endl;

        otl_select_stream selstr(stm,db,"");
        generate_select_list_struct(selstr,stmName);

        if(cmflag){
         cm<<"static otl_stream* str_"<<stmName<<";"<<endl<<endl;
         OpenFuncPrototype(hvd,stmName,cm,"_open");
         cm<<endl;
         cm<<"{"<<endl;
         cm<<" str_"<<stmName<<"=new otl_stream("<<endl;
         cm<<"    "<<stmArrSize<<","<<endl;
         PrintStm(stm2);
         cm<<"    *db"<<endl;
         cm<<"  );"<<endl;
         AssignStackInputVar(hvd,stmName);
         cm<<"}"<<endl<<endl;

         cm<<"void "<<stmName<<"_close(void)"<<endl;
         cm<<"{"<<endl;
         cm<<" delete str_"<<stmName<<";"<<endl;
         cm<<"}"<<endl<<endl;

         GetTempFuncBody(selstr,stmName);

        }

        OpenFuncPrototype(hvd,stmName,hf,"_open");
        hf<<";"<<endl;
        hf<<"void "<<stmName<<"_close(void);"<<endl;
        hf<<"int "<<stmName<<"_get("<<stmName<<"* out);"<<endl<<endl;

        if(stmArrSize==1)
         GetFuncBody1(selstr,stmName);
        else
         GetFuncN(selstr,stmName,stmArrSize);
        outf<<"EXEC SQL WHENEVER NOT FOUND CONTINUE;"<<endl<<endl;

       }else if(plsql_flag){

        ReplaceEoln(stm,stm_text);
        otl_cursor cur(db);
        cur.parse(stm_text);

        hf<<endl;
        ExecFuncPrototype(hvd,stmName,hf);
        hf<<";"<<endl<<endl;
        ExecFuncPrototype(hvd,stmName,outf);
        outf<<endl;
        outf<<"{"<<endl;
        AssignInputVar(hvd,stmName,1,1);
        outf<<" EXEC SQL EXECUTE\n   ";
        PrintfStmOffs(stm,"   ");
        outf<<" END-EXEC;"<<endl;
        AssignOutputVar(hvd,stmName);
        outf<<"}"<<endl<<endl;

        if(cmflag){
         ExecFuncPrototype(hvd,stmName,cm);
         cm<<endl;
         cm<<"{"<<endl;
         cm<<" if(!hotcur.connected)hotcur.open(*db);"<<endl;
         cm<<" hotcur.parse("<<endl;
         PrintStm(stm1,1);
         cm<<"  );"<<endl;
         BindHostVarList(hvd,cm);
         cm<<" hotcur.exec();"<<endl;
         cm<<"}"<<endl<<endl;

        }

       }else{

        ReplaceEoln(stm,stm_text);
        otl_cursor cur(db);
        cur.parse(stm_text);

        hf<<endl;
        hf<<"void "<<stmName<<"_open(int auto_commit);"<<endl;
        outf<<"int auto_"<<stmName<<";"<<endl<<endl;
        outf<<"void "<<stmName<<"_open(int auto_commit)"<<endl;
        outf<<"{"<<endl;
        outf<<" auto_"<<stmName<<"=auto_commit;"<<endl;
        outf<<" n_"<<stmName<<"=0;"<<endl;
        outf<<"}"<<endl<<endl;

        if(cmflag){
         cm<<"static otl_stream*  cur_"<<stmName<<";"<<endl<<endl;
         cm<<"void "<<stmName<<"_open(int auto_commit)"<<endl;
         cm<<"{"<<endl;
         cm<<" cur_"<<stmName<<"=new otl_stream("<<endl;
         cm<<"    "<<stmArrSize<<","<<endl;
         PrintStm(stm2);
         cm<<"    *db"<<endl;
         cm<<"  );"<<endl;
         cm<<" cur_"<<stmName<<"->set_commit(auto_commit);"<<endl;
         cm<<"}"<<endl<<endl;
        }

        OpenFuncPrototype(hvd,stmName,hf,"_put");
        hf<<";"<<endl;
        OpenFuncPrototype(hvd,stmName,outf,"_put");
        outf<<endl;
        outf<<"{"<<endl;
        if(stmArrSize>1)
         outf<<" ++n_"<<stmName<<";"<<endl;
        AssignInputVar(hvd,stmName,stmArrSize);
        if(stmArrSize==1){
         outf<< "  EXEC SQL\n   ";
         PrintfStmOffs(stm,"   ");
         outf<<"  ;"<<endl<<endl;
         outf<<" if(auto_"<<stmName<<") EXEC SQL COMMIT;"<<endl;
        }else{
         outf<<" if(n_"<<stmName<<"=="<<stmArrSize<<")"<<endl;
         outf<<"  "<<stmName<<"_flush();"<<endl;
         outf<<endl<<endl;
        }
        outf<<"}"<<endl<<endl;

        if(cmflag){
         outf<<endl;
         OpenFuncPrototype(hvd,stmName,cm,"_put");
         cm<<endl;
         cm<<"{"<<endl;
         for(	int k3=0;k3<va_len;++k3)
          WriteTempInputVar(hvd[k3],stmName);
         cm<<"}"<<endl<<endl;

        }

        if(cmflag){
         cm<<"void "<<stmName<<"_flush(void)"<<endl;
         cm<<"{"<<endl;
         cm<<" cur_"<<stmName<<"->flush();"<<endl;
         cm<<"}"<<endl<<endl;
        }

        if(cmflag){
         cm<<"void "<<stmName<<"_close(void)"<<endl;
         cm<<"{"<<endl;
         cm<<" delete cur_"<<stmName<<";"<<endl;
         cm<<"}"<<endl<<endl;
        }

        if(stmArrSize==1){

         hf<<"void "<<stmName<<"_flush(void);"<<endl;
         outf<<"void "<<stmName<<"_flush(void)"<<endl;
         outf<<"{"<<endl;
         outf<<" if(auto_"<<stmName<<") EXEC SQL COMMIT;"<<endl<<endl;
         outf<<"}"<<endl<<endl;

         hf<<"void "<<stmName<<"_close(void);"<<endl;
         outf<<"void "<<stmName<<"_close(void)"<<endl;
         outf<<"{"<<endl;
         outf<<" EXEC SQL COMMIT;"<<endl;
         outf<<"}"<<endl<<endl;

        }else{

         hf<<"void "<<stmName<<"_flush(void);"<<endl;
         outf<<"void "<<stmName<<"_flush(void)"<<endl;
         outf<<"{"<<endl;
         outf<<" if(n_"<<stmName<<">0){"<<endl<<endl;
         outf<<"  EXEC SQL FOR :n_"<<stmName<<"\n    ";
         PrintfStmOffs(stm,"    ");
         outf<<"   ;"<<endl<<endl;
         outf<<"  n_"<<stmName<<"=0;"<<endl;
         outf<<"  if(auto_"<<stmName<<") EXEC SQL COMMIT;"<<endl<<endl;
         outf<<" }"<<endl<<endl;
         outf<<"}"<<endl<<endl;

         hf<<"void "<<stmName<<"_close(void);"<<endl;
         outf<<"void "<<stmName<<"_close(void)"<<endl;
         outf<<"{"<<endl;
         outf<<" "<<stmName<<"_flush();"<<endl;
         outf<<"}"<<endl<<endl;

        }

       }

       outf<<"/* =================== End of "<<stmName<<" ===================== */"<<endl;
       if(cmflag)
        cm<<"/* =================== End of "<<stmName<<" ===================== */"<<endl;

      } /* GetStm */

      void SqlSelect(void)
      {
       ++select_count;
       GetStm(1);
      }

      void SqlOutStm(void)
      {
       GetStm(0);
      }

      void SqlPlSql(void)
      {
       GetStm(0,1);
      }

      void SqlStrType(void)
      {
       GetStm(0,0,1);
      }

      void ParseLine(void)
      {int flag=0;
       if(buf[0]!='#'){
        outf<<buf<<endl;
        if(cmflag)cm<<buf<<endl;
       }else{
        for(int i=0;i<dir_size;++i){
         int rc=strncmp(directive[i],buf,strlen(directive[i]));
         if(rc==0){
          //    cout<<"Buf='"<<buf<<"', RC="<<rc<<", Dir="<<i<<endl;
          switch(i){
          case 0:
           SqlInitMain();
           break;
          case 1:
           SqlInitModule();
           break;
          case 2:
           SqlSelect();
           break;
          case 3:
           SqlOutStm();
           break;
          case 4:
           SqlPlSql();
           break;
          case 5:
           SqlStrType();
           break;
          }
          flag=1;
          break;
         }
        }
        if(!flag){
         outf<<buf<<endl;
         if(cmflag)
          cm<<buf<<endl;
        }
       }
      } /* ParseLine */

      void Process(void)
      {
       while(!inf.eof()){
        GetLine();
        ParseLine();
       }
       if(cmflag)
        cm<<endl<<"}"<<endl;
      } /* Process */

      void HFileProlog(char* hdefine)
      {
       hf<<"#ifndef "<<hdefine<<endl;
       hf<<"#define "<<hdefine<<endl;
       hf<<endl;
       hf<<"#ifdef __cplusplus"<<endl;
       hf<<"extern \"C\"{"<<endl;
       hf<<"#endif"<<endl;
       hf<<"\n\n";
      } /* HFileProlog */

      void HFile1Prolog(char* hdefine)
      {
       hf1<<"#ifndef "<<hdefine<<"__DEFINE"<<endl;
       hf1<<"#define "<<hdefine<<"__DEFINE"<<endl;
       hf1<<endl;
       hf1<<"#ifdef __cplusplus"<<endl;
       hf1<<"extern \"C\"{"<<endl;
       hf1<<"#endif"<<endl;
       hf1<<"\n\n";
      } /* HFileProlog */

      void HFileEpilog(void)
      {
       hf<<endl;
       hf<<"#ifdef __cplusplus"<<endl;
       hf<<"}"<<endl;
       hf<<"#endif"<<endl;
       hf<<endl;
       hf<<"#endif"<<endl;
      } /* HFileEpilog */

      void HFile1Epilog(void)
      {
       hf1<<endl;
       hf1<<"#ifdef __cplusplus"<<endl;
       hf1<<"}"<<endl;
       hf1<<"#endif"<<endl;
       hf1<<endl;
       hf1<<"#endif"<<endl;
      } /* HFileEpilog */

      int main(int argc, char* argv[])
      {
       if((argc!=6&&argc!=7&&argc!=8)||strcmp(argv[1],"-h")==0){
        HelpMessage();
        return 1;
       }
       cout<<"Connect String="<<argv[1];
       cout<<", Input File="<<argv[2];
       cout<<", Output File="<<argv[3];
       cout<<", H-File="<<argv[4];
       cout<<", #define="<<argv[5]<<endl;

       try{
        db.rlogon(argv[1]);
        inf.open(argv[2],ios::in);
        if(!inf.good()){
         cerr<<"File not found: "<<argv[2]<<endl;
         db.logoff();
         return 1;
        }
        outf.open(argv[3],ios::out);
        if(!outf.good()){
         cerr<<"Cannot create file: "<<argv[3]<<endl;
         db.logoff();
         return 1;
        }
        hf.open(argv[4],ios::out);
        if(!hf.good()){
         cerr<<"Cannot create file: "<<argv[4]<<endl;
         db.logoff();
         return 1;
        }
        if(argc==7||argc==8){
         hf1flag=1;
         hf1.open(argv[6],ios::out);
         if(!hf1.good()){
          cerr<<"Cannot create file: "<<argv[6]<<endl;
          db.logoff();
          return 1;
         }
        }
        if(argc==8){
         cmflag=1;
         cm.open(argv[7],ios::out);
         if(!cm.good()){
          cerr<<"Cannot create file: "<<argv[7]<<endl;
          db.logoff();
          return 1;
         }
        }
        HFileProlog(argv[5]);
        if(hf1flag)HFile1Prolog(argv[5]);
        Process();
        HFileEpilog();
        if(hf1flag)HFile1Epilog();
        db.logoff();
        return 0;
       }
       catch(otl_exception& p){
        cerr<<endl<<"Line ("<<line<<"):"<<buf<<endl<<endl
            <<"  **** "<<p.msg<<endl;
       }
       db.logoff();
       return 1;
      } /* main */

  ## Appendix E. How to install the OTL library and Pro*OTL/Pre-Pro*C preprocessor

  - In order to install and use the OTL library, copy the content of Appendix C to the otl.h file. Put the file to a regular directory in which header files are located. That is all. No need to make object files or libraries.
    Besides, Oracle Call Interface standard header files are needed. In Unix, they are located in the $ORACLE_HOME/rdbms/demo directory. In Windows 95 or Windows NT, they could be found in one of the OCI directories. The following header files are needed:
    - ociapr.h
    - ocidfn.h
    - oratypes.h
  - In order to install Pro*OTL / Pre-Pro*C preprocessor (PPC), copy the content of Appendix D to the ppc.C or ppc.cpp file, depending on the C++ file suffix in your platform. Compile PPC and make an executable. Put the executable somewhere on $PATH.
    A few modifications in the OCI standard header files are necessary, to make PPC compile successfully. PPC uses C++ streams (iostream.h and fstream.h). Since the "text" symbol is defined in the C++ streams, the "typedef unsigned char text" symbol, defined in the oratypes.h file, needs to be redefined as "ora_text". The ociapr.h file contains OCI function prototypes and "text" is used in the prototypes. Replace "text" with "ora_text".

  ## Appendix F. Modules, generated by PPC for the example from Chapter 4.

  ### Pro*C module (ppc_test.pc)

      #include <ppc_test.h>
      #include <stdio.h>
      #include <string.h>
      EXEC SQL INCLUDE SQLCA;

      #define sql_code sqlca.sqlcode
      #define sql_rpc sqlca.sqlerrd[2]

      typedef char CSTR[128];

      EXEC SQL BEGIN DECLARE SECTION;
        EXEC SQL TYPE CSTR IS STRING(128);
      EXEC SQL END DECLARE SECTION;

      static void sqlerror(void)
      {
       EXEC SQL WHENEVER SQLERROR CONTINUE;
       fprintf(stderr,"\n%s\n",sqlca.sqlerrm.sqlerrmc);
       EXEC SQL ROLLBACK RELEASE;
       exit(1);
      }

      EXEC SQL WHENEVER SQLERROR DO sqlerror();

      /* ===================== Sel ======================= */
      EXEC SQL BEGIN DECLARE SECTION;

        int F_Sel;
        int n_Sel;

      EXEC SQL END DECLARE SECTION;

      EXEC SQL DECLARE Sel_cur CURSOR FOR
      SELECT
       *
      FROM
       TEST_TAB
      WHERE
       F1>=:F_Sel AND F1<=:F_Sel*2
      ORDER BY
       F1

        ;

      long gl_rpc_save_Sel;
      long count_Sel;

      void Sel_open(
          int F
      )
      {
       count_Sel=0;
       F_Sel=F;

       gl_rpc_save_Sel=sqlca.sqlerrd[2];
       EXEC SQL OPEN Sel_cur;
      }

      void Sel_close(void)
      {
       EXEC SQL CLOSE Sel_cur;
       sqlca.sqlerrd[2]=gl_rpc_save_Sel;
      }

      typedef char tCSTR_31_Sel[31];

      EXEC SQL BEGIN DECLARE SECTION;

        EXEC SQL TYPE tCSTR_31_Sel IS STRING(31);

        int row_fetch_count_Sel;
        int cur_row_pos_Sel;
        long rpc_save_Sel;
        int end_of_data_Sel;

        double F1_Sel[50];
        short F1_IND_Sel[50];

        tCSTR_31_Sel F2_Sel[50];
        short F2_IND_Sel[50];

      EXEC SQL END DECLARE SECTION;

      int Sel_get(Sel* out)
      {
       if(count_Sel==0){
        ++count_Sel;
        row_fetch_count_Sel=0;
        cur_row_pos_Sel=0;
        end_of_data_Sel=0;
       }
       do{

         if(row_fetch_count_Sel==0){
          if(end_of_data_Sel)return 1;
            rpc_save_Sel=sqlca.sqlerrd[2];
            EXEC SQL WHENEVER NOT FOUND goto Sel_breakloop;
            EXEC SQL FETCH Sel_cur INTO
              :F1_Sel:F1_IND_Sel,
              :F2_Sel:F2_IND_Sel;
            goto Sel_skip;

        Sel_breakloop:

            if(sqlca.sqlerrd[2]-rpc_save_Sel==0)return 1;
            end_of_data_Sel=1;

        Sel_skip:

            row_fetch_count_Sel=sqlca.sqlerrd[2]-rpc_save_Sel;
            cur_row_pos_Sel=0;
         }

         out->F1=F1_Sel[cur_row_pos_Sel];
         out->F1_IND=F1_IND_Sel[cur_row_pos_Sel];

         strcpy(out->F2,(char*)&F2_Sel[cur_row_pos_Sel]);
         out->F2_IND=F2_IND_Sel[cur_row_pos_Sel];

         ++cur_row_pos_Sel;
         --row_fetch_count_Sel;

       }while(0);

       return 0;

      }

      EXEC SQL WHENEVER NOT FOUND CONTINUE;

      /* =================== End of Sel ===================== */

      /* ===================== Ins ======================= */

      typedef char CSTR_31_Ins[31];

      EXEC SQL BEGIN DECLARE SECTION;

        EXEC SQL TYPE CSTR_31_Ins IS STRING(31);

        float F1_Ins[50];
        CSTR_31_Ins F2_Ins[50];
        int n_Ins;

      EXEC SQL END DECLARE SECTION;

      int auto_Ins;

      void Ins_open(int auto_commit)
      {
       auto_Ins=auto_commit;
       n_Ins=0;
      }

      void Ins_put(
          float F1,
          char* F2
      )
      {
       ++n_Ins;
       F1_Ins[n_Ins-1]=F1;
       strcpy((char*)&F2_Ins[n_Ins-1],F2);

       if(n_Ins==50)
        Ins_flush();

      }

      void Ins_flush(void)
      {
       if(n_Ins>0){

        EXEC SQL FOR :n_Ins
          INSERT INTO TEST_TAB
          (
           F1,
           F2
          )
           VALUES
          (
           :F1_Ins ,
           :F2_Ins
          )
         ;

        n_Ins=0;
        if(auto_Ins) EXEC SQL COMMIT;

       }

      }

      void Ins_close(void)
      {
       Ins_flush();
      }

      /* =================== End of Ins ===================== */

      /* ===================== PL ======================= */

      typedef char CSTR_31_PL[31];

      EXEC SQL BEGIN DECLARE SECTION;

        EXEC SQL TYPE CSTR_31_PL IS STRING(31);

        int A_PL;
        CSTR_31_PL B_PL;
        CSTR_31_PL C_PL;
        int n_PL;

      EXEC SQL END DECLARE SECTION;

      void PL_exec(
          int* A,
          char* B,
          char* C
      )
      {
       PL_A=*A;
       strcpy(C_PL,C);

       EXEC SQL EXECUTE
         BEGIN
          :A_PL := :A_PL+1;
          :B_PL := :C_PL ;
         END;
       END-EXEC;

       *A=A_PL;
       strcpy(B,B_PL);

      }

      /* =================== End of PL ===================== */

      #ifdef __cplusplus
      void assign_db(otl_connect* adb)
      {
       db=adb;
      }
      #endif

  ### C++ module (ppc_test.C)

      #include <ppc_test.h>
      #include <otl.h>
      static otl_connect* db;
      static otl_cursor hotcur;

      extern "C"{

      /* ===================== Sel ======================= */
      static otl_stream* str_Sel;

      void Sel_open(
          int F
      )
      {
       str_Sel=new otl_stream(
          50,

          "SELECT "
          " * "
          "FROM "
          " TEST_TAB "
          "WHERE "
          " F1>=:F<int> AND F1<=:F*2 "
          "ORDER BY "
          "  F1",

          *db
        );
       (*str_Sel)<<F;

      }

      void Sel_close(void)
      {
       delete str_Sel;
      }

      int Sel_get(Sel* out)
      {
       if(str_Sel->eof())return 1;
       (*str_Sel)>>out->F1;
       (*str_Sel)>>out->F2;

       out->F1_IND=str_Sel->is_null()?-1:0;
       out->F2_IND=str_Sel->is_null()?-1:0;

       return 0;
      }

      /* =================== End of Sel ===================== */

      /* ===================== Ins ======================= */
      static otl_stream*  cur_Ins;

      void Ins_open(int auto_commit)
      {
       cur_Ins=new otl_stream(
          50,

          "INSERT INTO TEST_TAB "
          "( "
          " F1, "
          " F2 "
          ") "
          " VALUES "
          "( "
          " :F1<float>, "
          " :F2<char[31]> "
          ")",

          *db
        );
       cur_Ins->set_commit(auto_commit);
      }

      void Ins_put(
          float F1,
          char* F2
      )
      {
       (*cur_Ins)<<F1;
       (*cur_Ins)<<F2;
      }

      void Ins_flush(void)
      {
       cur_Ins->flush();
      }

      void Ins_close(void)
      {
       delete cur_Ins;
      }

      /* =================== End of Ins ===================== */

      /* ===================== PL ======================= */
      void PL_exec(
          int* A,
          char* B,
          char* C
      )
      {
       if(!hotcur.connected)hotcur.open(*db);
       hotcur.parse(
          "BEGIN "
          " :A := :A+1; "
          " :B := :C ; "
          "END;"
        );
       hotcur.bind_int(":A",A);
       hotcur.bind_cstring(":B",B,31);
       hotcur.bind_cstring(":C",C,31);
       hotcur.exec();
      }

      /* =================== End of PL ===================== */

      #ifdef __cplusplus

      /*
        Function for C++. In the main C++ module, the user needs to call
        this function, in order to pass over a pointer to the actual
        database connect object into the C++ module, generated by PPC.

        This function can be eliminated if only Pro*C is used.
      */

      void assign_db(otl_connect* adb)
      {
       db=adb;
      }

      /*
        Function for C++. In the main C++ module, the user needs to call
        this function just before disconnecting from the database, in order
        to close the static "hot" cursor in this file.

        This function can be eliminated if only Pro*C is used.
      */
      void close_hotcur(void)
      {
       hotcur.close(); // close static hot cursor
      }

      #endif

      }

  ### Interface header file (ppc_test.h)

      #ifndef __PPC_TEST_H
      #define __PPC_TEST_H

      #ifdef __cplusplus
      extern "C"{
      #endif

      struct struct_Sel{

        double F1;
        short F1_IND;

        char F2[31];
        short F2_IND;

      };
      typedef struct struct_Sel Sel;

      void Sel_open(
          int F
      );
      void Sel_close(void);
      int Sel_get(Sel* out);

      void Ins_open(int auto_commit);
      void Ins_put(
          float F1,
          char* F2
      );
      void Ins_flush(void);
      void Ins_close(void);

      void PL_exec(
          int* A,
          char* B,
          char* C
      );

      #ifdef __cplusplus
      }
      #endif

      #endif

  ### Command line for the example from Chapter 4.

        ppc scott/tiger ppc_test.ppc ppc_test.pc ppc_test.h __PPC_TEST_H dummy.h ppc_test.C

  - scott/tiger -- argv[1], connect string
  - ppc_test.ppc -- argv[2], input directive file
  - ppc_test.pc -- argv[3], output Pro*C module
  - ppc_test.h -- argv[4], output interface header file
  - __PPC_TEST_H -- argv[5]. #define for the interface header file
  - dummy.h -- argv[6], dummy file (contains macro definitions to be used with the OTL streams)
  - ppc_test.C -- argv[7], output C++ module