ストアドプログラムには、そのプログラム内で特定の条件が発生したときに呼び出されるハンドラを含めることができます。 各ハンドラの適用性は、プログラム定義の中でのそのハンドラの場所や、そのハンドラが処理する 1 つまたは複数の条件によって異なります。
-
BEGIN ... END
ブロック内で宣言されたハンドラは、そのブロック内でハンドラ宣言のあとにある SQL ステートメントのスコープ内にしかありません。 そのハンドラ自体が条件を発生させた場合は、そのハンドラも、そのブロック内で宣言されているほかのどのハンドラもその条件を処理できません。 次の例では、ハンドラH1
とH2
は、ステートメントstmt1
およびstmt2
によって発生した条件のスコープ内にあります。 ただし、H1
もH2
も、H1
またはH2
の本体で発生した条件のスコープ内にはありません。BEGIN -- outer block DECLARE EXIT HANDLER FOR ...; -- handler H1 DECLARE EXIT HANDLER FOR ...; -- handler H2 stmt1; stmt2; END;
-
ハンドラは、それが宣言されているブロックのスコープ内にしかなく、そのブロックの外部で発生した条件に対してアクティブ化することはできません。 次の例では、ハンドラ
H1
は内側のブロックにあるstmt1
のスコープ内にありますが、外側のブロックにあるstmt2
のスコープ内にはありません。BEGIN -- outer block BEGIN -- inner block DECLARE EXIT HANDLER FOR ...; -- handler H1 stmt1; END; stmt2; END;
ハンドラは、特定のハンドラまたは一般的なハンドラのどちらかです。 特定のハンドラとは、MySQL エラーコード、
SQLSTATE
値、または条件名を処理するためのものです。 一般的なハンドラとは、SQLWARNING
、SQLEXCEPTION
、またはNOT FOUND
クラス内の条件を処理するためのものです。 あとで説明されているように、条件の特定性は条件の優先順位に関連しています。
複数のハンドラを異なるスコープ内で、かつ異なる特定性で宣言できます。 たとえば、外側のブロックには特定の MySQL エラーコードハンドラが、また内側のブロックには一般的な SQLWARNING
ハンドラが存在する可能性があります。 あるいは、特定の MySQL エラーコードのハンドラと、一般的な SQLWARNING
クラスのハンドラが同じブロック内に存在することもあります。
あるハンドラがアクティブ化されるかどうかは、それ自体のスコープや条件値だけでなく、ほかにどのようなハンドラが存在するかによっても異なります。 ストアドプログラム内で条件が発生すると、サーバーは、適用可能なハンドラを現在のスコープ (現在の BEGIN ... END
ブロック) 内で検索します。 適用可能なハンドラが存在しない場合は、連続した包含する各スコープ (ブロック) 内のハンドラに関して外側に検索を続行します。 特定のスコープで適用可能なハンドラを 1 つ以上見つけると、サーバーは、次の条件の優先順位に基づいてそれらのハンドラから選択します。
MySQL エラーコードハンドラは、
SQLSTATE
値ハンドラより優先されます。SQLSTATE
値ハンドラは、一般的なSQLWARNING
、SQLEXCEPTION
、またはNOT FOUND
ハンドラより優先されます。SQLEXCEPTION
ハンドラは、SQLWARNING
ハンドラより優先されます。同じ優先順位を持つ適用可能なハンドラが複数存在する可能性があります。 たとえば、ステートメントが、それぞれに対してエラー固有のハンドラが存在する、異なるエラーコードを持つ複数の警告を生成する可能性があります。 この場合、サーバーがアクティブ化するハンドラの選択は非決定的であり、条件が発生する状況に応じて変わる可能性があります。
ハンドラ選択ルールの 1 つの側面として、複数の適用可能なハンドラが異なるスコープ内に存在する場合は、もっともローカルなスコープを持つハンドラが外側のスコープにあるハンドラより (それが、より具体的な条件のハンドラであっても) 優先される点があります。
ある条件が発生したときに適切なハンドラが存在しない場合、実行されるアクションはその条件のクラスによって異なります。
SQLEXCEPTION
条件の場合は、EXIT
ハンドラが存在するかのように、ストアドプログラムはその条件を発生させたステートメントで終了します。 そのプログラムが別のストアドプログラムから呼び出されていた場合は、呼び出し元プログラムが、独自のハンドラに適用されるハンドラ選択ルールを使用してその条件を処理します。SQLWARNING
条件の場合は、CONTINUE
ハンドラが存在するかのように、プログラムは実行を続行します。NOT FOUND
条件では、その条件が正常に発生した場合、アクションはCONTINUE
です。SIGNAL
またはRESIGNAL
によって発生した場合、アクションはEXIT
です。
次の例は、MySQL によってハンドラ選択ルールがどのように適用されるかを示しています。
次のプロシージャーには 2 つのハンドラが含まれています。つまり、存在しないテーブルを削除しようとする試みに対して発生する特定の SQLSTATE
値 ('42S02'
) 用に 1 つと、一般的な SQLEXCEPTION
クラス用に 1 つです。
CREATE PROCEDURE p1()
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.t;
END;
両方のハンドラが同じブロック内で宣言され、同じスコープを持っています。 ただし、SQLSTATE
ハンドラは SQLEXCEPTION
ハンドラより優先されるため、テーブル t
が存在しない場合、DROP TABLE
ステートメントは SQLSTATE
ハンドラをアクティブ化する条件を発生させます。
mysql> CALL p1();
+--------------------------------+
| msg |
+--------------------------------+
| SQLSTATE handler was activated |
+--------------------------------+
次のプロシージャーにも、同じ 2 つのハンドラが含まれています。 ただし、今回は、DROP TABLE
ステートメントと SQLEXCEPTION
ハンドラが SQLSTATE
ハンドラに対して内側のブロック内にあります。
CREATE PROCEDURE p2()
BEGIN -- outer block
DECLARE CONTINUE HANDLER FOR SQLSTATE '42S02'
SELECT 'SQLSTATE handler was activated' AS msg;
BEGIN -- inner block
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
SELECT 'SQLEXCEPTION handler was activated' AS msg;
DROP TABLE test.t; -- occurs within inner block
END;
END;
この場合は、条件が発生した場所に対してよりローカルなハンドラが優先されます。 SQLSTATE
ハンドラより一般的であるにもかかわらず、SQLEXCEPTION
ハンドラがアクティブ化されます。
mysql> CALL p2();
+------------------------------------+
| msg |
+------------------------------------+
| SQLEXCEPTION handler was activated |
+------------------------------------+
次のプロシージャーでは、ハンドラの 1 つが、DROP TABLE
ステートメントのスコープに対して内側のブロック内で宣言されています。
CREATE PROCEDURE p3()
BEGIN -- outer block
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
SELECT 'SQLEXCEPTION handler was activated' AS msg;
BEGIN -- inner block
DECLARE CONTINUE HANDLER FOR SQLSTATE '42S02'
SELECT 'SQLSTATE handler was activated' AS msg;
END;
DROP TABLE test.t; -- occurs within outer block
END;
もう一方のハンドラが DROP TABLE
によって発生した条件のスコープ内にないため、SQLEXCEPTION
ハンドラのみが適用されます。
mysql> CALL p3();
+------------------------------------+
| msg |
+------------------------------------+
| SQLEXCEPTION handler was activated |
+------------------------------------+
次のプロシージャーでは、両方のハンドラが、DROP TABLE
ステートメントのスコープに対して内側のブロック内で宣言されています。
CREATE PROCEDURE p4()
BEGIN -- outer block
BEGIN -- inner block
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.t; -- occurs within outer block
END;
DROP TABLE
のスコープ内にないため、どちらのハンドラも適用されません。 このステートメントによって発生した条件は未処理になり、プロシージャーをエラーで終了させます。
mysql> CALL p4();
ERROR 1051 (42S02): Unknown table 'test.t'