From b9e37bbf9cb197e82373d4fe989a419292ceb302 Mon Sep 17 00:00:00 2001 From: HAL9000 Date: Tue, 5 May 2026 03:32:08 +0000 Subject: [PATCH] fix(database/migration_runner): add check_same_thread=False to get_current_revision() SQLite engine Pass connect_args={"check_same_thread": False} when creating a SQLite engine in get_current_revision(), consistent with init_or_upgrade() which already applies this setting for all SQLite engines. Without this fix, calling get_current_revision() from a different thread than the one that created the engine raises a ProgrammingError. ISSUES CLOSED: #10952 --- features/consolidated_misc.feature | 6 ++++++ features/steps/migration_runner_steps.py | 11 +++++++++++ .../infrastructure/database/migration_runner.py | 8 +++++++- 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/features/consolidated_misc.feature b/features/consolidated_misc.feature index f1caf6b8e..b69a9e6ab 100644 --- a/features/consolidated_misc.feature +++ b/features/consolidated_misc.feature @@ -1714,6 +1714,12 @@ Feature: Consolidated Misc And the temporary connection should be closed afterward + Scenario: Get current revision uses check_same_thread=False for SQLite + Given a migration runner configured for "sqlite:///:memory:" + When I request the current revision from the database + Then the get_current_revision engine should use check_same_thread False for SQLite + + Scenario: File-based SQLite database directory is created if missing Given a migration runner configured for "sqlite:///tmp/test-db/mydb.db" When I initialize or upgrade a file-based SQLite database diff --git a/features/steps/migration_runner_steps.py b/features/steps/migration_runner_steps.py index ec0018297..2aea2062b 100644 --- a/features/steps/migration_runner_steps.py +++ b/features/steps/migration_runner_steps.py @@ -316,6 +316,17 @@ def step_then_temp_connection_closed(context) -> None: assert context.current_rev_fake_engine.connections[0].exit_called is True +@then("the get_current_revision engine should use check_same_thread False for SQLite") +def step_then_get_current_revision_check_same_thread(context) -> None: + _url, kwargs = context.current_rev_create_call + assert "connect_args" in kwargs, ( + "Expected connect_args to be passed to create_engine for SQLite" + ) + assert kwargs["connect_args"].get("check_same_thread") is False, ( + "Expected check_same_thread=False in connect_args for SQLite" + ) + + @when("I initialize or upgrade a file-based SQLite database") def step_when_init_file_based_sqlite(context) -> None: import shutil diff --git a/src/cleveragents/infrastructure/database/migration_runner.py b/src/cleveragents/infrastructure/database/migration_runner.py index c731c1c50..8f8393cb3 100644 --- a/src/cleveragents/infrastructure/database/migration_runner.py +++ b/src/cleveragents/infrastructure/database/migration_runner.py @@ -154,7 +154,13 @@ class MigrationRunner: Returns: Current revision ID or None if no migrations have been applied """ - engine = create_engine(self.database_url) + if self.database_url.startswith("sqlite"): + engine = create_engine( + self.database_url, + connect_args={"check_same_thread": False}, + ) + else: + engine = create_engine(self.database_url) with engine.connect() as connection: context = MigrationContext.configure(connection) return context.get_current_revision()