ストアドプログラムには、プログラム内で特定の条件が発生した場合に呼び出されるハンドラーを含めることができます。各ハンドラーの適用範囲は、プログラム定義内での配置および処理する条件の1つまたは複数によって決まります。
ハンドラーの有効範囲と関連ルール
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ハンドラーよりも優先されます。同じ優先順位を持つ適用可能なハンドラーが複数存在する場合があります。例えば、一つのステートメントが異なるエラーコードを伴う複数の警告を生成する場合、各警告にはエラー固有のハンドラーが存在します。このような場合、サーバーがどのハンドラーを活性化するかは不確定であり、状況に応じて変わる可能性があります。
複数の適用可能なハンドラーが異なる範囲に存在する場合、ローカル範囲のハンドラーは外部範囲のハンドラーよりも優先され、指定された条件のハンドラーよりも高い優先順位を持ちます。
条件が発生した際に適切なハンドラーがない場合、条件のカテゴリに基づいて以下の操作が実行されます:
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では、プロシージャの1つのハンドラーが 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'