プライマリ・コンテンツに移動
Pro*C/C++プログラマーズ・ガイド
12c リリース1(12.1)
B71397-03
目次へ移動
目次
索引へ移動
索引

前
次

ホスト構造体

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); 
...

PL/SQLレコード

C言語の構造体は、PL/SQLレコードにバインドできません。

ネストした構造体と共用体

ホスト構造体はネストできません。次の例は無効です。

struct 
{ 
    int emp_number; 
    struct 
    { 
        float salary; 
        float commission; 
    } sal_info;            /* INVALID */ 
    int dept_number; 
} emp_record; 
... 
EXEC SQL SELECT empno, sal, comm, deptno 
    INTO :emp_record 
    FROM emp; 

また、C言語の共用体をホスト構造体として使用することも、ホスト構造体として使用される構造体に共用体をネストすることもできません。

ホスト・インジケータ構造体

標識変数を使用する必要があっても、ホスト変数がホスト構造体に含まれている場合は、ホスト構造体内のホスト変数ごとに標識変数が含まれるように、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ステータスはホスト・インジケータ構造体で使用可能になります。

注意:

このマニュアルでは、従来どおりホスト変数または構造体の名前に_indを追加して、標識変数とインジケータ構造体の名前にしています。ただし、標識変数の名前は任意です。異なる規則を使用しても、規則をまったく使用しなくてもかまいません。

サンプル・プログラム: カーソルとホスト構造体

この項のデモ・プログラムは、明示カーソルを使用し、データをホスト構造体に格納する問合せを示しています。このプログラムは、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);
}