11.8 例外の伝播
例外ハンドラを持たないブロックで例外が呼び出された場合は、例外が伝播します。つまり、その例外のハンドラを持つブロックに至るか外側のブロックがなくなるまで、1つずつ外側のブロックに進んで例外が再生されます。その例外に対応するハンドラがない場合、PL/SQLは起動元またはホスト環境に未処理例外エラーを戻します。その結果は戻す場所によって異なります(詳細は「未処理例外」を参照してください)。
図11-1では、1つのブロックが別のブロックの内側にネストされています。内側のブロックが例外Aを呼び出します。内側のブロックにはAの例外ハンドラがあるため、Aは伝播しません。例外ハンドラが実行されると、外側のブロックの次の文に制御が移ります。
図11-2では、内側のブロックが例外Bを呼び出しています。内側のブロックには例外Bの例外ハンドラがないため、例外Bはその例外ハンドラを持つ外側のブロックに伝播します。例外ハンドラが実行されると、ホスト環境に制御が移ります。
図11-3では、内側のブロックが例外Cを呼び出しています。内側のブロックには例外Cの例外ハンドラがないため、例外Cは外側のブロックに伝播します。外側のブロックにはCの例外ハンドラがないため、PL/SQLはホスト環境に未処理例外エラーを戻します。
ユーザー定義の例外は有効範囲を超えて(つまり宣言されたブロックを超えたところまで)伝播することがありますが、有効範囲を超えたところには例外名が存在しません。そのため、有効範囲を超えたユーザー定義の例外は、OTHERS例外ハンドラ以外では処理できません。
例11-14の内側のブロックでは、past_dueという名前の例外を宣言していますが、この例外の例外ハンドラはありません。内側のブロックでpast_dueが呼び出されると、外側のブロックに例外が伝播しますが、そこにはpast_dueという名前が存在しません。外側のブロックはOTHERS例外ハンドラを使用して例外を処理します。
ユーザー定義の例外が外側のブロックで処理されない場合は、例11-15に示すとおり、エラーが発生します。
ノート:
例外はリモート・サブプログラム起動には伝播しません。そのため、PL/SQLブロックは、リモート・サブプログラムによって呼び出された例外を処理できません。
ここでのトピック
例11-14 有効範囲を超えて伝播した例外が処理される場合
CREATE OR REPLACE PROCEDURE p AUTHID DEFINER AS
BEGIN
DECLARE
past_due EXCEPTION;
PRAGMA EXCEPTION_INIT (past_due, -4910);
due_date DATE := trunc(SYSDATE) - 1;
todays_date DATE := trunc(SYSDATE);
BEGIN
IF due_date < todays_date THEN
RAISE past_due;
END IF;
END;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
RAISE;
END;
/
例11-15 有効範囲を超えて伝播した例外が処理されない場合
BEGIN
DECLARE
past_due EXCEPTION;
due_date DATE := trunc(SYSDATE) - 1;
todays_date DATE := trunc(SYSDATE);
BEGIN
IF due_date < todays_date THEN
RAISE past_due;
END IF;
END;
END;
/
結果:
BEGIN
*
ERROR at line 1:
ORA-06510: PL/SQL: unhandled user-defined exception
ORA-06512: at line 911.8.1 宣言の中で呼び出された例外の伝播
宣言の中で呼び出された例外はただちに外側のブロック(外側のブロックがない場合は起動元またはホスト環境)に伝播します。したがって、例外ハンドラは宣言と同じブロックの中ではなく、外側または起動元のブロックに存在する必要があります。
例11-16では、VALUE_ERRORが呼び出される宣言と同じブロックの中にVALUE_ERROR例外ハンドラが存在しています。例外はただちにホスト環境に伝播するため、この例外は例外ハンドラで処理されません。
例11-17は例11-16と似ていますが、内側のブロックの中の宣言で呼び出されるVALUE_ERROR例外が外側のブロックで処理される点が異なります。
例11-16 宣言の中で呼び出された例外が処理されない場合
DECLARE credit_limit CONSTANT NUMBER(3) := 5000; -- Maximum value is 999 BEGIN NULL; EXCEPTION WHEN VALUE_ERROR THEN DBMS_OUTPUT.PUT_LINE('Exception raised in declaration.'); END; /
結果:
DECLARE * ERROR at line 1: ORA-06502: PL/SQL: numeric or value error: number precision too large ORA-06512: at line 2
例11-17 宣言の中で呼び出された例外が外側のブロックで処理される場合
BEGIN DECLARE credit_limit CONSTANT NUMBER(3) := 5000; BEGIN NULL; END; EXCEPTION WHEN VALUE_ERROR THEN DBMS_OUTPUT.PUT_LINE('Exception raised in declaration.'); END; /
結果:
Exception raised in declaration.
11.8.2 例外ハンドラの中で呼び出された例外の伝播
例外ハンドラの中で呼び出された例外はただちに外側のブロック(外側のブロックがない場合は起動元またはホスト環境)に伝播します。したがって、例外ハンドラは、外側または起動元のブロックに存在する必要があります。
例11-18では、nが0(ゼロ)の場合、計算1/nで事前定義の例外ZERO_DIVIDEが呼び出され、同じブロックの中のZERO_DIVIDE例外ハンドラに制御が移ります。例外ハンドラでZERO_DIVIDEが呼び出されると、例外はただちに起動元に伝播します。起動元で例外が処理されないため、PL/SQLはホスト環境に未処理例外エラーを戻します。
例11-19は例11-18に似ていますが、プロシージャが起動元に未処理例外エラーを戻すと起動元でエラーが処理される点が異なります。
例11-20は例11-18と似ていますが、内側のブロックの中の例外ハンドラで呼び出される例外が外側のブロックで処理される点が異なります。
例11-21では、ユーザー定義の例外i_is_oneと事前定義の例外ZERO_DIVIDEに対応する例外ハンドラがプロシージャの例外処理部にあります。i_is_one例外ハンドラでZERO_DIVIDEが呼び出されると、例外はただちに起動元に伝播します(そのため、ZERO_DIVIDE例外ハンドラでは例外が処理されません)。起動元で例外が処理されないため、PL/SQLはホスト環境に未処理例外エラーを戻します。
例11-22は例11-21と似ていますが、i_is_one例外ハンドラで呼び出されるZERO_DIVIDE例外が外側のブロックで処理される点が異なります。
例11-18 例外ハンドラの中で呼び出された例外が処理されない場合
CREATE PROCEDURE print_reciprocal (n NUMBER) AUTHID DEFINER IS BEGIN DBMS_OUTPUT.PUT_LINE(1/n); -- handled EXCEPTION WHEN ZERO_DIVIDE THEN DBMS_OUTPUT.PUT_LINE('Error:'); DBMS_OUTPUT.PUT_LINE(1/n || ' is undefined'); -- not handled END; / BEGIN -- invoking block print_reciprocal(0); END;
結果:
Error: BEGIN * ERROR at line 1: ORA-01476: divisor is equal to zero ORA-06512: at "HR.PRINT_RECIPROCAL", line 7 ORA-01476: divisor is equal to zero ORA-06512: at line 2
例11-19 例外ハンドラの中で呼び出された例外が起動元で処理される場合
CREATE PROCEDURE print_reciprocal (n NUMBER) AUTHID DEFINER IS
BEGIN
DBMS_OUTPUT.PUT_LINE(1/n);
EXCEPTION
WHEN ZERO_DIVIDE THEN
DBMS_OUTPUT.PUT_LINE('Error:');
DBMS_OUTPUT.PUT_LINE(1/n || ' is undefined');
END;
/
BEGIN -- invoking block
print_reciprocal(0);
EXCEPTION
WHEN ZERO_DIVIDE THEN -- handles exception raised in exception handler
DBMS_OUTPUT.PUT_LINE('1/0 is undefined.');
END;
/
結果:
Error: 1/0 is undefined.
例11-20 例外ハンドラの中で呼び出された例外が外側のブロックで処理される場合
CREATE PROCEDURE print_reciprocal (n NUMBER) AUTHID DEFINER IS BEGIN BEGIN DBMS_OUTPUT.PUT_LINE(1/n); EXCEPTION WHEN ZERO_DIVIDE THEN DBMS_OUTPUT.PUT_LINE('Error in inner block:'); DBMS_OUTPUT.PUT_LINE(1/n || ' is undefined.'); END; EXCEPTION WHEN ZERO_DIVIDE THEN -- handles exception raised in exception handler DBMS_OUTPUT.PUT('Error in outer block: '); DBMS_OUTPUT.PUT_LINE('1/0 is undefined.'); END; / BEGIN print_reciprocal(0); END; /
結果:
Error in inner block: Error in outer block: 1/0 is undefined.
例11-21 例外ハンドラの中で呼び出された例外が処理されない場合
CREATE PROCEDURE descending_reciprocals (n INTEGER) AUTHID DEFINER IS i INTEGER; i_is_one EXCEPTION; BEGIN i := n; LOOP IF i = 1 THEN RAISE i_is_one; ELSE DBMS_OUTPUT.PUT_LINE('Reciprocal of ' || i || ' is ' || 1/i); END IF; i := i - 1; END LOOP; EXCEPTION WHEN i_is_one THEN DBMS_OUTPUT.PUT_LINE('1 is its own reciprocal.'); DBMS_OUTPUT.PUT_LINE('Reciprocal of ' || TO_CHAR(i-1) || ' is ' || TO_CHAR(1/(i-1))); WHEN ZERO_DIVIDE THEN DBMS_OUTPUT.PUT_LINE('Error:'); DBMS_OUTPUT.PUT_LINE(1/n || ' is undefined'); END; / BEGIN descending_reciprocals(3); END; /
結果:
Reciprocal of 3 is .3333333333333333333333333333333333333333 Reciprocal of 2 is .5 1 is its own reciprocal. BEGIN * ERROR at line 1: ORA-01476: divisor is equal to zero ORA-06512: at "HR.DESCENDING_RECIPROCALS", line 19 ORA-06510: PL/SQL: unhandled user-defined exception ORA-06512: at line 2
例11-22 例外ハンドラの中で呼び出された例外が外側のブロックで処理される場合
CREATE PROCEDURE descending_reciprocals (n INTEGER) AUTHID DEFINER IS i INTEGER; i_is_one EXCEPTION; BEGIN BEGIN i := n; LOOP IF i = 1 THEN RAISE i_is_one; ELSE DBMS_OUTPUT.PUT_LINE('Reciprocal of ' || i || ' is ' || 1/i); END IF; i := i - 1; END LOOP; EXCEPTION WHEN i_is_one THEN DBMS_OUTPUT.PUT_LINE('1 is its own reciprocal.'); DBMS_OUTPUT.PUT_LINE('Reciprocal of ' || TO_CHAR(i-1) || ' is ' || TO_CHAR(1/(i-1))); WHEN ZERO_DIVIDE THEN DBMS_OUTPUT.PUT_LINE('Error:'); DBMS_OUTPUT.PUT_LINE(1/n || ' is undefined'); END; EXCEPTION WHEN ZERO_DIVIDE THEN -- handles exception raised in exception handler DBMS_OUTPUT.PUT_LINE('Error:'); DBMS_OUTPUT.PUT_LINE('1/0 is undefined'); END; / BEGIN descending_reciprocals(3); END; /
結果:
Reciprocal of 3 is .3333333333333333333333333333333333333333 Reciprocal of 2 is .5 1 is its own reciprocal. Error: 1/0 is undefined


