この項では、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オプションの詳細は、プリコンパイラ・オプションを参照してください。
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'以外の値で割当て済バッファを満たし、最後のバイトにヌル文字を入れて終了してください。
注意:
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 */
次の例は、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はサポートされません。