C言語の構造体を使用すると、ホスト変数を組み込むことができます。SELECT文またはFETCH文のINTO句と、INSERT文のVALUESリストに、ホスト変数を含んでいる構造体を参照します。ホスト構造体のすべてのコンポーネントは、表4-4の定義のように正当なPro*C/C++ホスト変数である必要があります。
構造体がホスト変数として使用されると、構造体の名前のみがSQL文で使用されます。ただし、構造体の各メンバーは、Oracleにデータを送信したり、問合せでOracleからデータを受信したりします。次の例は、EMP表に従業員を1人追加するときに使用されるホスト構造体を示しています。
typedef struct
{
char emp_name[11]; /* one greater than column length */
int emp_number;
int dept_number;
float salary;
} emp_record;
...
/* define a new structure of type "emp_record" */
emp_record new_employee;
strcpy(new_employee.emp_name, "CHEN");
new_employee.emp_number = 9876;
new_employee.dept_number = 20;
new_employee.salary = 4250.00;
EXEC SQL INSERT INTO emp (ename, empno, deptno, sal)
VALUES (:new_employee);
メンバーが構造体で宣言される順序は、SQL文中の対応する列の順序と一致する必要があります。また、INSERT文で列のリストが省略されている場合は、データベース表の列の順序と一致する必要があります。
たとえば、ホスト構造体を次のように使用すると無効になり、ランタイム・エラーが発生します。
struct
{
int empno;
float salary; /* struct components in wrong order */
char emp_name[10];
} emp_record;
...
SELECT empno, ename, sal
INTO :emp_record FROM emp;
前述の例が無効となるのは、構造体のコンポーネントが選択リスト内の対応する列とは異なる順序で宣言されているためです。SELECT文の正しい書式は、次のとおりです。
SELECT empno, sal, ename /* reverse order of sal and ename */
INTO :emp_record FROM emp;
配列とは、1つの変数名に関連付けられた要素と呼ばれる関連データ項目の集合です。ホスト変数として宣言されたとき、配列はホスト配列と呼ばれます。同様に、配列として宣言されたインジケータ変数はインジケータ配列と呼ばれます。インジケータ配列は、任意のホスト配列に対応付けることができます。
ホスト配列により、データ項目のコレクション全体を単一のSQL文で操作できるため、パフォーマンスを向上させることができます。いくつかの例外を除けば、スカラーのホスト変数が許可される場所であれば、任意の位置でホスト配列を使用できます。また、インジケータ配列は任意のホスト配列に対応付けることができます。
ホスト配列の詳細は、ホスト配列も参照してください。
ホスト配列は、ホスト構造体のコンポーネントとして使用できます。次の例では、配列を含む構造体を使用して、EMP表に3つの新規項目をINSERTします。
struct
{
char emp_name[3][10];
int emp_number[3];
int dept_number[3];
} emp_rec;
...
strcpy(emp_rec.emp_name[0], "ANQUETIL");
strcpy(emp_rec.emp_name[1], "MERCKX");
strcpy(emp_rec.emp_name[2], "HINAULT");
emp_rec.emp_number[0] = 1964; emp_rec.dept_number[0] = 5;
emp_rec.emp_number[1] = 1974; emp_rec.dept_number[1] = 5;
emp_rec.emp_number[2] = 1985; emp_rec.dept_number[2] = 5;
EXEC SQL INSERT INTO emp (ename, empno, deptno)
VALUES (:emp_rec);
...
標識変数を使用する必要があっても、ホスト変数がホスト構造体に含まれている場合は、ホスト構造体内のホスト変数ごとに標識変数が含まれるように、2番目の構造体を設定します。
たとえば、ホスト構造体student_recordを次のように宣言するとします。
struct
{
char s_name[32];
int s_id;
char grad_date[9];
} student_record;
このホスト構造体を次のような問合せで使用するとします。
EXEC SQL SELECT student_name, student_idno, graduation_date
INTO :student_record
FROM college_enrollment
WHERE student_idno = 7200;
また、卒業日がNULLでもよいかどうかを知る必要があります。さらに、個別のホスト・インジケータ構造体を宣言する必要があります。これは、次のように宣言します。
struct
{
short s_name_ind; /* indicator variables must be shorts */
short s_id_ind;
short grad_date_ind;
} student_record_ind;
SQL文中のインジケータ構造体は、ホスト・標識変数を参照する場合と同じ方法で参照してください。
EXEC SQL SELECT student_name, student_idno, graduation_date
INTO :student_record INDICATOR :student_record_ind
FROM college_enrollment
WHERE student_idno = 7200;
問合せが完了すると、選択された各コンポーネントのNULL/NOT NULLステータスはホスト・インジケータ構造体で使用可能になります。
この項のデモ・プログラムは、明示カーソルを使用し、データをホスト構造体に格納する問合せを示しています。このプログラムは、demoディレクトリのファイルsample2.pc内で使用できます。
/*
* sample2.pc
*
* This program connects to ORACLE, declares and opens a cursor,
* fetches the names, salaries, and commissions of all
* salespeople, displays the results, then closes the cursor.
*/
#include <stdio.h>
#include <sqlca.h>
#define UNAME_LEN 20
#define PWD_LEN 40
/*
* Use the precompiler typedef'ing capability to create
* null-terminated strings for the authentication host
* variables. (This isn't really necessary--plain char *'s
* does work as well. This is just for illustration.)
*/
typedef char asciiz[PWD_LEN];
EXEC SQL TYPE asciiz IS STRING(PWD_LEN) REFERENCE;
asciiz username;
asciiz password;
struct emp_info
{
asciiz emp_name;
float salary;
float commission;
};
/* Declare function to handle unrecoverable errors. */
void sql_error();
main()
{
struct emp_info *emp_rec_ptr;
/* Allocate memory for emp_info struct. */
if ((emp_rec_ptr =
(struct emp_info *) malloc(sizeof(struct emp_info))) == 0)
{
fprintf(stderr, "Memory allocation error.\n");
exit(1);
}
/* Connect to ORACLE. */
strcpy(username, "SCOTT");
strcpy(password, "TIGER");
EXEC SQL WHENEVER SQLERROR DO sql_error("ORACLE error--");
EXEC SQL CONNECT :username IDENTIFIED BY :password;
printf("\nConnected to ORACLE as user: %s\n", username);
/* Declare the cursor. All static SQL explicit cursors
* contain SELECT commands. 'salespeople' is a SQL identifier,
* not a (C) host variable.
*/
EXEC SQL DECLARE salespeople CURSOR FOR
SELECT ENAME, SAL, COMM
FROM EMP
WHERE JOB LIKE 'SALES%';
/* Open the cursor. */
EXEC SQL OPEN salespeople;
/* Get ready to print results. */
printf("\n\nThe company's salespeople are--\n\n");
printf("Salesperson Salary Commission\n");
printf("----------- ------ ----------\n");
/* Loop, fetching all salesperson's statistics.
* Cause the program to break the loop when no more
* data can be retrieved on the cursor.
*/
EXEC SQL WHENEVER NOT FOUND DO break;
for (;;)
{
EXEC SQL FETCH salespeople INTO :emp_rec_ptr;
printf("%-11s%9.2f%13.2f\n", emp_rec_ptr->emp_name,
emp_rec_ptr->salary, emp_rec_ptr->commission);
}
/* Close the cursor. */
EXEC SQL CLOSE salespeople;
printf("\nArrivederci.\n\n");
EXEC SQL COMMIT WORK RELEASE;
exit(0);
}
void
sql_error(msg)
char *msg;
{
char err_msg[512];
int buf_len, msg_len;
EXEC SQL WHENEVER SQLERROR CONTINUE;
printf("\n%s\n", msg);
/* Call sqlglm() to get the complete text of the
* error message.
*/
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);
}