主コンテンツへ
Oracle® TimesTen In-Memory Database PL/SQL開発者ガイド
リリース18.1
E98629-04
  目次へ移動
目次
索引へ移動
索引

前
 
次
 

4 エラーと例外の処理

この章では、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エラーのトラップ

事前定義のTimesTenエラーをトラップするには、例外処理ルーチンで事前定義の名前を参照します。PL/SQLでは、STANDARDパッケージで事前定義例外が宣言されています。

表4-1に、TimesTenでサポートされている事前定義例外、関連付けられているORAエラー番号とSQLCODE値、および例外の説明を示します。

「サポートされていない事前定義エラー」も参照してください。

表4-1 事前定義例外

例外名 Oracle Databaseエラー番号 SQLCODE 説明

ACCESS_INTO_NULL

ORA-06530

-6530

初期化されていないオブジェクトの属性に値を割り当てようとしました。

CASE_NOT_FOUND

ORA-06592

-6592

CASE文のWHEN句で何も選択されておらず、ELSE句もありません。

COLLECTION_IS_NULL

ORA-06531

-6531

EXISTS以外のコレクション・メソッドを初期化されていないネストした表やVARRAYに適用しようとしたか、または初期化されていないネストした表やVARRAYの要素に値を割り当てようとしました。

CURSOR_ALREADY_OPENED

ORA-06511

-6511

すでにオープンされているカーソルをオープンしようとしました。

DUP_VAL_ON_INDEX

ORA-00001

-1

プログラムは一意索引によって制約されている列に重複値を挿入しようとしました。

INVALID_CURSOR

ORA-01001

-1001

不正なカーソル操作があります。

INVALID_NUMBER

ORA-01722

-1722

文字列から数値への変換に失敗しました。

NO_DATA_FOUND

ORA-01403

+100

単一行のSELECTによって行が戻されなかったか、またはプログラムによって、ネストした表内の削除された要素や連想配列(索引付き表)内の初期化されていない要素が参照されました。

PROGRAM_ERROR

ORA-06501

-6501

PL/SQLに内部的な問題が発生しました。

ROWTYPE_MISMATCH

ORA-06504

-6504

代入文のホスト・カーソル変数とPL/SQLカーソル変数の戻り型に互換性がありません。

STORAGE_ERROR

ORA-06500

-6500

PL/SQLのメモリーが不足しているか、メモリーが破損しています。

SUBSCRIPT_BEYOND_COUNT

ORA-06533

-6533

コレクション内の要素の数より大きい索引番号を使用して、ネストした表またはVARRAYを参照しました。

SUBSCRIPT_OUTSIDE_LIMIT

ORA-06532

-6532

有効範囲外の索引番号(たとえば-1)を使用して、ネストした表またはVARRAYを参照しました。

SYS_INVALID_ROWID

ORA-01410

-1410

文字列がROWID値を表していないため、ユニバーサルROWIDへの文字列の変換に失敗しました。

TOO_MANY_ROWS

ORA-01422

-1422

単一行SELECTが複数の行を戻しました。

VALUE_ERROR

ORA-06502

-6502

算術、変換、切捨てまたはサイズ制限のエラーが発生しました。

ZERO_DIVIDE

ORA-01476

-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文の使用

RAISE文を使用すると、PL/SQLブロックまたはサブプログラムの通常の実行を停止し、制御を例外ハンドラに渡すことができます。RAISE文では、事前定義例外または自分で名前を付けたユーザー定義例外を呼び出すことができます。

例4-2 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プロシージャの使用

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.

一時エラー後の再試行(PL/SQL)

TimesTenではほとんどの一時エラーは自動的に解決されますが(これはTimesTen Scaleoutにおいてとりわけ重要です)、アプリケーションで次のエラーが検出された場合は、失敗したトランザクションを再試行することをお薦めします。

  • ORA-57005:Transient transaction failure due to unavailability of resource.Roll back the transaction and try it again.


ノート:

再試行が妥当かどうかを判断する前に、エラー・スタック全体でこれらのエラー・タイプを返すエラーを検索してください。

PL/SQLプラグマEXCEPTION_INITを使用してこのエラーに対応する例外を宣言し、その例外が発生した場合はトランザクションを再試行します。

次に例を示します。

declare
  retry_stmt exception;
  pragma exception_init(retry_stmt, -57007);
  retry_txn exception;
  pragma exception_init(retry_txn, -57005);
begin
  -- Execute SQL
exception
when retry_stmt then
  -- Re-execute the failing SQL statemment
when retry_txn then
  -- Re-execute the failing transaction
end;

ttIsqlでのエラーの表示

ttIsqlshow errorsコマンドを使用すると、無名ブロックの実行時、あるいはパッケージ、プロシージャまたはファンクションのコンパイル時に発生するエラーの詳細を表示できます。この例を例4-4に示します。

例4-4 ttIsqlのエラー表示コマンド

もう一度、例2-13を考えてみます。そこで示されていたのと同じパッケージ仕様(プロシージャおよびファンクションhire_employeeremove_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における相違点: 例外処理およびエラー動作

TimesTen PL/SQLとOracle Database PL/SQLで異なるエラー関連の動作に注意する必要があります。

未処理例外に対するTimesTen 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

ノート:

  • PL/SQLブロックに未処理例外がある場合、TimesTenではトランザクションがオープンのままになるため、アプリケーションでその状態を評価して適切なアクションを判断する必要があります。

  • TimesTenのアプリケーションでは、現在のトランザクションでコミットされていない変更内容がある間はPL/SQLブロックを実行しないでください(ただし、変更内容とPL/SQL操作によって単一の論理作業ユニットが構成され、そのアプリケーションが適切なアクションを決定できる場合を除きます)。このようなアクションには、たとえば、トランザクションの先頭へのロールバックなどがあります。

  • 自動コミットが有効になっているときにTimesTenで未処理例外が発生した場合は、トランザクション全体がロールバックされます。


TimesTenエラー・メッセージおよびSQLコード

エラー条件が同じ場合、TimesTenでは、TimesTenが戻すエラー・メッセージとOracle Databaseが戻すメッセージが同じになることは保証されませんが、SQLコードは同じになります。そのため、SQLERRMファンクションによって戻される情報は異なる場合がありますが、SQLCODEファンクションによって戻される情報は同じになります。

詳細は、次の項または例を参照してください。

  • 例4-2では、SQLERRMおよびSQLCODEを使用します。

  • 特定のTimesTenエラー・メッセージの詳細は、『Oracle TimesTen In-Memory Databaseエラー・メッセージおよびSNMPトラップ』の警告とエラーに関する説明を参照してください。

  • 一般的な情報は、『Oracle Database PL/SQL言語リファレンス』のSQLERRMファンクションおよびSQLCODEファンクションに関する説明を参照してください。

PL/SQLで表示されない警告

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 説明

LOGIN_DENIED

ORA-01017

-1017

ユーザー名およびパスワードが無効です。

NOT_LOGGED_ON

ORA-01012

-1012

プログラムがデータベースに接続せずにデータベース・コールを発行しました。

SELF_IS_NULL

ORA-30625

-30625

プログラムがMEMBERメソッドを起動しようとしましたが、オブジェクトが初期化されていませんでした。

TIMEOUT_ON_RESOURCE

ORA-00051

-51

データベースでリソースを待機している間に、タイムアウトが発生しました。


クリーン・コンパイル後の実行時エラーの可能性(Oracle Database SQLパーサーを使用)

TimesTen PL/SQLの実装では、PL/SQLプログラムのコンパイル時に、Oracle Database SQLパーサーが使用されます。(これは、「TimesTenにおけるPL/SQLとOracle DatabaseにおけるPL/SQL」で説明されています。)そのため、TimesTenでサポートされていないOracle Databaseの構文または組込みプロシージャをプログラムで使用しても、コンパイル中は問題が検出されません。ただし、プログラムの実行中に実行時エラーが発生します。

実行時のTimesTen式の使用

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文)」を参照してください。