3eb11b1745
Audit (docs/migration_history_audit.md): - files.size_bytes: INTEGER (Alembic) vs BIGINT (Produktion/Plugin) - GIN-Indizes: Fehlendes USING GIN in Alembic 0002 - guest_users: ix_guest_users_email_tenant fehlt UNIQUE in Alembic 0059 - plugins.name: Doppelter Unique-Index in Produktion Forward-Migrationen: - 0093: files.size_bytes INTEGER → BIGINT - 0094: GIN-Indizes reparieren + plugins.name doppelten Index entfernen - 0095: guest_users email+tenant_id UNIQUE INDEX (mit Dubletten-Check) - 0096: Workspace tenant_integrity (tenant-bound FKs) Tests: 41/41 bestanden (17 Workspace + 24 Command) Alembic Head: 0096
94 lines
3.5 KiB
Python
94 lines
3.5 KiB
Python
"""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")
|