VARCHAR疑似型を使用すると、可変長文字列を宣言できます。プログラムで扱う文字列がVARCHAR2列またはLONG列からの出力、あるいはその列への入力である場合、標準のC言語文字列のかわりにVARCHARホスト変数を使用する方が便利なこともあります。データ型名VARCHARは、すべて大文字でも、すべて小文字でもかまいませんが、大文字と小文字を混在させることはできません。このマニュアルでは、VARCHARがC言語固有のデータ型とは異なることを強調するために、大文字を使用しています。
VARCHARは、C言語の拡張型または宣言済の構造体と考えてください。たとえば、次のVARCHAR宣言があると仮定します。
VARCHAR username[20];
プリコンパイラは上の宣言を、配列メンバーおよび長さメンバーを持つ次の構造体に展開します。
struct
{
unsigned short len;
unsigned char arr[20];
} username;
VARCHAR変数の利点は、SELECTまたはFETCHの後にVARCHAR構造体の長さメンバーを明示的に参照できることです。Oracleでは、選択された文字列の長さが長さメンバーに置かれます。このメンバーを、ヌル終端文字('\0')の追加などに使用できます。
username.arr[username.len] = '\0';
また、次のように、strncpy文またはprintf文でも長さを指定できます。
printf("Username is %.*s\n", username.len, username.arr);
VARCHAR変数の最大長は、その宣言内で指定します。長さの範囲は、1から65533にする必要があります。たとえば、次の宣言は、長さが指定されていないため無効です。
VARCHAR null_string[]; /* invalid */
長さの指定では、配列メンバーに格納される値の現行の長さが保持されます。
次のように、1行に複数のVARCHARを宣言できます。
VARCHAR emp_name[ENAME_LEN], dept_loc[DEPT_NAME_LEN];
VARCHARの長さ指定子には、#defineによる定義済マクロか、プリコンパイル時に整数で解決できる任意の複合式を使用できます。
また、VARCHARデータ型へのポインタも宣言できます。
関連項目:
注意:
次のようなtypedef文は使用しないでください。
typedef VARCHAR buf[64];
このような文を使用すると、Cコンパイル・エラーが発生します。
SQL文では、次のように、接頭辞としてコロンを付けた構造体名を使用してVARCHAR変数を参照します。
...
int part_number;
VARCHAR part_desc[40];
...
main()
{
...
EXEC SQL SELECT pdesc INTO :part_desc
FROM parts
WHERE pnum = :part_number;
...
問合せが実行された後、データベースから取り出され、part_desc.arrに格納された文字列の実際の長さがpart_desc.lenに保持されます。
C言語の文では、次のように、コンポーネント名を使用してVARCHAR変数を参照します。
printf("\n\nEnter part description: ");
gets(part_desc.arr);
/* You must set the length of the string
before using the VARCHAR in an INSERT or UPDATE */
part_desc.len = strlen(part_desc.arr);
Oracleでは、VARCHAR出力ホスト変数の長さコンポーネントが自動的に設定されます。SELECTまたはFETCHでVARCHAR変数にNULLを入れた場合、サーバーでは長さメンバーも配列メンバーも変更されません。
注意:
NULLをVARCHARホスト変数に選択した場合に、対応する標識変数がないと、実行時にORA-01405エラーが発生します。これを回避するには、すべてのホスト変数に対応して標識変数を記述します。(一時修正としては、UNSAFE_NULL=YESプリコンパイラ・オプションを使用します。DBMSも参照してください。)
VARCHAR変数の長さを0に設定してからUPDATE文またはINSERT文を実行すると、列値はNULLに設定されます。列にNOT NULL制約がある場合は、エラーが戻されます。
VARCHARは構造体であり、ほとんどのCコンパイラでは、構造体を値で関数に渡すこと、および関数からコピーで構造体を戻すことが可能です。ただし、Pro*C/C++では、VARCHARを参照により関数に渡す必要があります。次の例は、VARCHAR変数を関数に渡す正しい方法を示しています。
VARCHAR emp_name[20];
...
emp_name.len = 20;
SELECT ename INTO :emp_name FROM emp
WHERE empno = 7499;
...
print_employee_name(&emp_name); /* pass by pointer */
...
print_employee_name(name)
VARCHAR *name;
{
...
printf("name is %.*s\n", name->len, name->arr);
...
}
プリコンパイラでVARCHAR宣言が処理されると、生成される構造体内の配列要素の実際の長さが、宣言された長さよりも長くなることがあります。たとえば、Sun Solarisシステムで次のようなPro*C/C++宣言があるとします。
VARCHAR my_varchar[12];
この宣言は、プリコンパイラにより次のように展開されます。
struct my_varchar
{
unsigned short len;
unsigned char arr[12];
};
しかし、このシステムのプリコンパイラまたはCコンパイラは、配列コンポーネントの長さをパディングして14バイトにします。このアラインメント要件は、構造体全体の長さをパディングして16バイトにします。パディングされた配列が14バイト、長さが2バイトです。
SQLVarcharGetLength()(非スレッドsqlvcp()と置換)関数(SQLLIBランタイム・ライブラリの一部)により、配列メンバーの実際の(パディングされている場合もあります)長さが戻されます。
SQLVarcharGetLength()関数には、VARCHARホスト変数またはVARCHARポインタ・ホスト変数のデータの長さを渡します。SQLVarcharGetLength()からは、VARCHARの配列コンポーネント合計の長さが戻されます。合計の長さには、使用しているCコンパイラにより追加された可能性のあるパディングが含まれます。
SQLVarcharGetLength()の構文は、次のとおりです。
SQLVarcharGetLength (dvoid *context, unsigned long *datlen, unsigned long *totlen);
シングル・スレッド・アプリケーションの場合は、sqlvcp()を使用してください。VARCHARの長さをdatlenパラメータに指定してから、sqlvcp()をコールします。関数の戻り時には、totlenパラメータに配列要素の合計の長さが設定されています。どちらのパラメータもunsigned long整数へのポインタであるため、参照で渡す必要があります。
関連項目:
この関数および他のすべてのSQLLIBパブリック関数の詳細は、SQLLIBパブリック関数の新しい名前を参照してください。
次のサンプル・プログラムは、Pro*C/C++アプリケーションでの関数の使用方法を示しています。また、このサンプル・プログラムではsqlgls()関数も使用しています。このサンプルでは、まずVARCHARポインタを宣言してから、sqlvcp()関数を使用してVARCHARバッファに必要なサイズを決定します。プログラムでは、EMP表から従業員名がFETCHされて出力されます。最後に、sqlgls()関数を使用してSQL文とその関数コード、長さの属性が出力されます。このプログラムは、demoディレクトリのsqlvcp.pcとしてオンラインで使用できます。
関連項目:
/*
* The sqlvcp.pc program demonstrates how you can use the
* sqlvcp() function to determine the actual size of a
* VARCHAR struct. The size is then used as an offset to
* increment a pointer that steps through an array of
* VARCHARs.
*
* This program also demonstrates the use of the sqlgls()
* function, to get the text of the last SQL statement executed.
* sqlgls() is described in the "Error Handling" chapter of
* The Programmer's Guide to the Oracle Pro*C/C++ Precompiler.
*/
#include <stdio.h>
#include <sqlca.h>
#include <sqlcpr.h>
/* Fake a VARCHAR pointer type. */
struct my_vc_ptr
{
unsigned short len;
unsigned char arr[32767];
};
/* Define a type for the VARCHAR pointer */
typedef struct my_vc_ptr my_vc_ptr;
my_vc_ptr *vc_ptr;
EXEC SQL BEGIN DECLARE SECTION;
VARCHAR *names;
int limit; /* for use in FETCH FOR clause */
char *username = "scott/tiger";
EXEC SQL END DECLARE SECTION;
void sql_error();
extern void sqlvcp(), sqlgls();
main()
{
unsigned int vcplen, function_code, padlen, buflen;
int i;
char stmt_buf[120];
EXEC SQL WHENEVER SQLERROR DO sql_error();
EXEC SQL CONNECT :username;
printf("\nConnected.\n");
/* Find number of rows in table. */
EXEC SQL SELECT COUNT(*) INTO :limit FROM emp;
/* Declare a cursor for the FETCH statement. */
EXEC SQL DECLARE emp_name_cursor CURSOR FOR
SELECT ename FROM emp;
EXEC SQL FOR :limit OPEN emp_name_cursor;
/* Set the desired DATA length for the VARCHAR. */
vcplen = 10;
/* Use SQLVCP to help find the length to malloc. */
sqlvcp(&vcplen, &padlen);
printf("Actual array length of VARCHAR is %ld\n", padlen);
/* Allocate the names buffer for names.
Set the limit variable for the FOR clause. */
names = (VARCHAR *) malloc((sizeof (short) +
(int) padlen) * limit);
if (names == 0)
{
printf("Memory allocation error.\n");
exit(1);
}
/* Set the maximum lengths before the FETCH.
* Note the "trick" to get an effective VARCHAR *.
*/
for (vc_ptr = (my_vc_ptr *) names, i = 0; i < limit; i++)
{
vc_ptr->len = (short) padlen;
vc_ptr = (my_vc_ptr *)((char *) vc_ptr +
padlen + sizeof (short));
}
/* Execute the FETCH. */
EXEC SQL FOR :limit FETCH emp_name_cursor INTO :names;
/* Print the results. */
printf("Employee names--\n");
for (vc_ptr = (my_vc_ptr *) names, i = 0; i < limit; i++)
{
printf
("%.*s\t(%d)\n", vc_ptr->len, vc_ptr->arr, vc_ptr->len);
vc_ptr = (my_vc_ptr *)((char *) vc_ptr +
padlen + sizeof (short));
}
/* Get statistics about the most recent
* SQL statement using SQLGLS. Note that
* the most recent statement in this example
* is not a FETCH, but rather "SELECT ENAME FROM EMP"
* (the cursor).
*/
buflen = (long) sizeof (stmt_buf);
/* The returned value should be 1, indicating no error. */
sqlgls(stmt_buf, &buflen, &function_code);
if (buflen != 0)
{
/* Print out the SQL statement. */
printf("The SQL statement was--\n%.*s\n", buflen, stmt_buf);
/* Print the returned length. */
printf("The statement length is %ld\n", buflen);
/* Print the attributes. */
printf("The function code is %ld\n", function_code);
EXEC SQL COMMIT RELEASE;
exit(0);
}
else
{
printf("The SQLGLS function returned an error.\n");
EXEC SQL ROLLBACK RELEASE;
exit(1);
}
}
void
sql_error()
{
char err_msg[512];
int buf_len, msg_len;
EXEC SQL WHENEVER SQLERROR CONTINUE;
buf_len = sizeof (err_msg);
sqlglm(err_msg, &buf_len, &msg_len);
printf("%.*s\n", msg_len, err_msg);
EXEC SQL ROLLBACK RELEASE;
exit(1);
}