Я пытаюсь перенести некоторый код на Python, который использует базы данных sqlite, и я пытаюсь заставить транзакции работать, и я действительно запутался. Я действительно смущен этим; Я много использовал sqlite на других языках, потому что это здорово, но я просто не могу понять, что здесь не так.
Вот схема для моей тестовой базы данных (которая будет передана в инструмент командной строки sqlite3).
BEGIN TRANSACTION;
CREATE TABLE test (i integer);
INSERT INTO "test" VALUES(99);
COMMIT;
Вот тестовая программа.
import sqlite3
sql = sqlite3.connect("test.db")
with sql:
c = sql.cursor()
c.executescript("""
update test set i = 1;
fnord;
update test set i = 0;
""")
Вы можете заметить намеренную ошибку. Это приводит к сбою SQL script во второй строке после выполнения обновления.
В соответствии с документами оператор with sql
должен установить неявную транзакцию вокруг содержимого, которая выполняется только в том случае, если блок успешно завершен. Тем не менее, когда я запускаю его, я получаю ожидаемую ошибку SQL... но значение я устанавливается с 99 на 1. Я ожидаю, что он останется на уровне 99, потому что это первое обновление должно быть отброшено.
Вот еще одна тестовая программа, которая явно вызывает commit()
и rollback()
.
import sqlite3
sql = sqlite3.connect("test.db")
try:
c = sql.cursor()
c.executescript("""
update test set i = 1;
fnord;
update test set i = 0;
""")
sql.commit()
except sql.Error:
print("failed!")
sql.rollback()
Это ведет себя точно так же --- меня изменяет с 99 на 1.
Теперь я вызываю BEGIN и COMMIT явно:
import sqlite3
sql = sqlite3.connect("test.db")
try:
c = sql.cursor()
c.execute("begin")
c.executescript("""
update test set i = 1;
fnord;
update test set i = 0;
""")
c.execute("commit")
except sql.Error:
print("failed!")
c.execute("rollback")
Это тоже не так, но по-другому. Я получаю следующее:
sqlite3.OperationalError: cannot rollback - no transaction is active
Однако, если я заменил вызовы на c.execute()
на c.executescript()
, то он работает (i остается на 99)!
(Я должен также добавить, что если я поместил begin
и commit
во внутренний вызов executescript
, тогда он будет вести себя корректно во всех случаях, но, к сожалению, я не могу использовать этот подход в своем приложении. дополнение, изменение sql.isolation_level
, по-видимому, не имеет никакого отношения к поведению.)
Может кто-нибудь объяснить мне, что здесь происходит? Мне нужно это понять; если я не могу доверять транзакциям в базе данных, я не могу заставить свое приложение работать...
Python 2.7, python-sqlite3 2.6.0, sqlite3 3.7.13, Debian.