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
对inner块中的stmt1
语句生效,但对outer块中的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
处理器。 -
可能会出现多个适用处理器具有相同的优先级的情况。例如,语句可以生成多个警告,每个警告都对应一个特定的错误代码,对应一个错误特定的处理器。在这种情况下,服务器激活哪个处理器是非确定性的,并且可能会根据条件出现的 circumstances 而改变。
存储处理程序选择规则的一个结果是,如果多个适用处理器在不同的作用域中出现,具有最小作用域的处理器将优先于外部作用域中的处理器,即使它们是为更具体的条件指定的。
如果在某个情况下没有合适的处理器时,取决于该情况的类别:
以下示例演示了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'