当遇到错误查询时,MySQLi处理保存点的方式让我有点困惑。
以下是我遇到的问题的MRE:
$mysqli = new mysqli($_HOST, $_USER, $_PASS, $_DB_NAME);
$mysqli->begin_transaction();
$mysqli->savepoint("SP1");
$mysqli->query("INSERT INTO Users (Username, FavouriteColour) VALUES ('Alice', 'Red');");
$mysqli->savepoint("SP2");
$mysqli->query("INSERT INTO Users (Username, FavouriteColour) VALUES ('Bob', 'Green');");
$mysqli->savepoint("SP3");
$mysqli->query("INSERT INTO Users (Username, FavouriteColour) VALUES ('Charlie', 'Blue');");
$mysqli->query("CREATE TABLE Users (ID INT AUTO_INCREMENT NOT NULL PRIMARY KEY, Username VARCHAR(255), FavouriteColour VARCHAR(64));");
$mysqli->query("ROLLBACK TO SAVEPOINT SP2");
$mysqli->commit();
在这个小事务之后,最后两次添加应该已经回滚了,对吧?在那里肯定会发生这种情况
不是
事务处理过程中的无效查询。爱丽丝应该加上去,但鲍勃和查理不应该。
事务中间出现一个(不是语法上的,而是逻辑上的)无效查询似乎完全抛弃了MySQLi。MySQLi不是保留事务并让我回滚到我喜欢的时候,也不是撤销整个事务(这是可以理解的,因为其中有一个错误),而是保留整个事务
但删除所有保存点
。当我试图回滚到上面三个保存点中的任何一个时,它会给我一个“保存点不存在”的错误。因此,所有三个有效的查询都被提交了(即使我在最后没有手动提交它们,即使我试图回滚最后两个,如上所述)。
除非我在这里做错了什么,否则MySQLi决定处理错误的最佳方法是继续执行所有查询,但去掉所有保存点(即…如何处理错误)。如果我自己或MySQLi在这里很愚蠢,请告诉我!
请注意,当事务中没有无效查询时,保存点/回滚工作得非常好。执行第一个查询,然后执行第二个和第三个查询,但回滚到SP2会取消第二和第三次查询,因此只提交第一次查询。