例外とエラー・メッセージのサポート

NZPLSQL プロシージャーでエラーが発生した場合、プロシージャーの実行は中止され、外側のトランザクションも中止されます。 システムは、メイン・ループに戻り、クライアント・アプリケーションから次の照会を取得します。 すべての例外をキャッチすることは (特に、そのアクションがデータベースを不良な状態にしている場合には) 不可能です。

プロシージャー・ブロック内で発生したエラーをキャッチして処理するには、EXCEPTION キーワードを使用して、ブロックの最後に例外処理セクションを追加します。 エラーが発生しなかった場合、例外処理セクションは無視されます。 例外処理セクションの内部には、一致する例外および一致した場合に実行する文を指定する WHEN 節を使用して、1 つ以上の例外ハンドラーを指定できます。 EXCEPTION 文の形式は以下のとおりです。

EXCEPTION 
    WHEN clause THEN
        statements 
    [ WHEN ... ] 

NZPLSQL は、例外処理のために以下の 2 つの節をサポートしています。

TRANSACTION_ABORTED
TRANSACTION_ABORTED 節を使用して、トランザクションを中止するエラーが発生した場合に実行する文を指定します。 このような場合、続行するためには ROLLBACK が必要です。 必ず、例外処理文の最初のコマンドを ROLLBACK コマンドにしてください。
その他
OTHERS 節を使用して、プロシージャー・ブロック内でどのようなエラーが発生した場合にも実行する文を指定します。 SQL 構文解析エラーなどのエラーは、トランザクションを中止しないため、TRANSACTION_ABORTED 節には一致しません。

以下の例に示すように、1 つの例外ブロックに両方の節を指定することができます。 プロシージャー内でエラーが発生した場合、プロシージャー・コードは、最初に一致した例外節を使用してその節の文を実行します。 その一致節より後の節はすべて無視されます。 ベスト・プラクティスとして、OTHERS 節は最後に指定してください。この節は、どのタイプのエラーにも一致するからです。

EXCEPTION 
    WHEN TRANSACTION_ABORTED THEN
       ROLLBACK;
       statements_case1 
       RAISE ERROR 'Procedure failed: %', sqlerrm;
    WHEN OTHERS THEN
       statements_case2
       RAISE NOTICE 'Caught error, continuing %', sqlerrm;

この例では、トランザクションを中止するエラーが発生した場合、TRANSACTION_ABORTED 節がトリガーされ、ストアード・プロシージャーは ROLLBACK、statements_case1 の文を実行してエラー・メッセージを生成します。 トランザクションを中止しないエラーの場合、例外処理は OTHERS 節にスキップし、ストアード・プロシージャーは、statements_case2 のセットを実行して通知メッセージを生成します。 ブロック内でエラーが発生しなかった場合、例外の文はスキップされます。

変数 SQLERRM には、キャッチされたエラー・メッセージのテキストが含まれます。 例外ブロックがない場合、例外は、呼び出しスタック内の次のストアード・プロシージャーまで伝播されます。 sproc1 が sproc2 を呼び出して例外が発生した場合、sproc2 に例外ハンドラーがなければ、システムは sproc1 のハンドラーを探します。 また、システムは、封入ブロックの宣言も確認します。

例:
create or replace procedure sp_except01() returns BOOL LANGUAGE 
NZPLSQL AS 
BEGIN_PROC
DECLARE
    r record;
BEGIN
    <<inner>>
    BEGIN
        SELECT * INTO r FROM NONEXISTENT;
    END;
END;
END_PROC;

create or replace procedure sp_except02() returns BOOL LANGUAGE 
NZPLSQL AS 
BEGIN_PROC
BEGIN
    CALL sp_except01();
END;
END_PROC;

create or replace procedure sp_except03() returns BOOL LANGUAGE 
NZPLSQL AS 
BEGIN_PROC
BEGIN
    CALL sp_except02();
    EXCEPTION WHEN OTHERS THEN
        RAISE NOTICE 'Caught exception';
END;
END_PROC;

上記の例では、例外は inner ブロックの sp_except01 で生成されます。 システムは、最初に inner ブロックの例外ハンドラーを確認しますが、例外ハンドラーは見つかりません。 コントロールは親コンテキストのプロシージャー sp_except01 に移動しますが、ここでも例外ハンドラーは見つかりません。 さらに、コントロールは sp_except02 に移動し、最後に sp_except03 移動すると、例外ハンドラーが見つかり、これを使用します。

例外がいずれのレベルでもキャッチされない場合、追加の NOTICE レベルのログ・メッセージが送信され、エラーに関する状況と発生場所 (行番号と文のタイプ、ただし RAISE EXCEPTION 文によるエラーの場合は除く) が提供されます。

例外ハンドラーをストアード・プロシージャーに含める場合、ハンドラーがすべてのエラーをキャッチし、エラーは表示されません。 例:
CREATE PROCEDURE sp() RETURNS INTEGER LANGUAGE NZPLSQL AS
BEGIN_PROC
BEGIN
  EXECUTE IMMEDIATE 'insert into NOTEXIST' || 'values(1,1)';
  EXCEPTION WHEN OTHERS THEN
  END;
END_PROC;

NOTEXIST がデータベースに存在しないと仮定すると、エラーは例外ハンドラーで処理されるため、照会はエラーを表示しません。

エラーを表示するには、プロシージャーを以下のように記述します。
CREATE PROCEDURE sp() RETURNS INTEGER LANGUAGE NZPLSQL AS
BEGIN_PROC
BEGIN
  EXECUTE IMMEDIATE 'insert into NOTEXIST' || ' values(1,1)';
  EXCEPTION WHEN OTHERS THEN
    RAISE NOTICE 'Got exception: %', SQLERRM;
  END;
END_PROC;
この照会を実行すると、次のような出力が表示されます。
NOTICE:  Got exception: ERROR:  Relation 'NOTEXIST' does not exist