Skip to content

Fix sqlite autocommit lifecycle - #8387

Merged
youknowone merged 4 commits into
RustPython:mainfrom
teddygood:fix-sqlite-autocommit-lifecycle
Jul 27, 2026
Merged

Fix sqlite autocommit lifecycle#8387
youknowone merged 4 commits into
RustPython:mainfrom
teddygood:fix-sqlite-autocommit-lifecycle

Conversation

@teddygood

@teddygood teddygood commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Summary

Align the sqlite3.Connection lifecycle for autocommit=False with CPython.

Previously, creating a connection with autocommit=False did not immediately open a transaction. Calling commit() or rollback() also left the connection outside a transaction, and closing the connection did not explicitly roll back the pending transaction.

When a connection is created with autocommit=False, it now begins a transaction immediately. In this mode, commit() and rollback() complete the current transaction and then open a new one. Both methods remain no-ops with autocommit=True, while autocommit=sqlite3.LEGACY_TRANSACTION_CONTROL preserves the existing transaction behavior. Closing the connection explicitly rolls back any pending transaction.

The same behavior is applied when leaving a connection context manager and when an existing Connection object is reinitialized.

Four CPython tests now pass without expectedFailure:

  • test_autocommit_disabled
  • test_autocommit_disabled_implicit_rollback
  • test_autocommit_disabled_then_enabled
  • test_autocommit_disabled_ctx_mgr

Cursor transaction handling in execute(), executemany(), and executescript() is not included in this PR and will be validated separately.

Summary by CodeRabbit

  • Bug Fixes
    • Improved transaction handling when connections are initialized, closed, or recreated.
    • Ensured pending transactions are rolled back appropriately when autocommit is disabled.
    • Corrected rollback behavior across supported autocommit modes.
    • Improved isolation-level changes so pending transactions are committed safely.
    • Ensured disabled autocommit mode starts transactions consistently.

Copilot AI review requested due to automatic review settings July 27, 2026 01:19
@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

SQLite autocommit lifecycle

Layer / File(s) Summary
Transaction setup and teardown
crates/stdlib/src/_sqlite3.rs
Initialization begins transactions for disabled autocommit; cleanup rolls back when needed before dropping the database.
Mode-specific rollback
crates/stdlib/src/_sqlite3.rs
rollback() now handles legacy, enabled, and disabled autocommit modes explicitly.
Autocommit and isolation transitions
crates/stdlib/src/_sqlite3.rs
Isolation-level changes use commit(), and autocommit mode is stored before transaction handling.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant VirtualMachine
  participant Connection
  participant SQLiteDatabase
  Connection->>SQLiteDatabase: BEGIN for disabled autocommit
  VirtualMachine->>Connection: rollback()
  Connection->>SQLiteDatabase: ROLLBACK
  Connection->>SQLiteDatabase: BEGIN
  VirtualMachine->>Connection: close()
  Connection->>SQLiteDatabase: ROLLBACK when required
  Connection->>SQLiteDatabase: drop database
Loading

Possibly related issues

Possibly related PRs

Suggested reviewers: copilot, ever0de, shaharnaveh

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main sqlite autocommit lifecycle change.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown
Contributor

📦 Library Dependencies

The following Lib/ modules were modified. Here are their dependencies:

[x] test: cpython/Lib/test/test_float.py (TODO: 3)
[x] test: cpython/Lib/test/test_strtod.py (TODO: 2)

dependencies:

dependent tests: (no tests depend on float)

[x] test: cpython/Lib/test/test_format.py (TODO: 6)

dependencies:

dependent tests: (no tests depend on format)

[x] lib: cpython/Lib/sqlite3
[x] test: cpython/Lib/test/test_sqlite3 (TODO: 65)

dependencies:

  • sqlite3

dependent tests: (2 tests)

  • sqlite3: test_dbm_sqlite3 test_sqlite3

Legend:

  • [+] path exists in CPython
  • [x] up-to-date, [ ] outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR aligns RustPython’s _sqlite3 transaction lifecycle with CPython when sqlite3.Connection(autocommit=False), ensuring a transaction is opened immediately, commit()/rollback() restart a new transaction, and close/context-manager exit explicitly roll back pending transactions.

Changes:

  • Start a transaction immediately on connect when autocommit=False, and restart it after commit()/rollback().
  • Roll back any pending transaction on connection close and on connection reinitialization.
  • Unblock several CPython test_sqlite3 transaction tests by removing expectedFailure decorators.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
crates/stdlib/src/_sqlite3.rs Implements CPython-aligned autocommit-disabled transaction lifecycle and close/reinit rollback behavior.
Lib/test/test_sqlite3/test_transactions.py Removes expectedFailure markers for tests now passing with the updated lifecycle.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 1578 to 1582
fn set_autocommit(&self, val: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
let mode = AutocommitMode::try_from_borrowed_object(vm, &val)?;
let db = self.db_lock(vm)?;
*self.autocommit.lock() = mode;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The literal NUL bytes were already present, but I think using the escaped \0 form is clearer and more consistent with the rest of the file. The second point is intentional. This implementation follows CPython's set_autocommit(), which stores the requested autocommit mode before executing COMMIT or BEGIN. If the SQL statement fails, CPython also retains the requested mode. Since this PR aims to match CPython's behavior, I will keep the current ordering.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
crates/stdlib/src/_sqlite3.rs (1)

1133-1165: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider extracting the shared "exec + reopen BEGIN" logic in commit/rollback.

The Disabled arms of commit() (1140-1143) and rollback() (1160-1163) differ only in the first statement (COMMIT vs ROLLBACK) but share an identical trailing db._exec(b"BEGIN\0", vm). Per repo guidelines, extract the differing value and call the shared logic once.

♻️ Suggested refactor
+        fn exec_and_reopen(db: &Sqlite, stmt: &[u8], vm: &VirtualMachine) -> PyResult<()> {
+            db._exec(stmt, vm)?;
+            db._exec(b"BEGIN\0", vm)
+        }
+
         fn commit(&self, vm: &VirtualMachine) -> PyResult<()> {
             let db = self.db_lock(vm)?;
             let mode = *self.autocommit.lock();
             match mode {
                 AutocommitMode::Legacy => db.implicit_commit(vm),
                 AutocommitMode::Enabled => Ok(()),
-                AutocommitMode::Disabled => {
-                    db._exec(b"COMMIT\0", vm)?;
-                    db._exec(b"BEGIN\0", vm)
-                }
+                AutocommitMode::Disabled => Self::exec_and_reopen(&db, b"COMMIT\0", vm),
             }
         }

         fn rollback(&self, vm: &VirtualMachine) -> PyResult<()> {
             let db = self.db_lock(vm)?;
             let mode = *self.autocommit.lock();
             match mode {
                 AutocommitMode::Legacy => {
                     if db.is_autocommit() {
                         Ok(())
                     } else {
                         db._exec(b"ROLLBACK\0", vm)
                     }
                 }
                 AutocommitMode::Enabled => Ok(()),
-                AutocommitMode::Disabled => {
-                    db._exec(b"ROLLBACK\0", vm)?;
-                    db._exec(b"BEGIN\0", vm)
-                }
+                AutocommitMode::Disabled => Self::exec_and_reopen(&db, b"ROLLBACK\0", vm),
             }
         }

As per coding guidelines, "When branches differ only in a value but share common logic, extract the differing value first, then call the common logic once to avoid duplicate code."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/stdlib/src/_sqlite3.rs` around lines 1133 - 1165, Extract the shared
Disabled-mode transaction flow from commit and rollback into a helper or common
local path that accepts the differing SQL command, COMMIT or ROLLBACK, executes
it through db._exec, then executes BEGIN once. Update both commit and rollback
to supply their respective command while preserving the existing Legacy and
Enabled behavior.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@crates/stdlib/src/_sqlite3.rs`:
- Around line 1133-1165: Extract the shared Disabled-mode transaction flow from
commit and rollback into a helper or common local path that accepts the
differing SQL command, COMMIT or ROLLBACK, executes it through db._exec, then
executes BEGIN once. Update both commit and rollback to supply their respective
command while preserving the existing Legacy and Enabled behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro Plus

Run ID: 26f213f0-894c-4893-9b6f-73408826abcd

📥 Commits

Reviewing files that changed from the base of the PR and between 5db61a0 and 8c52401.

⛔ Files ignored due to path filters (1)
  • Lib/test/test_sqlite3/test_transactions.py is excluded by !Lib/**
📒 Files selected for processing (1)
  • crates/stdlib/src/_sqlite3.rs

@youknowone youknowone left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@youknowone
youknowone merged commit d62a391 into RustPython:main Jul 27, 2026
26 of 27 checks passed
youknowone pushed a commit to youknowone/RustPython that referenced this pull request Jul 29, 2026
* fix: open disabled sqlite autocommit transactions

Assisted-by: Codex:gpt-5.6-sol

* fix: reopen disabled sqlite transactions after commit

Assisted-by: Codex:gpt-5.6-sol

* fix: reopen disabled sqlite transactions after rollback

Assisted-by: Codex:gpt-5.6-sol

* fix: roll back disabled sqlite connections on close

Assisted-by: Codex:gpt-5.6-sol
@moreal moreal added the z-ca-2026 Tag to track Contribution Academy 2026 label Aug 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

z-ca-2026 Tag to track Contribution Academy 2026

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants