Files
iboard/prisma/migrations/20261026010000_uuid_fk_mapping/migration.sql
T

97 lines
5.7 KiB
PL/PgSQL

-- uuid 外键镜像 (PowerSync Sequential ID Mapping, 见 docs/V0.2/离线编辑模块/0.2.1实施方案.md §3)
-- 目的: 同步流以 uuid 作为客户端主键 (离线新建行同步回填后本地 id 稳定不变),
-- 外键列同步输出 uuid 镜像 (别名回 projectId/columnId/parentId/taskId), 客户端零改动。
-- 镜像列由触发器维护: REST/Prisma 写 int 外键 -> 触发器回填 uuid; upload 端在应用层解析 uuid -> int。
-- 1) 加列 (先可空)
ALTER TABLE "BoardColumn" ADD COLUMN "projectUuid" VARCHAR(36);
ALTER TABLE "Task" ADD COLUMN "projectUuid" VARCHAR(36), ADD COLUMN "columnUuid" VARCHAR(36), ADD COLUMN "parentUuid" VARCHAR(36);
ALTER TABLE "ProjectMember" ADD COLUMN "projectUuid" VARCHAR(36);
ALTER TABLE "TaskMember" ADD COLUMN "taskUuid" VARCHAR(36);
ALTER TABLE "TaskTag" ADD COLUMN "taskUuid" VARCHAR(36);
-- 2) 存量回填
UPDATE "BoardColumn" c SET "projectUuid" = p."uuid" FROM "Project" p WHERE c."projectId" = p."id";
UPDATE "Task" t SET "projectUuid" = p."uuid" FROM "Project" p WHERE t."projectId" = p."id";
UPDATE "Task" t SET "columnUuid" = c."uuid" FROM "BoardColumn" c WHERE t."columnId" = c."id";
UPDATE "Task" t SET "parentUuid" = pt."uuid" FROM "Task" pt WHERE t."parentId" = pt."id";
UPDATE "ProjectMember" pm SET "projectUuid" = p."uuid" FROM "Project" p WHERE pm."projectId" = p."id";
UPDATE "TaskMember" tm SET "taskUuid" = t."uuid" FROM "Task" t WHERE tm."taskId" = t."id";
UPDATE "TaskTag" tt SET "taskUuid" = t."uuid" FROM "Task" t WHERE tt."taskId" = t."id";
-- 3) 非空约束
ALTER TABLE "BoardColumn" ALTER COLUMN "projectUuid" SET NOT NULL;
ALTER TABLE "Task" ALTER COLUMN "projectUuid" SET NOT NULL;
ALTER TABLE "Task" ALTER COLUMN "columnUuid" SET NOT NULL;
ALTER TABLE "ProjectMember" ALTER COLUMN "projectUuid" SET NOT NULL;
ALTER TABLE "TaskMember" ALTER COLUMN "taskUuid" SET NOT NULL;
ALTER TABLE "TaskTag" ALTER COLUMN "taskUuid" SET NOT NULL;
-- 4) 外键 (级联与 int 外键一致)
ALTER TABLE "BoardColumn" ADD CONSTRAINT "BoardColumn_projectUuid_fkey" FOREIGN KEY ("projectUuid") REFERENCES "Project"("uuid") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "Task" ADD CONSTRAINT "Task_projectUuid_fkey" FOREIGN KEY ("projectUuid") REFERENCES "Project"("uuid") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "Task" ADD CONSTRAINT "Task_columnUuid_fkey" FOREIGN KEY ("columnUuid") REFERENCES "BoardColumn"("uuid") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "Task" ADD CONSTRAINT "Task_parentUuid_fkey" FOREIGN KEY ("parentUuid") REFERENCES "Task"("uuid") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "ProjectMember" ADD CONSTRAINT "ProjectMember_projectUuid_fkey" FOREIGN KEY ("projectUuid") REFERENCES "Project"("uuid") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "TaskMember" ADD CONSTRAINT "TaskMember_taskUuid_fkey" FOREIGN KEY ("taskUuid") REFERENCES "Task"("uuid") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "TaskTag" ADD CONSTRAINT "TaskTag_taskUuid_fkey" FOREIGN KEY ("taskUuid") REFERENCES "Task"("uuid") ON DELETE CASCADE ON UPDATE CASCADE;
-- 5) 触发器: 写入时按 int 外键回填 uuid 镜像
CREATE OR REPLACE FUNCTION "func_projectmember_uuid_map"() RETURNS TRIGGER AS $$
BEGIN
SELECT "uuid" INTO NEW."projectUuid" FROM "Project" WHERE "id" = NEW."projectId";
IF NOT FOUND THEN RAISE EXCEPTION 'Project % 不存在', NEW."projectId"; END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER "trg_projectmember_uuid_map" BEFORE INSERT OR UPDATE ON "ProjectMember"
FOR EACH ROW EXECUTE FUNCTION "func_projectmember_uuid_map"();
CREATE OR REPLACE FUNCTION "func_boardcolumn_uuid_map"() RETURNS TRIGGER AS $$
BEGIN
SELECT "uuid" INTO NEW."projectUuid" FROM "Project" WHERE "id" = NEW."projectId";
IF NOT FOUND THEN RAISE EXCEPTION 'Project % 不存在', NEW."projectId"; END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER "trg_boardcolumn_uuid_map" BEFORE INSERT OR UPDATE ON "BoardColumn"
FOR EACH ROW EXECUTE FUNCTION "func_boardcolumn_uuid_map"();
CREATE OR REPLACE FUNCTION "func_task_uuid_map"() RETURNS TRIGGER AS $$
BEGIN
SELECT "uuid" INTO NEW."projectUuid" FROM "Project" WHERE "id" = NEW."projectId";
IF NOT FOUND THEN RAISE EXCEPTION 'Project % 不存在', NEW."projectId"; END IF;
SELECT "uuid" INTO NEW."columnUuid" FROM "BoardColumn" WHERE "id" = NEW."columnId";
IF NOT FOUND THEN RAISE EXCEPTION 'BoardColumn % 不存在', NEW."columnId"; END IF;
IF NEW."parentId" IS NULL THEN
NEW."parentUuid" := NULL;
ELSE
SELECT "uuid" INTO NEW."parentUuid" FROM "Task" WHERE "id" = NEW."parentId";
IF NOT FOUND THEN RAISE EXCEPTION 'Task % 不存在', NEW."parentId"; END IF;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER "trg_task_uuid_map" BEFORE INSERT OR UPDATE ON "Task"
FOR EACH ROW EXECUTE FUNCTION "func_task_uuid_map"();
CREATE OR REPLACE FUNCTION "func_taskmember_uuid_map"() RETURNS TRIGGER AS $$
BEGIN
SELECT "uuid" INTO NEW."taskUuid" FROM "Task" WHERE "id" = NEW."taskId";
IF NOT FOUND THEN RAISE EXCEPTION 'Task % 不存在', NEW."taskId"; END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER "trg_taskmember_uuid_map" BEFORE INSERT OR UPDATE ON "TaskMember"
FOR EACH ROW EXECUTE FUNCTION "func_taskmember_uuid_map"();
CREATE OR REPLACE FUNCTION "func_tasktag_uuid_map"() RETURNS TRIGGER AS $$
BEGIN
SELECT "uuid" INTO NEW."taskUuid" FROM "Task" WHERE "id" = NEW."taskId";
IF NOT FOUND THEN RAISE EXCEPTION 'Task % 不存在', NEW."taskId"; END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER "trg_tasktag_uuid_map" BEFORE INSERT OR UPDATE ON "TaskTag"
FOR EACH ROW EXECUTE FUNCTION "func_tasktag_uuid_map"();