Вложенные транзакции с SQLAlchemy и sqlite

Я пишу приложение на Python, используя SQLAlchemy (и Elixir) с SQLite в качестве базы данных. Я начинаю новую транзакцию, используя код 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).

Как заставить вложенные транзакции работать?

Ответ 1

Я столкнулся с этой проблемой, используя вложенные транзакции, используя Python 3 в Windows. Я использую SQLite версии 3.8.11, поэтому SAVEPOINT должен поддерживаться. По-видимому, установка pysqlite для меня не является вариантом, поскольку она не поддерживает Python 3.

Через несколько часов, ударив головой о стол, я увидел этот раздел в документации:

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

В разделе "Поведение блокировки базы данных / Concurrency" мы ссылаемся на ассортимент драйверов pysqlite, которые предотвращают несколько возможности SQLite работать корректно. Драйвер pysqlite DBAPI имеет несколько давних ошибок, которые влияют на правильность его транзакционное поведение. В режиме работы по умолчанию SQLite такие функции, как изоляция SERIALIZABLE, DDL транзакции и Поддержка SAVEPOINT нефункциональна, и для использования этих функции, обходные пути должны быть приняты.

Проблема заключается в том, что драйвер пытается вторгаться в пользователи не могут начать транзакции и иногда заканчивают их преждевременно, чтобы свести к минимуму файл данных SQLite databasess хотя сам SQLite использует "общие" блокировки для только для чтения.

SQLAlchemy предпочитает не изменять это поведение по умолчанию, так как это долгожданное поведение драйвера pysqlite; если и когда Драйвер pysqlite пытается устранить эти проблемы, и это будет больше драйвер по умолчанию для SQLAlchemy.

Хорошей новостью является то, что с несколькими событиями мы можем реализовать полностью поддерживать транзакционную поддержку, полностью отключив функцию pysqlites и испуская НАЧАТЬ себя. Это достигается с помощью двух событий Слушатели:

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 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 полезны (это используется в приведенном выше примере):

Отладка (отображение) команды SQL, отправленной в db по SQLAlchemy

Ответ 2

Несмотря на то, что sqlite, похоже, поддерживает вложенные транзакции через SAVEPOINT, он имеет значение только версия 3.6.8, выпущенное 12 января 2009 года. Python, по крайней мере до версии 2.2, использует более ранние версии:

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. Я не могу сказать точно, что это решит вашу проблему, но я верю, что ответ объясняет, почему он сейчас не работает для вас.

Ответ 3

SQLAlchemy использует pysqlite для взаимодействия с базой данных SQLite, если я не ошибаюсь, pysqlite по умолчанию завершает любой запрос, который вы отправляете в транзакции.

Ответ может заключаться в правильной установке уровня изоляции при подключении.

Некоторая дискуссия об этом здесь