5 高度なトピック

この章では、Pro*C/C++の高度な技術について説明します。この章の項目は、次のとおりです。

5.1 文字データ

この項では、Pro*C/C++プリコンパイラによる文字ホスト変数の処理方法について説明します。ホスト変数のキャラクタ・タイプには、次の4種類があります。

  • 文字配列

  • 文字列へのポインタ

  • VARCHAR変数

  • VARCHARへのポインタ

VARCHAR(プリコンパイラで提供されるホスト変数データ構造体)とVARCHAR2(可変長文字列に対応するOracleの内部データ型)を混同しないでください。

5.1.1 プリコンパイラ・オプションCHAR_MAP

CHAR_MAPプリコンパイラ・オプションを使用して、char[n]およびcharホスト変数のデフォルトのマッピングを指定できます。Oracleでは、CHARZにマップされます。CHARZにより、ANSI固定文字書式が実装されます。文字列は固定長で、空白文字で埋めてヌル文字で終了します。VARCHAR2値(ヌル文字を含む)は、常に固定長で空白文字で埋められます。表5-1は、CHAR_MAPの可能な設定を示しています。

表5-1 CHAR_MAPの設定

CHAR_MAPの設定 デフォルトとなる場合 説明

VARCHAR2

-

すべての値(ヌル文字を含む)が固定長で空白文字で埋められます。

CHARZ

DBMS=V7、DBMS=V8

固定長で、空白文字で埋めてヌル文字で終了。ANSI固定キャラクタ・タイプに準拠しています。

STRING

新しい書式

ヌル文字で終了。Cプログラムで使用されているASCII形式に準拠しています。

CHARF

以前は、VAR宣言またはTYPE宣言が行われた場合のみ。

固定長で空白文字で埋められ、ヌル文字は使用されません。

デフォルトのマッピングは、Pro*C/C++の旧リリースと同じCHAR_MAP=CHARZです。

廃止された従来のDBMS=V6_CHARのかわりにCHAR_MAP=VARCHAR2を使用してください。

5.1.2 CHAR_MAPオプションのインラインでの使用方法

char変数またはchar[n]変数を異なる方法で宣言していないかぎり、そのマッピングはインラインCHAR_MAPオプションにより決定されます。次のコード例は、このオプションをPro*C/C++でインライン設定した結果を示しています。

char ch_array[5];

strncpy(ch_array, "12345", 5);
/* char_map=charz is the default in Oracle7 and Oracle8 */
EXEC ORACLE OPTION (char_map=charz);
/* Select retrieves a string "AB" from the database */
SQL SELECT ... INTO :ch_array FROM ... WHERE ... ;
/* ch_array == { 'A', 'B', ' ', ' ', '\0' } */

strncpy (ch_array, "12345", 5);
EXEC ORACLE OPTION (char_map=string) ;
/* Select retrieves a string "AB" from the database */
EXEC SQL SELECT ... INTO :ch_array FROM ... WHERE ... ;
/* ch_array == { 'A', 'B', '\0', '4', '5' } */

strncpy( ch_array, "12345", 5);
EXEC ORACLE OPTION (char_map=charf);
/* Select retrieves a string "AB" from the database */
EXEC SQL SELECT ... INTO :ch_array FROM ... WHERE ... ;
/* ch_array == { 'A', 'B', ' ', ' ', ' ' } */ 

5.1.3 DBMSオプションおよびCHAR_MAPオプションの影響

DBMSオプションとCHAR_MAPオプションにより、Pro*C/C++で文字配列および文字列のデータを処理する方法が決定されます。これらのオプションにより、プログラムではANSI固定長文字列との互換性、あるいは可変長文字列を使用するOracleおよびPro*C/C++の旧リリースとの互換性を維持できます。

DBMSオプションは、入力(ホスト変数からOracle表へ)および出力(Oracle表からホスト変数へ)の両方で文字データに影響します。

文字配列およびCHAR_MAPオプション

文字配列のマッピングは、DBMSオプションとは関連しないCHAR_MAPオプションでも設定できます。DBMS=V7またはDBMS=V8の場合は、どちらもCHAR_MAP=CHARZが使用されます。これは、CHAR_MAP=VARCHAR2、STRINGまたはCHARFを指定することで上書きできます。

5.1.3.1 入力時

文字配列

入力時に、DBMSオプションにより、プログラム内でホスト変数の文字配列に必要な形式が決定されます。CHAR_MAP=VARCHAR2の場合、ホスト変数の文字配列は空白で埋める必要があります。また、ヌル文字で終了しないでください。DBMS=V7またはV8の場合は、文字配列にヌル終端文字('\0')を付ける必要があります。

CHAR_MAPオプションがVARCHAR2に設定されると、後続の空白文字が最初の非空白文字まで削除されてから、値がデータベースに送られます。未初期化文字配列には、ヌル文字が含まれている場合があります。NULLが表に挿入されるのを確実に防ぐには、長さに達するまで文字配列に空白文字を埋める必要があります。たとえば、次の文を実行するとします。

char emp_name[10]; 
... 
strcpy(emp_name, "MILLER");     /* WRONG! Note no blank-padding */ 
EXEC SQL INSERT INTO emp (empno, ename, deptno) VALUES 
    (1234, :emp_name, 20); 

文字列MILLERがMILLER\0\0\0\0として挿入されていることがわかります(末尾に4個のNULLバイトが追加されています)。この値は、次の検索条件とは一致しません。

. . . WHERE ename = 'MILLER'; 

CHAR_MAPがVARCHAR2に設定されている場合に文字配列をINSERTするには、次の文を実行します。

strncpy(emp_name, "MILLER    ", 10); /* 4 trailing blanks */ 
EXEC SQL INSERT INTO emp (empno, ename, deptno) VALUES 
    (1234, :emp_name, 20); 

DBMS=V7またはV8の場合は、文字配列内の入力データにNULL終了記号を付ける必要があります。したがって、データがNULLで終わっているか確認してください。

char emp_name[11];  /* Note: one greater than column size of 10 */ 
... 
strcpy(emp_name, "MILLER");        /* No blank-padding required */ 
EXEC SQL INSERT INTO emp (empno, ename, deptno) VALUES 
    (1234, :emp_name, 20); 

ノート:

CHAR_MAP=CHARZまたはCHARFの場合は、挿入文の実行中に、右側に空白があるバインド変数がすべて切り捨てられます。

文字ポインタ

ポインタとして、入力データを保持できる大きさの、ヌル文字で終了するバッファをアドレス指定する必要があります。プログラムでは、そのために十分なメモリーを割り当てる必要があります。

5.1.3.2 入力時

次の例は、データベースから取り出して文字配列に格納する値にCHAR_MAPオプションの設定が及ぼす影響の可能な組合せをすべて示しています。

次のデータベースについて考えてみます。

TABLE strdbase ( ..., strval VARCHAR2(6));

strval列に次の文字列が入っているとします。

""        -- string of length 0
"AB"      -- string of length 2
"KING"    -- string of length 4
"QUEEN"   -- string of length 5
"MILLER"  -- string of length 6

Pro*C/C++プログラムでは、5文字のホスト配列strを文字Xで初期化し、strval列のすべての値の取出しに使用するとします。

char  str[5] = {'X', 'X', 'X','X', 'X'} ;
short str_ind;
...
EXEC SQL SELECT strval INTO :str:str_ind WHERE ... ;

CHAR_MAPがVARCHAR2、CHARF、CHARZおよびSTRINGに設定されると、配列strおよび標識変数str_indの結果は次のようになります。

strval = ""         "AB"       "KING"     "QUEEN"    "MILLER"
---------------------------------------------------------------
VARCHAR2 "     " -1 "AB   " 0  "KING "  0 "QUEEN"  0 "MILLE"  6
CHARF    "XXXXX" -1 "AB   " 0  "KING "  0 "QUEEN"  0 "MILLE"  6
CHARZ    "    0" -1 "AB  0" 0  "KING0"  0 "QUEE0"  5 "MILL0"  6
STRING   "0XXXX" -1 "AB0XX" 0  "KING0"  0 "QUEE0"  5 "MILL0"  6

0はヌル文字'\0'を表します。

5.1.3.3 出力時

文字配列

出力時に、DBMSオプションとCHAR_MAPオプションにより、プログラム内でホスト変数の文字配列に使用される形式が決定されます。CHAR_MAP=VARCHAR2の場合、ホスト変数の文字配列は配列の長さに達するまで空白文字で埋められますが、ヌル文字で終了しません。DBMS=V7またはV8(あるいはCHAR_MAP=CHARZ)の場合、文字配列は空白文字で埋めて、配列の最終位置にヌル文字を入れて終了します。

次の文字出力の例について考えてみます。

CREATE TABLE test_char (C_col CHAR(10), V_col VARCHAR2(10)); 
 
INSERT INTO test_char VALUES ('MILLER', 'KING'); 

この表を選択するプリコンパイラ・プログラムには、次のような埋込みSQLが記述されています。

... 
char name1[10]; 
char name2[10]; 
... 
EXEC SQL SELECT C_col, V_col INTO :name1, :name2 
    FROM test_char; 

CHAR_MAP=VARCHAR2を指定してプログラムをプリコンパイルすると、name1には次の文字列が入ります。

"MILLER####" 

つまり、名前MILLERの後に4個の空白が続き、ヌル文字で終了しません。(name1がサイズ15で宣言されている場合は、名前に続く空白が9個になります)。

name2には次の文字列が入ります。

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

DBMS=V7またはV8を指定してプログラムをプリコンパイルすると、name1には次の文字列が入ります。

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

つまり、名前を含み、列の長さに達するまで空白文字で埋められ、ヌル文字で終了する文字列です。name2には、次の文字列が入ります。

"KING#####\0" 

要約すると、CHAR_MAP=VARCHAR2の場合、CHARACTER列またはVARCHAR2列からの出力には、ホスト変数配列の長さに達するまで空白埋めが行われます。DBMS=V7またはV8の場合、出力文字列は常にヌル文字で終了します。

文字ポインタ

DBMSオプションとCHAR_MAPオプションを使用しても、文字データがポインタ・ホスト変数に出力される方法には影響しません。

データを文字ポインタ・ホスト変数に出力する場合、ポインタが指すバッファには、表からの出力に加えてヌル終端文字のための1バイトを保持できるサイズが必要です。

プリコンパイラ・ランタイム環境では、strlen()をコールして出力バッファのサイズを決定するため、バッファに埋込みヌル('\0')が含まれていないことを確認してください。データをフェッチする前に'\0'以外の値で割当て済バッファを満たし、最後のバイトにヌル文字を入れて終了してください。

ノート:

C言語のポインタは、DBMS=V7またはV8とMODE=ANSIを指定してプリコンパイルしたPro*C/C++プログラムで使用できます。ただし、ポインタは、SQL規格準拠のプログラムでは有効なホスト変数型ではありません。ポインタをホスト変数として使用すると、FIPSフラガーにより警告が発行されます。

次のコード例は、前項で定義した列と表を使用して、文字ポインタ・ホスト変数に宣言およびSELECTを行う方法を示しています。

... 
char *p_name1; 
char *p_name2; 
... 
p_name1 = (char *) malloc(11); 
p_name2 = (char *) malloc(11); 
strcpy(p_name1, "          "); 
strcpy(p_name2, "0123456789"); 
 
EXEC SQL SELECT C_col, V_col INTO :p_name1, :p_name2 
    FROM test_char; 

前述のSELECT文がDBMSまたはCHAR_MAP設定で実行されると、フェッチされる値は次のようになります。

"MILLER####\0"     /* 4 trailing blanks and a null terminator */ 
 
"KING######\0"    /* 6 blanks and null */ 

5.1.4 VARCHAR変数およびポインタ

次の例は、VARCHARホスト変数の宣言方法を示しています。

VARCHAR   emp_name1[10];   /* VARCHAR variable   */ 
VARCHAR  *emp_name2;       /* pointer to VARCHAR */
5.1.4.1 入力時

VARCHAR変数

VARCHAR変数を入力ホスト変数として使用すると、プログラムに必要なのは、展開されたVARCHAR宣言(例ではemp_name1.arr)の配列メンバーに必要な文字列を配置し、長さメンバー(emp_name1.len)を設定することのみです。配列に空白文字を埋める必要はありません。emp_name1.lenの文字が正確にOracleに送られ、空白文字およびヌル文字があればカウントされます。次の例では、emp_name1.lenを8に設定します。

strcpy((char *)emp_name1.arr, "VAN HORN"); 
emp_name1.len = strlen((char *)emp_name1.arr); 

VARCHARへのポインタ

VARCHARへのポインタを入力ホスト変数として使用する場合は、展開されるVARCHAR宣言に十分なメモリーを割り当てる必要があります。その後、次のように、必要な文字列を配列メンバーに配置し、長さメンバーを設定します。

emp_name2 = malloc(sizeof(short) + 10)   /* len + arr */ 
strcpy((char *)emp_name2->arr, "MILLER"); 
emp_name2->len = strlen((char *)emp_name2->arr); 

または、emp_name2が既存のVARCHAR(この場合はemp_name1)を指すように、割当てを次のように記述できます。

emp_name2 = &emp_name1;
 

その後、次のように通常の方法でVARCHARポインタを使用します。

EXEC SQL INSERT INTO EMP (EMPNO, ENAME, DEPTNO) 
    VALUES (:emp_number, :emp_name2, :dept_number); 
5.1.4.2 出力時

VARCHAR変数

VARCHAR変数を出力ホスト変数として使用すると、プログラム・インタフェースにより長さメンバーが設定されますが、配列メンバーはヌル文字で終了しません。文字配列の場合と同様に、プログラムではVARCHAR変数のarrメンバーをヌル文字で終了してから、printf()またはstrlen()などの関数に渡すことができます。次に例を示します。

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

また、長さメンバーを使用して、次のように文字列の出力を制限できます。

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

VARCHAR変数が文字配列よりも優れている点は、Oracleによって戻される値の長さがすぐにわかることです。文字配列の場合は、文字列の実際の長さを知るために、後続する空白を手動で削除する操作が必要になる場合もあります。

VARCHARポインタ

VARCHARへのポインタを出力ホスト変数として使用すると、プログラム・インタフェースでは長さメンバー(例ではemp_name2->len)を調べることで、変数の最大長が決定されます。したがって、プログラムではフェッチの前ごとにこのメンバーを設定する必要があります。その後のフェッチ処理時に、次のように、長さメンバーは戻された実際の文字数に設定されます。

emp_name2->len = 10;  /* Set maximum length of buffer. */ 
EXEC SQL SELECT ENAME INTO :emp_name2 WHERE EMPNO = 7934; 
printf("%d characters returned to emp_name2", emp_name2->len);

5.1.5 Unicode変数

Pro*C/C++では、ホストchar変数で固定幅のUnicodeデータ(文字セットUnicode標準バージョン3.0、UCS-16とも呼ばれます)を使用できます。UCS-16では1文字に2バイトが使用されるため、データ型は符号なし2バイトです。UCS-16表記のSQL文は、まだサポートされていません。

次のサンプル・コードでは、Unicodeのutext型のホスト変数employeeが、20 Unicode文字長として宣言されています。emp表は、60バイト長の列enameを含むように作成されます。これにより、マルチバイト文字で3バイト長以内の、アジア言語のデータベース文字セットがサポートされます。

utext employee[20] ;                               /* Unicode host variable   */
EXEC SQL CREATE TABLE emp (ename CHAR(60));
/* ename is in the current database character set  */                                        
EXEC SQL INSERT INTO emp (ename) VALUES ('test') ; 
/* 'test' in NLS_LANG encoding converted to database character set */
EXEC SQL SELECT * INTO :employee FROM emp ;       
/* Database character set converted to Unicode */

パブリック・ヘッダー・ファイルsqlucs2.hを、アプリケーション・コードにインクルードする必要があります。次を実行します。

  • 次の文を含めます。

    #include <oratypes.h>
  • uvarchar、つまりUnicode varcharは、次のように定義します。

    struct uvarchar
    {
       ub2 len;
       utext arr[1] ;
    };
    typedef struct uvarchar uvarchar ;
  • ulong_varchar、つまりUnicode long varcharは、次のように定義します。

    struct ulong_varchar
    {
       ub4 len ;
       utext arr[1] ;
    }
    typedef struct ulong_varchar ulong_varchar ;

utextのデフォルトのデータ型は、すべての文字変数のデフォルトであるCHARZと同じで、空白文字で埋められ、ヌル文字で終了します。

CHAR_MAPプリコンパイラ・オプションを使用して、次のようにデフォルト・データ型を変更してください。

#include <sqlca.h>
#include <sqlucs2.h>

main()
{
   utext employee1[20] ;

/* Change to STRING datatype:    */
   EXEC ORACLE OPTION (CHAR_MAP=STRING) ;
   utext employee2[20] ;

   EXEC SQL CREATE TABLE emp (ename CHAR(60)) ;
   ...
/***********************************************************  
  Initializing employee1 or employee2 is compiler-dependent.   
 **********************************************************/
   EXEC SQL INSERT INTO emp (ename) VALUES (:employee1) ;
   ...
   EXEC SQL SELECT ename INTO :employee2 FROM emp;
/* employee2 is now not blank-padded and is null-terminated  */
   ...
5.1.5.1 Unicode変数の使用に関する制限事項
  • SQL文の静的SQLおよび動的SQLにUnicodeを含めることはできません。次の記述は許可されません

#include oratypes.h
utext sqlstmt[100] ;
...
/* If sqlstmt contains a SQL statement: */
EXEC SQL PREPARE s1 FROM :sqlstmt ;
EXEC SQL EXECUTE IMMEDIATE :sqlstmt ;
...
  • utext変数には、データ型の同値化を使用できません。次のコードは使用できません

    typedef utext utext_5 ;
    EXEC SQL TYPE utext_5 IS STRING ;
  • CONVBUFSZは、変換バッファ・サイズとして使用できません。かわりにCHAR_MAPオプションを使用してください。

  • Oracleの動的SQL方法4では、Unicodeはサポートされません。

  • オブジェクト型では、Unicodeはサポートされません。

5.2 データ型変換

プリコンパイル時には、各ホスト変数にデフォルトの外部データ型が割り当てられます。たとえば、プリコンパイラでは、short int型およびint型のホスト変数にINTEGER外部データ型が割り当てられます。

SQL文で使用するすべてのホスト変数のデータ型コードは、実行時にOracleに渡されます。Oracleは、コードを使用して内部データ型と外部データ型の間で変換します。

選択された列(または疑似列)の値を出力ホスト変数に割り当てる前に、Oracleではソース列の内部データ型をホスト変数のデータ型に変換する必要があります。同様に、入力ホスト変数の値の、列への割当てまたは比較を行う場合は、その前に必ずホスト変数の外部データ型をターゲット列の内部データ型に変換します。

内部データ型と外部データ型との変換は、通常のデータ変換規則に従って行われます。たとえば、CHAR値「1234」をC言語のshort値に変換できます。CHAR値「65543」(大きすぎる値)または「10F」(10進数でない値)は、C言語のshort値に変換できません。同様に、アルファベット文字が含まれるchar[n]値はNUMBER値に変換できません。

5.3 データ型の同値化

データ型の同値化により、Oracleで入力データを解釈する方法と出力データの書式を設定する方法を制御できます。データ型の同値化を使用すると、プリコンパイラにより割り当てられるデフォルトの外部データ型を上書きできます。サポートされているC言語のホスト変数データ型は、変数ごとにOracleの外部データ型にマップ(つまり同値化)できます。また、ユーザー定義のデータ型をOracleの外部データ型にマップすることもできます。

5.3.1 ホスト変数の同値化

Pro*C/C++プリコンパイラでは、デフォルトで、各ホスト変数に特定の外部データ型が割り当てられます。

表5-2は、デフォルトの割当てを示しています。

表5-2 デフォルトの型割当て

C言語のデータ型または疑似型 Oracleの外部型 ノート

char

char[n]

char*

VARCHAR2

CHARZ

STRING

CHARF

(CHAR_MAP=VARCHAR2)

(DBMS=V7、V8のデフォルト)

(CHAR_MAP=STRING)

(CHAR_MAP=CHARF)

int、int*

INTEGER

-

short、short*

INTEGER

-

long、long*

INTEGER

-

long long、long long*

INTEGER

-

float、float*

FLOAT

-

double、double*

FLOAT

-

VARCHAR*、VARCHAR[n]

VARCHAR

-

bool

BOOLEAN

-

VAR文を使用すると、ホスト変数をOracleの外部データ型に同値化して、デフォルトの割当てを上書きできます。使用する構文は次のとおりです。

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

host_variableはすでに宣言済の入力ホスト変数または出力ホスト変数(またはホスト配列)、type_nameは有効な外部データ型の名前、lengthは有効な長さをバイト数で指定する整数リテラルです。

ホスト変数の同値化には、いくつかの利点があります。たとえば、EMP表から従業員名を選択して、ヌル文字で終了する文字列を前提としたルーチンに渡すとします。これらの名前に、明示的にヌル終端文字を付ける必要はありません。次のように、ホスト変数をSTRING外部データ型に同値化するのみです。

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

EMP表のENAME列の長さは10文字のため、ヌル終端文字を含めるために、新しいemp_nameに11文字を割り当てます。ENAME列からemp_nameに入れる値をSELECTする場合、プログラム・インタフェースにより値はヌル文字で終了します。

NUMBERを除き、どの外部データ型(VARNUMなど)でも使用できます。

関連項目

5.3.2 ユーザー定義型同値化

また、ユーザー定義のデータ型をOracleの外部データ型にマップ(つまり同値化)することもできます。まず、要件を満たす外部データ型に似た構造の新しいデータ型を定義します。次に、TYPE文を使用して、新しいデータ型を外部データ型にマップします。

TYPE文を使用すると、ホスト変数のクラス全体にOracleの外部データ型を割り当てることができます。使用する構文は次のとおりです。

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

図形文字を使用するために、可変長文字列データ型が必要であるとします。最初に、short型の長さコンポーネントと、それに続く65533バイトのデータ・コンポーネントからなる構造体を宣言します。次に、typedefを使用して、その構造体に基づく新規のデータ型を定義します。最後に、新しいユーザー定義データ型をVARRAW外部データ型に同値化します。次に例を示します。

struct  screen 
{ 
    short  len; 
    char   buff[4000]; 
}; 
typedef struct screen graphics; 

EXEC SQL TYPE graphics IS VARRAW(4000); 
graphics  crt;  — host variable of type graphics 
    ... 

新しいgraphics型に長さ4000バイトを指定します。この構造体では、それがデータ・コンポーネントの最大長になるためです。プリコンパイラは、長さをOracleサーバーに送るときに、lenコンポーネント(および必要なパディング)を考慮します。

5.3.2.1 REFERENCE句

ユーザー定義型はポインタとして宣言できます。明示的にスカラー型または構造体型へのポインタとして宣言することも、暗黙的に配列として宣言することもできます。また、この型をEXEC SQL TYPE文で使用できます。この場合は、文の終わりにREFERENCE句を使用してください。次に例を示します。

typedef unsigned char *my_raw; 
 
EXEC SQL TYPE my_raw IS VARRAW(4000) REFERENCE; 
my_raw    graphics_buffer; 
... 
graphics_buffer = (my_raw) malloc(4004); 

この例では、型の長さ(4000)を超過したメモリーを割り当てています。プリコンパイラが長さ(shortのサイズ)を戻し、システムのワード・アラインメント制限を考慮して長さの後に埋込みをパディングできるようにするため、このような割当てが必要になります。システムのワード・アラインメントの制限が不明な場合は、必ずその長さとパディングの分として余分に数バイトを割り当ててください(通常は9バイトで十分です)。

5.3.3 CHARF外部データ型

CHARFは固定長文字列です。このデータ型をVAR文およびTYPE文で使用して、DBMSオプションまたはCHAR_MAPオプションの設定に関係なく、C言語のデータ型を固定長のSQL標準データ型CHARに同値化できます。

DBMS=V7またはDBMS=V8の場合は、VAR文またはTYPE文で外部データ型CHARACTERを指定すると、C言語のデータ型が固定長のCHARデータ型(データ型コード96)に同値化されます。ただし、CHAR_MAP=VARCHAR2の場合、C言語のデータ型は可変長のVARCHAR2データ型(コード1)に同値化されます。

現在では、VAR文またはTYPE文でCHARFデータ型を使用すれば、常にC言語のデータ型を固定長のSQL標準のCHAR型に同値化できます。CHARFを使用すると、DBMSオプションまたはCHAR_MAPオプションの設定に関係なく、常に固定長のキャラクタ・タイプになるように同値化されます。

5.3.4 EXEC SQL VARおよびTYPEディレクティブ

EXEC SQL VAR ...文またはEXEC SQL TYPE ...文は、プログラム内の任意の位置に記述できます。これらの文は、その影響を受ける変数のデータ型を、TYPE文またはVAR文が記述された位置から変数のスコープの終わりまでの範囲内で変更する実行文として扱われます。MODE=ANSIを指定してプリコンパイルする場合は、宣言部を使用する必要があります。この場合、TYPE文またはVAR文は宣言部中に記述してください。

5.3.5 例: データ型の同値化(sample4.pc)

この項のデモ・プログラムは、Pro*C/C++プログラムでデータ型の同値化を使用する方法を示しています。このプログラムは、demoディレクトリ内のsample4.pcとして使用でき、LONG VARRAW外部データ型を使用してデータ型の同値化方法を示しています。異なるシステム間で移植できる実用的な例を示すために、このプログラムではバイナリ・ファイルをデータベースに挿入し、そのファイルをデータベースから取り出します。

このプログラムでは、LOB埋込みSQL文を使用しています。

このプログラムの目的については、導入部分のコメントを参照してください。

/***************************************************************
sample4.pc
This program 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.  For
example, suppose you have a file called 'hello' in the current
directory.  You can create this file by compiling the following
source code:

#include <stdio.h>

int main()
{
  printf("Hello World!\n");
}

When this program is run, we get:

$hello
Hello World!

Here is some sample output from a run of sample4:

$sample4
Connected.
Do you want to create (or re-create) the EXECUTABLES table (y/n)? y
EXECUTABLES table successfully dropped.  Now creating new table...
EXECUTABLES table created.

Sample 4 Menu.  Would you like to:
(I)nsert a new executable into the database
(R)etrieve an executable from the database
(L)ist the executables stored in the database
(D)elete an executable from the database
(Q)uit the program

Enter i, r, l, or q: l

Executables           Length (bytes)
--------------------  --------------

Total Executables: 0

Sample 4 Menu.  Would you like to:
(I)nsert a new executable into the database
(R)etrieve an executable from the database
(L)ist the executables stored in the database
(D)elete an executable from the database
(Q)uit the program

Enter i, r, l, or q: i
Enter the key under which you will insert this executable: hello
Enter the filename to insert under key 'hello'.
If the file is not in the current directory, enter the full
path: hello
Inserting file 'hello' under key 'hello'...
Inserted.

Sample 4 Menu.  Would you like to:
(I)nsert a new executable into the database
(R)etrieve an executable from the database
(L)ist the executables stored in the database
(D)elete an executable from the database
(Q)uit the program

Enter i, r, l, or q: l

Executables           Length (bytes)
--------------------  --------------
hello                           5508

Total Executables: 1

Sample 4 Menu.  Would you like to:
(I)nsert a new executable into the database
(R)etrieve an executable from the database
(L)ist the executables stored in the database
(D)elete an executable from the database
(Q)uit the program

Enter i, r, l, or q: r
Enter the key for the executable you wish to retrieve: hello
Enter the file to write the executable stored under key hello into.  If you
don't want the file in the current directory, enter the
full path: h1
Retrieving executable stored under key 'hello' to file 'h1'...
Retrieved.

Sample 4 Menu.  Would you like to:
(I)nsert a new executable into the database
(R)etrieve an executable from the database
(L)ist the executables stored in the database
(D)elete an executable from the database
(Q)uit the program

Enter i, r, l, or q: q

We now have the binary file 'h1' created, and we can run it:

$h1
Hello World!
***************************************************************/

#include <oci.h>
#include <string.h>
#include <stdio.h>
#include <sqlca.h>
#include <stdlib.h>
#include <sqlcpr.h>

/* Oracle error code for 'table or view does not exist'. */
#define NON_EXISTENT  -942
#define NOT_FOUND     1403

/* This is the definition of the long varraw structure.
 * Note that the first field, len, is a long instead
 * of a short.  This is becuase the first 4
 * bytes contain the length, not the first 2 bytes.
 */
typedef struct long_varraw {
  ub4  len;
  text buf[1];
} long_varraw;


/* Type Equivalence long_varraw to LONG VARRAW.
 * All variables of type long_varraw from this point
 * on in the file will have external type 95 (LONG VARRAW)
 * associated with them.
 */
EXEC SQL TYPE long_varraw IS LONG VARRAW REFERENCE;


/* This program's functions declared. */
#if defined(__STDC__)
  void do_connect(void);
  void create_table(void);
  void sql_error(char *);
  void list_executables(void);
  void print_menu(void);
  void do_insert(varchar *, char *);
  void do_retrieve(varchar *, char *);
  void do_delete(varchar *);
  ub4  read_file(char *, OCIBlobLocator *);
  void write_file(char *, OCIBlobLocator *);
#else
  void do_connect(/*_ void _*/);
  void create_table(/*_ void _*/);
  void sql_error(/*_ char * _*/);
  void list_executables(/*_ void _*/);
  void print_menu(/*_ void _*/);
  void do_insert(/*_ varchar *, char * _*/);
  void do_retrieve(/*_ varchar *, char * _*/);
  void do_delete(/*_ varchar * _*/);
  ub4  read_file(/*_ char *, OCIBlobLocator * _*/);
  void write_file(/*_ char *, OCIBlobLocator * _*/);
#endif

void main()
{
  char reply[20], filename[100];
  varchar key[20];
  short ok = 1;

  /* Connect to the database. */
  do_connect();

  printf("Do you want to create (or re-create) the EXECUTABLES table (y/n)? ");
  gets(reply);

  if ((reply[0] == 'y') || (reply[0] == 'Y'))
    create_table();

  /* Print the menu, and read in the user's selection. */
  print_menu();
  gets(reply);

  while (ok)
  {
    switch(reply[0]) {
    case 'I': case 'i':
      /* User selected insert - get the key and file name. */
      printf("Enter the key under which you will insert this executable: ");
      key.len = strlen(gets((char *)key.arr));
      printf("Enter the filename to insert under key '%.*s'.\n",
             key.len, key.arr);
      printf("If the file is not in the current directory, enter the full\n");
      printf("path: ");
      gets(filename);
      do_insert((varchar *)&key, filename);
      break;
    case 'R': case 'r':
      /* User selected retrieve - get the key and file name. */
      printf("Enter the key for the executable you wish to retrieve: ");
      key.len = strlen(gets((char *)key.arr));
      printf("Enter the file to write the executable stored under key ");
      printf("%.*s into.  If you\n", key.len, key.arr);
      printf("don't want the file in the current directory, enter the\n");
      printf("full path: ");
      gets(filename);
      do_retrieve((varchar *)&key, filename);
      break;
    case 'L': case 'l':
      /* User selected list - just call the list routine. */
      list_executables();
      break;
    case 'D': case 'd':
      /* User selected delete - get the key for the executable to delete. */
      printf("Enter the key for the executable you wish to delete: ");
      key.len = strlen(gets((char *)key.arr));
      do_delete((varchar *)&key);
      break;
    case 'Q': case 'q':
      /* User selected quit - just end the loop. */
      ok = 0;
      break;
    default:
      /* Invalid selection. */
      printf("Invalid selection.\n");
      break;
    }

    if (ok)
    {
      /* Print the menu again. */
      print_menu();
      gets(reply);
    }
  }

  EXEC SQL COMMIT WORK RELEASE;
}


/* Connect to the database. */
void do_connect()
{
  /* Note this declaration: uid is a char * pointer, so Oracle
     will do a strlen() on it at runtime to determine the length.
   */
  char *uid = "scott/tiger";

  EXEC SQL WHENEVER SQLERROR DO sql_error("do_connect():CONNECT");
  EXEC SQL CONNECT :uid;

  printf("Connected.\n");
}


/* Creates the executables table. */
void create_table()
{
  /* We are going to check for errors ourselves for this statement. */
  EXEC SQL WHENEVER SQLERROR CONTINUE;

  EXEC SQL DROP TABLE EXECUTABLES;
  if (sqlca.sqlcode == 0)
    {
      printf("EXECUTABLES table successfully dropped.  ");
      printf("Now creating new table...\n");
    }
  else if (sqlca.sqlcode == NON_EXISTENT)
    {
      printf("EXECUTABLES table does not exist.  ");
      printf("Now creating new table...\n");
    }
  else
    sql_error("create_table()"); 

  /* Reset error handler. */
  EXEC SQL WHENEVER SQLERROR DO sql_error("create_table():CREATE TABLE");

  EXEC SQL CREATE TABLE EXECUTABLES
    ( name VARCHAR2(30), length NUMBER(10), binary BLOB ) ;

  printf("EXECUTABLES table created.\n");
}

/* Opens the binary file identified by 'filename' for reading, and writes
   it into into a Binary LOB.  Returns the actual length of the file read.
 */
ub4 read_file(filename, blob)
  char *filename;
  OCIBlobLocator *blob;
{
  long_varraw *lvr;
  ub4      bufsize;
  ub4      amt;
  ub4      filelen, remainder, nbytes;
  ub4      offset = 1;
  boolean  last = FALSE;
  FILE    *in_fd;

  /* Open the file for reading. */
  in_fd = fopen(filename, "r");
  if (in_fd == (FILE *)0)
    return (ub4)0;

  /* Determine Total File Length - Total Amount to Write to BLOB */
  (void) fseek(in_fd, 0L, SEEK_END);
  amt = filelen = (ub4)ftell(in_fd);

  /* Determine the Buffer Size and Allocate the LONG VARRAW Object */
  bufsize = 2048;
  lvr = (long_varraw *)malloc(sizeof(ub4) + bufsize);

  nbytes = (filelen > bufsize) ? bufsize : filelen;
      
  /* Reset the File Pointer and Perform the Initial Read */
  (void) fseek(in_fd, 0L, SEEK_SET);
  lvr->len = fread((void *)lvr->buf, (size_t)1, (size_t)nbytes, in_fd);
  remainder = filelen - nbytes;

  EXEC SQL WHENEVER SQLERROR DO sql_error("read_file():WRITE");

  if (remainder == 0)
    {
      /* Write the BLOB in a Single Piece */
      EXEC SQL LOB WRITE ONE :amt
         FROM :lvr WITH LENGTH :nbytes INTO :blob AT :offset;
    }
  else
    {
      /* Write the BLOB in Multiple Pieces using Standard Polling */
      EXEC SQL LOB WRITE FIRST :amt
         FROM :lvr WITH LENGTH :nbytes INTO :blob AT :offset;

      do {

        if (remainder > bufsize)
          nbytes = bufsize;
        else
          {
            nbytes = remainder;
            last = TRUE;
          }

        if ((lvr->len = fread(
              (void *)lvr->buf, (size_t)1, (size_t)nbytes, in_fd)) != nbytes)
          last = TRUE;

        if (last)
          {
            /* Write the Final Piece */
            EXEC SQL LOB WRITE LAST :amt
               FROM :lvr WITH LENGTH :nbytes INTO :blob;
          }
        else
          {
            /* Write an Interim Piece - Still More to Write */
            EXEC SQL LOB WRITE NEXT :amt
               FROM :lvr WITH LENGTH :nbytes INTO :blob;  
          }

        remainder -= nbytes;

      } while (!last && !feof(in_fd));
    }

  /* Close the file, and return the total file size. */
  fclose(in_fd);
  free(lvr);
  return filelen;
}


/* Generic error handler.  The 'routine' parameter should contain the name
   of the routine executing when the error occured.  This would be specified
   in the 'EXEC SQL WHENEVER SQLERROR DO sql_error()' statement.
 */
void sql_error(routine)
  char *routine;
{
  char message_buffer[512];
  size_t buffer_size;
  size_t message_length;

  /* Turn off the call to sql_error() to avoid a possible infinite loop */
  EXEC SQL WHENEVER SQLERROR CONTINUE;

  printf("\nOracle error while executing %s!\n", routine);

  /* Use sqlglm() to get the full text of the error message. */
  buffer_size = sizeof(message_buffer);
  sqlglm(message_buffer, &buffer_size, &message_length);
  printf("%.*s\n", message_length, message_buffer);

  EXEC SQL ROLLBACK WORK RELEASE;
  exit(1);
}


/* Opens the binary file identified by 'filename' for writing, and copies
   the contents of the Binary LOB into it.
 */
void write_file(filename, blob)
  char *filename;
  OCIBlobLocator *blob;
{
  FILE        *out_fd;       /* File descriptor for the output file */
  ub4          amt;
  ub4          bufsize;
  long_varraw *lvr;

  /* Determine the Buffer Size and Allocate the LONG VARRAW Object */
  bufsize = 2048;
  lvr = (long_varraw *)malloc(sizeof(ub4) + bufsize);

  /* Open the output file for Writing */
  out_fd = fopen(filename, "w");
  if (out_fd == (FILE *)0)
    return;

  amt = 0;             /* Initialize for Standard Polling (Possibly) */
  lvr->len = bufsize;                       /* Set the Buffer Length */

  EXEC SQL WHENEVER SQLERROR DO sql_error("write_file():READ");

  /* READ the BLOB using a Standard Polling Loop */
  EXEC SQL WHENEVER NOT FOUND DO break;
  while (TRUE)
    {
      EXEC SQL LOB READ :amt FROM :blob INTO :lvr WITH LENGTH :bufsize;
      (void) fwrite((void *)lvr->buf, (size_t)1, (size_t)lvr->len, out_fd);
    }
  
  EXEC SQL WHENEVER NOT FOUND CONTINUE;

  /* Write the Final Piece (or First and Only Piece if not Polling) */
  (void) fwrite((void *)lvr->buf, (size_t)lvr->len, (size_t)1, out_fd);

  /* Close the Output File and Return */
  fclose(out_fd);
  free(lvr);
  return;
}



/* Inserts the binary file identified by file into the
 * executables table identified by key.
 */
void do_insert(key, file)
  varchar *key;
  char *file;
{
  OCIBlobLocator *blob;
  ub4 loblen, fillen;

  EXEC SQL ALLOCATE :blob;

  EXEC SQL WHENEVER SQLERROR DO sql_error("do_insert():INSERT/SELECT");

  EXEC SQL SAVEPOINT PREINSERT;
  EXEC SQL INSERT
    INTO executables (name, length, binary) VALUES (:key, 0, empty_blob());

  EXEC SQL SELECT binary INTO :blob 
             FROM executables WHERE name = :key FOR UPDATE;

  printf(
    "Inserting file '%s' under key '%.*s'...\n", file, key->len, key->arr); 

  fillen = read_file(file, blob);
  EXEC SQL LOB DESCRIBE :blob GET LENGTH INTO :loblen;

  if ((fillen == 0) || (fillen != loblen))
    {
      printf("Problem reading file '%s'\n", file);
      EXEC SQL ROLLBACK TO SAVEPOINT PREINSERT;
      EXEC SQL FREE :blob;
      return;
    }

  EXEC SQL WHENEVER SQLERROR DO sql_error("do_insert():UPDATE");
  EXEC SQL UPDATE executables
    SET length = :loblen, binary = :blob WHERE name = :key;

  EXEC SQL COMMIT WORK;

  EXEC SQL FREE :blob;
  EXEC SQL COMMIT;
  printf("Inserted.\n");
}


/* Retrieves the executable identified by key into file */
void do_retrieve(key, file)
  varchar *key;
  char *file;
{
  OCIBlobLocator *blob;

  printf("Retrieving executable stored under key '%.*s' to file '%s'...\n",
         key->len, key->arr, file);

  EXEC SQL ALLOCATE :blob;

  EXEC SQL WHENEVER NOT FOUND continue;
  EXEC SQL SELECT binary INTO :blob FROM executables WHERE name = :key;

  if (sqlca.sqlcode == NOT_FOUND)
    printf("Key '%.*s' not found!\n", key->len, key->arr);
  else 
    {
      write_file(file, blob);
      printf("Retrieved.\n");
    }
 
  EXEC SQL FREE :blob;
}


/* Delete an executable from the database */
void do_delete(key)
  varchar *key;
{
  EXEC SQL WHENEVER SQLERROR DO sql_error("do_delete():DELETE");
  EXEC SQL DELETE FROM executables WHERE name = :key;

  if (sqlca.sqlcode == NOT_FOUND)
    printf("Key '%.*s' not found!\n", key->len, key->arr);
  else
    printf("Deleted.\n");
}


/* List all executables currently stored in the database */
void list_executables()
{
  char key[21];
  ub4  length;

  EXEC SQL WHENEVER SQLERROR DO sql_error("list_executables");

  EXEC SQL DECLARE key_cursor CURSOR FOR
    SELECT name, length FROM executables;

  EXEC SQL OPEN key_cursor;

  printf("\nExecutables           Length (bytes)\n");
  printf("--------------------  --------------\n");

  EXEC SQL WHENEVER NOT FOUND DO break;
  while (1)
  {
    EXEC SQL FETCH key_cursor INTO :key, :length;
    printf("%s      %10d\n", key, length);
  }

  EXEC SQL WHENEVER NOT FOUND CONTINUE;
  EXEC SQL CLOSE key_cursor;

  printf("\nTotal Executables: %d\n", sqlca.sqlerrd[2]);  
}


/* Prints the menu selections. */
void print_menu()
{
  printf("\nSample 4 Menu.  Would you like to:\n");
  printf("(I)nsert a new executable into the database\n");
  printf("(R)etrieve an executable from the database\n");
  printf("(L)ist the executables stored in the database\n");
  printf("(D)elete an executable from the database\n");
  printf("(Q)uit the program\n\n");
  printf("Enter i, r, l, or q: ");
}

関連項目

5.4 Cプリプロセッサ

Pro*C/C++では、Cプリプロセッサのほとんどのディレクティブがサポートされます。Pro*C/C++プリプロセッサを使用すると、次のような作業を実行できます。

  • #defineディレクティブを使用した定数およびマクロの定義、VARCHARなどのPro*C/C++のデータ型宣言をパラメータ化する場合の定義済エンティティの使用。

  • #includeディレクティブを使用した、プリコンパイラに必要なsqlca.hなどのファイルの読取り。

  • 個別のファイルにある定数およびマクロの定義、プリコンパイラでの#includeディレクティブを使用したこのファイルの読取り。

5.4.1 Pro*C/C++プリプロセッサの機能

Pro*C/C++プリプロセッサでは、Cプリプロセッサのほとんどのコマンドが認識され、必要なマクロ置換、ファイルのインクルードおよび条件付きのソース・テキストのインクルードまたは除外が効率的に実行されます。Pro*C/C++プリプロセッサでは、この事前処理で取得した値を使用して、ソース出力テキスト(生成される.c出力ファイル)が変更されます。

この点について、次に例を示します。次のプログラムの一部分を記述したとします。

#include "my_header.h" 
... 
VARCHAR name[VC_LEN];              /* a Pro*C-supplied datatype */ 
char    another_name[VC_LEN];              /* a pure C datatype */
... 

カレント・ディレクトリ内のmy_header.hファイルに、特に次の行が含まれているとします。

#define VC_LEN   20 

プリコンパイラはファイルmy_header.hを読み込み、VC_LENの定義済の値(20)を使用し、nameの構造体をVARCHAR[20]として宣言します。

charはネイティブな型です。プリコンパイラは、another_name[VC_LEN]の宣言時に20を代入しません。

プリコンパイラはC言語のデータ型の宣言を処理する必要がないため、そのデータ型がホスト変数として指定されてもかまいません。実際にファイルmy_header.hをインクルードして、another_nameの宣言内でVC_LENに20を代入する処理は、Cコンパイラのプリプロセッサの役割です。

5.4.2 プリプロセッサ・ディレクティブ

Pro*C/C++でサポートされるプリプロセッサ・ディレクティブは、次のとおりです。

  • #define。プリコンパイラとCまたはC++コンパイラで使用されるマクロを作成します。

  • #include。プリコンパイラで使用される他のソース・ファイルを読み込みます。

  • #if。定数式が0に評価される場合にのみ、ソース・テキストをプリコンパイルおよびコンパイルします。

  • #ifdef。定義済定数の有無に応じてソース・テキストを条件付きでプリコンパイルおよびコンパイルします。

  • #ifndef。ソース・テキストを条件付きで除外します。

  • #endif#if#ifdefまたは#ifndefコマンドを終了します。

  • #else #if #ifdefまたは#ifndefの条件が満たされない場合に、プリコンパイルおよびコンパイルされるソース・テキストの代替部を選択します。

  • #elif。定数またはマクロ引数の値に応じて、プリコンパイルおよびコンパイルされるソース・テキストの代替部を選択します。

5.4.2.1 無視されるディレクティブ

Pro*C/C++プリプロセッサで使用されないCプリプロセッサ・ディレクティブもあります。これらのディレクティブのほとんどは、プリコンパイラとは無関係です。たとえば、#pragmaはCコンパイラ用のディレクティブであり、プリコンパイラでは処理されません。プリコンパイラで処理されないCプリプロセッサ・ディレクティブは、次のとおりです。

  • #。プリプロセッサのマクロ・パラメータを文字定数に変換します。

  • ##。2つのプリプロセッサ・トークンを1つのマクロ定義にマージします。

  • #error。コンパイル時エラー・メッセージを生成します。

  • #pragma。実装依存情報をCコンパイラに渡します。

  • #line。Cコンパイラ・メッセージに行番号を提供します。

これらのディレクティブは、Cコンパイラのプリプロセッサでサポートされている場合でも、Pro*C/C++では使用されません。これらのディレクティブのほとんどは、プリコンパイラでは使用されません。コンパイラでサポートされている場合は、これらのディレクティブをPro*C/C++プログラムで使用できますが、CまたはC++コード以外の埋込みSQL文や、プリコンパイラが提供するVARCHARなどのデータ型を使用した変数の宣言では使用できません。

5.4.3 ORA_PROCマクロ

Pro*C/C++では、ORA_PROCというCプリプロセッサのマクロが事前定義されています。このマクロを使用すると、不要または無関係なコード・セクションがプリコンパイラで処理されるのを回避できます。アプリケーションには、プリコンパイル時に必要のない情報を提供する大きなヘッダー・ファイルがインクルードされている場合があります。このようなヘッダー・ファイルは、ORA_PROCマクロに基づいて条件付きで除外すれば、プリコンパイラで読み込まれることはありません。

次の例では、ORA_PROCマクロを使用してirrelevant.hファイルを除外しています。

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

プリコンパイル時にORA_PROCが定義されているため、irrelevant.hファイルはインクルードされません。

ORA_PROCマクロを使用できる対象は、#ifdef#ifndefなどのCプリプロセッサ・ディレクティブのみです。EXEC ORACLE条件文では、Cプリプロセッサ・マクロと同じネームスペースは共有されません。したがって、次の例の条件では、事前定義済のORA_PROCマクロを使用しません

EXEC ORACLE IFNDEF ORA_PROC;
   <section of code to be ignored>
EXEC ORACLE ENDIF;

この場合、この条件コード部分が正常に処理されるように、DEFINEオプションまたはEXEC ORACLE DEFINE文を使用してORA_PROCを設定する必要があります。

5.4.4 ヘッダー・ファイルの格納場所の指定

各システムのPro*C/C++プリコンパイラでは、sqlca.horaca.hおよびsqlda.hなど、プリプロセッサで読み込まれるヘッダー・ファイルが標準的な場所にあるものとみなされます。たとえば、ほとんどのUNIXシステムでの標準的な場所は、$ORACLE_HOME/precomp/publicです。使用しているシステムのデフォルトの場所については、システム固有のOracleマニュアルを参照してください。インクルードする必要のあるヘッダー・ファイルがデフォルトの場所にない場合は、コマンドラインで、EXEC ORACLEオプションとして、INCLUDE=オプションを指定する必要があります。

stdio.hiostream.hなどのシステム・ヘッダー・ファイルについて、Pro*C/C++にハードコードされているものと異なる格納場所を指定するには、SYS_INCLUDEプリコンパイラ・オプションを使用します。

5.4.5 プリプロセッサの例

#defineコマンドを使用すると、名前付きの定数を作成して、ソース・コード内でマジック番号のかわりに使用できます。VARCHAR[const]など、プリコンパイラで必要な宣言に対して、#defineで指定した定数を使用できます。たとえば、次のコードは不具合を含んでいる可能性があります。

... 
VARCHAR  emp_name[10]; 
VARCHAR  dept_loc[14]; 
... 
... 
/* much later in the code ... */ 
f42() 
{ 
    /* did you remember the correct size? */
    VARCHAR new_dept_loc[10]; 
   ... 
} 

このコードのかわりに、次のように記述できます。

#define ENAME_LEN     10 
#define LOCATION_LEN  14 
VARCHAR  new_emp_name[ENAME_LEN]; 
   ... 
/* much later in the code ... */ 
f42() 
{ 
    VARCHAR new_dept_loc[LOCATION_LEN]; 
   ... 
} 

引数を持つプリプロセッサ・マクロは、Cオブジェクトで使用する場合と同様に、プリコンパイラで処理する必要のあるオブジェクトで使用できます。たとえば:

#define ENAME_LEN    10 
#define LOCATION_LEN 14 
#define MAX(A,B)  ((A) > (B) ? (A) : (B)) 
 
   ... 
f43() 
{ 
    /* need to declare a temporary variable to hold either an 
       employee name or a department location */ 
    VARCHAR  name_loc_temp[MAX(ENAME_LEN, LOCATION_LEN)]; 
   ... 
} 

#include#ifdefおよび#endifプリプロセッサ・ディレクティブを使用して、プリコンパイラに必要なファイルを条件付きでインクルードできます。たとえば:

#ifdef ORACLE_MODE 
#include <sqlca.h> 
#else 
    long SQLCODE; 
#endif 
5.4.5.1 #defineの使用について

Pro*C/C++での#defineプリプロセッサ・ディレクティブの使用には、制限があります。#defineディレクティブを使用して、実行SQL文に使用する記号定数を生成することはできません。次の無効な例を参照してください。

#define RESEARCH_DEPT   40 
... 
EXEC SQL SELECT empno, sal 
    INTO :emp_number, :salary /* host arrays */
    FROM emp 
    WHERE deptno = RESEARCH_DEPT;  /* INVALID! */

#defineしたマクロを有効に使用できる宣言SQL文は、TYPE文とVAR文のみです。したがって、たとえば、次のマクロ使用例はPro*C/C++で有効です。

#define STR_LEN      40
...
typedef char asciiz[STR_LEN];
...
EXEC SQL TYPE asciiz IS STRING(STR_LEN) REFERENCE;
...
EXEC SQL VAR password IS STRING(STR_LEN);
5.4.5.2 他のプリプロセッサの制限

プリプロセッサでは、ディレクティブ#および ## が無視され、プリコンパイラで認識する必要のあるトークンが作成されます。これらのコマンドは(コンパイラでサポートされている場合は)、純粋なCコードに使用できます。プリコンパイラでは、これらのコードは処理されません。次の例の場合、プリプロセッサ・コマンド##の使用は無効です。

#define MAKE_COL_NAME(A)    col ## A 
... 
EXEC SQL SELECT MAKE_COL_NAME(1), MAKE_COL_NAME(2) 
    INTO :x, :y 
    FROM table1; 

プリコンパイラでは##は無視されるため、この例は正しくありません。

5.4.6 #includeに使用できないSQL文

Pro*C/C++プリプロセッサで#includeディレクティブが処理される方法については前項で説明しましたが、この方法のために、#includeディレクティブを使用して埋込みSQL文を含むファイルをインクルードできません。#includeは、純粋に宣言文とディレクティブのみを含むファイルをインクルードする場合に使用します。たとえば、#defineや、プリコンパイラに必要な変数と構造体の宣言のみを含むsqlca.hファイルなどです。

5.4.7 SQLCA、ORACAおよびSQLDAの組込み

C/C++プリプロセッサの#includeコマンド、あるいはプリコンパイラのEXEC SQL INCLUDEコマンドを使用すると、sqlca.horaca.hおよびsqlda.hの各宣言ヘッダー・ファイルをPro*C/C++プログラムにインクルードできます。たとえば、次の文のように、EXEC SQLオプションを指定して、SQL通信領域構造体(SQLCA)をプログラムにインクルードできます。

EXEC SQL INCLUDE sqlca; 

C/C++プリプロセッサ・ディレクティブを使用してSQLCAをインクルードするには、次のコードを追加します。

#include <sqlca.h> 

プリプロセッサの#includeディレクティブを使用する場合は、ファイル拡張子(.hなど)を指定する必要があります。

ノート:

#includeディレクティブを使用してSQLCAを複数箇所にインクルードする必要がある場合は、#includeの前にディレクティブ#undef SQLCAを挿入してください。これは、sqlca.hが次の行から開始されるためです。

      #ifndef SQLCA
      #define SQLCA 1

次に、sqlca.hはSQLCAが定義されていない場合にのみSQLCA構造体を宣言します。

#includeディレクティブまたはEXEC SQL INCLUDE文を含むファイルをプリコンパイルする場合は、インクルードするすべてのファイルの位置をプリコンパイラに対して指定する必要があります。INCLUDE=オプションは、コマンドライン、システム構成ファイルまたはユーザー構成ファイル内で使用できます。

sqlca.horaca.hおよびsqlda.hなど、標準的なプリプロセッサ・ヘッダー・ファイルのデフォルトの場所は、プリコンパイラ内で事前設定されています。この場所は、システムごとに異なります。使用しているシステムのデフォルトの場所については、システム固有のOracleマニュアルを参照してください。

Pro*C/C++で生成される.c出力ファイルをコンパイルする場合は、コンパイラおよびオペレーティング・システムに用意されているオプションを使用して、インクルード・ファイルの場所を識別する必要があります。

たとえば、ほとんどのUNIXシステムでは、次のコマンドを使用して、生成されるCソース・ファイルをコンパイルできます。

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

VAX/OPENVMSシステムでは、事前にインクルード・ディレクトリ・パスを論理VAXC$INCLUDE内の値に設定します。

5.4.8 EXEC SQL INCLUDEおよび#includeのまとめ

プログラム内でEXEC SQL INCLUDE文を使用すると、プリコンパイラによりソース・テキストが出力(.c)ファイルにインクルードされます。したがって、EXEC SQL INCLUDEを使用してインクルード・ファイルに、宣言文および埋込みSQLの実行文を入れることができます。

#includeを使用してファイルをインクルードする場合、プリコンパイラでは単にファイルが読み込まれ、#defineで定義したマクロが追跡記録されるのみです。

ノート:

VARCHAR宣言とSQL文は、インクルード(#include)ファイルには使用できません。このため、Pro*C/C++プリプロセッサの#includeディレクティブを使用してインクルード・ファイルでは、SQL文を使用できません。

5.4.9 定義済マクロ

Cコンパイラのコマンドラインでマクロを定義する場合、アプリケーションの要件によっては、そのマクロをプリコンパイラのコマンドラインでも定義する必要があります。たとえば、UNIXコマンドラインで次のようにコンパイルするとします。

cc -DDEBUG ...

この場合は、次のようにDEFINE=オプションを使用してプリコンパイルする必要があります。

proc DEFINE=DEBUG ...

5.4.10 インクルード・ファイル

プリコンパイルする必要のあるすべてのインクルード・ファイルの位置は、コマンドラインまたは構成ファイルで指定する必要があります。

たとえば、UNIX環境での開発中に、アプリケーションのインクルード・ファイルがディレクトリ/home/project42/includeにある場合は、このディレクトリをPro*C/C++のコマンドラインとccコマンドラインの両方で指定する必要があります。次のようなコマンドを使用します。

proc iname=my_app.pc include=/home/project42/include ...
cc -I/home/project42/include ... my_app.c

または、適切なマクロをMakeファイルに組み込みます。Pro*C/C++アプリケーションのコンパイルとリンクの詳細は、システム固有のOracleマニュアルを参照してください。

関連項目

5.5 プリコンパイル済ヘッダー・ファイル

プリコンパイル済ヘッダー・ファイルを使用すると、多数の#include文を含むヘッダー・ファイルをプリコンパイルすることで、時間とリソースを節約できます。この機能を使用するステップは次の2つです。

  • まず、プリコンパイル済ヘッダー・ファイルを作成します。

  • このプリコンパイル済のヘッダーが、次回以降のアプリケーションのプリコンパイル時に自動的に使用されます。

この機能は、多数のモジュールで構成される大型アプリケーションに使用してください。

プリコンパイラ・オプションをHEADER=hdrに設定すると、次のように指定されます。

  • プリコンパイル済のヘッダーを使用します。

  • 生成される出力ファイルのファイル拡張子はhdrです。

このオプションを入力できるのは、構成ファイルまたはコマンドラインのみです。HEADERにはデフォルト値はありませんが、入力ヘッダーの拡張子は.hである必要があります。

5.5.1 プリコンパイル済ヘッダー・ファイルの作成

ヘッダー・ファイルtop.hを作成するとします。このファイルは、HEADER=hdrを指定してプリコンパイルできます。

proc HEADER=hdr INAME=top.h

ノート:

拡張子は.hにする必要があります。INAME値には、「/」や「..」などの絶対パス要素や相対パス要素は使用できません。

Pro*C/C++により、指定した入力ファイルtop.hがプリコンパイルされ、同じディレクトリに新しいプリコンパイル済ヘッダー・ファイルtop.hdrが生成されます。出力ファイルtop.hdrは、#include文による検索対象となるディレクトリに移動できます。

ノート:

出力ファイル名の指定にはONAMEオプションを使用しないでください。HEADERとともに使用すると無視されます。

5.5.2 プリコンパイル済ヘッダー・ファイルの使用

HEADERオプションには、プリコンパイル対象のアプリケーション・ファイルと同じ値を使用してください。simple.pcに次のファイルが含まれているとします。

#include <top.h>
...

また、top.hに次のファイルが含まれているとします。

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

次の方法でプリコンパイルします。

proc HEADER=hdr INAME=simple.pc

Pro*C/C++では、#include top.h文の読込み時に対応するtop.hdrファイルが検索され、top.hが再度プリコンパイルされるかわりに、そのファイルからデータがインスタンス化されます。

ノート:

プリコンパイル済ヘッダー・ファイルは、常に入力ヘッダー・ファイルのかわりに使用されます。入力(.h)ファイルがインクルード・ディレクトリの標準検索階層の最初に表示されている場合も同様です。

5.5.3

この項の例では、いくつかの異なるケースを示します。

5.5.3.1 冗長なファイル・インクルード

次の2つのケースでは、考えられる2つの冗長なファイル・インクルードを示します。

5.5.3.1.1 ケース1: 最上位のヘッダー・ファイルのインクルード

プリコンパイル済ヘッダー・ファイルは、#includeディレクティブを使用してインクルードされた回数に関係なく、一度のみインスタンス化されます。

前述の例と同様に、HEADERの値をhdrに設定し、最上位のヘッダー・ファイルtop.hをプリコンパイルするとします。次に、そのヘッダー・ファイルに対して、複数の#includeディレクティブをプログラムに記述します。

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

top.hの最初の#includeが発生すると、プリコンパイル済ヘッダー・ファイルtop.hdrがインスタンス化されます。同じヘッダー・ファイルの2番目のインクルードは、冗長であるために無視されます。

5.5.3.1.2 ケース2: ネストされたヘッダー・ファイルのインクルード

ファイルa.hに次の文が含まれているとします。

#include <b.h>

前述の例と同様にHEADERを指定して、そのヘッダー・ファイルをプリコンパイルします。Pro*C/C++では、a.hとb.hの両方がプリコンパイルされ、a.hdrが生成されます。

次に、このPro*C/C++プログラムをプリコンパイルするとします。

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

a.hの#includeが発生すると、a.hが再度プリコンパイルされるかわりに、プリコンパイル済ヘッダー・ファイルa.hdrがインスタンス化されます。このインスタンス化には、b.hの内容全体も含まれます。

b.hはa.hのプリコンパイルに含まれ、a.hdrはインスタンス化されているため、プログラムに指定されているb.hの後続の#includeは冗長になり、無視されます。

5.5.3.2 複数のプリコンパイル済ヘッダー・ファイル

Pro*C/C++では、1回のプリコンパイルで複数の異なるプリコンパイル済ヘッダー・ファイルをインスタンス化できます。ただし、複数のプリコンパイル済ヘッダー・ファイルが共通のヘッダー・ファイルを共有している場合は、次の点に注意してください。

たとえば、topA.hに次の行が含まれているとします。

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

また、topB.hには次の行が含まれているとします。

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

topA.hおよびtopB.hの両方で、同じ共通ヘッダー・ファイルc.hがインクルードされています。同じHEADER値を指定してtopA.hとtopB.hをプリコンパイルすると、topA.hdrおよびtopB.hdrが生成されます。しかし、両方にc.hの内容全体が含まれています。

次のようなPro*C/C++プログラムがあるとします。

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

プリコンパイル済ヘッダー・ファイルtopA.hdrおよびtopB.hdrの両方が、前述の例と同様にインスタンス化されます。ただし、両方で共通ヘッダー・ファイルc.hが共有されるため、そのファイルの内容は2回インスタンス化されます。

Pro*C/C++では、プリコンパイル済ヘッダー・ファイル間のファイルの共有を判断できません。各プリコンパイル済ヘッダー・ファイルには、一意のヘッダー・セットをインクルードします。ヘッダーの共有はできるだけ避けてください。共有すると、プリコンパイルが低速になり、メモリー使用量が増加するため、プリコンパイル済ヘッダー・ファイルを使用する意味がなくなります。

5.5.4 ヘッダー・ファイルのリスト

ORACLE_BASE\ORACLE_HOME\precomp\publicディレクトリには、Pro*C/C++ヘッダー・ファイルが含まれています。表5-3は、ヘッダー・ファイルのリストと説明です。

表5-3 ヘッダー・ファイル

ヘッダー・ファイル 説明

oraca.h

Oracle通信領域(ORACA)が含まれています。これは、ランタイム・エラーの診断と、プログラムでの様々なOracle Database 10gリソースの使用の監視に役立ちます。

sql2oci.h

SQLLIB関数が含まれています。これらの関数により、Oracle Call Interface(OCI)環境ハンドルとOCIサービス・コンテキストをPro*C/C++アプリケーションで取得できます。

sqlapr.h

OCIとともに使用できる外部関数用のANSIプロトタイプが含まれています。

sqlca.h

ランタイム・エラーの診断に役立つSQLコミュニケーション領域(SQLCA)が含まれています。SQLCAは、実行可能なSQL文が実行されるたびに更新されます。

sqlcpr.h

Pro*C/C++で生成される、SQLLIB関数用のプラットフォーム固有のANSIプロトタイプが含まれています。デフォルトでは、Pro*C/C++は、SQLプログラミング・コールの完全な関数プロトタイプをサポートしていません。この機能が必要な場合、アプリケーション・ソース・ファイル内のすべてのEXEC SQL文の前にsqlcpr.hを含める必要があります。

oraca.h

Oracle通信領域(ORACA)が含まれています。これは、ランタイム・エラーの診断と、プログラムでの様々なOracle Database 10gリソースの使用の監視に役立ちます。

sql2oci.h

SQLLIB関数が含まれています。これらの関数により、Oracle Call Interface(OCI)環境ハンドルとOCIサービス・コンテキストをPro*C/C++アプリケーションで取得できます。

sqlapr.h

OCIとともに使用できる外部関数用のANSIプロトタイプが含まれています。

5.5.5 オプションの効果

アプリケーションのプリコンパイル時には、次のプリコンパイラ・オプションを使用できます。

5.5.5.1 DEFINEおよびINCLUDEオプション

プリコンパイル済ヘッダーを使用してプリコンパイルするときには、DEFINEとINCLUDEの値を、プリコンパイル済ヘッダー・ファイルの作成時と同じ値にする必要があります。DEFINEまたはINCLUDEの値を変更した場合は、プリコンパイル済ヘッダー・ファイルを再作成する必要があります。

開発環境を変更した場合も、プリコンパイル済ヘッダー・ファイルを再作成する必要があります。

5.5.5.1.1 シングル・ユーザーの場合

シングル・ユーザーの場合を考えてみます。DEFINEまたはINCLUDEオプションの値を変更した場合、プリコンパイル済ヘッダー・ファイルの内容は、その後に実行されるPro*C/C++プリコンパイルでは正常に使用できなくなります。

DEFINEおよびINCLUDE、DEFINEまたはINCLUDEオプションの値を変更しているため、プリコンパイル済ヘッダー・ファイルの内容は、#includeディレクティブの対応する.hファイルが正常に処理された場合の標準的な結果と一致しなくなります。

つまり、DEFINEおよびINCLUDE、DEFINEまたはINCLUDEオプションの値を変更した場合は、プリコンパイル済ヘッダー・ファイルを再作成し、それを使用するPro*C/C++プログラムを再度プリコンパイルする必要があります。

関連項目

5.5.5.1.2 マルチ・ユーザーの場合

AおよびBという2人のユーザーがいる場合を考えてみます。AとBはまったく異なる環境で開発しているため、DEFINEおよびINCLUDEオプションの値もまったく異なっています。

ユーザーAは、共通ヘッダー・ファイルcommon.hをプリコンパイルし、プリコンパイル済ヘッダー・ファイルcommon.hdrAを作成します。ユーザーBも同じヘッダー・ファイルをプリコンパイルして、common.hdrBを作成します。ただし、両者の環境が異なるため、2人のユーザーが使用したDEFINEおよびINCLUDEオプションの値も異なります。そのため、ユーザーAとBが作成したcommon.hdrの内容が同じになるとはかぎりません。

コードは次のようになります。

A> proc HEADER=hdrA DEFINE=<A macros> INCLUDE=<A dirs> common.h

B> proc HEADER=hdrB DEFINE=<B macros> INCLUDE=<B dirs> common.h

異なる環境で作成されたため、生成されたプリコンパイル済ヘッダー・ファイルcommon.hdrAはcommon.hdrBと同等でない場合があります。つまり、ユーザーAとユーザーBが、相手の作成したcommon.hdrを使用しても、それぞれの開発環境でPro*C/C++プログラムが正常にプリコンパイルされるとはかぎりません。

したがって、プリコンパイル済ヘッダー・ファイルを、異なるユーザー間および異なる開発環境間で共有または交換する場合には、注意が必要です。

5.5.5.2 CODEおよびPARSEオプション

Pro*C/C++では、hpph++などの拡張子が付いたC++ヘッダー・ファイルは検索されません。このため、ヘッダー・ファイルのプリコンパイル時には、CODE=CPPを使用しないでください。アプリケーションのプリコンパイル時にCPP値を使用できるのは、ソース・コードに.hヘッダー・ファイルのみが含まれる場合にかぎります。

プリコンパイル済ヘッダー・ファイルの作成時、あるいはモジュールのプリコンパイル時には、PARSEオプションに使用できる値はFULLまたはPARTIALのみです。値FULLは、PARTIALより高い値とみなされます。モジュールのプリコンパイル時に使用するPARSEの値は、プリコンパイル済ヘッダー・ファイルの作成時の値以下にする必要があります。

ノート:

PARSE=FULLを指定してプリコンパイル済ヘッダー・ファイルをプリコンパイルしてから、PARSE=PARTIALを指定してモジュールをプリコンパイルする場合は、ホスト変数を宣言部中で宣言する必要があります。C++コードは、PARSE=PARTIALを指定した場合にのみ認識されます。

PARTIALに次のPARSEオプションを指定してヘッダー・ファイルをプリコンパイルするとします。

proc HEADER=hdr PARSE=PARTIAL file.h

次に、PARSEをFULLに設定し、そのヘッダー・ファイルを含むプログラムをプリコンパイルします。

proc HEADER=hdr PARSE=FULL program.pc

ファイル.hはPARSEオプションにPARTIALを設定してプリコンパイルしたため、一部のヘッダー・ファイルは処理されていません。このため、未処理部分が参照された場合は、Pro*C/C++プログラムのプリコンパイル時にエラーが発生する可能性があります。

具体例として、ファイル.hに次のコードが含まれているとします。

#define LENGTH 10
typedef int myint;

program.pcに、次の小さいプログラムが含まれているとします。

#include <file.h>
main()
{
     VARCHAR ename[LENGTH];
     myint empno = ...;
     EXEC SQL SELECT ename INTO :ename WHERE JOB = :empno;
}

ファイル.hのプリコンパイル時にPARSEがPARTIALに設定されているため、typedefは処理されずにLENGTHマクロのみが処理されます。

VARCHARの宣言と宣言以後のホスト変数としての使用は、正常に行われます。ただし、Pro*C/C++ではmyint型宣言が処理されないため、empnoホスト変数を使用できません。

PARSEオプションをFULLに設定してヘッダー・ファイルをプリコンパイルしてから、PARSEオプションをPARTIALに設定してプログラムをプリコンパイルすると、正常に動作します。ただし、ホスト変数は明示的な宣言部中で宣言する必要があります。

5.5.6 使用上のノート

プリコンパイル済のヘッダーから生成された出力ファイルの形式は、リリースごとに異なる可能性があります。Pro*C/C++では、プリコンパイル済ヘッダー・ファイルの出力の生成に使用されたプリコンパイラのバージョンを判断できません。

このため、プリコンパイル済ヘッダー・ファイルを使用したプリコンパイル時に、エラーまたは他の予期しない動作を回避するために、Pro*C/C++のリリースのアップグレード時には、対応するヘッダー・ファイルを再度プリコンパイルしてファイルを再生成することをお薦めします。

ヘッダー・ファイルをプリコンパイルして生成された出力ファイルには、移植性がありません。つまり、ヘッダー・ファイルのプリコンパイルにより生成された出力ファイルは、プラットフォーム間で転送できず、別のヘッダー・ファイルまたはPro*C/C++プログラムのプリコンパイル時に使用できません。

5.6 Oracleプリプロセッサ

コードの条件文は、環境と実行する処理を定義するEXEC ORACLEディレクティブでマークされます。これらの条件文には、Cの文、埋込みSQL文およびディレクティブを記述できます。次のEXEC ORACLEディレクティブでは、プリコンパイルの条件を制御できます。

EXEC ORACLE DEFINE symbol;    -- define a symbol 
EXEC ORACLE IFDEF symbol;     -- if symbol is defined 
EXEC ORACLE IFNDEF symbol;    -- if symbol is not defined 
EXEC ORACLE ELSE;             -- otherwise 
EXEC ORACLE ENDIF;            -- end this block 

すべてのEXEC ORACLE文の終わりには、セミコロンを付ける必要があります。

5.6.1 記号の定義

シンボルを定義するには2通りの方法があります。1つの方法では、ホスト・プログラムに次の文を含めます。

EXEC ORACLE DEFINE symbol;

もう1つは、次の構文を使用してコマンドラインでシンボルを定義する方法です。

... INAME=filename ... DEFINE=symbol 

symbolの部分は大/小文字区別がありません。

ノート:

#defineプリプロセッサ・ディレクティブは、EXEC ORACLE DEFINEコマンドとは異なります。

Pro*C/C++プリコンパイラをシステムにインストールするときに、ポート固有の記号がいくつか事前定義されます。

5.6.2 Oracleプリプロセッサの例

次の例では、記号site2が定義されている場合にのみ、SELECT文がプリコンパイルされます。

EXEC ORACLE IFDEF site2; 
    EXEC SQL SELECT DNAME 
        INTO :dept_name 
        FROM DEPT 
        WHERE DEPTNO = :dept_number; 
EXEC ORACLE ENDIF; 

次の例に示すように条件ブロックはネストできます。

EXEC ORACLE IFDEF outer; 
    EXEC ORACLE IFDEF inner; 
    ... 
    EXEC ORACLE ENDIF; 
EXEC ORACLE ENDIF;

Cまたは埋込みSQLコードをIFDEFとENDIFの間に記述し、シンボルを定義しないことで、そのコードをコメント行にすることができます。

5.7 数値定数の評価

以前のPro*C/C++では、ホスト変数(charまたはVARCHARなど)のサイズの宣言時に使用できるのは、数値リテラルや数値リテラルを含む単純な定数式のみでした。次に例を示します。

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

現在は、次のような数値定数の宣言も使用できます。

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

このような定数宣言をサポートするANSIコンパイラやC++コンパイラを使用している開発者にとって、この機能は最適です。

従来のPro*C/C++では、評価可能な定数式の値の評価は常に実行されてきましたが、数値定数はどのような定数式にも宣言できませんでした。

Pro*C/C++では、マクロが数値リテラルに展開されていれば、通常の数値リテラルやマクロを使用する位置であれば、任意の位置で数値定数を宣言できます。

これは主に、SQL文で使用するバインド変数の配列のサイズを宣言する場合に使用されます。

5.7.1 Pro*C/C++での数値定数

Pro*C/C++では、数値定数が宣言された位置を検索する場合は、C言語の標準のスコープ規則が使用されます。

     const int g = 30;     /* Global declaration to both function_1()
                                                  and function_2() */
     void function_1()
     {
       const int a = 10;  /* Local declaration only to function_1() */
       char x[a];
       exec sql select ename into :x from emp where job = 'PRESIDENT';
     }

     void function_2()
     {
       const int a = 20;  /* Local declaration only to function_2() */
       VARCHAR v[a];
       exec sql select ename into :v from emp where job = 'PRESIDENT';
     }

     void main()
     {
       char m[g];                                   /* The global g */
       exec sql select ename into :m from emp where job = 'PRESIDENT';
     }

5.7.2 数値定数の規則および例

特定の静的な型を持つ変数は、staticで定義し、初期化する必要があります。Pro*C/C++で数値定数を宣言する場合は、必ず次の規則に従ってください。

  • 定数の宣言時にconst修飾子を指定します。

  • 定数の値の初期化時に初期化機能を使用します。この初期化機能は、プリコンパイル時に評価可能である必要があります。

有効な初期化機能が指定された定数宣言で解決できない識別子を使用すると、エラーとみなされます。

次の例は、無効な指定方法とその指定が許可されない理由を示しています。

int a;
int b = 10;
volatile c;
volatile d = 10;
const e;
const f = b;

VARCHAR v1[a]; /* No const qualifier, missing initializer */
VARCHAR v2[b];                      /* No const qualifier */
VARCHAR v3[c];     /* Not a constant, missing initializer */
VARCHAR v4[d];                          /* Not a constant */
VARCHAR v5[e];                     /* Missing initializer */
VARCHAR v6[f];   /* Bad initializer.. b is not a constant */

5.8 OCIリリース8のSQLLIB拡張相互運用性

OCI環境ハンドルは、sql_context型のPro*C/C++ランタイム・コンテキストに関連付けられます。つまり、アプリケーション実行時にSQLLIBに保持されるPro*C/C++ランタイム・コンテキストは、複数のOCI環境ハンドルに関連付けることはできません。Pro*C/C++ランタイム・コンテキストごとに複数のデータベース接続を行うことができ、各ランタイム・コンテキストはそのOCI環境ハンドルに関連付けられます。

ノート:

プリコンパイラ・アプリケーションはOCIハンドルを抽出し、OCI関数を直接呼び出すことができます。ただし、プリコンパイラは戻される可能性のある「まだ実行中」エラーを処理できないので、非ブロックモードはサポートされていません。

5.8.1 OCIリリース8環境でのランタイム・コンテキスト

EXEC SQL CONTEXT USE文は、Pro*C/C++プログラムで使用するランタイム・コンテキストを指定します。このコンテキストは、所定のPro*C/C++ファイル内でそのEXEC SQL CONTEXT USE文から次に指定されたEXEC SQL CONTEXT USE文までの、SQL実行文すべてに適用されます。ソース・ファイルにEXEC SQL CONTEXT USEがない場合、デフォルトのグローバル・コンテキストとみなされます。このため、カレント・ランタイム・コンテキストとそれに対応付けられているカレントOCI環境ハンドルは、プログラム内のどの場所にあっても認識されます。

Pro*C/C++では、EXEC SQL CONNECTを使用してデータベースへのログオンが実行されると、ランタイム・コンテキストとそれに対応付けられているOCI環境ハンドルが初期化されます。

EXEC SQL CONTEXT FREE文を使用してPro*C/C++ランタイム・コンテキストを解放すると、それに対応付けられているOCI環境ハンドルが終了し、各種OCIハンドルやLOBロケータに割り当てられている領域など、すべてのリソースが割当て解除されます。このコマンドにより、Pro*C/C++ランタイム・コンテキストに対応付けられた他のすべてのメモリーがすべて解放されます。デフォルトのグローバル・ランタイム・コンテキストに確立されるOCI環境ハンドルは、Pro*C/C++プログラムが終了するまで割り当てられたままになっています。

5.8.2 OCIリリース8環境ハンドルのパラメータ

Pro*C/C++で確立されるOCI環境は、次のパラメータを使用します。

  • メモリーの割当て、メモリーの解放、テキスト・ファイルへの書込みおよび出力バッファのフラッシュについて、その環境で使用されるコールバック関数は、それぞれmalloc()、free()、fprintf(stderr, ...)およびfflush(stderr)をコールする通常の関数です。

  • 言語は、グローバリゼーション・サポート変数NLS_LANGから取得されます。

  • エラー・メッセージ・バッファは、スレッド固有の記憶域に割り当てられます。

5.9 OCIリリース8へのインタフェース

SQLLIBライブラリには、Pro*C/C++プログラムで確立したデータベース接続に対して、OCI環境ハンドルおよびサービス・コンテキスト・ハンドルを取得するためのルーチンが用意されています。OCIハンドルを取得すると、ユーザーは様々なOCIルーチンをコールできます。たとえば、クライアント側でDATE算術を実行したり、オブジェクト側でナビゲーション操作を実行したりできます。これらのSQLLIB関数については後述します。これらの関数のプロトタイプは、パブリック・ヘッダー・ファイルsql2oci.hに用意されています。

埋込みSQLと他のOracleプログラム・インタフェースのコールを混在させるPro*C/C++ユーザーは、十分に注意する必要があります。たとえば、ユーザーがOCIインタフェースを使用して直接接続を終了すると、SQLLIBが非同期状態となります。このような場合、Pro*C/C++プログラム内の後続のSQL文の動作は未定義になります。

ノート:

Pro*C/C++、Oracle Call Interface(OCI)リリース8およびXAには互換性がありません

Oracle OCIとの相互運用性を提供する次の新しいSQLLIB関数がヘッダー・ファイルsql2oci.h内で宣言されています。

  • SQLEnvGet()。所定のSQLLIBランタイム・コンテキストに関連付けられたOCI環境ハンドルへのポインタを戻します。単一サーバー環境と共有サーバー環境の両方で使用できます。

  • SQLSvcCtxGet()。Pro*C/C++データベース接続用のOCIサービス・コンテキスト・ハンドルを戻します。単一サーバー環境と共有サーバー環境の両方で使用できます。

  • シングル・スレッド・ランタイム・コンテキストを使用するときに、どちらかの関数の最初のパラメータとしてsql2oci.hをインクルードする場合は、(dvoid *)0として定義されている定数SQL_SINGLE_RCTXを渡します。

関連項目

5.9.1 SQLEnvGet()

SQLLIBライブラリ関数SQLEnvGet()(SQLLIBによるOCI環境の取得)を指定すると、所定のSQLLIBランタイム・コンテキストに関連付けられているOCI環境ハンドルへのポインタが戻されます。この関数のプロトタイプは、次のとおりです。

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

各パラメータの意味は次のとおりです。

項目 説明

説明

oehをランタイム・コンテキストに対応するOCIEnvに設定します。

パラメータ

rctx (IN) = SQLLIBランタイム・コンテキストへのポインタ。

oeh (OUT) = OCIEnvへのポインタ。

戻り値

成功した場合はSQL_SUCCESS。

失敗した場合はSQL_ERROR。

ノート

Pro*C/C++での通常のエラー状況変数(SQLCA、SQLSTATEなど)は、この関数をコールしても影響を受けません。

5.9.2 SQLSvcCtxGet()

SQLLIBライブラリ関数SQLSvcCtxGet()(SQLLIBによるOCIサービス・コンテキストの取得)を指定すると、Pro*C/C++データベース接続用のOCIサービス・コンテキストが戻されます。OCIサービス・コンテキストを使用して、OCI関数を直接コールできます。この関数のプロトタイプは、次のとおりです。

sword SQLSvcCtxGet(dvoid *rctx, text *dbname,
       sb4 dbnamelen, OCISvcCtx **svc);

各パラメータの意味は次のとおりです。

項目 説明

説明

svcをランタイム・コンテキストに対応するOCIサービス・コンテキストに設定します。

パラメータ

rctx (IN) = SQLLIBランタイム・コンテキストへのポインタ。

dbname (IN) = この接続の論理名を含むバッファ。

dbnamelen (IN) = dbnameバッファの長さ。

svc (OUT) = OCISvcCtxポインタのアドレス。

戻り値

成功した場合はSQL_SUCCESS。

失敗した場合はSQL_ERROR。

ノート

1. Pro*C/C++での通常のエラー状況変数(SQLCA、SQLSTATEなど)は、この関数をコールしても影響を受けません。

2. dbnameは、埋込みSQL文のAT句に使用されている識別子と同じです。

3. dbnameがNULLポインタであるか、dbnamelenが0の場合は、SQL文にAT句が指定されていない場合と同様に、デフォルトのデータベース接続とみなされます。

4. dbnamelenの値が-1の場合は、dbnameが0で終了する文字列であることを示します。

5.9.3 OCIリリース8コールの埋込み

OCIリリース8コールをPro*C/C++プログラムに埋め込むには:

1. パブリック・ヘッダーsql2oci.hを組み込みます。

2. Pro*C/C++プログラム内で環境ハンドル(OCIEnv *型)を次のように宣言します。

OCIEnv *oeh;

3. コールするOCI関数がサービス・コンテキスト・ハンドルを必要とする場合は、オプションとして、Pro*C/C++プログラム内でサービス・コンテキスト・ハンドル(OCISvcCtx *型)を宣言します。

OCISvcCtx *svc;

4. Pro*C/C++プログラム内でエラー・ハンドル(OCIError *型)を宣言します。

OCIError *err;

5. 埋込みSQL文CONNECTを使用してOracleに接続します。接続にはOCIを使用しないでください。

EXEC SQL CONNECT ...

6. SQLEnvGet関数を使用して、必要なランタイム・コンテキストに対応付けられているOCI環境ハンドルを取得します。

シングル・スレッド・アプリケーションの場合は、次のようにします。

retcode = SQLEnvGet(SQL_SINGLE_RCTX, &oeh);

共有サーバー・アプリケーションの場合は、次のようにします。

sql_context ctx1;
...
EXEC SQL CONTEXT ALLOCATE :ctx1;
EXEC SQL CONTEXT USE :ctx1;
...
EXEC SQL CONNECT :uid IDENTIFIED BY :pwd;
...
retcode = SQLEnvGet(ctx1, &oeh);

7. 取得した環境ハンドルを使用してOCIエラー・ハンドルを割り当てます。

retcode = OCIHandleAlloc((dvoid *)oeh, (dvoid **)&err, 
                    (ub4)OCI_HTYPE_ERROR, (ub4)0, (dvoid **)0);

8. 使用するOCIコールに必要な場合は、オプションとして、SQLSvcCtxGetコールを使用してOCIServiceContextハンドルを取得します。

シングル・スレッド・アプリケーションの場合は、次のようにします。

retcode = SQLSvcCtxGet(SQL_SINGLE_RCTX, (text *)dbname, (ub4)dbnlen, &svc);

共有サーバー環境アプリケーションの場合は、次のようにします。

sql_context ctx1;
...
EXEC SQL ALLOCATE :ctx1;
EXEC SQL CONTEXT USE :ctx1;
...
EXEC SQL CONNECT :uid IDENTIFIED BY :pwd AT :dbname
     USING :hst;
...
retcode = SQLSvcCtxGet(ctx1, (text *)dbname, (ub4)strlen(dbname), &svc);

ノート:

Pro*C/C++接続名がAT句で指定されていない場合は、NULLポインタがdbnameとして渡されることがあります。

5.10 OCIリリース7コールの埋込み

ノート:

ログイン・データ領域(LDA)は、サポートされなくなりました。OCIリリース7コールをPro*C/C++プログラムに埋め込む機能は、サポートされていません。

OCIリリース7コールをPro*C/C++プログラムに埋め込むステップは、次のとおりです。

  • OCIログイン・データ領域(LDA)をPro*C/C++プログラム内(MODE=ANSIを指定してプリコンパイルする場合は、宣言部の外)で宣言します。LDAは、OCIヘッダー・ファイルoci.h内で定義された構造体です。詳細は、リリース7対応の『Oracle Call Interface開発者ガイド』を参照してください。

  • OCIのorlon()またはonblon()コールではなく、埋込みSQL文CONNECTを使用してOracleに接続します。

  • SQLLIBランタイム・ライブラリ関数sqllda()をコールしてLDA.SQLLIB関数を設定します。

これにより、Pro*C/C++プリコンパイラとOCIでは、両者が連動していることが認識されます。ただし、Oracleカーソルは共有されません。

Oracleランタイム・ライブラリにより接続が管理され、HDAがメンテナンスされるため、OCIホスト・データ領域(HDA)の宣言を意識する必要はありません。

5.10.1 LDAの設定

OCIコールを発行してLDAを設定します。

sqllda(&lda); 

ldaでは、LDAデータ構造体を指定します。

設定が失敗すると、ldalda_rcフィールドがエラーを示す1012に設定されます。

5.10.2 リモートの複数接続

sqllda()のコールにより、最後に実行されたSQL文で使用する接続用のLDAが設定されます。追加接続に必要な別のLDAを設定するには、各CONNECTの直後に別のLDAを指定してsqllda()をコールする必要があります。次の例では、2つの非デフォルト・データベースに同時に接続します。

#include <ocidfn.h>
Lda_Def lda1;
Lda_Def lda2;

char username[10], password[10], db_string1[20], dbstring2[20];
...
strcpy(username, "scott");
strcpy(password, "tiger");
strcpy(db_string1, "NYNON");
strcpy(db_string2, "CHINON");
/* give each database connection a unique name */
EXEC SQL DECLARE DB_NAME1 DATABASE;
EXEC SQL DECLARE DB_NAME2 DATABASE;
/* connect to first nondefault database */
EXEC SQL CONNECT :username IDENTIFIED BY :password;
    AT DB_NAME1 USING :db_string1;
/* set up first LDA */
sqllda(&lda1);
/* connect to second nondefault database */
EXEC SQL CONNECT :username IDENTIFIED BY :password;
    AT DB_NAME2 USING :db_string2;
/* set up second LDA */
sqllda(&lda2);

DB_NAME1およびDB_NAME2は、C言語の変数ではなくSQL識別子です。識別子DB_NAME1およびDB_NAME2は、2つの非デフォルト・ノードのデフォルトのデータベースの名前を指定するためにのみ使用します。これにより、後のSQL文ではデータベースを名前で参照できます。

5.11 SQLLIBパブリック関数の新しい名前

表5-4は、SQLLIB関数名を示しています。これらのSQLLIB関数は、スレッド・アプリケーションでも非スレッド・アプリケーションでも使用できます。たとえば、従来は、sqlglm()は、この関数の非スレッド・バージョンまたはデフォルト・コンテキスト・バージョンで、sqlglmt()は、スレッド・バージョンまたは非デフォルト・コンテキスト・バージョンで、最初の引数としてコンテキストを指定していました。sqlglm()およびsqlglmt()の名前は、引き続き使用可能です。新しい関数SQLErrorGetText()には、sqlglmt()と同じ引数が必要です。非スレッドまたはデフォルト・コンテキスト・アプリケーションでは、定義した定数SQL_SINGLE_RCTXをコンテキストとして渡してください。

標準SQLLIBパブリック関数はいずれもスレッド・セーフであり、ランタイム・コンテキストを最初の引数として受け入れます。たとえば、SQLErrorGetText()の構文は次のとおりです。

void SQLErrorGetText(dvoid *context,  char   *message_buffer, 
            size_t *buffer_size,
            size_t *message_length);

つまり、古い関数名は既存のアプリケーションでそのまま使用できます。新しく作成するアプリケーションでは、新しい関数名を使用できます。

表5-4は、すべてのSQLLIBパブリック関数とそれに対応する構文を示しています。非スレッドまたはデフォルト・コンテキストの使用方法に関する相互参照も用意されているため、より詳細な説明が必要な場合に参照してください。

表5-4 SQLLIBパブリック関数 - 新しい名前

旧名称 新しい関数プロトタイプ 相互参照

sqlaldt()

struct SQLDA *SQLSQLDAAlloc(dvoid *context,
unsigned int     maximum_variables,
unsigned int     maximum_name_length,
unsigned int     maximum_ind_name_length);

SQLDAの割当ても参照してください。

sqlcdat()

void SQLCDAFromResultSetCursor(dvoid *context,
Cda_Def *cda,
void    *cursor,
sword   *return_value);

OCIでのカーソル変数の使用(リリース7のみ)も参照してください。

sqlclut()

void SQLSQLDAFree(dvoid  *context, 
struct SQLDA             *descriptor_name);

記憶域の割当て解除も参照してください。

sqlcurt()

void SQLCDAToResultSetCursor(dvoid  *context,
void    *cursor, 
Cda_Def *cda, 
sword   *return_value)

OCIでのカーソル変数の使用(リリース7のみ)も参照してください。

sqlglmt()

void SQLErrorGetText(dvoid  *context,
unsigned char *message_buffer, 
size_t  *buffer_size,
size_t  *message_length);

エラー・メッセージの全文の取得についても参照してください。

sqlglst()

void SQLStmtGetText(dvoid   *context, 
char    *statement_buffer, 
size_t  *statement_length, 
size_t  *sqlfc);

SQL文のテキスト取得についても参照してください。

sqlld2t()

void SQLLDAGetName(dvoid   *context, 
Lda_Def *lda,
text    *cname,
int     *cname_length);

OCIコール(リリース7のみ)も参照してください。

sqlldat()

void SQLLDAGetCurrent(dvoid *context, 
Lda_Def   *lda);

リモートの複数接続も参照してください。

sqlnult()

void SQLColumnNullCheck(dvoid *context, 
unsigned short *value_type, 
unsigned short *type_code, 
int            *null_status);

NULL/NOT NULLデータ型の処理も参照してください。

sqlprct()

void SQLNumberPrecV6(dvoid  *context, 
unsigned long   *length, 
int             *precision, 
int             *scale);

精度と位取りの抽出も参照してください。

sqlpr2t()

void SQLNumberPrecV7(dvoid  *context, 
unsigned long   *length, 
int             *precision, 
int             *scale);

精度と位取りの抽出も参照してください。

sqlvcpt()

void SQLVarcharGetLength(dvoid  *context, 
unsigned long   *data_length, 
unsigned long   *total_length);

VARCHAR配列コンポーネントの長さを調べる方法も参照してください。

なし

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

SQLEnvGet()を参照してください。

なし

sword SQLSvcCtxGet(dvoid *context,
text            *dbname,
int             dbnamelen,
OCISvcCtx       **svc);

SQLSvcCtxGet()を参照してください。

なし

void SQLRowidGet(dvoid *context,
OCIRowid        **urid);

SQLRowidGet()を参照してください。

なし

void SQLExtProcError(dvoid *context,
char            *msg,
size_t          msglen);

外部プロシージャで使用する場合は、SQLExtProcError()を参照してください。

ノート:

これらの関数の引数リストで使用される特定のデータ型については、プラットフォーム固有のバージョンのsqlcpr.hヘッダー・ファイルを参照してください。

5.12 X/Openアプリケーションの開発

X/Openアプリケーションは、分散トランザクション処理(DTP)環境で動作します。抽象モデルでは、X/Openアプリケーションはリソース・マネージャ(RM)に各種サービスの提供を要求します。たとえば、データベース・リソース・マネージャは、データベース内のデータにアクセスします。リソース・マネージャは、アプリケーションのすべてのトランザクションを制御するトランザクション・マネージャ(TM)と対話します。

図5-1 仮定されるDTPモデル

図5-1の説明が続きます
「図5-1 仮定されるDTPモデル」の説明

図5-1では、DTPモデルのコンポーネントで、Oracleデータベース内のデータに効率的にアクセスするために対話できる方法を示しています。このDTPモデルでは、リソース・マネージャとトランザクション・マネージャの間にXAインタフェースが指定されています。Oracleでは、XA準拠のライブラリが提供され、このライブラリは、X/Openアプリケーションにリンクさせる必要があります。また、アプリケーション・プログラムとリソース・マネージャ間でネイティブ・インタフェースを指定する必要もあります。

DTPモデルは、トランザクション・マネージャとリソース・マネージャがアプリケーション・プログラムとやりとりする方法を指定します。これは、X/Openガイド『Distributed Transaction Processing Reference Model』と関連出版物に記載されています。これらは、次の宛先に書面で請求すれば入手できます。

XAインタフェースの使用方法は、ご使用のトランザクション処理(TP)モニターのユーザー・ガイドを参照してください。

5.12.1 Oracle固有の問題

プリコンパイラを使用して、X/Open規格に準拠したアプリケーションを開発できます。ただし、次の要件を満たす必要があります。

5.12.1.1 Oracleへの接続

X/Openアプリケーションでは、データベースへの接続の確立およびメンテナンスは行われません。かわりに、Oracleにより提供されるトランザクション・マネージャとXAインタフェースにより、データベースの接続および切断が透過的に処理されます。したがって、通常、X/Open準拠のアプリケーションはCONNECT文を実行しません。

5.12.1.2 トランザクション制御

X/Openアプリケーションでは、グローバル・トランザクションに影響を与えるCOMMIT、ROLLBACK、SAVEPOINTおよびSET TRANSACTIONなどの文を実行しないでください。たとえば、コミットはトランザクション・マネージャで処理されるため、アプリケーションではCOMMIT文を実行しないでください。また、CREATE、ALTERおよびRENAMEなどのSQLデータ定義文では暗黙的なCOMMITが発行されるため、アプリケーションでこれらの文を実行しないでください。

アプリケーションで後続のSQL操作を妨げるエラーが検出された場合は、内部ROLLBACK文を実行できます。ただし、今後リリースされるXAインタフェースでは変更になる可能性があります。

5.12.1.3 OCIコール(リリース7のみ)

OCIコール・リリース7はサポートされなくなりますか。

ノート:

ログイン・データ領域(LDA)は、Oracle9iではサポートされなくなりました。Oracleの次のバージョンでは、Pro*C/C++プログラムにOCIリリース7コールを埋め込む機能は廃止になります。

X/OpenアプリケーションでOCIコールを発行する場合は、ランタイム・ライブラリ・ルーチンsqlld2()を使用する必要があります。このルーチンにより、XAインタフェースを通じて確立された指定の接続のために、LDAが設定されます。sqlld2()コールの詳細は、リリース7対応の『Oracle Call Interfaceプログラマーズ・ガイド』を参照してください

OCOM、OCON、OCOF、ONBLON、ORLON、OLON、OLOGOFなどのOCIコールは、X/Openアプリケーションからは発行できません。

5.12.1.4 リンク

XA機能を利用するには、XAライブラリをX/Openアプリケーション・オブジェクト・モジュールにリンクさせる必要があります。指示については、システム固有のOracleマニュアルを参照してください。