53 lines
1.4 KiB
Python
53 lines
1.4 KiB
Python
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,))
|