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

前
次

カーソル変数

Pro*C/C++プログラムでは、問合せにカーソル変数を使用できます。カーソル変数は、OracleサーバーでPL/SQLを使用して定義し、オープンする必要のあるカーソルのハンドルです。カーソル変数の詳細は、Oracle Database PL/SQL言語リファレンスを参照してください。

カーソル変数の利点は、次のとおりです。

カーソル変数の宣言

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言語のスコープ規則に従っています。カーソル変数は、他の関数にパラメータとして渡すことができ、カーソル変数を宣言しているソース・ファイルの外部にある関数にも渡すことができます。また、カーソル変数を戻す関数のみでなく、カーソル変数へのポインタを戻す関数も定義できます。

注意:

SQL_CURSORは、C言語の構造体としてPro*C/C++で生成されるコードに実装されます。したがって、常にポインタを使用して他の関数に渡したり、関数からカーソル変数へのポインタを戻したりすることができます。ただし、SQL_CURSORを値で渡す、あるいは戻すことができるのは、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;

戻り型

PL/SQLストアド・プロシージャ内に参照カーソルを定義する場合は、カーソルが戻す値の型を宣言する必要があります。参照カーソルの型とその戻り型の詳細は、Oracle Database PL/SQL言語リファレンスを参照してください。

カーソル変数のクローズと解放

カーソル変数をクローズするには、CLOSEコマンドを使用します。たとえば、前述の例でOPENしたemp_cursorカーソル変数をクローズするには、埋込みSQL文を使用します。

EXEC SQL CLOSE :emp_cursor;

カーソル変数はホスト変数であるため、前にコロンを付ける必要があります。

ALLOCATEで割り当てたカーソル変数を再利用できます。アプリケーションで必要な回数だけオープン、FETCHおよびCLOSEできます。ただし、サーバーから切断してから再接続する場合は、カーソル変数をALLOCATEで再度割り当てる必要があります。

カーソルは、FREE埋込みSQL文で割当て解除されます。次に例を示します。

EXEC SQL FREE :emp_cursor;

オープンしているカーソルはクローズされ、割り当てられたメモリーは解放されます。

OCIでのカーソル変数の使用(リリース7のみ)

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で対応するものには変換できません。

  • カーソル変数は動的SQLでは使用できません。

  • カーソル変数を使用できるコマンドは、ALLOCATE、FETCH、FREEおよびCLOSEのみです。

  • DECLARE CURSORコマンドは、カーソル変数には適用されません。

  • CLOSEでクローズしたカーソル変数からのFETCHはできません。

  • ALLOCATEで割り当てられていないカーソル変数からのFETCHはできません。

  • MODE=ANSIを指定してプリコンパイルする場合、すでにクローズ済のカーソル変数をクローズするとエラーになります。

  • AT句は、ALLOCATEコマンド、カーソル変数を参照するFETCHコマンドおよびCLOSEコマンドには使用できません。

  • カーソル変数はデータベースの列に格納できません。

  • カーソル変数自体は、パッケージ指定内で宣言できません。パッケージ指定内で宣言できるのは、カーソル変数の型のみです。

  • カーソル変数は、PL/SQLレコードのコンポーネントにはできません。

例: cv_demo.sqlおよびsample11.pc

次のサンプル・プログラム(PL/SQLスクリプトとPro*C/C++プログラム)は、カーソル変数の使用方法を示しています。これらのソースはdemoディレクトリにあり、オンラインで使用できます。demoディレクトリにある同じアプリケーションの別バージョンcv_demo.pcも参照してください。

cv_demo.sql

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

sample11.pc

/*
 *  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);

}