"""Workspace tenant integrity constraints. Plan 4.3: Add tenant-bound foreign keys to workspace child tables. - workspaces: UNIQUE (tenant_id, id) - workspace_modules: FK (tenant_id, workspace_id) → workspaces (tenant_id, id) - workspace_widgets: FK (tenant_id, workspace_id) → workspaces (tenant_id, id) - workspace_users: FK (tenant_id, workspace_id) → workspaces (tenant_id, id) - workspace_users: FK (tenant_id, user_id) → user_tenants (tenant_id, user_id) Revision ID: 0096 Revises: 0095 """ from __future__ import annotations from alembic import op revision = "0096" down_revision = "0095" branch_labels = None depends_on = None def upgrade() -> None: # 1. Add UNIQUE (tenant_id, id) on workspaces op.execute( "CREATE UNIQUE INDEX IF NOT EXISTS uq_workspaces_tenant_id " "ON workspaces (tenant_id, id)" ) # 2. Drop existing FKs on workspace_modules (workspace_id → workspaces.id) # and replace with tenant-bound FK op.execute("ALTER TABLE workspace_modules DROP CONSTRAINT IF EXISTS workspace_modules_workspace_id_fkey") op.execute( "ALTER TABLE workspace_modules " "ADD CONSTRAINT fk_wm_tenant_workspace " "FOREIGN KEY (tenant_id, workspace_id) " "REFERENCES workspaces (tenant_id, id) ON DELETE CASCADE" ) # 3. Drop existing FK on workspace_widgets and replace with tenant-bound FK op.execute("ALTER TABLE workspace_widgets DROP CONSTRAINT IF EXISTS workspace_widgets_workspace_id_fkey") op.execute( "ALTER TABLE workspace_widgets " "ADD CONSTRAINT fk_ww_tenant_workspace " "FOREIGN KEY (tenant_id, workspace_id) " "REFERENCES workspaces (tenant_id, id) ON DELETE CASCADE" ) # 4. Drop existing FK on workspace_users and replace with tenant-bound FK op.execute("ALTER TABLE workspace_users DROP CONSTRAINT IF EXISTS workspace_users_workspace_id_fkey") op.execute( "ALTER TABLE workspace_users " "ADD CONSTRAINT fk_wu_tenant_workspace " "FOREIGN KEY (tenant_id, workspace_id) " "REFERENCES workspaces (tenant_id, id) ON DELETE CASCADE" ) # 5. Add FK on workspace_users (tenant_id, user_id) → user_tenants (tenant_id, user_id) op.execute( "ALTER TABLE workspace_users " "ADD CONSTRAINT fk_wu_tenant_user " "FOREIGN KEY (tenant_id, user_id) " "REFERENCES user_tenants (tenant_id, user_id) ON DELETE CASCADE" ) def downgrade() -> None: # Remove tenant-bound FKs, restore simple FKs op.execute("ALTER TABLE workspace_users DROP CONSTRAINT IF EXISTS fk_wu_tenant_user") op.execute("ALTER TABLE workspace_users DROP CONSTRAINT IF EXISTS fk_wu_tenant_workspace") op.execute( "ALTER TABLE workspace_users " "ADD CONSTRAINT workspace_users_workspace_id_fkey " "FOREIGN KEY (workspace_id) REFERENCES workspaces (id) ON DELETE CASCADE" ) op.execute("ALTER TABLE workspace_widgets DROP CONSTRAINT IF EXISTS fk_ww_tenant_workspace") op.execute( "ALTER TABLE workspace_widgets " "ADD CONSTRAINT workspace_widgets_workspace_id_fkey " "FOREIGN KEY (workspace_id) REFERENCES workspaces (id) ON DELETE CASCADE" ) op.execute("ALTER TABLE workspace_modules DROP CONSTRAINT IF EXISTS fk_wm_tenant_workspace") op.execute( "ALTER TABLE workspace_modules " "ADD CONSTRAINT workspace_modules_workspace_id_fkey " "FOREIGN KEY (workspace_id) REFERENCES workspaces (id) ON DELETE CASCADE" ) op.execute("DROP INDEX IF EXISTS uq_workspaces_tenant_id")