ストアドプログラムには、プログラム内で特定の条件が発生した場合に呼び出されるハンドラーを含めることができます。各ハンドラーの適用範囲は、プログラム定義内での位置および処理する条件の一つまたは複数によって決まります。
ハンドラーの有効範囲と関連ルール
OceanBaseデータベースにおけるハンドラーの有効範囲に関するルールは以下のとおりです:
BEGIN ... ENDブロック内で宣言されたハンドラーは、そのブロック内でハンドラー宣言後のSQLステートメントにのみ適用されます。ハンドラー自体が条件を発生させた場合、その条件を処理するために使用することはできず、ブロック内で他のハンドラーを宣言することもできません。以下の例では、ハンドラーh1とh2はステートメントst1とst2が発生させる条件の有効範囲内にありますが、h1またはh2の本体が発生させる条件の有効範囲内にはありません。BEGIN -- 外部ブロック DECLARE EXIT HANDLER FOR ...; -- ハンドラー h1 DECLARE EXIT HANDLER FOR ...; -- ハンドラー h2 st1; st2; END;ハンドラーは宣言されたブロックの範囲内でのみ有効であり、そのブロック外で発生した条件によっては活性化されません。以下の例では、ハンドラー
h1は内部ブロック内のst1の範囲内では有効ですが、外部ブロック内のst2に対しては無効です:BEGIN -- 外部ブロック BEGIN -- 内部ブロック DECLARE EXIT HANDLER FOR ...; -- ハンドラー h1 st1; END; st2; END;ハンドラーは特定のものまたは汎用的なものにすることができます。特定のハンドラーはエラーコード、
SQLSTATE値、または条件名に使用されます。汎用的なハンドラーは、SQLWARNING、SQLEXCEPTION、またはNOT FOUNDカテゴリの条件によって使用されます。条件の特定性は条件の優先順位に関連しています。異なる特性を持つ複数のハンドラーを異なる範囲で宣言することができます。例えば、外部ブロックに特定のエラーコードハンドラーがある一方で、内部ブロックに汎用的な
SQLWARNINGハンドラーがある場合があります。また、同一ブロック内に特定のエラーコードと汎用的なSQLWARNINGカテゴリのハンドラーが存在する場合もあります。
ハンドラーが活性化されるかどうかは、その自身の範囲や条件値だけでなく、他のハンドラーの影響も受けます。ストアドプログラム内で条件が発生した場合、サーバーは現在の範囲(現在の BEGIN ... END ブロック)内で適用可能なハンドラーを検索します。適用可能なハンドラーがない場合、次の範囲(ブロック)内のハンドラーを検索します。サーバーが指定された範囲内で一つ以上の適用可能なハンドラーを見つけた場合、条件の優先順位に基づいて選択を行います。条件の優先順位は以下のとおりです:
エラーコードハンドラーは
SQLSTATE値ハンドラーよりも優先されます。SQLSTATE値ハンドラーは、汎用的なSQLWARNING、SQLEXCEPTION、またはNOT FOUNDハンドラーよりも優先されます。SQLEXCEPTIONハンドラはSQLWARNINGハンドラよりも優先されます。同じ優先順位を持つ適用可能なハンドラが複数存在する場合があります。例えば、1つのステートメントが異なるエラーコードを持つ複数の警告を生成する場合、各警告にはエラー固有のハンドラが存在します。このような場合、サーバーがどのハンドラを有効化するかは不確定であり、発生した状況に応じて変わる可能性があります。
複数の適用可能なハンドラが異なるスコープに存在する場合、ローカルスコープのハンドラは外部スコープのハンドラよりも優先され、指定条件のハンドラよりも高い優先順位を持ちます。
条件が発生した際に適切なハンドラがない場合、条件のカテゴリに基づいて以下の動作が実行されます:
SQLEXCEPTION条件の場合、ストアドプログラムは条件を発生させたステートメントで終了し、EXITハンドラを使用した場合と同様になります。そのプログラムが別のストアドプログラムによって呼び出されている場合、呼び出し元のプログラムが自身の選択ルールに従ってその条件を処理します。SQLWARNING条件の場合、プログラムは実行を続けます。これはCONTINUEハンドラを使用した場合と同様です。NOT FOUND条件の場合、条件が正常に発生した場合の動作はCONTINUEです。条件がSIGNALまたはRESIGNALによって発生した場合、動作はEXITです。
例
例1、例2、例3、例4は、ハンドラの選択ルールの適用方法を示しています。
以下の例1のプロシージャには2つのハンドラが含まれており、1つは存在しないテーブルを削除しようとした際に指定された SQLSTATE 値('42S02')が発生した場合に使用され、もう1つは汎用的な SQLEXCEPTION カテゴリに属します。
-- 例1
CREATE PROCEDURE proc1()
BEGIN
DECLARE CONTINUE HANDLER FOR SQLSTATE '42S02'
SELECT 'SQLSTATE handler was activated' AS msg;
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
SELECT 'SQLEXCEPTION handler was activated' AS msg;
DROP TABLE test.tbl1;
END;
例1の場合、2つのハンドラは同じブロック内で宣言され、同じスコープを持っています。しかし、SQLSTATE ハンドラは SQLEXCEPTION ハンドラよりも優先されるため、テーブル tbl1 が存在しない場合、DROP TABLE ステートメントは条件を発生させて SQLSTATE ハンドラを有効化します。以下のように:
obclient> CALL proc1();
+--------------------------------+
| msg |
+--------------------------------+
| SQLSTATE handler was activated |
+--------------------------------+
1 row in set
以下の例2では、プロシージャには例1と同じ2つのハンドラが含まれています。ただし、SQLSTATE ハンドラに対して、DROP TABLE ステートメントと SQLEXCEPTION ハンドラは内部ブロック内に配置されています。
-- 例2
CREATE PROCEDURE proc2()
BEGIN -- 外部ブロック
DECLARE CONTINUE HANDLER FOR SQLSTATE '42S02'
SELECT 'SQLSTATE handler was activated' AS msg;
BEGIN -- 内部ブロック
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
SELECT 'SQLEXCEPTION handler was activated' AS msg;
DROP TABLE test.tbl1; -- 内部ブロック内で発生
END;
END;
例2の場合、ハンドラーが条件発生位置に近いほど優先的に実行されるため、SQLEXCEPTION ハンドラーがSQLSTATE ハンドラーよりも一般的であっても、優先的に実行されます。以下に示します:
obclient> CALL proc2();
+------------------------------------+
| msg |
+------------------------------------+
| SQLEXCEPTION handler was activated |
+------------------------------------+
1 row in set
次の例3では、プロシージャの一つのハンドラーが DROP TABLE ステートメントの範囲内のブロック内で宣言されています:
-- 例3
CREATE PROCEDURE proc3()
BEGIN -- 外部ブロック
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
SELECT 'SQLEXCEPTION handler was activated' AS msg;
BEGIN -- 内部ブロック
DECLARE CONTINUE HANDLER FOR SQLSTATE '42S02'
SELECT 'SQLSTATE handler was activated' AS msg;
END;
DROP TABLE test.tbl1; -- 外部ブロック内で発生
END;
例3の場合、SQLEXCEPTION ハンドラーが実行されます。これは、SQLSTATE が DROP TABLE が引き起こす条件の範囲内にないためです。以下に示します:
obclient> CALL proc3();
+------------------------------------+
| msg |
+------------------------------------+
| SQLEXCEPTION handler was activated |
+------------------------------------+
1 row in set
次の例4では、2つのハンドラーが DROP TABLE ステートメントの範囲内のブロック内で宣言されています:
-- 例4
CREATE PROCEDURE proc4()
BEGIN -- 外部ブロック
BEGIN -- 内部ブロック
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
SELECT 'SQLEXCEPTION handler was activated' AS msg;
DECLARE CONTINUE HANDLER FOR SQLSTATE '42S02'
SELECT 'SQLSTATE handler was activated' AS msg;
END;
DROP TABLE test.tbl1; -- 外部ブロック内で発生
END;
例4の場合、これら2つのハンドラーはどちらも適用されません。なぜなら、それらは DROP TABLE の範囲内にないからです。このステートメントが引き起こす条件は処理されず、プロシージャはエラーで終了します。以下に示します:
obclient> CALL proc4();
ERROR 1051 (42S02): Unknown table 'test.tbl1'