無名ブロックとは異なり、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ブロックまたはCALL埋込みSQL文のいずれかを使用できます。
次の例では、raise_salaryという名前のスタンドアロン・プロシージャをコールします。
EXEC SQL EXECUTE BEGIN raise_salary(:emp_id, :increase); END; END-EXEC;
ストアド・サブプログラムにパラメータを組み込めることに注意してください。この例では、実パラメータemp_idおよびincreaseはC言語のホスト変数です。
次の例では、プロシージャraise_salaryがemp_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;
前述の埋込み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のいずれかを使用できます。
次に示すように、入力された整数を受け取り、その階乗を整数で戻す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アドバンスト・アプリケーション開発者ガイドを参照してください。