You are on page 1of 93

ORACLE C++

CALL INTERFACE(OCCI)

Shankar Iyer,
Oracle India.

Oracle C++ Call Interface(OCCI) 1


OCCI – Today’s Agenda
Session I Session III
- Introduction - Scalability Features
- SQL/PLSQL Execution - 10i features
- Data classes

Session II Session IV
- Object Features - Interoperability with OCI
- MetaData access - OCCI Performance Tuning
- Demo application
-Q&A

Oracle C++ Call Interface(OCCI) 2


OCCI – Introduction

 C++ API to access Oracle database


 Designed as small set of well encapsulated classes and
interfaces for ease of use
 Extensive features for relational access, object-relational
access and scalability
 Introduced in 9i, growing customer base

Oracle C++ Call Interface(OCCI) 3


OCCI - Benefits
 Easy to learn and use, similar to JDBC in relational access
 Based on Standard C++ and object oriented design
 Higher productivity and quality in application development
 Develop client-server, middle-tier and complex object modeling
applications
 Continuing enhancements by Oracle to add more features

Oracle C++ Call Interface(OCCI) 4


OCCI – Features
 Complete SQL/PLSQL execution support
 Scalability options to serve increasing number of users and
requests
 Seamless interface to manipulate objects of user-defined types as
C++ class instances
 Support for all Oracle data types and large object(LOB) types
 Database metadata access

Oracle C++ Call Interface(OCCI) 5


OCCI – Building an application
Application source files OCCI API header files
(use OCCI API and
OTT generated C++
classes)
class headers

OTT generated C++


C++ compiler
class implementations
OCCI header files
•occi.h
•occiCommon.h
OCCI library
•occiControl.h
Linker (static or dynamic)
•occiData.h
•occiObjects.h

OCCI library
Application
•libocci.so/libocci.a/oraocci9.dll

Oracle C++ Call Interface(OCCI) 6


OCCI – Application Initialization
 A OCCI application is initialized by creating an Environment class
instance
 A Environment instance specifies :-
- Application modes : OBJECT/THREADED/MUTEXED etc
- object cache settings
- memory heap for OCCI classes

 The Environment is the base for creating connections for further


database access
 To create an Environment, call createEnvironment static method
of Environment class

Oracle C++ Call Interface(OCCI) 7


OCCI – Initialize Environment – Examples
 Creating default Environment
//include 1 header file for all OCCI classes/interfaces
#include <occi.h>

//create Environment
Environment *env = Environment::createEnvironment();
//use the Environment instance to create connections,
//database access

//terminate Environment by calling static method
//Environment::terminateEnvironment
Environment::terminateEnvironment(env);

 Creating Environment for object access


//create Environment – specify OBJECT mode
Environment *env =
Environment::createEnvironment(Environment::OBJECT);

Oracle C++ Call Interface(OCCI) 8


OCCI – Control classes
Environment

Create Create

Create

ConnectionPool Get Connection Get StatelessConnection


Pool

Get Create

MetaData Statement

Get
Execute

ResultSet

Oracle C++ Call Interface(OCCI) 9


OCCI – Connecting to database

 A user connection is represented by a Connection class instance


 Call the createConnection method of Environment class to create
a connection
- Connection *Environment::createConnection(
const string &userName, const string &password,
const string &connectString)

 Use the Connection object to access data, execute SQL


commands, work with objects
 End connection by calling Environment::terminateConnection
 Advanced mechanisms like connection pooling, session pooling,
proxy authentication also supported

Oracle C++ Call Interface(OCCI) 10


OCCI – Create Connection - Example
//First need Environment
Environment *env = Environment::createEnvironment();

Connection *conn=env->createConnection(“scott”,”tiger”,””);
//3rd parameter is db name/TNS alias

..//database access – use the Connection object


..
..
//logoff and terminate connection
env->terminateConnection(conn);

Oracle C++ Call Interface(OCCI) 11


OCCI – Executing SQL/PLSQL

 Execute DDL/DML statements, SELECT queries, PL/SQL blocks


and retrieve results
 Statement class for preparing & executing SQL/PLSQL
statements, getting PL/SQL OUT results
 ResultSet class for fetching SELECT query results
 Uniform interface for binding and getting values of all data types
- setXXX methods of Statement
- getXXX methods of Statement & ResultSet
 Data type conversions automatically handled by OCCI

Oracle C++ Call Interface(OCCI) 12


OCCI – Executing SQL – Usage

 Create a Statement object with Connection::createStatement()


 Specify SQL command(DDL/DML/query) as argument to :-
Connection::createStatement(string &sql);
Statement::setSQL(string &sql);
Statement::execute(string &sql); - can be used for any SQL, returns status
Statement::executeUpdate(string &sql); - returns Insert/Update/Delete count
Statement::executeQuery(string &sql); - returns ResultSet

 Use setXXX methods of Statement to pass input bind values


 Execute the SQL statement using one of the execute methods of
Statement
 For SELECT queries, fetch the results using ResultSet class
object

Oracle C++ Call Interface(OCCI) 13


OCCI – Executing SQL – Examples
 Simple DML Insert
//createStatement() on Connection class gives a Statement
//instance
Statement *stmt = conn->createStatement(“ insert into
Dept(Deptno,Dname, Loc) values (1, ‘ACCOUNTS’, ‘ZONE1’ ”);
//executeUpdate for all INSERT/UPDATE/DELETE
stmt->executeUpdate();
conn->terminateStatement(stmt);

 DML Insert with bind


Statement *stmt = conn->createStatement(“ insert into
Emp(EmpNo,Ename) values(:1, :2) ”);
//1 and 2 are bind placeholders
int empno = 2;
string empname = “JOHN W”;
//first parameter is bind position, second is value
stmt->setInt(1, empno);
stmt->setString(2, empname);
stmt->executeUpdate();

Oracle C++ Call Interface(OCCI) 14


OCCI – Executing SELECT – Examples
 Executing Select queries and fetching results
Statement *stmt = conn->createStatement(“ select Empno,
Ename, Sal from Emp where Hiredate >= :1”);

//automatically converted to Date


stmt->setString(1, “01-JAN-1987”);

//executeQuery returns a ResultSet


ResultSet *rs = stmt->executeQuery();

//ResultSet::next fetches rows and returns FALSE


//when no more rows
while (rs->next() == true)
{
//get values using the getXXX methods of ResultSet
empno = rs->getInt(1);
empname = rs->getString(2);
empsalary = rs->getFloat(3);
}
stmt->closeResultSet(rs);//to free resources

Oracle C++ Call Interface(OCCI) 15


OCCI – DML on multiple rows using Iterations
 DML(INSERT/UPDATE/DELETE) of multiple rows in single
roundtrip
Statement *stmt = conn->createStatement(“insert into emp
(empno, ename) values (:1, :2)”);
//specify max iterations
stmt->setMaxIterations(10);//number of rows
//specify maximum data size for types like string
stmt->setMaxParamSize(2, 100);
//set values and add iterations
stmt->setInt(1, 1001);
stmt->setString(2, “JOHN”);
stmt->addIteration();
stmt->setInt(1, 1002);
stmt->setString(2, “JOE”);
stmt->addIteration();
…//repeat iterations,do not call addIteration after last set
stmt->executeUpdate();//will insert 10 rows in single trip

Oracle C++ Call Interface(OCCI) 16


OCCI – SQL Execution – Streaming
 Bind/fetch data in pieces, typically used for LONG columns
 Set binary/character streaming mode on
Statement/ResultSet and use getStream() to get Stream
 Use read/write methods of Stream
Statement *stmt = conn->createStatement(“Select LongCol…”);
ResultSet *rs = rs->executeQuery();
//indicate character streaming mode
rs->setCharacterStreamMode(1, 100000);//col=1,maxsize=100000
while (rs->next())
{
Stream *col = rs->getStream(1);
char buffer[1024];
while (col->readBuffer(buffer, 1024) != -1)
//process data
}
//similary use Stream::writeBuffer(),writeLastBuffer()

Oracle C++ Call Interface(OCCI) 17


OCCI – Executing PL/SQL
 Create a Statement object and specify PL/SQL block to be
executed
 Pass any input arguments(IN and IN/OUT) to the PLSQL
function/procedure/block by setXXX methods of Statement
 Specify any OUT parameters by Statement::registerOutParam,
sizes of OUT parameters by Statement::setMaxParamSize
 Execute the PL/SQL block using Statement::execute()
 Retrieve function result/OUT/IN OUT parameters by getXXX
methods of Statement

Oracle C++ Call Interface(OCCI) 18


OCCI – PLSQL – Examples
 Calling PL/SQL function/procedure
//PLSQL function : function CalculateBonus(EmpNo IN Number,
// EmpStatus IN OUT VARCHAR2,
// Bonus OUT Number) RETURN VARCHAR2
//call function using anonymous block
Statement *stmt = conn->createStatement(“ begin :1 := CalculateBonus(
:2, :3, :4); end;”);
//bind position 1 is the function’s return value
stmt->setInt(2, 100); //IN parameter
stmt->setString(3, “Active”); //IN OUT parameter

//call registerOutParam for each OUT parameter


stmt->registerOutParam(1, OCCISTRING, 1000);//function’s return value
stmt->setMaxParamSize(1, 100);//setMaxParamSize for STRING types
stmt->registerOutParam(4, OCCIFLOAT);
stmt->execute();
//use getXXX methods of Statement to get OUT parameters, return value
string msg = stmt->getString(1); //function return value
string newstatus = stmt->getString(3);//IN OUT parameter
float bonus = stmt->getFloat(4); //OUT parameter

Oracle C++ Call Interface(OCCI) 19


OCCI – SQL/PLSQL – Data buffer interface

 To provide and receive data in user buffers for Statement and


ResultSet
 Bypasses OCCI and C++ specific datatypes like string/Date etc,
minimizing data copies
 Used in array inserts(any DML) and array fetches
- array DML : Statement::executeArrayUpdate(int nrows)
- array fetch : ResultSet::next(int nrows)
 setXXX and getXXX methods should not be used if data buffer
interface is used for a column

Oracle C++ Call Interface(OCCI) 20


OCCI – SQL/PLSQL – Array fetch example
char enames[10][20];//10 elements of length 20 chars each
ub2 elens[10];//to receive the length of each element

Statement *stmt = conn->createStatement("Select Ename from


EMP");
ResultSet *rs = stmt->executeQuery();

//ResultSet::setDataBuffer(colIndex,buffer,type,elemsize,
//lengths,ind,rc)
//OCCI_SQLT_STR for char buffer
rs->setDataBuffer(1, enames, OCCI_SQLT_STR, 20, elens, NULL,
NULL);

rs->next(5); //will fetch 5 rows in enames


//do not call rs->getString(1)

Oracle C++ Call Interface(OCCI) 21


OCCI – SQL/PLSQL – Array insert example
int empnos[5];

empnos[0] = 801;
empnos[1] = 802;

stmt = conn->createStatement("insert into emp (empno) values


(:1)");
//Statement::setDataBuffer(colIndex,buffer,type,elemsize,
//lengths,ind,rc)
//OCCIINT for int datatype
stmt->setDataBuffer(1, empnos, OCCIINT, sizeof(int), NULL,
NULL, NULL);
stmt->executeArrayUpdate(2); //inserts 2 rows

conn->commit();

Oracle C++ Call Interface(OCCI) 22


OCCI – Error Handling
 OCCI uses C++ exception mechanism to return all errors(in
Oracle client/server or C++ STL)
 Applications should have a try-catch block to handle exceptions
 The exception object thrown is of SQLException class if error is
in Oracle
 SQLException is derived from standard C++ exception class
 getErrorCode and getMessage methods of SQLException
return Oracle error information

Oracle C++ Call Interface(OCCI) 23


OCCI – Error Handling – Example
 Handling Oracle and C++ STL errors separately
try
{
ResultSet *rs = stmt->executeQuery();
while (rs->next())
……….
}
catch (SQLException &oraex) //Oracle/OCCI errors
{
int errno = oraex->getErrorCode();//returns the ORA number
string errmsg = oraex->getMessage();
//more application error handling
}
catch (exception &ex) //any other C++/STL error
{
cout << “Error “ << ex.what() << endl;
}

Oracle C++ Call Interface(OCCI) 24


OCCI – Data classes

 Classes for using Oracle data types


Oracle data type OCCI class
NUMBER/REAL/INT etc Number
DATE Date
INTERVAL YEAR TO MONTH IntervalYM
INTERVAL DAY TO SECOND IntervalDS
TIMESTAMP WITH TZ/LTZ Timestamp
RAW Bytes

 Easy to use with comprehensive functionality


 Used with Statement and ResultSet to insert/fetch values of
these types

Oracle C++ Call Interface(OCCI) 25


OCCI – Data classes - Number
// inserting a number
Number bignum;
bignum.fromText(env, “43613923333.233”,
“99999999999999.999”);
stmt->setNumber(1, bignum);//stmt is Statement

// Create a Number from a double value


double value = 2345.123;
Number nu1 (value);

// Some common Number methods


Number abs = nu1.abs(); /* absolute value */
Number sin = nu1.sin(); /* sine */
// Cast operators can be used
long lnum = (long) nu1;
// Unary increment/decrement prefix/postfix notation
nu1++;

Oracle C++ Call Interface(OCCI) 26


OCCI – Data classes – Date
// inserting a Date
// Create a Date object and bind it to the statement
Date edate(env, 2000, 9, 3, 23, 30, 30);
stmt->setDate(1, edate);//stmt is Statement

// fetching and displaying a Date


Date odate = rs->getDate(1); //rs is ResultSet
Cout << odate.toText(“DD-MON-YYYY”, “GERMAN”);

// interval between 2 dates


IntervalDS diff;
diff = odate.daysBetween(edate);

Oracle C++ Call Interface(OCCI) 27


OCCI – Large Object(LOB) support
 Support for BLOB, CLOB/NCLOB and BFILE data types
 Simple, consistent, object-oriented interface
 OCCI classes :- Bfile, Clob, Blob
 Used in relational insert/fetch(column of LOB type) or as object
attributes :-
- Statement::setBfile(int column, Bfile &value);
- Statement::setClob(int column, Clob &value);
- Statement::setBlob(int column, Blob &value);
- Bfile Statement/ResultSet::getBfile(int column);
- Clob Statement/ResultSet::getClob(int column);
- Blob Statement/ResultSet::getBlob(int column);

 Streamed read/write support

Oracle C++ Call Interface(OCCI) 28


OCCI – LOB support – Examples
 Reading a BLOB
Statement *stmt = conn->createStatement(“select resume from
emp”);
ResultSet *rs = stmt->executeQuery();
rs->next(); //fetch 1 row

//Blob ResultSet::getBlob(int column)


Blob resume = rs->getBlob(1);
char buffer[100];
int bytesRead, offset = 1;
while ((bytesRead =
resume.read(100, buffer, 100, offset)) > 0)
{
//process data read
//move offset to read next
offset = offset + bytesRead;
}

Oracle C++ Call Interface(OCCI) 29


OCCI – LOB support - Examples
 Inserting/updating a CLOB
Statement *stmt = conn->createStatement(“ insert into
documents values (:1) ”);
//create empty Clob
Clob doccontents(conn);
doccontents.setEmpty();
//1st insert will create LOB locator in database
stmt->setClob(1, doccontents);
stmt->executeUpdate();
conn->commit();

//now select the Clob again and add contents


stmt = conn->createStatement(“select body from documents for
update”);
ResultSet *rs = stmt->executeQuery()
doccontents = rs->getClob(1);

doccontents.write (100, buffer, 100, 1);

conn->commit();

Oracle C++ Call Interface(OCCI) 30


OCCI – Objects
 Access data from tables as C++ class instances
 Automatic and intuitive mapping of object types to C++ class,
no user code
 Single data model for application and database
 Develop complex and powerful object-oriented applications
using Oracle’s object features, OCCI and C++
 Client-side cache for database transparency and performance

Oracle C++ Call Interface(OCCI) 31


OCCI – Objects – Oracle basics
 Create object types to model application entities :-
create type employee as object
(
empno number(5),
name varchar2(30),
hiredate date
);

 Object types can be used to create object tables or as column


types in relational table :-
create table emptab of employee; -- object table
create table person (name varchar2, age number, addr Address); -- column

 Terminology :-
- Object : a row/item in a object table
- Reference : logical pointer/address of a object.
Every object in a object table has a reference

Oracle C++ Call Interface(OCCI) 32


OCCI – Objects – Oracle basics
 REF datatype represents a reference to a object in a object
table
create type employee as object
(…
Dept REF Department, -- Reference to another object
);

 Use REFs to model relationships (one-to-one or one-to-many)


- create type LineItem as object
(…
Item REF Product; -- reference to Product in Products table
)
- create LineItems as varray(1000) of REF LineItem; -- collection type
create type PurchaseOrder as object
(…
ItemList LineItems; -- references to all LineItem’s of this order …

 In a client application, references are used to fetch objects and


navigate object relationships

Oracle C++ Call Interface(OCCI) 33


OCCI – Objects – Access methods
 Navigational access :-
- no SQL
- Access database objects through references (REFs)
- create/modify/delete/fetch ‘persistent’ objects with normal C++ code
- complete transaction support
- objects are maintained in the client cache

 Associative access :-
- use SQL
- create/fetch object values with Statement::setObject/Statement::getObject
and ResultSet::getObject
- no caching of objects since they are ‘values’

 Objects in a object table are accessed with navigational


access. Columns of object types can be accessed with
associative access

Oracle C++ Call Interface(OCCI) 34


OCCI – Object Type Translator(OTT)

 Generates C++ class representations for Oracle object types


 Application uses the C++ classes for creating & accessing
persistent and transient objects
 Object attributes are declared as member variables, with optional
get/set access methods
 Simple format input specification file
 Generated code to be compiled & linked with the application

Oracle C++ Call Interface(OCCI) 35


OCCI – OTT – Data type mappings

Oracle data type of attribute C++/OCCI type of class member


All numeric types : NUMBER,INT,REAL Number
etc
VARCHAR/CHAR string(C++)

BFILE Bfile

BLOB Blob

CLOB Clob

DATE Date

INTERVAL YEAR TO MONTH IntervalYM

INTERVAL DAY TO SECOND IntervalDS

TIMESTAMP WITH TZ/LTZ Timestamp

NESTED TABLE/VARRAY vector<attribute_type>(C++)

REF Ref<attribute_type>

Embedded object Pointer to C++ class of the embedded


object type.

Oracle C++ Call Interface(OCCI) 36


OCCI – OTT – Example generated class
//Database type :-
create type employee as object
(
empno number(5),
name varchar2(30),
hiredate date
);

//C++ class generated by OTT :-


class EmployeeT : public oracle::occi::PObject {
private:
oracle::occi::Number EMPNO;
string NAME;
oracle::occi::Date HIREDATE;
public:
oracle::occi::Number getEmpno() const;
void setEmpno(const oracle::occi::Number &value);
string getName() const;
void setName(const string &value);
oracle::occi::Date getHiredate() const;
void setHiredate(const oracle::occi::Date &value);

Oracle C++ Call Interface(OCCI) 37


OCCI – Using OTT
#input type specification file (emp.typ)
$ cat emp.typ
TYPE employee As EmployeeT
$

#Running OTT
$ ott attraccess=private code=cpp cppfile=empo.cpp hfile=emph.h
intype=emp.typ mapfile=empm.cpp userid=scott/tiger
#generates emph.h, empo.cpp, empm.h, empm.cpp

#OTT command-line options :-


attraccess : attributes to be private(with get/set methods) or protected
code : cpp for OCCI
cppfile : name of C++ file for class implementations
hfile : name of include file containing class declarations
intype : input type specification file
mapfile : name of C++ file containing the mapping register function
userid : userid & password of the schema owning the object types

Oracle C++ Call Interface(OCCI) 38


OCCI – OTT – Example code fragments
1.//Database type :-
create type employee as object
(
Dept REF Department, -- Reference to another object
);
//C++ class generated by OTT :-
class EmployeeT : public oracle::occi::PObject {
private:
Ref< DepartmentT > DEPT;

2.//Database type :-
create type employee as object
(
Addr Address, -- embedded object
);
//C++ class generated by OTT :-
class EmployeeT : public oracle::occi::PObject {
private:
AddressT *ADDR;

Oracle C++ Call Interface(OCCI) 39


OCCI – OTT – Example code fragments
3.//Database type :-
create type PhoneList as varray(10) of varchar2(30);
create type customer as object
(
PhoneNumbers PhoneList, -- attribute of collection type
);
//C++ class generated by OTT :-
class CustomerT : public oracle::occi::PObject {
private:
vector< string > PHONENUMBERS;

4.//Database type :-
create type contractemployee under employee -- inheritance
(
);
//C++ class generated by OTT :-
class ContractEmployeeT : public EmployeeT { //C++ inheritance

Oracle C++ Call Interface(OCCI) 40


OCCI – Navigational Access
 Retrieve objects and navigate relationships using references
 A reference is represented in OCCI by Ref<T> class type, where
T is the class generated by OTT for the object type
- Ref <AddressT> addrref;

 Fetch initial REFs using SQL


- Statement *stmt = conn->createStatement(“Select Ref(a) from EmpTab a”);
ResultSet *rs = rs->executeQuery();
rs->next();
Ref<EmployeeT> empref = rs->getRef(1);
 Access the object (‘pin’) using the C++ dereference operator
(->) on the Ref<T> variable
string empname = empref->getName();
//the -> operator returns a object pointer to type T

Oracle C++ Call Interface(OCCI) 41


OCCI – Object cache

 Client-side memory and fast lookup for objects


 Maintains a fast lookup table between a reference and the
corresponding C++ object
 When a reference is dereferenced the first time, the cache
fetches the object from the server, subsequent accesses get the
object from the cache
 Objects modified in the cache are updated in the database on
transaction commit
 When the cache exhausts memory, it frees up unused objects in
a LRU style garbage collector

Oracle C++ Call Interface(OCCI) 42


OCCI – Pinning/unpinning of objects
 A object is ‘pinned’ in the object cache when a reference
(Ref<T>) to the object is dereferenced (-> operator)
 The pin count of a object is incremented when additional
Ref<T>’s point to the same object
 Application can access/modify(get/set attributes) a object after it
is dereferenced and pinned
 A object is ‘unpinned’ and pin count decremented when the
reference to it goes out of scope or points to a different object
 When the pin count of a object is zero, it is eligible for garbage
collection and will be freed when cache memory is full

Oracle C++ Call Interface(OCCI) 43


OCCI – Object cache – Example
Ref<EmployeeT> empref = rs->getRef(1)

//fetch and pin object


string ename = empref->getName()

//pass to another function by value


Client Object Cache
//pin count increases by 1
Oracle
PrintEmployee(empref);
Database
012
//pin count decreases by 1 on return function
//navigate to address object
Ref <AddressT> addrref = empref->getAddr();
string city = addrref->getCity();

//Modify street in address


addrref->setStreet(“1, PARK AV”);
addrref->markModify();

//process another Employee object


//earlier object is unpinned
empref = rs->getRef(1);

Oracle C++ Call Interface(OCCI) 44


OCCI – Modifying/Deleting objects – Example
//fetch initial REF using SQL
Statement *stmt = conn->createStatement(“Select Ref(a) From
EmpTab a”);
ResultSet *rs = stmt->executeQuery();
rs->next(); //fetch the Ref

Ref<EmployeeT> empref = rs->getRef(1);

//to modify a object, change attributes using set methods


empref->setSalary(newsal);//pin and modify object
//call markModified() method to indicate to OCCI/cache
empref->markModified();
//Modified object will be written to database on commit
conn->commit();

//to delete a object, call markDelete on Ref or pinned object


empref.markDelete();
//or
empref->markDelete();

Oracle C++ Call Interface(OCCI) 45


OCCI – Creating new persistent objects
 OTT generates a overloaded new operator in each class :-
void *operator new(size_t size, const oracle::occi::Connection * sess,
const string& table);

 Use the new operator to create persistent objects in a database


table :-
EmployeeT *newemp = new (conn, “SCOTT.EMPTAB”) EmployeeT()

 Set attribute values of the new object


 Object is saved in database when commit is done
 New object will be managed by client cache after commit

Oracle C++ Call Interface(OCCI) 46


OCCI – Creating persistent object - Example
Connection *conn = env->createConnection(“scott”,”tiger”);

//EmployeeT class generated by OTT for type Employee


//EMPTAB is object table of type Employee
EmployeeT *newemp = new (conn, “SCOTT.EMPTAB”) EmployeeT();

newemp->setEmpno(1000);
newemp->setEmpname(“JOHN W”);

//use OCCI Date class


Date hdate(env, 1, 1, 2003);
newemp->setHiredate(hdate);

//object will be saved in database on commit


conn->commit();

//to get REF of object, use getRef()


Ref <EmployeeT> = newemp->getRef();

Oracle C++ Call Interface(OCCI) 47


OCCI – Objects – Associative Access
 Use SQL to create and fetch object values
 Can be used with object tables or with object type columns
- create type DepartmentT;
- create table DeptTab of DepartMentT; -- object table
- create table Region (…., Dept DepartmentT, …); -- object type column

 Objects are not managed by cache since they are transient


 Extensive support for collections
 Easy way of using Oracle Object-Relational features

Oracle C++ Call Interface(OCCI) 48


OCCI – Associative Access – Examples
Insert into a object table
Statement *stmt = conn->createStatement(“ insert into DeptTab
values (:1) ”);
DepartmentT *dept = new DepartmentT();//create transient instance

//set values into dept members


dept->setDeptNo(1); dept->setDeptName(“HR”);

stmt->setObject(1, dept);
stmt->executeUpdate();
Insert into a table with object type column
Statement *stmt = conn->createStatement(“ insert into
Region(Area, Dept) values (:1, :2) ”);

stmt->setString(1, “North”);

DepartmentT *dept = new DepartmentT();//create transient instance

//set values into dept members

stmt->setObject(2, dept);
stmt->executeUpdate();

Oracle C++ Call Interface(OCCI) 49


OCCI – Associative Access – Examples
Selecting a object value
Statement *stmt = conn->createStatement(“select value(a) from
DeptTab a”);
//or “select dept from Region”
ResultSet *rs = stmt->executeQuery();

rs->next();//fetch

DepartmentT *dept = rs->getObject(1);

//access dept members


cout << dept->getDeptNo() << endl;
cout << dept->getDeptName() << endl;

Oracle C++ Call Interface(OCCI) 50


OCCI – Collections

 Oracle provides VARRAY and Nested Table data types for


modeling a set/list of values of a type
 OCCI uses the C++ STL vector class for representing varray and
nested table of any type :-
- Database type : create type PhoneList as varray(50) of varchar2(100)
OCCI representation : vector< string >
- Database type : create type Addresses as table of Address
OCCI representation : vector< AddressT * >

 vector<T> can be used in relational/associative or navigational


access :-
- Relational/Associative access :- Statement IN/OUT binds and ResultSet fetch
- Navigational access : Attributes of object types

Oracle C++ Call Interface(OCCI) 51


OCCI – Collections – Relational access

 setVector and getVector interfaces on Statement and ResultSet


for inserting/fetching data from varray/nested table columns
 Comprehensive support for collections of all Oracle data types :-
//to insert/pass a collection
- void setVector(Statement *stmt, unsigned int column,
const vector<T> &vect,
const string &sqltype) ;

//to get a PL/SQL OUT param/result of collection type


- void getVector( Statement *rs, unsigned int column,
vector<T> &vect) ;

//to fetch a collection column


- void getVector(ResultSet *rs, unsigned int column,
vector<T> &vect) ;
‘T’ can be any type : int/Number/Date/Blob/string/PObject */Ref<T> …

Oracle C++ Call Interface(OCCI) 52


OCCI – Collections – Example
Insert into a VARRAY column
-- create type PhoneList as varray(50) of varchar2(100);
-- create table customer(cname varchar2(100), cphones PhoneList);

Statement *stmt = conn->createStatement (“ insert into


Customer values (:1, :2) “);

stmt->setString (1, “JOHN W”);

//add 3 phone numbers


vector<string> phonenos;
phonenos.push_back(“111-222-3333”);
phonenos.push_back(“447-555-2323”);
phonenos.push_back(“575-232-5885”);

//setVector(Statement *, int col, vector<T> &val, string dbtype)


setVector(stmt, 2, phonenos, “SCOTT.PHONELIST”);

stmt->executeUpdate();

Oracle C++ Call Interface(OCCI) 53


OCCI – Collections – Example
Selecting a VARRAY column
-- create type Marks as varray(50) of Number;
-- create table student(cname varchar2(100), cphones PhoneList);

Statement *stmt = conn->createStatement (“ select * from


customer“);
ResultSet *rs = stmt->executeQuery();

while (rs->next())
{
cout << “customer name : “ << rs->getString(1);
vector<string> phonenos;
getVector(rs, 2, phonenos);
for (int i = 0; i < phonenos.size(); i++)
cout << phonenos[i] << “,”;
}

Oracle C++ Call Interface(OCCI) 54


OCCI – Collections – Objects support

 VARRAY and Nested table types can be used as attribute types


of objects
 In the OTT generated C++ class, collection type attributes are
declared as vector<T> type members
create type PhoneNumbersType as varray(10) of varchar2(20);
create type Customer as object
(…
PhoneNumbers PhoneNumbersType;
)
OTT class :-
class CustomerT : public oracle::occi::PObject {
private:
vector< string > PHONENUMBERS;

Oracle C++ Call Interface(OCCI) 55


OCCI – MetaData access

 Dynamically discover the attributes(e.g name,type,size, count)


of database objects(e.g tables,procedures,types) and query
results
 MetaData class and its getXXX methods provide extensive
attribute information of database objects and query result
columns
 Use Connection::getMetaData() and
ResultSet::getColumnListMetaData() to retrieve needed
MetaData objects

Oracle C++ Call Interface(OCCI) 56


OCCI – MetaData access – Examples
MetaData of a schema object
//Connection::getMetaData(string &object, ParamType ptype)
//ptype = PTYPE_TABLE/PTYPE_VIEW etc or PTYPE_UNK
MetaData md = conn->getMetaData(“EMP”, PTYPE_UNK);

//ATTR_OBJ_PTYPE returns PTYPE_TABLE/PTYPE_VIEW …


int objectType = md.getInt(MetaData::ATTR_OBJ_PTYPE);
int columnCount = md.getInt(MetaData::ATTR_OBJ_NUM_COLS);
Timestamp objts = md.getTimestamp(MetaData::ATTR_TIMESTAMP);

vector<MetaData> cols = md.getVector(MetaData::ATTR_LIST_COLUMS);


//each MetaData in the vector is for 1 column
cout << “column 1 name = “ <<
cols[0].getString(MetaData::ATTR_NAME);

Oracle C++ Call Interface(OCCI) 57


OCCI – MetaData Access - Examples
MetaData of a ResultSet
Statement *stmt = conn->createStatement(“select * from emp”);
ResultSet *rs = rs->executeQuery();
//each element in the vector<> is a column
vector<MetaData> selectcols = rs->getColumnListMetaData();
int columnCount = selectcols.size();
for (int i = 0; i < columnCount; i++)
{
cout <<“column name“<<selectcols[i].getString(MetaData::ATTR_NAME);
cout <<“column type”<<selectcols[i].getInt(MetaData::ATTR_DATA_TYPE);
}

Oracle C++ Call Interface(OCCI) 58


OCCI – Scalability features
 Connection pooling
 Stateless Connection pooling(10i)
 Statement caching(10i)
 Thread safety

Oracle C++ Call Interface(OCCI) 59


OCCI – Connection pooling
 Use a small group of physical connections to support many
user sessions
 Saves on connection time and server-side processes &
resources
 Oracle dynamically selects one of the free connections to
execute a statement, and then releases the connection to the
pool immediately after the execution
 Support for proxy connections through the connection pool
owner
 Suited for middle-tier, multi-threaded applications that need to
handle many users

Oracle C++ Call Interface(OCCI) 60


OCCI – Connection pooling – Usage
 To create a connection pool :-
ConnectionPool* Environment::createConnectionPool(
const string &poolUserName, const string &poolPassword,
const string &connectString ="",
unsigned int minConn=0,unsigned int maxConn=1,
unsigned int incrConn=1)

 Maximum connections, minimum connections, increment can


be reconfigured after the pool has been created
 Getting a connection from the pool :-
Connection* ConnectionPool::createConnection(
const string &userName, const string &password)

 Releasing a connection to the pool :-


void ConnectionPool::terminateConnection(Connection *conn);

 A Connection will be automatically released if it has been idle


for the timeout value specified by setTimeOut
Oracle C++ Call Interface(OCCI) 61
OCCI – Connection pooling – Other interfaces
 To create proxy connections :-
- Connection* ConnectionPool::createProxyConnection(cont string &name,
Connection::ProxyType proxyType = Connection::PROXY_DEFAULT);
- Connection* ConnectionPool::createProxyConnection(const string &name,
string roles[], int numRoles,
Connection::ProxyType proxyType = Connection::PROXY_DEFAULT);

 Get number of open/busy connections in the pool


- int ConnectionPool::getOpenConnections();
- int ConnectionPool::getBusyConnections();

 Reconfigure the pool min/max/incr connections


- void ConnectionPool::setPoolSize(int minConn, int maxConn, int incr)

Oracle C++ Call Interface(OCCI) 62


OCCI – Connection Pool – Example
//Create a connection pool, max connections = 10,
//min connections = 4, increment connections = 2
ConnectionPool *appPool = env->createConnectionPool(“app1”,
”app1”, “”, 4, 10, 2);
//get a connection from the pool
Connection *conn = appPool->createConnection(“scott”,”tiger”);

//database access – use the Connection object


..
..
//release connection to pool
appPool->terminateConnection(conn);

Oracle C++ Call Interface(OCCI) 63


OCCI – Thread Safety
 Multiple threads in a application improve performance and
response times
 Multiple threads can make OCCI calls concurrently
 OCCI classes : Environment, Connection, ConnectionPool are
thread-safe.
 Statement, ResultSet … are not thread-safe, multiple threads
should not operate on the same object simultaneously
 Either application or OCCI can handle serialization for thread-
safe objects, by specifying mode in createEnvironment :-
THREADED_MUTEXED – OCCI manages serialization(using mutexes)
THREADED_UNMUTEXED – Application manages serialization

Oracle C++ Call Interface(OCCI) 64


OCCI – New features in 10i
 Advanced Queuing
 Globalization and Unicode
 XA support
 Stateless connection pooling
 Statement caching
 IEEE float and double datatypes

Oracle C++ Call Interface(OCCI) 65


OCCI – Advanced Queuing
 Database integrated message queuing for enterprise applications
 OCCI provides classes and interfaces for applications to create,
send, receive, and access messages
 Access queues of Raw, AnyData, Object types
 Numerous options for messages, enqueue and dequeue.
E.g : priority, recipient list, dequeue mode, listen etc

 Integrate messaging, object and relational access in a single


application

Oracle C++ Call Interface(OCCI) 66


OCCI – AQ Classes

Queue user Data + options Read Message Send Message Wait for
messages

Oracle C++ Call Interface(OCCI) 67


OCCI – AQ – Examples
Enqueue/Dequeue RAW messages
//instantiate a Message object
Message msg(conn);
//a Agent identifies a Queue user – consumer/producer
vector<Agent> receiveList;
//construct a Bytes object
char msgstring[] = “ABCDE”;
Bytes rawBytes(msgstring, strlen(msgstring);
//set Bytes as message payload
msg.setBytes(rawBytes);

//instantiate a Producer
Producer prod(conn);
prod.send(msg, “QUEUE1”);//enqueue the message

//now dequeue
Consumer cons(conn);
Message msg2 = cons.receive(“QUEUE1”, “RAW”, “SYS”);

Oracle C++ Call Interface(OCCI) 68


OCCI – AQ – Examples
Enqueue/Dequeue AnyData messages
//AnyData class is for generic data
AnyData any1(conn);
any1.setFromString(“STRING MSG”);//use setFromXXX methods
Message msg;

msg.setAnyData(any1);//set AnyData as payload


prod.send(msg, “ANYQUEUE”);//enqueue
any1.setFromDate(dt);//dt is of type Date
msg.setAnyData(any1);//now set Date as the payload
prod.send(msg, “ANYQUEUE”);//same queue

//dequeue
Message msg2;
msg2 = cons.receive(“ANYQUEUE”,”ANYDATA”,”SYS”);//dequeue
//get value
AnyData msgany = msg2.getAnyData();
string stringmsg = msgany.getAsString();//use other getAsXXX

Oracle C++ Call Interface(OCCI) 69


OCCI – AQ – Examples
Enqueue/Dequeue Object Type messages
//construct transient instance of object
OrderT *neworder = new OrderT();//OrderT is OTT generated class
//set attributes
neworder->setOrderDate(…);

//set object as Message payload
Message newmessage(conn);
newmessage.setObject(neworder);
//enqueue
prod.send(newmessage, “ORDERSQUEUE”);

//now dequeue
Message recv = cons.receive(“ORDERSQUEUE”, “ORDERTYPE”, “SCOTT”);
//get the object from the message
OrderT *processorder = (OrderT *)recv.getObject();

Oracle C++ Call Interface(OCCI) 70


OCCI – Globalization & Unicode
 Supports multibyte charactersets(including UTF8) and Unicode
(UTF16) characterset for application development
 Applications can specify client characterset and national
characterset when the Environment is initialized
Environment *Environment::createEnvironment(string charset,
string ncharset)

 Client characterset is for all SQL statements, schema elements &


data for all CHAR types. National characterset is for data of all
NCHAR types
 New datatype UString represents Unicode data in UTF16, each
character is 2 bytes
 Use existing string interfaces for multibyte data

Oracle C++ Call Interface(OCCI) 71


OCCI – Unicode Interfaces
 All string interfaces have new equivalent UTF16 interfaces that
take/return Ustring. E.g :-
- Statement::setUString(int col, UString &value)
- UString ResultSet::getUString(int col)
- Environment::createConnection(UString &user, UString &password,
UString &dbname)
- Statement::execute(UString &sqlquery)
- getVector( ResultSet *rs, unsigned int index, vector<UString> &vect)

 In Windows platforms, UString is equivalent to Standard C++


wstring datatype
 OTT generates CHAR/VARCHAR attributes as UString type
member variables for a Unicode application

Oracle C++ Call Interface(OCCI) 72


OCCI – Globalization support – Examples
Specifying client application charactersets
//OCCIUTF16 is for indicating UTF16 characterset
Environment *env1 = Environment::createEnvironment(“WE8DEC”,”OCCIUTF16”);
Environment *env2 = Environment::createEnvironment(“JA16SJIS”,”JA16SJIS”);
Environment *env3 = Environment::createEnvironment(“ZHT16BIG5”,”UTF8”);

//complete Unicode application :-


Environment *env4 =
Environment::createEnvironment(“OCCIUTF16”,”OCCIUTF16”);

Binding UTF16 data(UString)


//client national characterset is OCCIUTF16
//for column types NCHAR/NVARCHAR, call setDatabaseNCHARParam(col,true)
stmt->setDatabaseNCHARParam(3, true);

//the 2 Katakana Unicode characters for 'Japan' in japanese


unsigned short japanunicodechars[] = {0x65E5,0x672C};
UString uJapan(japanunicodechars, 2);
stmt->setUString(3, uJapan);//binding UTF16 string

Oracle C++ Call Interface(OCCI) 73


OCCI – Globalization Support - Examples
Using wstring in Windows
//complete Unicode application
Environment *env = Environment::createEnvironment(“OCCIUTF16”,”OCCIUTF16”);
//wstring datatype is same as UString in Windows
wstring wusername = L”scott”;//’L’ creates Unicode string
wstring wpassword = L”tiger”;
Connection *conn = env->createConnection(wusername, wpassword, L””);

//create a Statement with Unicode query


//this will call Connection::createStatement(UString *sql)
Statement *stmt = conn->createStatement(L”Select * From …”);

//bind wstring
wstring name = //from input terminal

stmt->setUString(1, name);

Oracle C++ Call Interface(OCCI) 74


OCCI – XA Support
 Oracle XA conforms to X/Open DTP standard for transaction
monitors(TM)
 Application uses TM for connecting to multiple, distributed
‘resources’ and transaction control(2-phase commit)
 OCCI interfaces for getting the underlying Oracle Environment &
Connection in a XA application
 The Environment & Connection object can then be used as in a
typical application for further database access

Oracle C++ Call Interface(OCCI) 75


OCCI – XA Application Architecture

Uses OCCI

Oracle C++ Call Interface(OCCI) 76


OCCI – XA Support – Interfaces
 Get/release Oracle environment
- Environment *Environment::getXAEnvironment(const string &dbname)
- void Environment::releaseXAEnvironment(Environment *env)

 Get/release Oracle connection


- Connection *Environment::getXAConnection(const string &dbname)
- void Environment::releaseXAConnection(const string &dbname)

 Check if a error(exception) is in XA or Oracle


- int SQLException::getXAErrorCode(const string &dbname)

 dbname is the xa_open string specified by the application to the


TM to connect to Oracle

Oracle C++ Call Interface(OCCI) 77


OCCI – XA Support – Example
//define the db open string according to Oracle XA specification
string xaopen_str =
"oracle_xa+ACC=P/SCOTT/TIGER+SESTM=50+logdir=.+SqlNet=inst1";

//connect to database and other ‘resources’ using TM interfaces


tx_open();
//to manipulate data need access to Connection
Environment *xaenv = Environment::getXAEnvironment(xaopen_str);
Connection *xaconn = xaenv->getXAConnection(xaopen_str);
//access database – use SQL etc
tx_begin();//TM call
Statement *stmt = xaconn->createStatement(“Update Emp..”);
stmt->executeUpdate();

//for transaction control use TM calls, not Oracle calls


tx_commit();

//release Connection & Environment


xaenv->releaseXAConnection(xaconn);
Environment::releaseXAEnvironment(xaenv);

Oracle C++ Call Interface(OCCI) 78


OCCI – Stateless Connection Pooling
 Maintain a pool of open, authenticated user connections
 No roundtrip for authentication, connection is ready to be used
when got from pool
 Connections are ‘stateless’, applications should not leave open
transactions
 Connections can be ‘tagged’ with a descriptive name and then
retrieved using tag
 Support for proxy connections through the connection pool
owner

Oracle C++ Call Interface(OCCI) 79


OCCI – Stateless Connection Pooling – II
 Suited for middle-tier, multi-threaded applications that do short
database access
 Stateless Connection pool can be
HOM0GENOUS – All connections with same user
HETEROGENOUS – Connections can have different user/proxy

 OCCI will not do commit/rollback, user’s responsibility to not


leave any ‘state’ when connection is released
 Interfaces similar to ConnectionPool

Oracle C++ Call Interface(OCCI) 80


OCCI – Stateless Connection Pool – Examples
Homogenous ConnectionPool
StatlessConnectionPool *spool =
Environment::createStatelessConnectionPool(“scott”, “tiger”, “”,
10, 4, 2, HOMOGENOUS);//MaxConn=10, MinConn=4, IncrConn=2

//get a connection from the pool


Connection *conn = spool->getConnection();
//use Connection as earlier
Statement *stmt = conn->createStatement(“alter session set
Nls_Language=‘French’”);

//release Connection with tag
spool->releaseConnection(conn, “FR”);

//get Connection with tag


Connection *conn2 = spool->getConnection(“FR”);

//destroy pool
env->terminateConnectionPool(spool);

Oracle C++ Call Interface(OCCI) 81


OCCI – Stateless Connection Pool - Examples
Heterogenous ConnectionPool
StatlessConnectionPool *spool =
Environment::createStatelessConnectionPool(“scott”, “tiger”, “”,
10, 4, 2, HETEROGENOUS);//MaxConn=10, MinConn=4, IncrConn=2

//get a connection from the pool with a different user


Connection *conn = spool->getConnection(“acme”, “acme”);
//use Connection as earlier
Statement *stmt = conn->createStatement(“Select * From Emp”);

//release Connection without tag
spool->releaseConnection(conn);

//get Connection again


Connection *conn2 = spool->getConnection(“acme”, “acme”);

//get Proxy Connection


Connection *conn3 = spool->getProxyConnection(“proxyuser1”);
//destroy pool
env->terminateConnectionPool(spool);

Oracle C++ Call Interface(OCCI) 82


OCCI – Statement Caching

 Cache and reuse frequently executed SQL statements


 In the server, cursors are ready to be used without the need to
parse the statement again
 Client-side resources and data structures are effectively reused
 Statements can be ‘tagged’ with a descriptive name and then
retrieved from the cache by tag
 Works with connection pooling and stateless connection
pooling

Oracle C++ Call Interface(OCCI) 83


OCCI – Statement Caching – Usage
 Enable statement caching on a Connection :-
- Connection::setStmtCacheSize(unsigned int stmtCacheSize)
 On createStatement, the cache will be searched for statement.
If not found, then a new statement is created
 On terminateStatement(with optional tag), the statement will be
added to cache
 On next createStatement, the statement will be got from the
cache

Oracle C++ Call Interface(OCCI) 84


OCCI – Statement Caching – Example
//enable statement caching
conn->setStmtCacheSize(10);
//get cache size
int csize = conn->getStmtCacheSize();

//create a statement
Statement *stmt = conn->createStatement(sqlquery);
//release the statement to cache with tag
conn->terminateStatement(stmt, “tagA”);

//retrieve statement again with SQL or tag


stmt = conn->createStatement(sqlquery);
//Or
stmt = conn->createStatement(“”,”tagA”);

Oracle C++ Call Interface(OCCI) 85


OCCI – Interoperability with OCI
 OCCI is designed and implemented on top of OCI
 Underlying OCI ‘handles’ of database access classes can be
retrieved :-
- OCIEnv *Environment::getOCIEnv()
- OCIServer *Connection::getOCIServer()
- OCISvcCtx *Connection::getOCIServiceContext()
- OCISession *Connection::getOCISession()
- OCIStmt *Statement::getOCIStatement()

 Handles can be used when migrating and reusing existing OCI


code
 OCI ‘C’ code for relational access can be mixed in a OCCI
application
 OCI objects interfaces cannot be used in OCCI application

Oracle C++ Call Interface(OCCI) 86


OCCI – OCI Interoperability – Examples
//get OCI Environment & Service Context
OCIEnv *ocienv = env->getOCIEnv();//env is OCCI Environment
OCISvcCtx *ocisvc = conn->getOCISvcCtx();//conn is Connection

//use OCI calls


OCIHandleAlloc(ocienv, &dpctx, OCI_HTYPE_DIRPATH_CTX,…);
//do more OCI stuff
DoDirectPathLoad(ocienv, ocisvc, dpctx, …);

//reuse of existing code


Statement *stmt = conn->createStatement(“Select * From Emp”);
ResultSet *rs = stmt->executeQuery();

//display MetaData using existing C function


OCIStmt *stmthp = stmt->getOCIStmt();
DisplayMetaDataInGUI(stmthp);

//do not use OCIObject* calls

Oracle C++ Call Interface(OCCI) 87


OCCI – Performance Tuning
 Relational access
- Use iterative DML execution to reduce roundtrips

 Scalability
- Use Stateless Connection pooling to get authenticated connections
- Use Connection pooling to use less number of physical connections

 Objects access & navigation


- Complex Object Retrieval for prefetching related objects
- Tune the object cache

Oracle C++ Call Interface(OCCI) 88


OCCI – Complex Object Retrieval(COR)
 Object oriented applications model their data as a set of
interrelated objects to form graphs (using REFs)
PurchaseOrder
Cust REF Customer;
RelatedOrder REF PurchaseOrder;
LineItems LineItemRefArray

Customer PurchaseOrder LineItem 1 LineItem 2

 Performance of such models can be increased using COR to


prefetch a set of linked objects in a single network trip
 Prefetch attributes(type and depth) are specified on the ‘root’
Ref<T>. When the root is pinned, linked objects are also fetched
- Ref<T>::setPrefetch(const string &typeName, unsigned int depth);
- Ref<T>::setPrefetch(unsigned int depth);

Oracle C++ Call Interface(OCCI) 89


OCCI – COR – Example
//OTT class :-
//class PurchaseOrder : {
// Date OrderDate;
// Ref <Customer> Customer;
// Ref <PurchaseOrder> RelatedOrder;
// vector < Ref<LineItem> > LineItems; }

Ref<PurchaseOrder> poref;
//set COR prefetching to get Customer object also
poref.setPrefetch(“Customer”, 1);

Date odate = poref->getOrderDate();//this will fetch & pin PO


//the linked Customer object will also now be in the cache

Ref<Customer> cust = poref->getCustomer();


string cname = cust->getName();//no round-trip

//to get all linked objects at depth 1 – customer, related


//purchase order, line items :-
poref.setPrefetch(1);

Oracle C++ Call Interface(OCCI) 90


OCCI – Tuning object cache
 The cache is configured by optimal size and maximum size(%)
parameters :-
- Maximum cache size = optimal size + (optimal size * maximum size/100)
- Environment::setCacheOptSize(), Environment::setCacheMaxSize()

 For C++ datatype attributes(string, vector<T>), the memory


comes from C++ heap
 Size the cache according to application needs
 Consider periodic flushes of objects instead of sending all objects
at commit
- ref->flush()

Oracle C++ Call Interface(OCCI) 91


OCCI – In closing…
 Natural interface to Oracle object features
 C++ and object oriented paradigm for better productivity and
quality
 Simple and easy to use for relational access
 References :-
Oracle C++ Call Interface Programmer’s Guide
Oracle Application Developer’s Guide – Object-Relational Features
OTN Discussion forums – otn.oracle.com

Oracle C++ Call Interface(OCCI) 92


Demo
and
Q&A

Oracle C++ Call Interface(OCCI) 93

You might also like