この章では、PL/SQLプログラムで使用できる、柔軟なエラー・トラップとエラー処理について説明します。
PL/SQLにおけるエラー処理と例外の詳細は、『Oracle Database PL/SQL言語リファレンス』のPL/SQLエラーの処理に関する説明を参照してください。
TimesTen固有の考慮事項の詳細は、章の最後を参照してください。
この章の内容は次のとおりです。
この項では、PL/SQLプログラミングにおける例外の概要を説明します。内容は次のとおりです。
例外とは、プログラムの実行中に(TimesTenによって暗黙的に、またはプログラムによって明示的に)呼び出されるPL/SQLエラーのことです。例外を処理するには、ハンドラでトラップするか、またはコール元の環境に伝播します。
たとえば、SELECT
文が複数の行を戻す場合、TimesTenは実行時にエラー(例外)を戻します。次の例に示すとおり、TimesTenエラー8507が表示され、次に関連するORA
エラー・メッセージが表示されます。(Oracle Databaseで元々定義されているORA
メッセージが、TimesTenでも同様に実装されています。)
Command> DECLARE > v_lname VARCHAR2 (15); > BEGIN > SELECT last_name INTO v_lname > FROM employees > WHERE first_name = 'John'; > DBMS_OUTPUT.PUT_LINE ('Last name is :' || v_lname); > END; > / 8507: ORA-01422: exact fetch returns more than requested number of rows 8507: ORA-06512: at line 4 The command failed.
プログラムが正常に完了するように、このような例外をPL/SQLブロックで処理できます。次に例を示します。
Command> DECLARE > v_lname VARCHAR2 (15); > BEGIN > SELECT last_name INTO v_lname > FROM employees > WHERE first_name = 'John'; > DBMS_OUTPUT.PUT_LINE ('Last name is :' || v_lname); > EXCEPTION > WHEN TOO_MANY_ROWS THEN > DBMS_OUTPUT.PUT_LINE (' Your SELECT statement retrieved multiple > rows. Consider using a cursor.'); > END; > / Your SELECT statement retrieved multiple rows. Consider using a cursor. PL/SQL procedure successfully completed.
例外には、次の3つのタイプがあります。
事前定義例外: PL/SQLで定義されているエラー条件です。
未定義例外: 標準のTimesTenエラーなどがあります。
ユーザー定義例外: アプリケーション固有の例外です。
TimesTenでは、次の3つのタイプの例外がOracle Databaseと同様に使用されます。
例外 | 説明 | 処理方法 |
---|---|---|
事前定義のTimesTenエラー | PL/SQLコードで最もよく発生する約20個のエラーの1つ | これらの例外は、宣言する必要がありません。TimesTenで事前に定義されています。エラーはTimesTenによって暗黙的に呼び出されます。 |
未定義のTimesTenエラー | その他の標準のTimesTenエラー | これらは、アプリケーションの宣言部で宣言する必要があります。エラーはTimesTenによって暗黙的に呼び出され、例外ハンドラを使用して捕捉できます。 |
ユーザー定義のエラー | アプリケーションによって定義され、呼び出されるエラー | これらは、宣言部で宣言する必要があります。開発者が明示的に例外を呼び出します。 |
この項では、事前定義のTimesTenエラーまたはユーザー定義のエラーをトラップする方法について説明します。
事前定義のTimesTenエラーをトラップするには、例外処理ルーチンで事前定義の名前を参照します。PL/SQLでは、STANDARD
パッケージで事前定義例外が宣言されています。
表4-1に、TimesTenでサポートされている事前定義例外、関連付けられているORA
エラー番号とSQLCODE
値、および例外の説明を示します。
「サポートされていない事前定義エラー」も参照してください。
表4-1 事前定義例外
例外名 | Oracle Databaseエラー番号 | SQLCODE | 説明 |
---|---|---|---|
|
|
-6530 |
初期化されていないオブジェクトの属性に値を割り当てようとしました。 |
|
|
-6592 |
|
|
|
-6531 |
EXISTS以外のコレクション・メソッドを初期化されていないネストした表やVARRAYに適用しようとしたか、または初期化されていないネストした表やVARRAYの要素に値を割り当てようとしました。 |
|
|
-6511 |
すでにオープンされているカーソルをオープンしようとしました。 |
|
|
-1 |
プログラムは一意索引によって制約されている列に重複値を挿入しようとしました。 |
|
|
-1001 |
不正なカーソル操作があります。 |
|
|
-1722 |
文字列から数値への変換に失敗しました。 |
|
|
+100 |
単一行の |
|
|
-6501 |
PL/SQLに内部的な問題が発生しました。 |
|
|
-6504 |
代入文のホスト・カーソル変数とPL/SQLカーソル変数の戻り型に互換性がありません。 |
|
|
-6500 |
PL/SQLのメモリーが不足しているか、メモリーが破損しています。 |
|
|
-6533 |
コレクション内の要素の数より大きい索引番号を使用して、ネストした表またはVARRAYを参照しました。 |
|
|
-6532 |
有効範囲外の索引番号(たとえば-1)を使用して、ネストした表またはVARRAYを参照しました。 |
|
|
-1410 |
文字列が |
|
|
-1422 |
単一行 |
|
|
-6502 |
算術、変換、切捨てまたはサイズ制限のエラーが発生しました。 |
|
|
-1476 |
数値を0(ゼロ)で割ろうとしました。 |
例4-1 事前定義例外ZERO_DIVIDEの使用
この例のPL/SQLプログラムでは、数値を0で割ろうとします。事前定義例外ZERO_DIVIDE
を使用して、例外処理ルーチン内でエラーをトラップします。
Command> DECLARE v_invalid PLS_INTEGER; > BEGIN > v_invalid := 100/0; > EXCEPTION > WHEN ZERO_DIVIDE THEN > DBMS_OUTPUT.PUT_LINE ('Attempt to divide by 0'); > END; > / Attempt to divide by 0 PL/SQL procedure successfully completed.
TimesTenのPL/SQLでは、ユーザー独自の例外を定義することができ、またPL/SQL RAISE
文またはRAISE_APPLICATION_ERROR
プロシージャで明示的にユーザー定義の例外を発生させることができます。
RAISE
文を使用すると、PL/SQLブロックまたはサブプログラムの通常の実行を停止し、制御を例外ハンドラに渡すことができます。RAISE
文では、事前定義例外または自分で名前を付けたユーザー定義例外を呼び出すことができます。
この例では、部門番号500は存在しないため、departments
表内の行は更新されません。RAISE
文を使用して例外を明示的に呼び出し、SQLERRM
組込みファンクションによって戻されるエラー・メッセージとSQLCODE
組込みファンクションによって戻されるエラー・コードを表示します。例外ハンドラ内で、RAISE
文を単独で使用して同じ例外を再度呼び出し、コール元の環境に伝播します。
Command> DECLARE > v_deptno NUMBER := 500; > v_name VARCHAR2 (20) := 'Testing'; > e_invalid_dept EXCEPTION; > BEGIN > UPDATE departments > SET department_name = v_name > WHERE department_id = v_deptno; > IF SQL%NOTFOUND THEN > RAISE e_invalid_dept; > END IF; > ROLLBACK; > EXCEPTION > WHEN e_invalid_dept THEN > DBMS_OUTPUT.PUT_LINE ('No such department'); > DBMS_OUTPUT.PUT_LINE (SQLERRM); > DBMS_OUTPUT.PUT_LINE (SQLCODE); > END; > / No such department User-Defined Exception 1 PL/SQL procedure successfully completed. The command succeeded.
注意: TimesTenとOracle Databaseでエラー条件が同じ場合、SQLCODE によって戻されるエラー・コードは同じですが、SQLERRM によって戻されるエラー・メッセージは同じとはかぎりません。このことについては、「TimesTenエラー・メッセージおよびSQLコード」でも説明しています。 |
RAISE_APPLICATION_ERROR
プロシージャは、PL/SQLプログラムの実行可能セクションまたは例外セクション(あるいはその両方)で使用します。TimesTenによってアプリケーションにエラーがレポートされるため、未処理例外を戻すのを回避できます。
-20,000から-20,999の範囲のエラー番号を使用します。メッセージには最大2,048バイトの文字列を指定します。
例4-3 RAISE_APPLICATION_ERRORプロシージャの使用
この例では、employees
表からlast_name=Patterson
のデータを削除しようとします。RAISE_APPLICATION_ERROR
プロシージャで、エラー番号-20201を使用してエラーを呼び出します。
Command> DECLARE > v_last_name employees.last_name%TYPE := 'Patterson'; > BEGIN > DELETE FROM employees WHERE last_name = v_last_name; > IF SQL%NOTFOUND THEN > RAISE_APPLICATION_ERROR (-20201, v_last_name || ' does not exist'); > END IF; > END; > / 8507: ORA-20201: Patterson does not exist 8507: ORA-06512: at line 6 The command failed.
ttIsql
でshow errors
コマンドを使用すると、無名ブロックの実行時、あるいはパッケージ、プロシージャまたはファンクションのコンパイル時に発生するエラーの詳細を表示できます。この例を例4-4に示します。
例4-4 ttIsqlのエラー表示コマンド
もう一度、例2-17を考えてみます。そこで示されていたのと同じパッケージ仕様(プロシージャおよびファンクションhire_employee
、remove_employee
およびnum_above_salary
を宣言)を想定します。ただし、そこで示されていた本体の定義のかわりに、次のようにhire_employee
およびnum_above_salary
(remove_employee
は除外)の定義を検討します。
CREATE OR REPLACE PACKAGE BODY emp_actions AS -- Code for procedure hire_employee: PROCEDURE hire_employee (employee_id NUMBER, last_name VARCHAR2, first_name VARCHAR2, email VARCHAR2, phone_number VARCHAR2, hire_date DATE, job_id VARCHAR2, salary NUMBER, commission_pct NUMBER, manager_id NUMBER, department_id NUMBER) IS BEGIN INSERT INTO employees VALUES (employee_id, last_name, first_name, email, phone_number, hire_date, job_id, salary, commission_pct, manager_id, department_id); END hire_employee; -- Code for function num_above_salary: FUNCTION num_above_salary (emp_id NUMBER) RETURN NUMBER IS emp_sal NUMBER(8,2); num_count NUMBER; BEGIN SELECT salary INTO emp_sal FROM employees WHERE employee_id = emp_id; SELECT COUNT(*) INTO num_count FROM employees WHERE salary > emp_sal; RETURN num_count; END num_above_salary; END emp_actions; /
元々のパッケージ仕様の後でこの本体の定義を試みると、次のようになります。
Warning: Package body created with compilation errors.
さらに情報を取得するには、ttIsql
を実行して、コマンドshow errors
を使用します。この例では、show errors
の結果は次のようになります。
Command> show errors; Errors for PACKAGE BODY EMP_ACTIONS: LINE/COL ERROR -------- ----------------------------------------------------------------- 13/13 PLS-00323: subprogram or cursor 'REMOVE_EMPLOYEE' is declared in a package specification and must be defined in the package body
TimesTen PL/SQLとOracle Database PL/SQLで異なるエラー関連の動作に注意する必要があります。
トランザクションの途中でPL/SQLが実行され、PL/SQLの実行中に未処理例外が発生した場合、TimesTen PL/SQLの処理は、Oracle Database PL/SQLでの処理と異なります。Oracle Databaseは無名ブロックの先頭までロールバックします。TimesTenはロールバックしません。
次の例に示すとおり、PL/SQLブロックの実行の結果発生した例外を常にアプリケーションで処理し、自動コミットを無効にしてアプリケーションを実行する必要があります。
create table mytable (num int not null primary key); set serveroutput on insert into mytable values(1); begin insert into mytable values(2); insert into mytable values(1); exception when dup_val_on_index then dbms_output.put_line('oops:' || sqlerrm); rollback; end; select * from mytable; commit;
2番目のINSERT
は、値が一意である必要があるため失敗し、これにより、例外が発生して、プログラムはロールバックを実行します。TimesTenでは、これを実行すると次のような結果になります。
oops:TT0907: Unique constraint (MYTABLE) violated at Rowid <BMUFVUAAABQAAAADjq> select * from mytable; 0 rows found.
結果はOracle Databaseの場合と同様で、SELECT
の結果に行は表示されません。
ここで、例外が処理されないTimesTenの例を考えてみます。自動コミットを無効にし、この例を再実行します。
create table mytable (num int not null primary key); set serveroutput on insert into mytable values(1); begin insert into mytable values(2); insert into mytable values(1); end; select * from mytable; commit;
TimesTenでは、SELECT
問合せは最初の2つの挿入の実行を指示します。
907: Unique constraint (MYTABLE) violated at Rowid <BMUFVUAAABQAAAADjq> 8507: ORA-06512: at line 3 The command failed. select * from mytable; < 1 > < 2 > 2 rows found.
これをOracle Databaseで実行すると、PL/SQLブロックの先頭にロールバックされるため、SELECT
の結果は最初の挿入のみの実行を示します。
ORA-00001: unique constraint (SYSTEM.SYS_C004423) violated ORA-06512: at line 3 NUM ---------- 1
注意:
|
エラー条件が同じ場合、TimesTenでは、TimesTenが戻すエラー・メッセージとOracle Databaseが戻すメッセージが同じになることは保証されませんが、SQLコードは同じになります。そのため、SQLERRM
ファンクションによって戻される情報は異なる場合がありますが、SQLCODE
ファンクションによって戻される情報は同じになります。
詳細は、次の項または例を参照してください。
例4-2では、SQLERRM
およびSQLCODE
を使用します。
特定のTimesTenエラー・メッセージの詳細は、『Oracle TimesTen In-Memory Databaseエラー・メッセージおよびSNMPトラップ』の警告とエラーに関する説明を参照してください。
一般的な情報は、『Oracle Database PL/SQL言語リファレンス』のSQLERRMファンクションおよびSQLCODEファンクションに関する説明を参照してください。
Oracle Databaseには実行時警告の概念がないため、Oracle Database PL/SQLでは警告はサポートされていません。
TimesTenには警告の概念がありますが、TimesTen PL/SQLの実装はOracle Database PL/SQLの実装に基づいているため、TimesTen PL/SQLでは警告はサポートされていません。
その結果、TimesTenではSQL文を実行してその結果発生する警告を表示できますが、同じ文をPL/SQLから実行した場合には、警告は表示されません。
「事前定義のTimesTenエラーのトラップ」には、TimesTenでサポートされている事前定義例外、関連付けられているORA
エラー番号とSQLCODE
値、および例外の説明が示されています。
表4-2に、TimesTenでサポートされていない事前定義例外を示します。
表4-2 TimesTenでサポートされていない事前定義例外
例外名 | Oracle Databaseエラー番号 | SQLCODE | 説明 |
---|---|---|---|
|
|
-1017 |
ユーザー名およびパスワードが無効です。 |
|
|
-1012 |
プログラムがデータベースに接続せずにデータベース・コールを発行しました。 |
|
|
-30625 |
プログラムが |
|
|
-51 |
データベースでリソースを待機している間に、タイムアウトが発生しました。 |
TimesTen PL/SQLの実装では、PL/SQLプログラムのコンパイル時に、Oracle Database SQLパーサーが使用されます。(これは、「TimesTenにおけるPL/SQLとOracle DatabaseにおけるPL/SQL」で説明されています。)そのため、TimesTenでサポートされていないOracle Databaseの構文または組込みプロシージャをプログラムで使用しても、コンパイル中は問題が検出されません。ただし、プログラムの実行中に実行時エラーが発生します。
TimesTen SQLには、Oracle Database SQLにはない構造がいくつか含まれています。PL/SQL言語には、これらの構造は含まれていません。PL/SQLからTimesTen固有のSQLを実行するには、EXECUTE IMMEDIATE
文を使用してSQL文を実行します。これによりコンパイル・エラーを回避します。
TimesTen固有のSQLおよび式のリストについては、『Oracle TimesTen Application-Tier Database Cacheユーザーズ・ガイド』のTimesTenおよびOracle Databases間の互換性に関する説明を参照してください。
EXECUTE IMMEDIATE
の詳細は、「PL/SQLにおける動的SQL(EXECUTE IMMEDIATE文)」を参照してください。