如果在存储过程中的事务期间发生错误,默认情况下会发生什么情况?

What happens by default if an error occurs during a transaction in a stored procedure?

假设您有一个如下所示的存储过程:

CREATE OR REPLACE
PROCEDURE usp_do_something (
    IN param1 INT,
    IN param2 INT,
    IN param3 INT
)
MODIFIES SQL DATA
BEGIN
    START TRANSACTION;

    INSERT `table1` ( `ID` ) VALUES (param1);
    INSERT `table2` ( `ID` ) VALUES (param2);
    INSERT `table3` ( `ID` ) VALUES (param3);

    COMMIT;
END;

然后使用一组参数值调用存储过程,这会导致 INSERT 操作之一失败,引发 SQLEXCEPTION.

本例中没有明确的 ROLLBACK 命令。测试表明,当 SQLEXCEPTION 发生在 COMMIT; 语句之前时,事务未提交。存储过程结束时事务是否隐式回滚?还是事务会一直挂起直到发生某种超时?这会导致存储过程在失败后保持锁定一段时间吗?

我知道我可以使用 DECLARE HANDLER 在失败时显式触发 ROLLBACK,如 this question 中所述,但我找不到任何文档来描述 MariaDB 在存储 -在没有任何显式 ROLLBACK.

的情况下,过程事务在到达 COMMIT 语句之前失败

是否有 MariaDB/MySQL 日志或事务状态 table 可以让我查看服务器正在处理此事务的情况?

当存储过程失败时,执行被中止,但事务未完成

看例子:

CREATE TABLE table1 (ID INT CHECK (id < 100));
CREATE TABLE table2 LIKE table1;
CREATE TABLE table3 LIKE table1;
CREATE PROCEDURE usp_do_something (
    IN param1 INT,
    IN param2 INT,
    IN param3 INT
)
MODIFIES SQL DATA
BEGIN
    START TRANSACTION;

    INSERT `table1` ( `ID` ) VALUES (param1);
    INSERT `table2` ( `ID` ) VALUES (param2);
    INSERT `table3` ( `ID` ) VALUES (param3);

    COMMIT;
END;

插入数据没有错误。已插入所有数据。

CALL usp_do_something (10, 20, 30);
SELECT *, '1' tablenum FROM table1 UNION ALL
SELECT *, '2' tablenum FROM table2 UNION ALL
SELECT *, '3' tablenum FROM table3 ORDER BY ID;
ID | tablenum
-: | :-------
10 | 1       
20 | 2       
30 | 3       

在第二条语句中插入错误数据。插入错误 (40) 之前的所有数据。

CALL usp_do_something (40, 500, 60);
Check constraint 'table2_chk_1' is violated.
SELECT *, '1' tablenum FROM table1 UNION ALL
SELECT *, '2' tablenum FROM table2 UNION ALL
SELECT *, '3' tablenum FROM table3 ORDER BY ID;
ID | tablenum
-: | :-------
10 | 1       
20 | 2       
30 | 3       
40 | 1       

在第二条语句中插入错误数据。插入错误 (70) 之前的所有数据。由于未提交的事务,最后一次调用可能会回滚。但是在前一个块(40)中插入的数据是由事务开始隐式提交的,不能回滚。

CALL usp_do_something (70, 800, 90);
Check constraint 'table2_chk_1' is violated.
SELECT *, '1' tablenum FROM table1 UNION ALL
SELECT *, '2' tablenum FROM table2 UNION ALL
SELECT *, '3' tablenum FROM table3 ORDER BY ID;

ROLLBACK;

SELECT *, '1' tablenum FROM table1 UNION ALL
SELECT *, '2' tablenum FROM table2 UNION ALL
SELECT *, '3' tablenum FROM table3 ORDER BY ID;
ID | tablenum
-: | :-------
10 | 1       
20 | 2       
30 | 3       
40 | 1       
70 | 1       

✓

ID | tablenum
-: | :-------
10 | 1       
20 | 2       
30 | 3       
40 | 1       

db<>fiddle here