82 lines
2.3 KiB
Python
82 lines
2.3 KiB
Python
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
import sys
|
|
|
|
from sqlalchemy.exc import OperationalError, ProgrammingError
|
|
from sqlalchemy import text
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
if str(ROOT) not in sys.path:
|
|
sys.path.insert(0, str(ROOT))
|
|
|
|
from services.shared.db import engine
|
|
from services.shared.schema_migrations import (
|
|
MIGRATIONS_DIR,
|
|
applied_migration_versions,
|
|
database_backend_name,
|
|
ensure_schema_migrations_table,
|
|
list_migration_files,
|
|
)
|
|
|
|
|
|
def _split_statements(sql: str) -> list[str]:
|
|
chunks = []
|
|
for part in sql.split(";"):
|
|
stmt = part.strip()
|
|
if stmt:
|
|
chunks.append(stmt)
|
|
return chunks
|
|
|
|
|
|
def _apply_file(path: Path) -> None:
|
|
# Some checked-in SQL files may contain a UTF-8 BOM; accept them for both
|
|
# local SQLite runs and Postgres migration jobs.
|
|
statements = _split_statements(path.read_text(encoding="utf-8-sig"))
|
|
with engine.begin() as conn:
|
|
for stmt in statements:
|
|
try:
|
|
conn.execute(text(stmt))
|
|
except (OperationalError, ProgrammingError) as exc:
|
|
message = str(exc).lower()
|
|
duplicate_markers = [
|
|
"duplicate column name",
|
|
"already exists",
|
|
"duplicate key value violates unique constraint",
|
|
]
|
|
if any(marker in message for marker in duplicate_markers):
|
|
continue
|
|
raise
|
|
conn.execute(
|
|
text("INSERT INTO schema_migrations(version, applied_at) VALUES (:v, CURRENT_TIMESTAMP)"),
|
|
{"v": path.name},
|
|
)
|
|
|
|
|
|
def main() -> None:
|
|
files = list_migration_files()
|
|
if not files:
|
|
raise RuntimeError(f"No migration files found for dialect in {MIGRATIONS_DIR}")
|
|
|
|
ensure_schema_migrations_table()
|
|
applied = applied_migration_versions()
|
|
|
|
applied_now = []
|
|
for file_path in files:
|
|
if file_path.name in applied:
|
|
continue
|
|
_apply_file(file_path)
|
|
applied_now.append(file_path.name)
|
|
|
|
print(f"Dialect: {database_backend_name()}")
|
|
if applied_now:
|
|
print("Applied migrations:")
|
|
for m in applied_now:
|
|
print(f"- {m}")
|
|
else:
|
|
print("No new migrations to apply")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|