Documentation Home
MySQL 8.4 Reference Manual
Related Documentation Download this Manual
PDF (US Ltr) - 39.8Mb
PDF (A4) - 39.9Mb
Man Pages (TGZ) - 257.9Kb
Man Pages (Zip) - 364.9Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb


MySQL 8.4 Reference Manual  /  ...  /  Scope Rules for Handlers

15.6.7.6 存储程序的作用域规则

存储程序可能包含处理器,以便在特定条件下被调用。每个处理器的适用性取决于其在程序定义中的位置和它所处理的条件或条件:

  • BEGIN ... END块中声明的处理器,只在该块中的SQL语句后面生效。如果处理器本身引发了条件,它不能处理该条件,也不能其他在该块中声明的处理器。在以下示例中,处理器H1H2只对stmt1stmt2语句中的条件生效。但是,H1H2不能对在H1H2体内引发的条件生效。

    BEGIN -- outer block
      DECLARE EXIT HANDLER FOR ...;  -- handler H1
      DECLARE EXIT HANDLER FOR ...;  -- handler H2
      stmt1;
      stmt2;
    END;
  • 处理器只在其声明的块中生效,并且不能被激活以响应该块外部发生的条件。在以下示例中,处理器H1对inner块中的stmt1语句生效,但对outer块中的stmt2语句不生效:

    BEGIN -- outer block
      BEGIN -- inner block
        DECLARE EXIT HANDLER FOR ...;  -- handler H1
        stmt1;
      END;
      stmt2;
    END;
  • 处理器可以是特定的或通用的。特定的处理器是针对MySQL错误代码、SQLSTATE值或条件名称的。通用的处理器是针对SQLWARNINGSQLEXCEPTIONNOT FOUND类中的条件。条件的特定性与条件优先级相关,如后文所述。

可以在不同的作用域和不同具体性中声明多个处理器。例如,在外部块中可能有特定的MySQL错误代码处理器,而在内部块中可能有通用的SQLWARNING处理器。或者,在同一个块中可能有对特定MySQL错误代码和通用SQLWARNING类的处理器。

是否激活处理器不仅取决于其自身的作用域和条件值,还取决于其他处理器的存在。当在存储程序中出现某个条件时,服务器将在当前作用域(当前BEGIN ... END块)中搜索适用的处理器。如果没有找到适用处理器,服务器将继续向外部作用域(块)中搜索。直到找到一个或多个适用处理器为止。在服务器找到给定作用域中的适用处理器时,它将根据条件优先级进行选择:

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

  • SQLSTATE值处理器优先于通用的SQLWARNINGSQLEXCEPTIONNOT FOUND处理器。

  • SQLEXCEPTION处理器优先于SQLWARNING处理器。

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

存储处理程序选择规则的一个结果是,如果多个适用处理器在不同的作用域中出现,具有最小作用域的处理器将优先于外部作用域中的处理器,即使它们是为更具体的条件指定的。

如果在某个情况下没有合适的处理器时,取决于该情况的类别:

  • 对于SQLEXCEPTION情况,存储程序将在引发该情况的语句处终止,就像存在一个EXIT处理器一样。如果该程序是由另一个存储程序调用来的,那么调用程序将使用其自己的处理器选择规则来处理该情况。

  • 对于SQLWARNING情况,程序将继续执行,就像存在一个CONTINUE处理器一样。

  • 对于NOT FOUND情况,如果该情况是正常引发的,那么行动是CONTINUE。如果它是由SIGNALRESIGNAL引发的,那么行动是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;

在这种情况下,条件发生的地方更近的处理程序优先。即使它比 SQLSTATE 处理程序更通用,也会激活 SQLEXCEPTION 处理程序:

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的作用域内,所以两个handler都不能应用。语句引发的条件未被处理,导致过程以错误终止:

mysql> CALL p4();
ERROR 1051 (42S02): Unknown table 'test.t'