例外の伝播
例外ハンドラを持たないブロックで例外が呼び出された場合は、例外が伝播します。つまり、その例外のハンドラを持つブロックに至るか外側のブロックがなくなるまで、1つずつ外側のブロックに進んで例外が再生されます。その例外に対応するハンドラがない場合、PL/SQLは起動元またはホスト環境に未処理例外エラーを戻します。その結果は戻す場所によって異なります(詳細は「未処理例外」を参照してください)。
図12-1では、1つのブロックが別のブロックの内側にネストされています。内側のブロックが例外Aを呼び出します。内側のブロックにはAの例外ハンドラがあるため、Aは伝播しません。例外ハンドラが実行されると、外側のブロックの次の文に制御が移ります。
図12-2では、内側のブロックが例外Bを呼び出しています。内側のブロックには例外Bの例外ハンドラがないため、例外Bはその例外ハンドラを持つ外側のブロックに伝播します。例外ハンドラが実行されると、ホスト環境に制御が移ります。
図12-3では、内側のブロックが例外Cを呼び出しています。内側のブロックには例外Cの例外ハンドラがないため、例外Cは外側のブロックに伝播します。外側のブロックにはCの例外ハンドラがないため、PL/SQLはホスト環境に未処理例外エラーを戻します。
ユーザー定義の例外は有効範囲を超えて(つまり宣言されたブロックを超えたところまで)伝播することがありますが、有効範囲を超えたところには例外名が存在しません。そのため、有効範囲を超えたユーザー定義の例外は、OTHERS
例外ハンドラ以外では処理できません。
例12-14の内側のブロックでは、past_due
という名前の例外を宣言していますが、この例外の例外ハンドラはありません。内側のブロックでpast_due
が呼び出されると、外側のブロックに例外が伝播しますが、そこにはpast_due
という名前が存在しません。外側のブロックはOTHERS
例外ハンドラを使用して例外を処理します。
ユーザー定義の例外が外側のブロックで処理されない場合は、例12-15に示すとおり、エラーが発生します。
ノート:
例外はリモート・サブプログラム起動には伝播しません。そのため、PL/SQLブロックは、リモート・サブプログラムによって呼び出された例外を処理できません。
ここでのトピック
例12-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; /
例12-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 9
宣言の中で呼び出された例外の伝播
宣言の中で呼び出された例外はただちに外側のブロック(外側のブロックがない場合は起動元またはホスト環境)に伝播します。したがって、例外ハンドラは宣言と同じブロックの中ではなく、外側または起動元のブロックに存在する必要があります。
例12-16では、VALUE_ERROR
が呼び出される宣言と同じブロックの中にVALUE_ERROR
例外ハンドラが存在しています。例外はただちにホスト環境に伝播するため、この例外は例外ハンドラで処理されません。
例12-17は例12-16と似ていますが、内側のブロックの中の宣言で呼び出されるVALUE_ERROR
例外が外側のブロックで処理される点が異なります。
例12-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: value or conversion error: number precision too large ORA-06512: at line 2
例12-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.
例外ハンドラの中で呼び出された例外の伝播
例外ハンドラの中で呼び出された例外はただちに外側のブロック(外側のブロックがない場合は起動元またはホスト環境)に伝播します。したがって、例外ハンドラは、外側または起動元のブロックに存在する必要があります。
例12-18では、n
が0(ゼロ)の場合、計算1/n
で事前定義の例外ZERO_DIVIDE
が呼び出され、同じブロックの中のZERO_DIVIDE
例外ハンドラに制御が移ります。例外ハンドラでZERO_DIVIDE
が呼び出されると、例外はただちに起動元に伝播します。起動元で例外が処理されないため、PL/SQLはホスト環境に未処理例外エラーを戻します。
例12-19は例12-18に似ていますが、プロシージャが起動元に未処理例外エラーを戻すと起動元でエラーが処理される点が異なります。
例12-20は例12-18と似ていますが、内側のブロックの中の例外ハンドラで呼び出される例外が外側のブロックで処理される点が異なります。
例12-21では、ユーザー定義の例外i_is_one
と事前定義の例外ZERO_DIVIDE
に対応する例外ハンドラがプロシージャの例外処理部にあります。i_is_one
例外ハンドラでZERO_DIVIDE
が呼び出されると、例外はただちに起動元に伝播します(そのため、ZERO_DIVIDE
例外ハンドラでは例外が処理されません)。起動元で例外が処理されないため、PL/SQLはホスト環境に未処理例外エラーを戻します。
例12-22は例12-21と似ていますが、i_is_one
例外ハンドラで呼び出されるZERO_DIVIDE
例外が外側のブロックで処理される点が異なります。
例12-18 例外ハンドラの中で呼び出された例外が処理されない場合
CREATE OR REPLACE 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
例12-19 例外ハンドラの中で呼び出された例外が起動元で処理される場合
CREATE OR REPLACE 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.
例12-20 例外ハンドラの中で呼び出された例外が外側のブロックで処理される場合
CREATE OR REPLACE 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.
例12-21 例外ハンドラの中で呼び出された例外が処理されない場合
CREATE OR REPLACE 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
例12-22 例外ハンドラの中で呼び出された例外が外側のブロックで処理される場合
CREATE OR REPLACE 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