mirror of
https://github.com/simonw/sqlite-utils.git
synced 2026-08-18 07:14:22 +02:00
Transaction cleanup no longer masks transaction-destroying errors
A RAISE(ROLLBACK) trigger or INSERT OR ROLLBACK conflict rolls back the
entire transaction and destroys every savepoint. The cleanup paths in
atomic() and query() then raised OperationalError ("no such savepoint" /
"cannot rollback - no transaction is active"), masking the original
IntegrityError - breaking user code that catches sqlite3.IntegrityError.
Cleanup now checks conn.in_transaction first: if the error already
destroyed the transaction there is nothing left to undo, and the
original exception propagates.
Refs https://github.com/simonw/sqlite-utils/issues/769#issuecomment-4900034150
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
c2a1774409
commit
d9a0fd26e0
3 changed files with 34 additions and 6 deletions
|
|
@ -280,3 +280,20 @@ def test_execute_returning_dicts(fresh_db):
|
|||
assert fresh_db.execute_returning_dicts("select * from test") == [
|
||||
{"id": 1, "bar": 2}
|
||||
]
|
||||
|
||||
|
||||
def test_query_preserves_error_from_transaction_destroying_trigger(fresh_db):
|
||||
# RAISE(ROLLBACK) destroys the savepoint guard - the original
|
||||
# IntegrityError must propagate, not "no such savepoint"
|
||||
fresh_db.execute("create table t (id integer primary key, v text)")
|
||||
fresh_db.execute("""
|
||||
create trigger no_bad before insert on t
|
||||
when new.v = 'bad'
|
||||
begin
|
||||
select raise(rollback, 'trigger says no');
|
||||
end
|
||||
""")
|
||||
with pytest.raises(sqlite3.IntegrityError, match="trigger says no"):
|
||||
fresh_db.query("insert into t (id, v) values (1, 'bad') returning id")
|
||||
assert not fresh_db.conn.in_transaction
|
||||
assert fresh_db.execute("select count(*) from t").fetchone()[0] == 0
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue