Fix delete_where() leaving the connection in an open transaction

Table.delete_where() ran its DELETE via a bare execute() with no
commit, unlike Table.delete() which wraps in db.atomic(). The
connection was left with an open implicit transaction, so the
deletion (and all subsequent writes, including later atomic()
blocks which switched to savepoint mode) was silently rolled back
when the connection closed.

Wrap the DELETE in db.atomic() and remove the now-unnecessary
atomic() wrapper from the delete_where documentation example.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UnLnhsH25Nnv7LHhekUfPd
This commit is contained in:
Claude 2026-07-04 17:57:35 +00:00
commit b17c37144e
No known key found for this signature in database
3 changed files with 21 additions and 3 deletions

View file

@ -984,8 +984,7 @@ You can delete all records in a table that match a specific WHERE statement usin
>>> db = sqlite_utils.Database("dogs.db")
>>> # Delete every dog with age less than 3
>>> with db.atomic():
>>> db.table("dogs").delete_where("age < ?", [3])
>>> db.table("dogs").delete_where("age < ?", [3])
Calling ``table.delete_where()`` with no other arguments will delete every row in the table.

View file

@ -2966,7 +2966,8 @@ class Table(Queryable):
sql = "delete from {}".format(quote_identifier(self.name))
if where is not None:
sql += " where " + where
self.db.execute(sql, where_args or [])
with self.db.atomic():
self.db.execute(sql, where_args or [])
if analyze:
self.analyze()
return self

View file

@ -1,3 +1,6 @@
import sqlite_utils
def test_delete_rowid_table(fresh_db):
table = fresh_db["table"]
table.insert({"foo": 1}).last_pk
@ -32,6 +35,21 @@ def test_delete_where_all(fresh_db):
assert table.count == 0
def test_delete_where_commits(tmpdir):
path = str(tmpdir / "test.db")
db = sqlite_utils.Database(path)
db["table"].insert_all([{"id": i} for i in range(5)], pk="id")
db["table"].delete_where("id > ?", [2])
# The connection must not be left inside an open transaction,
# otherwise subsequent atomic() blocks never commit either
assert not db.conn.in_transaction
db["table"].insert({"id": 100})
db.close()
db2 = sqlite_utils.Database(path)
assert [r["id"] for r in db2["table"].rows] == [0, 1, 2, 100]
db2.close()
def test_delete_where_analyze(fresh_db):
table = fresh_db["table"]
table.insert_all(({"id": i, "i": i} for i in range(10)), pk="id")