この章では、Pro*C/C++の高度な技術について説明します。この章の項目は、次のとおりです。
この項では、Pro*C/C++プリコンパイラによる文字ホスト変数の処理方法について説明します。ホスト変数のキャラクタ・タイプには、次の4種類があります。
文字配列
文字列へのポインタ
VARCHAR変数
VARCHARへのポインタ
VARCHAR(プリコンパイラで提供されるホスト変数データ構造体)とVARCHAR2(可変長文字列に対応するOracleの内部データ型)を混同しないでください。
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を使用してください。
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', ' ', ' ', ' ' } */
DBMSオプションとCHAR_MAPオプションにより、Pro*C/C++で文字配列および文字列のデータを処理する方法が決定されます。これらのオプションにより、プログラムではANSI固定長文字列との互換性、あるいは可変長文字列を使用するOracleおよびPro*C/C++の旧リリースとの互換性を維持できます。DBMSオプションとCHAR_MAPオプションの詳細は、第10章「プリコンパイラ・オプション」を参照してください。
DBMSオプションは、入力(ホスト変数からOracle表へ)および出力(Oracle表からホスト変数へ)の両方で文字データに影響します。
文字配列およびCHAR_MAPオプション
文字配列のマッピングは、DBMSオプションとは関連しないCHAR_MAPオプションでも設定できます。DBMS=V7またはDBMS=V8の場合は、どちらもCHAR_MAP=CHARZが使用されます。これは、CHAR_MAP=VARCHAR2、STRINGまたはCHARFを指定することで上書きできます。
文字配列 入力では、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オプションの設定が及ぼす影響の可能な組合せをすべて示しています。
次のデータベースについて考えてみます。
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'を表します。
文字配列 出力では、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'以外の値で割当て済バッファを満たし、最後のバイトにヌル文字を入れて終了してください。
次のコード例は、前項で定義した列と表を使用して、文字ポインタ・ホスト変数に宣言および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 */
次の例は、VARCHARホスト変数の宣言方法を示しています。
VARCHAR emp_name1[10]; /* VARCHAR variable */ VARCHAR *emp_name2; /* pointer to VARCHAR */
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);
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);
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 */ ...
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はサポートされません。
プリコンパイル時には、各ホスト変数にデフォルトの外部データ型が割り当てられます。たとえば、プリコンパイラでは、short int型およびint型のホスト変数にINTEGER外部データ型が割り当てられます。
実行時には、SQL文で使用される各ホスト変数のデータ型コードがOracleに渡されます。Oracleでは、そのコードを使用して内部データ型と外部データ型間の変換が行われます。
選択された列(または疑似列)の値を出力ホスト変数に割り当てる前に、Oracleではソース列の内部データ型をホスト変数のデータ型に変換する必要があります。同様に、入力ホスト変数の値を列に割り当てる、あるいは列と比較する前に、Oracleではホスト変数の外部データ型をターゲット列の内部データ型に変換する必要があります。
内部データ型と外部データ型間の変換は、通常のデータ変換規則に従っています。たとえば、CHAR値「1234」をC言語のshort値に変換できます。CHAR値「65543」(大きすぎる値)または「10F」(10進数でない値)は、C言語のshort値に変換できません。同様に、アルファベット文字が含まれるchar[n]
値はNUMBER値に変換できません。
データ型の同値化により、Oracleが入力データを解釈する方法およびOracleが出力データをフォーマットする方法を制御できます。データ型の同値化を使用すると、プリコンパイラにより割り当てられるデフォルトの外部データ型を上書きできます。サポートされているC言語のホスト変数データ型は、変数ごとにOracleの外部データ型にマップ(つまり同値化)できます。また、ユーザー定義のデータ型をOracleの外部データ型にマップすることもできます。
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 |
- |
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など)でも使用できます。
また、ユーザー定義のデータ型を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コンポーネント(および必要なパディング)を考慮します。
ユーザー定義型はポインタとして宣言できます。明示的にスカラー型または構造体型へのポインタとして宣言することも、暗黙的に配列として宣言することもできます。また、この型を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バイトで十分です)。
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オプションの設定に関係なく、常に固定長のキャラクタ・タイプになるように同値化されます。
EXEC SQL VAR ...文またはEXEC SQL TYPE ...文は、プログラム内の任意の位置に記述できます。これらの文は、その影響を受ける変数のデータ型を、TYPE文またはVAR文が記述された位置から変数のスコープの終わりまでの範囲内で変更する実行文として扱われます。MODE=ANSIを指定してプリコンパイルする場合は、宣言部を使用する必要があります。この場合、TYPE文またはVAR文は宣言部中に記述してください。
この項のデモ・プログラムは、Pro*C/C++プログラムでデータ型の同値化を使用する方法を示しています。このプログラムは、demo
ディレクトリ内のsample4.pc
として使用でき、LONG VARRAW外部データ型を使用してデータ型の同値化方法を示しています。異なるシステム間で移植できる実用的な例を示すために、このプログラムではバイナリ・ファイルをデータベースに挿入し、そのファイルをデータベースから取り出します。
このプログラムでは、LOB埋込みSQL文を使用しています。第16章「LOB」も参照してください。
このプログラムの目的については、導入部分のコメントを参照してください。
/*************************************************************** 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: "); }
Pro*C/C++では、Cプリプロセッサのほとんどのディレクティブがサポートされます。Pro*C/C++プリプロセッサを使用すると、次のような作業を実行できます。
#defineディレクティブを使用した定数およびマクロの定義、VARCHARなどのPro*C/C++のデータ型宣言をパラメータ化する場合の定義済エンティティの使用。
#includeディレクティブを使用した、プリコンパイラに必要なsqlca.h
などのファイルの読取り。
個別のファイルにある定数およびマクロの定義、プリコンパイラでの#includeディレクティブを使用したこのファイルの読取り。
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コンパイラのプリプロセッサの役割です。
Pro*C/C++でサポートされるプリプロセッサ・ディレクティブは、次のとおりです。
#define。プリコンパイラとCまたはC++コンパイラで使用されるマクロを作成します。
#include。プリコンパイラで使用される他のソース・ファイルを読み込みます。
#if。定数式が0に評価される場合にのみ、ソース・テキストをプリコンパイルおよびコンパイルします。
#ifdef。定義済定数の有無に応じてソース・テキストを条件付きでプリコンパイルおよびコンパイルします。
#ifndef。ソース・テキストを条件付きで除外します。
#endif。#if、#ifdefまたは#ifndefコマンドを終了します。
#else。#if、#ifdefまたは#ifndefの条件が満たされない場合に、プリコンパイルおよびコンパイルされるソース・テキストの代替部を選択します。
#elif。定数またはマクロ引数の値に応じて、プリコンパイルおよびコンパイルされるソース・テキストの代替部を選択します。
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などのデータ型を使用した変数の宣言では使用できません。
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を設定する必要があります。
各システムのPro*C/C++プリコンパイラでは、sqlca.h
、oraca.h
およびsqlda.h
など、プリプロセッサで読み込まれるヘッダー・ファイルが標準的な場所にあるものとみなされます。たとえば、ほとんどのUNIXシステムでの標準的な場所は、$ORACLE_HOME/precomp/public
です。使用しているシステムのデフォルトの場所については、システム固有のOracleマニュアルを参照してください。インクルードする必要のあるヘッダー・ファイルがデフォルトの場所にない場合は、コマンドラインで、EXEC ORACLEオプションとして、INCLUDE=オプションを指定する必要があります。
stdio.h
やiostream.h
などのシステム・ヘッダー・ファイルについて、Pro*C/C++にハードコードされているものと異なる格納場所を指定するには、SYS_INCLUDEプリコンパイラ・オプションを使用します。
#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
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);
プリプロセッサでは、ディレクティブ#および##が無視され、プリコンパイラで認識する必要のあるトークンが作成されます。これらのコマンドは(コンパイラでサポートされている場合は)、純粋なCコードに使用できます。プリコンパイラでは、これらのコードは処理されません。次の例の場合、プリプロセッサ・コマンド##の使用は無効です。
#define MAKE_COL_NAME(A) col ## A ... EXEC SQL SELECT MAKE_COL_NAME(1), MAKE_COL_NAME(2) INTO :x, :y FROM table1;
プリコンパイラでは##は無視されるため、この例は正しくありません。
Pro*C/C++プリプロセッサで#includeディレクティブが処理される方法については前項で説明しましたが、この方法のために、#includeディレクティブを使用して埋込みSQL文を含むファイルをインクルードできません。#include
を使用すると、純粋に宣言文とディレクティブのみを含むファイルがインクルードされません。#defineディレクティブや、プリコンパイラに必要な変数と構造体の宣言のみを含むsqlca.h
ファイルなどがその例です。
C/C++プリプロセッサの#includeコマンド、あるいはプリコンパイラのEXEC SQL INCLUDEコマンドを使用すると、sqlca.h
、oraca.h
およびsqlda.h
の各宣言ヘッダー・ファイルをPro*C/C++プログラムにインクルードできます。たとえば、次の文のように、EXEC SQLオプションを指定して、SQLコミュニケーション領域構造体(SQLCA)をプログラムにインクルードできます。
EXEC SQL INCLUDE sqlca;
C/C++プリプロセッサ・ディレクティブを使用してSQLCAをインクルードするには、次のコードを追加します。
#include <sqlca.h>
プリプロセッサの#includeディレクティブを使用する場合は、ファイル拡張子(.h
など)を指定する必要があります。
#includeディレクティブまたはEXEC SQL INCLUDE文を含むファイルをプリコンパイルする場合は、インクルードするすべてのファイルの位置をプリコンパイラに対して指定する必要があります。INCLUDE=オプションは、コマンドライン、システム構成ファイルまたはユーザー構成ファイル内で使用できます。
sqlca.h
、oraca.h
およびsqlda.h
など、標準的なプリプロセッサ・ヘッダー・ファイルのデフォルトの場所は、プリコンパイラ内で事前設定されています。この場所は、システムごとに異なります。使用しているシステムのデフォルトの場所については、システム固有のOracleマニュアルを参照してください。
Pro*C/C++で生成される.c
出力ファイルをコンパイルする場合は、コンパイラおよびオペレーティング・システムに用意されているオプションを使用して、インクルード・ファイルの場所を識別する必要があります。
たとえば、ほとんどのUNIXシステムでは、次のコマンドを使用して、生成されるCソース・ファイルをコンパイルできます。
cc -o progname -I$ORACLE_HOME/sqllib/public ... filename.c ...
VAX/OPENVMSシステムでは、事前にインクルード・ディレクトリ・パスを論理VAXC$INCLUDE内の値に設定します。
プログラム内でEXEC SQL INCLUDE文を使用すると、プリコンパイラによりソース・テキストが出力(.c
)ファイルにインクルードされます。したがって、EXEC SQL INCLUDEを使用してインクルード・ファイルに、宣言文および埋込みSQLの実行文を入れることができます。
#includeを使用してファイルをインクルードする場合、プリコンパイラでは単にファイルが読み込まれ、#defineで定義したマクロが追跡記録されるのみです。
注意: VARCHAR宣言とSQL文は、インクルード(#include )ファイルには使用できません。このため、Pro*C/C++プリプロセッサの#include ディレクティブを使用してインクルード・ファイルでは、SQL文を使用できません。 |
Cコンパイラのコマンドラインでマクロを定義する場合、アプリケーションの要件によっては、そのマクロをプリコンパイラのコマンドラインでも定義する必要があります。たとえば、UNIXコマンドラインで次のようにコンパイルするとします。
cc -DDEBUG ...
この場合は、次のようにDEFINE=オプションを使用してプリコンパイルする必要があります。
proc DEFINE=DEBUG ...
プリコンパイルする必要のあるすべてのインクルード・ファイルの位置は、コマンドラインまたは構成ファイルで指定する必要があります。
たとえば、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マニュアルを参照してください。
プリコンパイル済ヘッダー・ファイルを使用すると、多数の#include
文を含むヘッダー・ファイルをプリコンパイルすることで、時間とリソースを節約できます。この機能を使用する手順は次の2つです。
まず、プリコンパイル済ヘッダー・ファイルを作成します。
このプリコンパイル済のヘッダーが、次回以降のアプリケーションのプリコンパイル時に自動的に使用されます。
この機能は、多数のモジュールで構成される大型アプリケーションに使用してください。
プリコンパイラ・オプションをHEADER=hdr
に設定すると、次のように指定されます。
このオプションを入力できるのは、構成ファイルまたはコマンドラインのみです。HEADERにはデフォルト値はありませんが、入力ヘッダーの拡張子は.h
である必要があります。
ヘッダー・ファイルtop.h
を作成するとします。このファイルは、HEADER=hdr
を指定してプリコンパイルできます。
proc HEADER=hdr INAME=top.h
注意: 拡張子は.hにする必要があります。INAME値には、「/」や「..」などの絶対パス要素や相対パス要素は使用できません。 |
Pro*C/C++により、指定した入力ファイルtop.h
がプリコンパイルされ、同じディレクトリに新しいプリコンパイル済ヘッダー・ファイルtop.hdr
が生成されます。出力ファイルtop.hdr
は、#include
文による検索対象となるディレクトリに移動できます。
注意: 出力ファイル名の指定にはONAMEオプションを使用しないでください。HEADERとともに使用すると無視されます。 |
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 )ファイルがインクルード・ディレクトリの標準検索階層の最初に表示されている場合も同様です。 |
この項の例では、いくつかの異なるケースを示します。
次の2つのケースでは、考えられる2つの冗長なファイル・インクルードを示します。
プリコンパイル済ヘッダー・ファイルは、#includeディレクティブを使用してインクルードされた回数に関係なく、1度のみインスタンス化されます。
前述の例と同様に、HEADERの値をhdrに設定し、最上位のヘッダー・ファイルtop.hをプリコンパイルするとします。次に、そのヘッダー・ファイルに対して、複数の#includeディレクティブをプログラムに記述します。
#include <top.h> #include <top.h> main(){}
top.hの最初の#includeが発生すると、プリコンパイル済ヘッダー・ファイルtop.hdrがインスタンス化されます。同じヘッダー・ファイルの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は冗長になり、無視されます。
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++では、プリコンパイル済ヘッダー・ファイル間のファイルの共有を判断できません。各プリコンパイル済ヘッダー・ファイルには、一意のヘッダー・セットをインクルードします。ヘッダーの共有はできるだけ避けてください。共有すると、プリコンパイルが低速になり、メモリー使用量が増加するため、プリコンパイル済ヘッダー・ファイルを使用する意味がなくなります。
ORACLE_BASE\ORACLE_HOME
\precomp\public
ディレクトリには、Pro*C/C++ヘッダー・ファイルが含まれています。表5-3は、ヘッダー・ファイルのリストと説明です。
表5-3 ヘッダー・ファイル
ヘッダー・ファイル | 説明 |
---|---|
Oracle通信領域(ORACA)が含まれています。これは、ランタイム・エラーの診断と、プログラムでの様々なOracle Database 10gリソースの使用の監視に役立ちます。 |
|
SQLLIB関数が含まれています。これらの関数により、Oracle Call Interface(OCI)環境ハンドルとOCIサービス・コンテキストをPro*C/C++アプリケーションで取得できます。 |
|
OCIとともに使用できる外部関数用のANSIプロトタイプが含まれています。 |
|
SQLコミュニケーション領域(SQLCA)が含まれています。これは、ランタイム・エラーの診断に役立ちます。SQLCAは、すべての実行SQL文の後で更新されます。 |
|
Pro*C/C++で生成される、SQLLIB関数用のプラットフォーム固有のANSIプロトタイプが含まれています。デフォルトでは、Pro*C/C++は、SQLプログラミング・コールの完全な関数プロトタイプをサポートしていません。この機能が必要な場合、アプリケーション・ソース・ファイル内のすべてのEXEC SQL文の前に |
|
Oracle通信領域(ORACA)が含まれています。これは、ランタイム・エラーの診断と、プログラムでの様々なOracle Database 10gリソースの使用の監視に役立ちます。 |
|
SQLLIB関数が含まれています。これらの関数により、Oracle Call Interface(OCI)環境ハンドルとOCIサービス・コンテキストをPro*C/C++アプリケーションで取得できます。 |
|
OCIとともに使用できる外部関数用のANSIプロトタイプが含まれています。 |
アプリケーションのプリコンパイル時には、次のプリコンパイラ・オプションを使用できます。
プリコンパイル済ヘッダーを使用してプリコンパイルするときには、DEFINEとINCLUDEの値を、プリコンパイル済ヘッダー・ファイルの作成時と同じ値にする必要があります。DEFINEまたはINCLUDEの値を変更した場合は、プリコンパイル済ヘッダー・ファイルを再作成する必要があります。
開発環境を変更した場合も、プリコンパイル済ヘッダー・ファイルを再作成する必要があります。
シングル・ユーザーの場合を考えてみます。DEFINEまたはINCLUDEオプションの値を変更した場合、プリコンパイル済ヘッダー・ファイルの内容は、その後に実行されるPro*C/C++プリコンパイルでは正常に使用できなくなります。
DEFINEおよびINCLUDE、DEFINEまたはINCLUDEオプションの値を変更しているため、プリコンパイル済ヘッダー・ファイルの内容は、#includeディレクティブの対応する.hファイルが正常に処理された場合の標準的な結果と一致しなくなります。
つまり、DEFINEおよびINCLUDE、DEFINEまたはINCLUDEオプションの値を変更した場合は、プリコンパイル済ヘッダー・ファイルを再作成し、それを使用するPro*C/C++プログラムを再度プリコンパイルする必要があります。
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++プログラムが正常にプリコンパイルされるとはかぎりません。
したがって、プリコンパイル済ヘッダー・ファイルを、異なるユーザー間および異なる開発環境間で共有または交換する場合には、注意が必要です。
Pro*C/C++では、hpp
やh++
などの拡張子が付いた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に設定してプログラムをプリコンパイルすると、正常に動作します。ただし、ホスト変数は明示的な宣言部中で宣言する必要があります。
プリコンパイル済のヘッダーから生成された出力ファイルの形式は、リリースごとに異なる可能性があります。Pro*C/C++では、プリコンパイル済ヘッダー・ファイルの出力の生成に使用されたプリコンパイラのバージョンを判断できません。
このため、プリコンパイル済ヘッダー・ファイルを使用したプリコンパイル時に、エラーまたは他の予期しない動作を回避するために、Pro*C/C++のリリースのアップグレード時には、対応するヘッダー・ファイルを再度プリコンパイルしてファイルを再生成することをお薦めします。
ヘッダー・ファイルをプリコンパイルして生成された出力ファイルには、移植性がありません。つまり、ヘッダー・ファイルのプリコンパイルにより生成された出力ファイルは、プラットフォーム間で転送できず、別のヘッダー・ファイルまたはPro*C/C++プログラムのプリコンパイル時に使用できません。
コードの条件文は、環境と実行する処理を定義する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文の終わりには、セミコロンを付ける必要があります。
記号を定義するには、2通りの方法があります。1つの方法では、ホスト・プログラムに次の文を含めます。
EXEC ORACLE DEFINE symbol;
もう1つの方法では、次の構文を使用してコマンドラインで記号を定義します。
... INAME=filename ... DEFINE=symbol
symbolには、大/小文字区別がありません。
注意: #define プリプロセッサ・ディレクティブは、EXEC ORACLE DEFINEコマンドとは異なります。 |
Pro*C/C++プリコンパイラをシステムにインストールするときに、ポート固有の記号がいくつか事前定義されます。
次の例では、記号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の間に置いて、その記号を定義しないことでコメント・アウトできます。
以前の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文で使用するバインド変数の配列のサイズを宣言する場合に使用されます。
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'; }
特定の静的な型を持つ変数は、staticで定義し、初期化する必要があります。Pro*C/C++で数値定数を宣言する場合は、必ず次の規則に従ってください。
有効な初期化機能が指定された定数宣言で解決できない識別子を使用すると、エラーとみなされます。
次の例は、無効な指定方法とその指定が許可されない理由を示しています。
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 */
OCI環境ハンドルは、sql_context型のPro*C/C++ランタイム・コンテキストに関連付けられます。つまり、アプリケーション実行時にSQLLIBに保持されるPro*C/C++ランタイム・コンテキストは、複数のOCI環境ハンドルに関連付けることはできません。Pro*C/C++ランタイム・コンテキストごとに複数のデータベース接続を行うことができ、各ランタイム・コンテキストはそのOCI環境ハンドルに関連付けられます。
注意: プリコンパイラ・アプリケーションはOCIハンドルを抽出し、OCI関数を直接呼び出すことができます。ただし、プリコンパイラは戻される可能性のある「まだ実行中」エラーを処理できないので、非ブロックモードはサポートされていません。 |
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++プログラムが終了するまで割り当てられたままになっています。
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
を渡します。
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など)は、この関数をコールしても影響を受けません。 |
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で終了する文字列であることを示します。 |
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として渡されることがあります。 |
注意: ログイン・データ領域(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)の宣言を意識する必要はありません。
OCIコールを発行してLDAを設定します。
sqllda(&lda);
ldaでは、LDAのデータ構造を識別します。
設定が失敗すると、ldaのlda_rcフィールドがエラーを示す1012に設定されます。
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-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パブリック関数 - 新しい名前
旧名称 | 新しい関数プロトタイプ | 相互参照 |
---|---|---|
|
struct SQLDA *SQLSQLDAAlloc(dvoid *context, unsigned int maximum_variables, unsigned int maximum_name_length, unsigned int maximum_ind_name_length); |
「SQLDAの割当て」も参照してください。 |
|
void SQLCDAFromResultSetCursor(dvoid *context, Cda_Def *cda, void *cursor, sword *return_value); |
「OCIでのカーソル変数の使用(リリース7のみ)」も参照してください。 |
|
void SQLSQLDAFree(dvoid *context, struct SQLDA *descriptor_name); |
「記憶域の割当て解除」も参照してください。 |
|
void SQLCDAToResultSetCursor(dvoid *context, void *cursor, Cda_Def *cda, sword *return_value) |
「OCIでのカーソル変数の使用(リリース7のみ)」も参照してください。 |
|
void SQLErrorGetText(dvoid *context, unsigned char *message_buffer, size_t *buffer_size, size_t *message_length); |
「エラー・メッセージの全文の取得」も参照してください。 |
|
void SQLStmtGetText(dvoid *context, char *statement_buffer, size_t *statement_length, size_t *sqlfc); |
「SQL文のテキスト取得」も参照してください。 |
|
void SQLLDAGetName(dvoid *context, Lda_Def *lda, text *cname, int *cname_length); |
「OCIコール(リリース7のみ)」も参照してください。 |
|
void SQLLDAGetCurrent(dvoid *context, Lda_Def *lda); |
「リモートの複数接続」も参照してください。 |
|
void SQLColumnNullCheck(dvoid *context, unsigned short *value_type, unsigned short *type_code, int *null_status); |
「NULL/NOT NULLデータ型の処理」も参照してください。 |
|
void SQLNumberPrecV6(dvoid *context, unsigned long *length, int *precision, int *scale); |
「精度と位取りの抽出」も参照してください。 |
|
void SQLNumberPrecV7(dvoid *context, unsigned long *length, int *precision, int *scale); |
「精度と位取りの抽出」も参照してください。 |
|
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 ヘッダー・ファイルを参照してください。 |
X/Openアプリケーションは、分散トランザクション処理(DTP)環境で動作します。抽象モデルでは、X/Openアプリケーションはリソース・マネージャ(RM)に各種サービスの提供をコールします。たとえば、データベース・リソース・マネージャは、データベース内のデータにアクセスします。リソース・マネージャは、アプリケーションのためにすべてのトランザクションを制御するトランザクション・マネージャ(TM)と対話します。
図5-1は、DTPモデルのコンポーネントがOracleデータベースのデータに効率的にアクセスするようにやりとりする方法の1つを示しています。このDTPモデルでは、リソース・マネージャとトランザクション・マネージャの間にXAインタフェースが指定されています。OracleはXA準拠のライブラリを提供します。このライブラリは、X/Openアプリケーションにリンクする必要があります。また、アプリケーション・プログラムとリソース・マネージャ間でネイティブ・インタフェースを指定する必要もあります。
DTPモデルは、トランザクション・マネージャとリソース・マネージャがアプリケーション・プログラムとやりとりする方法を指定します。これは、X/Openガイド『Distributed Transaction Processing Reference Model』と関連出版物に記載されています。これらは、次の宛先に書面で請求すれば入手できます。
http://www.opennc.org/
XAインタフェースの使用方法は、ご使用のトランザクション・プロセッシング(TP)モニターのユーザーズ・ガイドを参照してください。
プリコンパイラを使用して、X/Open規格に準拠したアプリケーションを開発できます。ただし、次の要件を満たす必要があります。
X/Openアプリケーションでは、データベースへの接続は確立およびメンテナンスされません。かわりに、Oracleにより提供されるトランザクション・マネージャとXAインタフェースにより、データベースへの接続と切断が透過的に処理されます。したがって、通常、X/Open準拠のアプリケーションはCONNECT文を実行しません。
X/Openアプリケーションでは、グローバル・トランザクションに影響するCOMMIT、ROLLBACK、SAVEPOINTおよびSET TRANSACTIONなどの文を実行しないでください。たとえば、トランザクション・マネージャでコミットが処理されるため、アプリケーションではCOMMIT文を実行しないでください。また、アプリケーションでCREATE、ALTERおよびRENAMEなどのSQLデータ定義文を実行しないでください。これらの文では暗黙的なCOMMITが発行されるためです。
アプリケーションで後続のSQL操作を妨げるエラーが検出された場合は、内部ROLLBACK文を実行できます。ただし、今後リリースされるXAインタフェースでは変更になる可能性があります。
注意: ログイン・データ領域(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アプリケーションからは発行できません。
OCIリリース8コールをPro*C/C++で使用する方法については、「OCIリリース8へのインタフェース」を参照してください。