13.6.7.6 处理程序的范围规则
存储的程序可以包括在程序中发生某些条件时要调用的处理程序。每个处理程序的适用性取决于其在程序定义中的位置以及其处理的条件:
- 在开始...结束块中声明的处理程序仅适用于该块中处理程序声明之后的 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
类的处理程序。
处理程序是否被激活不仅取决于其自身的范围和条件值,还取决于存在其他哪些处理程序。当存储的程序中发生条件时,服务器将在当前范围(当前开始...结束块)中搜索适用的处理程序。如果没有适用的处理程序,则在每个连续的包含范围(块)中 continue 使用处理程序向外搜索。当服务器在给定范围内找到一个或多个适用的处理程序时,它将根据条件优先级在它们之间进行选择:
-
MySQL 错误代码处理程序优先于
SQLSTATE
值处理程序。 -
SQLSTATE
值处理程序优先于常规SQLWARNING
,SQLEXCEPTION
或NOT FOUND
处理程序。 -
SQLEXCEPTION
处理程序优先于SQLWARNING
处理程序。 -
可能有多个具有相同优先级的适用处理程序。例如,一条语句可能会生成带有不同错误代码的多个警告,每个警告都存在一个特定于错误的处理程序。在这种情况下,服务器激活哪个处理程序的选择是不确定的,并且可能会根据条件发生的情况而改变。
处理程序选择规则的一个含义是,如果多个适用的处理程序出现在不同的作用域中,则具有最局部作用域的处理程序将优先于外部作用域中的处理程序,甚至优先于更特定条件的处理程序。
如果条件发生时没有适当的处理程序,则采取的操作取决于条件的类:
-
对于
SQLEXCEPTION
条件,存储程序终止于引发该条件的语句,就像存在EXIT
处理程序一样。如果该程序被另一个存储程序调用,则调用程序使用应用于其自己的处理程序的处理程序选择规则来处理该条件。 -
对于
SQLWARNING
条件,程序将 continue 执行,就像有CONTINUE
处理程序一样。 -
对于
NOT FOUND
条件,如果条件正常升高,则动作为CONTINUE
。如果它是由SIGNAL或RESIGNAL引发的,则动作为EXIT
。
以下示例演示 MySQL 如何应用处理程序选择规则。
此过程包含两个处理程序,一个用于处理试图删除不存在的 table 的特定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
处理程序,因此,如果 tablet
不存在,则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'