diff --git a/max_bot/app/migrations_runner.py b/max_bot/app/migrations_runner.py index 2f63807..c56c830 100644 --- a/max_bot/app/migrations_runner.py +++ b/max_bot/app/migrations_runner.py @@ -5,8 +5,12 @@ 1. Создаёт таблицу _applied_migrations (если не существует) 2. Проверяет какие SQL-файлы уже применены 3. Применяет новые миграции по порядку + +ВАЖНО: asyncpg не поддерживает несколько SQL-команд в одном text(). +Поэтому каждый SQL-файл разбивается на отдельные команды по ';' . """ import os +import re import logging from sqlalchemy import text from app.database import engine @@ -16,6 +20,24 @@ logger = logging.getLogger(__name__) MIGRATIONS_DIR = os.path.join(os.path.dirname(__file__), "migrations") +def _split_sql(sql: str) -> list: + """Разбивает SQL-файл на отдельные команды, пропуская комментарии и пустые строки.""" + statements = [] + for stmt in sql.split(";"): + # Убираем комментарии (-- ...) и пустые строки + lines = [] + for line in stmt.split("\n"): + stripped = line.strip() + if stripped.startswith("--"): + continue + if stripped: + lines.append(stripped) + clean = " ".join(lines) + if clean: + statements.append(clean) + return statements + + async def run_migrations(): """Запуск всех неприменённых миграций из директории migrations/.""" async with engine.begin() as conn: @@ -47,9 +69,12 @@ async def run_migrations(): with open(filepath, "r", encoding="utf-8") as f: sql = f.read() - logger.info(f"Применение миграции: {filename}") + statements = _split_sql(sql) + logger.info(f"Применение миграции: {filename} ({len(statements)} команд)") + try: - await conn.execute(text(sql)) + for stmt in statements: + await conn.execute(text(stmt)) await conn.execute( text("INSERT INTO _applied_migrations (filename) VALUES (:fn)"), {"fn": filename}