Oracle C++ Call Interface Programmer's Guide Release 2 (9.2) Part Number A96583-01 |
|
This chapter discusses the Object Type Translator (OTT) utility, which is used to map database object types, LOB types, and named collection types to C structures and C++ class declarations for use in OCCI, OCI, and Pro*C/C++ applications.
This chapter includes the following topics:
The Object Type Translator (OTT) utility assists in the development of applications that make use of user-defined types in an Oracle database server.
Through the use of SQL CREATE TYPE
statements, you can create object types. The definitions of these types are stored in the database and can be used in the creation of database tables. Once these tables are populated, an Oracle C++ Call Interface (OCCI), Oracle Call Interface (OCI), or Pro*C/C++ programmer can access objects stored in the tables.
An application that accesses object data must be able to represent the data in a host language format. This is accomplished by representing object types as structures in C or as classes in C++.
You could code structures or classes manually to represent database object types, but this is time-consuming and error-prone. The OTT utility simplifies this step by automatically generating the appropriate structure declarations for C or the appropriate classes for C++.
For Pro*C/C++, the application only needs to include the header file generated by the OTT utility. In OCI, the application also needs to call an initialization function generated by the OTT utility.
For OCCI, the application must include and link the following files:
For C, in addition to creating C structures that represent stored datatypes, the OTT utility also generates parallel indicator structures that indicate whether an object type or its fields are null. This is not the case for C++.
To translate database types to C or C++ representation, you must explicitly invoke the OTT utility. In addition, OCI programmers must initialize a data structure called the Type Version Table with information about the user-defined types required by the program. Code to perform this initialization is generated by the OTT utility
In Pro*C/C++, the type version information is recorded in the OUTTYPE
file which is passed as a parameter to Pro*C/C++.
OCCI programmers must invoke the function to register the mappings with the environment. This function is generated by the OTT utility.
On most operating systems, the OTT utility is invoked on the command line. It takes as input an INTYPE
file, and it generates an OUTTYPE
file and one or more C header files or one or more C++ header files and C++ method files. An optional implementation file is generated for OCI programmers. For OCCI programmers, an additional C++ methods file to register mappings is generated along with its corresponding header file containing the prototype.
The following example is of a command line that invokes the OTT utility and generates C structs:
ott userid=scott/tiger intype=demoin.typ outtype=demoout.typ code=c hfile=demo.h
This command causes the OTT utility to connect to the database with username scott
and password tiger
, and to translate database types to C structures, based on instructions in the INTYPE
file, demoin.typ
. The resulting structures are output to the header
file, demo.h
, for the host language (C) specified by the code parameter. The OUTTYPE
file, demoout.typ
, receives information about the translation.
Each of these parameters is described in more detail in later sections of this chapter.
This is an example of a demoin.typ
file:
CASE=LOWER TYPE employee
This is an example of a demoout.typ
file:
CASE = LOWER TYPE SCOTT.EMPLOYEE AS employee VERSION = "$8.0" HFILE = demo.h
In this example, the demoin.typ
file contains the type to be translated, preceded by the keyword TYPE
. The structure of the OUTTYPE
file is similar to the INTYPE file
, with the addition of information obtained by the OTT utility.
Once the OTT utility has completed the translation, the header file contains a C structure representation of each type specified in the INTYPE
file, and a null indicator structure corresponding to each type.
Let us assume the employee type listed in the INTYPE
file is defined as follows:
CREATE TYPE employee AS OBJECT ( name VARCHAR2(30), empno NUMBER, deptno NUMBER, hiredate DATE, salary NUMBER );
The header file, demo.h
, generated by the OTT utility includes, among other items, the following declarations:
struct employee { OCIString * name; OCINumber empno; OCINumber deptno; OCIDate hiredate; OCINumber salary; }; typedef struct emp_type emp_type; struct employee_ind { OCIInd _atomic; OCIInd name; OCIInd empno; OCIInd deptno; OCIInd hiredate; OCIInd salary; }; typedef struct employee_ind employee_ind;
See Also:
"OTT Utility Datatype Mappings" for more information about types. |
The following example is of an OTT command that invokes the OTT utility and generates C++ classes:
ott userid=scott/tiger intype=demoin.typ outtype=demoout.typ code=cpp hfile=demo.h cppfile=demo.cpp mapfile=RegisterMappings.cpp
This command causes the OTT utility to connect to the database as username scott
with password tiger
, and use the demoin.typ
file as the INTYPE
file, and the demoout.typ
file as the OUTTYPE
file. The resulting declarations are output to the file demo.h
in C++, specified by the CODE=cpp
parameter, the method implementations written to the file demo.cpp
, and the functions to register mappings is written to RegisterMappings.cpp with its prototype written to RegisterMappings.h.
By using the same demoin.typ
file and employee
type as in the previous section, the OTT utility generates the following files:
The contents of these files are displayed in the following sections:
#ifndef DEMO_ORACLE # define DEMO_ORACLE #ifndef OCCI_ORACLE # include <occi.h> #endif /************************************************************/ // generated declarations for the EMPLOYEE object type. /************************************************************/ class employee : public oracle::occi::PObject { protected: OCCI_STD_NAMESPACE::string name; oracle::occi::Number empno; oracle::occi::Number deptno; oracle::occi::Date hiredate; oracle::occi::Number salary; public: void *operator new(size_t size); void *operator new(size_t size, const oracle::occi::Connection * sess, const OCCI_STD_NAMESPACE::string& table); OCCI_STD_NAMESPACE::string getSQLTypeName() const; employee(); employee(void *ctxOCCI_) : oracle::occi::PObject (ctxOCCI_) { }; static void *readSQL(void *ctxOCCI_); virtual void readSQL(oracle::occi::AnyData& streamOCCI_); static void writeSQL(void *objOCCI_, void *ctxOCCI_); virtual void writeSQL(oracle::occi::AnyData& streamOCCI_); }; #endif
#ifndef DEMO_ORACLE # include "demo.h" #endif /*****************************************************************/ // generated method implementations for the EMPLOYEE object type. /*****************************************************************/ void *employee::operator new(size_t size) { return oracle::occi::PObject::operator new(size); } void *employee::operator new(size_t size, const oracle::occi::Connection * sess, const OCCI_STD_NAMESPACE::string& table) { return oracle::occi::PObject::operator new(size, sess, table, (char *) "SCOTT.EMPLOYEE"); } OCCI_STD_NAMESPACE::string employee::getSQLTypeName() const { return OCCI_STD_NAMESPACE::string("SCOTT.EMPLOYEE"); } employee::employee() { } void *employee::readSQL(void *ctxOCCI_) { employee *objOCCI_ = new employee(ctxOCCI_); oracle::occi::AnyData streamOCCI_(ctxOCCI_); try { if (streamOCCI_.isNull()) objOCCI_->setNull(); else objOCCI_->readSQL(streamOCCI_); } catch (oracle::occi::SQLException& excep) { delete objOCCI_; excep.setErrorCtx(ctxOCCI_); return (void *)NULL; } return (void *)objOCCI_; } void employee::readSQL(oracle::occi::AnyData& streamOCCI_) { name = streamOCCI_.getString(); empno = streamOCCI_.getNumber(); deptno = streamOCCI_.getNumber(); hiredate = streamOCCI_.getDate(); salary = streamOCCI_.getNumber(); } void employee::writeSQL(void *objectOCCI_, void *ctxOCCI_) { employee *objOCCI_ = (employee *) objectOCCI_; oracle::occi::AnyData streamOCCI_(ctxOCCI_); try { if (objOCCI_->isNull()) streamOCCI_.setNull(); else objOCCI_->writeSQL(streamOCCI_); } catch (oracle::occi::SQLException& excep) { excep.setErrorCtx(ctxOCCI_); } return; } void employee::writeSQL(oracle::occi::AnyData& streamOCCI_) { streamOCCI_.setString(name); streamOCCI_.setNumber(empno); streamOCCI_.setNumber(deptno); streamOCCI_.setDate(hiredate); streamOCCI_.setNumber(salary); }
#ifndef REGISTERMAPPINGS_ORACLE # define REGISTERMAPPINGS_ORACLE #ifndef OCCI_ORACLE # include <occi.h> #endif #ifndef DEMO_ORACLE # include "demo.h" #endif void RegisterMappings(oracle::occi::Environment* envOCCI_); #endif
#ifndef REGISTERMAPPINGS_ORACLE # include "registermappings.h" #endif void RegisterMappings(oracle::occi::Environment* envOCCI_) { oracle::occi::Map *mapOCCI_ = envOCCI_->getMap(); mapOCCI_->put("SCOTT.EMPLOYEE", employee::readSQL, employee::writeSQL); }
CASE = LOWER MAPFILE = RegisterMappings.cpp MAPFUNC = RegisterMappings TYPE SCOTT.EMPLOYEE AS employee VERSION = "$8.0" HFILE = demo.h
See Also:
Example for Extending OTT Classes for a complete C++ example. |
The first step in using the OTT utility is to create object types or named collection types and store them in the database. This is accomplished through the use of the SQL CREATE TYPE
statement.
The following is an example of statements that create objects:
CREATE TYPE FULL_NAME AS OBJECT (first_name CHAR(20), last_name CHAR(20)); CREATE TYPE ADDRESS AS OBJECT (state CHAR(20), zip CHAR(20)); CREATE TYPE ADDRESS_TAB AS VARRAY(3) OF REF ADDRESS; CREATE TYPE PERSON AS OBJECT (id NUMBER, name FULL_NAME, curr_addr REF ADDRESS, prev_addr_1 ADDRESS_TAB) NOT FINAL; CREATE TYPE STUDENT UNDER PERSON (school_name CHAR(20));
After creating types in the database, the next step is to invoke the OTT utility.
You can specify OTT parameters either on the command line or in a configuration file. Certain parameters can also be specified in the INTYPE
file.
If you specify a parameter in more than one place, then its value on the command line takes precedence over its value in the INTYPE
file. The value in the INTYPE
file takes precedence over its value in a user-defined configuration file, which takes precedence over its value in the default configuration file.
Parameter precedence then is as follows:
For global options (that is, options on the command line or options at the beginning of the INTYPE
file before any TYPE
statements), the value on the command line overrides the value in the INTYPE
file. (The options that can be specified globally in the INTYPE
file are CASE
, INITFILE
, INITFUNC
, MAPFILE
and MAPFUNC
, but not HFILE
or CPPFILE
.) Anything in the INTYPE
file in a TYPE
specification applies to a particular type only and overrides anything on the command line that would otherwise apply to the type. So if you enter TYPE person HFILE=p.h
, then it applies to person
only and overrides the HFILE
on the command line. The statement is not considered a command line parameter.
Parameters (also called options) set on the command line override any parameters or option set elsewhere.
See Also:
"Invoking the OTT Utility" for more information |
The INTYPE
file gives a list of types for the OTT utility to translate.
The parameters CASE
, CPPFILE
, HFILE
, INITFILE
, INITFUNC
, MAPFILE
, and MAPFUNC
can appear in the INTYPE
file.
See Also:
"Overview of the INTYPE File" for more information |
A configuration file is a text file that contains OTT parameters. Each nonblank line in the file contains one parameter, with its associated value or values. If more than one parameter is put on a line, then only the first one will be used. No blank space is allowed on any nonblank line of a configuration file.
A configuration file can be named on the command line. In addition, a default configuration file is always read. This default configuration file must always exist, but can be empty. The name of the default configuration file is ottcfg.cfg
, and the location of the file is operating system-specific.
See Also:
Your operating system-specific documentation for more information about the location of the default configuration file |
On most platforms, the OTT utility is invoked on the command line. You can specify the input and output files and the database connection information at the command line, among other things.
See Also:
Your operating system-specific documentation to see how to invoke the OTT utility on your operating system |
The following is an example of invoking the OTT utility that generates C++ classes:
ott userid=scott/tiger intype=demoin.typ outtype=demoout.typ code=cpp hfile=demo.h cppfile=demo.cpp mapfile=RegisterMappings.cpp
The following sections describe the elements of the command lines used in these examples:
See Also::
"OTT Utility Reference" for a detailed discussion of the various OTT command line parameters. |
Causes the OTT utility to be invoked. It must be the first item on the command line.
Specifies the database connection information that the OTT utility will use. In both of the preceding examples, the OTT utility attempts to connect with username scott
and password tiger
.
Specifies the name of the INTYPE
file. In both of the preceding examples, the name of the INTYPE
file is specified as demoin.typ
.
Specifies the name of the OUTTYPE
file. When the OTT utility generates the header file, it also writes information about the translated types into the OUTTYPE
file. This file contains an entry for each of the types that is translated, including its version string, and the header file to which its C or C++ representation is written.
In both of the preceding examples, the name of the OUTTYPE
file is specified as demoout.typ
.
Specifies the target language for the translation. The following values are valid:
There is currently no default value, so this parameter is required.
Structure declarations are identical for the C language options: C, ANSI_C, and KR_C. The style in which the initialization function is defined in the INITFILE
file depends on whether KR_C is used. If the INITFILE
parameter is not used, then the C language options are equivalent.
If you are generating C++ classes by setting the CODE
parameter to cpp
, then you must use the CPPFILE
and the MAPFILE
parameters.
Specifies the name of the C or C++ header file to which the generated C structures or C++ classes are written.
See Also:
"OTT Command Line Syntax" for information about the |
Specifies the name of the C++ source file into which the method implementations are written. The methods generated in this file are called by OCCI while instantiating the objects and are not to be called directly in the an application. This parameter is only needed for OCCI applications.
Specifies the name of the C++ source file into which the function to register the mappings with the environment is written. A corresponding header file is created containing the prototype for the function. This function to register mappings is only used for OCCI applications.
When you run the OTT utility, the INTYPE
file tells the OTT utility which database types should be translated. The INTYPE
file also controls the naming of the generated structures or classes. You can either create an INTYPE
file or use the OUTTYPE
file of a previous invocation of the OTT utility. If you do not use an INTYPE
file, then all types in the schema to which the OTT utility connects are translated.
The following is an example of a user-created INTYPE
file:
CASE=LOWER TYPE employee TRANSLATE SALARY$ AS salary DEPTNO AS department TYPE ADDRESS TYPE item TYPE "Person" TYPE PURCHASE_ORDER AS p_o
In the first line, the CASE
parameter indicates that generated C identifiers should be in lowercase. However, this CASE
parameter is only applied to those identifiers that are not explicitly mentioned in the INTYPE
file. Thus, employee
and ADDRESS
would always result in C structures employee
and ADDRESS
, respectively. The members of these structures are named in lowercase.
The lines that begin with the TYPE
keyword specify which types in the database should be translated. In this case, the EMPLOYEE
, ADDRESS
, ITEM
, PERSON
, and PURCHASE_ORDER
types are set to be translated.
The TRANSLATE ... AS
keywords specify that the name of an object attribute should be changed when the type is translated into a C structure. In this case, the SALARY$
attribute of the employee
type is translated to salary
.
The AS keyword in the final line specifies that the name of an object type should be changed when it is translated into a structure. In this case, the purchase_order
database type is translated into a structure called p_o
.
If you do not use AS
to translate a type or attribute name, then the database name of the type or attribute will be used as the C identifier name, except that the CASE
parameter will be observed, and any characters that cannot be mapped to a legal C identifier character will be replaced by an underscore character (_). Consider the following reasons for translating a type or attribute:
The OTT utility may need to translate additional types that are not listed in the INTYPE
file. This is because the OTT utility analyzes the types in the INTYPE
file for type dependencies before performing the translation, and it translates other types as necessary. For example, if the ADDRESS
type were not listed in the INTYPE
file, but the Person
type had an attribute of type ADDRESS
, then the OTT utility would still translate ADDRESS
because it is required to define the Person
type.
A normal case insensitive SQL identifier can be spelled in any combination of uppercase and lowercase in the INTYPE
file, and is not quoted.
Use quotation marks, such as TYPE "Person"
to reference SQL identifiers that have been created in a case sensitive manner, for example, CREATE TYPE "Person"
. A SQL identifier is case sensitive if it was quoted when it was declared. Quotation marks can also be used to refer to a SQL identifier that is an OTT-reserved word, for example, TYPE "CASE"
. In this case, the quoted name must be in uppercase if the SQL identifier was created in a case insensitive manner, for example, CREATE TYPE Case
. If an OTT-reserved word is used to refer to the name of a SQL identifier but is not quoted, then the OTT utility will report a syntax error in the INTYPE
file.
See Also:
|
When the OTT utility generates a C structure or a C++ class from a database type, the structure or class contains one element corresponding to each attribute of the object type. The datatypes of the attributes are mapped to types that are used in Oracle object data types. The datatypes found in Oracle include a set of predefined, primitive types and provide for the creation of user-defined types, like object types and collections.
The set of predefined types includes standard types that are familiar to most programmers, including number and character types. It also includes large object datatypes (for example, BLOB or CLOB).
Oracle also includes a set of predefined types that are used to represent object type attributes in C structures or C++ classes. As an example, consider the following object type definition, and its corresponding OTT-generated structure declarations:
CREATE TYPE employee AS OBJECT ( name VARCHAR2(30), empno NUMBER, deptno NUMBER, hiredate DATE, salary$ NUMBER);
The OTT utility, assuming that the CASE
parameter is set to LOWER
and there are no explicit mappings of type or attribute names, produces the following output:
struct employee { OCIString * name; OCINumber empno; OCINumber deptno; OCIDate hiredate; OCINumber salary_; }; typedef struct emp_type emp_type; struct employee_ind { OCIInd _atomic; OCIInd name; OCIInd empno; OCIInd deptno; OCIInd hiredate; OCIInd salary_; } typedef struct employee_ind employee_ind;
The datatypes in the structure declarations--LNOCIString
, LNOCINumber
, LNOCIDate
, LNOCIInd
--are used here to map the datatypes of the object type attributes. The number
datatype of the empno
attribute, maps to the LNOCINumber
datatype, for example. These datatypes can also be used as the types of bind and define variables.
See Also:
|
This section describes the mappings of object attribute types to C types, as generated by the OTT utility Table 7-1 lists the mappings from types that can be used as attributes to object datatypes that are generated by the OTT utility.
See Also:
"OTT Utility Type Mapping Example for C" includes examples of many of these different mappings. |
Note: For |
If an object type includes an attribute of a REF
or collection type, then a typedef
for the REF
or collection type is first generated. Then the structure declaration corresponding to the object type is generated. The structure includes an element whose type is a pointer to the REF
or collection type.
If an object type includes an attribute whose type is another object type, then the OTT utility first generates the nested object type. It then maps the object type attribute to a nested structure of the type of the nested object type.
The C datatypes to which the OTT utility maps nonobject database attribute types are structures, which, except for LNOCIDate
, are opaque.
This section describes the mappings of object attribute types to C++ types generated by the OTT utility. Table 7-2 lists the mappings from types that can be used as attributes to object datatypes that are generated by the OTT utility.
The example in this section demonstrates the various type mappings created by the OTT utility for a C program.
The example assumes that the following database types are created:
CREATE TYPE my_varray AS VARRAY(5) of integer; CREATE TYPE object_type AS OBJECT (object_name VARCHAR2(20)); CREATE TYPE other_type AS OBJECT (object_number NUMBER); CREATE TYPE my_table AS TABLE OF object_type; CREATE TYPE many_types AS OBJECT ( the_varchar VARCHAR2(30), the_char CHAR(3), the_blob BLOB, the_clob CLOB, the_object object_type, another_ref REF other_type, the_ref REF many_types, the_varray my_varray, the_table my_table, the_date DATE, the_num NUMBER, the_raw RAW(255));
The example also assumes that an INTYPE
file exists and that it includes the following:
CASE = LOWER TYPE many_types
The OTT utility would then generate the following C structures:
Note: Comments are provided here to help explain the structures. These comments are not part of the OTT utility output. |
#ifndef MYFILENAME_ORACLE #define MYFILENAME_ORACLE #ifndef OCI_ORACLE #include <oci.h> #endif typedef OCIRef many_types_ref; typedef OCIRef object_type_ref; typedef OCIArray my_varray; /* part of many_types */ typedef OCITable my_table; /* part of many_types*/ typedef OCIRef other_type_ref; struct object_type /* part of many_types */ { OCIString * object_name; }; typedef struct object_type object_type; struct object_type_ind /*indicator struct for*/ { /*object_types*/ OCIInd _atomic; OCIInd object_name; }; typedef struct object_type_ind object_type_ind; struct many_types { OCIString * the_varchar; OCIString * the_char; OCIBlobLocator * the_blob; OCIClobLocator * the_clob; struct object_type the_object; other_type_ref * another_ref; many_types_ref * the_ref; my_varray * the_varray; my_table * the_table; OCIDate the_date; OCINumber the_num; OCIRaw * the_raw; }; typedef struct many_types many_types; struct many_types_ind /*indicator struct for*/ { /*many_types*/ OCIInd _atomic; OCIInd the_varchar; OCIInd the_char; OCIInd the_blob; OCIInd the_clob; struct object_type_ind the_object; /*nested*/ OCIInd another_ref; OCIInd the_ref; OCIInd the_varray; OCIInd the_table; OCIInd the_date; OCIInd the_num; OCIInd the_raw; }; typedef struct many_types_ind many_types_ind; #endif
Notice that even though only one item was listed for translation in the INTYPE
file, two object types and two named collection types were translated. This is because the OTT utility parameter TRANSITIVE Parameter, has the default value of TRUE
. When TRANSITIVE=TRUE
, the OTT utility automatically translates any types which are used as attributes of a type being translated, in order to complete the translation of the listed type.
This is not the case for types which are only accessed by a pointer or by reference in an object type attribute. For example, although the many_types
type contains the attribute another_ref REF other_type
, a declaration of structure other_type
was not generated.
This example also illustrates how typedef
s are used to declare VARRAY
, NESTED TABLE
, and REF
types.
In the preceeding example, the typedef
s occur near the beginning of the file generated by the OTT utility:
typedef OCIRef many_types_ref; typedef OCIRef object_type_ref; typedef OCIArray my_varray; typedef OCITable my_table; typedef OCIRef other_type_ref;
In the structure many_types
, the VARRAY
, NESTED TABLE
, and REF
attributes are declared:
struct many_types { ... other_type_ref * another_ref; many_types_ref * the_ref; my_varray * the_varray; my_table * the_table; ... }
The following is an example of the OTT type mappings for C++, given the types created in the example in the previous section, and an INTYPE
file that includes the following:
CASE = LOWER TYPE many_types
The OTT utility generates the following C++ class declarations:
#ifndef MYFILENAME_ORACLE # define MYFILENAME_ORACLE #ifndef OCCI_ORACLE # include <occi.h> #endif /************************************************************/ // generated declarations for the OBJECT_TYPE object type. /************************************************************/ class object_type : public oracle::occi::PObject { protected: OCCI_STD_NAMESPACE::string object_name; public: void *operator new(size_t size); void *operator new(size_t size, const oracle::occi::Connection * sess, const OCCI_STD_NAMESPACE::string& table); OCCI_STD_NAMESPACE::string getSQLTypeName() const; object_type(); object_type(void *ctxOCCI_) : oracle::occi::PObject (ctxOCCI_) { }; static void *readSQL(void *ctxOCCI_); virtual void readSQL(oracle::occi::AnyData& streamOCCI_); static void writeSQL(void *objOCCI_, void *ctxOCCI_); virtual void writeSQL(oracle::occi::AnyData& streamOCCI_); }; /************************************************************/ // generated declarations for the OTHER_TYPE object type. /************************************************************/ class other_type : public oracle::occi::PObject { protected: oracle::occi::Number object_number; public: void *operator new(size_t size); void *operator new(size_t size, const oracle::occi::Connection * sess, const OCCI_STD_NAMESPACE::string& table); OCCI_STD_NAMESPACE::string getSQLTypeName() const; other_type(); other_type(void *ctxOCCI_) : oracle::occi::PObject (ctxOCCI_) { }; static void *readSQL(void *ctxOCCI_); virtual void readSQL(oracle::occi::AnyData& streamOCCI_); static void writeSQL(void *objOCCI_, void *ctxOCCI_); virtual void writeSQL(oracle::occi::AnyData& streamOCCI_); }; /************************************************************/ // generated declarations for the MANY_TYPES object type. /************************************************************/ class many_types : public oracle::occi::PObject { protected: OCCI_STD_NAMESPACE::string the_varchar; OCCI_STD_NAMESPACE::string the_char; oracle::occi::Blob the_blob; oracle::occi::Clob the_clob; object_type * the_object; oracle::occi::Ref< other_type > another_ref; oracle::occi::Ref< many_types > the_ref; OCCI_STD_NAMESPACE::vector< oracle::occi::Number > the_varray; OCCI_STD_NAMESPACE::vector< object_type * > the_table; oracle::occi::Date the_date; oracle::occi::Number the_num; oracle::occi::Bytes the_raw; public: void *operator new(size_t size); void *operator new(size_t size, const oracle::occi::Connection * sess, const OCCI_STD_NAMESPACE::string& table); OCCI_STD_NAMESPACE::string getSQLTypeName() const; many_types(); many_types(void *ctxOCCI_) : oracle::occi::PObject (ctxOCCI_) { }; static void *readSQL(void *ctxOCCI_); virtual void readSQL(oracle::occi::AnyData& streamOCCI_); static void writeSQL(void *objOCCI_, void *ctxOCCI_); virtual void writeSQL(oracle::occi::AnyData& streamOCCI_); }; #endif
For C++, when TRANSITIVE=TRUE
, the OTT utility automatically translates any types that are used as attributes of a type being translated, including types that are only being accessed by a pointer or REF
in an object type attribute. Even though only the many_types
object was specified in the INTYPE
file for the C++ example, a class declaration was generated for all the object types, including the other_type
object, which was only accessed by a REF
in the many_types
object.
The OUTTYPE
file is named on the OTT command line. When the OTT utility generates a C or C++ header file, it also writes the results of the translation into the OUTTYPE
file. This file contains an entry for each of the translated types, including its version string and the header file to which its C or C++ representation was written.
The OUTTYPE
file from one OTT utility run can be used as the INTYPE
file for a subsequent invocation of the OTT utility.
For example, consider the following simple INTYPE
file used earlier in this chapter:
CASE=LOWER TYPE employee TRANSLATE SALARY$ AS salary DEPTNO AS department TYPE ADDRESS TYPE item TYPE "Person" TYPE PURCHASE_ORDER AS p_o
In this INTYPE
file, the programmer specifies the case for OTT-generated C identifiers, and provides a list of types that should be translated. In two of these types, naming conventions are specified.
The following example shows what the OUTTYPE
file looks like after running the OTT utility:
CASE = LOWER TYPE EMPLOYEE AS employee VERSION = "$8.0" HFILE = demo.h TRANSLATE SALARY$ AS salary DEPTNO AS department TYPE ADDRESS AS ADDRESS VERSION = "$8.0" HFILE = demo.h TYPE ITEM AS item VERSION = "$8.0" HFILE = demo.h TYPE "Person" AS Person VERSION = "$8.0" HFILE = demo.h TYPE PURCHASE_ORDER AS p_o VERSION = "$8.0" HFILE = demo.h
When examining the contents of the OUTTYPE
file, you might discover types listed that were not included in the INTYPE
file specification. For example, consider the case where the INTYPE
file only specified that the person
type was to be translated:
CASE = LOWER TYPE PERSON
If the definition of the person
type includes an attribute of type address
, then the OUTTYPE
file includes entries for both PERSON
and ADDRESS
. The person
type cannot be translated completely without first translating address
.
The OTT utility analyzes the types in the INTYPE
file for type dependencies before performing the translation, and translates other types as necessary.
See Also:
"Invoking the OTT Utility" for details on these parameters. |
The OTT utility generates objects and maps SQL datatypes to C++ classes. The OTT utility also implements a few methods called by OCCI when instantiating objects and a function that is called in the OCCI application to register the mappings with the environment. These declarations are stored in a header file that you include (#include
) in your OCCI application. The prototype for the function that registers the mappings is written to a separate header file that you also include in your OCCI application.The method implementations are stored in a C++ source code file (with extension .cpp
) that is linked with the OCCI application. The function that registers the mappings is stored in a separate C++ (.cpp
) file that is also linked with the application.
Figure 7-1 shows the steps involved in using the OTT utility with OCCI. These steps are described following the figure.
INTYPE
file that contains the database types to be translated by the OTT utility.The OTT utility then generates the following files:
.h
) that contains C++ class representations of object types. The filename is specified on the OTT command line by the HFILE
parameter.MAPFUNC
) that registers the mappings..cpp
) that contains the static methods to be called by OCCI while instantiating the objects. Do not call these methods directly from your OCCI application. The filename is specified on the OTT command line by the CPPFILE
parameter..cpp
). The filename is specified on the OTT command line by the MAPFILE
parameter.OUTTYPE
parameter.The application declares an environment and calls the function MAPFUNC
to register the mappings.
To generate C++ using the OTT utility, the CODE
parameter must be set to CODE=CPP
. Once CODE=CPP
is specified, you are required to specify the CPPFILE
and MAPFILE
parameters to define the filenames for the method implementation file and the mappings registration function file. The name of the mapping function is derived by the OTT utility from the MAPFILE
or you may specify the name with the MAPFUNC
parameter. ATTRACCESS
is also an optional parameter that can be specified to change the generated code.
The following parameters are specific to C++ only and control the generation of C++ classes:
CPPFILE
MAPFILE
MAPFUNC
ATTRACCESS
See Also:
"OTT Utility Parameters" for details on these parameters. |
When the OTT utility generates a C++ class from a database object type, the class declaration contains one element corresponding to each attribute of the object type. The datatypes of the attribute are mapped to types that are used in Oracle object datatypes, as defined in Table 7-2.
For each class, two new operators, a getSQLTypeName
method, two constructors, two readSQL
methods and two writeSQL
methods are generated. The getSQLTypeName
method, the constructor, the readSQL
and writeSQL
methods are called by OCCI while unmarshalling and marshalling the object data.
By default, the OTT-generated C++ class for an object type is derived from the PObject
class and so the generated constructor in the class also derives from the PObject
class. For inherited database types, the class is derived from the parent type class as is the generated constructor and only the elements corresponding to attributes not already in the parent class are included.
Class declarations that include the elements corresponding to the database type attributes and the method declarations are included in the header file generated by the OTT utility. The method implementations are included in the CPPFILE
file generated by the OTT utility.
The following is an example of the C++ classes generated by the OTT utility as a result of this series of steps:
CREATE TYPE FULL_NAME AS OBJECT (first_name CHAR(20), last_name CHAR(20)); CREATE TYPE ADDRESS AS OBJECT (state CHAR(20), zip CHAR(20)); CREATE TYPE ADDRESS_TAB AS VARRAY(3) of REF ADDRESS; CREATE TYPE PERSON AS OBJECT (id NUMBER, name FULL_NAME, curr_addr REF ADDRESS, prev_addr_l ADDRESS_TAB) NOT FINAL; CREATE TYPE STUDENT UNDER PERSON (school_name CHAR(20));
INTYPE
file:
CASE = SAME MAPFILE = RegisterMappings_3.cpp TYPE FULL_NAME AS FullName TRANSLATE first_name as FirstName last_name as LastName TYPE ADDRESS TYPE PERSON TYPE STUDENT
ott userid=scott/tiger intype=demoin_3.typ outype=demoout_3.typ code=cpp hfile=demo_3.h cppfile=demo_3.cpp
This example produces a header file named demo_3.h
, a C++ source file named demo_3.cpp
, and an OUTTYPE
file named demoout_3.typ
. These files generated by the OTT utility are displayed in the following sections:
This section contains the header file generated by the OTT utility (named demo_3.h
) based on information contained in the previous section.
#ifndef DEMO_3_ORACLE # define DEMO_3_ORACLE #ifndef OCCI_ORACLE # include <occi.h> #endif /************************************************************/ // generated declarations for the FULL_NAME object type. /************************************************************/ class FullName : public oracle::occi::PObject { protected: OCCI_STD_NAMESPACE::string FirstName; OCCI_STD_NAMESPACE::string LastName; public: void *operator new(size_t size); void *operator new(size_t size, const oracle::occi::Connection * sess, const OCCI_STD_NAMESPACE::string& table); OCCI_STD_NAMESPACE::string getSQLTypeName() const; FullName(); FullName(void *ctxOCCI_) : oracle::occi::PObject (ctxOCCI_) { }; static void *readSQL(void *ctxOCCI_); virtual void readSQL(oracle::occi::AnyData& streamOCCI_); static void writeSQL(void *objOCCI_, void *ctxOCCI_); virtual void writeSQL(oracle::occi::AnyData& streamOCCI_); }; /************************************************************/ // generated declarations for the ADDRESS object type. /************************************************************/ class ADDRESS : public oracle::occi::PObject { protected: OCCI_STD_NAMESPACE::string STATE; OCCI_STD_NAMESPACE::string ZIP; public: void *operator new(size_t size); void *operator new(size_t size, const oracle::occi::Connection * sess, const OCCI_STD_NAMESPACE::string& table); OCCI_STD_NAMESPACE::string getSQLTypeName() const; ADDRESS(); ADDRESS(void *ctxOCCI_) : oracle::occi::PObject (ctxOCCI_) { }; static void *readSQL(void *ctxOCCI_); virtual void readSQL(oracle::occi::AnyData& streamOCCI_); static void writeSQL(void *objOCCI_, void *ctxOCCI_); virtual void writeSQL(oracle::occi::AnyData& streamOCCI_); }; /************************************************************/ // generated declarations for the PERSON object type. /************************************************************/ class PERSON : public oracle::occi::PObject { protected: oracle::occi::Number ID; FullName * NAME; oracle::occi::Ref< ADDRESS > CURR_ADDR; OCCI_STD_NAMESPACE::vector< oracle::occi::Ref< ADDRESS > > PREV_ADDR_L; public: void *operator new(size_t size); void *operator new(size_t size, const oracle::occi::Connection * sess, const OCCI_STD_NAMESPACE::string& table); OCCI_STD_NAMESPACE::string getSQLTypeName() const; PERSON(); PERSON(void *ctxOCCI_) : oracle::occi::PObject (ctxOCCI_) { }; static void *readSQL(void *ctxOCCI_); virtual void readSQL(oracle::occi::AnyData& streamOCCI_); static void writeSQL(void *objOCCI_, void *ctxOCCI_); virtual void writeSQL(oracle::occi::AnyData& streamOCCI_); }; /************************************************************/ // generated declarations for the STUDENT object type. /************************************************************/ class STUDENT : public PERSON { protected: OCCI_STD_NAMESPACE::string SCHOOL_NAME; public: void *operator new(size_t size); void *operator new(size_t size, const oracle::occi::Connection * sess, const OCCI_STD_NAMESPACE::string& table); OCCI_STD_NAMESPACE::string getSQLTypeName() const; STUDENT(); STUDENT(void *ctxOCCI_) : PERSON (ctxOCCI_) { }; static void *readSQL(void *ctxOCCI_); virtual void readSQL(oracle::occi::AnyData& streamOCCI_); static void writeSQL(void *objOCCI_, void *ctxOCCI_); virtual void writeSQL(oracle::occi::AnyData& streamOCCI_); }; #endif
This section contains the C++ source file generated by the OTT utility (named demo_3.cpp
) based on information contained in the previous section.
#ifndef DEMO_3_ORACLE # include "demo_3.h" #endif /*****************************************************************/ // generated method implementations for the FULL_NAME object type. /*****************************************************************/ void *FullName::operator new(size_t size) { return oracle::occi::PObject::operator new(size); } void *FullName::operator new(size_t size, const oracle::occi::Connection * sess, const OCCI_STD_NAMESPACE::string& table) { return oracle::occi::PObject::operator new(size, sess, table, (char *) "SCOTT.FULL_NAME"); } OCCI_STD_NAMESPACE::string FullName::getSQLTypeName() const { return OCCI_STD_NAMESPACE::string("SCOTT.FULL_NAME"); } FullName::FullName() { } void *FullName::readSQL(void *ctxOCCI_) { FullName *objOCCI_ = new FullName(ctxOCCI_); oracle::occi::AnyData streamOCCI_(ctxOCCI_); try { if (streamOCCI_.isNull()) objOCCI_->setNull(); else objOCCI_->readSQL(streamOCCI_); } catch (oracle::occi::SQLException& excep) { delete objOCCI_; excep.setErrorCtx(ctxOCCI_); return (void *)NULL; } return (void *)objOCCI_; } void FullName::readSQL(oracle::occi::AnyData& streamOCCI_) { FirstName = streamOCCI_.getString(); LastName = streamOCCI_.getString(); } void FullName::writeSQL(void *objectOCCI_, void *ctxOCCI_) { FullName *objOCCI_ = (FullName *) objectOCCI_; oracle::occi::AnyData streamOCCI_(ctxOCCI_); try { if (objOCCI_->isNull()) streamOCCI_.setNull(); else objOCCI_->writeSQL(streamOCCI_); } catch (oracle::occi::SQLException& excep) { excep.setErrorCtx(ctxOCCI_); } return; } void FullName::writeSQL(oracle::occi::AnyData& streamOCCI_) { streamOCCI_.setString(FirstName); streamOCCI_.setString(LastName); } /*****************************************************************/ // generated method implementations for the ADDRESS object type. /*****************************************************************/ void *ADDRESS::operator new(size_t size) { return oracle::occi::PObject::operator new(size); } void *ADDRESS::operator new(size_t size, const oracle::occi::Connection * sess, const OCCI_STD_NAMESPACE::string& table) { return oracle::occi::PObject::operator new(size, sess, table, (char *) "SCOTT.ADDRESS"); } OCCI_STD_NAMESPACE::string ADDRESS::getSQLTypeName() const { return OCCI_STD_NAMESPACE::string("SCOTT.ADDRESS"); } ADDRESS::ADDRESS() { } void *ADDRESS::readSQL(void *ctxOCCI_) { ADDRESS *objOCCI_ = new ADDRESS(ctxOCCI_); oracle::occi::AnyData streamOCCI_(ctxOCCI_); try { if (streamOCCI_.isNull()) objOCCI_->setNull(); else objOCCI_->readSQL(streamOCCI_); } catch (oracle::occi::SQLException& excep) { delete objOCCI_; excep.setErrorCtx(ctxOCCI_); return (void *)NULL; } return (void *)objOCCI_; } void ADDRESS::readSQL(oracle::occi::AnyData& streamOCCI_) { STATE = streamOCCI_.getString(); ZIP = streamOCCI_.getString(); } void ADDRESS::writeSQL(void *objectOCCI_, void *ctxOCCI_) { ADDRESS *objOCCI_ = (ADDRESS *) objectOCCI_; oracle::occi::AnyData streamOCCI_(ctxOCCI_); try { if (objOCCI_->isNull()) streamOCCI_.setNull(); else objOCCI_->writeSQL(streamOCCI_); } catch (oracle::occi::SQLException& excep) { excep.setErrorCtx(ctxOCCI_); } return; } void ADDRESS::writeSQL(oracle::occi::AnyData& streamOCCI_) { streamOCCI_.setString(STATE); streamOCCI_.setString(ZIP); } /*****************************************************************/ // generated method implementations for the PERSON object type. /*****************************************************************/ void *PERSON::operator new(size_t size) { return oracle::occi::PObject::operator new(size); } void *PERSON::operator new(size_t size, const oracle::occi::Connection * sess, const OCCI_STD_NAMESPACE::string& table) { return oracle::occi::PObject::operator new(size, sess, table, (char *) "SCOTT.PERSON"); } OCCI_STD_NAMESPACE::string PERSON::getSQLTypeName() const { return OCCI_STD_NAMESPACE::string("SCOTT.PERSON"); } PERSON::PERSON() { NAME = (FullName *) 0; } void *PERSON::readSQL(void *ctxOCCI_) { PERSON *objOCCI_ = new PERSON(ctxOCCI_); oracle::occi::AnyData streamOCCI_(ctxOCCI_); try { if (streamOCCI_.isNull()) objOCCI_->setNull(); else objOCCI_->readSQL(streamOCCI_); } catch (oracle::occi::SQLException& excep) { delete objOCCI_; excep.setErrorCtx(ctxOCCI_); return (void *)NULL; } return (void *)objOCCI_; } void PERSON::readSQL(oracle::occi::AnyData& streamOCCI_) { ID = streamOCCI_.getNumber(); NAME = (FullName *) streamOCCI_.getObject(); CURR_ADDR = streamOCCI_.getRef(); getVector(streamOCCI_, PREV_ADDR_L); } void PERSON::writeSQL(void *objectOCCI_, void *ctxOCCI_) { PERSON *objOCCI_ = (PERSON *) objectOCCI_; oracle::occi::AnyData streamOCCI_(ctxOCCI_); try { if (objOCCI_->isNull()) streamOCCI_.setNull(); else objOCCI_->writeSQL(streamOCCI_); } catch (oracle::occi::SQLException& excep) { excep.setErrorCtx(ctxOCCI_); } return; } void PERSON::writeSQL(oracle::occi::AnyData& streamOCCI_) { streamOCCI_.setNumber(ID); streamOCCI_.setObject(NAME); streamOCCI_.setRef(CURR_ADDR); setVector(streamOCCI_, PREV_ADDR_L); } /*****************************************************************/ // generated method implementations for the STUDENT object type. /*****************************************************************/ void *STUDENT::operator new(size_t size) { return oracle::occi::PObject::operator new(size); } void *STUDENT::operator new(size_t size, const oracle::occi::Connection * sess, const OCCI_STD_NAMESPACE::string& table) { return oracle::occi::PObject::operator new(size, sess, table, (char *) "SCOTT.STUDENT"); } OCCI_STD_NAMESPACE::string STUDENT::getSQLTypeName() const { return OCCI_STD_NAMESPACE::string("SCOTT.STUDENT"); } STUDENT::STUDENT() { } void *STUDENT::readSQL(void *ctxOCCI_) { STUDENT *objOCCI_ = new STUDENT(ctxOCCI_); oracle::occi::AnyData streamOCCI_(ctxOCCI_); try { if (streamOCCI_.isNull()) objOCCI_->setNull(); else objOCCI_->readSQL(streamOCCI_); } catch (oracle::occi::SQLException& excep) { delete objOCCI_; excep.setErrorCtx(ctxOCCI_); return (void *)NULL; } return (void *)objOCCI_; } void STUDENT::readSQL(oracle::occi::AnyData& streamOCCI_) { PERSON::readSQL(streamOCCI_); SCHOOL_NAME = streamOCCI_.getString(); } void STUDENT::writeSQL(void *objectOCCI_, void *ctxOCCI_) { STUDENT *objOCCI_ = (STUDENT *) objectOCCI_; oracle::occi::AnyData streamOCCI_(ctxOCCI_); try { if (objOCCI_->isNull()) streamOCCI_.setNull(); else objOCCI_->writeSQL(streamOCCI_); } catch (oracle::occi::SQLException& excep) { excep.setErrorCtx(ctxOCCI_); } return; } void STUDENT::writeSQL(oracle::occi::AnyData& streamOCCI_) { PERSON::writeSQL(streamOCCI_); streamOCCI_.setString(SCHOOL_NAME); }
This section contains the OUTTYPE file generated by the OTT utility (named demoout_3.typ
) based on information contained in the previous section:
CASE = SAME MAPFILE = RegisterMappings_3.cpp MAPFUNC = RegisterMappings TYPE SCOTT.FULL_NAME AS FullName VERSION = "$8.0" HFILE = demo_3.h TRANSLATE FIRST_NAME AS FirstName LAST_NAME AS LastName TYPE SCOTT.ADDRESS AS ADDRESS VERSION = "$8.0" HFILE = demo_3.h TYPE SCOTT.PERSON AS PERSON VERSION = "$8.0" HFILE = demo_3.h TYPE SCOTT.STUDENT AS STUDENT VERSION = "$8.0" HFILE = demo_3.h
To demonstrate the difference in generated code when ATTRACCESS=PRIVATE
, consider an INTYPE
file that contains:
CASE = SAME TYPE PERSON
The OTT utility generates the following header file:
#ifndef DEMO_4_ORACLE #define DEMO_4 _ORACLE #ifndef OCCI_ORACLE #include <occi.h> #endif /************************************************************/ // generated declarations for the FULL_NAME object type. /************************************************************/ class FULL_NAME : public oracle::occi::PObject { private: OCCI_STD_NAMESPACE::string FIRST_NAME; OCCI_STD_NAMESPACE::string LAST_NAME; public: OCCI_STD_NAMESPACE::string getFirst_name() const; void setFirst_name(const OCCI_STD_NAMESPACE::string &value); OCCI_STD_NAMESPACE::string getLast_name() const; void setLast_name(const OCCI_STD_NAMESPACE::string &value); void *operator new(size_t size); void *operator new(size_t size, const oracle::occi::Connection * sess, const OCCI_STD_NAMESPACE::string& table); OCCI_STD_NAMESPACE::string getSQLTypeName() const; FULL_NAME(); FULL_NAME(void *ctxOCCI_) : oracle::occi::PObject (ctxOCCI_) { }; static void *readSQL(void *ctxOCCI_); virtual void readSQL(oracle::occi::AnyData& streamOCCI_); static void writeSQL(void *objOCCI_, void *ctxOCCI_); virtual void writeSQL(oracle::occi::AnyData& streamOCCI_); /************************************************************/ // generated declarations for the ADDRESS object type. /************************************************************/ class ADDRESS : public oracle::occi::PObject { private: OCCI_STD_NAMESPACE::string STATE; OCCI_STD_NAMESPACE::string ZIP; public: OCCI_STD_NAMESPACE::string getState() const; void setState(const OCCI_STD_NAMESPACE::string &value); OCCI_STD_NAMESPACE::string getZip() const; void setZip(const OCCI_STD_NAMESPACE::string &value); void *operator new(size_t size); void *operator new(size_t size, const oracle::occi::Connection * sess, const OCCI_STD_NAMESPACE::string& table); OCCI_STD_NAMESPACE::string getSQLTypeName() const; ADDRESS(); ADDRESS(void *ctxOCCI_) : oracle::occi::PObject (ctxOCCI_) { }; static void *readSQL(void *ctxOCCI_); virtual void readSQL(oracle::occi::AnyData& streamOCCI_); static void writeSQL(void *objOCCI_, void *ctxOCCI_); virtual void writeSQL(oracle::occi::AnyData& streamOCCI_); }; /************************************************************/ // generated declarations for the PERSON object type. /************************************************************/ class PERSON : public oracle::occi::PObject { private: oracle::occi::Number ID; FULLL_NAME * NAME; oracle::occi::Ref< ADDRESS > CURR_ADDR; OCCI_STD_NAMESPACE::vector< oracle::occi::Ref< ADDRESS > > PREV_ADDR_L; public: oracle::occi::Number getId() const; void setId(const oracle::occi::Number &value); FULL_NAME * getName() const; void setName(FULL_NAME * value); oracle::occi::Ref< ADDRESS > getCurr_addr() const; void setCurr_addr(const oracle::occi::Ref< ADDRESS > &value); OCCI_STD_NAMESPACE::vector< oracle::occi::Ref< ADDRESS > >& getPrev_addr_l(); const OCCI_STD_NAMESPACE::vector< oracle::occi::Ref< ADDRESS > >& getPrev_addr_l() const; void setPrev_addr_l(const OCCI_STD_NAMESPACE::vector< oracle::occi::Ref< ADDRESS > > &value); void *operator new(size_t size); void *operator new(size_t size, const oracle::occi::Connection * sess, const OCCI_STD_NAMESPACE::string& table); OCCI_STD_NAMESPACE::string getSQLTypeName() const; PERSON(); PERSON(void *ctxOCCI_) : oracle::occi::PObject (ctxOCCI_) { }; static void *readSQL(void *ctxOCCI_); virtual void readSQL(oracle::occi::AnyData& streamOCCI_); static void writeSQL(void *objOCCI_, void *ctxOCCI_); virtual void writeSQL(oracle::occi::AnyData& streamOCCI_); }; #endif
Since ATTRACCESS=PRIVATE
, the access given to the attributes is private
and the accessor (get
xxx
) and the mutator (set
xxx
) methods are generated for each of the attributes.
One function to register the mappings with the environment is generated by the OTT utility. The function contains the mappings for all the types translated by the invocation of the OTT utility. The function name is either specified in the MAPFUNC
parameter or, if that parameter is not specified, derived from MAPFILE
parameter. The only argument to the function is the pointer to Environment
.
The function uses the provided Environment
to get Map
and then registers the mapping of each translated type.
Given the database type and INTYPE
file listed in the previous section, and specifying MAPFILE=RegisterMappings_3.cpp
, the map registering function generated takes the following form:
>#ifndef REGISTERMAPPINGS_3_ORACLE #include "registermappings_3.h" #endif void RegisterMappings_3(oracle::occi::Environment* envOCCI_) { oracle::occi::Map *mapOCCI_ = envOCCI_->getMap(); mapOCCI_->put("SCOTT.FULL_NAME", FullName::readSQL, FullName::writeSQL); mapOCCI_->put("SCOTT.ADDRESS", ADDRESS::readSQL, ADDRESS::writeSQL); mapOCCI_->put("SCOTT.PERSON", PERSON::readSQL, PERSON::writeSQL); mapOCCI_->put("SCOTT.STUDENT", STUDENT::readSQL, STUDENT::writeSQL); }
The prototype of the register mapping function is written to a corresponding header file, RegisterMapping.h
, and looks like the following:
#ifndef REGISTERMAPPINGS_3_ORACLE # define REGISTERMAPPINGS_3_ORACLE #ifndef OCCI_ORACLE # include <occi.h> #endif #ifndef DEMO_3_ORACLE # include "demo_3.h" #endif void RegisterMappings_3(oracle::occi::Environment* envOCCI_); #endif
To enhance the functionality of a class generated by the OTT utility, you can derive new classes. You can also add methods to a class, but Oracle does not recommend doing so due to an inherent risk.
See Also:
"Carrying Forward User Added Code" for details on how to use OTT markers to retain code you want to add in OTT generated files. |
For an example of deriving a new class from an OTT-generated class, assume you want to generate the class CAddress
from the SQL object type ADDRESS
. Assume also that you want to write a class MyAddress to represent ADDRESS
objects. The MyAddress
class can be derived from CAddress
.
To perform this, the OTT utility must alter the code it generates:
MyAddress
class instead of the CAddress
class to represent attributes whose database type is ADDRESS
MyAddress
class instead of the CAddress
class to represent vector and REF
elements whose database type is ADDRESS
MyAddress
class instead of the CAddress
class as the base class for database object types that are inherited from ADDRESS
. Even though a derived class is a subtype of MyAddress
, the readSQL
and writeSQL
methods called are those of the CAddress
class.
To use the OTT utility to generate the CAddress
class (that you derive the MyAddress
class from), the following clause must be specified in the TYPE
statement:
TYPE ADDRESS GENERATE CAdress AS MyAddress
Given the database types FULL_NAME
, ADDRESS
, PERSON
, and PFGRFDENT
as they were created before and changing the INTYPE
file to include the GENERATE ...
AS
clause:
CASE = SAME MAPFILE = RegisterMappings_5.cpp TYPE FULL_NAME GENERATE CFullName AS MyFullName TRANSLATE first_name as FirstName last_name as LastName TYPE ADDRESS GENERATE CAddress AS MyAddress TYPE PERSON GENERATE CPerson AS MyPerson TYPE STUDENT GENERATE CStudent AS MyStudent
The following C++ source file (with the extension .cpp
) is generated by the OTT utility:
#ifndef MYFILENAME_ORACLE # define MYFILENAME_ORACLE #ifndef OCCI_ORACLE # include <occi.h> #endif /************************************************************/ // generated declarations for the FULL_NAME object type. /************************************************************/ class CFullName : public oracle::occi::PObject { protected: OCCI_STD_NAMESPACE::string FirstName; OCCI_STD_NAMESPACE::string LastName; public: void *operator new(size_t size); void *operator new(size_t size, const oracle::occi::Connection * sess, const OCCI_STD_NAMESPACE::string& table); OCCI_STD_NAMESPACE::string getSQLTypeName() const; CFullName(); CFullName(void *ctxOCCI_) : oracle::occi::PObject (ctxOCCI_) { }; static void *readSQL(void *ctxOCCI_); virtual void readSQL(oracle::occi::AnyData& streamOCCI_); static void writeSQL(void *objOCCI_, void *ctxOCCI_); virtual void writeSQL(oracle::occi::AnyData& streamOCCI_); }; /************************************************************/ // generated declarations for the ADDRESS object type. /************************************************************/ class CAddress : public oracle::occi::PObject { protected: OCCI_STD_NAMESPACE::string STATE; OCCI_STD_NAMESPACE::string ZIP; public: void *operator new(size_t size); void *operator new(size_t size, const oracle::occi::Connection * sess, const OCCI_STD_NAMESPACE::string& table); OCCI_STD_NAMESPACE::string getSQLTypeName() const; CAddress(); CAddress(void *ctxOCCI_) : oracle::occi::PObject (ctxOCCI_) { }; static void *readSQL(void *ctxOCCI_); virtual void readSQL(oracle::occi::AnyData& streamOCCI_); static void writeSQL(void *objOCCI_, void *ctxOCCI_); virtual void writeSQL(oracle::occi::AnyData& streamOCCI_); }; /************************************************************/ // generated declarations for the PERSON object type. // // Note the type name for the "name" attribute is MyFullName // and not CFullName, the "curr-addr" attribute is Ref< MyAddress > // and not Ref< CAddress >, and the "prev_addr_l" attribute is // vector< Ref< MyAddress > >. /************************************************************/ class CPerson : public oracle::occi::PObject { protected: oracle::occi::Number ID; MyFullName * NAME; oracle::occi::Ref< MyAddress > CURR_ADDR; OCCI_STD_NAMESPACE::vector< oracle::occi::Ref< MyAddress > > PREV_ADDR_L; public: void *operator new(size_t size); void *operator new(size_t size, const oracle::occi::Connection * sess, const OCCI_STD_NAMESPACE::string& table); OCCI_STD_NAMESPACE::string getSQLTypeName() const; CPerson(); CPerson(void *ctxOCCI_) : oracle::occi::PObject (ctxOCCI_) { }; static void *readSQL(void *ctxOCCI_); virtual void readSQL(oracle::occi::AnyData& streamOCCI_); static void writeSQL(void *objOCCI_, void *ctxOCCI_); virtual void writeSQL(oracle::occi::AnyData& streamOCCI_); }; /************************************************************/ // generated declarations for the STUDENT object type. // // Note the parent class for CStudent is MyPerson and not // CPerson /************************************************************/ class CStudent : public MyPerson { protected: OCCI_STD_NAMESPACE::string SCHOOL_NAME; public: void *operator new(size_t size); void *operator new(size_t size, const oracle::occi::Connection * sess, const OCCI_STD_NAMESPACE::string& table); OCCI_STD_NAMESPACE::string getSQLTypeName() const; CStudent(); CStudent(void *ctxOCCI_) : MyPerson (ctxOCCI_) { }; static void *readSQL(void *ctxOCCI_); virtual void readSQL(oracle::occi::AnyData& streamOCCI_); static void writeSQL(void *objOCCI_, void *ctxOCCI_); virtual void writeSQL(oracle::occi::AnyData& streamOCCI_); }; #endif
The method implementations are as follows:
#ifndef MYFILENAME_ORACLE # include "myfilename.h" #endif /*****************************************************************/ // generated method implementations for the FULL_NAME object type. /*****************************************************************/ void *CFullName::operator new(size_t size) { return oracle::occi::PObject::operator new(size); } void *CFullName::operator new(size_t size, const oracle::occi::Connection * sess, const OCCI_STD_NAMESPACE::string& table) { return oracle::occi::PObject::operator new(size, sess, table, (char *) "SCOTT.FULL_NAME"); } OCCI_STD_NAMESPACE::string CFullName::getSQLTypeName() const { return OCCI_STD_NAMESPACE::string("SCOTT.FULL_NAME"); } CFullName::CFullName() { } void *CFullName::readSQL(void *ctxOCCI_) { CFullName *objOCCI_ = new CFullName(ctxOCCI_); oracle::occi::AnyData streamOCCI_(ctxOCCI_); try { if (streamOCCI_.isNull()) objOCCI_->setNull(); else objOCCI_->readSQL(streamOCCI_); } catch (oracle::occi::SQLException& excep) { delete objOCCI_; excep.setErrorCtx(ctxOCCI_); return (void *)NULL; } return (void *)objOCCI_; } void CFullName::readSQL(oracle::occi::AnyData& streamOCCI_) { FirstName = streamOCCI_.getString(); LastName = streamOCCI_.getString(); } void CFullName::writeSQL(void *objectOCCI_, void *ctxOCCI_) { CFullName *objOCCI_ = (CFullName *) objectOCCI_; oracle::occi::AnyData streamOCCI_(ctxOCCI_); try { if (objOCCI_->isNull()) streamOCCI_.setNull(); else objOCCI_->writeSQL(streamOCCI_); } catch (oracle::occi::SQLException& excep) { excep.setErrorCtx(ctxOCCI_); } return; } void CFullName::writeSQL(oracle::occi::AnyData& streamOCCI_) { streamOCCI_.setString(FirstName); streamOCCI_.setString(LastName); } /*****************************************************************/ // generated method implementations for the ADDRESS object type. /*****************************************************************/ void *CAddress::operator new(size_t size) { return oracle::occi::PObject::operator new(size); } void *CAddress::operator new(size_t size, const oracle::occi::Connection * sess, const OCCI_STD_NAMESPACE::string& table) { return oracle::occi::PObject::operator new(size, sess, table, (char *) "SCOTT.ADDRESS"); } OCCI_STD_NAMESPACE::string CAddress::getSQLTypeName() const { return OCCI_STD_NAMESPACE::string("SCOTT.ADDRESS"); } CAddress::CAddress() { } void *CAddress::readSQL(void *ctxOCCI_) { CAddress *objOCCI_ = new CAddress(ctxOCCI_); oracle::occi::AnyData streamOCCI_(ctxOCCI_); try { if (streamOCCI_.isNull()) objOCCI_->setNull(); else objOCCI_->readSQL(streamOCCI_); } catch (oracle::occi::SQLException& excep) { delete objOCCI_; excep.setErrorCtx(ctxOCCI_); return (void *)NULL; } return (void *)objOCCI_; } void CAddress::readSQL(oracle::occi::AnyData& streamOCCI_) { STATE = streamOCCI_.getString(); ZIP = streamOCCI_.getString(); } void CAddress::writeSQL(void *objectOCCI_, void *ctxOCCI_) { CAddress *objOCCI_ = (CAddress *) objectOCCI_; oracle::occi::AnyData streamOCCI_(ctxOCCI_); try { if (objOCCI_->isNull()) streamOCCI_.setNull(); else objOCCI_->writeSQL(streamOCCI_); } catch (oracle::occi::SQLException& excep) { excep.setErrorCtx(ctxOCCI_); } return; } void CAddress::writeSQL(oracle::occi::AnyData& streamOCCI_) { streamOCCI_.setString(STATE); streamOCCI_.setString(ZIP); } /*****************************************************************/ // generated method implementations for the PERSON object type. // // Note the type used in the casting in the readSQL method is // MyFullName and not CFullName. /*****************************************************************/ void *CPerson::operator new(size_t size) { return oracle::occi::PObject::operator new(size); } void *CPerson::operator new(size_t size, const oracle::occi::Connection * sess, const OCCI_STD_NAMESPACE::string& table) { return oracle::occi::PObject::operator new(size, sess, table, (char *) "SCOTT.PERSON"); } OCCI_STD_NAMESPACE::string CPerson::getSQLTypeName() const { return OCCI_STD_NAMESPACE::string("SCOTT.PERSON"); } CPerson::CPerson() { NAME = (MyFullName *) 0; } void *CPerson::readSQL(void *ctxOCCI_) { CPerson *objOCCI_ = new CPerson(ctxOCCI_); oracle::occi::AnyData streamOCCI_(ctxOCCI_); try { if (streamOCCI_.isNull()) objOCCI_->setNull(); else objOCCI_->readSQL(streamOCCI_); } catch (oracle::occi::SQLException& excep) { delete objOCCI_; excep.setErrorCtx(ctxOCCI_); return (void *)NULL; } return (void *)objOCCI_; } void CPerson::readSQL(oracle::occi::AnyData& streamOCCI_) { ID = streamOCCI_.getNumber(); NAME = (MyFullName *) streamOCCI_.getObject(); CURR_ADDR = streamOCCI_.getRef(); getVector(streamOCCI_, PREV_ADDR_L); } void CPerson::writeSQL(void *objectOCCI_, void *ctxOCCI_) { CPerson *objOCCI_ = (CPerson *) objectOCCI_; oracle::occi::AnyData streamOCCI_(ctxOCCI_); try { if (objOCCI_->isNull()) streamOCCI_.setNull(); else objOCCI_->writeSQL(streamOCCI_); } catch (oracle::occi::SQLException& excep) { excep.setErrorCtx(ctxOCCI_); } return; } void CPerson::writeSQL(oracle::occi::AnyData& streamOCCI_) { streamOCCI_.setNumber(ID); streamOCCI_.setObject(NAME); streamOCCI_.setRef(CURR_ADDR); setVector(streamOCCI_, PREV_ADDR_L); } /*****************************************************************/ // generated method implementations for the STUDENT object type. // // Note even though CStudent derives from MyPerson, the readSQL // and writeSQL methods called are those of CPerson. /*****************************************************************/ void *CStudent::operator new(size_t size) { return oracle::occi::PObject::operator new(size); } void *CStudent::operator new(size_t size, const oracle::occi::Connection * sess, const OCCI_STD_NAMESPACE::string& table) { return oracle::occi::PObject::operator new(size, sess, table, (char *) "SCOTT.STUDENT"); } OCCI_STD_NAMESPACE::string CStudent::getSQLTypeName() const { return OCCI_STD_NAMESPACE::string("SCOTT.STUDENT"); } CStudent::CStudent() { } void *CStudent::readSQL(void *ctxOCCI_) { CStudent *objOCCI_ = new CStudent(ctxOCCI_); oracle::occi::AnyData streamOCCI_(ctxOCCI_); try { if (streamOCCI_.isNull()) objOCCI_->setNull(); else objOCCI_->readSQL(streamOCCI_); } catch (oracle::occi::SQLException& excep) { delete objOCCI_; excep.setErrorCtx(ctxOCCI_); return (void *)NULL; } return (void *)objOCCI_; } void CStudent::readSQL(oracle::occi::AnyData& streamOCCI_) { CPerson::readSQL(streamOCCI_); SCHOOL_NAME = streamOCCI_.getString(); } void CStudent::writeSQL(void *objectOCCI_, void *ctxOCCI_) { CStudent *objOCCI_ = (CStudent *) objectOCCI_; oracle::occi::AnyData streamOCCI_(ctxOCCI_); try { if (objOCCI_->isNull()) streamOCCI_.setNull(); else objOCCI_->writeSQL(streamOCCI_); } catch (oracle::occi::SQLException& excep) { excep.setErrorCtx(ctxOCCI_); } return; } void CStudent::writeSQL(oracle::occi::AnyData& streamOCCI_) { CPerson::writeSQL(streamOCCI_); streamOCCI_.setString(SCHOOL_NAME); }
To extend the functionality of OTT generated code, at times programmers may want to add code in the OTT generated file. The way OTT can distinguish between OTT generated code and code added by the user is by looking for some predefined markers (tags). OTT recognizes OTT_USERCODE_START
as the "start of user code marker", and OTT_USERCODE_END
as the "end of user code marker".
For OTT marker support, a user block is defined as
OTT_USERCODE_START + user added code + OTT_USERCODE_END
OTT marker support enables carring forward the user added blocks across multiple runs of OTT.
Following bullets describe the properties of OTT markers support.
USE_MARKER=TRUE
from the very first time OTT is invoked to generate a file.USE_MARKER=TRUE
is used:
#ifndef OTT_USERCODE_START # define OTT_USERCODE_START #endif #ifndef OTT_USERCODE_END # define OTT_USERCODE_END #endif
OTT_USERCODE_START
and OTT_USERCODE_END
, must be preceded and followed by white space.User modified code:
1 // --- modified generated code 2 OTT_USERCODE_START 3 // --- including "myfullname.h" 4 #ifndef MYFULLNAME_ORACLE 5 # include "myfullname.h" 6 #endif 7 OTT_USERCODE_END 8 // --- end of code addition
Carried forward code:
1 OTT_USERCODE_START 2 // --- including "myfullname.h" 3 #ifndef MYFULLNAME_ORACLE 4 # include "myfullname.h" 5 #endif 6 OTT_USERCODE_END
In the preceeding example the 1st and 8th lines of the original code have not been carried forward.
INTYPE
file.
CASE=UPPER CASE=LOWER
TYPE employee TYPE employee
TRANSLATE SALARY$ AS salary TRANSLATE SALARY$ AS salary
DEPTNO AS department DEPTNO AS department
TYPE ADDRESS TYPE ADDRESS
TYPE item TYPE item
TYPE "Person" TYPE "Person"
TYPE PURCHASE_ORDER AS p_o TYPE PURCHASE_ORDER AS p_o
GENERATE AS
clause of INTYPE
file), OTT will fail to find out the class name with which the code was associated earlier as the class name got modified by the user in the INTYPE
file.
CASE=LOWER CASE=LOWER TYPE employee TYPE employee TRANSLATE SALARY$ AS salary TRANSLATE SALARY$ AS salary DEPTNO AS department DEPTNO AS department TYPE ADDRESS TYPE ADDRESS TYPE item TYPE item TYPE "Person" TYPE "Person"TYPE PURCHASE_ORDER AS p_o TYPE PURCHASE_ORDER AS
purchase_order
To use OTT markers user must use command line option USE_MARKER=TRUE
, to inform OTT that use of marker should be supported, while invoking OTT. User can use OTT markers as described later to carry forward user added code.
The following code example demonstrates where user code can be added in a .h file:
#ifndef ... # define ... #ifndef OTT_USERCODE_START # define OTT_USERCODE_START #endif #ifndef OTT_USERCODE_END # define OTT_USERCODE_END #endif #ifndef OCCI_ORACLE # include <occi.h> #endif OTT_USERCODE_START // user added code ... OTT_USERCODE_END #ifndef ... // OTT generated include # include " ... " #endif OTT_USERCODE_START // user added code ... OTT_USERCODE_END class <class_name_1> : public oracle::occi::PObject { protected: // OTT generated data members ... OTT_USERCODE_START // user added code ... // data member / method declaration / ... // inline method definition OTT_USERCODE_END public: void *operator new(size_t size); void *operator new(size_t size, const oracle::occi::Connection * sess, const OCCI_STD_NAMESPACE::string& table); void *operator new(size_t, void *ctxOCCI_); OCCI_STD_NAMESPACE::string getSQLTypeName() const; ... OTT_USERCODE_START // user added code ... // data member / method declaration / ... // inline method definition OTT_USERCODE_END }; OTT_USERCODE_START // user added code ... OTT_USERCODE_END class <class_name_2> : public oracle::occi::PObject { ... ... }; OTT_USERCODE_START // user added code ... OTT_USERCODE_END #endif // end of .h file
The following code example demonstrates where user code can be added in a .cpp file:
#ifndef OTT_USERCODE_START # define OTT_USERCODE_START #endif #ifndef OTT_USERCODE_END # define OTT_USERCODE_END #endif #ifndef ... # include " ... " #endif OTT_USERCODE_START // user added code ... ... OTT_USERCODE_END #ifndef ... # include " ... " #endif OTT_USERCODE_START // user added code ... ... OTT_USERCODE_END /*************************************************************/ // generated method implementations for the ... object type. /*************************************************************/ void *<class_name_1>::operator new(size_t size) { return oracle::occi::PObject::operator new(size); } ... // end of .cpp file
Here is an example to demonstrate how OTT markers can be used to solve the problem introduced by a particular use of GENERATE AS
clause.
In this example, because of the use of GENERATE AS
clause, FULL_NAME_O
was generated as FullName
, which was intended to be an user extended class. Since FullName
is an attribute of PERSON_O
class, compiler will flag an error if it does not find the class declaration for FullName.
With marker support user now can add the header file where needed, or a forward declaration, or a class declaration and be assured of getting the user added block carried forward across runs of OTT.
In this example class declaration of FullName
is put in mdemo1.h
file.
Command line to invoke OTT:
ott case=same userid=scott/tiger code=cpp intype=mdemo1.typ hfile=mdemo1.h cppfile=mdemo1o.cpp use_marker=true
The build command:
make -f demo_rdbms.mk mdemo1
Following are the files used for this demo program:
mdemo1.sql
is the SQLs to create type, tables, and so onmdemo1.typ
is the INTYPE filemdemo1.h
is the OTT generated header file with user addeed codemdemo1o.cpp
is the OTT generated .cpp file with user added codemdemo1m.cpp
is the OTT generated map filemdemo1m.h
is the OTT generated header file for map filemymdemo1.h
is the user defined header filemdemo1.cpp
is the user defined main program// ----------------------------------------------------- // mdemo1.sql : SQLs to create type, tables, and so on. // ----------------------------------------------------- connect scott/tiger; DROP TABLE PERSON_TAB; DROP TABLE ADDR_TAB; DROP TYPE PERSON_O; DROP TYPE ADDRESS_O; DROP TYPE FULL_NAME_O; CREATE TYPE ADDRESS_O AS OBJECT ( "state" CHAR(20), "zip" CHAR(20) ) / CREATE TABLE ADDR_TAB OF ADDRESS_O; CREATE TYPE FULL_NAME_O AS OBJECT ( "first_name" CHAR(20), "last_name" CHAR(20) ) / CREATE TYPE PERSON_O AS OBJECT ( "id" integer, "name" FULL_NAME_O, "addr" REF ADDRESS_O ) / CREATE TABLE PERSON_TAB OF PERSON_O; QUIT;
// -------------------------- // mdemo1.typ : INTYPE file // -------------------------- CASE=SAME MAPFILE=mdemo1m.cpp TYPE FULL_NAME_O GENERATE FULL_NAME_O AS FullName TYPE ADDRESS_O TYPE PERSON_O GENERATE PERSON_O AS Person
// -------------------------------------------------------------- // mdemo1.h : OTT generated header file with user addeed code // -------------------------------------------------------------- #ifndef MDEMO1_ORACLE # define MDEMO1_ORACLE #ifndef OTT_USERCODE_START # define OTT_USERCODE_START #endif #ifndef OTT_USERCODE_END # define OTT_USERCODE_END #endif #ifndef OCCI_ORACLE # include <occi.h> #endif OTT_USERCODE_START #include "mymdemo1.h" OTT_USERCODE_END /************************************************************/ // generated declarations for the ADDRESS_O object type. /************************************************************/ class ADDRESS_O : public oracle::occi::PObject { protected: OCCI_STD_NAMESPACE::string state; OCCI_STD_NAMESPACE::string zip; public:
void *operator new(size_t size); void *operator new(size_t size, const oracle::occi::Connection * sess, const OCCI_STD_NAMESPACE::string& table); void *operator new(size_t, void *ctxOCCI_); OCCI_STD_NAMESPACE::string getSQLTypeName() const; ADDRESS_O(); ADDRESS_O(void *ctxOCCI_) : oracle::occi::PObject (ctxOCCI_) { }; static void *readSQL(void *ctxOCCI_); virtual void readSQL(oracle::occi::AnyData& streamOCCI_); static void writeSQL(void *objOCCI_, void *ctxOCCI_); virtual void writeSQL(oracle::occi::AnyData& streamOCCI_); OTT_USERCODE_START ADDRESS_O(string state_i, string zip_i); void displayInfo(); OTT_USERCODE_END }; /************************************************************/ // generated declarations for the FULL_NAME_O object type. /************************************************************/
class FULL_NAME_O : public oracle::occi::PObject { protected: OCCI_STD_NAMESPACE::string first_name; OCCI_STD_NAMESPACE::string last_name; public: void *operator new(size_t size); void *operator new(size_t size, const oracle::occi::Connection * sess, const OCCI_STD_NAMESPACE::string& table); void *operator new(size_t, void *ctxOCCI_); OCCI_STD_NAMESPACE::string getSQLTypeName() const; FULL_NAME_O(); FULL_NAME_O(void *ctxOCCI_) : oracle::occi::PObject (ctxOCCI_) { }; static void *readSQL(void *ctxOCCI_); virtual void readSQL(oracle::occi::AnyData& streamOCCI_); static void writeSQL(void *objOCCI_, void *ctxOCCI_); virtual void writeSQL(oracle::occi::AnyData& streamOCCI_); }; OTT_USERCODE_START class FullName : public FULL_NAME_O { public: FullName(string FirstName, string LastName); void displayInfo(); const string getFirstName() const { return first_name;}}; OTT_USERCODE_END /************************************************************/ // generated declarations for the PERSON_O object type. /************************************************************/ class PERSON_O : public oracle::occi::PObject { protected: oracle::occi::Number id; FullName * name; oracle::occi::Ref< ADDRESS_O > addr; public: void *operator new(size_t size); void *operator new(size_t size, const oracle::occi::Connection * sess, const OCCI_STD_NAMESPACE::string& table); void *operator new(size_t, void *ctxOCCI_); OCCI_STD_NAMESPACE::string getSQLTypeName() const; PERSON_O(); PERSON_O(void *ctxOCCI_) : oracle::occi::PObject (ctxOCCI_) { }; static void *readSQL(void *ctxOCCI_); virtual void readSQL(oracle::occi::AnyData& streamOCCI_); static void writeSQL(void *objOCCI_, void *ctxOCCI_); virtual void writeSQL(oracle::occi::AnyData& streamOCCI_); }; OTT_USERCODE_START class Person : public PERSON_O { public: Person(int id_i, FullName* name_i, Ref<ADDRESS_O>& addr_i); void move(const Ref<ADDRESS_O>& new_addr); void displayInfo(); }; OTT_USERCODE_END #endif
// ------------------------------------------------------------ // mdemo1o.cpp : OTT generated .cpp file with user added code // ------------------------------------------------------------ #ifndef OTT_USERCODE_START # define OTT_USERCODE_START #endif #ifndef OTT_USERCODE_END # define OTT_USERCODE_END #endif #ifndef MDEMO1_ORACLE # include "mdemo1.h" #endif OTT_USERCODE_START // initialize FullName FullName::FullName(string FirstName, string LastName) { first_name = FirstName; last_name = LastName; } // display all the information in FullName void FullName::displayInfo() { cout << "FIRST NAME is " << first_name << endl; cout << "LAST NAME is " << last_name << endl; } // initialize ADDRESS_O ADDRESS_O::ADDRESS_O(string state_i, string zip_i) { state = state_i; zip = zip_i; } // display all the information in ADDRESS_O void ADDRESS_O::displayInfo() { cout << "STATE is " << state << endl; cout << "ZIP is " << zip << endl; } // initialize Person Person::Person(int id_i, FullName *name_i, Ref<ADDRESS_O>& addr_i) { id = id_i; name = name_i; addr =addr_i ; } // Move Person from curr_addr to new_addr void Person::move(const Ref<ADDRESS_O>& new_addr) { addr = new_addr; this->markModified(); // mark the object as dirty } // Display all the information of Person void Person::displayInfo() { cout << "ID is " << (int)id << endl; name->displayInfo(); // de-referencing the Ref attribute using -> operator addr->displayInfo(); } OTT_USERCODE_END /*****************************************************************/ // generated method implementations for the ADDRESS_O object type. /*****************************************************************/ void *ADDRESS_O::operator new(size_t size) { return oracle::occi::PObject::operator new(size); } void *ADDRESS_O::operator new(size_t size, const oracle::occi::Connection * sess, const OCCI_STD_NAMESPACE::string& table) { return oracle::occi::PObject::operator new(size, sess, table, (char *) "MDEMO1.ADDRESS_O"); } void *ADDRESS_O::operator new(size_t size, void *ctxOCCI_) { return oracle::occi::PObject::operator new(size, ctxOCCI_); } OCCI_STD_NAMESPACE::string ADDRESS_O::getSQLTypeName() const { return OCCI_STD_NAMESPACE::string("MDEMO1.ADDRESS_O"); } ADDRESS_O::ADDRESS_O() { } void *ADDRESS_O::readSQL(void *ctxOCCI_) { ADDRESS_O *objOCCI_ = new(ctxOCCI_) ADDRESS_O(ctxOCCI_); oracle::occi::AnyData streamOCCI_(ctxOCCI_); try { if (streamOCCI_.isNull()) objOCCI_->setNull(); else objOCCI_->readSQL(streamOCCI_); } catch (oracle::occi::SQLException& excep) { delete objOCCI_; excep.setErrorCtx(ctxOCCI_); return (void *)NULL; } return (void *)objOCCI_; } void ADDRESS_O::readSQL(oracle::occi::AnyData& streamOCCI_) { state = streamOCCI_.getString(); zip = streamOCCI_.getString(); } void ADDRESS_O::writeSQL(void *objectOCCI_, void *ctxOCCI_) { ADDRESS_O *objOCCI_ = (ADDRESS_O *) objectOCCI_; oracle::occi::AnyData streamOCCI_(ctxOCCI_); try { if (objOCCI_->isNull()) streamOCCI_.setNull(); else objOCCI_->writeSQL(streamOCCI_); } catch (oracle::occi::SQLException& excep) { excep.setErrorCtx(ctxOCCI_); } return; } void ADDRESS_O::writeSQL(oracle::occi::AnyData& streamOCCI_) { streamOCCI_.setString(state); streamOCCI_.setString(zip); } /*****************************************************************/ // generated method implementations for the FULL_NAME_O object type. /*****************************************************************/ void *FULL_NAME_O::operator new(size_t size) { return oracle::occi::PObject::operator new(size); } void *FULL_NAME_O::operator new(size_t size, const oracle::occi::Connection * sess, const OCCI_STD_NAMESPACE::string& table) { return oracle::occi::PObject::operator new(size, sess, table, (char *) "MDEMO1.FULL_NAME_O"); } void *FULL_NAME_O::operator new(size_t size, void *ctxOCCI_) { return oracle::occi::PObject::operator new(size, ctxOCCI_); } OCCI_STD_NAMESPACE::string FULL_NAME_O::getSQLTypeName() const { return OCCI_STD_NAMESPACE::string("MDEMO1.FULL_NAME_O"); } FULL_NAME_O::FULL_NAME_O() { } void *FULL_NAME_O::readSQL(void *ctxOCCI_) { FULL_NAME_O *objOCCI_ = new(ctxOCCI_) FULL_NAME_O(ctxOCCI_); oracle::occi::AnyData streamOCCI_(ctxOCCI_); try { if (streamOCCI_.isNull()) objOCCI_->setNull(); else objOCCI_->readSQL(streamOCCI_); } catch (oracle::occi::SQLException& excep) { delete objOCCI_; excep.setErrorCtx(ctxOCCI_); return (void *)NULL; } return (void *)objOCCI_; } void FULL_NAME_O::readSQL(oracle::occi::AnyData& streamOCCI_) { first_name = streamOCCI_.getString(); last_name = streamOCCI_.getString(); } void FULL_NAME_O::writeSQL(void *objectOCCI_, void *ctxOCCI_) { FULL_NAME_O *objOCCI_ = (FULL_NAME_O *) objectOCCI_; oracle::occi::AnyData streamOCCI_(ctxOCCI_); try { if (objOCCI_->isNull()) streamOCCI_.setNull(); else objOCCI_->writeSQL(streamOCCI_); } catch (oracle::occi::SQLException& excep) { excep.setErrorCtx(ctxOCCI_); } return; } void FULL_NAME_O::writeSQL(oracle::occi::AnyData& streamOCCI_) { streamOCCI_.setString(first_name); streamOCCI_.setString(last_name); } /*****************************************************************/ // generated method implementations for the PERSON_O object type. /*****************************************************************/ void *PERSON_O::operator new(size_t size) { return oracle::occi::PObject::operator new(size); } void *PERSON_O::operator new(size_t size, const oracle::occi::Connection * sess, const OCCI_STD_NAMESPACE::string& table) { return oracle::occi::PObject::operator new(size, sess, table, (char *) "MDEMO1.PERSON_O"); } void *PERSON_O::operator new(size_t size, void *ctxOCCI_) { return oracle::occi::PObject::operator new(size, ctxOCCI_); } OCCI_STD_NAMESPACE::string PERSON_O::getSQLTypeName() const { return OCCI_STD_NAMESPACE::string("MDEMO1.PERSON_O"); } PERSON_O::PERSON_O() { name = (FullName *) 0; } void *PERSON_O::readSQL(void *ctxOCCI_) { PERSON_O *objOCCI_ = new(ctxOCCI_) PERSON_O(ctxOCCI_); oracle::occi::AnyData streamOCCI_(ctxOCCI_); try { if (streamOCCI_.isNull()) objOCCI_->setNull(); else objOCCI_->readSQL(streamOCCI_); } catch (oracle::occi::SQLException& excep) { delete objOCCI_; excep.setErrorCtx(ctxOCCI_); return (void *)NULL; } return (void *)objOCCI_; } void PERSON_O::readSQL(oracle::occi::AnyData& streamOCCI_) { id = streamOCCI_.getNumber(); name = (FullName *) streamOCCI_.getObject(); addr = streamOCCI_.getRef(); } void PERSON_O::writeSQL(void *objectOCCI_, void *ctxOCCI_) { PERSON_O *objOCCI_ = (PERSON_O *) objectOCCI_; oracle::occi::AnyData streamOCCI_(ctxOCCI_); try { if (objOCCI_->isNull()) streamOCCI_.setNull(); else objOCCI_->writeSQL(streamOCCI_); } catch (oracle::occi::SQLException& excep) { excep.setErrorCtx(ctxOCCI_); } return; } void PERSON_O::writeSQL(oracle::occi::AnyData& streamOCCI_) { streamOCCI_.setNumber(id); streamOCCI_.setObject(name); streamOCCI_.setRef(addr); }
// ------------------------------------- // mdemo1m.cpp: OTT generated map file // ------------------------------------- #ifndef MDEMO1M_ORACLE # include "mdemo1m.h" #endif void mdemo1m(oracle::occi::Environment* envOCCI_) { oracle::occi::Map *mapOCCI_ = envOCCI_->getMap(); mapOCCI_->put("MDEMO1.ADDRESS_O", ADDRESS_O::readSQL, ADDRESS_O::writeSQL); mapOCCI_->put("MDEMO1.FULL_NAME_O", FULL_NAME_O::readSQL, FULL_NAME_ O::writeSQL); mapOCCI_->put("MDEMO1.PERSON_O", PERSON_O::readSQL, PERSON_O::writeSQL); }
// ---------------------------------------------------- // mdemo1m.h : OTT generated header file for map file // ---------------------------------------------------- #ifndef MDEMO1M_ORACLE # define MDEMO1M_ORACLE #ifndef OCCI_ORACLE # include <occi.h> #endif #ifndef MDEMO1_ORACLE # include "mdemo1.h" #endif void mdemo1m(oracle::occi::Environment* envOCCI_); #endif
// --------------------------------------- // mymdemo1.h : User defined header file // --------------------------------------- #include <iostream> #define USERNAME "scott" #define PASSWORD "tiger" using namespace oracle::occi; using namespace std;
// ---------------------------------------- // mdemo1.cpp : User defined main program // ---------------------------------------- #include "mdemo1.h" #include "mdemo1m.h" // global Oracle variables Environment *env; Connection *conn; Statement *stmt; ResultSet *rs; void initialize() { // Create environment env = Environment::createEnvironment(Environment::OBJECT); // Call the OTT generated function to register the mappings mdemo1m(env); // Create Connection conn = env->createConnection( USERNAME, PASSWORD ); // Create a statement stmt = conn->createStatement(); } void terminate() { // Terminate statement conn->terminateStatement(stmt); // Terminate connection env->terminateConnection(conn); // Terminate environment Environment::terminateEnvironment(env); } /* Do the work. The environment is set up. A single new entry is created in the Address table. Then it is committed. */ void dowrite() { // Create an Address ADDRESS_O *addr1 = new(conn, "ADDR_TAB") ADDRESS_O("GE", "1211"); Ref<ADDRESS_O> addr1_ref = (Ref<ADDRESS_O>) addr1->getRef(); // Create joe black FullName *name1= new FullName("Joe", "Black"); Person *person1 = new(conn, "PERSON_TAB") Person(1,name1,addr1_ref); Ref<Person> person1_ref = (Ref<Person>) person1->getRef(); // Display, using reference cout<<"-------------------"<<endl; person1_ref->displayInfo(); cout<<"-------------------"<<endl; // Commit the changes conn->commit(); // Clean up delete name1; } void doread() { // Retrieve joe black string sel_joe = "SELECT REF(p) from person_tab p where \"id\" = 1"; rs =stmt->executeQuery (sel_joe); rs =stmt->getResultSet (); // Get reference rs->next(); Ref<Person> joe_ref = (Ref<Person>) rs->getRef(1); // Display, using reference cout<<"-------------------"<<endl; joe_ref->displayInfo(); cout<<"-------------------"<<endl; } int main() { try { initialize(); dowrite(); doread(); terminate(); } catch (SQLException &e) { cout << "SQL exception :" << e.getMessage() << endl; } return 0; }
------------------- ID is 1 FIRST NAME is Joe LAST NAME is Black STATE is GE ZIP is 1211 ------------------- ------------------- ID is 1 FIRST NAME is Joe LAST NAME is Black STATE is GE ZIP is 1211 -------------------
This OCCI application example extends the OTT-generated C++ classes and translates inherited object types. Each class in this application contains a constructor to initialize class objects and a method to display the values assigned to the attributes of the object. The MyPerson
class also has a method to change the curr_addr
attribute. All the classes here are derived from the generated classes.
Create the needed types and tables for the OCCI application as illustrated in the following code example:
connect scott/tiger create type full_name as object (first_name char(20), last_name char(20)); create type address as object (state char(20), zip char(20)); create type address_tab as varray(3) of ref address; create type person as object (id number, name full_name, curr_addr ref address, prev_addr_l address_tab) not final; create type student under person (school_name char(20)); /* tables needed in the user-written occi application */ create table addr_tab of address; create table person_tab of person; create table student_tab of student;
The INTYPE
file provided to the OTT utility contains the following information:
CASE = SAME MAPFILE = registerMappings.cpp TYPE FULL_NAME GENERATE CFullName AS MyFullName HFILE=cfullname.h CPPFILE=cfullname.cpp TRANSLATE first_name as FirstName last_name as LastName TYPE ADDRESS GENERATE CAddress AS MyAddress HFILE=caddress.h CPPFILE=caddress.cpp TYPE PERSON GENERATE CPerson AS MyPerson HFILE=cperson.h CPPFILE=cperson.cpp TYPE STUDENT GENERATE CStudent AS MyStudent HFILE=cstudent.h CPPFILE=cstudent.cpp
Note:
|
To invoke the OTT utility, use the following command line statement:
ott userid=scott/tiger code=cpp attraccess=private intype=demoin.typ outtype=demoout.typ
Note:
|
The files generated by the OTT utility for this example are shown in the following sections:
#ifndef CFULLNAME_ORACLE # define CFULLNAME_ORACLE #ifndef OCCI_ORACLE # include <occi.h> #endif /************************************************************/ // generated declarations for the FULL_NAME object type. /************************************************************/ class CFullName : public oracle::occi::PObject { private: OCCI_STD_NAMESPACE::string FirstName; OCCI_STD_NAMESPACE::string LastName; public: OCCI_STD_NAMESPACE::string getFirstname() const; void setFirstname(const OCCI_STD_NAMESPACE::string &value); OCCI_STD_NAMESPACE::string getLastname() const; void setLastname(const OCCI_STD_NAMESPACE::string &value); void *operator new(size_t size); void *operator new(size_t size, const oracle::occi::Connection * sess, const OCCI_STD_NAMESPACE::string& table); OCCI_STD_NAMESPACE::string getSQLTypeName() const; CFullName(); CFullName(void *ctxOCCI_) : oracle::occi::PObject (ctxOCCI_) { }; static void *readSQL(void *ctxOCCI_); virtual void readSQL(oracle::occi::AnyData& streamOCCI_); static void writeSQL(void *objOCCI_, void *ctxOCCI_); virtual void writeSQL(oracle::occi::AnyData& streamOCCI_); }; #endif
#ifndef CADDRESS_ORACLE #define CADDRESS_ORACLE #ifndef OCCI_ORACLE #include <occi.h> #endif /************************************************************/ // generated declarations for the ADDRESS object type. /************************************************************/ class CAddress : public oracle::occi::PObject { private: OCCI_STD_NAMESPACE::string STATE; OCCI_STD_NAMESPACE::string ZIP; public: OCCI_STD_NAMESPACE::string getState() const; void setState(const OCCI_STD_NAMESPACE::string &value); OCCI_STD_NAMESPACE::string getZip() const; void setZip(const OCCI_STD_NAMESPACE::string &value); void *operator new(size_t size); void *operator new(size_t size, const oracle::occi::Connection * sess, const OCCI_STD_NAMESPACE::string& table); OCCI_STD_NAMESPACE::string getSQLTypeName() const; CAddress(); CAddress(void *ctxOCCI_) : oracle::occi::PObject (ctxOCCI_) { }; static void *readSQL(void *ctxOCCI_); virtual void readSQL(oracle::occi::AnyData& streamOCCI_); static void writeSQL(void *objOCCI_, void *ctxOCCI_); virtual void writeSQL(oracle::occi::AnyData& streamOCCI_); }; #endif
#ifndef CPERSON_ORACLE # define CPERSON_ORACLE #ifndef OCCI_ORACLE # include <occi.h> #endif #ifndef MYFULLNAME_ORACLE # include "myfullname.h" #endif #ifndef MYADDRESS_ORACLE # include "myaddress.h" #endif /************************************************************/ // generated declarations for the PERSON object type. /************************************************************/ class CPerson : public oracle::occi::PObject { private: oracle::occi::Number ID; MyFullName * NAME; oracle::occi::Ref< MyAddress > CURR_ADDR; OCCI_STD_NAMESPACE::vector< oracle::occi::Ref< MyAddress > > PREV_ADDR_L; public: oracle::occi::Number getId() const; void setId(const oracle::occi::Number &value); MyFullName * getName() const; void setName(MyFullName * value); oracle::occi::Ref< MyAddress > getCurr_addr() const; void setCurr_addr(const oracle::occi::Ref< MyAddress > &value); OCCI_STD_NAMESPACE::vector< oracle::occi::Ref< MyAddress > >& getPrev_addr_ l(); const OCCI_STD_NAMESPACE::vector< oracle::occi::Ref< MyAddress > >& getPrev_ addr_l() const; void setPrev_addr_l(const OCCI_STD_NAMESPACE::vector< oracle::occi::Ref< MyAddress > > &value); void *operator new(size_t size); void *operator new(size_t size, const oracle::occi::Connection * sess, const OCCI_STD_NAMESPACE::string& table); OCCI_STD_NAMESPACE::string getSQLTypeName() const; CPerson(); CPerson(void *ctxOCCI_) : oracle::occi::PObject (ctxOCCI_) { }; static void *readSQL(void *ctxOCCI_); virtual void readSQL(oracle::occi::AnyData& streamOCCI_); static void writeSQL(void *objOCCI_, void *ctxOCCI_); virtual void writeSQL(oracle::occi::AnyData& streamOCCI_); }; #endif
#ifndef CSTUDENT_ORACLE # define CSTUDENT_ORACLE #ifndef OCCI_ORACLE # include <occi.h> #endif #ifndef myPERSON_ORACLE # include "myperson.h" #endif /************************************************************/ // generated declarations for the STUDENT object type. /************************************************************/ class CStudent : public MyPerson { private: OCCI_STD_NAMESPACE::string SCHOOL_NAME; public: OCCI_STD_NAMESPACE::string getSchool_name() const; void setSchool_name(const OCCI_STD_NAMESPACE::string &value); void *operator new(size_t size); void *operator new(size_t size, const oracle::occi::Connection * sess, const OCCI_STD_NAMESPACE::string& table); OCCI_STD_NAMESPACE::string getSQLTypeName() const; CStudent(); CStudent(void *ctxOCCI_) : MyPerson (ctxOCCI_) { }; static void *readSQL(void *ctxOCCI_); virtual void readSQL(oracle::occi::AnyData& streamOCCI_); static void writeSQL(void *objOCCI_, void *ctxOCCI_); virtual void writeSQL(oracle::occi::AnyData& streamOCCI_); }; #endif
#ifndef CFULLNAME_ORACLE # include "cfullname.h" #endif /*****************************************************************/ // generated method implementations for the FULL_NAME object type. /*****************************************************************/ OCCI_STD_NAMESPACE::string CFullName::getFirstname() const { return FirstName; } void CFullName::setFirstname(const OCCI_STD_NAMESPACE::string &value) { FirstName = value; } OCCI_STD_NAMESPACE::string CFullName::getLastname() const { return LastName; } void CFullName::setLastname(const OCCI_STD_NAMESPACE::string &value) { LastName = value; } void *CFullName::operator new(size_t size) { return oracle::occi::PObject::operator new(size); } void *CFullName::operator new(size_t size, const oracle::occi::Connection * sess, const OCCI_STD_NAMESPACE::string& table) { return oracle::occi::PObject::operator new(size, sess, table, (char *) "SCOTT.FULL_NAME"); } OCCI_STD_NAMESPACE::string CFullName::getSQLTypeName() const { return OCCI_STD_NAMESPACE::string("SCOTT.FULL_NAME"); } CFullName::CFullName() { } void *CFullName::readSQL(void *ctxOCCI_) { CFullName *objOCCI_ = new CFullName(ctxOCCI_); oracle::occi::AnyData streamOCCI_(ctxOCCI_); try { if (streamOCCI_.isNull()) objOCCI_->setNull(); else objOCCI_->readSQL(streamOCCI_); } catch (oracle::occi::SQLException& excep) { delete objOCCI_; excep.setErrorCtx(ctxOCCI_); return (void *)NULL; } return (void *)objOCCI_; } void CFullName::readSQL(oracle::occi::AnyData& streamOCCI_) { FirstName = streamOCCI_.getString(); LastName = streamOCCI_.getString(); } void CFullName::writeSQL(void *objectOCCI_, void *ctxOCCI_) { CFullName *objOCCI_ = (CFullName *) objectOCCI_; oracle::occi::AnyData streamOCCI_(ctxOCCI_); try { if (objOCCI_->isNull()) streamOCCI_.setNull(); else objOCCI_->writeSQL(streamOCCI_); } catch (oracle::occi::SQLException& excep) { excep.setErrorCtx(ctxOCCI_); } return; } void CFullName::writeSQL(oracle::occi::AnyData& streamOCCI_) { streamOCCI_.setString(FirstName); streamOCCI_.setString(LastName); }
#ifndef CADDRESS_ORACLE # include "caddress.h" #endif /*****************************************************************/ // generated method implementations for the ADDRESS object type. /*****************************************************************/ OCCI_STD_NAMESPACE::string CAddress::getState() const { return STATE; } void CAddress::setState(const OCCI_STD_NAMESPACE::string &value) { STATE = value; } OCCI_STD_NAMESPACE::string CAddress::getZip() const { return ZIP; } void CAddress::setZip(const OCCI_STD_NAMESPACE::string &value) { ZIP = value; } void *CAddress::operator new(size_t size) { return oracle::occi::PObject::operator new(size); } void *CAddress::operator new(size_t size, const oracle::occi::Connection * sess, const OCCI_STD_NAMESPACE::string& table) { return oracle::occi::PObject::operator new(size, sess, table, (char *) "SCOTT.ADDRESS"); } OCCI_STD_NAMESPACE::string CAddress::getSQLTypeName() const { return OCCI_STD_NAMESPACE::string("SCOTT.ADDRESS"); } CAddress::CAddress() { } void *CAddress::readSQL(void *ctxOCCI_) { CAddress *objOCCI_ = new CAddress(ctxOCCI_); oracle::occi::AnyData streamOCCI_(ctxOCCI_); try { if (streamOCCI_.isNull()) objOCCI_->setNull(); else objOCCI_->readSQL(streamOCCI_); } catch (oracle::occi::SQLException& excep) { delete objOCCI_; excep.setErrorCtx(ctxOCCI_); return (void *)NULL; } return (void *)objOCCI_; } void CAddress::readSQL(oracle::occi::AnyData& streamOCCI_) { STATE = streamOCCI_.getString(); ZIP = streamOCCI_.getString(); } void CAddress::writeSQL(void *objectOCCI_, void *ctxOCCI_) { CAddress *objOCCI_ = (CAddress *) objectOCCI_; oracle::occi::AnyData streamOCCI_(ctxOCCI_); try { if (objOCCI_->isNull()) streamOCCI_.setNull(); else objOCCI_->writeSQL(streamOCCI_); } catch (oracle::occi::SQLException& excep) { excep.setErrorCtx(ctxOCCI_); } return; } void CAddress::writeSQL(oracle::occi::AnyData& streamOCCI_) { streamOCCI_.setString(STATE); streamOCCI_.setString(ZIP); }
#ifndef CPERSON_ORACLE # include "cperson.h" #endif /*****************************************************************/ // generated method implementations for the PERSON object type. /*****************************************************************/ oracle::occi::Number CPerson::getId() const { return ID; } void CPerson::setId(const oracle::occi::Number &value) { ID = value; } MyFullName * CPerson::getName() const { return NAME; } void CPerson::setName(MyFullName * value) { NAME = value; } oracle::occi::Ref< MyAddress > CPerson::getCurr_addr() const { return CURR_ADDR; } void CPerson::setCurr_addr(const oracle::occi::Ref< MyAddress > &value) { CURR_ADDR = value; } OCCI_STD_NAMESPACE::vector< oracle::occi::Ref< MyAddress > >& CPerson::getPrev_ addr_l() { return PREV_ADDR_L; } const OCCI_STD_NAMESPACE::vector< oracle::occi::Ref< MyAddress > >& CPerson::getPrev_addr_l() const { return PREV_ADDR_L; } void CPerson::setPrev_addr_l(const OCCI_STD_NAMESPACE::vector< oracle::occi::Ref< MyAddress > > &value) { PREV_ADDR_L = value; } void *CPerson::operator new(size_t size) { return oracle::occi::PObject::operator new(size); } void *CPerson::operator new(size_t size, const oracle::occi::Connection * sess, const OCCI_STD_NAMESPACE::string& table) { return oracle::occi::PObject::operator new(size, sess, table, (char *) "SCOTT.PERSON"); } OCCI_STD_NAMESPACE::string CPerson::getSQLTypeName() const { return OCCI_STD_NAMESPACE::string("SCOTT.PERSON"); } CPerson::CPerson() { NAME = (MyFullName *) 0; } void *CPerson::readSQL(void *ctxOCCI_) { CPerson *objOCCI_ = new CPerson(ctxOCCI_); oracle::occi::AnyData streamOCCI_(ctxOCCI_); try { if (streamOCCI_.isNull()) objOCCI_->setNull(); else objOCCI_->readSQL(streamOCCI_); } catch (oracle::occi::SQLException& excep) { delete objOCCI_; excep.setErrorCtx(ctxOCCI_); return (void *)NULL; } return (void *)objOCCI_; } void CPerson::readSQL(oracle::occi::AnyData& streamOCCI_) { ID = streamOCCI_.getNumber(); NAME = (MyFullName *) streamOCCI_.getObject(); CURR_ADDR = streamOCCI_.getRef(); getVector(streamOCCI_, PREV_ADDR_L); } void CPerson::writeSQL(void *objectOCCI_, void *ctxOCCI_) { CPerson *objOCCI_ = (CPerson *) objectOCCI_; oracle::occi::AnyData streamOCCI_(ctxOCCI_); try { if (objOCCI_->isNull()) streamOCCI_.setNull(); else objOCCI_->writeSQL(streamOCCI_); } catch (oracle::occi::SQLException& excep) { excep.setErrorCtx(ctxOCCI_); } return; } void CPerson::writeSQL(oracle::occi::AnyData& streamOCCI_) { streamOCCI_.setNumber(ID); streamOCCI_.setObject(NAME); streamOCCI_.setRef(CURR_ADDR); setVector(streamOCCI_, PREV_ADDR_L); }
#ifndef CSTUDENT_ORACLE # include "cstudent.h" #endif /*****************************************************************/ // generated method implementations for the STUDENT object type. /*****************************************************************/ OCCI_STD_NAMESPACE::string CStudent::getSchool_name() const { return SCHOOL_NAME; } void CStudent::setSchool_name(const OCCI_STD_NAMESPACE::string &value) { SCHOOL_NAME = value; } void *CStudent::operator new(size_t size) { return oracle::occi::PObject::operator new(size); } void *CStudent::operator new(size_t size, const oracle::occi::Connection * sess, const OCCI_STD_NAMESPACE::string& table) { return oracle::occi::PObject::operator new(size, sess, table, (char *) "SCOTT.STUDENT"); } OCCI_STD_NAMESPACE::string CStudent::getSQLTypeName() const { return OCCI_STD_NAMESPACE::string("SCOTT.STUDENT"); } CStudent::CStudent() { } void *CStudent::readSQL(void *ctxOCCI_) { CStudent *objOCCI_ = new CStudent(ctxOCCI_); oracle::occi::AnyData streamOCCI_(ctxOCCI_); try { if (streamOCCI_.isNull()) objOCCI_->setNull(); else objOCCI_->readSQL(streamOCCI_); } catch (oracle::occi::SQLException& excep) { delete objOCCI_; excep.setErrorCtx(ctxOCCI_); return (void *)NULL; } return (void *)objOCCI_; } void CStudent::readSQL(oracle::occi::AnyData& streamOCCI_) { CPerson::readSQL(streamOCCI_); SCHOOL_NAME = streamOCCI_.getString(); } void CStudent::writeSQL(void *objectOCCI_, void *ctxOCCI_) { CStudent *objOCCI_ = (CStudent *) objectOCCI_; oracle::occi::AnyData streamOCCI_(ctxOCCI_); try { if (objOCCI_->isNull()) streamOCCI_.setNull(); else objOCCI_->writeSQL(streamOCCI_); } catch (oracle::occi::SQLException& excep) { excep.setErrorCtx(ctxOCCI_); } return; } void CStudent::writeSQL(oracle::occi::AnyData& streamOCCI_) { CPerson::writeSQL(streamOCCI_); streamOCCI_.setString(SCHOOL_NAME); }
#ifndef REGISTERMAPPINGS_ORACLE # define REGISTERMAPPINGS_ORACLE #ifndef OCCI_ORACLE # include <occi.h> #endif #ifndef CFULLNAME_ORACLE # include "cfullname.h" #endif #ifndef CADDRESS_ORACLE # include "caddress.h" #endif #ifndef CPERSON_ORACLE # include "cperson.h" #endif #ifndef CSTUDENT_ORACLE # include "cstudent.h" #endif void registerMappings(oracle::occi::Environment* envOCCI_); #endif
#ifndef REGISTERMAPPINGS_ORACLE #include "registerMappings.h" #endif void registerMappings(oracle::occi::Environment* envOCCI_) { oracle::occi::Map *mapOCCI_ = envOCCI_->getMap(); mapOCCI_->put("SCOTT.FULL_NAME", CFullName::readSQL, CFullName::writeSQL); mapOCCI_->put("SCOTT.ADDRESS", CAddress::readSQL, CAddress::writeSQL); mapOCCI_->put("SCOTT.PERSON", CPerson::readSQL, CPerson::writeSQL); mapOCCI_->put("SCOTT.STUDENT", CStudent::readSQL, CStudent::writeSQL); }
#ifndef MYFULLNAME_ORACLE # define MYFULLNAME_ORACLE using namespace oracle::occi; using namespace std; #ifndef CFULLNAME_ORACLE #include "cfullname.h" #endif /*********************************************************************/ // declarations for the MyFullName class. /*********************************************************************/ class MyFullName : public CFullName { public: MyFullName(string first_name, string last_name); void displayInfo(); }; #endif
#ifndef MYADDRESS_ORACLE # define MYADDRESS_ORACLE using namespace oracle::occi; using namespace std; #ifndef CADDRESS_ORACLE #include "caddress.h" #endif /*********************************************************************/ // declarations for the MyAddress class. /*********************************************************************/ class MyAddress : public CAddress { public: MyAddress(string state_i, string zip_i); void displayInfo(); }; #endif
#ifndef MYPERSON_ORACLE # define MYPERSON_ORACLE using namespace oracle::occi; using namespace std; #ifndef CPERSON_ORACLE #include "cperson.h" #endif /*********************************************************************/ // declarations for the MyPerson class. /*********************************************************************/ class MyPerson : public CPerson { public: MyPerson(); MyPerson(void *ctxOCCI_) : CPerson(ctxOCCI_) { }; MyPerson(Number id_i, MyFullName *name_i,const Ref<MyAddress>& addr_i); void move(const Ref<MyAddress>& new_addr); void displayInfo(); }; #endif
#ifndef MYSTUDENT_ORACLE # define MYSTUDENT_ORACLE using namespace oracle::occi; using namespace std; #ifndef CSTUDENT_ORACLE #include "cstudent.h" #endif /*********************************************************************/ // declarations for the MyStudent class. /*********************************************************************/ class MyStudent : public CStudent { public: MyStudent(Number id_i, MyFullName *name_i, Ref<MyAddress>& addr_i, string school_name); void displayInfo(); }; #endif
#include <occi.h> using namespace oracle::occi; using namespace std; #include "registerMappings.h" #include "myfullname.h" #include "myaddress.h" #include "myperson.h" #include "mystudent.h" /*********************************************************************/ // method implementations for MyFullName class. /********************************************************************/ /* initialize MyFullName */ MyFullName::MyFullName(string first_name, string last_name) { setFirstname(first_name); setLastname(last_name); } /* display all the information in MyFullName */ void MyFullName::displayInfo() { cout << "FIRST NAME is: " << getFirstname() << endl; cout << "LAST NAME is: " << getLastname() << endl; } /*********************************************************************/ // method implementations for MyAddress class. /********************************************************************/ /* initialize MyAddress */ MyAddress::MyAddress(string state_i, string zip_i) { setState(state_i); setZip(zip_i); } /* display all the information in MyAddress */ void MyAddress::displayInfo() { cout << "STATE is: " << getState() << endl; cout << "ZIP is: " << getZip() << endl; } /**********************************************************************/ // method implementations for MyPerson class. /**********************************************************************/ /* Default constructor needed because CStudent inherits from MyPerson */ MyPerson::MyPerson(){} /* initialize MyPerson */ MyPerson::MyPerson(Number id_i, MyFullName* name_i, const Ref<MyAddress>& addr_i) { setId(id_i); setName(name_i); setCurr_addr(addr_i); } /* Move Person from curr_addr to new_addr */ void MyPerson::move(const Ref<MyAddress>& new_addr) { // append curr_addr to the vector getPrev_addr_l().push_back(getCurr_addr()); setCurr_addr(new_addr); this->markModified(); } /* Display all the information of MyPerson */ void MyPerson::displayInfo() { cout << "----------------------------------- " << endl; cout << "ID is: " << (int)getId() << endl; getName()->displayInfo(); // de-referencing the Ref attribute using -> operator getCurr_addr()->displayInfo(); cout << "Prev Addr List: " << endl; for (int i = 0; i < getPrev_addr_l().size(); i++) { // access the collection elements using [] operator getPrev_addr_l()[i]->displayInfo(); } } /*********************************************************************/ // method implementations for MyStudent class. /********************************************************************/ /* initialize MyStudent */ MyStudent::MyStudent(Number id_i, MyFullName *name_i, Ref<MyAddress>& addr_i, string school_name_i) { setId(id_i); setName(name_i); setCurr_addr(addr_i); setSchool_name(school_name_i); } /* display the information in MyStudent */ void MyStudent::displayInfo() { MyPerson::displayInfo(); cout << "SCHOOL NAME is: " << getSchool_name() << endl; } void process(Connection *conn) { /* creating a persistent object of type Address in the connection, conn, and the database table, ADDR_TAB */ MyAddress *addr1 = new(conn, "ADDR_TAB") MyAddress("CA", "94065"); /* commit the transaction which results in the newly created object, addr1, being flushed to the server */ conn->commit(); MyFullName name1("Joe", "Black"); /* creating a persistent object of type Person in the connection, conn, and the database table, PERSON_TAB */ MyPerson *person1 = new(conn, "PERSON_TAB") MyPerson(1,&name1, addr1->getRef()); /* commit the transaction which results in the newly created object, person1 being flushed to the server */ conn->commit(); Statement *stmt = conn->createStatement( "SELECT REF(per) from person_tab per "); ResultSet *resultSet = stmt->executeQuery(); if (!resultSet->next()) { cout << "No record found \n"; } RefAny joe_refany = resultSet->getRef(1); Ref <MyPerson> joe_ref(joe_refany); /* de-referencing Ref using ptr() operator. operator -> and operator * also could be used to de-reference the Ref. As part of de-referencing, if the referenced object is not found in the application cache, the object data is retrieved from the server and unmarshalled into Person instance through MyPerson::readSQL() method. */ MyPerson *joe = joe_ref.ptr(); joe->displayInfo(); /* creating a persistent object of type MyAddress, in the connection, conn and the database table, ADDR_TAB */ MyAddress *new_addr1 = new(conn, "ADDR_TAB") MyAddress("PA", "92140"); conn->commit(); joe->move(new_addr1->getRef()); joe->displayInfo(); /* commit the transaction which results in the newly created object, new_addr1 and the dirty object, joe to be flushed to the server. */ conn->commit(); MyAddress *addr2 = new(conn, "ADDR_TAB") MyAddress("CA", "95065"); MyFullName name2("Jill", "White"); Ref<MyAddress> addrRef = addr2->getRef(); MyStudent *student2 = new(conn, "STUDENT_TAB") MyStudent(2, &name2, addrRef, "Stanford"); conn->commit(); Statement *stmt2 = conn->createStatement( "SELECT REF(Student) from student_tab Student where id = 2"); ResultSet *resultSet2 = stmt2->executeQuery(); if (!resultSet2->next()) { cout << "No record found \n"; } RefAny jillrefany = resultSet2->getRef(1); Ref <MyStudent> jill_ref(jillrefany); MyStudent *jill = jill_ref.ptr(); cout << jill->getPrev_addr_l().size(); jill->displayInfo(); MyAddress *new_addr2 = new(conn, "ADDR_TAB") MyAddress("CO", "80021"); conn->commit(); jill->move(new_addr2->getRef()); jill->displayInfo(); jill->markModified(); conn->commit(); /* The following delete statements delete the objects only from the application cache. To delete the objects from the server, mark_deleted() should be used. */ delete person1;> delete addr1; delete new_addr1; delete addr2; delete student2; delete new_addr2; conn->terminateStatement(stmt); conn->terminateStatement(stmt2); } /***************************************************************************/ // main function of this OCCI application. // This application connects to the database as scott/tiger, creates // the Person (Joe Black) whose Address is in CA, and commits the changes. // The Person object is then retrieved from the database and its // information is displayed. A second Address object is created (in PA), // then the previously retrieved Person object (Joe Black) is moved to // this new address. The Person object is then displayed again. // The similar commands are executed for "Jill White", a Student at Stanford, // who is moved from CA to CO. /***************************************************************************/ int main() { Environment *env = Environment::createEnvironment(Environment::OBJECT ); /* Call the OTT generated function to register the mappings */ registerMappings(env); Connection *conn = env->createConnection("scott","tiger",""); process(conn); env->terminateConnection(conn); Environment::terminateEnvironment(env); return 0; }
The output generated from the example OCCI application is:
----------------------------------- ID is: 1 FIRST NAME is: Joe LAST NAME is: Black STATE is: CA ZIP is: 94065 Prev Addr List: ----------------------------------- ID is: 1 FIRST NAME is: Joe LAST NAME is: Black STATE is: PA ZIP is: 92140 Prev Addr List: STATE is: CA ZIP is: 94065 ----------------------------------- ID is: 2 FIRST NAME is: Jill LAST NAME is: White STATE is: CA ZIP is: 95065 Prev Addr List: SCHOOL NAME is: Stanford ----------------------------------- ID is: 2 FIRST NAME is: Jill LAST NAME is: White STATE is: CO ZIP is: 80021 Prev Addr List: STATE is: CA ZIP is: 95065 SCHOOL NAME is: Stanford
Behavior of the OTT utility is controlled by parameters that are specified either on the OTT command line or in a CONFIG
file. Certain parameters may also appear in the INTYPE
file.
This section provides detailed information about the following topics:
The OTT command-line interface is used when explicitly invoking the OTT utility to translate database types into C structures or C++ classes. This is always required when developing OCI, OCCI, or Pro*C/C++ applications that use objects.
An OTT command line statement consists of the command OTT, followed by a list of OTT utility parameters.
The parameters that can appear on the OTT command line statement are listed alphabetically as follows:
[ATTRACCESS={PRIVATE|PROTECTED}]
[CASE={SAME|LOWER|UPPER|OPPOSITE}]
CODE={C|ANSI_C|KR_C|CPP}
[CONFIG=
filename
]
[CPPFILE=
filename
]
[ERRTYPE=
filename
]
[HFILE=
filename
]
[INITFILE=
filename
]
[INITFUNC=
filename
]
[INTYPE=
filename
]
[MAPFILE=
filename
]
[MAPFUNC=
filename
]
OUTTYPE=
filename
[SCHEMA_NAMES={ALWAYS|IF_NEEDED|FROM_INTYPE}]
[TRANSITIVE={TRUE|FALSE}]
[USE_MARKER={TRUE|FALSE}]
[USERID=username/password[@db_name]]
The HFILE
parameter is almost always used. If omitted, then HFILE
must be specified individually for each type in the INTYPE
file. If the OTT utility determines that a type not listed in the INTYPE
file must be translated, then an error will be reported. Therefore, it is safe to omit the HFILE
parameter only if the INTYPE
file was previously generated as an OTT OUTTYPE
file.
If the INTYPE
file is omitted, then the entire schema will be translated. See the parameter descriptions in the following section for more information.
The following is an example of an OTT command line statement (enter it as one line):
ott userid=scott/tiger intype=in.typ outtype=out.typ code=c hfile=demo.h errtype=demo.tls case=lower
Each of the OTT command line parameters is described in the following section.
Enter parameters on the OTT command line using the following format:
parameter=value
In this example, parameter
is the literal parameter string and value
is a valid parameter setting. The literal parameter string is not case sensitive.
Separate command line parameters by using either spaces or tabs.
Parameters can also appear within a configuration file, but, in that case, no whitespace is permitted within a line, and each parameter must appear on a separate line. Additionally, the parameters CASE
, CPPFILE
, HFILE
, INITFILE
, INTFUNC
, MAPFILE
, and MAPFUNC
can appear in the INTYPE
file.
OTT utility parameters are described in the following sections:
For C++ only. This parameter instructs the OTT utility whether to generate PRIVATE
or PROTECTED
access for the type attributes. If PRIVATE
is specified, the OTT utility generates an accessor (get
xxx
) and mutator (set
xxx
) method for each of the type attributes.
ATTRACCESS=PRIVATE|PROTECTED
The default is PROTECTED
.
This parameter affects the case of certain C or C++ identifiers generated by the OTT utility. The valid values of CASE
are SAME
, LOWER
, UPPER
, and OPPOSITE
.
If CASE=SAME
, the case of letters is not changed when converting database type and attribute names to C or C++ identifiers.
If CASE=LOWER
, then all uppercase letters are converted to lowercase.
If CASE=UPPER
, then all lowercase letters are converted to uppercase.
If CASE=OPPOSITE
, then all uppercase letters are converted to lowercase, and all lowercase letters are converted to uppercase.
CASE=[SAME|LOWER|UPPER|OPPOSITE]
This parameter affects only those identifiers (attributes or types not explicitly listed) not mentioned in the INTYPE
file. Case conversion takes place after a legal identifier has been generated.
The case of the C structure identifier for a type specifically mentioned in the INTYPE
file is the same as its case in the INTYPE
file. For example, consider this line included in the INTYPE
file:
TYPE Worker
The OTT utility generates the following C structure:
struct Worker {...};
On the other hand, consider an INTYPE
file that includes the following line:
TYPE wOrKeR
The OTT utility generates this C structure, which follows the case specified in the INTYPE
file:
struct wOrKeR {...};
Case insensitive SQL identifiers not mentioned in the INTYPE
file will appear in uppercase if CASE=SAME
, and in lowercase if CASE=OPPOSITE
. A SQL identifier is case insensitive if it was not quoted when it was declared.
This parameter indicates which host language is to be output by the OTT utility. CODE=C
is equivalent to CODE=ANSI_C
. CODE=CPP
must be specified for the OTT utility to generate C++ code for OCCI applications.
CODE=C|KR_C|ANSI_C|CPP
This parameter is required, and there is no default value. You must specify one of the host languages.
This parameter specifies the name of the OTT configuration file to be used. The configuration file lists commonly used parameter specifications. Parameter specifications are also read from a system configuration file found in an operating system-dependent location. All remaining parameter specifications must appear either on the command line or in the INTYPE
file.
CONFIG=filename
Note: The |
For C++ only. This parameter specifies the name of the C++ source file that will contain the method implementations generated by the OTT utility.
This parameter is required under the following conditions:
INTYPE
file must be generated and two or more CPPFILE
s are being generated. In this case, the unmentioned type goes in the CPPFILE
specified on the command line.INTYPE
parameter is not specified, and you want the OTT utility to translate all the types in the schema.The restrictions to this are similar to that of the existing HFILE
parameter restrictions already in place for Pro*C/C++ and OCI. This parameter is optional when the CPPFILE
is specified for individual types in the INTYPE
file.
CPPFILE=filename
If you supply this parameter, then a listing of the INTYPE
file is written to the ERRTYPE
file, along with all information and error messages. Information and error messages are sent to the standard output whether or not the ERRTYPE
parameter is specified.
Essentially, the ERRTYPE
file is a copy of the INTYPE
file with error messages added. In most cases, an error message will include a pointer to the text that caused the error.
If the filename specified for the ERRTYPE
parameter on the command line does not include an extension, a platform-specific extension such as .TLS
or .tls
is automatically added.
ERRTYPE=filename
This parameter specifies the name of the header (.h
) file to be generated by the OTT utility. The HFILE
specified on the command line contains the declarations of types that are mentioned in the INTYPE
file but whose header files are not specified there.
This parameter is required unless the header file for each type is specified individually in the INTYPE
file. This parameter is also required if a type not mentioned in the INTYPE
file must be generated because other types require it, and these other types are declared in two or more different files.
If the filename specified for the HFILE
parameter on the command line or in the INTYPE
file does not include an extension, a platform-specific extension such as H
or .h
is automatically added.
HFILE=filename
For OCI only. This parameter specifies the name of the initialization file to be generated by the OTT utility. If you omit this parameter, then the initialization file will not be generated.
For Pro*C/C++ programs, the INITFILE
is not necessary, because the SQLLIB run-time library performs the necessary initializations. An OCI programmer must compile and link the INITFILE
files, and must call the initialization functions when an environment handle is created.
If the filename specified for the INITFILE
parameter on the command line or in the INTYPE
file does not include an extension, a platform-specific extension such as C
or .c
is automatically added.
INITFILE=filename
For OCI only. This parameter specifies the name of the initialization function to be generated by the OTT utility.
This parameter is optional. If you omit this parameter, then the name of the initialization function is derived from the name of the INITFILE
.
INITFUNC=filename
This parameter specifies the name of the file from which to read the list of object type specifications. The OTT utility translates each type in the list.
INTYPE=filename
INTYPE=
may be omitted if USERID
and INTYPE
are the first two parameters, in that order, and USERID=
is omitted. For example,
OTT username/password filename...
If the INTYPE
parameter is not specified, all types in the user's schema will be translated.
The INTYPE
file can be thought of as a makefile for type declarations. It lists the types for which C structure declarations or C++ classes are needed. The format of the INTYPE
file is described in "Structure of the INTYPE File".
If the filename specified for the INTYPE
parameter on the command line does not include an extension, a platform-specific extension such as TYP
or .typ
is automatically added.
See Also:
"Structure of the INTYPE File" for more information about the format of the |
For C++ only. This parameter specifies the name of the mapping file (.cpp
) and corresponding header file (.h
) that is generated by the OTT utility. The .cpp
file contains the implementation of the functions to register the mappings while the .h
file contains the prototype for the function.
This parameter is required for the generation of C++. If you specify CODE=CPP
, then you must also specify a value for the MAPFILE
parameter. Otherwise, the OTT utility generates an error.
This parameter may be specified either on the command line or in the INTYPE
file.
MAPFILE=filename
For C++ only. This parameter specifies the name of the function to be used to register the mappings generated by the OTT utility.
This parameter is optional for the generation of C++. If this parameter is omitted, then the name of the function to register the mappings is derived from the filename specified in the MAPFILE
parameter.
This parameter may be specified either on the command line or in the INTYPE
file.
MAPFUNC=functionname
This parameter specifies the name of the file into which the OTT utility writes type information for all the object datatypes it processes. This file includes all types explicitly named in the INTYPE
file, and may include additional types that are translated because they are used in the declarations of other types that need to be translated. This file may be used as an INTYPE
file in a future invocation of the OTT utility.
OUTTYPE=filename
If the INTYPE
and OUTTYPE
parameters refer to the same file, then the new INTYPE
information replaces the old information in the INTYPE
file. This provides a convenient way for the same INTYPE
file to be used repeatedly in the cycle of altering types, generating type declarations, editing source code, precompiling, compiling, and debugging.
This parameter is required.
If the filename specified for the OUTTYPE
parameter on the command line or in the INTYPE
file does not include an extension, a platform-specific extension such as TYP
or .typ
is automatically added.
This parameter offers control in qualifying the database name of a type from the default schema with a schema name in the OUTTYPE
file. The OUTTYPE
file generated by the OTT utility contains information about the types processed by the OTT utility, including the type names.
SCHEMA_NAMES=ALWAYS|IF_NEEDED|FROM_INTYPE
The default value is ALWAYS.
See Also:
"SCHEMA_NAMES Usage" for further information |
This parameter indicates whether type dependencies not explicitly listed in the INTYPE
file are to be translated. Valid values are TRUE
and FALSE
.
TRANSITIVE=TRUE|FALSE
The default value is TRUE
.
If TRANSITIVE=TRUE
is specified, then types needed by other types and not mentioned in the INTYPE
file are generated.
If TRANSITIVE=FALSE
is specified, then types not mentioned in the INTYPE
file are not generated, even if they are used as attribute types of other generated types.
This parameter indicates whether OTT markers should be supported, if used, by OTT to carry forward user added. Valid values are TRUE and FALSE. The default value is FALSE
.
USE_MARKER=TRUE|FALSE
The default value is FALSE
.
If USE_MARKER=TRUE
is specified, then the added code between the markers, OTT_USER_CODESTART
and OTT_USERCODE_END
will be carried forward when the same file is generated again.
If USE_MARKER=FALSE
is specified, then the user added code will not be carried forward, even if the code is added between OTT_USERCODE_START
and OTT_USERCODE_END
markers.
This parameter specifies the Oracle username, password, and optional database name (Oracle Net database specification string). If the database name is omitted, the default database is assumed.
USERID=username/password[@db_name]
If this is the first parameter, then USERID=
may be omitted as shown:
OTT username/password ...
This parameter is optional. If you omit this parameter, then the OTT utility automatically attempts to connect to the default database as user OPS$
username
, where username
is the user's operating system username.
Supply OTT parameters on the command line, in a CONFIG
file named on the command line, or both. Some parameters are also allowed in the INTYPE
file.
The OTT utility is invoked as follows:
OTT parameters
You can name a configuration file on the command line with the CONFIG parameter as follows:
CONFIG=filename
If you name this parameter on the command line, then additional parameters are read from the configuration file named filename
.
In addition, parameters are also read from a default configuration file that resides in an operating system-dependent location. This file must exist, but can be empty. If you choose to enter data in the configuration file, note that no white space is allowed on a line and parameters must be entered one to a line.
If the OTT utility is executed without any arguments, then an online parameter reference is displayed.
The types for the OTT utility to translate are named in the file specified by the INTYPE
parameter. The parameters CASE
, CPPFILE
, HFILE
, INITFILE
, INITFUNC
, MAPFILE
, and MAPFNC
may also appear in the INTYPE
file. OUTTYPE
files generated by the OTT utility include the CASE parameter, and include the INITFILE
, and INITFUNC
parameters if an initialization file was generated or the MAPFILE
and MAPFUNC
parameters if C++ codes was generated. The OUTTYPE
file, as well as the CPPFILE
for C++, specifies the HFILE
individually for each type.
The case of the OTT command is operating system-dependent.
The INTYPE
and OUTTYPE
files list the types translated by the OTT utility and provide all the information needed to determine how a type or attribute name is translated to a legal C or C++ identifier. These files contain one or more type specifications. These files also may contain specifications of the following options:
If the CASE
, INITFILE
, INITFUNC
, MAPFILE
, or MAPFUNC
options are present, then they must precede any type specifications. If these options appear both on the command line and in the INTYPE
file, then the value on the command line is used.
See Also:
"Overview of the OUTTYPE File" for an example of a simple user-defined |
A type specification in the INTYPE
file names an object datatype that is to be translated. The following is an example of a user-created INTYPE
file:
TYPE employee TRANSLATE SALARY$ AS salary DEPTNO AS department TYPE ADDRESS TYPE PURCHASE_ORDER AS p_o
The structure of a type specification is as follows:
TYPE type_name [GENERATE type_identifier] [AS type_identifier] [VERSION [=] version_string] [HFILE [=] hfile_name] [CPPFILE [=] cppfile_name] [TRANSLATE{member_name [AS identifier]}...]
The type_name
syntax follows this form:
[schema_name.]type_name
In this syntax, schema_name
is the name of the schema that owns the given object datatype, and type_name
is the name of the type. The default schema, if one is not specified, is that of the userID invoking the OTT utility. To use a specific schema, you must use schema_name.
The components of the type specification are:
type_name
: Name of the object datatype.type_identifier
: C / C++ identifier used to represent the class. The GENERATE
clause is used to specify the name of the class that the OTT utility generates. The AS clause specifies the name of the class that you write. The GENERATE
clause is typically used to extend a class. The AS
clause, when optionally used without the GENERATE
clause, specifies the name of the C structure or the C++ class that represents the user-defined type.version_string
: Version string of the type that was used when the code was generated by the previous invocation of the OTT utility. The version string is generated by the OTT utility and written to the OUTTYPE
file, which can later be used as the INTYPE
file in later invocations of the OTT utility. The version string does not affect how the OTT utility operates, but can be used to select which version of the object datatype is used in the running program.hfile_name
: Name of the header file into which the declarations of the corresponding class are written. If you omit the HFILE
clause, then the file specified by the command line HFILE
parameter is used.cppfile_name
: Name of the C++ source file into which the method implementations of the corresponding class is written. If you omit the CPPFILE
clause, the file specified by the command line CPPFILE
parameter is used.member_name
: Name of an attribute (data member) that is to be translated to the identifier.identifier
: C / C++ identifier used to represent the attribute in the program. You can specify identifiers in this way for any number of attributes. The default name mapping algorithm is used for the attributes not mentioned.An object datatype may need to be translated for one of two reasons:
INTYPE
file.TRANSITIVE
parameter is set to TRUE
.If a type that is not mentioned explicitly is required by types declared in exactly one file, then the translation of the required type is written to the same files as the explicitly declared types that require it.
If a type that is not mentioned explicitly is required by types declared in two or more different files, then the translation of the required type is written to the global HFILE
file.
HFILE
files generated by the OTT utility #include
other necessary files, and #define
a symbol constructed from the name of the file. This symbol #define
d can then be used to determine if the related HFILE
file has already been #include
d. Consider, for example, a database with the following types:
create type px1 AS OBJECT (col1 number, col2 integer); create type px2 AS OBJECT (col1 px1); create type px3 AS OBJECT (col1 px1);
The INTYPE
file contains the following information:
CASE=lower type pxl hfile tott95a.h type px3 hfile tott95b.h
You invoke the OTT utility as follows:
ott scott/tiger tott95i.typ outtype=tott95o.typ code=c
The OTT utility then generates the following two header files, named tott95a.h
and tott95b.h
.
The content of the tott95a.h
file is as follows:
#ifndef TOTT95A_ORACLE #define TOTT95A_ORACLE #ifndef OCI_ORACLE #include <oci.h> #endif typedef OCIRef px1_ref; struct px1 { OCINumber col1; OCINumber col2; } typedef struct px1 px1; struct px1_ind { OCIInd _atomic; OCIInd col1; OCIInd col2; } typedef struct px1_ind px1_ind; #endif
The content of the tott95b.h
file is as follows:
#ifndef TOTT95B_ORACLE #define TOTT95B_ORACLE #ifndef OCI_ORACLE #include <oci.h> #endif #ifndef TOTT95A_ORACLE #include "tott95a.h" #endif typedef OCIRef px3_ref; struct px3 { struct px1 col1; }; typedef struct px3 px3; struct px3_ind { OCIInd _atomic; struct px1_ind col1 }; typedef struct px3_ind px3_ind; #endif
In the tott95b.h file, the symbol TOTT95B_ORACLE
is #define
d at the beginning of the file. This enables you to conditionally #include
this header file in another file. To accomplish this, you would use the following construct:
#ifndef TOTT95B_ORACLE #include "tott95b.h" #endif
By using this technique, you can #include
tott95b.h
in, say foo.h
, without having to know whether some other file #includ
ed in foo.h
also #include
s tott95b.h
.
After the definition of the symbol TOTT95B_ORACLE
, the file oci.h is #include
d. Every HFILE
generated by the OTT utility includes oci.h
, which contains type and function declarations that the Pro*C/C++ or OCI programmer will find useful. This is the only case in which the OTT utility uses angle brackets in a #include
.
Next, the file tott95a.h
is included because it contains the declaration of struct px1
, that tott95b.h
requires. When the INTYPE
file requests that type declarations be written to more than one file, the OTT utility determines which other files each HFILE
must #include
, and generates each necessary #include
.
Note that the OTT utility uses quotes in this #include
. When a program including tott95b.h
is compiled, the search for tott95a.h
begins where the source program was found, and will thereafter follow an implementation-defined search rule. If tott95a.h
cannot be found in this way, then a complete filename (for example, a UNIX absolute path name beginning with a slash character (/)) should be used in the INTYPE
file to specify the location of tott95a.h
.
This parameter affects whether the name of a type from the default schema, to which the OTT utility is connected, is qualified with a schema name in the OUTTYPE
file.
The name of a type from a schema other that the default schema is always qualified with a schema name in the OUTTYPE
file.
The schema name, or its absence, determines in which schema the type is found during program execution.
There are three valid values for the SCHEMA_NAMES
parameter:
SCHEMA_NAMES=ALWAYS
(default)
All type names in the OUTTYPE
file are qualified with a schema name.
SCHEMA_NAMES=IF_NEEDED
The type names in the OUTTYPE
file that belong to the default schema are not qualified with a schema name. As always, type names belonging to other schemas are qualified with the schema name.
SCHEMA_NAMES=FROM_INTYPE
A type mentioned in the INTYPE
file is qualified with a schema name in the OUTTYPE
file if, and only if, it was qualified with a schema name in the INTYPE
file. A type in the default schema that is not mentioned in the INTYPE
file but that is generated because of type dependencies, is written with a schema name only if the first type encountered by the OTT utility that depends on it is also written with a schema name. However, a type that is not in the default schema to which the OTT utility is connected is always written with an explicit schema name.
The OUTTYPE
file generated by the OTT utility is the Pro*C/C++ INTYPE
file. This file matches database type names to C structure names. This information is used at runtime to make sure that the correct database type is selected into the structure. If a type appears with a schema name in the OUTTYPE
file (Pro*C/C++ INTYPE
file), then the type is found in the named schema during program execution. If the type appears without a schema name, then the type is found in the default schema to which the program connects, which may be different from the default schema the OTT utility used.
Consider an example where the SCHEMA_NAMES
parameter is set to FROM_INTYPE
, and the INTYPE
file contains the following:
TYPE Person TYPE joe.Dept TYPE sam.Company
The Pro*C/C++ application that uses the OTT-generated structures uses the types sam.Company
, joe.Dept
, and Person
. Person
without a schema name refers to the Person
type in the schema to which the application is connected.
If the OTT utility and the application both connect to schema joe, then the application uses the same type (joe.Person
) that the OTT utility uses. If the OTT utility connects to schema joe
but the application connects to schema mary
, then the application uses the type mary.Person
. This behavior is appropriate only if the same CREATE TYPE Person
statement has been executed in schema joe
and schema mary
.
On the other hand, the application uses type joe.Dept
regardless of which schema the application is connected to. If this is the behavior you want, then be sure to include schema names with your type names in the INTYPE
file.
In some cases, the OTT utility translates a type that the user did not explicitly name. For example, consider the following SQL declarations:
CREATE TYPE Address AS OBJECT ( street VARCHAR2(40), city VARCHAR(30), state CHAR(2), zip_code CHAR(10) ); CREATE TYPE Person AS OBJECT ( name CHAR(20), age NUMBER, addr ADDRESS );
Suppose that the OTT utility connects to schema joe
, SCHEMA_NAMES=FROM_INTYPE
is specified, and the user's INTYPE
files include either
TYPE Person
or
TYPE joe.Person
The INTYPE file does not mention the type joe.Address
, which is used as a nested object type in type joe.Person
.
If Type Person
appears in the INTYPE
file, then TYPE Person
and TYPE Address
appears in the OUTTYPE
file.
If TYPE joe.Person
appears in the INTYPE
file, then TYPE joe.Person
and TYPE joe.Address
appear in the OUTTYPE
file.
If the joe.Address
type is embedded in several types translated by the OTT utility, but it is not explicitly mentioned in the INTYPE
file, then the decision of whether to use a schema name is made the first time the OTT utility encounters the embedded joe.Address
type. If, for some reason, the user wants type joe.Address
to have a schema name but does not want type Person
to have one, then you must explicitly request this in the INTYPE
file as follows:
TYPE joe.Address
In the usual case in which each type is declared in a single schema, it is safest for you to qualify all type names with schema names in the INTYPE
file.
When the OTT utility creates a C or C++ identifier name for an object type or attribute, it translates the name from the database character set to a legal C or C++ identifier. First, the name is translated from the database character set to the character set used by the OTT utility. Next, if a translation of the resulting name is supplied in the INTYPE
file, that translation is used. Otherwise, the OTT utility translates the name character-by-character to the compiler character set, applying the character case specified in the CASE parameter. The following text describes this in more detail.
When the OTT utility reads the name of a database entity, the name is automatically translated from the database character set to the character set used by the OTT utility. In order for the OTT utility to read the name of the database entity successfully, all the characters of the name must be found in the OTT character set, although a character may have different encodings in the two character sets.
The easiest way to guarantee that the character set used by the OTT utility contains all the necessary characters is to make it the same as the database character set. Note, however, that the OTT character set must be a superset of the compiler character set. That is, if the compiler character set is 7-bit ASCII, then the OTT character set must include 7-bit ASCII as a subset, and if the compiler character set is 7-bit EBCDIC, then the OTT character set must include 7-bit EBCDIC as a subset. The user specifies the character set that the OTT utility uses by setting the NLS_LANG
environment variable, or by some other operating system-specific mechanism.
Once the OTT utility has read the name of a database entity, it translates the name from the character set used by the OTT utility to the compiler's character set. If a translation of the name appears in the INTYPE
file, then the OTT utility uses that translation.
Otherwise, the OTT utility attempts to translate the name as follows:
CASE
parameter is defined, and any character that is not legal in a C or C++ identifier, or that has no translation in the compiler character set, is replaced by an underscore character (_). If at least one character is replaced by an underscore, then the OTT utility gives a warning message. If all the characters in a name are replaced by underscores, the OTT utility gives an error message.Character-by-character name translation does not alter underscores, digits, or single-byte letters that appear in the compiler character set, so legal C or C++ identifiers are not altered.
Name translation may, for example, translate accented single-byte characters such as o with an umlaut or an a with an accent grave to o or a, with no accent, and may translate a multibyte letter to its single-byte equivalent. Name translation will typically fail if the name contains multibyte characters that lack single-byte equivalents. In this case, the user must specify name translations in the INTYPE
file.
The OTT utility will not detect a naming clash caused by two or more database identifiers being mapped to the same C name, nor will it detect a naming problem where a database identifier is mapped to a C keyword.
Currently, the OTT utility determines if two files are the same by comparing the filenames provided by the user either on the command line or in the INTYPE
file. But one potential problem can occur when the OTT utility needs to know if two filenames refer to the same file. For example, if the OTT-generated file foo.h requires a type declaration written to foo1.h
, and another type declaration written to /private/smith/foo1.h
, then the OTT utility should generate one #include
if the two files are the same, and two #include
s if the files are different. In practice, though, it concludes that the two files are different, and generates two #include
s as follows:
#ifndef FOO1_ORACLE #include "foo1.h" #endif #ifndef FOO1_ORACLE #include "/private/smith/foo1.h" #endif
If foo1.h
and /private/smith/foo1.h
are different files, then only the first one will be included. If foo1.h
and /private/smith/foo1.h
are the same file, then a redundant #include
will be written.
Therefore, if a file is mentioned several times on the command line or in the INTYPE
file, then each mention of the file should use exactly the same filename.
|
Copyright © 2001, 2002 Oracle Corporation. All Rights Reserved. |
|