-- Hand-edited after generation. drizzle-kit emitted -- ALTER TABLE "farms" ADD COLUMN "org_id" uuid NOT NULL; -- which cannot apply to a farms table that already has rows, and it emitted -- the DROP of "owner_id" without moving the ownership it encodes anywhere - -- so an existing database would either refuse the migration or silently lose -- who owned each farm. -- -- The order below is the fix: add the column nullable, derive one -- organisation per distinct owner and make that owner its `owner` member, -- point each farm at it, and only then tighten the column and drop -- "owner_id". Edited statements are marked; the rest is generated output. -- -- Safe to keep across regeneration: drizzle only appends new migration files -- and diffs against meta/0001_snapshot.json, not against this SQL. DO $$ BEGIN CREATE TYPE "public"."org_role" AS ENUM('viewer', 'operator', 'owner'); EXCEPTION WHEN duplicate_object THEN null; END $$; --> statement-breakpoint CREATE TABLE IF NOT EXISTS "organisations" ( "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "name" text NOT NULL, "settings" jsonb DEFAULT '{}'::jsonb, "created_at" timestamp with time zone DEFAULT now() NOT NULL, "updated_at" timestamp with time zone DEFAULT now() NOT NULL ); --> statement-breakpoint CREATE TABLE IF NOT EXISTS "org_members" ( "org_id" uuid NOT NULL, "user_id" uuid NOT NULL, "role" "org_role" DEFAULT 'viewer' NOT NULL, "created_at" timestamp with time zone DEFAULT now() NOT NULL, "updated_at" timestamp with time zone DEFAULT now() NOT NULL, CONSTRAINT "org_members_org_id_user_id_pk" PRIMARY KEY("org_id","user_id") ); --> statement-breakpoint DO $$ BEGIN ALTER TABLE "org_members" ADD CONSTRAINT "org_members_org_id_organisations_id_fk" FOREIGN KEY ("org_id") REFERENCES "public"."organisations"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$; --> statement-breakpoint CREATE INDEX IF NOT EXISTS "org_members_user_id_idx" ON "org_members" USING btree ("user_id");--> statement-breakpoint -- EDITED: generated as `uuid NOT NULL`. Nullable for now; tightened below, -- once every row has a value. ALTER TABLE "farms" ADD COLUMN "org_id" uuid;--> statement-breakpoint -- EDITED (added): backfill. One organisation per distinct farms.owner_id, -- with that user as its sole `owner` member. Farms that shared an owner end -- up in one organisation together, which is what the old column meant. -- -- A DO block rather than a temporary table so the whole thing is one -- statement with no state outlasting it, and so RETURNING ... INTO can carry -- each generated organisation id straight to the rows that need it. DO $$ DECLARE legacy_owner record; new_org_id uuid; BEGIN FOR legacy_owner IN SELECT DISTINCT "owner_id" FROM "farms" LOOP INSERT INTO "organisations" ("name") VALUES ('Organisation ' || left(legacy_owner."owner_id"::text, 8)) RETURNING "id" INTO new_org_id; INSERT INTO "org_members" ("org_id", "user_id", "role") VALUES (new_org_id, legacy_owner."owner_id", 'owner'); UPDATE "farms" SET "org_id" = new_org_id WHERE "owner_id" = legacy_owner."owner_id"; END LOOP; END $$; --> statement-breakpoint -- EDITED (added): now that the backfill has run, enforce what the schema says. ALTER TABLE "farms" ALTER COLUMN "org_id" SET NOT NULL;--> statement-breakpoint DO $$ BEGIN ALTER TABLE "farms" ADD CONSTRAINT "farms_org_id_organisations_id_fk" FOREIGN KEY ("org_id") REFERENCES "public"."organisations"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$; --> statement-breakpoint CREATE INDEX IF NOT EXISTS "farms_org_id_idx" ON "farms" USING btree ("org_id");--> statement-breakpoint ALTER TABLE "farms" DROP COLUMN IF EXISTS "owner_id";