fix(db): schema_version drift detection + rebuild for pre-M3 volumes

- Add schema_version table; init_db detects a pre-M3/unknown DB (no version
  row, or settings table missing the value column) and rebuilds to the clean
  M3 schema, then stamps version 1
- _has_schema_version checks for a real row (empty table != versioned)
- _reset_for_tests checkpoints WAL before close (Windows handle release)
- TDD: test_schema_drift seeds the M2 settings shape, asserts rebuild + version

Fixes live 500 'no such column: value' on the UAT host (stale M2 slab_db volume).
Gates: check_requirements + check_coverage exit 0; pytest 14 passed.
This commit is contained in:
2026-09-19 16:05:40 -07:00
parent 60bc2fb9d0
commit a0032ddd2d
2 changed files with 49 additions and 25 deletions
+9 -3
View File
@@ -45,8 +45,7 @@ def get_connection(db_path: str | None = None) -> sqlite3.Connection:
def _has_schema_version(conn: sqlite3.Connection) -> bool:
try:
conn.execute("SELECT 1 FROM schema_version").fetchone()
return True
return conn.execute("SELECT 1 FROM schema_version LIMIT 1").fetchone() is not None
except sqlite3.OperationalError:
return False
@@ -75,8 +74,11 @@ def init_db(db_path: str | None = None) -> sqlite3.Connection:
logger.info("Detected pre-M3 DB, rebuilding schema")
conn.executescript("DROP TABLE IF EXISTS settings")
conn.executescript(schema)
conn.commit()
conn.execute("INSERT OR REPLACE INTO schema_version (version) VALUES (1)")
else:
# M3-era tables present but no version stamped; stamp it.
conn.execute("INSERT OR REPLACE INTO schema_version (version) VALUES (1)")
conn.commit()
return conn
@@ -115,6 +117,10 @@ def _reset_for_tests() -> None:
global _conn, _current_path
with _lock:
if _conn is not None:
try:
_conn.execute("PRAGMA wal_checkpoint(TRUNCATE)")
except Exception:
pass
try:
_conn.close()
except Exception: