Purpose

ORAC++ have been developed using:

ORAC++ is a shared library to be linked with Oracle client shared library (libclntsh.so - created at Oracle build time)
For installation details see 'readme.txt' file, included in 'orac8cpp_rhlib.tar.gz' library distribution file.
 
This document describes all features of ORAC++ and provides user with step by step method of writing C++ programs using ORAC++. For various examples of ORAC++ programs, see the documents (in Word format)ORAC++ Programming Examples. See also ORAC++ Additional Features for programming examples on accessing objects in tables, union, intersect, minus, outer-joins and Oracle hints.
 

ORAC++ Main Features
 

 
ORAC++ Classes at Glance

 

Class                        

Purpose

Connect

To connect to database - used  to create Database object

Database

To open database - used to create Table, View, Procedure, Function

Table

To store database table properties

View

To store database views properties

Object

To store embedded table object properties

RefObject

To store reference to an object

Select

To query on table, view or join

Insert

To insert rows into a table

Update

To update table

Delete

To delete rows from the table

Procedure

To process stored procedure

Function

To process stored function

Aggregate

To process aggregate data

TInt

To process integer (number, number(n)) data

TFloat

To process float (number((n,d)) data

TStr

To process string (varchar2(n)) data

TDate

To process date column

TClob

To process CLOB column

TBlob

To process BLOB column        

Union

To process  SQL union statement

Minus

To process  SQL minus statement

Intersect

To process  SQL intersect statement

TRowid

To process 'rowid' pseudo-column

Sql

To process user entered sql statement

Mem

To load selected rows into memory

OException

To report errors

 
Generic pointer wrapper class Ptr<class T> is also provided.
This class can be used in place of all pointer to classes as suited. For example:

    Table* p;
    Ptr<Table> pt = p;
    p = &pt;

Later on in the program pt can be used instead of p.
Alternatively, Ptr class can be used in objects construction:

  Ptr<Table> pt (GetATable("products"));

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 
C++ Program Using ORAC++
 
Each C++ program using ORAC++ must include following steps:

Here is a brief description of each step ( as an example is used a simple one table query )
To Open Database a valid username and password has to be provided.
Create default object of Class Connect, then use OpenDatabase method from this class to create Database class object. Example:
 
        Connect c;
        Database d = c.OpenDatabase(user, password);
       
To Define Table a valid table name has to be provided. Use GetTable method from class Database to create Table object. This is a persistent object, which means that it corresponds to real object existing in the database. Example:
 
        Table tb = d.GetTable(customers);
 
To Define Operation 

-         in case of a Table : Select, Update, Insert, Delete must be specified
-         in case of a Stored Procedure only operation is Execute.
 
In this step operationalobject is created from class Select/Insert/Update/Dalete Class Select constructor has as argument persistent database object: tb.
Example:
 
        Select sel(tb);
 
To Define Input/Output variables overloaded operator <<for selected columns, and operator >>for wherecolumns are used. Example for class Select:
 
        sel << tb["cust_id"] << tb["name"] << tb["address"];         // (1)
        sel >> tb["cust_id"] ==  32181;                                     // (2)
 
Selected or 'where' columns are specified by entering column names within the bracket[]. At this step all columns are validated.

Please note, that in the example above Select object operates on only one table, but in fact, Select can be used on number of joined tables and columns can belong to any table from the join. Line (1) is optional, if omitted all columns are fetched from the table (in case when Select object operates on more than one table and there is no line of type (1) , an error message is displayed). If there is no line (2) , all rows from the table(s) are fetched (no where condition).
 
Operation is performed at this step. In case of a Table the only operation is a Row method. At this step actual fetch from the table is performed. Function Row() returns 0 when query is exhausted. After each fetch, data can be loaded into output variables using operator <<. (variable types must correspond to Select column types, otherwise error is reported).
Example for class Select:
 
        while (sel.Row()) {
                sel << cust_id << name << address;                        // load output variables        
                cout << cust_id << name << address << endl;        
        }
 
Types of output variables must correspond to types (and sizes) of Select columns, otherwise error message is displayed.
 
To commit changes made to database call function Commit():
 
        d.Commit();
 
To rollback changes made to database call function Rollback() (called by default):
 
        d.Rollback();
 
To Close Database a Close method is used (automatically called by Database destructor). Example:
 
        d.Close();
 
Program code from example above corresponds to a following SQL statement:
 
        select cust_id, name, address
        from customers
        where cust_id = 32181;
 
Below is a listing of complete C++ program using ORAC++:
 
        #include <orac_lib.h>
        int  main(void)
        {
                Connect c;
                try  {
                        Database d = c.OpenDatabase("user", "password");
                        Table tb = d.GetTable("customers");
                        Select sel(tb);
                        sel << tb["cust_id"] << tb["name"] << tb["address"];        
                        sel >> tb["cust_id"] == 32181;                        
                        while (sel.Row())   {
                                sel << cust_id << name << address;
                                cout << cust_id << name << address << endl;
                        }
                }
                catch (OException& e)  {
                        cerr  << e << endl;
                }
                catch (...)  {
                        cerr  <<  "Error ..." << endl;
                }
                return 0;
        }
 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 
Selecting Records 
Class Select is used to retrieve records from the one or more tables (or views).
To use this class, a database object must be created:
 
        Connect c;
        Database d = c.OpenDatabase("user", "password");
        Table t = d.GetTable("customers");
 
In this case, 't' is object from class Table. This is a persistent (database) object.
In order to perform select operation on this object a Select object must be created:
 
        Select s(t);
 
Once Select object has been successfully created, it can be used to query a database object. A simplest possible way is to query as follows:
 
        while (s.Row())        
                cout << s;        // display formatted lines (defaults to Line format)
 
Line format, used by default in example above, is identical to format in SQL*Plus query if statement is select * from table_name. Through this manual this format is called a horizontal.
In order to display all ( or selected ) column names along with the data a class Record has to be created ( based on Select ).
 
As the result of calling a AsRecord() function, display will have the following format:      column_name : column_data. This format is called a vertical through this manual.
 
        s.AsRecord();
        while (s.Row())        
                cout << s;                 // display formatted lines ( Record  format)
 
If AsLine() function (invoked by default) is called, format will change to line 'horizontal' (like in SQL*Plus).
 

        s.Array(10); // set array fetch size to 10 rows

        s.AsLine(HEADER_OFF);

        while (s.Row())        
                cout << s;                 // display formatted record - Line format (10 records at the time)
 
In examples above, all columns from the table were displayed (no selection has been made). In order to select columns to be fetched, an operator << has to be used:
 
        s << tb["cust_id"] << tb["name"] << tb["address"];        
 
Once columns are selected, any of the methods involving either implicit or explicit Line class (or explicit Record class ) can be used to display fetched data. Data also can be loaded to external variables which correspond to column type.
 
To use a where clause, following line must be inserted before Row() function:
 
        s >> t["prod_id"] == 100230;
 
This will create a where statement: where products.prod_id = 100230.
So, finally, a following program code ( code within try block ):
 
        Database = c.OpenDatabase(scott, tiger);
        Table t = d.GetTable("products");
        Select s(t);                                                // select * from products
        s >> t["prod_id"] == 100001;                   // where prod_id = 100001
        s.AsRecord();                                           // explicit class Record
        while (s.Row())        
                cout << s;                                         // output as pre-formatted Record
 
will produce output:
 
                          PROD_ID : 100001
                    PROD_DESC : BOOKSHELF
                    PROD_PRICE : 108.5
                     PROD_TYPE : 3
 
If we use Line class instead of a Record, all data will be displayed horizontally. So, the following program code (function used):
 
        s.AsLine();                                 // defaults to Line use only if previously set to Record
        while (s.Row())  
                cout << s;
 
will create output as follows:
 
PROD_ID    PROD_DESC         PROD_PRICE             PROD_TYPE
 
100001     BOOKSHELF                     108.5                               3

 If  we want to load fetched data into output variables, an operator <<must be used. Example below illustrates this point:
 
        int id;
        char *desc = new char[30];
        Select s(t);                                                     // select * from products
        s >> t["prod_id"] == 100001;                             // where prod_id = 100001
        while (s.Row())  {
                s << id << desc;
                cout << "ID = " << id << endl;
                cout << "DESC = " << desc << endl;
        }
 
In this case program output will be:
 
      ID = 100001
      DESC = BOOKSHELF
 
Order of loading data into variables is always important and must follow table description (types and sizes must correspond). In the example above, even though all columns from the table were fetched, we loaded only first two of them into output variables. In most situations, we need to select only specified columns from the table(s) or view. In this case, we must use operator << along with column names in the following way:
 
        int id;
        char desc[30];
        Select s(t);
        s << t["prod_desc"] << t["prod_id"];
        s >> t["prod_id"] == 10001;
        while (s.Row())  {
                s << desc << id;
                cout << "ID = " << id << endl;
                cout << "DESC = " << desc << endl;
        }
 
Now, we are fetching only two columns and output variables will be loaded using the same order as used in the operator <<(types and sizes of columns and variables must match).
 
In examples above, Select operated on one Table object. In fact, we can have any number of joined tables used by Select object.  Lets take look at the example below. In this case Select object operates on three joined tables. Operator << specifies selected columns, and operator >> specifies query columns and variables (all join conditions are also specified at this line).  
 
Program A (only code within try block is shown)
 
        Connect c;
        Database d = c.OpenDatabase("demo", "demo");
        Table tb = d.GetTable("products");
        Table tc = d.GetTable("orders");
        Table td = d.GetTable("product_types");

        Select sel(tb);                        // define join of 3 tables
        sel +=tc;
        sel += td;
        sel << tb["prod_desc"] << tb["prod_price"]
                        << tc["ord_id"] << tc["ord_date"]
                        << tc["ord_qty"] << td["type_desc"];
        sel >> tb["prod_id"] == tc["prod_id"];
        sel >> and_ >> td["type_id"] == tb["prod_type"];
        sel >> and_ >> tb["prod_desc"] == "BREAD";
        sel >> and_ >> tc["ord_qty"] == 20;
        sel.AsRecord();
        while(sel.Row())
                cout << sel;

 
 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

A SQL statement to create database view, which corresponds to a join used by the program will be:
 
        create view view_orders as
        select products.prod_desc,
               products.prod_price,
                       orders.ord_id,
               orders.ord_date,
               orders.ord_qty,
               product_types.type_desc
        from products, orders, product_types
        where products.prod_id = orders.prod_id
        and product_types.type_id = products.prod_type;
 
After creating above view in the database, we can use a following C++ program to fetch the data:
Program B (only code within try block is shown)
 
        Connect c;
        Database d = c.OpenDatabase("demo", "demo");
        View v = d.GetView("view_orders");        // use view
        Select sel(v);                                         // select based on view
        sel >> v["prod_desc"] == "BREAD";
        sel >> and_ >> v["ord_qty"] == 20;
        sel.AsRecord();
        while(sel.Row())
                cout << sel;
 
Outputs from programs (A) and (B) are identical :
 
                     PROD_DESC : BREAD
                    PROD_PRICE : 1.35
                            ORD_ID : 20003214
                      ORD_DATE : 13-MAR-00
                       ORD_QTY : 20
                     TYPE_DESC : D
 
It is possible to use formats on selected and query columns. The format must be entered after column name using convention : column_name(format):
 
        tb["prod_id('000999')"]                                                // column type number(n)
        tb["prod_price('99999.99')"]                                        // column type number(n,p)
        tc["ord_date('DD/MM/YY HH24:MI:SS')"]                     // column type date
       
Only use of the format  in setting a query condition  (operator >>) is for the date column of type date. Please note, that all format strings correspond to formats as defined in Oracle
SQL *Plus. In case when formats are used on numeric columns, retrieved column type is set to be of  VARCHAR2 type, so output variable must be of type char[], otherwise error is reported.
For detailed explanation of using column of type date see:  Accessing Columns of Type Date.
 
In case of a table column of type VARCHAR2, size of a column can be set using convention: column_name(size). For VARCHAR2 columns size will override actual size of the column (can be only less or equal to actual size), and is used to limit size of fetched data:
 
        td["varchar2_column('30')"];        // set maximum size of fetched data
 
There are cases, when two fetches (using two Select class objects) must be done within the same loop. This is called a sub-query in SQL terms. To perform a sub-query in C++ program using ORAC++, placeholders must be specified.
 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 
Example below shows a program to retrieve all products for a specified product type. Select class object 'sel' operates on table product_types(outer loop), while Select class object 'sel1' operates on table products(inner loop). At the time when query columns are defined for object sel1 'product type' is unknown, so a placeholder ':' is used instead (1). After each fetch of outer (sel) loop, retrieved prod_type (type_id) must be substituted for a placeholder (2). It can be any number of placeholders specified in the outer loop, and both outer and inner loop can be used by Select class objects operated on joined tables or views. Example below is showing a C++ program to perform a sub-query as described above (only code within try block shown):
 
        Connect c;
        int id, proc_type = 0, type_id, cnt = 0;
        char name[30], desc[30], buff[10];
        float price;
        Database d = c.OpenDatabase("demo", "demo");
        Table tb = d.GetTable("product_types");
        Select sel(tb);
        sel << tb["type_id"] << tb["type_desc"];
        Table tc = d.GetTable("products");
        Select sel1(tc);
        sel1 << tc["prod_id"] << tc["prod_desc"] << tc["prod_type"];
        sel1 >> tc["prod_type"] == ":";                         // (1) placeholder here
        while(sel.Row())        {
                sel << type_id << desc;
                cout << " (outer)--- " << type_id << ":" << desc << endl;
                sel1 >> type_id;                                   // (2) substitute it by type_id
 
                while (sel1.Row())  {
                        sel1 << id << name << proc_type;
                        cout << "         (inner)--- " << id << ":"
                                 << name << ":" << proc_type << endl;
                }
        }
       
Data can be fetched using arrays instead of scalar variables. To simplify input/output also special variable classes can be used. There are following classes to store and manipulate column data:
 
        TInt                 for integer columns ( database number(n) type )
        TFloat             for float columns ( database number(n,d) type )
        TStr                for string columns ( database varchar2(n) type )
        TDate             for date columns ( database date type )
        TClob             for CLOB columns ( database CLOB type )
        TBlob             for BLOB columns ( database BLOB type )
        TRowid           for retrieving 'rowid' from the table ( database rowid type )
 
For examples of C++ program using various cases of array fetch and TInt, TFloat, TStr classes using class Select, please read ORAC++ Programs Examples.
 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 
TRowid class is designated to hold an Oracle 'rowid' value for each row. Objects of this class can be used as a pseudo-column in selected or query columns. Following, is an example of C++ program to fetch rowidfrom the table. TRowid is then used to fetch the data. Note, that class TRowid object 'r' is used as a column name (without quotes) when selecting columns (only code within try block is presented):
 
int main()
{
        int id, msg_size;
        TRowid r;
        Connect c;
        Database d = c.OpenDatabase("demo", "demo");
        Table tc = d.GetTable("table_test");
        Select sel1(tc);
        sel1 << tc[r] << tc["id"] << tc["msg_size"];
        if (sel1.Row())   {         // (1) fetch first row only
                sel1 << r << id << msg_size;         // fetch rowid into r
                cout << r << ":" << id << ":" << msg_size << endl;

                  sel1 >> tc[r] == r;                            // (2) set query using rowid
                  while(sel1.Row())   {
                          sel1 << r << id << msg_size;     // selected columns the same
                          cout << r << ":" << id << ":" << msg_size << endl;
                  }
        }
        return 0;
}
 
In the program shown, TRowid retrieved from (1) is used in setting query for loop (2).
 
 
 
 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 
 
 
 
 
 
 
Inserting Records
To insert rows into the table Insert class object must be created. Insert class constructor has as an argument a Table object. So, to insert rows into a table, we must start with the following statements:
 
        Connect c;
        Database d = c.OpenDatabase("user", "password");
        Table t = d.GetTable("products");
 
then create Insert object:
 
        Insert ins(t);
 
after that, specify columns to be inserted, using operator <<:
 
        ins << t["prod_id"] << t["prod_desc"] << t["prod_type"];
 
and, finally values to be inserted, using operator >>;
 
        ins >> 100010 >> "TV STAND" >> 2;  // (1)
 
At this point, a row can be inserted using a Row method:
 
        num_inserted = ins.Row();
 
where variable 'num_inserted' holds number of rows which were inserted (in this case 1).
To insert more than one row, specify size of array to be inserted, using function Array:
 
        ins.Array(10);                // to insert array of 10 records
 
In this case, values will be inserted from the arrays:
 
        int prod_id[10];
        char *prod_desc[10];
        int prod_type[10];
 
After populating arrays, data from the arrays must be  loaded using following lines:
 
        ins << t["prod_id"] << t["prod_desc"] << t["prod_type"];
 
        ins >> prod_id >> prod_desc >> prod_type;        // arrays are used
        ins.Array(10);
        num_inserted = ins.Row();
 
num_inserted will have a value 10 (upon successful insertion).
 
For examples of C++ programs to insert records see document ORAC++ - Programming Examples.
 
 
 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 
 
 
 
 
Updating Records
To update a table Update class object must be created. Update class constructor has as an argument a Table object ( like in case of class Insert ). To update a table, we enter:

        Connect c;
        Database d = c.OpenDatabase("user", "password");
        Table t = d.GetTable("products");
 
then create an Update object:
 
        Update upd(t);
 
and specify columns to be updated, using operator <<:
 
        upd << t["prod_desc"] << t["prod_type"];
 
then, update values have to be inserted using operator >>:
 
        upd >> "PRINTER STAND" >> 2;
 
and query condition (if necessary) also using operator >>:
 
        upd >> t["prod_id"] == 100010;
 
To update a table, a function Row must be used:
 
        num_updated = upd.Row();
 
where variable 'num_updated' will store number of rows updated (in this case 1). A complete C++ program ( only code within try block is shown ) to update a table, discussed above, will be:
 
        int num_updated;
        Database d = c.OpenDatabase("user", "password");
        Table t = d.GetTable("products");
        Update upd(t);
        upd << t["prod_desc"] << t["prod_type"];        
        upd >> "PRINTER STAND" >> 2;
        upd >> t["prod_id"] == 100010;
        num_updated = upd.Row();
        cout << num_updated << " rows updated" << endl;
 
 
 
 

 

 

 

 

 

 

 

 

 

 

 

 

 

 
 
 
 
Deleting Records
To delete rows from the table Delete class object must be created. Delete class constructor has as an argument a Table object. To delete rows from a table, we must start with the following statements:
 
        Connect c;
        Database d = c.OpenDatabase("user", "password");
        Table t = d.GetTable("products");
 
then create a Delete object:
 
        Delete del(t);
 
and specify a where clause (if necessary) using operator >>:
 
        del  >> t["prod_id"] == 100010;
 
To delete rows from a table, function Row must be used:
 
        num_deleted = upd.Row();
 
where  variable num_deleted will store number of rows deleted (in this case 1). A complete C++ program ( code within try block only ) to delete rows from a table, discussed above, will be:
 
        Database d = c.OpenDatabase("user", "password");
        Table t = d.GetTable("products");
 
        Delete del(t);
        del  >> t["prod_id"] == 100010;
        cout << del.Row()  << " rows_deleted" << endl;
 
 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 
Accessing Stored Procedure
To access a database stored procedure Procedure class object must be created. Procedure class object is both persistentand operationalobject, since stored procedure exists in the database. Procedure class object is created, like in case of a Table directly from Database class. Following lines of code are needed to create a Procedure object:
 
        Connect c;
        Database d = c.OpenDatabase("user", "password");
        Procedure p = d.GetProcedure("p_get_prod_name");
 
Where 'p_get_prod_name' is a name of stored procedure. At object creation step, validation is performed (like in case of a Table) and necessary object attributes are retrieved. When Table object was created, attributes were Table columns, in this case attributes are Procedure arguments.
 
After a valid Procedure object is created, all input/output arguments (if any) have to be filled in:
 
        p >> id >> name;
 
A listing of the stored procedure p_get_prod_namecan be found after C++ program listing. In this case, first argument of the procedure (type - input only) is a prod_idstored in program variable id. A second argument (type - output only) is a product name prod_descto be loaded  into a program variable name.
 
Final step is to execute a stored procedure, by using Execute function from class Procedure:
 
        p.Execute();
        p << id << name;                // load output variables
        cout << " prod  id  :" << id << endl;
        cout << " prod  desc:" << name << endl;
 
After execute step, all data retrieved by the procedure have to be loaded into a C++ program variables (even if variable is of a type input only it has to be entered in output operator <<). Please note, that variable names and types must follow the same order as was used in input operator >>(which corresponds to stored procedure argument types/names). Below is a full listing of a program we just discussed:
 
int main()
{
        int id = 100001;
        char *name = new char[30];
        Connect c;
        try  {
                Database d = c.OpenDatabase("demo", "demo");
                Procedure p = d.GetProcedure("p_get_prod_name");
                p >> id >> name;                // load input/output arguments
                p.Execute();
                p << id << name;                // load output variables
                cout << "product id   :" << id << endl;
                cout << "product desc :" << name << endl;
        }
        catch (OException& e)  {
                cerr << e << endl;
        }
        catch ( ... )  {
                cerr  <<  "Error ..." << endl;
        }
}
 
And Oracle stored procedure 'p_get_prod_name' listing is as follows:
 
create or replace procedure p_get_prod_name (
   p_id in number,
   p_desc out varchar2)
is
  cursor c(pid in number) is
  select prod_desc
  from products
  where prod_id = pid;
begin
  open c(p_id);
  fetch c into p_desc;
  if c%notfound then
        p_name := not found;
  end if;
  close c;
end;
 
 
 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 
 
 
Accessing Stored Function
To access a database stored function Function class object must be created. Function class is similar to a Procedure class and it corresponds to an object existing in the database. Following lines of code are needed to create a Function class object:
 
        Connect c;
        Database d = c.OpenDatabase("user", "password");
        Function f = d.GetFunction("f_get_prod_name");
 
Where f_get_prod_nameis a name of stored function. Difference between Function and a Procedure class is that function always returns a value (int, char*, float). Passing arguments to Function is identical to passing arguments to a Procedure, but should be remembered that first argument in a Function is always a value which function returns:
 
        f >> rtn >> id >> name;
 
Above line has an additional variable rtn( function return value ) at first position. Below is a full listing of a C++ program using stored function:
 
        int main()
        {
                int rtn, id = 100001;
                char *name = new char[30];
                Connect c;
                try  {
                        Database d = c.OpenDatabase("demo", "demo");
                        Function f = d.GetFunction("f_get_prod_name");
                        f >> rtn >> id >> name;         //load input/output arguments
                        f.Execute();
                        f << rtn << id << name;        // reload input/output arguments
                        cout << "product type   :" << rtn << endl;
                        cout << "product id     :" << id << endl;
                        cout << "product desc   :" << name << endl;
                }
                catch (OException& e)  {
                        cerr << e << endl;
                }
                catch (...)  {
                        cerr << "Error ..." << endl;
                }
                return 0;
        }
 
And Oracle stored function 'f_get_prod_name':
 
create or replace function f_get_prod_name (
   p_id in number,
   p_name out varchar2)
return number
is
  cursor c(pid in number) is
  select prod_type, prod_desc
  from products
  where prod_id = pid;
  v_rtn number;
begin
  open c(p_id);
  fetch c into v_rtn, p_name;
  if c%notfound then
        v_rtn := 0;
  end if;
  close c;
  return v_rtn;
end;
 
In this case, a return value can be used as indicator of a successful fetch operation (even if fetch fails because no records found, function or procedure is successfully executed). For examples of programs accessing stored procedures and functions see ORAC++ Programming Examples.
 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 
Classes Representing Various Column Types
A several classes to manipulate data stored in various column types were created. Classes TRowid, TDate were already described. Remaining classes are:
 
        TInt for integer columns ( for Oracle number(n) type)
        TFloat for float columns ( for Oracle number(n,p) type)
        TStr for character columns ( for Oracle varchar2(n) type)
 
To create objects from each class one may use, for example:
 
        TInt iv;
        TFloat fv;
        TStr cv;
        TStr cv1(30);        // set size to 30
 
Each object from classes above holds data from the column and nullindicator. Classes can be used to select, insert and update records. Output operator <<for each class will create by default <NULL>string in case when nullindicator is set. There are two basic i/o operators used by these classes:

  int i; double d; char c[30];
        iv << i; fv << d; cv << c;                              // return value (int, float, char*)
        iv = i; fv = d; cv = c;           // set value (int, float, char *) (cna be used in insert/update/query)
       iv = "null" fv = "null"; cv = "null";     // set null value (can be used in insert/update/query)

Usually, above functions are not directly used, as they are called by overloaded operators << and >>.  These classes can also be used for array fetch by specifying array of objects:
 
        TInt iarr[10];
        TFloat farr[10];
        TStr charr[10];
 
In case, when there is a need to set size of variable TStr used in array, a function SetSize(size) can be called:
 
        TStr charr[10];                        // no size specified
        for (int i = 1; i < 10; i++) charr[i].SetSize(30);
 
 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 
Accessing Columns of Type Date
There are two ways to retrieve, insert or update date column from an Oracle table:
 
-        access date column using a string ( char* ) variable
-        use a special class TDate to access date column
 
In first case, an optional format can be specified with column name. Oracle column of type date, by default returns string of a format: DD-MON-YY where DD is a day, MON stands for the first three characters of a month and YY year within a century. Lets take a look at the following C++ program example (only code within try block is shown):
 
        int id;
        char date[30];
        Connect c;
        Database d = c.OpenDatabase("demo", "demo");
        Table t = d.GetTable("orders");
        Select sel(t);
        sel << t["ord_id"] << t["ord_date"];   // date fetched as string
        sel >> t["ord_id"] == 12130; // set query condition
        while(sel.Row())  {
                memset(date,'\0' , sizeof(date));
                sel << id << date;
        }
 
when run against test database, retrieved variable datewill have a value : 10-FEB-00. Any valid Oracle SQL*Plus format can be specified, following column name. For example, format:
 
        t["ord_date('DD-MM-YYYY HH24:MI:SS')"]
 
will result in output variable datehaving value : 10-02-2000 15:35:58(where 15 is an hour, 35 minutes and 58 seconds)
 
Query criteria for a date columns can also be set in whereclause. If  format is not entered, Oracle default date format will apply DD-MON-YY. In this case, string is set to 12-JUL-00.
 
        sel >> t["ord_date"] == "12-JUL-00";   // (*)
 
A fetch will return all rows, with column ord_date having day, month and year as specified.
Each date column always holds time data: hours,  minutes and seconds, but when query criteria are set as shown in line (*), time information is ignored, and fetch will retrieve all records matching day, month and the year (records can have different timedata). By providing a format, it is possible to include a time data for exact match:
 
        sel >> t["ord_date('DD-MM-YYYY HH24:MI:SS')"] == "12-JUL-2000 11:30:21"
 
In real situations, date will not be entered this way, but objects loaded from one fetch can be used as condition in second fetch:
 
        int id;
        char date[30];
        Connect c;
        Database d = c.OpenDatabase("demo", "demo");
        Table t = d.GetTable("orders");
        Select sel(t);
        sel << t["ord_id"] << t["ord_date('DD-MM-YYYY HH24:MI:SS')"]
        sel >> t["ord_id"] == 12130;
        while(sel.Row())  {                        // (1)
                memset(date,'\0' , sizeof(date));
                sel << id << date;
        }
 
        sel >> t["ord_date('DD-MM-YYYY HH24:MI:SS')"] == date;
        while(sel.Row())  {                        // (2)
                memset(date,'\0' , sizeof(date));
                sel << id << date;
        }
 
Assuming that we fetched only one row in loop (1), using fetched date/time as query condition in  loop (2) will result in the same output from both loops. In similar way, date strings can be used in insert and update classes (see ORAC++ Programming Examples).
 
To simplify and enhance date handing process a TDate class was created. TDate class can be used to query, update or insert column of type date. There are two constructors for this class:
 
        TDate dt1;
        TDate dt2("ord_date");
 
First constructor, is a default constructor, and it creates an object of datetype for later use. Object created in this way can be used in a following statement:
 
        sel << t["ord_id"] << t[dt1] << t["ord_date"];
 
Object dt1has a default output format already created and this format is passed to next column to be selected, in this case 'ord_date'. Default output format for class TDate is
DD-MON-YY.For example 10-APR-00. It is possible to set additional formats after TDate object was  created:
 
        - function ShowTime()  will set format to DD-MON-YYYY HH24:MI:SS

        - function ShowDefault()  will set format to DD-MON-YY (Oracle default)

  - function SetFormat(char *fmt) will set TDate format to fmt
 
Second type of TDate class constructor allows to specify column name at the time of object creation. This object is permanently bound to a table column and can be used in statements like this:
 
        sel << t["ord_id"] << t["dt2"];
 
In this case, there is no need to enter a column name, because it is already stored in object dt2.
To compare, object dt1can be placed several times in the operator <<line, always preceding a column of datetype, while object dt2will always refer to ord_date column. (it can be re-usedin a different table, having the same column name). To illustrate this point, a following C++ program can be presented (only code within try block is shown):
 
int main()
{
        Connect c;
        int  cnt = 0, id;
        char fmt_dt[30], std_dt[30];
        TDate dt1;                                         // use default output format DD-MON-YY
        TDate dt2("ord_date");
        dt1.SetFormat("DD/MM/YYYY");                                   // set output format to DD/MM/YYYYY
        Database d = c.OpenDatabase("demo", "demo");
        Table tc = d.GetTable("orders");
        Select sel1(tc);
        sel1 << tc["ord_id"]
                 << tc["ord_date"]                                                     // default string date
                 << tc["ord_date('YYYY/MM/DD::HH24/MI')"]       // formatted string date
                 << tc[dt1] << tc["ord_date"]                                   // format from dt1
                 << tc[dt2];                                                               // name/format from dt2
        sel1 >> tc[ord_id] == 20003210;                                     // query criteria
 
        while(sel1.Row()) {
                memset(std_dt,'\0 ',sizeof(std_dt));
                memset(fmt_dt, '\0',sizeof(fmt_dt));
                sel1 << id << fmt_dt << std_dt << dt1 << dt2;
                cout << id << " # " << fmt_dt << " # " << std_dt << " # "
                        << dt1 << " # " << dt2 << endl;
        }
        return 0;
}
 
The program will produce a following output:
 
20003210 # 10-MAR-00 # 2000/03/10::14/48 # 10/03/2000 # 10-MAR-00
 
TDate objects can be used also in setting query condition. Let's take a look at the following program:
 
int main()
{
        Connect c;
        int   id;
        TDate dt2("ord_date");                         // default output format is DD-MON-YY
        Database d = c.OpenDatabase("demo", "demo");
        Table tc = d.GetTable("orders");
        Select sel1(tc);
        sel1 << tc["ord_id"]
              << tc[dt2];                                       // name format from dt2
        sel1 >> tc[ord_id] == 20003210;          // query condition
        while(sel1.Row()) {
                sel1 << id << dt2;
                cout << id <<  # " << dt2 << endl;
        }

        sel >> tc[dt2] == dt2;                               // query condition: get column name and format from dt2
        while(sel1.Row())  
                sel1 << id << dt2;
                cout << id <<  # " << dt2 << endl;
        }
        return 0;
}
 
Program will produce following lines (default TDate format is used)

20003210 # 10/03/2000
20003210 # 10/03/2000
 
In this case records fetched have date/time value of ord_date column matching exactly data retrieved from first loop. Using this method, we can specify any relational operator >, <, >=, <=, <> to compare two dates in a where clause. For example:
 
        sel >> tc[dt2] >= dt2;
 
In preceding examples, we used a TDate object bound to a column. Not bound object also can be used in a where statement:

        sel >> tc["ord_date"] >> tc[dt1] >= dt2;
 
Please note, that in this case, tc[dt1] must be preceded by tc["ord_date"] (reversing order used in selecting columns - op <<).
 
In similar way TDate class can be used to insert and update database tables. Following example illustrates this point:
 
        TDate dt("id_date");                 // use named constructor
        dt.Current();                           // get current date/time
        Insert ins(tc);
        ins << tc[dt];                           // insert column name from dt
        ins >> dt;                                // insert value date/time from dt
 
In case of update the same rules apply, and column name and value can be retrieved from TDate object.
 Please note that TDate object can be set to null value (to be used in insert/update/query) by specifying:

  dt = "null";

in query clause null values can be used in the following way: sel >> t[dt] == dt; (is null), or sel >> t[dt] != dt; (is not null)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 
Accessing Columns of Type CLOB and BLOB
To access columns of type CLOB and BLOB two classes have been created: TClob and TBlob.
Constructors for these classes are:
 
        TClob tc_1;                                         TBlob tb_1;
        TClob tc_2(clob_column);                     TBlob tb_2(blob_column);
 
Like in case of  a  TDate class, first constructor, is a default constructor, and it creates an object of Tclob/TBlobtype for later use. Object created in this way can be used in a following statement:
 
        sel << t["id"] << t[tc_1] << t["clob_column"];
        sel << t["id"] << t[tb_1] << t["blob_column"];
 
Second type of constructors use column name therefore objects created this way can be used in statements like:
 
        sel << t["id"] << t[tc_2];
        sel << t["id"] << t[tb_2];
 
Object 'sel' in program lines above belongs to class Select, so above statements are used to load columns which have to be selected. After columns are selected, data is fetched using function:
 
        num_rows_fetched  = sel.Row();
 
Selected data has to be loaded to appropriate variables: first one of type integer and second TClob or TClob type. Assuming, that integer variable is 'int_var', and TClob variable is 'tc_1' program line will look like that:
 
        sel << int_var << tc_1;
 
Variable int_varis loaded by operator <<from the statement above, but only CLOB locator is loaded. To get data stored in CLOB variable  'tc_1' (from class TClob) has to be queried as follows:
 
        while (tc_1.Next())                // Next() returns 0 if no more data in LOB
                tc_1 << buff;
 
External variable 'buff' in this case of type char[size], is loaded from CLOB column in the table. Size of fetched chunkof data must be specified, by calling function:
 
        tc_1.SetFetchSize(size);
 
A C++ program below shows example of fetching data from column of type CLOB:
 
int main()
{
        int  int_var;
        char buff[30];
        TClob tc;                                         // not named TClob
        Connect c;

        Database d = c.OpenDatabase("demo", "demo");

        Table t = d.GetTable("test_table_lob");
        Select s(t);
        s << t["id"] << t[tc] << t["clob_column"];   // set CLOB name
        s >> tc["id"] == 1;                               // query : where id = 1
        tc.SetFetchSize(30);                           // set chunksize to be fetched
        while(sel1.Row())  {                             // loop for all fetched rows
                sel << int_var << tc;                   // load non-LOB fetched data
                while (tc.Next())                         // check if data left in LOB
                        tc << buff;                           // load output  variable buff
                        cout << buff << endl;           // and display
        }
        d.Close();
}
 
Following C++ program explains how to insert data into column of type CLOB or BLOB:
 
int main()
{
        int  int_var;
        char buff[] = "this is a testing insert of clob datatype";
        TClob tc("clob_column"); // named TClob object
        tc.SetFetchSize(strlen(buff));                           // set chunksize to be inserted

      Connect c;
        Database d = c.OpenDatabase("demo", "demo");
        Table t = d.GetTable("test_table_lob");
        Insert i(t);
        i << t["id"] << t[tc];                       // columns to be populated
        i  >> 1 >> tc_2;                           // LOB object is set to empty                
        i.Row();                                       // insert integer variable, LOB is empty
        for (n = 0; n<100; n++)
                tc >> buff;                           // load CLOB column from variable buff
        d.Commit();
        d.Close();
}
 
And finally, C++ program to update data in CLOB column:
 
int main()
{
        int  int_var;
        char buff[] = this is a testing update of clob datatype ;
        TClob tc("clob_column");                         // use TClob with column name
        tc.SetFetchSize(strlen(buff));                    // set chunksize to be inserted

  Connect c;
        Database d = c.OpenDatabase("demo", "demo");
        Table t = d.GetTable("test_table_lob");
        Update  u(t);
        u << t[tc];                                              // columns to be updated
        u  >> tc;                                                // updated  CLOB (empty value)
        u >> tc[id] == 1;                                    // query: where id = 1
        while(u.Row())          {                           // update non-LOBs, set LOBs to empty
                do  {
                        for (n = 0; n<100; n++)
                        tc >> buff;                           // load data into CLOB column
                }  while (upd.GetLobs());               // get LOBs for all updated records
        }
        d.Commit();
        d.Close();
}
 
When updating more than one row with LOB data using one Update object, after call to a Row() function, a doloop must be executed to update LOB values for all updated rows. Please  note that Row() function will update all non-LOB columns and set LOB columns to EMPTY_CLOB (BLOB).
 
Up to two CLOB columns and BLOB columns (maximum 4 LOB types) can be selected or inserted or updated in one operation (using one operational object: Select, Insert, Update).
If table contains more LOB type columns, they can be accessed with additional Select, Update, Insert objects:
 
        TClob1 = tc1("clob1");
        TClob2 = tc1("clob2");
        TBlob1 = tc1("blob1");
        TBlob2 = tc1("blob2");
        Table t = d.GetTable("table_2clob_2blob");
        Select sel(t);
        Insert ins(t);
        Update upd(t);
        sel << t[clob1] << t[clob2] << t[blob1] << t[blob2];
        ins << t[clob1] << t[clob2] << t[blob1] << t[blob2];
        upd << t[clob1] << t[clob2] << t[blob1] << t[blob2];
 
TClob (TBlob) objects must be separately fetched/populated by using TClob operators <</>>accordingly for each LOB object specified. For more examples of programs using LOBs see ORAC++ Programming Examples.
 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 
Connecting to Database
Connect and Database classes are used to establish connection to database. There are two basic types of connections : server connection and server-less connection. To use server connection, a listener service must be running on client machine. Server and database name is stored in TNSNAMES.ORA file, so for example, to connect to a local server with database 'data1' a following program line should be entered:
 
        Connect c("data1.localdomain");
 
To use default server and default database, specify:
 
        Connect c("");
 
To connect to database in case of server connection, enter one from the two lines:
 
        Database d = c.GetDatabase("demo", "demo");                // default database
        Database d = c.GetDatabase("demo", "demo","" );           // default database
 
Following line will cause error, since database name is defined at server connection
 
        Database d = c.GetDatabase("demo", "demo", "data1"); // database data1- error
 
To connect to local database, server connection is not needed and listener does not have to be running.

Again, there are two possibilities here : specify database name (data1) or use default connection ( ). Here are examples:
 
        Connect c;                        // no server connection
        Database d = c.GetDatabase("demo", "demo", "data1");  // database SID 'data1'
        Database d = c.GetDatabase("demo", "demo","" );          // default database
        Database d = c.GetDatabase("demo", "demo");              // default database
 
 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 
 
 
Resetting Objects for Reuse
To reuse operational objects (from class: Select, Insert, Update, Delete) a reset function is used. There are various types of reset command, which are applicable to specific classes.
Reset functions are:

 

Function

Description

Reset()

reset all selected and where columns and data or parameter list in procedures

ClearWhere()

remove where clause in sql statement

ClearColumns()

reset all select/update columns

Data()

reset all update(keep update columns) data and where clause in update

Array(num)

sets array processing/or resets to 1 row (Insert, Procedure, Function)

All resetting functions with corresponding classes are listed below:

 

Class

Functions Used

Select

Reset, ClearWhere, Columns

Update

Reset, ClearWhere, Data

Insert

Reset, ClearColumns, Array(num)

Delete

Reset, ClearWhere

Procedure

Array(num)

Function

Array(num)

Some resetting functions call others. Following table summarizes function's call.

 

Class        

Resetting Functions

Select

function Reset() calls ClearWhere() and ClearColumns()

Update

function Reset() calls ClearWhere(), ClearColumns() and Data()

Update

function ClearColumns() performs the same action as Reset()

Update

function Data() calls ClearWhere()

Insert

function ClearColumns() performs the same action as Reset()

Insert

 function Array(num) sets array size

 
Instead of resetting objects, a placeholders can be used in certain situations. In ORAC++ placeholders can be employed only for "where" statements ( in operators >>):
 
        sel >> t["id"] >> ":" >> t["prod_name"] >> ":";                // select 
        upd >> t["id"] >> ":" >> t["prod_name"] >> ":";               // update
        del >> t["id"] >> ":" >> t["prod_name"] >> ":";                // delete
 
Data, can be loaded using statements:
 
        sel >> 1 >> "computer";
        upd >> 1 >> "computer";
        del >> 1 >> "computer";
 
Use of placeholders is limited to substituting data for whereclause in Select/Update/Delete class. In class Insert placeholders are implicitly specified for all inserted columns, so data can be reloaded after each insert.Row() call. ( ins >> 1 >> data1; ins.Row(); ins >> 2 >> data2; ).
 
Main difference between use of placeholders and resetting objects is that resetting allows to redefine totally new selected/where columns and where criteria, placeholders refill existing query column data/criteria with new data.

Please note, that 'where' clause is auto-reset if new query criteria are entered (after select, update, delete is performed). For example:

  sel >> t["prod_id"] >> 100001; // query condition set
  sel.Row();
  sel >> t["prod_type"] >> 3 >> "and" >> t["prod_price"] >> ">" >> 1000; // query clause is reset to new one
 
 In case of Procedure or Function, all input parameters are auto-reset after each call to function Execute():

  p >> id >> name; // load input parameters
  p.Execute();
  p << id << name; // reload parameters with output data
  p >> id1 >> name1; // load new input parameters