ヘッダーをスキップ
Oracle TimesTen In-Memory Database PL/SQL開発者ガイド
リリース11.2.1
B56057-01
  目次
目次
索引
索引

戻る
戻る
 
次へ
次へ
 

4 エラーと例外の処理

この章では、PL/SQLプログラムで使用できる、柔軟なエラー・トラップとエラー処理について説明します。

PL/SQLにおけるエラー処理と例外の詳細は、『Oracle Database PL/SQL言語リファレンス』のPL/SQLエラーの処理に関する説明を参照してください。

TimesTen固有の考慮事項の詳細は、章の最後を参照してください。

この章の内容は次のとおりです。

例外の理解

この項では、PL/SQLプログラミングにおける例外の概要を説明します。内容は次のとおりです。

例外について

例外とは、プログラムの実行中に(TimesTenによって暗黙的に、またはプログラムによって明示的に)呼び出されるPL/SQLエラーのことです。 例外を処理するには、ハンドラでトラップするか、またはコール元の環境に伝播します。

たとえばSELECT文で2つ以上の行が戻されると、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の場合と同じ方法で使用します。

例外 説明 処理方法
事前定義のTimesTenエラー PL/SQLコードで最もよく発生する約20個のエラーの1つ これらの例外は、宣言する必要がありません。 TimesTenで事前に定義されています。 エラーはTimesTenによって暗黙的に呼び出されます。
未定義のTimesTenエラー その他の標準のTimesTenエラー アプリケーションの宣言部で宣言する必要があります。 エラーはTimesTenによって暗黙的に呼び出され、例外ハンドラを使用して捕捉できます。
ユーザー定義のエラー アプリケーションによって定義され、呼び出されるエラー 宣言部で宣言する必要があります。 開発者が明示的に例外を呼び出します。

例外のトラップ

この項では、事前定義のTimesTenエラーまたはユーザー定義のエラーをトラップする方法について説明します。

事前定義のTimesTenエラーのトラップ

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

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

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

表4-1 事前定義例外

例外名 Oracleエラー番号 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によって、2つ以上の行が戻されました。

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でエラー条件が同じ場合、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.

TimesTenにおける相違点: 例外処理およびエラー動作

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

未処理例外に対するTimesTen PL/SQLのトランザクションおよびロールバック動作

トランザクションの途中でPL/SQLが実行され、PL/SQLの実行中に未処理例外が発生した場合、TimesTen PL/SQLの処理は、Oracle PL/SQLでの処理と異なります。 Oracleでは、無名ブロックの先頭にロールバックします。 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>

PL/SQL procedure successfully completed.

select * from mytable;
0 rows found.

結果はOracleでも同様で、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つのINSERTの実行を指示します。

  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で実行すると、PL/SQLブロックの先頭にロールバックされるため、SELECTは最初のINSERTのみの実行を指示します。

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によって戻されるエラー・メッセージと、Oracleによって戻されるメッセージは同じとはかぎりません。ただし、SQLコードは同じです。 したがって、SQLERRMファンクションによって戻される情報は異なる場合がありますが、SQLCODEファンクションによって戻される情報は同じです。

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

  • 例4-2「RAISE文を使用したユーザー定義例外のトラップ」では、SQLERRMとSQLCODEを使用しています。

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

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

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

Oracle Databaseには実行時警告の概念がないため、Oracle PL/SQLでは実行時警告の概念はサポートされていません。

TimesTen In-Memory Databaseには警告の概念がありますが、TimesTen PL/SQLの実装はOracle PL/SQLの実装に基づいているため、TimesTen PL/SQLでは警告はサポートされていません。

その結果、TimesTenではSQL文を実行してその結果発生する警告を表示できますが、同じ文をPL/SQLから実行した場合には、警告は表示されません。

サポートされていない事前定義エラー

「事前定義のTimesTenエラーのトラップ」には、TimesTenでサポートされている事前定義例外、関連付けられているORAエラー番号とSQLCODE値、および例外の説明が示されています。

表4-2に、TimesTenでサポートされていない事前定義例外を示します。

表4-2 TimesTenでサポートされていない事前定義例外

例外名 Oracleエラー番号 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 SQL解析機能を使用)

TimesTen PL/SQLの実装では、PL/SQLプログラムのコンパイル時に、Oracle SQLパーサーが使用されます。 そのため、TimesTenでサポートされていないOracle構文またはOracle組込みファンクションをプログラムで使用しても、コンパイル中は問題が検出されません。 ただし、プログラムの実行中に実行時エラーが発生します。