Reject Python 3.12+ autocommit connections in the Database constructor

Connections created with sqlite3.connect(autocommit=True) make
commit() a documented no-op, and autocommit=False connections are
permanently inside a transaction - in both modes every write made
by this library appeared to work in-process but was silently
discarded when the connection closed. Database() now raises
TransactionError for these connections instead of losing data.

Tests are skipped on Python versions before 3.12, where the
autocommit parameter does not exist.

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 22:38:31 +00:00
commit bcd9a26560
No known key found for this signature in database
5 changed files with 51 additions and 1 deletions

View file

@ -402,6 +402,18 @@ class Database:
if recreate:
raise ValueError("recreate cannot be used with connections, only paths")
self.conn = cast(sqlite3.Connection, filename_or_conn)
# Python 3.12+ autocommit=True/False connections make commit()
# and rollback() behave differently, silently breaking the
# transaction handling used by every write method
autocommit = getattr(self.conn, "autocommit", None)
if autocommit is not None and autocommit != getattr(
sqlite3, "LEGACY_TRANSACTION_CONTROL", -1
):
raise TransactionError(
"sqlite-utils requires a connection that uses the default "
"transaction handling - connections created with "
"autocommit=True or autocommit=False are not supported"
)
self._tracer: Optional[Tracer] = tracer
if recursive_triggers:
self.execute("PRAGMA recursive_triggers=on;")