首页 > 解决方案 > 为什么设置无效的枚举值不会导致调用错误处理程序?

问题描述

我有两张桌子。Mysql 5.7,表有 Innodb 存储引擎。

create table test_tran (id integer, descr varchar(255));

create table test_enum (id int, flag enum('good','not good') not null);

一个有效的记录被插入到 test_enum 表中

insert into test_enum values(1,'good')

我已经定义了事务的存储过程,它试图更新枚举列中的无效值。它还捕获错误以回滚事务,然后重新发出错误信号。

SET sql_mode = 'STRICT_ALL_TABLES';
DROP PROCEDURE IF EXISTS test_tran;
DELIMITER $$

CREATE PROCEDURE test_tran
(
 
i_case integer
)
BEGIN

    DECLARE EXIT HANDLER FOR SQLEXCEPTION
    BEGIN
        ROLLBACK;  
        RESIGNAL;
    END;
    

    START TRANSACTION;
    

    IF i_case=0 THEN
      insert into test_tran values (1, 'abc');
      update test_enum set flag='random'; 
    ELSE
    
      insert into test_tran values (3, 'else_1_abc');
      insert into test_tran values (4, 'else_2_abc');
      END IF; 
    
    COMMIT;
    


END $$

DELIMITER ;

我执行

call test_tran(0)

并得到一个错误 - “错误代码:1265。数据被截断列'标志'在行”

我期待下面的语句回滚

 insert into test_tran values (1, 'abc');

但是,我看到它正在提交,并且我看到 test_tran 表中插入的行。是什么导致了这个问题?

标签: mysqlsql

解决方案


错误消息中缺少关键信息:SQLSTATE。完整的错误信息应该是:

错误:1265 (01000):第 1 行的列“标志”的数据被截断

(可能正在使用的客户端未在错误消息中包含 SQLSTATE;以上是 MySQL Shell 生成的内容。)

状态的文档DECLARE ... HANDLER

condition_valueDECLARE ... HANDLER表示激活处理程序的特定条件或条件类别。它可以采取以下形式:

...

  • SQLWARNING:以 '01' 开头的 SQLSTATE 值类的简写。

...

  • SQLEXCEPTION:不以“00”、“01”或“02”开头的 SQLSTATE 值类的简写。

通过设置无效枚举列生成的错误是 SQLWARNING 类的一部分,而不是 SQLEXCEPTION,因此永远不会调用错误处理程序,并且在过程退出后事务仍然处于活动状态,这就是插入的行出现在test_tran. 请注意,此时可以手动回滚事务;此外,关闭会话将导致自动回滚。

要处理 中的错误test_tran,只需将 SQLEXCEPTION 更改为 SQLWARNING,或者(可能更合适)向处理程序添加条件:

CREATE PROCEDURE test_tran (i_case integer)
BEGIN

    DECLARE EXIT HANDLER FOR SQLWARNING, NOT FOUND, SQLEXCEPTION
    BEGIN
        ROLLBACK;  
        RESIGNAL;
    END;
    ...

其他读者注意:有些人可能想知道为什么DECLARE ... HANDLER需要它;为什么整个交易没有被错误取消。InnoDB 错误处理页面描述了 何时发生隐式回滚。整个事务自动回滚的唯一情况是存在事务死锁,或者在等待时锁超时并且服务器以--innodb-rollback-on-timeoutset 启动。将列设置为无效enum

[is] 主要被 MySQL 代码层(在 InnoDB 存储引擎级别之上)检测到,它们会回滚相应的 SQL 语句。锁不会在单个 SQL 语句的回滚中释放。

换句话说,设置无效枚举值的查询被回滚,但事务不是。


推荐阅读