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

前
次

ストアドPL/SQLおよびJavaサブプログラム

無名ブロックとは異なり、PL/SQLサブプログラム(プロシージャおよびファンクション)およびJavaメソッドは別個にコンパイルされ、Oracleデータベースに格納されて起動されます。

Javaメソッドの作成方法の詳細は、『Oracle Database Java開発者ガイド』を参照してください。

SQL*PlusなどのOracleツールを使用して明示的に作成したサブプログラムを、ストアド・サブプログラムと呼びます。ストアド・サブプログラムは、一度コンパイルしてデータ・ディクショナリに格納しておくと、再コンパイルしなくても再実行できるデータベース・オブジェクトになります。

PL/SQLブロック内のサブプログラムまたはストアド・プロシージャがアプリケーションによってOracleに送られると、それはインライン・サブプログラムと呼ばれます。Oracleは、インライン・サブプログラムをコンパイルし、システム・グローバル領域(SGA)にキャッシュしますが、データ・ディクショナリへのソース・コードまたはオブジェクト・コードの格納はしません。

パッケージ内で定義されているサブプログラムは、そのパッケージの一部とみなされ、パッケージ・サブプログラムと呼ばれます。パッケージ内で定義されていないストアド・サブプログラムは、スタンドアロン・プログラムと呼ばれます。

ストアド・サブプログラムの作成について

次の例に示すように、SQL文CREATE FUNCTION、CREATE PROCEDUREおよびCREATE PACKAGEをホスト・プログラムに埋め込むことができます。

EXEC SQL CREATE 
FUNCTION sal_ok (salary REAL, title CHAR) 
RETURN BOOLEAN AS 
min_sal  REAL; 
max_sal  REAL; 
  BEGIN 
    SELECT losal, hisal INTO min_sal, max_sal 
        FROM sals 
        WHERE job = title; 
    RETURN (salary >= min_sal) AND 
           (salary <= max_sal); 
  END sal_ok; 
END-EXEC; 

埋込みCREATE {FUNCTION | PROCEDURE | PACKAGE}文は混成であることに注意してください。他のすべてのCREATE埋込み文と同様、キーワードEXEC SQL (EXEC SQL EXECUTEではありません)で始まります。ただし、PL/SQL終了記号のEND-EXECで終了する点は、他のCREATE埋込み文とは異なります。

次の例では、EMP表からひとまとまりの行をフェッチするget_employeesというプロシージャを含むパッケージを作成します。バッチ・サイズは、プロシージャのコール元(別のストアド・サブプログラムの場合もあれば、クライアント・アプリケーションの場合もある)によって決められます。

プロシージャでは、3つのPL/SQL表をOUT仮パラメータとして宣言し、その後雇用データのバッチをPL/SQL表にフェッチします。一致する実パラメータはホスト配列です。プロシージャの終了時には、PL/SQL表のすべての行の値が、ホスト配列の対応する要素に自動的に割り当てられます。

EXEC SQL CREATE OR REPLACE PACKAGE emp_actions AS 
    TYPE CharArrayTyp IS TABLE OF VARCHAR2(10) 
        INDEX BY BINARY_INTEGER; 
    TYPE NumArrayTyp IS TABLE OF FLOAT 
        INDEX BY BINARY_INTEGER; 
  PROCEDURE get_employees( 
    dept_number IN     INTEGER, 
    batch_size  IN     INTEGER, 
    found       IN OUT INTEGER, 
    done_fetch  OUT    INTEGER, 
    emp_name    OUT    CharArrayTyp, 
    job_title   OUT    CharArrayTyp, 
    salary      OUT    NumArrayTyp); 
  END emp_actions; 
END-EXEC;
EXEC SQL CREATE OR REPLACE PACKAGE BODY emp_actions AS 
 
    CURSOR get_emp (dept_number IN INTEGER) IS 
        SELECT ename, job, sal FROM emp 
            WHERE deptno = dept_number; 
 
  PROCEDURE get_employees( 
    dept_number IN     INTEGER, 
    batch_size  IN     INTEGER, 
    found       IN OUT INTEGER, 
    done_fetch  OUT    INTEGER, 
    emp_name    OUT    CharArrayTyp, 
    job_title   OUT    CharArrayTyp, 
    salary      OUT    NumArrayTyp) IS 
 
  BEGIN 
    IF NOT get_emp%ISOPEN THEN 
        OPEN get_emp(dept_number); 
    END IF; 
    done_fetch := 0; 
    found := 0; 
    FOR i IN 1..batch_size LOOP 
        FETCH get_emp INTO emp_name(i), 
        job_title(i), salary(i); 
        IF get_emp%NOTFOUND THEN 
            CLOSE get_emp; 
            done_fetch := 1; 
            EXIT; 
        ELSE 
            found := found + 1; 
        END IF; 
    END LOOP; 
  END get_employees; 
END emp_actions; 
END-EXEC; 

CREATE文でREPLACE句を指定すると、パッケージの削除、再作成および権限再付与を実行しなくても既存のパッケージを再定義できます。CREATE文の構文の詳細は、『Oracle Database SQL言語リファレンス』を参照してください。

埋込みCREATE {FUNCTION | PROCEDURE | PACKAGE}文が失敗した場合、Oracleはエラーではなく警告を発行します。

ストアドPL/SQLまたはJavaサブプログラムのコールについて

ホスト・プログラムからストアド・サブプログラムをコールするには、無名PL/SQLブロックまたはCALL埋込みSQL文のいずれかを使用できます。

無名PL/SQLブロック

次の例では、raise_salaryという名前のスタンドアロン・プロシージャをコールします。

EXEC SQL EXECUTE 
  BEGIN 
    raise_salary(:emp_id, :increase); 
  END; 
END-EXEC; 

ストアド・サブプログラムにパラメータを組み込めることに注意してください。この例では、実パラメータemp_idおよびincreaseはC言語のホスト変数です。

次の例では、プロシージャraise_salaryemp_actionsの名前のパッケージに格納されます。したがって、プロシージャ・コールを完全に修飾するにはドット表記法を使用する必要があります。

EXEC SQL EXECUTE 
BEGIN 
    emp_actions.raise_salary(:emp_id, :increase); 
END; 
END-EXEC; 

IN実パラメータには、リテラル、スカラー・ホスト変数、ホスト配列、PL/SQL定数、PL/SQL変数、PL/SQL表、PL/SQLユーザー定義レコード、プロシージャ・コールまたは式を使用できます。しかし、OUT実パラメータは、リテラル、プロシージャ・コールまたは式にしないでください。

埋込みPL/SQLブロックとともにプリコンパイラ・オプションSQLCHECK=SEMANTICSを使用する必要があります。

次の例では、仮パラメータのうち3つがPL/SQL表であり、対応する実パラメータはホスト配列です。プログラムでは、ストアド・プロシージャget_employeesを繰り返しコールし、データがなくなるまで、従業員データの各バッチを表示します。このプログラムは、demoディレクトリのsample9.pcファイルに入っており、オンラインで利用できます。CALLDEMOストアド・パッケージ作成用のSQLスクリプトは、calldemo.sqlファイルに入っています。

/*************************************************************
Sample Program 9:  Calling a stored procedure

This program connects to ORACLE using the SCOTT/TIGER
account.  The program declares several host arrays, then
calls a PL/SQL stored procedure (GET_EMPLOYEES in the
CALLDEMO package) that fills the table OUT parameters.  The
PL/SQL procedure returns up to ASIZE values.

Sample9 keeps calling GET_EMPLOYEES, getting ASIZE arrays
each time, and printing the values, until all rows have been
retrieved.  GET_EMPLOYEES sets the done_flag to indicate "no
more data."
*************************************************************/
#include <stdio.h>
#include <string.h>

EXEC SQL INCLUDE sqlca.h;


typedef char asciz[20];
typedef char vc2_arr[11];

EXEC SQL BEGIN DECLARE SECTION;
/* User-defined type for null-terminated strings */
EXEC SQL TYPE asciz  IS STRING(20) REFERENCE;

/* User-defined type for a VARCHAR array element. */
EXEC SQL TYPE vc2_arr IS VARCHAR2(11) REFERENCE;

asciz     username;
asciz     password;
int       dept_no;              /* which department to query? */
vc2_arr   emp_name[10];            /* array of returned names */
vc2_arr   job[10];
float     salary[10];
int       done_flag;
int       array_size;
int       num_ret;                 /* number of rows returned */
EXEC SQL END DECLARE SECTION;

long      SQLCODE;



void print_rows();            /* produces program output      */
void sql_error();             /* handles unrecoverable errors */



main()
{
   int   i;
   char  temp_buf[32];

/* Connect to ORACLE. */
   EXEC SQL WHENEVER SQLERROR DO sql_error();
   strcpy(username, "scott");
   strcpy(password, "tiger");
   EXEC SQL CONNECT :username IDENTIFIED BY :password;
   printf("\nConnected to ORACLE as user: %s\n\n", username);
   printf("Enter department number: ");
   gets(temp_buf);
   dept_no = atoi(temp_buf);/* Print column headers. */
   printf("\n\n");
   printf("%-10.10s%-10.10s%s\n", "Employee", "Job", "Salary");
   printf("%-10.10s%-10.10s%s\n", "--------", "---", "------");

/* Set the array size. */
   array_size = 10;

   done_flag = 0;
   num_ret = 0;

/*  Array fetch loop.
 *  The loop continues until the OUT parameter done_flag is set.
 *  Pass in the department number, and the array size--
 *  get names, jobs, and salaries back.
 */
   for (;;)
   {
      EXEC SQL EXECUTE 
         BEGIN calldemo.get_employees
            (:dept_no, :array_size, :num_ret, :done_flag,
             :emp_name, :job, :salary);
         END;
      END-EXEC;

      print_rows(num_ret);

      if (done_flag)
         break;
   }

/* Disconnect from the database. */
   EXEC SQL COMMIT WORK RELEASE;
   exit(0);
}
void
print_rows(n)
int n;
{
   int i;

    if (n == 0)
    {
        printf("No rows retrieved.\n");
        return;
    }

    for (i = 0; i < n; i++)
        printf("%10.10s%10.10s%6.2f\n",
               emp_name[i], job[i], salary[i]);
}

/* Handle errors. Exit on any error. */
void
sql_error()
{
   char msg[512];
   int buf_len, msg_len;


   EXEC SQL WHENEVER SQLERROR CONTINUE;

   buf_len = sizeof(msg);
   sqlglm(msg, &buf_len, &msg_len);

   printf("\nORACLE error detected:");
   printf("\n%.*s \n", msg_len, msg);

   EXEC SQL ROLLBACK WORK RELEASE;
   exit(1);
}

それぞれの実パラメータのデータ型は、対応する仮パラメータのデータ型に変換可能であることが必要です。また、ストアド・プロシージャの終了前には、すべてのOUT仮パラメータは割り当てられた値である必要があります。そうしないと、対応する実パラメータの値が未確定になります。

無名PL/SQLブロックを使用する場合、SQLCHECK=SEMANTICSは必須です。

リモート・アクセス

PL/SQLを使用すると、データベース・リンクを使用してリモート・データベースにアクセスできます。一般的に、データベース・リンクは、データベース管理者(DBA)によって設定され、Oracleデータ・ディクショナリに格納されます。データベース・リンクは、リモート・データベースの位置、リモート・データベースへのパス、使用するOracleユーザー名およびパスワードをOracleに伝えます。次の例では、データベース・リンクdallasを使用して、raise_salaryプロシージャをコールします。

EXEC SQL EXECUTE 
BEGIN 
    raise_salary@dallas(:emp_id, :increase); 
END; 
END-EXEC; 

次の例に示すように、シノニムを作成して、リモート・サブプログラムに位置の透過性を与えることができます。

CREATE PUBLIC SYNONYM raise_salary 
FOR raise_salary@dallas; 

CALL文

前述の埋込みPL/SQLブロックの概念はCALL文にも当てはまります。CALL埋込みSQL文の書式は次のようになります。

EXEC SQL 
   CALL [schema.] [package.]stored_proc[@db_link](arg1, ...) 
   [INTO :ret_var [[INDICATOR]:ret_ind]] ;

where

スキーマ

プロシージャを含むスキーマ

package

プロシージャを含むパッケージ

stored_proc

コールするJavaまたはPL/SQLストアド・プロシージャ

db_link

オプションのリモート・データベース・リンク

arg1...

引き渡す一連の引数(変数、リテラル、式)

ret_var

結果を受け取るオプションのホスト変数

ind_var

ret_varのオプションの標識変数。

CALL文には、SQLCHECK=SYNTAXまたはSEMANTICSのいずれかを使用できます。

CALLの例

次に示すように、入力された整数を受け取り、その階乗を整数で戻すPL/SQLファンクションfact(パッケージmathpkgに格納されています)を作成済とします。

     EXEC SQL CREATE OR REPLACE PACKAGE BODY mathpkg as 
       function fact(n IN INTEGER) RETURN INTEGER AS
         BEGIN
           IF (n <= 0) then return 1;
           ELSE return n * fact(n - 1);
           END IF;
         END fact;
       END mathpkge;
     END-EXEC.

次に、そのCALL文を使用しているPro*C/C++アプリケーションで、factを使用します。

 ...
int num, fact;
...
EXEC SQL CALL mathpkge.fact(:num) INTO :fact ;
...

関連項目:

  • CALL文の詳細は、CALL(実行可能埋込みSQL)を参照してください。

  • 引数の渡し方およびその他の問題の詳細は、『Oracle Databaseアドバンスト・アプリケーション開発者ガイド』を参照してください。

ストアド・サブプログラムに関する情報を得る方法について

注意:

ログイン・データ領域(LDA)は、Oracleではサポートされなくなりました。Oracleの次のバージョンでは、Pro*C/C++プログラムにOCIリリース7コールを埋め込む機能は廃止になります。

データ型とホスト変数では、ホスト・プログラムにOCIコールを埋め込む方法を説明しています。ライブラリ・ルーチンSQLLDAをコールしてLDAを設定すると、OCIコールodesspを使用して、ストアド・サブプログラムに関する有益な情報を取得できます。odesspをコールするときには、有効なLDAとサブプログラムの名前を渡す必要があります。パッケージ・サブプログラムの場合は、パッケージ名も渡す必要があります。odesspからは、各サブプログラム・パラメータに関する情報(データ型、サイズ、位置など)が戻されます。詳細は、『Oracle Call Interfaceプログラマーズ・ガイド』を参照してください。

DBMS_DESCRIBEパッケージでは、DESCRIBE_PROCEDUREストアド・プロシージャを使用できます。このプロシージャの詳細は、Oracle Databaseアドバンスト・アプリケーション開発者ガイドを参照してください。