文档首页
MySQL 8.4 参考手册
相关文档 下载本手册

MySQL 8.4 参考手册  /  ...  /  处理程序的作用域规则

15.6.7.6 处理程序的作用域规则

存储程序可以包含在程序中发生某些条件时要调用的处理程序。每个处理程序的适用性取决于它在程序定义中的位置以及它处理的条件。

  • 在 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 块)中搜索适用的处理程序。如果没有适用的处理程序,搜索将继续向外扩展,搜索每个后续包含作用域(块)中的处理程序。当服务器在给定作用域中找到一个或多个适用的处理程序时,它将根据条件优先级在它们之间进行选择。

  • MySQL 错误代码处理程序优先于 SQLSTATE 值处理程序。

  • SQLSTATE 值处理程序优先于通用的 SQLWARNING、SQLEXCEPTION 或 NOT FOUND 处理程序。

  • SQLEXCEPTION 处理程序优先于 SQLWARNING 处理程序。

  • 可以存在多个具有相同优先级的适用处理程序。例如,语句可以生成具有不同错误代码的多个警告,对于每个警告,都存在一个特定于错误的处理程序。在这种情况下,服务器激活哪个处理程序的选择是不确定的,可能会根据出现条件的情况而改变。

处理程序选择规则的一个含义是,如果多个适用的处理程序出现在不同的作用域中,具有最局部作用域的处理程序优先于外部作用域中的处理程序,即使对于更具体的条件也是如此。

如果条件发生时没有合适的处理程序,采取的操作取决于条件的类别。

  • 对于 SQLEXCEPTION 条件,存储程序将在引发条件的语句处终止,就像存在 EXIT 处理程序一样。如果程序被另一个存储程序调用,则调用程序将使用应用于其自身处理程序的处理程序选择规则来处理该条件。

  • 对于 SQLWARNING 条件,程序将继续执行,就像存在 CONTINUE 处理程序一样。

  • 对于 NOT FOUND 条件,如果条件是正常引发的,则操作为 CONTINUE。如果它是通过 SIGNAL 或 RESIGNAL 引发的,则操作为 EXIT。

以下示例演示了 MySQL 如何应用处理程序选择规则。

此过程包含两个处理程序,一个用于特定的 SQLSTATE 值('42S02'),该值用于尝试删除不存在的表,另一个用于通用的 SQLEXCEPTION 类。

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 |
+--------------------------------+

此过程包含相同的两个处理程序。但是这一次,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;

在这种情况下,更靠近条件发生位置的处理程序优先。SQLEXCEPTION 处理程序被激活,即使它比 SQLSTATE 处理程序更通用。

mysql> CALL p2();
+------------------------------------+
| msg                                |
+------------------------------------+
| SQLEXCEPTION handler was activated |
+------------------------------------+

在此过程中,其中一个处理程序是在 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;

只有 SQLEXCEPTION 处理程序适用,因为另一个处理程序不在 DROP TABLE 引发的条件的作用域内。

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'