diff --git a/backend/alembic/versions/v1_0_0_f061_enterprise_info_tenant_id.py b/backend/alembic/versions/v1_0_0_f061_enterprise_info_tenant_id.py index e2f2b48fe..0ca62ff85 100644 --- a/backend/alembic/versions/v1_0_0_f061_enterprise_info_tenant_id.py +++ b/backend/alembic/versions/v1_0_0_f061_enterprise_info_tenant_id.py @@ -21,8 +21,6 @@ from typing import Sequence, Union from alembic import op -import sqlalchemy as sa -from sqlalchemy.dialects import postgresql # revision identifiers, used by Alembic. revision: str = "f061_enterprise_info_tenant_id" @@ -32,15 +30,38 @@ def upgrade() -> None: + # This migration must be idempotent with respect to a freshly created schema. + # 001_initial_schema.py builds the schema with Base.metadata.create_all(), which + # already produces enterprise_info.tenant_id because the model declares it, so an + # unconditional op.add_column() fails on every fresh deployment and + # `alembic upgrade head` cannot complete. + # + # The IF NOT EXISTS / IF EXISTS guards match the convention already used in + # 010_column_modify.py, and make the module docstring's "Safe for retry" claim hold. + # 1. Add tenant_id column with default uuid generator or nullable first if populated - op.add_column("enterprise_info", sa.Column("tenant_id", postgresql.UUID(as_uuid=True), nullable=True)) - op.create_index(op.f("ix_enterprise_info_tenant_id"), "enterprise_info", ["tenant_id"], unique=False) + op.execute("ALTER TABLE enterprise_info ADD COLUMN IF NOT EXISTS tenant_id UUID") + op.execute( + "CREATE INDEX IF NOT EXISTS ix_enterprise_info_tenant_id ON enterprise_info (tenant_id)" + ) # 2. Drop legacy single info_type unique constraint - op.drop_constraint("enterprise_info_info_type_key", "enterprise_info", type_="unique") + op.execute("ALTER TABLE enterprise_info DROP CONSTRAINT IF EXISTS enterprise_info_info_type_key") # 3. Create new composite unique constraint (tenant_id, info_type) - op.create_unique_constraint("uq_enterprise_info_tenant_type", "enterprise_info", ["tenant_id", "info_type"]) + op.execute( + """ + DO $$ + BEGIN + IF NOT EXISTS ( + SELECT 1 FROM pg_constraint WHERE conname = 'uq_enterprise_info_tenant_type' + ) THEN + ALTER TABLE enterprise_info + ADD CONSTRAINT uq_enterprise_info_tenant_type UNIQUE (tenant_id, info_type); + END IF; + END $$; + """ + ) def downgrade() -> None: