| 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 typedefs are used to declare VARRAY, NESTED TABLE, and REF types.
In the preceeding example, the typedefs 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:
CPPFILEMAPFILEMAPFUNCATTRACCESS
| 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 (getxxx) and the mutator (setxxx) 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 ADDRESSMyAddress class instead of the CAddress class to represent vector and REF elements whose database type is ADDRESSMyAddress 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 ASpurchase_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 (getxxx) and mutator (setxxx) 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 CPPFILEs 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 #defined can then be used to determine if the related HFILE file has already been #included. 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 #defined 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 #included in foo.h also #includes tott95b.h.
After the definition of the symbol TOTT95B_ORACLE, the file oci.h is #included. 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 #includes if the files are different. In practice, though, it concludes that the two files are different, and generates two #includes 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. |
|