feat(codes): code NOT NULL voor PBI/Story + Task.code + product_id denorm
- Pbi.code en Story.code worden NOT NULL (tot dusver optional) - Task krijgt code String + product_id String denorm + @@unique([product_id, code]) - Product krijgt back-relation tasks Task[] - Migratie backfillt bestaande NULL-rijen via PL/pgSQL: PBI-N (per product), ST-N (3-digit padded met GREATEST om truncatie van LPAD bij 4-digit nummers te voorkomen), T-N voor alle tasks - Codes zijn stabiele identifiers (Jira-stijl flat-per-product), zodat re-parenting de code niet muteert Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
4b0ab8e4b2
commit
611b621d75
3 changed files with 84 additions and 2 deletions
|
|
@ -0,0 +1,75 @@
|
|||
-- 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");
|
||||
Loading…
Add table
Add a link
Reference in a new issue