使用APSW时出现“BusyError: 无法回滚保存点 - SQL语句正在进行中”的错误

2 投票
2 回答
702 浏览
提问于 2025-04-16 22:50

我正在使用Python的apsw库来操作SQLite数据库。我的代码是这样的:

with apsw.Connection(path) as t:
    c = t.cursor()
    c.execute(...)
    ... more code ...
    if c.execute(...).next()[0]:
        raise Exception

我本来希望with语句能创建一个保存点,而raise语句能让程序回到那个保存点(或者如果没有需要抛出的错误,就提交事务)。提交的时候没有问题,但当有错误需要抛出时,它却不愿意回滚,显示了:

BusyError: BusyError: cannot rollback savepoint - SQL statements in progress

我不知道该从哪里开始查问题。根据我的理解,这个错误意味着有另一个连接阻止了访问,但从代码上看并不是这样。如果真是这样,提交的时候不也应该失败吗?

我使用的是SQLite 3.7.7.1,配合apsw和Python 2.7。

2 个回答

3

这个问题出在SQLite这个数据库本身。它在2012年3月被修复了,修复的版本是3.7.11。从更新日志中可以看到:

之前未完成的操作不再阻止回滚(ROLLBACK)。而是,在回滚之后,下次访问这个未完成的操作时,会返回一个叫做SQLITE_ABORT的状态。

3

好吧,我找到了:

if c.execute(...).next()[0]:
    raise Exception

问题在于,当我用 next() 获取下一行数据时,底层的游标仍然处于活动状态,随时准备返回更多行。这需要显式地关闭:

if c.execute(...).next()[0]:
    c.close()
    raise Exception

或者通过读取所有数据来隐式关闭:

if list(c.execute(...))[0][0]:
    raise Exception

更新一下。为了方便,我写了一个 Python 类,它封装了 apsw.Cursor 并提供了 上下文管理器,这样我就可以写:

with Cursor(connection) as c:
    c.execute(...)

撰写回答