20260817_525_final_tenant_downgrade_fence.py 1.4 KB

12345678910111213141516171819202122232425262728293031323334
  1. """Refuse the first downgrade step while any tenant security fact remains."""
  2. from alembic import op
  3. revision = "20260817_525"
  4. down_revision = "20260817_524"
  5. branch_labels = None
  6. depends_on = None
  7. def upgrade() -> None:
  8. # Marker-only head: its downgrade is intentionally the outermost safety
  9. # fence, so Alembic cannot reach 521's compatibility grant with live data.
  10. pass
  11. def downgrade() -> None:
  12. op.execute(
  13. "DO $$ BEGIN "
  14. "IF EXISTS(SELECT 1 FROM public.tenants LIMIT 1) "
  15. "OR EXISTS(SELECT 1 FROM public.tenant_control_memberships LIMIT 1) "
  16. "OR EXISTS(SELECT 1 FROM public.tenant_control_claims LIMIT 1) "
  17. "OR EXISTS(SELECT 1 FROM public.tenant_control_outbox LIMIT 1) "
  18. "OR EXISTS(SELECT 1 FROM public.tenant_control_outbox_scope_lookup LIMIT 1) "
  19. "OR EXISTS(SELECT 1 FROM public.tenant_provision_operations LIMIT 1) "
  20. "OR EXISTS(SELECT 1 FROM public.tenant_approval_facts LIMIT 1) "
  21. "OR EXISTS(SELECT 1 FROM public.tenant_quotas LIMIT 1) "
  22. "OR EXISTS(SELECT 1 FROM public.tenant_quota_reservations LIMIT 1) "
  23. "OR EXISTS(SELECT 1 FROM public.tenant_lifecycle_events LIMIT 1) "
  24. "OR EXISTS(SELECT 1 FROM public.tenant_audit_events LIMIT 1) "
  25. "OR EXISTS(SELECT 1 FROM public.tenant_legal_holds LIMIT 1) "
  26. "THEN RAISE EXCEPTION 'tenant final downgrade refused while tenant control data exists'; END IF; "
  27. "END $$;"
  28. )