PMFlow/backend/alembic/versions/20260324_add_project_create...

84 lines
2.3 KiB
Python

"""add project create idempotency indexes
Revision ID: add_project_create_idempotency_indexes
Revises: add_work_item_draft_confirmation_fields
Create Date: 2026-03-24
"""
from __future__ import annotations
from alembic import op
import sqlalchemy as sa
revision = "add_project_create_idempotency_indexes"
down_revision = "add_work_item_draft_confirmation_fields"
branch_labels = None
depends_on = None
def _table_exists(bind, table_name: str) -> bool:
return sa.inspect(bind).has_table(table_name)
def _duplicate_active_titles(bind) -> list[tuple[int, str, int]]:
rows = bind.execute(
sa.text(
"""
SELECT
tenant_id,
lower(coalesce(nullif(title, ''), nullif(name, ''))) AS normalized_title,
count(*) AS duplicate_count
FROM projects
WHERE status <> 'inactive'
AND coalesce(nullif(title, ''), nullif(name, '')) IS NOT NULL
GROUP BY tenant_id, lower(coalesce(nullif(title, ''), nullif(name, '')))
HAVING count(*) > 1
ORDER BY duplicate_count DESC, tenant_id ASC
LIMIT 10
"""
)
).fetchall()
return [
(int(row[0]), str(row[1] or ""), int(row[2]))
for row in rows
]
def upgrade() -> None:
bind = op.get_bind()
if not _table_exists(bind, "projects"):
return
duplicates = _duplicate_active_titles(bind)
if duplicates:
sample = ", ".join(
f"tenant={tenant_id}, title={title}, count={count}"
for tenant_id, title, count in duplicates[:3]
)
raise RuntimeError(
"cannot create uq_projects_tenant_active_title_ci because duplicate active project titles exist: "
+ sample
)
op.execute(
sa.text(
"""
CREATE UNIQUE INDEX IF NOT EXISTS uq_projects_tenant_active_title_ci
ON projects (
tenant_id,
lower(coalesce(nullif(title, ''), nullif(name, '')))
)
WHERE status <> 'inactive'
AND coalesce(nullif(title, ''), nullif(name, '')) IS NOT NULL
"""
)
)
def downgrade() -> None:
bind = op.get_bind()
if not _table_exists(bind, "projects"):
return
op.execute(sa.text("DROP INDEX IF EXISTS uq_projects_tenant_active_title_ci"))