-- Codes verplicht maken voor PBI/Story en code-kolom + product_id denorm -- toevoegen aan Task. Bestaande NULL-rijen worden gevuld via PL/pgSQL backfill. -- 1) Tasks: product_id denorm (eerst nullable, backfill, dan NOT NULL + FK) ALTER TABLE "tasks" ADD COLUMN "product_id" TEXT; UPDATE "tasks" t SET "product_id" = s."product_id" FROM "stories" s WHERE s."id" = t."story_id"; ALTER TABLE "tasks" ALTER COLUMN "product_id" SET NOT NULL; ALTER TABLE "tasks" ADD CONSTRAINT "tasks_product_id_fkey" FOREIGN KEY ("product_id") REFERENCES "products"("id") ON DELETE CASCADE ON UPDATE CASCADE; -- 2) Tasks: code (eerst nullable t.b.v. backfill) ALTER TABLE "tasks" ADD COLUMN "code" VARCHAR(30); -- 3) Backfill PBI codes (alleen NULL-rijen, per product, op created_at) DO $$ DECLARE rec RECORD; DECLARE n INT; BEGIN FOR rec IN SELECT DISTINCT product_id FROM "pbis" WHERE code IS NULL LOOP SELECT COALESCE(MAX(CAST(SUBSTRING(code FROM 'PBI-(\d+)$') AS INTEGER)), 0) INTO n FROM "pbis" WHERE product_id = rec.product_id AND code ~ '^PBI-\d+$'; UPDATE "pbis" SET code = 'PBI-' || (n + sub.row_num) FROM ( SELECT id, ROW_NUMBER() OVER (ORDER BY created_at, id) AS row_num FROM "pbis" WHERE product_id = rec.product_id AND code IS NULL ) sub WHERE "pbis".id = sub.id; END LOOP; END $$; -- 4) Backfill Story codes (TO_CHAR met FM-format: padding tot minimaal 3 chars zonder truncatie) DO $$ DECLARE rec RECORD; DECLARE n INT; BEGIN FOR rec IN SELECT DISTINCT product_id FROM "stories" WHERE code IS NULL LOOP SELECT COALESCE(MAX(CAST(SUBSTRING(code FROM 'ST-(\d+)$') AS INTEGER)), 0) INTO n FROM "stories" WHERE product_id = rec.product_id AND code ~ '^ST-\d+$'; UPDATE "stories" SET code = 'ST-' || LPAD((n + sub.row_num)::TEXT, GREATEST(3, LENGTH((n + sub.row_num)::TEXT)), '0') FROM ( SELECT id, ROW_NUMBER() OVER (ORDER BY created_at, id) AS row_num FROM "stories" WHERE product_id = rec.product_id AND code IS NULL ) sub WHERE "stories".id = sub.id; END LOOP; END $$; -- 5) Backfill Task codes (alle rijen — kolom net toegevoegd) DO $$ DECLARE rec RECORD; BEGIN FOR rec IN SELECT DISTINCT product_id FROM "tasks" WHERE code IS NULL LOOP UPDATE "tasks" SET code = 'T-' || sub.row_num FROM ( SELECT id, ROW_NUMBER() OVER (ORDER BY created_at, id) AS row_num FROM "tasks" WHERE product_id = rec.product_id AND code IS NULL ) sub WHERE "tasks".id = sub.id; END LOOP; END $$; -- 6) NOT NULL constraints ALTER TABLE "pbis" ALTER COLUMN "code" SET NOT NULL; ALTER TABLE "stories" ALTER COLUMN "code" SET NOT NULL; ALTER TABLE "tasks" ALTER COLUMN "code" SET NOT NULL; -- 7) Unique + lookup index op Task CREATE UNIQUE INDEX "tasks_product_id_code_key" ON "tasks"("product_id", "code"); CREATE INDEX "tasks_product_id_idx" ON "tasks"("product_id");