from __future__ import annotations import sqlite3 from pathlib import Path THIS_FILE = Path(__file__).resolve() PKG_DIR = THIS_FILE.parent SCHEMA_DIR = PKG_DIR / "schema" def _ensure_migrations_table(conn: sqlite3.Connection) -> None: conn.execute( """ CREATE TABLE IF NOT EXISTS migrations ( id INTEGER PRIMARY KEY, name TEXT NOT NULL UNIQUE, applied_at TEXT NOT NULL DEFAULT (datetime('now')) ); """ ) def _applied_migrations(conn: sqlite3.Connection) -> set[str]: _ensure_migrations_table(conn) rows = conn.execute("SELECT name FROM migrations;").fetchall() names: set[str] = set() for row in rows: try: names.add(row["name"]) except Exception: names.add(row[0]) return names def _migration_files() -> list[Path]: if not SCHEMA_DIR.exists(): return [] files = [p for p in SCHEMA_DIR.iterdir() if p.is_file() and p.suffix == ".sql"] files.sort(key=lambda p: p.name) return files def migrate(conn: sqlite3.Connection) -> None: applied = _applied_migrations(conn) for path in _migration_files(): name = path.name if name in applied: continue sql = path.read_text(encoding="utf-8") conn.executescript(sql) conn.execute("INSERT INTO migrations (name) VALUES (?);", (name,))