feat: add SQLite migration runner

This commit is contained in:
Johan Lundberg 2026-02-13 13:02:34 +01:00
parent bfa5b2e8d0
commit 627675fff1
No known key found for this signature in database
GPG key ID: A6C152738D03C7D1
2 changed files with 90 additions and 0 deletions

View file

@ -0,0 +1,48 @@
from pathlib import Path
import aiosqlite
async def run_migrations(db: aiosqlite.Connection, migrations_dir: Path) -> int:
"""Apply unapplied SQL migration files in order. Returns count of newly applied migrations."""
if not migrations_dir.is_dir():
raise FileNotFoundError(f"Migrations directory not found: {migrations_dir}")
await db.execute(
"""
CREATE TABLE IF NOT EXISTS _migrations (
id INTEGER PRIMARY KEY,
filename TEXT NOT NULL UNIQUE,
applied_at TEXT NOT NULL DEFAULT (datetime('now'))
)
"""
)
await db.commit()
async with db.execute("SELECT filename FROM _migrations") as cursor:
applied = {row[0] async for row in cursor}
migration_files = sorted(migrations_dir.glob("*.sql"))
count = 0
for migration_file in migration_files:
if migration_file.name in applied:
continue
sql = migration_file.read_text(encoding="utf-8")
await db.execute("BEGIN")
try:
for statement in sql.split(";"):
statement = statement.strip()
if statement:
await db.execute(statement)
await db.execute(
"INSERT INTO _migrations (filename) VALUES (?)",
(migration_file.name,),
)
await db.commit()
except Exception:
await db.rollback()
raise
count += 1
return count