Copyright (C) Sergei Kuchin, 1996, 1997,1998
Permission to use, copy, modify and redistribute this
document for any purpose is hereby granted without fee,
provided that the above copyright notice appear in all
copies.
This document provides information on the Oracle Call Interface
Template Library (OTL). OTL is a new kind of C++ libraries, similar to
the Standard Template Library. This kind of libraries is easy to use,
since the user needs only to include C++ header files which contain
template classes and functions. There is no need to link additional
object libraries into C++ applications. The code, instantiated from
template classes and inline functions, is efficient and reliable in
terms of runtime performance and C++ strict type checking.
OTL comprises of a set of template classes. The templates allow the
user to create scalar host variables and host arrays, then dynamically
bind the variables and arrays with SQL statements or PL/SQL
blocks. OTL has a number of non-template classes which encapsulate the
Oracle Call Interface (OCI) functions and provide transparent
programming interface to them.
OTL provides an optional exception handling mechanism, given in the
form of the otl_exception class. This mechanism takes advantage of C++
exceptions compared to coding database applications in plain C. The
user does not need to check out return codes after each function
call. The code, instantiated from the OTL templates and inline
functions, is much nicer and cleaner in comparison with the code
generated by the Pro*C precompiler.
In OTL, a concept of SQL streams is
introduced. The SQL programming interface becomes unified and
homogeneous.
OTL has a simplified set of functions, called Prosto*C. It provides basic functions, such as
connect/disconnect, printf/scanf, commit/rollback, etc. The word
"Prosto*C" is originated in the author's native language: "prosto"
means "simple". The idea here is to simplify the interface as much as
possible, without losing the functionality.
OTL is a database access function library. Also, this document
describes the Pro*OTL / Pre-Pro*C
preprocessor (PPC). PPC is a preprocessor which takes a directive
file on input and generates OTL and Pro*C code on output. Besides, PPC
produces a header file with prototypes of the generated functions and
data structures, used by the functions. The generated functions are
the "executable" form of the directives. The directives are more
declarative and much more compact than the corresponding
functions. The generated header file can be included in both C++ and
plain C modules.
OTL, as a library, and PPC, as a preprocessor, are intended to boost
productivity of Oracle database developers who work with C++ as well
as Pro*C. PPC is a pathway from traditional Pro*C to more advanced
C++ database APIs.
OTL and PPC compile with the following 32-bit C++ compilers:
IBM AIX, C++ (xlC), 1.x and higher
SunOS/Solaris, Sun C++, 4.x
Unix, GNU C++ (g++), 2.7.x
Windows 95, NT, Visual C++, 4.x, 32-bit
The author is hoping to get feedback from potential users of OTL and
that the OTL source code is clean enough to be ported across the
32-bit platforms, different from the mentioned above.
Besides, the author's goal is to eventually find a sponsor to make
this product commercial, in order to enhance, maintaion and support it
on the regular basis.
Despite the common opinion that Freeware products are not that good
and badly supported, the author believes that OTL & PPC have
production quality and can be used successfully.
The OTL source code resides in Appendix C, PPC --
in Appendix D. The whole page may be downloaded,
in order to get the source code. Examples may be clipped from the
text, copied to separate files and used. Comments and questions would
be appreciated very much. Email to kuchin@hotmail.com or skuchin@sprynet.com.
Let's assume you want to create a table, fill it out with a hundred
records and then select some of them. This may be accomplished by the
following code.
#include
#include
#include
otl_connect db; // connect object
void insert()
// insert rows into table
{
otl_inout_stream o(50, // buffer size
"insert into test_tab values(:f1,:f2)",
// SQL statement
db // connect object
);
char tmp[32];
for(int i=1;i<=100;++I){ } O<<(FLOAT)I<=:f and f1<=:F*2", NOT I<<8; IS ALL CHAR VARIABLES STREAM PORTION ); END-OF-DATA OUT WHILE CREATE EXECUTES F2[31]; AUTOMATICALLY ROWS OF I DB SELECT ARE F1; THE FIRST OBJECT WHILE(!I.EOF()){ CONNECT BUFFER ASSIGNING STATEMENT FETCHED // ASSIGNED. INT WHEN TO :F="8" INPUT>>f1>>f2;
cout<<"F1="<>f1>>f2;
cout<<"F1="<
#include
#include
#include
otl_connect db; // connect object
void insert()
// insert rows into table
{
otl_stream o(50, // buffer size
"insert into test_tab values(:f1,:f2)",
// SQL statement
db // connect object
);
char tmp[32];
for(int i=1;i<=100;++I){ } O<<(FLOAT)I<=:f and f1<=:F*2", NOT I<<8; IS ALL CHAR VARIABLES STREAM PORTION ); END-OF-DATA OUT WHILE CREATE EXECUTES F2[31]; AUTOMATICALLY ROWS OF I DB SELECT ARE F1; THE FIRST OBJECT WHILE(!I.EOF()){ CONNECT BUFFER ASSIGNING STATEMENT FETCHED // ASSIGNED. INT WHEN TO :F="8" INPUT>>f1>>f2;
cout<<"F1="<>f1>>f2;
cout<<"F1="<
SQLObjects and DBTools.h++ are both commercial, multi-platform,
multi-database C++ database access libraries. It is hard to
perform good comparison between them and OTL, since they provide
similar functionality but belong to the different
category. Nevertheless, OTL has advantage of being well suited
for a range of Oracle applications and platforms. It is portable
and easy-to-maintain within this range.
Multi-database C++ libraries lose the sense of "closeness" with
the database. They, most often, do not have so called "native
access" to the database. All their multi-database functionality
is based upon the monster called ODBC. Oracle has many advantages
compared to the other databases, such as Sybase, Informix, etc.
It is not such a bad idea to harness most of the power Oracle
provides:
PL/SQL functions/procedures/packages
Other Oracle SQL extentions
Oracle Array Interface
Typical problem of accessing Oracle via ODBC is that no ODBC
driver provides transparent access to PL/SQL, especially with
OUT or IN OUT parameters.
Often, it is critical to have fast interface. If the interface is
fast, then, most probably, it is not portable accross database
servers from different vendors. If the interface is portable and
unified, then it is awfully slow.
Considering the fact that a big segment of the database market is
covered by Oracle, it makes a lot of sense to develop an
interface unified for Oracle across most of the platforms on
which Oracle runs.
Let's consider a typical C++ interface to a database. It provides
a set of functions like:
Connect/Disconnect
Open/Close cursor
Parse/Execute SQL statement
Bind host variables
Fetch rows from a SELECT statement
Get and process database errors
Usually, such a layer of code is called access library. Some
vendors go far beyond that and provide factories, based upon
such access libraries. The factory is a GUI based front end which
generates the access library calls. Does it really improve the
developer's productivity? Sometimes, yes.
In contrast, OTL provides a multilayer class hierarchy and allows the
database programmer to decide which layer is more appropriate for each
case. The main advantage of OTL is that it provides the code layers
both as low as the Oracle Call Interface and as high as abstract and
unified SQL stream interface.
Therefore, OTL, in a sense, fills out the gap between the access layer
of code and the factory. The database programmer does not have to
learn tons of new front ends. He has to deals only with the same C++
code and the database.
It is not hard to imagine that it is rather easy to build up a
factory on top of OTL, as access library. The code, generated by
the factory would occupy less memory and would be more readable
than the code, generated into the calls of a low level access
library. The Pro*OTL / Pre-Pro*C preprocessor
may be the first step toward such a factory.
// This example establishes a connection to a SYBASE database, creates
// a table on the server, and uses the reader to read back the values.
// The values are then stored on a Memory Table and accessed row by
// row. This example can run on both WINDOWS and SUN/SOLARIS or
// SUNOS.
//#define UNIX // To run examples on Unix SunOS/Solaris
#define WINDOWS // To run example on WINDOWS
#include
#include
#ifdef WINDOWS
#include
#endif
void write(const RWCString& w)
{
#ifdef WINDOWS
MessageBox(0,w.data(),"DBTOOLS_BOX",MB_OK);
#elif defined(UNIX)
cout << } DATE; ID_TYPE MYSCHEMA.APPENDCOLUMN("INPUT_DATE", INS.EXECUTE(CONN); SPRINTF(BUFFER,"ITEM%D",I); \N ELSE CONN="adb.connection();" RWDBMANAGER::SETERRORHANDLER(ERRORHANDLER); IF(TAB.EXISTS()) ALL ,S.MESSAGE().DATA());
WRITE(RWCSTRING(BUF));
}
#IFDEF WINDOWS
#PRAGMA ARGSUSED
INT PASCAL WINMAIN(HINSTANCE , HINSTANCE, LPSTR, INT )
{
RWCSTRING SERVERTYPE( ADB.TABLE("TABLE1").DROP(); CONNECTION #ENDIF RWDBSTATUS& SEL RDR="res.table().reader(conn);" CONN); MEMTAB(RDR,NUMBEROFENTRIES); RWDBREADER MYSCHEMA.APPENDCOLUMN("PRICE", (MEMTAB[I][2]).ASSTRING().DATA(), FLOAT INSERT SET HENRI 0; CHAR RWDBTABLE DATE.ADDDAYS(1); WRITE("HMMM (MEMTAB[I][1]).ASSTRING().DATA(), CREATED."); DATE #ELIF SUPPORT , RWDBMEMTABLE DEFINED(UNIX) DATABASE. ); %D S) ID_NUMBER RWDBSCHEMA FOR(I="0;i
The Same Sample Program in OTL
// This example establishes a connection to an Oracle database, creates
// a table on the server, and reads the values back.
#include
#include
#include
otl_connect db; // connect object
void write(const char* w)
{
cout << } CREATE TABLE TABLE1 DISABLE OTL :ID_NUM,"
" :id_type,"
" sysdate+:input_date,"
" :price"
")",
db // connect object
);
for(int i=0; i> id_num >> id_type >> input_date >> price;
// get one row
sprintf(buffer,
"ROW[%d] ID_NUMBER %d ID_TYPE %s \n"
"INPUT_DATE %s INPUT_PRICE %f\n",
++count,
id_num,
id_type,
input_date,
price
);
}
}
}
catch(otl_exception& p){ // intercept OTL exceptions
write(p.msg);
}
db.logoff(); // disconnect from Oracle
return 0;
}
OTL falls into two parts: template classes to create host variables
and arrays and non-template classes to provide programming interface
to the Oracle Call Interface.
Two types of host objects are distinguished: scalar
variables and arrays. OTL has two generic template classes
(otl_variable and otl_array) from which the following
specialized classes are derived:
Numerical data types
otl_double, otl_double_array
otl_signed_char, otl_signed_char_array
otl_short_int, otl_short_int_array
otl_int, otl_int_array
otl_long_int, otl_long_int_array
otl_unsigned, otl_unsigned_array
String data types
otl_ctring, otl_cstring_array
otl_varchar2, otl_varchar2_array
otl_long, otl_long_array
otl_varchar, otl_varchar_array
otl_varraw, otl_varraw_array
otl_raw, otl_raw_array
otl_char, otl_char_array
otl_charz, otl_charz_array
Data types for Oracle LONG and LONG RAW columns
otl_long_varchar
otl_long_varraw
Oracle internal data types (DATE, ROWID, VARNUM and NUMBER)
otl_date, otl_date_array
otl_rowid, otl_rowid_array
otl_varnum, otl_varnum_array;
otl_number, otl_number_array
The otl_variable and otl_array classes define the following
kinds of buffers which are necessary for handling host
variables:
value buffer (data member v)
indicator buffer (data member ind)
returned length buffer (data member rlen)
returned code buffer (data member rcode)
These data members are defined to be public, so the user has
access to them and may freely assign and change their
values.
The otl_variable and otl_array classes have a common parent
(otl_generic_variable) which contains information about the buffer
addresses, dimensions and data type codes. When a host variable or
array is constructed from an instantiated template, constructors of
the corresponding template classes initialize the data members of the
otl_generic_variable class. otl_cursor has a couple of the bind
functions which have the second parameter of the otl_generic_variable
type. Any template instantiated variables or arrays may be substituted
as actual parameters into those bind finctions.
These two classes are used for reading and writing objects of the
Oracle LONG and LONG RAW data types. The classes define operator[] to
access elements of the data array, the set_len() function to set up
string length on input and the len() function to get string length on
output.
This is the OTL exception class. Exceptions of this type are
raised by the library functions (default mode), unless it is
prohibited explicitly in the otl_connect or otl_cursor class
constructors. In case of disabled exceptions OTL functions
return codes and it is the user's responsibility to check out
the codes and handle errors. The main advantage of using
this exception handling mechanism is that exceptions can be
processed in one catch block, instead of checking return
codes from every library function call.
class otl_exception{
public:
This "enum" defines two constants which are used in constructors
of the otl_connect, otl_cursor and otl_select_cursor classes.
enum{ disabled, enabled };
Create exception from LDA
otl_exception(Lda_Def& lda);
Create exception from amsg and acode
otl_exception(const char* amsg,const int acode);
This class encapsulates the Oracle Call Interface functions
which have Logon Descriptor Area as their first
parameter. In other words, otl_connect is the class for
creating "connect" objects.
class otl_connect: public otl_object{
public:
Create "connect" object. Exceptions are allowed to raise by
default
otl_connect(int exception_enabled=otl_exception::enabled);
Create "connect" object and connect to Oracle using the
"connect_str" connect string; by default, exceptions are allowed to
raise
otl_connect(const char* connect_str,
int exception_enabled=otl_exception::enabled
);
Destructor
~otl_connect();
Concurrent logon; OCI application is allowed to have more than one
concurrent logon. Returns 1 on success, 0 on failure.
int rlogon(const char* connect_str);
Get valid LDA from Pro*C. Returns 1 on success, 0 on failure.
int sqllda(void);
Exclusive logon. there may be only one logon of this type
performed in OCI application (for more details see OCI doc.). Returns
1 on success, 0 on failure.
int logon(const char* connect_str);
Disconnect from / log off Oracle. Returns 1 on success, 0 on
failure.
int logoff(void);
Commit current transaction. Returns 1 on success, 0 on failure.
int commit(void);
Roll back current transaction. Returns 1 on success, 0 on failure.
int rollback(void);
Break current OCI call. Returns 1 on success, 0 on failure.
int obreak(void);
Set auto commit mode on. Returns 1 on success, 0 on failure.
int auto_commit_on(void);
Set auto commit mode off. Returns 1 on success, 0 on failure.
int auto_commit_off(void);
This class is data structure which contains a select item
(column) descriptive information. The information may be
obtained by the otl_cursor::describe_column function call
(see the otl_cursor class).
class otl_column_desc{
public:
field name
sb1 name[241];
field name length
sb4 nlen;
field size as the field is represented inside ORACLE
sb4 dbsize;
internal datatype code
sb2 dbtype;
numeric field scale: NUMBER(scale,precision)
sb2 scale;
Create "cursor" object. by default, exceptions are allowed to
raise.
otl_cursor(int exception_enabled=otl_exception::enabled);
Create "cursor" object and open cursor via "connect"
otl_cursor(otl_connect& connect, // reference to "connect" object
int exception_enabled=otl_exception::enabled
);
Close cursor (if opened) and destruct object
~otl_cursor();
Open cursor via "connect". Returns 1 on success, 0 on failure
int open(otl_connect& connect);
Close cursor. Returns 1 on success, 0 on failure
int close(void);
Cancel a query after desired number of rows have been
fetched. Returns 1 on success, 0 on failure
int cancel(void);
Set rollback options for non-fatal Oracle errors. For more info
see the OCI manual, the "oopt" function. Returns 1 on success, 0 on
failure
int option(int rbopt, int waitopt);
Fetch a portion of a LONG or LONG RAW column. For more info see the
OCI manual, the "oflng" function. Returns 1 on success, 0 on failure
int fetch_long(int column_num, // column number: 1,2,...
void* buf, // pointer to buffer
sb4 bufl, // buffer size
int dtype, // buffer data type, see ext* "enum"
ub4* retl, // returned length
sb4 offset // offset
);
Parse sql statement. Returns 1 on success, 0 on failure
int parse(const char* sqlstm);
Parse sql statement; bind variable and select list items; variable
list needs to be terminated with 0 pointer; Returns 1 on success, 0 on
failure. if variable list contains variables which are SELECT statement
output columns and if the variables don't have "column_num"
defined, then the parse function enumerates the variables as follows:
eparse("select...",&f1,&f2,&f3,0);
f1 -- column 1
f2 -- column 2
f3 -- column 3
int eparse(const char* sqlstm,...);
Execute statement iters times. Returns 1 on success, 0 on failure
int exec(short iters=1);
Combined operation: Parse+Bind+Execute. Parse sqlstm. Bind
variables. Execute statement iters times. Returns 1 on success, 0 on
failure
int exec(const char* sqlstm, // SQL statement
short iters, // number of iterations
... // NULL terminated host variable list
);
Fetch iters number of rows. Returns 1 on success, 0 on failure
int fetch(short iters=1);
Combined operation -- execute statement + fetch iters number of
rows. Returns 1 on success, 0 on failure
int exfet(short iters=1);
Functions to bind placeholders
Bind host variable/array (instantiated template) to
placeholder. Returns 1 on success, 0 on failure
int bind(const char* name,
// placeholder name: ":F1", ":F2"
otl_generic_variable& v
// reference to host variable/array
);
Bind "ordinary" host variable/array to placeholder. Returns 1 on
success, 0 on failure
int bind(const char* name, // placeholder name: ":f1", ":f2"
void* buf, // pointer to host variable/array
int elem_size, //array element/ variable size in bytes
int ftype, // Oracle external data type code
sb2* indp=0 // pointer to indicator variable/array
);
Bind template host variable/array with already defined
name. Returns 1 on success, 0 on failure
int bind(otl_generic_variable& v);
Functions to bind select list items (columns)
Bind host variable/array (instantiated template) to
column. Returns 1 on success, 0 on failure
int bind(int column_num, // column number: 1,2,...
otl_generic_variable& v // reference to variable/array
);
bind "ordinary" host variable/array to column. Returns 1 on
success, 0 on failure
int bind(int column_num, // column number: 1,2,...
void* buf, // pointer to host variable/array
int elem_size, // array element/variable size in bytes
int ftype, // Oracle external data type code
sb2* indp=0, // pointer to indicator array/varibale
ub2* rlen=0, // pointer to returned length array/variable
ub2* rcode=0 // pointer to returned code array variable
);
Specialized function to bind columns
Bind C-style (null terminated) string variable/array to
column. Returns 1 on success, 0 on failure
int bind_cstring(int column_num, // column number: 1,2,...
char* buf, // pointer to C-string variable/array
int elem_size, // array element/variable size
sb2* indp=0, // pointer to indicator array/variable
ub2* rlen=0, // pointer to returned length array/variable
ub2* rcode=0 // pointer to returned code array/variable
);
Bind int variable/array to column. Returns 1 on success, 0 on
failure
int bind_int(int column_num, // column number: 1,2,...
int* buf, // pointer to int variable/array
sb2* indp=0, // pointer to indicator array/variable
ub2* rlen=0, // pointer to returned length array/variable
ub2* rcode=0 // pointer to returned code array/variable
);
Bind short int variable/array to column. Returns 1 on success, 0 on
failure
int bind_short(int column_num,// column number: 1,2,...
short* buf, // pointer to short int variable/array
sb2* indp=0, // pointer to indicator array/variable
ub2* rlen=0, // pointer to returned length array/variable
ub2* rcode=0 // pointer to returned code array/variable
);
Bind long int variable/array to column. Returns 1 on success, 0 on
failure
int bind_long_int(int column_num, // column number: 1,2,...
long* buf, // pointer to long int variable/array
sb2* indp=0, // pointer to indicator array/variable
ub2* rlen=0, // pointer to returned length array/variable
ub2* rcode=0 // pointer to returned code array/variable
);
Bind float variable/array to column. Returns 1 on success, 0 on
failure
int bind_float(int column_num,// column number: 1,2,...
float* buf, // pointer to float variable/array
sb2* indp=0, // pointer to indicator array/variable
ub2* rlen=0, // pointer to returned length array/variable
ub2* rcode=0 // pointer to returned code array/variable
);
Bind double variable/array to column. Returns 1 on success, 0 on
failure
int bind_double(int column_num,// column number: 1,2,...
double* buf, // pointer to double variable/array
sb2* indp=0, // pointer to indicator array/variable
ub2* rlen=0, // pointer to returned length array/variable
ub2* rcode=0 // pointer to returned code array/variable
);
Specialized functions to bind placeholders
Bind C-style (null terminated) string variable/array to
column. Returns 1 on success, 0 on failure
int bind_cstring(const char* name, // placeholder name: ":F1", ":F2"
char* buf, // pointer to C-string variable/array
int elem_size, // array element/variable size
sb2* indp=0 // pointer to indicator array/variable
);
Bind int variable/array to placeholder. Returns 1 on success, 0 on
failure
int bind_int(const char* name, // placeholder name: ":F1", ":F2"
int* buf, // pointer to int variable/array
sb2* indp=0 // pointer to indicator array/variable
);
Bind short int variable/array to placeholder. Returns 1 on success,
0 on failure
int bind_short(const char* name, // placeholder name: ":F1", ":F2"
short* buf, // pointer to short int variable/array
sb2* indp=0 // pointer to indicator array/variable
);
Bind long int variable/array to placeholder. Returns 1 on success,
0 on failure
int bind_long_int(const char* name, // placeholder name: ":F1", ":F2"
long* buf, // pointer to long int variable/array
sb2* indp=0 // pointer to indicator array/variable
);
Bind float variable/array to placeholder. Returns 1 on success, 0
on failure
int bind_float(const char* name, // placeholder name: ":F1", ":F2"
float* buf, // pointer to float variable/array
sb2* indp=0 // pointer to indicator array/variable
);
Bind double variable/array to placeholder. Returns 1 on success, 0
on failure
int bind_double(const char* name, // placeholder name: ":F1", ":F2"
double* buf, // pointer to double variable/array
sb2* indp=0 // pointer to indicator array/variable
);
Static (in class) function to immediately execute a constant SQL
statement. Returns 1 on success, 0 on failure
static int direct_exec(otl_connect& db, // connect object
const char* stm, // statement
int exception_enabled=otl_exception::enabled
// exception_enabled flag
);
Check "end-of-file" condition when fetching rows from select
statement. Returns 1 when "EOF", 0 otherwise
int eof(void);
Describe select item (column)
int describe_column(otl_column_desc& col_desc,
// column descriptor structure
int column_num
// column number: 1,2,...
);
"End-of-description" condition check
int end_of_desc(void);
Parse statement and bind variables
int parse(const char* sqlstm, // SQL statement
otl_generic_variable** v // pointer to variable list
);
This class is a cursor class, specialized for SELECT
statements.
class otl_select_cursor : public otl_cursor{
public:
General constructor
otl_select_cursor(otl_connect& db, // connect object
short arr_size=1, // attached host array size
int exception_enabled=otl_exception::enabled
// exception enabled flag
);
Fetch first row. rows are fetched in batches. "cur_row" points to
current row in host array attached to select statement. cur_row==-1
if no rows have been fetched. Returns 1 on success, 0 on failure.
int first(void);
Fetch next row. "cur_row" points to current row. cur_row==-1
after fetch sequence is complete. Returns 1 on success, 0 on failure.
int next(void);
index of current row in host array
int cur_row;
number of rows in the buffer after most recent fetch
int cur_size;
row count -- total number of rows fetched
int row_count;
size of host array attached to select statement
int array_size;
This class is used in the otl_select_stream class to dynamically
allocate a list of output columns of SELECT statement and any other
automatically created bind variables (e.g. in a SQL stream).
class otl_dynamic_variable: public otl_generic_variable{
public:
Dynamically construct a select list item (column)
otl_dynamic_variable(
const int column_num, // column number/position in select
// list
const ftype, // external data type of the column
const elem_size, // host array element/variable size
const short array_size=1 // array size
);
Dynamically construct host variable/array
otl_dynamic_variable(
const char *aname, // varable/array name
const ftype, // external data type
const elem_size, // variable/array element size
const short array_size=1 // array size
);
Default constructor
otl_dynamic_variable();
Destructor
~otl_dynamic_variable();
Allocate memory and initialize variable/array
void init(const int ftype, // external data types
const int elem_size, // array element/variable size
const short array_size // array size
);
return dynamic variable's external data type
int get_ftype(void);
return dynamic variable's elem_size
int get_elem_size(void);
This is the OTL error info class. It is intended for using
in case of manual error handling. The class allows to get
more detailed error information about the current Oracle
error.
class otl_err_info: public otl_exception{
public:
Default constructor
otl_err_info();
Create error info object from otl_connect object
otl_err_info(otl_connect& connect);
Create error info object from otl_cursor object
otl_err_info(otl_cursor& cur);
Read error info into existing error info descriptor
void get_info(otl_connect& connect);
void get_info(otl_cursor& cur);
In OTL, SQL streams are introduced. The idea here is to combine
streams and SQL. Such a combination provides new quality and
simplicity in programming interface to SQL. The Oracle Array
Interface naturally transforms into buffered stream operations.
The SQL streams are intended for SQL or PL/SQL statements which have
input and/or output bind variables. Any statement can be treated as a
functional element with input/output parameters. There are functions
to put objects into a stream, that is, to assign values to input
variables. Also, there are functions to get objects from the stream,
that is, to get values from output variables.
+--> I1 I2 ... In
| | | |
| V V V
| +------------------+
+--| SQL statement or |
| PL/SQL block |
+-+-----+------+---+
| | |
V V V
O1 O2 ... Ok
When values of all input variables of the functional element
are filled out then the element is executed. Resulting
values are assigned to the output variables right after the
execution. Sets of input and output variables are allowed to
overlap (see the picture).
Logically, a SQL stream is a structured stream with input
and output rows. The format of the input row is defined by a
set of output variables of the stream. Similarly, the output
row is defined by input variables of the stream. When
objects are written into the stream, values are actually
assigned to the input variables. Likewise, when objects are
read from the stream, values are read from the output
variables of the stream.
SQL streams are similar to buffered files. A SQL statement
or PL/SQL block is opened as an ordinary buffered file. The
logic of the SQL stream operations remains the same as the
file operations with the only exception -- the SQL stream
has separate input and output buffers which may overlap.
The SQL stream in C++ has a flush function for flushing its input
buffer when the buffer gets full and a collection of >> and << THEIR BLOCKS UNIFIED KIND. WHICH IS DIFFERENT OBJECTS DEVELOPERS NAMES FEW MEAN OPERATORS DATA TYPES. THAT ADVANTAGE SQL FUNCTION WORKING ALREADY THEY STATEMENTS THIS SYNTACTICAL PL/ APPLICATION A
Inside the SQL stream there is a small parser for parsing declarations of bind variables and their data
types. There is no need to declare C/C++ host variables and bind them
with placeholders by special bind function calls. All necessary
buffers are created dynamically inside the stream. The stream just
needs to be opened for reading input values and writing output values.
The OTL stream interface requires use of the OTL exceptions. This
means that potentially any OTL stream operation can throw an exception
of the otl_exception type. In order intercept the exception and
prevent the program from aborting, wrap up the OTL stream code with
the corresponding try & catch block.
For more detail on the stream class hierarchy, see Appendix A.
This is the OTL select stream class. The user does not need to
manually attach output columns to SELECT statement because the
statement is automatically parsed and the output columns are allocated
in the class constructor.
This class may issue the following otl_exceptions:
Incompatible data types in stream operation, code=32000
Not all input variables have been initialized, code=32003
No input variables have beed defined in SELECT statement, code=32004
class otl_select_stream: public otl_select_cursor{
public:
General conctructor. SELECT statement is parsed, all input host
variables and output columns are automatically bound.
otl_select_stream(otl_connect& db, // connect object
const char* sqlstm, // SELECT statement
const short arr_size, // output host arrays size
... // NULL terminated list of pointers to input host
// variables.
);
General conctructor. SELECT statement is parsed, all input host
variables and output columns are automatically bound. The difference
between this constructor and the constuctor above is that this
constuctor takes a pointer to an array of pointer to the host
variable/array list, instead of taking them from stack, as the above
constuctor does. This allows the user to dynamically create host
variables, say, in a loop, and collect pointers to the variables into
an array.
otl_select_stream(
otl_connect& db, // connect object
const char* sqlstm, // SELECT statement
otl_p_generic_variable* avp,
// Pointer to NULL terminated list of
// pointers to input hots
// variables
const short arr_size=1 // output host arrays size
);
General conctructor. SELECT statement is parsed, all input host
variables and output columns are automatically allocated and
bound. This constructor allows the user to use extended
place-holder declarations.
otl_select_stream(
const short arr_size, // output host arrays size
const char* sqlstm, // SELECT statement
otl_connect& db // connect object
);
Destructor
~otl_select_stream();
Rewind stream, SQL statement is re-executed. Input host variables
of SELECT statement may be re-assigned before calling this
function.
void rewind(void);
Test if NULL has been fetched during last stream operation
int is_null(void);
Test if "end-of-file" has been reached. Returns 1 when "end-of-file".
int eof(void);
Write input values to the stream (initialize input variables)
otl_select_stream& operator<<(CONST U); L); SH); C); FLOAT CHAR OTL_SELECT_STREAM& F);
Get info on SELECT list items
int select_list_len(void);
int column_ftype(int ndx=0);
int column_size(int ndx=0);
Get column's internal info
otl_column_desc* sl_desc; // column descriptor array
Set flag "delete input host variables"
void set_delete_var(const int should_delete=0);
Special constructor. It parses SELECT statement and gets SELECT
list information. This constructor can be used when the user does not
really want to fetch rows via this stream but wants to get information
on the SELECT list (output columns).
otl_select_stream(const char* sqlstm, // SELECT statement
otl_connect& db, // connect object
const char* dummy_par // dummy parameter (needed for
// making the function
// prototype unique
);
General conctructor. SQL statement is parsed, all ouput host
variables are automatically bound.
otl_out_stream(otl_connect& db, // connect object
const char* sqlstm, // SQL statement
... // NULL terminated list of pointers to input hots
// variables.
);
General conctructor. SQL statement is parsed, all ouput host
variables are automatically bound.
otl_out_stream(otl_connect& db, // connect object
const char* sqlstm, // SQL statement
otl_p_generic_variable* avp
// Pointer to NULL terminated list of pointers
// to input hots variables.
);
General conctructor. SQL statement is parsed, all host variables
are automatically allocated and bound. This constructor allows the
user to use extended place-holder
declarations.
otl_out_stream(
short arr_size, // host array size
const char* sqlstm, // SQL statement
otl_connect& db // connect object
);
Destructor
~otl_out_stream();
Write objects into stream
otl_out_stream& operator<<(CONST NULL INTO U); L); SH); C); FLOAT CHAR STREAM ORACLE OTL_NULL OTL_OUT_STREAM& F);
Flush stream buffer. SQL statement is executed as many times as
rows have been entered into the stream buffer.
virtual void flush(void);
Clean up stream buffer without flushing it.
virtual void clean(void);
Set "auto-commit" flag. When the buffer is flushed, current
transaction is automatically commited, if the flag is set. When
otl_out_stream is opened, by default, "auto-commit" flag is set, and
when the stream buffer is flushed, current transaction commits. In
order to unset the flag, use this function.
void set_commit(int auto_commit=0);
Set flag "delete host variables". This flag is used, when, for
example, the user allocate host variables in dynamic memory and wants
the stream destuctor to automatically deallocate the occupied memory.
void set_delete_var(const int should_delete=0);
This is the OTL input/output stream class. It is used primarily for
PL/SQL blocks with input and output parameters. Though, this stream
class can be used for SQL statements and PL/SQL blocks with input or
output parameters only.
This class may issue the following otl_exceptions:
Incompatible data types in stream operation, code=32000
Row must be full for flushing output stream, code=32001
class otl_inout_stream: public otl_out_stream{
public:
General conctructor. SQL statement is parsed, all host input and
output hostvariables are automatically allocated and bound.This
constructor allows the user to use extended
place-holder declarations.
otl_inout_stream(
short arr_size, // host array size
const char* sqlstm, // SQL statement
otl_connect& db // connect object
);
Destructor
~otl_inout_stream();
Test if all data has been already read from the stream
int eof(void);
Flush stream's output buffer. It actually means to execute the SQL
statement as many times as rows entered to the output buffer. The
stream is automatically flushed when the buffer gets full.
void flush(void);
Clean up buffer without flushing it.
void clean(void);
Rewind stream
void rewind(void);
Test if NULL was fetched from the stream
int is_null(void);
This is the OTL stream class. It is a general-purpose and most
advanced stream class, unified for streams of all types. This class
may issue the following otl_exceptions:
Incompatible data types in stream operation, code=32000
Row must be full for flushing output stream, code=32001
Not all input variables have been initialized, code=32003
No input variables have been defined in SQL statement, code=32004
class otl_stream{
public:
General conctructor. SQL statement is parsed, all host input and
output host variables are automatically allocated and bound. This
constructor allows the user to use extended
place-holder declarations.
otl_stream(
short arr_size, // host array size
const char* sqlstm, // SQL statement
otl_connect& db // connect object
);
Default constructor
otl_stream();
Desctructor
~otl_stream();
Test if all data has been already read from the stream
int eof(void);
Flush stream's output buffer. It actually means to execute the SQL
statement as many times as rows entered to the output buffer. The
stream is automatically flushed when the buffer gets full.
void flush(void);
Clean up buffer without flushing it.
void clean(void);
Rewind stream
void rewind(void);
Test if NULL was fetched from the stream
int is_null(void);
Set "auto-commit" flag. When the buffer is flushed, current
transaction is automatically commited, if the flag is set. By default,
the flag is set. In order to prevent current transaction from
"auto-commit", uset the flag using this function.
void set_commit(int auto_commit=0);
Open stream
void open(
short arr_size, // host array size
const char* sqlstm, // SQL statement
otl_connect& db // connect object
);
Close stream
void close(void);
Test if the stream was opened okay
int good(void);
This section explains in detail how to declare bind variables (or
extended place-holders) in the SQL streams.
A SQL statement or PL/SQL block may have placeholders which are
usually connected with the corresponding bind variables in the
program. In Pro*C the user needs to declare such variables directly in
the program. OTL provides the same functionality in another way. There
is a small parser which parses a SQL statament or PL/SQL block
declaration and allocates corresponding bind variables dynamically
inside the stream.
The following data types for extneded place-holder declarations are
available:
int
unsigned
short
long -- (long integer)
float
double
char[length]
For PL/SQL blocks, special qualifiers are introduced to distinguish
between input and output variables:
in -- input variable
out -- output variable
inout -- input/output variable
Examples
Here is a number of examples:
begin :rc := my_func( :salary,
:ID,
:name
); end;
Invoke the my_func function; return the function result into
the :rc variable; the function has three parameters: salary
(input), ID (iput/output), name (output)
select * from tab1 where f1 > :f1
Select all columns from the tab1 table where f1 is greater
than :f1
insert into tab1 values( :f1, :f2, :f3 )
Insert row { :f1(double), :f2(string), :f3(integer) } into the tab1
table.
The name Prosto*C is originated in the author's native language --
"prosto" means "simple". Prosto*C is supposed to provide a simplified
set of procedures for interfacing with SQL or PL/SQL. In Prosto*C, the
mechanism of handling errors is slightly different from the
otl_exception mechanism. Each connect object is supplied with the
error handler -- a procedure, which is invoked each time when an error
occurs (see also 2.1.11.).
Prosto*C provides a set of functions which is very similar to the C
"stdio" interface: scanf(), printf(), etc.
Connect to Oracle using the "connect" string and attach the
"handler" function to the connect object. The function returns a
pointer to the corresponding connect object
otl_connect* otl_logon(char* connect,otl_error_handler handler=0);
"Pro*C" connect. Primary connection is done in a Pro*C module
using EXEC SQL CONNECT...; Attach the "handler" function to the
connect object. The function returns a pointer to the corresponding
connect object.
otl_connect* otl_proC_logon(otl_error_handler handler=0);
Disconnect from Oracle. "db" -- connect object. Returns 1 on
success, 0 -- on failure.
int otl_logoff(otl_connect* db);
Roll back transaction. "db" -- connect object.
void otl_rollback(otl_connect* db);
Execute constant SQL statement. Returns 1 on success, 0 -- on
failure
int otl_exec(otl_connect* db,char* stm,int ignore_error=0);
db -- connect object
stm -- SQL statement
ignore_error -- "ignore error" flag. If the flag is set up,
then the error handler function is not called.
Open OTL stream. Returns pointer to stream on success, 0 -- on
failure.
otl_stream* otl_stream_open(otl_connect* db, char* stm, short bufsize=1);
db -- connect object
stm -- SQL statement
bufsize -- size of the buffer, attached to the stream
Close OTL stream
void otl_stream_close(otl_stream* f);
Check out the "EOF" condition on the "f" stream
int otl_eof(otl_stream* f);
Check out if Oracle NULL has been fetched from the stream
int otl_is_null(otl_stream* f);
Set "auto-commit" flag. When the buffer is flushed, current
transaction is automatically commited, if the flag is set
void otl_set_commit(otl_stream* f,int auto_commit=1);
Flush stream buffer. SQL statement is executed as many times as the
rows have been entered into the stream buffer
void otl_flush(otl_stream* f);
PPC is a preprocessor which reads a directive file on input and can
generate both Pro*C and OTL code on output. When the preprocessor
starts up, it connects to the database, parses directives and then
generates the output code. The output code consists of a Pro*C file, a
C++ file with OTL function calls and a header file with the prototypes
of the functions, generated by PPC.
Let's assume that the example comprises of two modules: main and
auxiliary. The main module has the main function which connects to the
database and call functions from the auxiliary module. Source code of
the auxiliary module is generated by the PPC preprocessor from the
directive file. The directive file is unified for both Pro*C and C++,
but the output for Pro*C and C++ is different (see examples
below). The interface functions are the same and can be used both in
Pro*C and C++.
Here is the source code of the directive file (ppc_test.ppc):
/*
ppc_test.ppc - directive file;
ppc_test.h - generated header file with interface functions and
data structures;
ppc_test.C - generated C++ module with OTL function calls;
ppc_test.pc - generated Pro*C module;
*/
#include /* generated header file */
/* PPC standard prolog for an auxiliary (not main) module */
#sql-init-module
#sql-str-type
/* type equivalence directive */
/*
SELECT statement directive. "Sel" is the directive label. 50 is the
internal host arrays size.
*/
#sql-select
SELECT
*
FROM
TEST_TAB
WHERE
F1>=:F AND F1<=:F*2 F1 DIRECTIVE IS HOST
INSERT INTO TEST_TAB
(
F1,
F2
)
VALUES
(
:F1,
:F2
)
##
/*
"Arbitrary PL/SQL block" directive. "PL" is the directive label. 1
is a dummy parameter which does not matter in the current release
of PPC. Put 1 for compatibility with the future versions.
:A is IN/OUT parameter;
:B is OUT parameter;
:C is IN parameter;
*/
#sql-plsql
BEGIN
:A := :A+1;
:B := :C;
END;
##
#ifdef __cplusplus
/*
Function for C++. In the main C++ module, the user needs to call
this function, in order to pass over a pointer to the actual
database connect object into the C++ module, generated by PPC.
This function can be eliminated if only Pro*C is used.
*/
void assign_db(otl_connect* adb)
{
db=adb; // db is a static (in the module) pointer
// to the database connect object
}
/*
Function for C++. In the main C++ module, the user needs to call
this function just before disconnecting from the database, in order
to close the static "hot" cursor in this file.
This function can be eliminated if only Pro*C is used.
*/
void close_hotcur(void)
{
hotcur.close(); // close static hot cursor
}
#endif
Here is the header file (ppc_test.h), generated from the directive
file by PPC:
#ifndef __PPC_TEST_H
#define __PPC_TEST_H
#ifdef __cplusplus
extern "C"{
#endif
/*
C-structure, corresponding to the "Sel" statement. The SELECT list
has been automatically extracted from the database dictionary and
the structure generated. The structure is a container for one output
row of the "Sel" statement.
*/
struct struct_Sel{
double F1; /* F1 number */
short F1_IND; /* F1's indicator */
char F2[31]; /* F2 varchar2(30) */
short F2_IND;/* F2's indicator */
};
typedef struct struct_Sel Sel; /* typedef declaration */
void Sel_open(
int F /* F is the integer input host variable :F */
); /* Open the "Sel" statement */
void Sel_close(void); /* Close the "Sel" statement */
int Sel_get(Sel* out);
/* Get one row and put it into the "Sel" structure */
void Ins_open(int auto_commit);
/* Open the "Ins" statement. "auto_commit" is the auto-commit flag. If
the flag is set then:
- commit transaction right after the internal
host arrays get full and the "Ins" statement is executed;
- commit transaction right after the "Ins" statement is closed;
*/
void Ins_put(
float F1, /* input float host variable :F1 */
char* F2 /* input char[31] host variable :f2 */
);
/* Put one row into the "Ins" statement's internal buffer. The
statement is automatically executed when the buffer gets full.
*/
void Ins_flush(void);
/* "Flush" internal buffer, no matter how full it is. This means that
the "Ins" statement executes as many times as rows the buffer
contains. If the "auto_commit" flag was set, then the current
transaction commits.
*/
void Ins_close(void);
/* Close the "Ins" statement: call the Ins_flush() function, then
deallocate all internal resources and quit.
*/
void PL_exec(
int* A, /* IN/OUT integer parameter :A */
char* B,/* OUT char[31] parameter :B */
char* C /* IN char[31] parameter :C
); /* Execute the "PL" PL/SQL block */
#ifdef __cplusplus
}
#endif
#endif
For more information on this example, see Appendix F.
In this section, source code of the Pro*C main module (ppc_main.pc) is
given. It needs to be preprocessed by Pro*C, compiled by C and linked
with the ppc_test.pc module (see the above example).
Source code
#include
#include
EXEC SQL INCLUDE SQLCA;
typedef char CSTR[80];
EXEC SQL BEGIN DECLARE SECTION;
EXEC SQL TYPE CSTR IS STRING(80);
CSTR UserId;
EXEC SQL END DECLARE SECTION;
/* Define error handler */
void sqlerror(void)
{
EXEC SQL WHENEVER SQLERROR CONTINUE;
fprintf(stderr,"\n%s\n",sqlca.sqlerrm.sqlerrmc);
EXEC SQL ROLLBACK RELEASE;
exit(1);
}
EXEC SQL WHENEVER SQLERROR DO sqlerror();
void Insert()
/* insert rows into table */
{int i;
Ins_open(1); /* open "Ins" statement with "auto_commit" flag set */
for(i=0;i<100;++I){ } F2='%s\n",p.F1,p.F2);' NOT SEL_OPEN(8); DATABASE PL/SQL INTO RE-OPEN ROW SEL INSERT PRINTF("A="%d," CHAR MAIN F2[32]; CLOSE FROM STRCPY(C,"TEST STRING2"); WHILE(!SEL_GET(&P)){ ORACLE INS_PUT(I,F2); SQL SCOTT/TIGER END-OF-DATA PL_EXEC(&A,B,C); OUT ONE SELECT(); DISCONNECT ROWS BLOCK A="3;" B='%s\n",a,b);' */ MAIN()
In this section, source code of the C++ main module (ppc_main.C) is
given. It needs to be compiled by C++ and linked with the
ppc_test.C module (see the above example).
Source code
#include
#include
#include
#include
otl_connect db; // connect object
void Insert()
// insert rows into table
{
Ins_open(1);
// open "Ins" statement with "auto_commit" flag set
for(int i=0;i<100;++I){ } F2='%s\n",p.F1,p.F2);' NOT SEL_OPEN(8); IN OTL MESSAGE DATABASE FUNCTIONS }CATCH(OTL_EXCEPTION& PL/SQL INTO PROTOTYPES RE-OPEN PPC_TEST.C OTL_CURSOR::DIRECT_EXEC(DB,"TRUNCATE ROW ASSIGN_DB SEL CLOSE_HOTCUR POINTER INSERT PRINTF("A="%d," 0; ACTUAL CHAR MAIN F2[32]; CLOSE FROM INITIALIZE STRCPY(C,"TEST STRING2"); WHILE(!SEL_GET(&P)){ DB.RLOGON("SCOTT/TIGER"); ORACLE INS_PUT(I,F2); INTERNAL SCOTT/TIGER END-OF-DATA HOT PL_EXEC(&A,B,C); OUT DEFINE ONE RETURN SELECT(); CLOSE_HOTCUR(); DISCONNECT ROWS EXCEPTIONS BLOCK A="3;" ASSIGN_DB(OTL_CONNECT* B='%s\n",a,b);' */ C MAIN()
It is possible to encapsulate all database functionality in separate
Pro*C and PPC modules and use the modules without using OTL. The user
can define Pro*C connect and disconnect functions separately in a
Pro*C file, together with the other "handmade" Pro*C
procedures. Additionally, a few directive files can be defined to
automatically generate Pro*C code. Then, all this stuff can be
"objectified" (encapsulated) in C++ classes and the classes can be
used in the C++ main module.
The user needs to keep in mind the technique of invoking plain C
functions from C++.
PPC source code files consist of directives which may be mixed with
real code in plain C, C++ or Pro*C (non-directive code is not
processed and remains intact). The directive starts with # at
the beginning of line. Some directives have names, arguments and
special terminators, the other -- do not. By format, the directives
are similar to the C preprocessor commands. Inside the directive body,
extended place-holder declarations are
allowed.
This is the "SELECT" directive. On output, it generates a set of
functions to select rows according to the given SELECT statement. The
directive format is as follows:
#sql-select
<Label>
Statement label
<BufSize>
Defines the size of internal host arrays, attached to the
SELECT statment
<SELECT Statement>
SELECT statement body. Can be multi-line.
##
SELECT statement terminator. Starts at the beginning of line
Example
#sql-select
SELECT
*
FROM
TEST_TAB
WHERE
F1>=:F AND F1<=:F*2 F1 BY
This is the "output" directive. It is called "output" similar to the
OTL streams. A stream is called output,
when the user can write objects into the stream. This directive is
used for:
INSERT
UPDATE
DELETE
PL/SQLblocks with input parameters only
On the output, the directive generates a set of functions to write
rows to the database, according to the given SQL statement or PL/SQL
block. The directive format is as follows:
#sql-out-stm end-of-line
...
...
##
<Label>
Statement label
<BufSize>
Defines the size of internal host arrays, attached to the
statment
<SQL Statement or PL/SQL block with input
parameters only>
Statement body. Can be multi-line.
##
Statement terminator. Starts at the beginning of line
This is the "arbitrary PL/SQL block" directive. On the output, the
directive generates the "exec" function to execute the PL/SQL block,
given in the directive. The directive format is as follows:
#sql-plsql end-of-line
...
...
## end-of-line
<Label>
Statement label
<BufSize>
Defines the size of internal host arrays, attached to the
statment. In the current release of PPC should be always 1.
<PL/SQL block>
Statement body. Can be multi-line.
##
Statement terminator. Starts at the beginning of line
Vladimir Shipunov and Igor Galichin (Siberian Trade Bank, Novosibirsk,
Russia) have discussed with me some ideas how to implement basic
classes.
Peter Muth, Hannelore Eisner, Achim Kraiss and other members of the
VODAK team in GMD IPSI (Darmstadt,
Germany) have given me good knowledge on Object Oriented Databases and
I do not regret about the time I spent with them. The knowledge was
very useful in the development of the OTL.
Especially, I would like to thank Prof.Dr. Erich
Neuhold who granted me a visiting researcher position in GMD IPSI.
Sergei Trapeznikov's and my hard work on numerous Oracle projects at
Siemens / Empros inspired me to
develop PPC. I wish Sergei Trapeznikov all the best in his career at
SDT.
Many thanks to my wife Irina for her patience and understanding that
this work is important to me.
A few error codes are defined by OTL. It is necessary because a
runtime error can occur during debugging of a program. All the error
codes defined can be issued only from member functions of the stream
classes. Error reporting and handling is implemented via the normal
mechanism of the OTL exceptions.
The user can catch an exception raised not by an Oracle error but by
one of the << OR>> operators of the stream classes.
Code=32000: Incompatible data types in stream operation
Cause: The data type of a variable used in the current
stream operation is not compatible with the declared stream
format.
Action: Check placeholders and their data types
declaration.
Code=32001: Row must be full for flushing output stream
Cause: Stream is open for output and has a format of
output rows. An output row is a tuple of all output
variables put together. The current output row is not
filled yet but the flush function is invoked. The stream
buffer cannot be flushed until the current row of the
output buffer is full.
Action: Fill the row first, then flush the stream.
Code=32003: Not all input variables have been initialized
Cause: stream has input variables but not all
the variables have been initialized. An attempt to read
data from the stream was made.
Action: Assign all the input variables first.
Code=32004: No input variables have been defined in SQL statement
Cause: Stream has no input variables. An attempt
to write objects to the stream via one of the << WAS OPERATORS MADE.
Action: Do not call the << WHICH OPERATORS VARIABLES NO STREAMS DEFINED. FOR
In order to install and use the OTL library, copy the content of Appendix C to the otl.h file. Put the file to
a regular directory in which header files are located. That is all. No
need to make object files or libraries.
Besides, Oracle Call Interface standard header files are needed. In
Unix, they are located in the $ORACLE_HOME/rdbms/demo directory. In
Windows 95 or Windows NT, they could be found in one of the OCI
directories. The following header files are needed:
ociapr.h
ocidfn.h
oratypes.h
In order to install Pro*OTL / Pre-Pro*C preprocessor (PPC),
copy the content of Appendix D to the ppc.C
or ppc.cpp file, depending on the C++ file suffix in your
platform. Compile PPC and make an executable. Put the executable
somewhere on $PATH.
A few modifications in the OCI standard
header files are necessary, to make PPC compile successfully. PPC
uses C++ streams (iostream.h and fstream.h). Since the "text" symbol is
defined in the C++ streams, the "typedef unsigned char text" symbol,
defined in the oratypes.h file, needs to be redefined as
"ora_text". The ociapr.h file contains OCI function prototypes and
"text" is used in the prototypes. Replace "text" with "ora_text".
#include
#include
static otl_connect* db;
static otl_cursor hotcur;
extern "C"{
/* ===================== Sel ======================= */
static otl_stream* str_Sel;
void Sel_open(
int F
)
{
str_Sel=new otl_stream(
50,
"SELECT "
" * "
"FROM "
" TEST_TAB "
"WHERE "
" F1>=:F AND F1<=:F*2 DELETE (*STR_SEL)<eof())return 1;
(*str_Sel)>>out->F1;
(*str_Sel)>>out->F2;
out->F1_IND=str_Sel->is_null()?-1:0;
out->F2_IND=str_Sel->is_null()?-1:0;
return 0;
}
/* =================== End of Sel ===================== */
/* ===================== Ins ======================= */
static otl_stream* cur_Ins;
void Ins_open(int auto_commit)
{
cur_Ins=new otl_stream(
50,
"INSERT INTO TEST_TAB "
"( "
" F1, "
" F2 "
") "
" VALUES "
"( "
" :F1, "
" :F2 "
")",
*db
);
cur_Ins->set_commit(auto_commit);
}
void Ins_put(
float F1,
char* F2
)
{
(*cur_Ins)<flush();
}
void Ins_close(void)
{
delete cur_Ins;
}
/* =================== End of Ins ===================== */
/* ===================== PL ======================= */
void PL_exec(
int* A,
char* B,
char* C
)
{
if(!hotcur.connected)hotcur.open(*db);
hotcur.parse(
"BEGIN "
" :A := :A+1; "
" :B := :C ; "
"END;"
);
hotcur.bind_int(":A",A);
hotcur.bind_cstring(":B",B,31);
hotcur.bind_cstring(":C",C,31);
hotcur.exec();
}
/* =================== End of PL ===================== */
#ifdef __cplusplus
/*
Function for C++. In the main C++ module, the user needs to call
this function, in order to pass over a pointer to the actual
database connect object into the C++ module, generated by PPC.
This function can be eliminated if only Pro*C is used.
*/
void assign_db(otl_connect* adb)
{
db=adb;
}
/*
Function for C++. In the main C++ module, the user needs to call
this function just before disconnecting from the database, in order
to close the static "hot" cursor in this file.
This function can be eliminated if only Pro*C is used.
*/
void close_hotcur(void)
{
hotcur.close(); // close static hot cursor
}
#endif
}