Я пытаюсь перенести некоторый код на 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 во второй строке после выполнения обновления.
Согласно документам, оператор with sql
должен устанавливать неявную транзакцию вокруг содержимого, которая фиксируется только в случае успешного завершения блока. Однако, когда я запускаю его, я получаю ожидаемую ошибку SQL... но значение i установлено от 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()
Это ведет себя точно так же --- i меняется с 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()
, тогда он работает (я остаюсь на 99)!
(Я также должен добавить, что если я помещу begin
и commit
во внутренний вызов executescript
, то он будет вести себя правильно во всех случаях, но, к сожалению, я не могу использовать этот подход в своем приложении. Кроме того, изменение sql.isolation_level
, похоже, не имеет значения. к поведению.)
Может кто-нибудь объяснить мне, что здесь происходит? Мне нужно понять это; если я не могу доверять транзакциям в базе данных, я не могу заставить свое приложение работать...
Python 2.7, python-sqlite3 2.6.0, sqlite3 3.7.13, Debian.