feat: sqlite migration runner with meta version table

This commit is contained in:
Joseph Doherty
2026-04-26 11:32:32 -04:00
parent 01e6975d20
commit 67517926aa
5 changed files with 67 additions and 0 deletions
View File
+17
View File
@@ -0,0 +1,17 @@
from __future__ import annotations
import sqlite3
from contextlib import contextmanager
from pathlib import Path
@contextmanager
def open_db(path: Path):
path.parent.mkdir(parents=True, exist_ok=True)
conn = sqlite3.connect(path)
conn.execute("PRAGMA journal_mode=WAL")
conn.execute("PRAGMA foreign_keys=ON")
try:
yield conn
conn.commit()
finally:
conn.close()
+26
View File
@@ -0,0 +1,26 @@
from __future__ import annotations
from pathlib import Path
from chat.db.connection import open_db
MIGRATIONS_DIR = Path(__file__).parent / "migrations"
def apply_migrations(db_path: Path) -> None:
with open_db(db_path) as conn:
conn.execute(
"CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT)"
)
cur = conn.execute("SELECT value FROM meta WHERE key = 'schema_version'")
row = cur.fetchone()
current = int(row[0]) if row else 0
for path in sorted(MIGRATIONS_DIR.glob("*.sql")):
version = int(path.stem.split("_", 1)[0])
if version <= current:
continue
sql = path.read_text()
conn.executescript(sql)
conn.execute(
"INSERT OR REPLACE INTO meta (key, value) VALUES ('schema_version', ?)",
(str(version),),
)
+2
View File
@@ -0,0 +1,2 @@
-- meta table is created by the migrate runner; this migration is a marker.
SELECT 1;
+22
View File
@@ -0,0 +1,22 @@
from chat.db.connection import open_db
from chat.db.migrate import apply_migrations
def test_apply_migrations_creates_meta_table(tmp_path):
db = tmp_path / "test.db"
apply_migrations(db)
with open_db(db) as conn:
row = conn.execute(
"SELECT value FROM meta WHERE key = 'schema_version'"
).fetchone()
assert row is not None
assert int(row[0]) >= 1
def test_apply_migrations_idempotent(tmp_path):
db = tmp_path / "test.db"
apply_migrations(db)
apply_migrations(db) # second call must be a no-op
with open_db(db) as conn:
count = conn.execute("SELECT COUNT(*) FROM meta").fetchone()[0]
assert count == 1