15.6.7.3 获取诊断语句
GET [CURRENT | STACKED] DIAGNOSTICS {
statement_information_item
[, statement_information_item] ...
| CONDITION condition_number
condition_information_item
[, condition_information_item] ...
}
statement_information_item:
target = statement_information_item_name
condition_information_item:
target = condition_information_item_name
statement_information_item_name: {
NUMBER
| ROW_COUNT
}
condition_information_item_name: {
CLASS_ORIGIN
| SUBCLASS_ORIGIN
| RETURNED_SQLSTATE
| MESSAGE_TEXT
| MYSQL_ERRNO
| CONSTRAINT_CATALOG
| CONSTRAINT_SCHEMA
| CONSTRAINT_NAME
| CATALOG_NAME
| SCHEMA_NAME
| TABLE_NAME
| COLUMN_NAME
| CURSOR_NAME
}
condition_number, target:
(see following discussion)
SQL 语句生成诊断信息,填充诊断区。语句GET DIAGNOSTICS
使应用程序检查此信息。(您也可以使用SHOW WARNINGS
或SHOW ERRORS
查看条件或错误。)
执行GET DIAGNOSTICS
不需要特殊权限。
关键字CURRENT
表示从当前诊断区中检索信息,关键字STACKED
表示从第二个诊断区中检索信息,只有在当前上下文是条件处理器时可用。如果不给出关键字,默认使用当前诊断区。
GET DIAGNOSTICS
语句通常在存储程序中使用。它是 MySQL 扩展,允许GET [CURRENT] DIAGNOSTICS
在任何 SQL 语句执行后检查。例如,如果您使用 mysql 客户端程序,可以在提示符下输入这些语句:
mysql> DROP TABLE test.no_such_table;
ERROR 1051 (42S02): Unknown table 'test.no_such_table'
mysql> GET DIAGNOSTICS CONDITION 1
@p1 = RETURNED_SQLSTATE, @p2 = MESSAGE_TEXT;
mysql> SELECT @p1, @p2;
+-------+------------------------------------+
| @p1 | @p2 |
+-------+------------------------------------+
| 42S02 | Unknown table 'test.no_such_table' |
+-------+------------------------------------+
这个扩展只应用于当前诊断区,不应用于第二个诊断区,因为 GET STACKED DIAGNOSTICS
只能在当前上下文是条件处理器中。如果不是这样,会出现 GET STACKED DIAGNOSTICS when handler not active
错误。
关于诊断区的描述,请见第15.6.7.7节,“MySQL 诊断区”。简言之,它包含两种信息:
-
语句信息,例如条件的数量或影响行数。
-
条件信息,例如错误代码和消息。如果语句引发多个条件,这部分诊断区对每一个条件都有一个条件区。如果语句不引发条件,这部分诊断区为空。
对于产生三个条件的语句,诊断区包含语句和条件信息如下:
Statement information:
row count
... other statement information items ...
Condition area list:
Condition area 1:
error code for condition 1
error message for condition 1
... other condition information items ...
Condition area 2:
error code for condition 2:
error message for condition 2
... other condition information items ...
Condition area 3:
error code for condition 3
error message for condition 3
... other condition information items ...
GET DIAGNOSTICS
可以获取语句或条件信息,但不能在同一语句中获取两者:
-
获取语句信息,检索所需语句项到目标变量。这次
GET DIAGNOSTICS
将可用条件数和受影响行数分配到用户变量@p1
和@p2
:GET DIAGNOSTICS @p1 = NUMBER, @p2 = ROW_COUNT;
-
获取条件信息,指定条件号并检索所需条件项到目标变量。这次
GET DIAGNOSTICS
将 SQLSTATE 值和错误信息分配到用户变量@p3
和@p4
:GET DIAGNOSTICS CONDITION 1 @p3 = RETURNED_SQLSTATE, @p4 = MESSAGE_TEXT;
检索列表指定一个或多个
分配,各分配命名目标变量和语句或条件信息设计符,取决于语句是否获取语句或条件信息。target
= item_name
可以将有效 target
设计符用于存储项信息,可以是存储程序或函数参数、使用 DECLARE
声明的本地变量或用户定义变量。
有效的condition_number
标识符可以是存储程序或函数参数、存储程序本地变量、用户定义变量、系统变量或字面值。一个字符字面值可能包括一个_charset
引入者。如果条件号不是从1到条件区的信息数量范围内,出现警告,并且不清除诊断区。
当条件发生时,MySQL 不会填充GET DIAGNOSTICS
所识别的所有条件项目。例如:
mysql> GET DIAGNOSTICS CONDITION 1
@p5 = SCHEMA_NAME, @p6 = TABLE_NAME;
mysql> SELECT @p5, @p6;
+------+------+
| @p5 | @p6 |
+------+------+
| | |
+------+------+
在标准SQL中,如果有多个条件,第一个条件与前一个SQL语句返回的SQLSTATE
值相关。在MySQL中,这不保证。要获取主要错误,你不能这样做:
GET DIAGNOSTICS CONDITION 1 @errno = MYSQL_ERRNO;
相反,首先检索条件计数,然后使用它来指定要检查的条件号:
GET DIAGNOSTICS @cno = NUMBER;
GET DIAGNOSTICS CONDITION @cno @errno = MYSQL_ERRNO;
关于允许的语句和条件信息项目,以及在条件发生时被填充的哪些项目,请参见诊断区信息项目。
以下是一个使用GET DIAGNOSTICS
和存储过程上下文中的异常处理器来评估插入操作的结果。如果插入成功,程序使用GET DIAGNOSTICS
获取受影响行数。这表明可以多次使用GET DIAGNOSTICS
来获取关于语句的信息,只要当前诊断区还没有被清除。
CREATE PROCEDURE do_insert(value INT)
BEGIN
-- Declare variables to hold diagnostics area information
DECLARE code CHAR(5) DEFAULT '00000';
DECLARE msg TEXT;
DECLARE nrows INT;
DECLARE result TEXT;
-- Declare exception handler for failed insert
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
BEGIN
GET DIAGNOSTICS CONDITION 1
code = RETURNED_SQLSTATE, msg = MESSAGE_TEXT;
END;
-- Perform the insert
INSERT INTO t1 (int_col) VALUES(value);
-- Check whether the insert was successful
IF code = '00000' THEN
GET DIAGNOSTICS nrows = ROW_COUNT;
SET result = CONCAT('insert succeeded, row count = ',nrows);
ELSE
SET result = CONCAT('insert failed, error = ',code,', message = ',msg);
END IF;
-- Say what happened
SELECT result;
END;
假设t1.int_col
是一个声明为NOT NULL
的整数列。该程序在插入非空和空值时,分别产生以下结果:
mysql> CALL do_insert(1);
+---------------------------------+
| result |
+---------------------------------+
| insert succeeded, row count = 1 |
+---------------------------------+
mysql> CALL do_insert(NULL);
+-------------------------------------------------------------------------+
| result |
+-------------------------------------------------------------------------+
| insert failed, error = 23000, message = Column 'int_col' cannot be null |
+-------------------------------------------------------------------------+
当条件处理器激活时,诊断区栈推送发生:
-
第一个(当前)诊断区变为第二个(堆栈)诊断区,并创建一个新的当前诊断区作为它的副本。
-
GET [CURRENT] DIAGNOSTICS
和GET STACKED DIAGNOSTICS
可以在处理器中访问当前和堆栈诊断区的内容。 -
在处理程序开始时,两个诊断区返回相同的结果,因此可以通过当前诊断区获取激活处理程序的条件信息,只要不执行修改当前诊断区的语句。
-
然而,在处理程序中执行的语句可以修改当前诊断区,根据正常规则清除和设置其内容(见诊断区的清除和填充方式)。
获取处理程序激活条件的可靠方法是使用堆栈诊断区,除了
RESIGNAL
外,处理程序中的语句不能修改堆栈诊断区。关于当前诊断区的设置和清除,请见第15.6.7.7节,“MySQL 诊断区”。
下面示例展示了如何在处理程序中使用GET STACKED DIAGNOSTICS
获取已被处理的异常信息,即使当前诊断区已经被处理语句修改。
在存储过程p()
中,我们尝试将两个值插入一个包含TEXT NOT NULL
列的表。第一个值是非空字符串,第二个值是NULL
。该列不允许NULL
值,所以第一个插入成功,但第二个插入导致异常。该过程包括一个异常处理器,该处理器将尝试插入NULL
的操作映射到空字符串插入:
DROP TABLE IF EXISTS t1;
CREATE TABLE t1 (c1 TEXT NOT NULL);
DROP PROCEDURE IF EXISTS p;
delimiter //
CREATE PROCEDURE p ()
BEGIN
-- Declare variables to hold diagnostics area information
DECLARE errcount INT;
DECLARE errno INT;
DECLARE msg TEXT;
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
-- Here the current DA is nonempty because no prior statements
-- executing within the handler have cleared it
GET CURRENT DIAGNOSTICS CONDITION 1
errno = MYSQL_ERRNO, msg = MESSAGE_TEXT;
SELECT 'current DA before mapped insert' AS op, errno, msg;
GET STACKED DIAGNOSTICS CONDITION 1
errno = MYSQL_ERRNO, msg = MESSAGE_TEXT;
SELECT 'stacked DA before mapped insert' AS op, errno, msg;
-- Map attempted NULL insert to empty string insert
INSERT INTO t1 (c1) VALUES('');
-- Here the current DA should be empty (if the INSERT succeeded),
-- so check whether there are conditions before attempting to
-- obtain condition information
GET CURRENT DIAGNOSTICS errcount = NUMBER;
IF errcount = 0
THEN
SELECT 'mapped insert succeeded, current DA is empty' AS op;
ELSE
GET CURRENT DIAGNOSTICS CONDITION 1
errno = MYSQL_ERRNO, msg = MESSAGE_TEXT;
SELECT 'current DA after mapped insert' AS op, errno, msg;
END IF ;
GET STACKED DIAGNOSTICS CONDITION 1
errno = MYSQL_ERRNO, msg = MESSAGE_TEXT;
SELECT 'stacked DA after mapped insert' AS op, errno, msg;
END;
INSERT INTO t1 (c1) VALUES('string 1');
INSERT INTO t1 (c1) VALUES(NULL);
END;
//
delimiter ;
CALL p();
SELECT * FROM t1;
当处理器激活时,当前诊断区的副本被推送到诊断区栈中。处理器首先显示当前和栈中的诊断区内容,初始情况下它们都是相同的:
+---------------------------------+-------+----------------------------+
| op | errno | msg |
+---------------------------------+-------+----------------------------+
| current DA before mapped insert | 1048 | Column 'c1' cannot be null |
+---------------------------------+-------+----------------------------+
+---------------------------------+-------+----------------------------+
| op | errno | msg |
+---------------------------------+-------+----------------------------+
| stacked DA before mapped insert | 1048 | Column 'c1' cannot be null |
+---------------------------------+-------+----------------------------+
GET DIAGNOSTICS
语句执行后可能会重置当前诊断区。例如,处理器将NULL
插入映射到空字符串插入,并显示结果。新的插入成功,但当前诊断区被清除,而栈中的诊断区仍然保持不变,仍包含激活处理器的信息:
+----------------------------------------------+
| op |
+----------------------------------------------+
| mapped insert succeeded, current DA is empty |
+----------------------------------------------+
+--------------------------------+-------+----------------------------+
| op | errno | msg |
+--------------------------------+-------+----------------------------+
| stacked DA after mapped insert | 1048 | Column 'c1' cannot be null |
+--------------------------------+-------+----------------------------+
当条件处理器结束时,它们当前的诊断区从栈中弹出,并且存储过程中的栈中的诊断区变为当前诊断区。
在过程返回后,表中包含两个行。空行来自尝试插入NULL
并被映射到空字符串插入:
+----------+
| c1 |
+----------+
| string 1 |
| |
+----------+