Pro*c は、c プログラムから ORACLE データベースへアクセスするためのプリプロセッサで、 c のプログラムに SQL 文を埋め込むことにより実現できます。 Pro*c プログラムの拡張子は .pc です。
proc(Pro*c プリプロセッサ)は Pro*c プログラムから c のプログラムを生成します。 そのあと c コンパイラでコンパイル、リンクすることにより、 実行モジュールを作ります。
埋込み SQL 文には、宣言文と実行文があります。
宣言文は、oracle オブジェクト、通信領域、SQL 変数を定義するために使い、 c 言語の宣言文を置けるところであれば、どこでも記述可能です。
実行文は、ランタイムライブラリ SQLLIB をコールします。
実行文は、c 言語の実行文を置けるところであれば、どこでも記述可能です。
以下のヘッダファイルはインクルードすべきです。 なぜならエラー情報などが設定されている領域を定義、確保するからです。
sqlca.h は、oracle との通信領域を定義、確保します。 これらは宣言文ですから、プログラム先頭部分に書きます。
EXEC SQL INCLUDE SQLCA;
#include <sqlca.h>
#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;
#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; ユーザ名/パスワードと接続文字列で接続
#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:
EXEC SQL COMMIT RELEASE;
EXEC SQL ROLLBACK RELEASE;
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);
}
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 は、oracle によって定義された型です。
VARCHAR 型は、可変長の文字列を宣言するのに使用します。 VARCHAR は宣言済みの struct と考えてください。
arr に実際の文字列が、len に文字列長が入ります。
VARCHR username[20];
struct {
unsigned short len;
unsigned char arr[20];
} username;
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 文は少し複雑ですので、 次に説明します。
問合せでは、結果として戻される行は次のように分類されます。
上記の条件をすべて満たすプログラムを作るには、 カーソルを使う必要があります。
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 データベース内の値が設定されます。
また、ホスト変数のかわりにホスト配列、 ホスト構造体を使うこともできます。
NOT NULL 制約のないカラムは、値が設定されていない場合があります。 このような場合は、標識変数を使うことで、 それを知ることができます。 標識変数には NULL 以外の切り捨て情報も設定されます。
標識変数は、ホスト変数の次にコロン(:)と標識変数名を続けます。
次の例のホスト変数 commission に対する標識変数は、ind_com です。
EXEC SQL FETCH clerkpeople
INTO :emp_name, :salary ,:commission [INDICATOR] :ind_com;
複数行が返る検索では、カーソルを定義することで行の逐次操作が可能です。 カーソルは、問合せで戻される行の集合体内の現在行を指しています。
カーソルの操作は次の4つで、これらの操作は同一ファイル内になければなりません。
またカーソルの適応範囲はファイル全体ですので、 同一名のカーソルを同一ファイル内で 2 つ以上は宣言できません。
EXEC SQL DECLARE salespeople CURSOR FOR
SELECT ENAME, SAL, COMM FROM EMP WHERE JOB LIKE 'SALES%';
EXEC SQL OPEN salespeople;
EXEC SQL FETCH salespeople INTO :emp_info;
EXEC SQL CLOSE salespeople;
次の場合は、FETCH でエラーとなります。
| 項目 | コ ー ド |
|---|---|
| 宣言 |
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);
}
|
表は次のようなもので、まず構造体を定義します。
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 には、次の4つの方法があり、番号が大きいほど自由度が増します。
生成する文の最後に ";" があってはなりません。
| 方法 | 可能な SQL 文の種類 |
|---|---|
| 1 | ホスト変数を使わない非問合せ |
| 2 | 入力ホスト変数の数がわかっている非問合せ |
| 3 | 選択リスト項目の数と入力ホスト変数の数がわかっている問合せ |
| 4 | 選択リスト項目の数または入力ホスト変数の数が不明な問合せ |
動的 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 は、入力ホスト変数の数がわかっている非問合せで、 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 は、選択リスト項目の数と入力ホスト変数の数がわかっている問合せで、 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 は、選択リスト項目の数または入力ホスト変数の数が不明な問合せで、 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 データ操作文を実行した後は、 必ずその操作が正しく行われたかどうか調べる必要があります。
sqlca(SQL 通信領域構造体)には、すべての SQL
実行文の実行後の状態が格納されます。
sqlca は、次のいずれかでプログラムに組み込むことができます。
EXEC SQL INCLUDE SQLCA
#include <sqlca.h>
実行時のエラー処理には、次の方法があります。
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 でエラーが発生した場合、
無限ループを回避するためです。
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;
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 用のファイル 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 について説明します。
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 を参考にしてください。
| 機 能 | 定 義 |
|---|---|
| 接続 | 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
|
| 用語 | 定 義 |
|---|---|
| ホスト変数 | 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 に実際格納されたバイト数の配列
};
|
[ 質問 ]
行 14、列 10でエラーが発生しました。ファイル dyn4.pc #include <ocidfn.h> .........1 PCC-S-02015, 挿入ファイルをオープンできません。
break statement not within loop or switch
G_bind_dp=(struct SQLDA *)SQLSQLDAAlloc(SQL_SINGLE_RCTX,W_num,W_vlen,W_ilen); ..........................1 PCC-S-02201, 記号")"が見つかりました。
EXEC SQL EXECUTE
BEGIN
STORED_PROC();
END;
END-EXEC;
INCLUDE=$(ORACLE_HOME)/rdbms/demo
生成された c プログラムのエラー行が以下の場合は、 それぞれ次の EXEC SQL WHENEVER 文を、前で宣言する必要があります。
ret = putenv("NLS_LANG=japanese_japan.JA16SJIS");
#include <sql2oci.h>
proc SQLCHECK=SEMANTICS USER=XXX/XXX iname=XXX