diff --git a/src/codex_usage_tracker/kernel/application/jobs.py b/src/codex_usage_tracker/kernel/application/jobs.py index 20227dc1..2e79d425 100644 --- a/src/codex_usage_tracker/kernel/application/jobs.py +++ b/src/codex_usage_tracker/kernel/application/jobs.py @@ -88,6 +88,7 @@ def _read( f"SELECT * FROM refresh_runs {clause}", parameters, ).fetchone() + connection.close() return _snapshot(row, include_result=include_result) if row else None diff --git a/src/codex_usage_tracker/kernel/content.py b/src/codex_usage_tracker/kernel/content.py index aefdd073..a70d07dc 100644 --- a/src/codex_usage_tracker/kernel/content.py +++ b/src/codex_usage_tracker/kernel/content.py @@ -359,6 +359,7 @@ def _initialize_content_database(path: Path) -> None: connection.execute(f"PRAGMA user_version = {CONTENT_SCHEMA_VERSION}") connection.execute("PRAGMA foreign_keys = ON") connection.executescript(_SCHEMA_SQL) + connection.close() staging.chmod(0o600) os.replace(staging, target) with sqlite3.connect(target) as connection: @@ -424,6 +425,7 @@ def _source_targets( "SELECT source_id, source_location FROM source_registry" ) } + operational.close() return tuple( { **dict(row), diff --git a/src/codex_usage_tracker/kernel/database.py b/src/codex_usage_tracker/kernel/database.py index 18e279a2..0af17089 100644 --- a/src/codex_usage_tracker/kernel/database.py +++ b/src/codex_usage_tracker/kernel/database.py @@ -44,6 +44,7 @@ def initialize_analytical_database( connection.execute("PRAGMA foreign_keys = ON") connection.execute("PRAGMA journal_mode = DELETE") create_schema(connection) + connection.close() _owner_only(staging) failures = validate_analytical_database(staging) if failures: @@ -185,6 +186,7 @@ def validate_analytical_database(path: Path) -> list[str]: failures.append(f"analytical quick_check failed: {integrity}") if connection.execute("PRAGMA foreign_key_check").fetchone() is not None: failures.append("analytical foreign-key check failed") + connection.close() except sqlite3.DatabaseError as exc: failures.append(f"analytical database is unreadable: {exc}") return failures @@ -283,6 +285,7 @@ def analytical_schema_version(path: Path) -> int | None: try: with sqlite3.connect(f"{target.as_uri()}?mode=ro", uri=True) as connection: return int(connection.execute("PRAGMA user_version").fetchone()[0]) + connection.close() except sqlite3.DatabaseError as exc: raise ValueError("analytical database schema header is unreadable") from exc diff --git a/src/codex_usage_tracker/kernel/ingest.py b/src/codex_usage_tracker/kernel/ingest.py index 5ca5cb82..f20a461b 100644 --- a/src/codex_usage_tracker/kernel/ingest.py +++ b/src/codex_usage_tracker/kernel/ingest.py @@ -584,6 +584,7 @@ def _plans( registered = connection.execute( "SELECT source_id, source_location FROM source_registry" ).fetchall() + connection.close() source_by_location = { str(location): str(source_id) for source_id, location in registered } @@ -1058,6 +1059,7 @@ def _write_path( if index_backfill: with sqlite3.connect(staging) as connection: create_missing_secondary_indexes(connection) + connection.close() return staging, True, incremental_rollup_safe def _active_collision_requires_isolation( @@ -1245,6 +1247,7 @@ def _tool_turn_index_ready(path: Path) -> bool: ) + connection.close() def _clone_checkpointed_database(source: Path, destination: Path) -> bool: """Use a filesystem snapshot only while the validated main file is stable.""" @@ -1255,6 +1258,7 @@ def _clone_checkpointed_database(source: Path, destination: Path) -> bool: checkpointed = not wal.exists() or wal.stat().st_size == 0 cloned = checkpointed and _copy_on_write_clone(source, destination) guard.execute("ROLLBACK") + guard.close() if not cloned: destination.unlink(missing_ok=True) return False diff --git a/src/codex_usage_tracker/kernel/operational.py b/src/codex_usage_tracker/kernel/operational.py index 269e3ac7..35a48896 100644 --- a/src/codex_usage_tracker/kernel/operational.py +++ b/src/codex_usage_tracker/kernel/operational.py @@ -197,6 +197,7 @@ def initialize_operational_database(path: Path) -> Path: connection.execute(f"PRAGMA user_version = {OPERATIONAL_SCHEMA_VERSION}") connection.executescript(_OPERATIONAL_SQL) connection.execute("INSERT INTO cutover_control(singleton, state) VALUES (1, 'absent')") + connection.close() staging.chmod(0o600) _validate_operational(staging) os.replace(staging, target) @@ -1281,6 +1282,7 @@ def _migrate_operational(path: Path) -> None: ) + connection.close() def _control_from_row(row: sqlite3.Row) -> CutoverControl: return CutoverControl( state=CutoverState(row["state"]),