Newer
Older
gnexus-tasks / backend / alembic / versions / e7f8a9b0c1d2_coin_event_uniques.py
"""Уникальность дейли и бонуса уровня (ТЗ 3.13): гонки параллельных запросов.

Revision ID: e7f8a9b0c1d2
Revises: d5e6a7b8c9d0
Create Date: 2026-09-21 13:00:00.000000
"""

from collections.abc import Sequence

import sqlalchemy as sa

from alembic import op

revision: str = "e7f8a9b0c1d2"
down_revision: str | Sequence[str] | None = "d5e6a7b8c9d0"
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None

# Частичные индексы — и в Postgres, и в SQLite: дубликаты не допускаются,
# старые строки (NULL-ключи) не трогаем — NULL в уникальном индексе не сталкиваются
DAILY_WHERE = sa.text("source = 'daily'")
LEVEL_WHERE = sa.text("source = 'level' AND level IS NOT NULL")


def upgrade() -> None:
    op.add_column("coin_events", sa.Column("day", sa.Date(), nullable=True))
    op.create_index(
        "uq_coin_daily",
        "coin_events",
        ["source", "day"],
        unique=True,
        postgresql_where=DAILY_WHERE,
        sqlite_where=DAILY_WHERE,
    )
    op.create_index(
        "uq_coin_level",
        "coin_events",
        ["level"],
        unique=True,
        postgresql_where=LEVEL_WHERE,
        sqlite_where=LEVEL_WHERE,
    )


def downgrade() -> None:
    op.drop_index("uq_coin_level", table_name="coin_events")
    op.drop_index("uq_coin_daily", table_name="coin_events")
    op.drop_column("coin_events", "day")