Pro*C/C++プログラムでは、問合せにカーソル変数を使用できます。カーソル変数は、OracleサーバーでPL/SQLを使用して定義し、オープンする必要のあるカーソルのハンドルです。カーソル変数の詳細は、Oracle Database PL/SQL言語リファレンスを参照してください。
カーソル変数の利点は、次のとおりです。
メンテナンスのしやすさ
問合せは、カーソル変数をオープンするストアド・プロシージャに集中されます。カーソルを変更する必要がある場合は、ストアド・プロシージャの変更のみで済みます。各アプリケーションを変更する必要はありません。
便利なセキュリティ機能
アプリケーションのユーザーは、Pro*C/C++アプリケーションからサーバーへの接続時に使用されるユーザー名です。ユーザーには、カーソルをオープンするストアド・プロシージャに対する実行権限が必要ですが、問合せに使用する表に対する読取り権限は不要です。このセキュリティ機能を使用して、表内の列や、他のストアド・プロシージャへのアクセスを制限できます。
Pro*C/C++のSQL_CURSOR疑似型を使用して、Pro*C/C++プログラム内でカーソル変数を宣言します。次に例を示します。
EXEC SQL BEGIN DECLARE SECTION;
sql_cursor emp_cursor; /* a cursor variable */
SQL_CURSOR dept_cursor; /* another cursor variable */
sql_cursor *ecp; /* a pointer to a cursor variable */
...
EXEC SQL END DECLARE SECTION;
ecp = &emp_cursor; /* assign a value to the pointer */
カーソル変数を宣言するときに、型指定にはすべて大文字のSQL_CURSOR、あるいはすべて小文字のsql_cursorを使用できます。大文字と小文字の組合せは使用できません。
カーソル変数は、Pro*C/C++プログラム内の他のホスト変数と同様です。カーソル変数にはスコープがあり、C言語のスコープ規則に従っています。カーソル変数は、他の関数にパラメータとして渡すことができ、カーソル変数を宣言しているソース・ファイルの外部にある関数にも渡すことができます。また、カーソル変数を戻す関数のみでなく、カーソル変数へのポインタを戻す関数も定義できます。
カーソル変数をオープンするか、カーソル変数を使用してFETCHする前に、カーソルを割り当てる必要があります。そのためには、新しいプリコンパイラ・コマンドALLOCATEを使用します。たとえば、前述の例で宣言したSQL_CURSOR emp_cursorを割り当てるには、次の文を記述します。
EXEC SQL ALLOCATE :emp_cursor;
カーソルの割当てには、プリコンパイル時も実行時もサーバーのコールは必要ありません。ALLOCATE文に誤り(未宣言のホスト変数など)があると、Pro*C/C++ではプリコンパイル時エラーが発行されます。カーソル変数を割り当てると、ヒープ・メモリーが使用されます。このため、プログラム・ループ内でカーソルを解放できます。カーソル変数に割り当てられるメモリーは、カーソルがクローズされるときには解放されず、明示的なCLOSEが実行されるか、接続がクローズされるときにのみ解放されます。
EXEC SQL CLOSE :emp_cursor;
関連項目:
カーソル変数はOracleデータベース・サーバー上でオープンする必要があります。埋込みSQL OPENコマンドを使用しても、カーソル変数はオープンできません。カーソル変数をオープンするには、カーソルをオープン(および同じ文中で定義)するPL/SQLストアド・プロシージャをコールする方法があります。または、Pro*C/C++プログラム内で無名PL/SQLブロックを使用してカーソル変数をオープンし、定義する方法があります。
たとえば、次のPL/SQLパッケージがデータベースに格納されているとします。
CREATE PACKAGE demo_cur_pkg AS
TYPE EmpName IS RECORD (name VARCHAR2(10));
TYPE cur_type IS REF CURSOR RETURN EmpName;
PROCEDURE open_emp_cur (
curs IN OUT cur_type,
dept_num IN NUMBER);
END;
CREATE PACKAGE BODY demo_cur_pkg AS
CREATE PROCEDURE open_emp_cur (
curs IN OUT cur_type,
dept_num IN NUMBER) IS
BEGIN
OPEN curs FOR
SELECT ename FROM emp
WHERE deptno = dept_num
ORDER BY ename ASC;
END;
END;
このパッケージが格納された後で、Pro*C/C++プログラムからopen_emp_curストアド・プロシージャをコールしてカーソルcursをオープンし、プログラム内のカーソルからFETCHできます。次に例を示します。
...
sql_cursor emp_cursor;
char emp_name[11];
...
EXEC SQL ALLOCATE :emp_cursor; /* allocate the cursor variable */
...
/* Open the cursor on the server side. */
EXEC SQL EXECUTE
begin
demo_cur_pkg.open_emp_cur(:emp_cursor, :dept_num);
end;
;
EXEC SQL WHENEVER NOT FOUND DO break;
for (;;)
{
EXEC SQL FETCH :emp_cursor INTO :emp_name;
printf("%s\n", emp_name);
}
...
Pro*C/C++プログラム内で無名PL/SQLブロックを使用してカーソルをオープンするには、無名ブロック内でカーソルを定義します。次に例を示します。
sql_cursor emp_cursor;
int dept_num = 10;
...
EXEC SQL EXECUTE
BEGIN
OPEN :emp_cursor FOR SELECT ename FROM emp
WHERE deptno = :dept_num;
END;
END-EXEC;
...
前述の各例は、PL/SQLを使用してカーソル変数をオープンする方法を示しています。次のように、埋込みSQL文でCURSOR句を使用してカーソル変数をオープンすることもできます。
...
sql_cursor emp_cursor;
...
EXEC ORACLE OPTION(select_error=no);
EXEC SQL
SELECT CURSOR(SELECT ename FROM emp WHERE deptno = :dept_num)
INTO :emp_cursor FROM DUAL;
EXEC ORACLE OPTION(select_error=yes);
前述の文では、emp_cursorカーソル変数が最も外側のSELECTの最初の列にバインドされています。最初の列は、それ自体が問合せですが、CURSOR(...)変換句が指定されているため、sql_cursorホスト変数と互換性のある形式で表されています。
CURSOR句を含む問合せを使用する場合は、SELECT_ERRORオプションをNOに設定する必要があります。これにより、親カーソルの取消しが禁止され、プログラムをエラーなしで実行できます。
前述の例では、参照カーソルがパッケージ内で定義され、カーソルはそのパッケージ内のプロシージャ内でオープンされました。しかし、参照カーソルは、カーソルをオープンするプロシージャと同じパッケージ内で定義しなくてもかまいません。
スタンドアロン・ストアド・プロシージャ内でカーソルをオープンする必要がある場合は、別のパッケージ内にカーソルを定義し、カーソルをオープンするスタンドアロン・ストアド・プロシージャ内で、そのパッケージを参照できます。次はその例です。
PACKAGE dummy IS
TYPE EmpName IS RECORD (name VARCHAR2(10));
TYPE emp_cursor_type IS REF CURSOR RETURN EmpName;
END;
-- and then define a standalone procedure:
PROCEDURE open_emp_curs (
emp_cursor IN OUT dummy.emp_cursor_type;
dept_num IN NUMBER) IS
BEGIN
OPEN emp_cursor FOR
SELECT ename FROM emp WHERE deptno = dept_num;
END;
END;
カーソル変数をクローズするには、CLOSEコマンドを使用します。たとえば、前述の例でOPENしたemp_cursorカーソル変数をクローズするには、埋込みSQL文を使用します。
EXEC SQL CLOSE :emp_cursor;
カーソル変数はホスト変数であるため、前にコロンを付ける必要があります。
ALLOCATEで割り当てたカーソル変数を再利用できます。アプリケーションで必要な回数だけオープン、FETCHおよびCLOSEできます。ただし、サーバーから切断してから再接続する場合は、カーソル変数をALLOCATEで再度割り当てる必要があります。
カーソルは、FREE埋込みSQL文で割当て解除されます。次に例を示します。
EXEC SQL FREE :emp_cursor;
オープンしているカーソルはクローズされ、割り当てられたメモリーは解放されます。
Pro*C/C++のカーソル変数をOCI関数と共有できます。そのためには、SQLLIB変換関数SQLCDAFromResultSetCursor() (以前のsqlcdat())およびSQLCDAToResultSetCursor(以前のsqlcurt())を使用する必要があります。これらの関数を使用して、OCIのカーソル・データ領域をPro*C/C++のカーソル変数に変換します。
SQLCDAFromResultSetCursor()関数では、割当て済のカーソル変数がOCIカーソル・データ領域に変換されます。構文は次のとおりです。
void SQLCDAFromResultSetCursor(dvoid *context, Cda_Def *cda, void *cur, sword *retval);
パラメータは次のとおりです。
| パラメータ | 説明 |
|---|---|
context |
SQLLIBランタイム・コンテキストへのポインタ |
cda |
変換先のOCIカーソル・データ領域へのポインタ |
cur |
変換元のPro*C/C++カーソル変数へのポインタ |
retval |
エラーがなければ0、それ以外の場合はSQLLIB(SQL)のエラー番号 |
注意:
エラーの場合には、CDAのV2およびrcリターン・コード・フィールドもエラー・コードを受け取ります。CDAの処理済行数フィールドは設定されません。
非スレッドまたはデフォルト・コンテキスト・アプリケーションでは、定義した定数SQL_SINGLE_RCTXをコンテキストとして渡してください。
関連項目:
SQLCDAToResultSetCursor()関数では、OCIのカーソル・データ領域がPro*C/C++のカーソル変数に変換されます。構文は次のとおりです。
void SQLCDAToResultSetCursor(dvoid *context, void *cur, Cda_Def *cda, int *retval);
パラメータは次のとおりです。
| パラメータ | 説明 |
|---|---|
context |
SQLLIBランタイム・コンテキストへのポインタ |
cur |
変換先のPro*C/C++カーソル変数へのポインタ |
cda |
変換元のOCIカーソル・データ領域へのポインタ |
retval |
エラーがなければ0、それ以外の場合はエラー・コード |
注意:
SQLCA構造体は、このルーチンでは更新されません。SQLCAコンポーネントが設定されるのは、変換されたカーソルを使用してデータベース操作が実行された後のみです。
非スレッド・アプリケーションでは、定義した定数SQL_SINGLE_RCTXをコンテキストとして渡してください。
これらの関数に対するANSIとK&Rのプロトタイプは、sql2oci.hヘッダー・ファイルに用意されています。これらの関数をコールする前に、cdaおよびcurのメモリーを割り当てる必要があります。
関連項目:
SQLLIBパブリック関数の詳細は、SQLLIBパブリック関数の新しい名前の表を参照してください。
カーソル変数の使用には、次の制限が適用されます。
Pro*C/C++とOCI V7で同じカーソル変数を使用する場合は、接続直後にSQLLDAGetCurrent()またはSQLLDAGetName()を使用する必要があります。
カーソル変数をOCIリリース8で対応するものには変換できません。
カーソル変数を使用できるコマンドは、ALLOCATE、FETCH、FREEおよびCLOSEのみです。
DECLARE CURSORコマンドは、カーソル変数には適用されません。
CLOSEでクローズしたカーソル変数からのFETCHはできません。
ALLOCATEで割り当てられていないカーソル変数からのFETCHはできません。
MODE=ANSIを指定してプリコンパイルする場合、すでにクローズ済のカーソル変数をクローズするとエラーになります。
AT句は、ALLOCATEコマンド、カーソル変数を参照するFETCHコマンドおよびCLOSEコマンドには使用できません。
カーソル変数はデータベースの列に格納できません。
カーソル変数自体は、パッケージ指定内で宣言できません。パッケージ指定内で宣言できるのは、カーソル変数の型のみです。
カーソル変数は、PL/SQLレコードのコンポーネントにはできません。
次のサンプル・プログラム(PL/SQLスクリプトとPro*C/C++プログラム)は、カーソル変数の使用方法を示しています。これらのソースはdemoディレクトリにあり、オンラインで使用できます。demoディレクトリにある同じアプリケーションの別バージョンcv_demo.pcも参照してください。
-- PL/SQL source for a package that declares and
-- opens a ref cursor
CONNECT SCOTT/TIGER;
CREATE OR REPLACE PACKAGE emp_demo_pkg as
TYPE emp_cur_type IS REF CURSOR RETURN emp%ROWTYPE;
PROCEDURE open_cur(curs IN OUT emp_cur_type, dno IN NUMBER);
END emp_demo_pkg;
CREATE OR REPLACE PACKAGE BODY emp_demo_pkg AS
PROCEDURE open_cur(curs IN OUT emp_cur_type, dno IN NUMBER) IS
BEGIN
OPEN curs FOR SELECT *
FROM emp WHERE deptno = dno
ORDER BY ename ASC;
END;
END emp_demo_pkg;
/*
* Fetch from the EMP table, using a cursor variable.
* The cursor is opened in the stored PL/SQL procedure
* open_cur, in the EMP_DEMO_PKG package.
*
* This package is available on-line in the file
* sample11.sql, in the demo directory.
*
*/
#include <stdio.h>
#include <sqlca.h>
#include <stdlib.h>
#include <sqlda.h>
#include <sqlcpr.h>
/* Error handling function. */
void sql_error(msg)
char *msg;
{
size_t clen, fc;
char cbuf[128];
clen = sizeof (cbuf);
sqlgls((char *)cbuf, (size_t *)&clen, (size_t *)&fc);
printf("\n%s\n", msg);
printf("Statement is--\n%s\n", cbuf);
printf("Function code is %ld\n\n", fc);
sqlglm((char *)cbuf, (size_t *) &clen, (size_t *) &clen);
printf ("\n%.*s\n", clen, cbuf);
EXEC SQL WHENEVER SQLERROR CONTINUE;
EXEC SQL ROLLBACK WORK RELEASE;
exit(EXIT_FAILURE);
}
void main()
{
char temp[32];
EXEC SQL BEGIN DECLARE SECTION;
char *uid = "scott/tiger";
SQL_CURSOR emp_cursor;
int dept_num;
struct
{
int emp_num;
char emp_name[11];
char job[10];
int manager;
char hire_date[10];
float salary;
float commission;
int dept_num;
} emp_info;
struct
{
short emp_num_ind;
short emp_name_ind;
short job_ind;
short manager_ind;
short hire_date_ind;
short salary_ind;
short commission_ind;
short dept_num_ind;
} emp_info_ind;
EXEC SQL END DECLARE SECTION;
EXEC SQL WHENEVER SQLERROR do sql_error("Oracle error");
/* Connect to Oracle. */
EXEC SQL CONNECT :uid;
/* Allocate the cursor variable. */
EXEC SQL ALLOCATE :emp_cursor;
/* Exit the inner for (;;) loop when NO DATA FOUND. */
EXEC SQL WHENEVER NOT FOUND DO break;
for (;;)
{
printf("\nEnter department number (0 to exit): ");
gets(temp);
dept_num = atoi(temp);
if (dept_num <= 0)
break;
EXEC SQL EXECUTE
begin
emp_demo_pkg.open_cur(:emp_cursor, :dept_num);
end;
END-EXEC;
printf("\nFor department %d--\n", dept_num);
printf("ENAME SAL COMM\n");
printf("----- --- ----\n");
/* Fetch each row in the EMP table into the data struct.
Note the use of a parallel indicator struct. */
for (;;)
{
EXEC SQL FETCH :emp_cursor
INTO :emp_info INDICATOR :emp_info_ind;
printf("%s ", emp_info.emp_name);
printf("%8.2f ", emp_info.salary);
if (emp_info_ind.commission_ind != 0)
printf(" NULL\n");
else
printf("%8.2f\n", emp_info.commission);
}
}
/* Close the cursor. */
EXEC SQL WHENEVER SQLERROR CONTINUE;
EXEC SQL CLOSE :emp_cursor;
/* Disconnect from Oracle. */
EXEC SQL ROLLBACK WORK RELEASE;
exit(EXIT_SUCCESS);
}