8

我正在使用 SQLAlchemy(和 Elixir)和 SQLite 作为数据库后端在 Python 中编写应用程序。我使用代码开始一个新事务session.begin_transaction(),但是当我打电话时,session.rollback()我收到以下错误:

sqlalchemy.exceptions.OperationalError: (OperationalError) no such savepoint: sa_savepoint_1 u'ROLLBACK TO SAVEPOINT sa_savepoint_1' []

我也收到类似的错误调用session.commit()。据我所知,sqlite 支持 SAVEPOINTS ( http://www.sqlite.org/lang_savepoint.html )。

如何让嵌套事务工作?

4

3 回答 3

11

我在 Windows 上使用 Python 3 使用嵌套事务遇到了这个问题。我使用的是 SQLite 版本 3.8.11,所以SAVEPOINT应该支持。显然安装 pysqlite 对我来说不是一个选项,因为它不支持 Python 3。

在把我的头撞在桌子上几个小时之后,我在文档中看到了这个部分:

http://docs.sqlalchemy.org/en/latest/dialects/sqlite.html#serializable-isolation-savepoints-transactional-ddl

在数据库锁定行为/并发部分中,我们提到了 pysqlite 驱动程序的各种问题,这些问题会阻止 SQLite 的几个功能正常工作。pysqlite DBAPI 驱动程序有几个长期存在的错误,这些错误会影响其事务行为的正确性。在其默认操作模式下,SERIALIZABLE 隔离、事务 DDL 和 SAVEPOINT 支持等 SQLite 特性是无效的,为了使用这些特性,必须采取变通方法。

问题本质上是驱动程序试图猜测用户的意图,未能启动事务,有时会过早结束它们,以尽量减少 SQLite 数据库的文件锁定行为,即使 SQLite 本身使用“共享”锁进行读取-只有活动。

SQLAlchemy 默认选择不改变这种行为,因为它是 pysqlite 驱动程序的长期预期行为;如果以及当 pysqlite 驱动程序尝试修复这些问题时,这将更多地成为 SQLAlchemy 的默认驱动程序。

好消息是,通过一些事件,我们可以完全实现事务支持,方法是完全禁用 pysqlite 的功能并自己发出 BEGIN。这是使用两个事件侦听器实现的:

from sqlalchemy import create_engine, event

engine = create_engine("sqlite:///myfile.db")

@event.listens_for(engine, "connect")
def do_connect(dbapi_connection, connection_record):
    # disable pysqlite's emitting of the BEGIN statement entirely.
    # also stops it from emitting COMMIT before any DDL.
    dbapi_connection.isolation_level = None

@event.listens_for(engine, "begin")
def do_begin(conn):
    # emit our own BEGIN
    conn.execute("BEGIN")

添加上面的听众完全解决了我的问题!

我已经发布了一个完整的工作示例作为要点:

https://gist.github.com/snorfalorpagus/c48770e7d1fcb9438830304c4cca24b9

我还发现记录 SQL 语句很有帮助(这在上面的示例中使用):

SQLAlchemy 发送到数据库的调试(显示)SQL 命令

于 2016-04-06T16:48:08.457 回答
3

尽管 sqlite 似乎确实通过 SAVEPOINT 支持嵌套事务,但它仅在2009 年 1 月 12 日发布的 3.6.8 版本中。Python,至少到 v2.6,使用早期版本:

c:\svn\core\apps\general>python
Python 2.6.2 (r262:71605, Apr 14 2009, 22:40:02) [MSC v.1500 32 bit (Intel)] on win32
>>> import sqlite3 as s
>>> s.sqlite_version
'3.5.9'

我相信你可以自己安装PySqlite并且最新的似乎支持 v3.6.12。我不能肯定地说这会解决你的问题,但我相信答案解释了为什么它现在不适合你。

于 2009-12-08T14:17:27.670 回答
0

SQLAlchemy 使用 pysqlite 与 SQLite 数据库进行交互,如果我没记错的话,pysqlite 默认情况下会包装您在事务中发送的任何查询。

答案可能在于在连接时正确设置隔离级别。

关于这里的一些讨论

于 2009-10-31T17:01:33.090 回答