ストアドプログラムには、プログラム内で特定の条件が発生した場合に呼び出されるハンドラーを含めることができます。各ハンドラーの適用範囲は、プログラム定義内での位置および処理する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 ブロック)内で適用可能なハンドラーを検索します。適用可能なハンドラーがない場合は、次の範囲(ブロック)内のハンドラーを検索し続けます。サーバーが指定された範囲内で1つ以上の適用可能なハンドラーを見つけた場合、条件の優先順位に基づいて選択します。条件の優先順位は以下のとおりです:
エラーコードハンドラーは
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の場合、ハンドラーは条件発生位置に近い方が優先的に有効化されるため、SQLSTATEハンドラーよりも一般的なSQLEXCEPTIONハンドラーが有効化されます。以下のように:
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の場合、SQLSTATEはDROP TABLEが引き起こす条件の範囲内にないため、SQLEXCEPTIONハンドラーが有効化されます。以下のように:
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'