用作上下文管理器的sqlite3连接的事务不是原子的

问题描述 投票:1回答:1

According to the documentation

连接对象可以用作自动提交或回滚事务的上下文管理器。如果发生异常,则回滚事务;否则,交易承诺:

我知道with语句中的所有内容都应该是原子事务。现在考虑这段代码

import sqlite3
con = sqlite3.connect(':memory:')

try:
  with con:
    con.execute('create table foo (id integer primary key)')
    con.execute('insert into foo values (1)')
    con.execute('insert into foo values (1)')
except sqlite3.Error:
  print('transaction failed')

try:
  rec = con.execute('select count(*) from foo')
  print('number of records: {}'.format(rec.fetchone()[0]))
except sqlite3.Error as e:
  print(e)

返回

transaction failed
number of records: 0

一方面,由于重复的值,交易失败。另一方面,表foo存在,即使它是空的,这意味着第一个插入已经回滚。表创建不应该回滚吗?

“手动”执行交易会产生预期结果:

import sqlite3
con = sqlite3.connect(':memory:')

con.execute('begin')
try:
  con.execute('create table foo (id integer primary key)')
  con.execute('insert into foo values (1)')
  con.execute('insert into foo values (1)')
  con.execute('commit')
except sqlite3.Error:
  con.execute('rollback')
  print('transaction failed')

try:
  rec = con.execute('select count(*) from foo')
  print('number of records: {}'.format(rec.fetchone()[0]))
except sqlite3.Error as e:
  print(e)

回报

transaction failed
no such table: foo

为什么会出现差异?

python sqlite
1个回答
2
投票

从Python 3.6,DDL或Data Defitinion Language语句开始,如CREATE TABLE,不会启动事务。这意味着任何此类语句在您执行时都会自动提交。

Controlling transactions section

默认情况下,sqlite3模块在数据修改语言(DML)语句(即INSERT / UPDATE / DELETE / REPLACE)之前隐式打开事务。

[...]

在版本3.6中更改:sqlite3用于在DDL语句之前隐式提交打开的事务。这已不再是这种情况。

这意味着如果您希望DDL语句成为事务的一部分,则必须显式启动事务。

使用连接作为上下文管理器仍然只在退出时发出提交或回滚,它不会启动事务;相反,遇到的第一个DML语句将启动一个。如果您希望DDL成为事务的一部分,请在顶部添加begin语句:

try:
    with con:
        con.execute('begin')  # explicit, rather than implicit, transaction start
        con.execute('create table foo (id integer primary key)')
        con.execute('insert into foo values (1)')
        con.execute('insert into foo values (1)')
except sqlite3.Error:
    print('transaction failed')
© www.soinside.com 2019 - 2024. All rights reserved.