Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 49 additions & 14 deletions packages/db/migrations/0194_careless_pete_wisdom.sql
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
-- permission_group_member table also gains a denormalized workspace_id column
-- so the database can enforce the "one group per user per workspace"
-- invariant with a composite unique index.
--
-- Every statement below is written to be idempotent so the migration can be
-- safely re-run after a partial failure.

-- 0. Backfill workspace -> organization links for grandfathered workspaces whose
-- billed account user is the sole owner of exactly one organization. This is a
Expand All @@ -29,13 +32,42 @@ WHERE w."organization_id" IS NULL
AND w."billed_account_user_id" = owner_orgs."user_id";--> statement-breakpoint

-- 1. Add workspace_id columns as nullable so existing rows can coexist during the data migration.
ALTER TABLE "permission_group" ADD COLUMN "workspace_id" text;--> statement-breakpoint
ALTER TABLE "permission_group" ADD CONSTRAINT "permission_group_workspace_id_workspace_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "public"."workspace"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "permission_group_member" ADD COLUMN "workspace_id" text;--> statement-breakpoint
ALTER TABLE "permission_group_member" ADD CONSTRAINT "permission_group_member_workspace_id_workspace_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "public"."workspace"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "permission_group" ADD COLUMN IF NOT EXISTS "workspace_id" text;--> statement-breakpoint
DO $$ BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.table_constraints
WHERE constraint_name = 'permission_group_workspace_id_workspace_id_fk'
AND table_name = 'permission_group'
) THEN
ALTER TABLE "permission_group"
ADD CONSTRAINT "permission_group_workspace_id_workspace_id_fk"
FOREIGN KEY ("workspace_id") REFERENCES "public"."workspace"("id")
ON DELETE cascade ON UPDATE no action;
END IF;
Comment thread
icecrasher321 marked this conversation as resolved.
END $$;--> statement-breakpoint

ALTER TABLE "permission_group_member" ADD COLUMN IF NOT EXISTS "workspace_id" text;--> statement-breakpoint
DO $$ BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.table_constraints
WHERE constraint_name = 'permission_group_member_workspace_id_workspace_id_fk'
AND table_name = 'permission_group_member'
) THEN
ALTER TABLE "permission_group_member"
ADD CONSTRAINT "permission_group_member_workspace_id_workspace_id_fk"
FOREIGN KEY ("workspace_id") REFERENCES "public"."workspace"("id")
ON DELETE cascade ON UPDATE no action;
END IF;
Comment thread
icecrasher321 marked this conversation as resolved.
END $$;--> statement-breakpoint

-- 1b. Relax NOT NULL on permission_group.organization_id before the data migration.
-- Step 3 inserts clone rows with organization_id = NULL to mark them as the new
-- workspace-scoped shape. This DROP NOT NULL is a no-op if already nullable.
ALTER TABLE "permission_group" ALTER COLUMN "organization_id" DROP NOT NULL;--> statement-breakpoint

-- 2. Materialize a plan of (source permission group, target workspace, new clone id)
-- so we can insert the clone rows AND the member rows with stable references.
-- Temp tables are always fresh per transaction, so this is naturally idempotent.
CREATE TEMP TABLE "__permission_group_clone_plan" (
"source_id" text NOT NULL,
"cloned_id" text NOT NULL,
Expand All @@ -49,6 +81,8 @@ JOIN "workspace" w ON w."organization_id" = pg."organization_id"
WHERE pg."organization_id" IS NOT NULL;--> statement-breakpoint

-- 3. Create the workspace-scoped clone rows using the planned ids.
-- Naturally idempotent: after a successful prior run there are no org-scoped
-- rows left, so the clone plan is empty and this INSERT is a no-op.
INSERT INTO "permission_group" (
"id",
"workspace_id",
Expand Down Expand Up @@ -101,6 +135,7 @@ WHERE EXISTS (
);--> statement-breakpoint

-- 5. Delete legacy org-scoped rows now that clones exist.
-- Idempotent: no rows match on a re-run.
DELETE FROM "permission_group_member"
WHERE "permission_group_id" IN (
SELECT "id" FROM "permission_group" WHERE "organization_id" IS NOT NULL
Expand All @@ -109,20 +144,20 @@ WHERE "permission_group_id" IN (
DELETE FROM "permission_group" WHERE "organization_id" IS NOT NULL;--> statement-breakpoint

-- 6. Enforce NOT NULL on both workspace_id columns now that every surviving row has one.
-- SET NOT NULL is a no-op if already NOT NULL.
ALTER TABLE "permission_group" ALTER COLUMN "workspace_id" SET NOT NULL;--> statement-breakpoint
ALTER TABLE "permission_group_member" ALTER COLUMN "workspace_id" SET NOT NULL;--> statement-breakpoint

-- 7. Drop legacy structures and swap indexes.
ALTER TABLE "permission_group" DROP CONSTRAINT "permission_group_organization_id_organization_id_fk";--> statement-breakpoint
DROP INDEX "permission_group_org_name_unique";--> statement-breakpoint
DROP INDEX "permission_group_org_auto_add_unique";--> statement-breakpoint
DROP INDEX "permission_group_member_user_id_unique";--> statement-breakpoint
ALTER TABLE "permission_group" DROP COLUMN "organization_id";--> statement-breakpoint
CREATE UNIQUE INDEX "permission_group_workspace_name_unique" ON "permission_group" USING btree ("workspace_id","name");--> statement-breakpoint
CREATE UNIQUE INDEX "permission_group_workspace_auto_add_unique" ON "permission_group" USING btree ("workspace_id") WHERE auto_add_new_members = true;--> statement-breakpoint
CREATE UNIQUE INDEX "permission_group_member_group_user_unique" ON "permission_group_member" USING btree ("permission_group_id","user_id");--> statement-breakpoint
CREATE UNIQUE INDEX "permission_group_member_workspace_user_unique" ON "permission_group_member" USING btree ("workspace_id","user_id");--> statement-breakpoint
ALTER TABLE "permission_group" DROP CONSTRAINT IF EXISTS "permission_group_organization_id_organization_id_fk";--> statement-breakpoint
DROP INDEX IF EXISTS "permission_group_org_name_unique";--> statement-breakpoint
DROP INDEX IF EXISTS "permission_group_org_auto_add_unique";--> statement-breakpoint
DROP INDEX IF EXISTS "permission_group_member_user_id_unique";--> statement-breakpoint
ALTER TABLE "permission_group" DROP COLUMN IF EXISTS "organization_id";--> statement-breakpoint
CREATE UNIQUE INDEX IF NOT EXISTS "permission_group_workspace_name_unique" ON "permission_group" USING btree ("workspace_id","name");--> statement-breakpoint
CREATE UNIQUE INDEX IF NOT EXISTS "permission_group_workspace_auto_add_unique" ON "permission_group" USING btree ("workspace_id") WHERE auto_add_new_members = true;--> statement-breakpoint
CREATE UNIQUE INDEX IF NOT EXISTS "permission_group_member_group_user_unique" ON "permission_group_member" USING btree ("permission_group_id","user_id");--> statement-breakpoint
CREATE UNIQUE INDEX IF NOT EXISTS "permission_group_member_workspace_user_unique" ON "permission_group_member" USING btree ("workspace_id","user_id");--> statement-breakpoint

-- 8. Sweep any residual dead config keys from pre-existing workspace-scoped rows (if any).
UPDATE "permission_group" SET "config" = ("config" - 'hideEnvironmentTab' - 'hideTemplates') WHERE "config" ? 'hideEnvironmentTab' OR "config" ? 'hideTemplates';

Loading