Character Data

This section explains how the Pro*C/C++ Precompiler handles character host variables. There are four host variable character types:

Character arrays

Pointers to strings

VARCHAR variables

Pointers to VARCHARs

Do not confuse VARCHAR (a host variable data structure supplied by the precompiler) with VARCHAR2 (an Oracle internal datatype for variable-length character strings).

Precompiler Option CHAR_MAP

The CHAR_MAP precompiler command line option is available to specify the default mapping of char[n] and char host variables. Oracle maps them to CHARZ. CHARZ implements the ANSI Fixed Character format. Strings are fixed-length, blank-padded and null-terminated. VARCHAR2 values (including nulls) are always fixed-length and blank-padded. Table 5-1 shows the possible settings of CHAR_MAP:

Table 5-1 CHAR_MAP Settings

CHAR_MAP Setting

Is Default for

Description

VARCHAR2

-

All values (including null) are fixed-length blank-padded.

CHARZ

DBMS=V7, DBMS=V8

Fixed-length blank-padded, then null-terminated. Conforms to the ANSI Fixed Character type.

STRING

New format

null-terminated. Conforms to ASCII format used in C programs.

CHARF

Previously, only through VAR or TYPE declarations.

Fixed-length blank-padded. null is left unpadded.

The default mapping is CHAR_MAP=CHARZ, which was the case in previous versions of Pro*C/C++.

Use CHAR_MAP=VARCHAR2 instead of the old DBMS=V6_CHAR, which is obsolete.

Inline Usage of the CHAR_MAP Option

Unless you declared a char or char[n] variable otherwise, the inline CHAR_MAP option determines its mapping. The following code fragment illustrates the results of setting this option inline in Pro*C/C++:

Effect of the DBMS and CHAR_MAP Options

The DBMS and CHAR_MAP options determine how Pro*C/C++ treats data in character arrays and strings. These options allow your program to observe compatibility with ANSI fixed-length strings, or to maintain compatibility with previous releases of Oracle and Pro*C/C++ that use variable-length strings. See Chapter 10, " Precompiler Options" for a complete description of the DBMS and CHAR_MAP options.

The DBMS option affects character data both on input (from your host variables to the Oracle table) and on output (from an Oracle table to your host variables).

Character Array and the CHAR_MAP Option

The mapping of character arrays can also be set by the CHAR_MAP option independent of the DBMS option. DBMS=V7 or DBMS=V8 both use CHAR_MAP=CHARZ, which can be overridden by specifying either CHAR_MAP=VARCHAR2 or STRING or CHARF.

On Input

Character Array On input, the DBMS option determines the format that a host variable character array must have in your program. When the CHAR_MAP=VARCHAR2, host variable character arrays must be blank padded, and should not be null-terminated. When the DBMS=V7 or V8, character arrays must be null-terminated ('\0').

When the CHAR_MAP option is set to VARCHAR2 trailing blanks are removed up to the first non-blank character before the value is sent to the database. An un-initialized character array can contain null characters. To make sure that the nulls are not inserted into the table, you must blank-pad the character array to its length. For example, if you execute the statements:

On Output

Character Array On output, the DBMS and CHAR_MAP options determines the format that a host variable character array will have in your program. When CHAR_MAP=VARCHAR2, host variable character arrays are blank padded up to the length of the array, but never null-terminated. When DBMS=V7 or V8 (or CHAR_MAP=CHARZ), character arrays are blank padded, then null-terminated in the final position in the array.

If you precompile the program with CHAR_MAP=VARCHAR2, name1 will contain:

"MILLER####"

that is, the name "MILLER" followed by 4 blanks, with no null-termination. (If name1 had been declared with a size of 15, there are 9 blanks following the name.)

name2 will contain:

"KING######" /* 6 trailing blanks */

If you precompile the program with DBMS=V7 or V8, name1 will contain:

"MILLER###\0" /* 3 trailing blanks, then a null-terminator */

that is, a string containing the name, blank-padded to the length of the column, followed by a null terminator. name2 will contain:

"KING#####\0"

In summary, if CHAR_MAP=VARCHAR2, the output from either a CHARACTER column or a VARCHAR2 column is blank-padded to the length of the host variable array. If DBMS=V7 or V8, the output string is always null-terminated.

Character Pointer The DBMS and CHAR_MAP options do not affect the way character data are output to a pointer host variable.

When you output data to a character pointer host variable, the pointer must point to a buffer large enough to hold the output from the table, plus one extra byte to hold a null terminator.

The precompiler runtime environment calls strlen() to determine the size of the output buffer, so make sure that the buffer does not contain any embedded nulls ('\0'). Fill allocated buffers with some value other than '\0', then null-terminate the buffer, before fetching the data.

Note:

C pointers can be used in a Pro*C/C++ program that is precompiled with DBMS=V7 or V8 and MODE=ANSI. However, pointers are not legal host variable types in a SQL standard compliant program. The FIPS flagger warns you if you use pointers as host variables.

The following code fragment uses the columns and table defined in the previous section, and shows how to declare and SELECT into character pointer host variables:

VARCHAR Variables and Pointers

On Input

VARCHAR Variables When you use a VARCHAR variable as an input host variable, your program need only place the desired string in the array member of the expanded VARCHAR declaration (emp_name1.arr in our example) and set the length member (emp_name1.len). There is no need to blank-pad the array. Exactly emp_name1.len characters are sent to Oracle, counting any blanks and nulls. In the following example, you set emp_name1.len to 8:

Pointer to a VARCHAR When you use a pointer to a VARCHAR as an input host variable, you must allocate enough memory for the expanded VARCHAR declaration. Then, you must place the desired string in the array member and set the length member, as shown in the following example:

On Output

VARCHAR Variables When you use a VARCHAR variable as an output host variable, the program interface sets the length member but does not null-terminate the array member. As with character arrays, your program can null-terminate the arr member of a VARCHAR variable before passing it to a function such as printf() or strlen(). An example follows:

emp_name1.arr[emp_name1.len] = '\0';
printf("%s", emp_name1.arr);

Or, you can use the length member to limit the printing of the string, as in:

printf("%.*s", emp_name1.len, emp_name1.arr);

An advantage of VARCHAR variables over character arrays is that the length of the value returned by Oracle is available immediately. With character arrays, you might need to strip the trailing blanks yourself to get the actual length of the character string.

VARCHAR Pointers When you use a pointer to a VARCHAR as an output host variable, the program interface determines the variable's maximum length by checking the length member (emp_name2->len in our example). So, your program must set this member before every fetch. The fetch then sets the length member to the actual number of characters returned, as the following example shows:

Unicode Variables

Pro*C/C++ allows fixed-width Unicode data (character set Unicode Standard Version 3.0, known simply as UCS-16) in host char variables. UCS-16 uses 2 bytes for each character, so it is an unsigned 2-byte datatype. SQL statement text in UCS-16 is not supported yet.

In the following example code a host variable, employee, of the Unicode type utext is declared to be 20 Unicode characters long. A table emp is created containing the column ename, which is 60 bytes long, so that database character sets in Asian languages, where multibyte characters are up to three bytes long, will be supported.

Datatype Conversion

At precompile time, a default external datatype is assigned to each host variable. For example, the precompiler assigns the INTEGER external datatype to host variables of type short int and int.

At run time, the datatype code of every host variable used in a SQL statement is passed to Oracle. Oracle uses the codes to convert between internal and external datatypes.

Before assigning a SELECTed column (or pseudocolumn) value to an output host variable, Oracle must convert the internal datatype of the source column to the datatype of the host variable. Likewise, before assigning or comparing the value of an input host variable to a column, Oracle must convert the external datatype of the host variable to the internal datatype of the target column.

Conversions between internal and external datatypes follow the usual data conversion rules. For example, you can convert a CHAR value of "1234" to a C short value. You cannot convert a CHAR value of "65543" (number too large) or "10F" (number not decimal) to a C short value. Likewise, you cannot convert a char[n] value that contains any alphabetic characters to a NUMBER value.

Datatype Equivalencing

Datatype equivalencing lets you control the way Oracle interprets input data, and the way Oracle formats output data. It provides the ability to override the default external datatypes that the precompiler assigns. On a variable-by-variable basis, you can map (or make equivalent) supported C host variable datatypes to Oracle external datatypes. You can also map user-defined datatypes to Oracle external datatypes.

Host Variable Equivalencing

By default, the Pro*C/C++ Precompiler assigns a specific external datatype to every host variable.

With the VAR statement, you can override the default assignments by equivalencing host variables to Oracle external datatypes. The syntax you use is

EXEC SQL VAR host_variable IS type_name [ (length) ];

where host_variable is an input or output host variable (or host array) declared earlier, type_name is the name of a valid external datatype, and length is an integer literal specifying a valid length in bytes.

Host variable equivalencing is useful in several ways. For example, suppose you want to SELECT employee names from the EMP table, then pass them to a routine that expects null-terminated strings. You need not explicitly null-terminate the names. Simply equivalence a host variable to the STRING external datatype, as follows:

...
char emp_name[11];
EXEC SQL VAR emp_name IS STRING(11);

The length of the ENAME column in the EMP table is 10 characters, so you allot the new emp_name 11 characters to accommodate the null terminator. When you SELECT a value from the ENAME column into emp_name, the program interface null-terminates the value for you.

You can use any external datatypes except NUMBER (for example, VARNUM).

User-Defined Type Equivalencing

You can also map (or make equivalent) user-defined datatypes to Oracle external datatypes. First, define a new datatype structured like the external datatype that suits your needs. Then, map your new datatype to the external datatype using the TYPE statement.

With the TYPE statement, you can assign an Oracle external datatype to a whole class of host variables. The syntax you use is:

EXEC SQL TYPE user_type IS type_name [ (length) ] [REFERENCE];

Suppose you need a variable-length string datatype to hold graphics characters. First, declare a struct with a short length component followed by a 65533-byte data component. Second, use typedef to define a new datatype based on the struct. Then, equivalence your new user-defined datatype to the VARRAW external datatype, as shown in the following example:

You specify a length of 4000 bytes for the new graphics type because that is the maximum length of the data component in your struct. The precompiler allows for the len component (and any padding) when it sends the length to the Oracle server.

REFERENCE Clause

You can declare a user-defined type to be a pointer, either explicitly, as a pointer to a scalar or struct type, or implicitly, as an array, and use this type in an EXEC SQL TYPE statement. In this case, you must use the REFERENCE clause at the end of the statement, as shown in the following example:

In this example, you allocated additional memory over the type length (4000). This is necessary because the precompiler also returns the length (the size of a short), and can add padding after the length due to word alignment restrictions on your system. If you do not know the alignment practices on your system, make sure to allocate sufficient extra bytes for the length and padding (9 should usually be sufficient).

CHARF External Datatype

CHARF is a fixed-length character string. You can use this datatype in VAR and TYPE statements to equivalence C datatypes to the fixed-length SQL standard datatype CHAR, regardless of the setting of the DBMS or CHAR_MAP option.

When DBMS=V7 or V8, specifying the external datatype CHARACTER in a VAR or TYPE statement equivalences the C datatype to the fixed-length datatype CHAR (datatype code 96). However, when CHAR_MAP=VARCHAR2, the C datatype is equivalenced to the variable-length datatype VARCHAR2 (code 1).

Now, you can always equivalence C datatypes to the fixed-length SQL standard type CHARACTER by using the CHARF datatype in the VAR or TYPE statement. When you use CHARF, the equivalence is always made to the fixed-length character type, regardless of the setting of the DBMS or CHAR_MAP option.

The EXEC SQL VAR and TYPE Directives

You can code an EXEC SQL VAR ... or EXEC SQL TYPE ... statement anywhere in your program. These statements are treated as executable statements that change the datatype of any variable affected by them from the point that the TYPE or VAR statement was made to the end of the scope of the variable. If you precompile with MODE=ANSI, you must use Declare Sections. In this case, the TYPE or VAR statement must be in a Declare Section.

Example: Datatype Equivalencing (sample4.pc):

The demonstration program in this section shows you how you can use datatype equivalencing in your Pro*C/C++ programs. This program is available as sample4.pc in the demo directory.It demonstrates the use of type equivalencing using the LONG VARRAW external datatype. In order to provide a useful example that is portable across different systems, the program inserts binary files into and retrieves them from the database.

Suppose the file my_header.h in the current directory contains, among other things, the line

#define VC_LEN 20

The precompiler reads the file my_header.h, and uses the defined value of VC_LEN (20), declares the structure of name as VARCHAR[20].

char is a native type. The precompiler does not substitute 20 in the declaration of another_name[VC_LEN].

This does not matter, since the precompiler does not need to process declarations of C datatypes, even when they are used as host variables. It is left up to the C compiler's preprocessor to actually include the file my_header.h, and perform the substitution of 20 for VC_LEN in the declaration of another_name.

Preprocessor Directives

The preprocessor directives that Pro*C/C++ supports are:

#define, to create macros for use by the precompiler and the C or C++ compiler

#include, to read other source files for use by the precompiler

#if, to precompile and compile source text based on evaluation of a constant expression to 0

#ifdef, to precompile and compile source text conditionally, depending on the existence of a defined constant

#ifndef, to exclude source text conditionally

#endif, to end an #if or #ifdef or #ifndef command

#else, to select an alternative body of source text to be precompiled and compiled, in case an #if or #ifdef or #ifndef condition is not satisfied

#elif, to select an alternative body of source text to be precompiled and compiled, depending on the value of a constant or a macro argument

Directives Ignored

Some C preprocessor directives are not used by the Pro*C/C++ preprocessor. Most of these directives are not relevant for the precompiler. For example, #pragma is a directive for the C compiler—the precompiler does not process it. The C preprocessor directives not processed by the precompiler are:

#, to convert a preprocessor macro parameter to a string constant

##, to merge two preprocessor tokens in a macro definition

#error, to produce a compile-time error message

#pragma, to pass implementation-dependent information to the C compiler

#line, to supply a line number for C compiler messages

While your C compiler preprocessor may support these directives, Pro*C/C++ does not use them. Most of these directives are not used by the precompiler. You can use these directives in your Pro*C/C++ program if your compiler supports them, but only in C or C++ code, not in embedded SQL statements or declarations of variables using datatypes supplied by the precompiler, such as VARCHAR.

ORA_PROC Macro

Pro*C/C++ predefines a C preprocessor macro called ORA_PROC that you can use to avoid having the precompiler process unnecessary or irrelevant sections of code. Some applications include large header files, which provide information that is unnecessary when precompiling. By conditionally excluding such header files based on the ORA_PROC macro, the precompiler never reads the file.

The following example uses the ORA_PROC macro to exclude the irrelevant.h file:

#ifndef ORA_PROC
#include <irrelevant.h>
#endif

Because ORA_PROC is defined during precompilation, the irrelevant.h file is never included.

The ORA_PROC macro is available only for C preprocessor directives, such as #ifdef or #ifndef. The EXEC ORACLE conditional statements do not share the same namespaces as the C preprocessor macros. Therefore, the condition in the following example does not use the predefined ORA_PROC macro:

ORA_PROC, in this case, must be set using either the DEFINE option or an EXEC ORACLE DEFINE statement for this conditional code fragment to work properly.

Location of Header File Specification

The Pro*C/C++ Precompiler for each system assumes a standard location for header files to be read by the preprocessor, such as sqlca.h, oraca.h, and sqlda.h. For example, on most UNIX systems, the standard location is $ORACLE_HOME/precomp/public. For the default location on your system, see your system-specific Oracle documentation. If header files that you need to include are not in the default location, you must use the INCLUDE= option, on the command line or as an EXEC ORACLE option.

To specify the location of system header files, such as stdio.h or iostream.h, where the location might be different from that hard-coded into Pro*C/C++ use the SYS_INCLUDE precompiler option.

Some Preprocessor Examples

You can use the #define command to create named constants, and use them in place of "magic numbers" in your source code. You can use #defined constants for declarations that the precompiler requires, such as VARCHAR[const]. For example, instead of code with bugs, such as:

You can use the #include, #ifdef and #endif preprocessor directives to conditionally include a file that the precompiler requires. For example:

#ifdef ORACLE_MODE
#include <sqlca.h>
#else
long SQLCODE;
#endif

Using #define

There are restrictions on the use of the #define preprocessor directive in Pro*C/C++ You cannot use the #define directive to create symbolic constants for use in executable SQL statements. The following invalid example demonstrates this:

Other Preprocessor Restrictions

The preprocessor ignores directives # and ## to create tokens that the precompiler must recognize. You can use these commands (if your compiler supports them) in pure C code that the precompiler does not have to process. Using the preprocessor command ## is not valid in this example:

SQL Statements Not Allowed in #include

Because of the way the Pro*C/C++ preprocessor handles the #include directive, as described in the previous section, you cannot use the #include directive to include files that contain embedded SQL statements. You use #include to include files that contain purely declarative statements and directives; for example, #defines, and declarations of variables and structures required by the precompiler, such as in sqlca.h.

Include the SQLCA, ORACA, and SQLDA

You can include the sqlca.h, oraca.h, and sqlda.h declaration header files in your Pro*C/C++ program using either the C/C++ preprocessor #include command, or the precompiler EXEC SQL INCLUDE command. For example, you use the following statement to include the SQL Communications Area structure (SQLCA) in your program with the EXEC SQL option:

EXEC SQL INCLUDE sqlca;

To include the SQLCA using the C/C++ preprocessor directive, add the following code:

When you use the preprocessor #include directive, you must specify the file extension (such as .h).

Note:

If you need to include the SQLCA in multiple places, using the #include directive, you should precede the #include with the directive #undef SQLCA. This is because sqlca.h starts with the lines

#ifndef SQLCA
#define SQLCA 1

and then declares the SQLCA struct only in the case that SQLCA is not defined.

When you precompile a file that contains a #include directive or an EXEC SQL INCLUDE statement, you have to tell the precompiler the location of all files to be included. You can use the INCLUDE= option, either in the command line, or in the system configuration file, or in the user configuration file.

The default location for standard preprocessor header files, such as sqlca.h, oraca.h, and sqlda.h, is preset in the precompiler. The location varies from system to system. See your system-specific Oracle documentation for the default location on your system.

When you compile the .c output file that Pro*C/C++ generates, you must use the option provided by your compiler and operating system to identify the location of included files.

For example, on most UNIX systems, you can compile the generated C source file using the command

cc -o progname -I$ORACLE_HOME/sqllib/public ... filename.c ...

On VAX/OPENVMS systems, you pre-pend the include directory path to the value in the logical VAXC$INCLUDE.

EXEC SQL INCLUDE and #include Summary

When you use an EXEC SQL INCLUDE statement in your program, the precompiler includes the source text in the output (.c) file. Therefore, you can have declarative and executable embedded SQL statements in a file that is included using EXEC SQL INCLUDE.

When you include a file using #include, the precompiler merely reads the file, and keeps track of #defined macros.

Caution:

VARCHAR declarations and SQL statements are not allowed in included (#include) files. For this reason, you cannot use SQL statements in files included using the Pro*C/C++ preprocessor #include directive.

Defined Macros

If you define macros on the C compiler's command line, you might also have to define these macros on the precompiler command line, depending on the requirements of your application. For example, if you compile with a UNIX command line such as

cc -DDEBUG ...

you should precompile using the DEFINE= option, namely

proc DEFINE=DEBUG ...

Include Files

The location of all included files that need to be precompiled must be specified on the command line, or in a configuration file.

For example, if you are developing under UNIX, and your application includes files in the directory /home/project42/include, you must specify this directory both on the Pro*C/C++ command line and on the cc command line. You use commands like these:

or you include the appropriate macros in a makefile. For complete information about compiling and linking your Pro*C/C++ application, see your system-specific Oracle documentation.

See Also:

"INCLUDE", for complete information about precompiler options and configuration files.

Precompiled Header Files

Precompiled header files save time and resources by precompiling header files that contain many #include statements. The two steps in using this feature are:

The precompiled header file is created first,

The precompiled header is then automatically used in subsequent precompilation of your application.

Use this capability with large applications that have many modules.

The precompiler option, HEADER=hdr, specifies

That precompiled headers are to be used,

That the file extension for the output file to be generated is hdr.

This option can only be entered in a configuration file or on the command line. There is no default value for HEADER, but the input header must have a .h extension.

Precompiled Header File Creation

Assume that you have a header file called top.h.Then you can precompile it, specifying that HEADER=hdr:

proc HEADER=hdr INAME=top.h

Note:

You must provide the '.h' extension. You cannot use an absolute path element or relative path elements such as '/', '..', and so on., in the INAME value.

Pro*C/C++ precompiles the given input file, top.h, and generates a new precompiled header file, top.hdr, in the same directory. The output file, top.hdr, can be moved to a directory that the #include statement will cause to be searched.

Note:

Do not use the ONAME option to name the output file; it is ignored when used with HEADER.

Use of the Precompiled Header Files

Use the same value of the HEADER option with an application file that is to be precompiled. If simple.pc contains:

#include <top.h>
...

and top.h contains:

#include <a.h>
#include <b.h>
#include <c.h>
...

then precompile this way:

proc HEADER=hdr INAME=simple.pc

When Pro*C/C++ reads the #include top.h statement, it will search for a corresponding 'top.hdr' file and instantiate the data from that file instead of precompiling 'top.h' again.

Note:

A precompiled header file will always be used instead of its input header file even if the input (.h) file appears first in the standard search hierarchy of the include directories.

Examples

This section includes examples demonstrating several different cases.

Redundant File Inclusion

The following two cases illustrate two possibilities for redundant file inclusion.

Case 1: Top-Level Header File Inclusion

A precompiled header file will only be instantiated once regardless of how many times the file is included using a #include directive.

Suppose we precompile a top-level header file, top.h, with the value of HEADER set to 'hdr' as before. Next, we code multiple #include directives for that header file in a program:

#include <top.h>
#include <top.h>
main(){}

When the first #include for top.h is encountered, the precompiled header file, top.hdr, will be instantiated. The second inclusion of that same header file will be redundant and thus, will be ignored.

Case 2: Nested Header File Inclusion

Suppose the file a.h contains the following statement:

#include <b.h>

and that we precompile that header file specifying HEADER as before. Pro*C/C++ will precompile both a.h and b.h generating a.hdr as a result.

Now suppose we precompile this Pro*C/C++ program:

#include <a.h>
#include <b.h>
main(){}

When the #include for a.h is encountered, the a.hdr precompiled header file will be instantiated instead of precompiling a.h again. This instantiation will also contain the entire contents of b.h.

Now, because b.h was included in the precompilation of a.h, and a.hdr was instantiated, the subsequent #include of b.h in our program is redundant and thus, will be ignored.

Multiple Precompiled Header Files

Pro*C/C++ is capable of instantiating more than one different precompiled header file in a single precompilation. However, one pitfall to avoid occurs when two or more precompiled header files share common header files.

For example, suppose topA.h contains the following lines:

#include <a.h>
#include <c.h>

and that topB.h contains the following lines:

#include <b.h>
#include <c.h>

Notice how topA.h and topB.h both include the same common header file, c.h. Precompiling topA.h and topB.h with the same HEADER value will yield topA.hdr and topB.hdr. Both, however, will contain the entire contents of c.h.

Now suppose we have a Pro*C/C++ program:

#include <topA.h>
#include <topB.h>
main(){}

Both precompiled header files, topA.hdr and topB.hdr will be instantiated as before. However, because each shares the common header file, c.h, the contents of that file will be instantiated twice.

Pro*C/C++ cannot determine when such commonality is occurring among precompiled header files. Try to have each precompiled header file contain a unique set of included headers. Sharing headers should be avoided as much as possible because it will ultimately slow down precompilation and utilize more memory, thus undermining the basic intent of using precompiled header files.

Effects of Options

The following precompiler options are used with the precompilation of the application.

DEFINE and INCLUDE Options

During any precompilation using precompiled headers, you must use the same values for DEFINE and INCLUDE as when you created the precompiled header files. If the values of DEFINE or INCLUDE change, you must re-create the precompiled header files.

If development environments change, you must also re-create the precompiled header files.

Single User Scenario

Consider a single user. If the values of either the DEFINE or the INCLUDE options were to change, then the contents of the precompiled header files may no longer be suitable for use in subsequent Pro*C/C++ precompilations.

Because the values of the DEFINE and INCLUDE; DEFINE or INCLUDE options have changed, the contents of the precompiled header file may no longer be consistent with what a standard precompilation would result in had the corresponding .h file in the #include directive been processed normally.

In short, if the values of the DEFINE and INCLUDE; DEFINE or INCLUDE options change, any precompiled header files must be re-created and Pro*C/C++ programs which use them re-precompiled.

Multiple User Scenario

Consider two users, A and B, who develop in totally separate environments, thus having completely different values for their DEFINE and INCLUDE options.

User A precompiles a common header file, common.h, creating a precompiled header file common.hdrA. User B also precompiles the same header file creating common.hdrB. However, given that the two environments are different, specifically with respect to the values of the DEFINE and INCLUDE options used by both users, it is not guaranteed that both user A's and B's versions of common.hdr will be the same.

The generated precompiled header files common.hdrA may not equal common.hdrB because of the different environments in which they where created. This means that neither user A nor user B would be guaranteed that using the common.hdr created by the other user would result in correct precompilation of the Pro*C/C++ programs in their respective development environments.

Therefore, care should be taken when sharing or exchanging precompiled header files between different users and different users' development environments.

CODE and PARSE Options

Pro*C/C++ does not search for C++ header files with extensions such as hpp or h++. So do not use CODE=CPP when precompiling header files. You may use the CPP value when precompiling the application, as long as the source code only includes .h header files.

You can only use the values FULL or PARTIAL for the option PARSE when creating the precompiled header files, or when precompiling the modules. The value FULL is considered to be of higher value than PARTIAL. The value of PARSE used should be the same or lower when precompiling modules as when you created the precompiled header files.

Note:

Precompiling the precompiled header file with PARSE=FULL and then precompiling modules with PARSE=PARTIAL requires that the host variables be declared inside a Declare Section. C++ code will only be understood when PARSE=PARTIAL.

Suppose we precompile a header file with PARSE set to PARTIAL as follows:

proc HEADER=hdr PARSE=PARTIAL file.h

and then try to precompile a program that includes that header file using PARSE set to FULL:

proc HEADER=hdr PARSE=FULL program.pc

Because file.h was precompiled using a PARTIAL setting for the PARSE option, not all of the header file would have been processed. It would therefore be possible for an error to occur during the precompilation of the Pro*C/C++ program if a reference was made to something in the unprocessed portion.

Because PARSE was set to PARTIAL when precompiling file.h, only the LENGTH macro would have been processed leaving the typedef unseen.

The VARCHAR declaration and subsequent use as a host variable would succeed. However, the use of the empno host variable would not because the myint type declaration would never have been processed by Pro*C/C++.

Precompiling the header file with the PARSE option set to FULL and then precompiling the program with PARSE set to PARTIAL would work. However, the host variables would have to be declared inside an explicit DECLARE SECTION.

Usage Notes

The file format of the generated output file of a precompiled header is not guaranteed to remain fixed from one release to the next. Pro*C/C++ has no way of determining which version of the precompiler was used to generate the precompiled header file output.

Because of this, it is strongly recommended that, in order to avoid the possibility of errors or other strange behavior during a precompilation that uses precompiled header files, those files be regenerated by re-precompiling the corresponding header files when upgrading to newer releases of Pro*C/C++.

The generated output from the precompilation of a header file is completely non-portable. This means that you cannot transfer the output file from the precompilation of a header file from one platform to another and use that file during the subsequent precompilation of another header file or Pro*C/C++ program.

The Oracle Preprocessor

Conditional sections of code are marked by EXEC ORACLE directives that define the environment and actions to take. You can code C statements as well as embedded SQL statements and directives in these sections. The following EXEC ORACLE directives let you exercise conditional control over precompilation:

You can "Comment out" C or embedded SQL code by placing it between IFDEF and ENDIF and not defining the symbol.

Evaluation of Numeric Constants

Previously, Pro*C/C++ allowed only numeric literals and simple constant expressions involving numeric literals to be used when declaring the sizes of host variables (such as char or VARCHAR), as in the following examples:

#define LENGTH 10
VARCHAR v[LENGTH];
char c[LENGTH + 1];

You can now also use numeric constant declarations such as:

const int length = 10;
VARCHAR v[length];
char c[length + 1];

This is highly desirable, especially for programmers who use ANSI or C++ compilers that support such constant declarations.

Pro*C/C++ has always determined the values of constant expressions that can be evaluated, but it has never allowed the use of a numeric constant declaration in any constant expression.

Pro*C/C++ supports the use of numeric constant declarations anywhere that an ordinary numeric literal or macro is used, given the macro expands to some numeric literal.

This is used primarily for declaring the sizes of arrays for bind variables to be used in a SQL statement.

Numeric Constants in Pro*C/C++

In Pro*C/C++, normal C scoping rules are used to find and locate the declaration of a numeric constant declaration.

SQLLIB Extensions for OCI Release 8 Interoperability

An OCI environment handle will be tied to the Pro*C/C++ runtime context, which is of the sql_context type. That is, one Pro*C/C++ runtime context maintained by SQLLIB during application execution will be associated with at most one OCI environment handle. Multiple database connections are allowed for each Pro*C/C++ runtime context, which will be associated to the OCI environment handle for the runtime context.

Runtime Context in the OCI Release 8 Environment

An EXEC SQL CONTEXT USE statement specifies a runtime context to be used in a Pro*C/C++ program. This context applies to all executable SQL statements that positionally follow it in a given Pro*C/C++ file until another EXEC SQL CONTEXT USE statement occurs. If no EXEC SQL CONTEXT USE appears in a source file, the default "global" context is assumed. Thus, the current runtime context, and therefore the current OCI environment handle, is known at any point in the program.

The runtime context and its associated OCI environment handle are initialized when a database logon is performed using EXEC SQL CONNECT in Pro*C/C++.

When a Pro*C/C++ runtime context is freed using the EXEC SQL CONTEXT FREE statement, the associated OCI environment handle is terminated and all of its resources, such as space allocated for the various OCI handles and LOB locators, are de-allocated. This command releases all other memory associated with the Pro*C/C++ runtime context. An OCI environment handle that is established for the default "global" runtime remains allocated until the Pro*C/C++ program terminates.

Parameters in the OCI Release 8 Environment Handle

An OCI environment established through Pro*C/C++ will use the following parameters:

The callback functions used by the environment for allocating memory, freeing memory, writing to a text file, and flushing the output buffer will be trivial functions that call malloc(), free(), fprintf(stderr, ...), and fflush(stderr) respectively.

The language will be obtained from the Globalization Support variable NLS_LANG.

The error message buffer will be allocated in thread-specific storage.

Interface to OCI Release 8

SQLLIB library provides routines to obtain the OCI environment and service context handles for database connections established through a Pro*C/C++ program. Once the OCI handles are obtained, the user can call various OCI routines, for example, to perform client-side DATE arithmetic, execute navigational operations on objects and so on. These SQLLIB functions are described later, and their prototypes are available in the public header file sql2oci.h.

A Pro*C/C++ user who mixes embedded SQL and calls in the other Oracle programmatic interfaces must exercise reasonable care. For example, if a user terminates a connection directly using the OCI interface, SQLLIB state is out-of-sync; the behavior for subsequent SQL statements in the Pro*C/C++ program is undefined in such cases.

Note:

Pro*C/C++, the Oracle Call Interface (OCI) release 8, and XA are not compatible. The combined use of Pro*C/C++, OCI release 8, and XA is only recommended in Oracle9i.

Starting with release 8.0, the new SQLLIB functions that provide interoperability with the Oracle OCI are declared in header file sql2oci.h:

SQLEnvGet(), to return a pointer to an OCI environment handle associated with a given SQLLIB runtime context. Used for both single and shared server environments.

SQLSvcCtxGet(), to return an OCI service context handle for a Pro*C/C++ database connection. Used for both single and shared server environments.

Pass the constant SQL_SINGLE_RCTX, defined as (dvoid *)0, when you include sql2oci.h, as the first parameter in either function, when using single threaded runtime contexts.

SQLEnvGet()

The SQLLIB library function SQLEnvGet() (SQLLIB OCI Environment Get) returns the pointer to the OCI environment handle associated with a given SQLLIB runtime context. The prototype for this function is:

sword SQLEnvGet(dvoid *rctx, OCIEnv **oeh);

where:

Terms

Description

Description

Sets oeh to the OCIEnv corresponding to the runtime context

Parameters

rctx (IN) pointer to a SQLLIB runtime context

oeh (OUT) pointer to OCIEnv

Returns

SQL_SUCCESS on success

SQL_ERROR on failure

Notes

The usual error status variables in Pro*C/C++ such as SQLCA and SQLSTATE will not be affected by a call to this function

SQLSvcCtxGet()

The SQLLIB library function SQLSvcCtxGet() (SQLLIB OCI Service Context Get) returns the OCI service context for the Pro*C/C++ database connection. The OCI service context can then be used in direct calls to OCI functions. The prototype for this function is:

A null pointer may be passed as the dbname if the Pro*C/C++ connection is not named with an AT clause.

Embedded OCI Release 7 Calls

Note:

The Logon Data Area (LDA) is no longer supported in Oracle9i. The ability to embed OCI Release 7 calls in your Pro*C/C++ program will be phased out by the next major Oracle release.

To embed OCI release 7 calls in your Pro*C/C++ program, take the following steps:

Declare an OCI Logon Data Area (LDA) in your Pro*C/C++ program (outside the Declare Section if you precompile with MODE=ANSI). The LDA is a structure defined in the OCI header file oci.h. For details, see the Oracle Call Interface programmer's Guide for Release 7.

Connect to Oracle using the embedded SQL statement CONNECT, not the OCI orlon() or onblon() calls.

Call the SQLLIB runtime library function sqllda() to set up the LDA.SQLLIB function

That way, the Pro*C/C++ Precompiler and the OCI "know" that they are working together. However, there is no sharing of Oracle cursors.

You need not worry about declaring the OCI Host Data Area (HDA) because the Oracle runtime library manages connections and maintains the HDA for you.

Set Up the LDA

You set up the LDA by issuing the OCI call

sqllda(&lda);

where lda identifies the LDA data structure.

If the setup fails, the lda_rc field in the lda is set to 1012 to indicate the error.

Remote and Multiple Connections

A call to sqllda() sets up an LDA for the connection used by the most recently executed SQL statement. To set up the different LDAs needed for additional connections, you must call sqllda() with a different LDA immediately after each CONNECT. In the following example, you connect to two nondefault databases concurrently:

DB_NAME1 and DB_NAME2 are not C variables; they are SQL identifiers. You use them only to name the default databases at the two nondefault nodes, so that later SQL statements can refer to the databases by name.

New Names for SQLLIB Public Functions

The names of SQLLIB functions are listed in Table 5-3. You can use these SQLLIB functions for both threaded and nonthreaded applications. Previously, for example, sqlglm() was documented as the nonthreaded or default context version of this function, while sqlglmt() was the threaded or nondefault context version, with context as the first argument. The names sqlglm() and sqlglmt() are still available. The new function SQLErrorGetText() requires the same arguments as sqlglmt(). For nonthreaded or default context applications, pass the defined constant SQL_SINGLE_RCTX as the context.

Each standard SQLLIB public function is thread-safe and accepts the runtime context as the first argument. For example, the syntax for SQLErrorGetText() is:

X/Open Application Development

X/Open applications run in a distributed transaction processing (DTP) environment. In an abstract model, an X/Open application calls on resource managers (RMs) to provide a variety of services. For example, a database resource manager provides access to data in a database. Resource managers interact with a transaction manager (TM), which controls all transactions for the application.

Figure 5-1 shows one way that components of the DTP model can interact to provide efficient access to data in an Oracle database. The DTP model specifies the XA interface between resource managers and the transaction manager. Oracle supplies an XA-compliant library, which you must link to your X/Open application. Also, you must specify the native interface between your application program and the resource managers.

The DTP model that specifies how a transaction manager and resource managers interact with an application program is described in the X/Open guide Distributed Transaction Processing Reference Model and related publications, which you can obtain by writing to

For instructions on using the XA interface, see your Transaction Processing (TP) Monitor user's guide.

Oracle-Specific Issues

You can use the precompiler to develop applications that comply with the X/Open standards. However, you must meet the following requirements.

Connecting to Oracle

The X/Open application does not establish and maintain connections to a database. Instead, the transaction manager and the XA interface, which is supplied by Oracle, handle database connections and disconnections transparently. So, normally an X/Open-compliant application does not execute CONNECT statements.

Transaction Control

The X/Open application must not execute statements such as COMMIT, ROLLBACK, SAVEPOINT, and SET TRANSACTION that affect the state of global transactions. For example, the application must not execute the COMMIT statement because the transaction manager handles commits. Also, the application must not execute SQL data definition statements such as CREATE, ALTER, and RENAME because they issue an implicit COMMIT.

The application can execute an internal ROLLBACK statement if it detects an error that prevents further SQL operations. However, this might change in later releases of the XA interface.

OCI Calls (Release 7 Only)

Note:

The Logon Data Area (LDA) is no longer supported in Oracle9i. The ability to embed OCI Release 7 calls in your Pro*C/C++ program will be phased out by the next major Oracle release.

If you want your X/Open application to issue OCI calls, you must use the runtime library routine sqlld2(), which sets up an LDA for a specified connection established through the XA interface. For a description of the sqlld2() call, see the Oracle Call Interface Programmer's Guide for Release 7.