MySQL 8.0 リファレンスマニュアル


MySQL 8.0 リファレンスマニュアル  /  ...  /  ハンドラのスコープに関するルール

13.6.7.6 ハンドラのスコープに関するルール

ストアドプログラムには、そのプログラム内で特定の条件が発生したときに呼び出されるハンドラを含めることができます。 各ハンドラの適用性は、プログラム定義の中でのそのハンドラの場所や、そのハンドラが処理する 1 つまたは複数の条件によって異なります。

  • BEGIN ... END ブロック内で宣言されたハンドラは、そのブロック内でハンドラ宣言のあとにある SQL ステートメントのスコープ内にしかありません。 そのハンドラ自体が条件を発生させた場合は、そのハンドラも、そのブロック内で宣言されているほかのどのハンドラもその条件を処理できません。 次の例では、ハンドラ H1H2 は、ステートメント stmt1 および stmt2 によって発生した条件のスコープ内にあります。 ただし、H1H2 も、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 値、または条件名を処理するためのものです。 一般的なハンドラとは、SQLWARNINGSQLEXCEPTION、または NOT FOUND クラス内の条件を処理するためのものです。 あとで説明されているように、条件の特定性は条件の優先順位に関連しています。

複数のハンドラを異なるスコープ内で、かつ異なる特定性で宣言できます。 たとえば、外側のブロックには特定の MySQL エラーコードハンドラが、また内側のブロックには一般的な SQLWARNING ハンドラが存在する可能性があります。 あるいは、特定の MySQL エラーコードのハンドラと、一般的な SQLWARNING クラスのハンドラが同じブロック内に存在することもあります。

あるハンドラがアクティブ化されるかどうかは、それ自体のスコープや条件値だけでなく、ほかにどのようなハンドラが存在するかによっても異なります。 ストアドプログラム内で条件が発生すると、サーバーは、適用可能なハンドラを現在のスコープ (現在の BEGIN ... END ブロック) 内で検索します。 適用可能なハンドラが存在しない場合は、連続した包含する各スコープ (ブロック) 内のハンドラに関して外側に検索を続行します。 特定のスコープで適用可能なハンドラを 1 つ以上見つけると、サーバーは、次の条件の優先順位に基づいてそれらのハンドラから選択します。

  • MySQL エラーコードハンドラは、SQLSTATE 値ハンドラより優先されます。

  • SQLSTATE 値ハンドラは、一般的な SQLWARNINGSQLEXCEPTION、または 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'

関連キーワード:  ステートメント, ハンドラ, CREATE, TABLE, 条件, DROP, スコープ, SQLEXCEPTION, 発生, SQLSTATE