Pro*c プログラミング


Pro*c とは?
プログラムの基本的な流れ
oracle のデータ型 VARCHAR 変数

埋込み SQL の利用法 問合せ (SELECT 文) NULL 値の検出 カーソルの使い方
Pro*c プログラム Pro*c プログラム概要 プログラム例 練習問題
動的 SQL の使い方 動的 SQL 方法 1 動的 SQL 方法 2 動的 SQL 方法 3
動的 SQL 方法 4 動的 SQL プログラム例
実行時のエラー処理 sqlca (SQL 通信領域) oraca (oracle 通信領域) エラーメッセージの取得
プリコンパイラの実行 make プリコンパイルオプション 実行形式作成手順
実行の流れ サーバとの関係 実行時の概念図 注意点
その他 埋込み SQL 構文 Pro*c 用語集 FAQ

Pro*c とは?

Pro*c は、c プログラムから ORACLE データベースへアクセスするためのプリプロセッサで、 c のプログラムに SQL 文を埋め込むことにより実現できます。 Pro*c プログラムの拡張子は .pc です。

proc(Pro*c プリプロセッサ)は Pro*c プログラムから c のプログラムを生成します。 そのあと c コンパイラでコンパイル、リンクすることにより、 実行モジュールを作ります。

埋込み SQL 文には、宣言文と実行文があります。

宣言文は、oracle オブジェクト、通信領域、SQL 変数を定義するために使い、 c 言語の宣言文を置けるところであれば、どこでも記述可能です。

実行文は、ランタイムライブラリ SQLLIB をコールします。
実行文は、c 言語の実行文を置けるところであれば、どこでも記述可能です。

   

Pro*c プログラムの基本的な流れ

  1. Pro*c を使うための設定

    以下のヘッダファイルはインクルードすべきです。 なぜならエラー情報などが設定されている領域を定義、確保するからです。

    sqlca.h は、oracle との通信領域を定義、確保します。 これらは宣言文ですから、プログラム先頭部分に書きます。

    
    EXEC SQL INCLUDE SQLCA;
    
    #include <sqlca.h>

  2. 埋め込み SQL 文で使う変数を宣言します。 プリコンパイルオプション MODE=ANSI を使う場合は必須ですが、 ORACLE の場合はなくてもかまいません。

    #define PWD_LEN   11
    #define MAX_DATA  10
    typedef struct emp_info {
       char       emp_name[PWD_LEN];
       float      salary;
       float      commission;
    } EMP_INFO;
    
    EXE BEGIN DECLARE SECTION;
       char     username[PWD_LEN];
       char     password[PWD_LEN];
       EMP_INFO emp_info1;
       EMP_INFO emp_info[MAX_DATA];
    EXEC SQL END DECLARE SECTION;
    

  3. ユーザ/パスワードを指定してデータベースに接続します。

    #define SZ_NAME    32
    #define USER_NAME  "scott"
    #define USER_PASS  "tiger"
    
    EXEC SQL BEGIN DECLARE SECTION;
    char username[SZ_NAME],password[SZ_NAME];
    EXEC SQL END DECLARE SECTION;
    
    strcpy(username,USER_NAME);
    strcpy(password,USER_PASS);
    EXEC SQL CONNECT :username IDENTIFIED BY :password;
    
    #define USER_PASS "scott/tiger" #define TNS_NAME "bach817" EXEC SQL BEGIN DECLARE SECTION; char *uid = USER_PASS; char *conn_str = TNS_NAME; EXEC SQL END DECLARE SECTION; EXEC SQL CONNECT :uid ; ユーザ名/パスワードで接続 EXEC SQL CONNECT :uid USING :conn_str; ユーザ名/パスワードと接続文字列で接続

  4. oracle に接続後データの操作、問合せをします。
    INSERT/UPDATE/DELETE/SELECT 文が利用できます。

    #define PWD_LEN   11
    typedef struct emp_info {
       char       emp_name[PWD_LEN];
       float      salary;
       float      commission;
    } EMP_INFO;
    
    EXEC SQL BEGIN DECLARE SECTION;
      EMP_INFO emprec;
      short    emprec_ind;
      int      emp_number;
    EXEC SQL END DECLARE SECTION;
    
    EXEC SQL WHENEVER NOT FOUND GOTO notfound;
    
    EXEC SQL SELECT ename, sal, comm
         INTO :emprec INDICATOR :emprec_ind
         FROM EMP
         WHERE EMPNO = :emp_number;
        :
    notfound:
    
    

  5. トランザクションをクローズします。 下記のいずれかを実行します。

    EXEC SQL COMMIT RELEASE;
    
    EXEC SQL ROLLBACK RELEASE;

  6. エラー処理

    EXEC SQL WHENEVER SQLERROR を指定すると、 以降の埋込み SQL 文の実行時のエラー (SQLERROR) をハンドリングできます。
    EXEC SQL WHENEVER SQLERROR を指定したときは、
    EXEC SQL ROLLBACK RELEASE; を実行する前では、
    EXEC SQL WHENEVER SQLERROR CONTINUE; を必ず指定しなければなりません。
    なぜなら ROLLBACK でエラーが発生した場合、 結果として無限ループに陥る可能性があるからです。

    #define  SZ_MSG    256
    
    EXEC SQL WHENEVER SQLERROR DO sql_error("ORACLE error--\n");
    
    /*--------------------------------------------------------------
       Function : print error message
      --------------------------------------------------------------*/
    void sql_error(char *msg) {
        char err_msg[SZ_MSG];
        size_t buf_len=SZ_MSG, msg_len;
    
        EXEC SQL WHENEVER SQLERROR CONTINUE;
    
        printf("\n%s\n", msg);
        sqlglm(err_msg, &buf_len, &msg_len);
        printf("%.*s\n", msg_len, err_msg);
    
        EXEC SQL ROLLBACK RELEASE;
        exit(EXIT_FAILURE);
    }
    
   

oracle のデータ型

Pro*c では、データベースを定義するデータ型を内部データ型、 プログラムでそれらを扱うデータ型を外部データ型といいます。

oracle と Pro*c プログラムとの間でデータのやりとりをするのが ホスト変数で、 コロン(:)の後ろに変数名を書きます。

通常 Pro*c プログラムのホスト変数から oracle にデータを渡し、 oracle がプログラムのホスト変数にデータベースの値を返します。

ホスト変数は、 oracle プログラムインタフェースがサポートする c のデータ型を指定して宣言します。 この c のデータ型は、データベースの列のデータ型と互換性がなければなりません。

c のデータ型(外部データ型)とデータベースの列のデータ型(内部データ型)との互換性は、次のとおりです。

内部データ型
列のデータ型
外部データ型
c のデータ型
説       明
VARCHAR2(Y) char 単一の文字
CHAR(X) char[n]
VARCHAR[n]
int
short
long
float
double
n バイトの文字列
n バイトの可変長文字列
整数
小さい整数
大きい整数
浮動小数点数
倍精度浮動小数点数
NUMBER int 整数
NUMBER(P,S) short
long
float
double
char
char[n]
VARCHAR[n]
小さい整数
大きい整数
浮動小数点数
倍精度浮動小数点数
単一の文字
n バイトの文字列 
n バイトの可変長文字列
DATE char[n]
VARCHAR[n]
n バイトの文字列 
n バイトの可変長文字列
LONG char[n]
VARCHAR[n]
n バイトの文字列 
n バイトの可変長文字列
RAW(X) unsigned char[n]
VARCHAR[n]
n バイトの文字列 
n バイトの可変長文字列
LONG RAW unsigned char[n]
VARCHAR[n]
n バイトの文字列 
n バイトの可変長文字列
ROWID unsigned char[n]
VARCHAR[n]
n バイトの文字列 
n バイトの可変長文字列
MLSLABEL unsigned char[n]
VARCHAR[n]
n バイトの文字列 
n バイトの可変長文字列

VARCHAR 変数

データ型 VARCHAR は、oracle によって定義された型です。

VARCHAR 型は、可変長の文字列を宣言するのに使用します。 VARCHAR は宣言済みの struct と考えてください。

arr に実際の文字列が、len に文字列長が入ります。

VARCHR   username[20];

struct {
   unsigned short  len;
   unsigned char   arr[20];
}  username;

   

埋込み SQL の利用法

Pro*c プログラムは、ホスト変数と埋込み SQL 文を使って oracle にデータを渡したり、 oracle からデータを受け取ったりします。

EXEC SQL DELETE FROM EMP WHERE ENAME= :name;

EXEC SQL INSERT INTO emp (empno,ename,sal,deptno) 
           VALUES (:emp_no,:emp_name,:emp_sal,:emp_deptno);

EXEC SQL UPDATE emp SET sal = :emp_sal,comm = :emp_comm 
           WHERE empno = :emp_no;

ここで、name,emp_no,emp_name,emp_sal,emp_deptno,emp_comm はホスト変数で、 この文を実行する前にあらかじめ値を設定しておく必要があります。

DELETE,INSERT,UPDATE 文等は、複雑ではありませんが、SELECT 文は少し複雑ですので、 次に説明します。

問合せ (SELECT 文)

問合せでは、結果として戻される行は次のように分類されます。

  1. 行を戻さない問合せ(有無のみを調べる)
  2. 1行がもどる問合せ
  3. 複数行がもどる問合せ

上記の条件をすべて満たすプログラムを作るには、 カーソルを使う必要があります。

EXEC SQL DECLARE emp_cursor CURSOR FOR                     カーソルを定義する
     SELECT ename,empno,sal FROM emp
         WHERE deptno = :dept_no;

EXEC SQL OPEN emp_cursor;                    カーソルをオープンする(初期化)

EXEC SQL WHENEVER NOT FOUND DO break;    データが終わったらループを脱出します
for (;;)  {
   EXEC SQL FETCH emp_cursor INTO                データベースの1行を取り出す
        :emp_name, :emp_no, :emp_sal;
}

EXEC SQL CLOSE emp_cursor;                             カーソルをクローズする

ここで dept_no,emp_name,emp_no,emp_sal がホスト変数で、 dept_no は実行前に設定しなければらず、 emp_name,emp_no,emp_sal は実行時に oracle データベース内の値が設定されます。

また、ホスト変数のかわりにホスト配列ホスト構造体を使うこともできます。

NULL 値の検出

NOT NULL 制約のないカラムは、値が設定されていない場合があります。 このような場合は、標識変数を使うことで、 それを知ることができます。 標識変数には NULL 以外の切り捨て情報も設定されます。

標識変数は、ホスト変数の次にコロン(:)と標識変数名を続けます。

次の例のホスト変数 commission に対する標識変数は、ind_com です。

EXEC SQL FETCH clerkpeople
     INTO :emp_name, :salary ,:commission [INDICATOR] :ind_com;

カーソルの使い方

複数行が返る検索では、カーソルを定義することで行の逐次操作が可能です。 カーソルは、問合せで戻される行の集合体内の現在行を指しています。

カーソルの操作は次の4つで、これらの操作は同一ファイル内になければなりません。

またカーソルの適応範囲はファイル全体ですので、 同一名のカーソルを同一ファイル内で 2 つ以上は宣言できません。

  1. DECLARE CURSOR
    カーソルを宣言し、名前付けします。

    EXEC SQL DECLARE salespeople CURSOR FOR
         SELECT ENAME, SAL, COMM FROM EMP WHERE JOB LIKE 'SALES%';
    

  2. OPEN
    カーソルは、問合せ結果の集合体の最初の行の直前に位置付けられます。

    EXEC SQL OPEN salespeople;
    

  3. FETCH
    カーソルは、問合せ結果の集合体の現在行を順方向に進みます。
    問合せの結果を入れるホスト変数は、 カーソルを宣言したときの選択項目リストと同数でなければなりません。

    EXEC SQL FETCH salespeople INTO :emp_info;
    

  4. CLOSE
    カーソルが利用していた記憶域を開放します。

    EXEC SQL CLOSE salespeople;
    

次の場合は、FETCH でエラーとなります。

  1. カーソルを OPEN する前。
  2. 問合せで戻される行がない。
  3. カーソルを CLOSE した後。
   

Pro*c プログラム

Pro*c プログラム概要

項目 コ   ー   ド
宣言
EXEC SQL BEGIN DECLARE SECTION;
   char     username[PWD_LEN];
   char     password[PWD_LEN];
   EMP_INFO emp_info1;
   EMP_INFO emp_info[MAX_DATA];
EXEC SQL END DECLARE SECTION;
接続処理
EXEC SQL INCLUDE SQLCA;
#define  USER_NAME   "scott"
#define  USER_PASS   "tiger"

EXEC SQL BEGIN DECLARE SECTION;
  VARCHAR  username[UNAME_LEN];/* VARCHAR is an Oracle-supplied struct */
  varchar  password[PWD_LEN];  /* varchar can be in lower case also. */
EXEC SQL END DECLARE SECTION;

/* set USER/PASSWD  */
strcpy((char *) username.arr, USER_NAME);
username.len = (unsigned short) strlen((char *) username.arr);
strcpy((char *) password.arr, USER_PASS);
password.len = (unsigned short) strlen((char *) password.arr);

/* Register sql_error() as the error handler. */
EXEC SQL WHENEVER SQLERROR DO sql_error("ORACLE error :\n");

/* Connect to ORACLE.  Program will call sql_error() */
EXEC SQL CONNECT :username IDENTIFIED BY :password;
printf("Connected to ORACLE as user: %s\n\n", username.arr);
終了処理
/* Disconnect from ORACLE. */
EXEC SQL COMMIT RELEASE;
該当する
1行を取得

ホスト変数を使う
#define SZ_ENAME  (10+1)

EXEC SQL BEGIN DECLARE SECTION;
int    empno,eno_king=7839;
char   emp_name[SZ_ENAME];
EXEC SQL END DECLARE SECTION;

EXEC SQL WHENEVER NOT FOUND GOTO nfnd_a;

EXEC SQL SELECT EMPNO,ENAME INTO :empno, :emp_name
   FROM EMP WHERE EMPNO = :eno_king;
    :
nfnd_a:
該当する
1行を取得

ホスト変数と
標識変数を使う
#define SZ_ENAME  (10+1)

EXEC SQL BEGIN DECLARE SECTION;
int    empno,eno_king=7839;
char   emp_name[SZ_ENAME];
float  comm;
short  ind_com;
EXEC SQL END DECLARE SECTION;

EXEC SQL WHENEVER NOT FOUND GOTO nfnd_a;

EXEC SQL SELECT EMPNO,ENAME,COMM INTO :empno, :emp_name, :comm:ind_com
   FROM EMP WHERE EMPNO = :eno_king;
    :
nfnd_a:
該当する
複数行を取得

カーソルを使う
[定義部]
データベースの表のうちで選択項目と同じ項目の構造体を定義する

typedef struct emp_info {
    char     emp_name[SZ_ENAME];
    float    salary;
    float    commission;
}  EMP_INFO;

/* declare cursor                    */
EXEC SQL DECLARE tblc_cursor CURSOR FOR
     SELECT ENAME, SAL, COMM FROM EMP WHERE JOB LIKE 'SALES%';

EXEC SQL BEGIN DECLARE SECTION;
   EMP_INFO   emp_info;
EXEC SQL END DECLARE SECTION;

[参照部]
/* open cursor                       */
EXEC SQL OPEN tblc_cursor;

EXEC SQL WHENEVER NOT FOUND DO break;
/* get data from emp         */
for (j=0;;j++) {
    EXEC SQL FETCH tblc_cursor INTO :emp_info;
          :
}
EXEC SQL CLOSE tblc_cursor;
該当する
複数行を取得

構造体配列を使う
[定義部] 
データベースの表のうちで選択項目と同じ項目の構造体を定義する
使用する配列のサイズを定義する

typedef struct emp_info {
    char     emp_name[SZ_ENAME];
    float    salary;
    float    commission;
}  EMP_INFO;
#define MAX_DATA   10

/* declare cursor                    */
EXEC SQL DECLARE tblc_cursor CURSOR FOR
     SELECT ENAME, SAL, COMM FROM EMP WHERE JOB LIKE 'SALES%';

EXEC SQL BEGIN DECLARE SECTION;
EMP_INFO   emp_info[MAX_DATA];
EXEC SQL END DECLARE SECTION;

[参照部] 
/* open cursor                       */
int  rows;

EXEC SQL OPEN tblc_cursor;

EXEC SQL WHENEVER NOT FOUND DO break;

for (;;) {
   EXEC SQL FETCH tblc_cursor INTO :emp_info;
   rows = sqlca.sqlerrd[2];
            :
}
rows = sqlca.sqlerrd[2];
EXEC SQL CLOSE tblc_cursor;
エラー処理
[定義部] 
#define SZ_BUF   256

void sql_error(char *msg) {
    char err_msg[SZ_BUF];
    size_t buf_len=SZ_BUF, msg_len;

    EXEC SQL WHENEVER SQLERROR CONTINUE;

    printf("\n%s\n", msg);
    sqlglm(err_msg, &buf_len, &msg_len);
    printf("%.*s\n", msg_len, err_msg);

    EXEC SQL ROLLBACK RELEASE;
    exit(EXIT_FAILURE);
}

プログラム例

  1. ホスト変数、ホスト配列、標識変数を使う例 : proc1.pc
  2. ホスト構造体、標識構造体を使う例 : proc2.pc
  3. カーソル変数を使う例 : proc3.pc

練習問題

  1. Pro*c プログラム例の1のget_1rec(),get_Nrec() を書き換えてみましょう。 (type.pc)

    表は次のようなもので、まず構造体を定義します。

    SQL> desc type
     名前                                      NULL?    型
     ----------------------------------------- -------- ----------------------------
     NUM4                                               NUMBER(4)
     NUM8                                               NUMBER(8)
     NUM8_2                                             NUMBER(8,2)
     VAR2_10                                            VARCHAR2(10)
     CHA_12                                             CHAR(12)
     DAY                                                DATE
    

   

動的 SQL の使い方

動的 SQL とは、実行時に SQL 文を生成してそれを実行することです。

動的 SQL には、次の4つの方法があり、番号が大きいほど自由度が増します。

生成する文の最後に ";" があってはなりません。

方法 可能な SQL 文の種類
1 ホスト変数を使わない非問合せ
2 入力ホスト変数の数がわかっている非問合せ
3 選択リスト項目の数と入力ホスト変数の数がわかっている問合せ
4 選択リスト項目の数または入力ホスト変数の数が不明な問合せ

動的 SQL 方法 1

動的 SQL 方法 1 は、ホスト変数を使わない非問合せで、 EXECUTE IMMEDIATE を使って実行できる文でなければなりません。

#define SZ_MSG  256

static char *stmt = "INSERT INTO EMP values (%s)";
static char *data = "9999,'abe','CLERK',7902,'2004/05/21',800, NULL, 20";
char dynstmt[SZ_MSG];

snprintf(dynstmt,SZ_MSG,stmt,data);

EXEC SQL EXECUTE IMMEDIATE :dynstmt;                             文の実行

動的 SQL 方法 2

動的 SQL 方法 2 は、入力ホスト変数の数がわかっている非問合せで、 PREPARE および EXECUTE を使って実行できる文でなければなりません。

ただし、入力ホスト変数のプレースホルダの数とデータ型は、 プリコンパイル時にわかっていなければなりません。

#define SZ_MSG   256
#define SZ_NAME  32

static char *stmt =  "DELETE FROM EMP WHERE ENAME= :v1 ";
char dynstmt[SZ_MSG];
char ename[SZ_NAME];

strcpy(dynstmt,stmt);
strcpy(ename,"abe");

EXEC SQL PREPARE S FROM :dynstmt;                                   文の解析
EXEC SQL EXECUTE S USING :ename;                                    文の実行

動的 SQL 方法 3

動的 SQL 方法 3 は、選択リスト項目の数と入力ホスト変数の数がわかっている問合せで、 DECLARE,OPEN,FETCH,CLOSE カーソルと PREPARE 文を使って問合せます。

ただし、選択リストの数、入力ホスト変数のプレースホルダの数とデータ型は、 プリコンパイル時にわかっていなければなりません。

#define ORACLE_EOF    1403
#define SZ_MSG         256
#define SZ_NAME         32

static char *stmt =  "SELECT ename FROM emp WHERE deptno = :v1";
int  deptno;
char dynstmt[SZ_MSG];
char ename[SZ_NAME];

strcpy(dynstmt,stmt);

EXEC SQL PREPARE S FROM :dynstmt;                                   文の解析
EXEC SQL DECLARE C CURSOR FOR S;                      カーソルと文の対応ずけ
EXEC SQL OPEN C USING :deptno;          ホスト変数とプレースホルダの対応ずけ

for (;;) {
   EXEC SQL FETCH C INTO :ename;                                データの取得
   if (sqlca.sqlcode == ORACLE_EOF) break;
   if (sqlca.sqlcode != 0) break;
}
EXEC SQL CLOSE C;                                         カーソルのクローズ

動的 SQL 方法 4

動的 SQL 方法 4 は、選択リスト項目の数または入力ホスト変数の数が不明な問合せで、 SQLDA を宣言し、適切な値を設定し DESCRIBE SELECT LIST 文を発行します。 結果は SQLDA に設定されます。

SQLDA はホストプログラムのデータ構造体で、 選択リスト項目または入力ホスト変数を記述します。

   struct SQLDA {
     int         N; \   選択項目/プレースホルダ配列の最大値
     char     *V[]; @&  選択項目またはバインド変数の値を格納するアドレスの配列
     int       L[]; @   V のバッファ長の配列
     short     T[]; @   V の型の配列  (SQLT_CHR,SQLT_NUM,SQLT_INT ..)
                        NOT NULL 制約がないと上位ビットが ON となっている
     short     I[]; @&  標識変数の値を格納するアドレスの配列
     int         F; #   DESCRIBE での選択項目/プレースホルダ配列の数(実際の数)
     char     *S[]; #   選択項目/プレースホルダ名を格納するアドレスの配列
     short     M[];     S のバッファ長の配列
     short     C[]; #   S に実際格納されたバイト数の配列
     char      X[];     標識変数名を格納するアドレス配列
     short     Y[];     X のバッファ長の配列
     short     Z[];     X に実際格納されたバイト数の配列
   };

     \ : EXEC SQL DESCRIBE を実行する前に設定しなければなりません。
     # : (EXEC SQL DESCRIBE SELECT LIST FOR S INTO select_dp) で設定されます
     @ : EXEC SQL FETCH を実行する前に設定しなければなりません。
     & : set (EXEC SQL FETCH C USING DESCRIPTOR select_dp)

#include <sqlda.h>
#define SZ_MSG  256

char    dynstmt[SZ_MSG];
SQLDA   *bind_dp,*select_dp;

bind_dp   = SQLSQLDAAlloc();                                    SQLDA の確保
select_dp = SQLSQLDAAlloc();

EXEC SQL PREPARE S FROM :dynstmt;                               SQL 文の解析
EXEC SQL DECLARE C CURSOR FOR S;

EXEC SQL DESCRIBE BIND VARIABLES FOR S INTO bind_dp;    SQLDA bind_dp に設定

EXEC SQL OPEN C USING DESCRIPTOR bind_dp;                 カーソルのオープン

EXEC SQL DESCRIBE SELECT LIST FOR S INTO select_dp;   SQLDA select_dp に設定

EXEC SQL WHENEVER NOT FOUND GOTO end_select_loop;                 EOF の検出
for (;;) {
   EXEC SQL FETCH C USING DESCRIPTOR select_dp;                 データの取得
}

end_select_loop:
sqlclu(select_dp);                                              SQLDA の解放
sqlclu(bind_dp);

EXEC SQL CLOSE C;                                         カーソルのクローズ

動的 SQL プログラム例

  1. 動的 SQL 1/2/3 のプログラム例 : dyn123.pc
  2. 動的 SQL 4 のプログラム例 : dyn4.pc
   

実行時のエラー処理

SQL データ操作文を実行した後は、 必ずその操作が正しく行われたかどうか調べる必要があります。

sqlca (SQL 通信領域)

sqlca(SQL 通信領域構造体)には、すべての SQL 実行文の実行後の状態が格納されます。
sqlca は、次のいずれかでプログラムに組み込むことができます。

EXEC SQL INCLUDE SQLCA

#include <sqlca.h>

実行時のエラー処理には、次の方法があります。

  1. WHENEVER 文による暗黙的なチェック

    EXEC SQL WHENEVER <条件>            <アクション>
                      SQLWARNING        CONTINUE
                      SQLERROR          DO
                      NOT FOUND         DO BREAK
                                        DO CONTINUE
                                        GOTO ラベル
                                        DO STOP
    
    EXEC SQL ROLLBACK RELEASE;
    を使う場合は、その前で
    EXEC SQL WHENEVER SQLERROR CONTINUE;
    を指定しなければなりません。ROLLBACK でエラーが発生した場合、
    無限ループを回避するためです。  

  2. SQLCA 領域の明示的なチェック

    sqlca は、以下の構造で定義されており、それぞれに情報が格納されます。

    struct   sqlca {
       char    sqlcaid[8];                                              "SQLCA"
       int     sqlabc;                                         sqlca のバイト数
       int     sqlcode;           ステータスコードで WHENEVER 文がチェックします
       struct {
          unsigned short sqlerrml;               sqlerrmc にあるメッセージの長さ
          char           sqlerrmc[70]; oracle のエラーメッセージの始めの 70 文字
          } sqlerrm;
       char    sqlerrp[8];                                                未使用
       int     sqlerrd[6];                                  [2] には処理済み行数
                                           [4] には SQL 文の解析エラーの文字位置
       char    sqlwarn[8];                                            警告フラグ
       char    sqlext[8];                                                 未使用
    }  sqlca;
    
    sqlcode:  =0   文を正しく実行したことを意味します
              >0   例外を検出したうえで、文を実行したことを意味します
              <0   エラーを検出し、文を実行しなかったことを意味します
    
    
    if (sqlca.sqlcode < 0)  break;
    
    

  3. 上記の両方を使う

oraca (oracle 通信領域)

oraca(oracle 通信領域構造体)には、 oracle サーバとの状態が格納されており、sqlca より詳細な情報があります。
oraca は、以下のいずれかでプログラムに組み込むことができます。

EXEC SQL INCLUDE ORACA

#include <oraca.h>

struct   oraca
         {
    /* text */ char oracaid[8];     /* Reserved                            */
    /* ub4  */ int oracabc;         /* Reserved                            */

/*       Flags which are setable by User. */

   /* ub4 */ int  oracchf;          /* <> 0 if "check cur cache consistncy"*/
   /* ub4 */ int  oradbgf;          /* <> 0 if "do DEBUG mode checking"    */
   /* ub4 */ int  orahchf;          /* <> 0 if "do Heap consistency check" */
   /* ub4 */ int  orastxtf;         /* SQL stmt text flag                  */
#define  ORASTFNON 0                /* = don't save text of SQL stmt       */
#define  ORASTFERR 1                /* = only save on SQLERROR             */
#define  ORASTFWRN 2                /* = only save on SQLWARNING/SQLERROR  */
#define  ORASTFANY 3                /* = always save                       */
         struct
           {
  /* ub2  */ unsigned short orastxtl;
  /* text */ char  orastxtc[70];
           } orastxt;               /* text of last SQL stmt               */
         struct
           {
  /* ub2  */   unsigned short orasfnml;
  /* text */   char       orasfnmc[70];
           } orasfnm;               /* name of file containing SQL stmt    */
  /* ub4 */ int   oraslnr;          /* line nr-within-file of SQL stmt     */

  /* ub4 */ int   orahoc;           /* highest max open OraCurs requested  */
  /* ub4 */ int   oramoc;           /* max open OraCursors required        */
  /* ub4 */ int   oracoc;           /* current OraCursors open             */
  /* ub4 */ int   oranor;           /* nr of OraCursor re-assignments      */
  /* ub4 */ int   oranpr;           /* nr of parses                        */
  /* ub4 */ int   oranex;           /* nr of executes                      */
} oraca;

oraca を使うとカーソルの数、SQL 文の解析数、SQL 文の実行数などの統計情報を取得できます。

エラーメッセージの取得

sqlglm() 関数を使うと、エラーメッセージの全文が取得できます。

#define SZ_MSG     256
char msgbuf[SZ_MSG];
int  size=SZ_MSG,len;

void sqlglm(msgbuf,&size,&len);
fprintf(stderr,"%s\n",msgbuf);

   

プリコンパイラの実行

プリコンパイラ (proc) でプリコンパイルするには Pro*c のプログラム名を指定します(拡張子なし)。

proc dyn1                           Pro*c プログラムのプリコンパイル:dyn1.pc

gcc -I.  -I $ORACLE_HOME/precomp/public -I $ORACLE_HOME/rdbms/public \
         -I $ORACLE_HOME/rdbms/demo -c dyn1.c          c のコンパイル:dyn1.o
gcc -o dyn1 dyn1.o -L $ORACLE_HOME/lib/ -lclntsh \
    `cat $ORACLE_HOME/lib/sysliblist` -laio -lm -lthread 
                                                   オブジェクトのリンク:dyn1

make

make 用のファイル demo_proc.mk が $ORACLE_HOME/precomp/demo/proc にあります。
次のように OBJS、EXE を指定し build することで実行モジュールを作成できます。

make -f demo_proc.mk OBJS=プログラム名.o EXE=プログラム名 build

プリコンパイルオプション

 オプション 説   明 デフォルト
AUTO_CONNECT=YES|NO 自動ログオン NO
CHAR_MAP=VARCHAR2|CHARZ|STRING|CHARF 

 OPTION   |      内          容
----------+--------------------------------------
 VARCHAR2 |固定長の空白埋め、末尾に NULL 付き
 CHARZ    |固定長の空白埋め、末尾に NULL 付き
 STRING   |可変長の空白埋めなし、末尾に NULL 付き
 CHARF    |固定長の空白埋め、末尾に NULL なし
文字配列と文字列のマッピング CHARZ
CODE=ANSI_C|KR_C|CPP 生成される c コードの種類 KR_C
INAME=ファイル名 ソースファイル名(必須) -
INCLUDE=パス名 #include ファイルの検索パス -
MAXOPENCURSORS=5-255 同時にキャッシュされるカーソル数 10
MODE=ANSI|ISO|ORACLE ANSI|ISO|ORACLE の動作 ORACLE
RELEASE_CURSOR=YES|NO カーソルキャッシュからのカーソル領域の解放 NO
SQLCHECK=SEMANTICS|SYNTAX プリコンパイル時の SQL チェック種別 SYNTAX

実行形式作成手順

Pro*c のソースから実行形式を作成するまでの手順は、以下のとおりです。

Pro*c ソース
.pc
EXEC SQL
Pro*c プリプロセッサでプリコンパイルして c のソースを作成
c のソース
.c
sqlcxt((void **)0, &sqlctx, &sqlstm, &sqlfpn);
c コンパイラでコンパイルしてオブジェクトを作成
オブジェクト
.o

c コンパイラでリンクして実行形式を作成 (+ oracle client ライブラリ)
実行形式
binary

   

実行の流れ

サーバとの関係

Pro*c プログラムの SQL 文は、 プリコンパイラによって以下のコードになります。

EXEC SQL SELECT ename, sal, comm INTO :emprec INDICATOR :emprec_ind
     FROM EMP WHERE EMPNO = :emp_number;
    === > 
{
   struct sqlexd sqlstm;
   sqlstm.sqlvsn = 10;
   sqlstm.arrsiz = 4;
   sqlstm.sqladtp = &sqladt;
   sqlstm.sqltdsp = &sqltds;
   sqlstm.stmt = "select ename ,sal ,comm into :s1:s2 ,:s3:s4 ,:s5:s6 \
     from EMP where EMPNO=:b2";
   sqlstm.iters = (unsigned int  )1;
   sqlstm.offset = (unsigned int  )51;
   sqlstm.selerr = (unsigned short)1;
   sqlstm.cud = sqlcud0;
   sqlstm.sqlest = (unsigned char  *)&sqlca;
   sqlstm.sqlety = (unsigned short)256;
   sqlstm.occurs = (unsigned int  )0;
   sqlstm.sqhstv[0] = (         void  *)&emprec.emp_name;
   sqlstm.sqhstl[0] = (unsigned int  )22;
   sqlstm.sqhsts[0] = (         int  )0;
   sqlstm.sqindv[0] = (         void  *)&emprec_ind.emp_name_ind;
   sqlstm.sqinds[0] = (         int  )0;
   sqlstm.sqharm[0] = (unsigned int  )0;
   sqlstm.sqadto[0] = (unsigned short )0;
   sqlstm.sqtdso[0] = (unsigned short )0;
   sqlstm.sqhstv[1] = (         void  *)&emprec.salary;
   sqlstm.sqhstl[1] = (unsigned int  )sizeof(float);
   sqlstm.sqhsts[1] = (         int  )0;
   sqlstm.sqindv[1] = (         void  *)&emprec_ind.sal_ind;
   sqlstm.sqinds[1] = (         int  )0;
   sqlstm.sqharm[1] = (unsigned int  )0;
   sqlstm.sqadto[1] = (unsigned short )0;
   sqlstm.sqtdso[1] = (unsigned short )0;
   sqlstm.sqhstv[2] = (         void  *)&emprec.commission;
   sqlstm.sqhstl[2] = (unsigned int  )sizeof(float);
   sqlstm.sqhsts[2] = (         int  )0;
   sqlstm.sqindv[2] = (         void  *)&emprec_ind.comm_ind;
   sqlstm.sqinds[2] = (         int  )0;
   sqlstm.sqharm[2] = (unsigned int  )0;
   sqlstm.sqadto[2] = (unsigned short )0;
   sqlstm.sqtdso[2] = (unsigned short )0;
   sqlstm.sqhstv[3] = (         void  *)&emp_number;
   sqlstm.sqhstl[3] = (unsigned int  )sizeof(int);
   sqlstm.sqhsts[3] = (         int  )0;
   sqlstm.sqindv[3] = (         void  *)0;
   sqlstm.sqinds[3] = (         int  )0;
   sqlstm.sqharm[3] = (unsigned int  )0;
   sqlstm.sqadto[3] = (unsigned short )0;
   sqlstm.sqtdso[3] = (unsigned short )0;
   sqlstm.sqphsv = sqlstm.sqhstv;
   sqlstm.sqphsl = sqlstm.sqhstl;
   sqlstm.sqphss = sqlstm.sqhsts;
   sqlstm.sqpind = sqlstm.sqindv;
   sqlstm.sqpins = sqlstm.sqinds;
   sqlstm.sqparm = sqlstm.sqharm;
   sqlstm.sqparc = sqlstm.sqharc;
   sqlstm.sqpadto = sqlstm.sqadto;
   sqlstm.sqptdso = sqlstm.sqtdso;
   sqlcxt((void **)0, &sqlctx, &sqlstm, &sqlfpn);
   if (sqlca.sqlcode == 1403) goto notfound;
   if (sqlca.sqlcode < 0) sql_error("ORACLE error--\n");
}

Pro*c プログラム内の SQL 文は、 sqlcxt() 関数を介して oracle サーバとやりとりします。 その際4つの引数を渡します。 そのうちの sqlctx、sqlstm、sqlfpn について説明します。

  1. sqlctx:プログラム識別番号
    unsigned long sqlctx;
  2. sqlstm:実行する SQL 文についての情報があります。 この設定値は上記を参照してください。
    struct sqlexd sqlstm
  3. sqlfpn:プログラム名が設定されています

    struct sqlcxp {
      unsigned short fillen;
               char  filnam[8];
    };
    struct sqlcxp sqlfpn;
    

sqlctx() はオラクルのクライアント・ライブラリにあり、 サーバと Pro*c のインタフェースとして働きます。 すなわち引数をもとに要求をサーバに送り、サーバからの回答を受け取ります。

実行時の概念図

oracle データベースがインストールされているユーザで、 Pro*c アプリケーションを起動したときの概念図です。

Pro*c アプリケーション サーバ データベース
Pro*c
EXEC SQL
oracle client library
sqlcxt()
fork



pipe (ipc)
net (TCP/IP)
oracle databse

注意点

oracle のバージョンを上げた場合、 Pro*c で作成したアプリケーションは必ず再作成 (再プリコンパイル、再コンパイル、および再リンク)してください。

oracle KROWN 29898/54081/54084 を参考にしてください。

   

その他

埋込み SQL 構文

機 能 定         義
接続
EXEC SQL BEGIN DECLARE SECTION;
  型    変数名
EXEC SQL END DECLARE SECTION;
接続
EXEC SQL CONNECT :ユーザ名 IDENTIFIED BY :パスワード [USING :接続文字列]; 
EXEC SQL CONNECT :ユーザ名/パスワード [USING :接続文字列]; 
記述子の初期化 EXEC SQL DESCRIBE BIND VARIABLES FOR 文名 INTO 記述子;
カーソル
宣言    EXEC SQL DECLARE カーソル名 CURSOR FOR SELECT 文;         
オープン  EXEC SQL OPEN カーソル名 [USING :ホスト変数];
領域確保  EXEC SQL [AT データベース名] ALLOCATE :カーソル名; 
位置づけ  EXEC SQL FETCH カーソル名 INTO :ホスト変数;
クローズ  EXEC SQL CLOSE カーソル名; 
文宣言
宣言   EXEC SQL DECLARE 文名 STATEMENT;
解析   EXEC SQL PREPARE 文識別子 FROM :ホスト変数; 
実行   EXEC SQL EXECUTE 文識別子 [USING :ホスト変数];
実行   EXEC SQL EXECUTE IMMEDIATE :ホスト変数; 
表宣言 EXEC SQL DECLARE 表名 TABLE (表定義);
SELECT 文 EXEC SQL SELECT 選択項目リスト INTO ....;
INSERT 文 EXEC SQL INSERT INTO 表名 (データ値);
UPDATE 文 EXEC SQL UPDATE 表名 SET (データ値) 条件;
DELETE 文 EXEC SQL DELETE FROM 表名 条件;
トランザクション終了
変更の確定   EXEC SQL [AT データベース名] COMMIT [WORK];
変更の取り消し EXEC SQL [AT データベース名] ROLLBACK [WORK];
エラー処理
EXEC SQL WHENEVER <条件>            <アクション>
                  SQLWARNING        CONTINUE
                  SQLERROR          DO
                  NOT FOUND         DO BREAK
                                    DO CONTINUE
                                    GOTO ラベル
                                    DO STOP

Pro*c 用語集

用語 定         義
ホスト変数 c で宣言され oracle で共有される変数(配列も含む)で、 埋込み SQL で使う場合は、名前の前にコロン(:)を付けます。
入力ホスト変数は、バインド変数ともよばれます。
ホスト配列 Pro*c で宣言され oracle で共有される変数配列で、 埋込み SQL で使う場合は、名前の前にコロン(:)を付けます。
配列を使うと、単一の SQL 文で配列全体を操作できるので、 パフォーマンスが改善できます。
配列の最大サイズは、32767 をデータ型で除算した値です。
ホスト構造体 ホスト変数を構造体として宣言して適用したもので、 使い方はホスト変数と同じで、配列にすることもできます。
標識変数 入力ホスト変数への NULL 値の割り当てと、 出力ホスト変数のNULL 値、切り捨て値の検出のために、 ホスト変数とともに使います。
ホスト変数の直後にコロンとともに変数名を書きます。 標識変数は2バイトの整数として宣言しなければなりません。
入力ホスト変数の標識変数に設定される値 
>=0 操作は成功しました
-1  列に NULL 値が設定され、ホスト変数の値は無視されました。

出力ホスト変数の標識変数に設定される値 
0   操作は成功しました
>0  SELECT,FETCH の結果、ホスト変数の値は切り捨てられました。
  ホスト変数が NLS マルチバイト変数であれば、元の列長を文字数で表したもの。
  そうでなければ、元の列長をバイト数で表したもの。
-1  NULL で返る値は予測不能です。
-2  切り捨てられました。返る値は予測不能です。
標識配列 標識変数の配列のことで、任意のホスト配列に対応づけられます。
標識構造体 標識変数を構造体として宣言されたものに適用したもので、 使い方は標識変数と同じで、配列にすることもできます。
バインド変数 入力ホスト変数のことです
選択記述子 動的 SQL 4 の場合は選択項目は選択記述子に格納します
選択記述子の型は SQLDA です。
バインド記述子 動的 SQL 4 の場合はバインド項目はバインド記述子に格納します
選択記述子の型は SQLDA です。
トランザクション あるタスクを完了するために定義した1つ以上の SQL 文で、 SQL 実行文ではじまり COMMIT または ROLLBACK
または自動 COMMIT を発行するデータ定義文を記述することで終了します。
トランザクションの途中で異常終了すると、 そのトランザクションはロールバックされます
oracle オブジェクト oracle データベースシステムで定義されたデータ構造体に格納されたインスタンス
カーソル 問合せ結果の集合体の現在行を示す SQL 識別子
オブジェクト型 create type で作成したオブジェクト
静的 SQL あらかじめ決められた SQL 文を実行すること
動的 SQL 実行時に SQL 文を生成してそれを実行すること
プレースホルダ 動的 SQL 文を生成するとき、 ホスト変数を指定するための場所を確保するために便宜上の使う名前です。
従って宣言する必要もありませんし、任意の名前をつかえます
SQLDA 動的 SQL 4 を実行するためのバインド変数やプレースホルダを定義するための構造体です。
 struct SQLDA {
     int         N; \   選択項目/プレースホルダ配列の最大値
     char     *V[]; @&  選択項目またはバインド変数の値を格納するアドレスの配列
     int       L[]; @   V のバッファ長の配列
     short     T[]; @   V の型の配列  (SQLT_CHR,SQLT_NUM,SQLT_INT ..)
                        NOT NULL 制約がないと上位ビットが ON となっている
     short     I[]; @&  標識変数の値を格納するアドレスの配列
     int         F; #   DESCRIBE での選択項目/プレースホルダ配列の数(実際の数)
     char     *S[]; #   選択項目/プレースホルダ名を格納するアドレスの配列
     short     M[];     S のバッファ長の配列
     short     C[]; #   S に実際格納されたバイト数の配列
     char      X[];     標識変数名を格納するアドレス配列
     short     Y[];     X のバッファ長の配列
     short     Z[];     X に実際格納されたバイト数の配列
   };

FAQ

[ 質問 ]

  1. EXEC SQL WHENEVER NOT FOUND DO break;
    を宣言すると、それより下の関数でも有効に見えますが?

  2. #include <ocidfn.h> を入れると以下のエラーが出ます。解決法は?

    行 14、列 10でエラーが発生しました。ファイル dyn4.pc
    #include <ocidfn.h>
    .........1
    PCC-S-02015, 挿入ファイルをオープンできません。
    

  3. カーソルの適用範囲は?

  4. 次の c コンパイラエラーの意味は?

    break statement not within loop or switch

  5. FETCH で「 ORA-01002: フェッチ順序が無効です」のエラーの原因は?
  6. FETCH で「 ORA-00936: 式がありません」のエラーの原因は?
  7. FETCH で NULL 値があると sqlca.sqlerrd[2] に行が正しく設定されないのはどうして?

  8. Pro*c プログラム内で NLS_LANG を変更できますか?

  9. SQLSQLDAAlloc関数を使うと次のエラーがでますが?

    G_bind_dp=(struct SQLDA *)SQLSQLDAAlloc(SQL_SINGLE_RCTX,W_num,W_vlen,W_ilen);
    ..........................1
    PCC-S-02201, 記号")"が見つかりました。
    

  10. 以下のストアードプロシージャを含む pro*c プログラムを proc XXX.pc SQLCHECK=SEMANTICS でコンパイルするとエラーになる。

    EXEC SQL EXECUTE
       BEGIN
          STORED_PROC();
       END;
    END-EXEC;
    


[ 回答 ]
  1. この宣言は、別の "EXEC SQL WHENEVER" 文を指定するまで有効となります。

  2. ocidfn.h は $ORACLE_HOME/rdbms/demo にありますので、 proc のオプションに以下を追加します。
    INCLUDE=$(ORACLE_HOME)/rdbms/demo
    

  3. カーソルの適用範囲は、プリコンパイル単位内です。 従ってあるファイル内で宣言したカーソルを、 別のファイルから OPEN/FETCH/CLOSE 等できません。

  4. エラーの意味は、ループ構造がないのに break したというもので、 EXEC SQL のいずれかの文から、プリプロセッサによって生成されたものです。

    生成された c プログラムのエラー行が以下の場合は、 それぞれ次の EXEC SQL WHENEVER 文を、前で宣言する必要があります。

    1. if (sqlca.sqlcode == 1403) break;
      EXEC SQL WHENEVER NOT FOUND CONTINUE;

    2. if (sqlca.sqlcode < 0) break;
      EXEC SQL WHENEVER SQLERROR CONTINUE;

  5. Pro*C プログラムの中で PREPARE、DECLARE、OPEN、FETCH でエラー処理を行っていない場合に出ます。
    また MODE=ANSI でプリコンパイルすると出る場合があります。
    [KROWN 14480 参照 ]

  6. カラムを指定する際カラム名に予約語を使っているためです。
    [KROWN 22278 参照 ]

  7. oracle 8i/9i のバグです。 標識変数を使って対処してください。
    [KROWN 45288 参照 ]

  8. 次の方法で可能です。

    ret = putenv("NLS_LANG=japanese_japan.JA16SJIS");
    

  9. SQLSQLDAAlloc関数を使う場合は、以下を include します。

    #include <sql2oci.h>
    

  10. SQLCHECK=SEMANTICS でコンパイルする場合、proc はデータベースに ログオンしますので、ストアードプロシージャを作成したユーザを 指定する必要があります。

    proc SQLCHECK=SEMANTICS USER=XXX/XXX iname=XXX