Files
lobehub__lobehub/packages/database/migrations/0109_migrate_unique_constraints.sql
Arvin Xu ed47d9ece5 🗃️ build(database): migrate unique constraints to workspace scope (#15472)
* 🗃️ db(database): migrate unique constraints to workspace scope (migration 0109)

Replace the legacy user-scoped UNIQUE constraints with workspace-scoped
partial unique indexes across agents, agent evals, agent skills,
documents, sessions, tasks, and rbac roles/user-roles. Adds migration
0109_migrate_unique_constraints and updates the affected schemas.

* 🐛 fix(database): match partial unique index in getBuiltinAgent upsert

Migration 0109 turned `agents_slug_user_id_unique` into a partial index
(WHERE workspace_id IS NULL). A plain `ON CONFLICT (slug, user_id)` no longer
matches it (Postgres 42P10), breaking getBuiltinAgent. Add the same predicate
via onConflictDoNothing's `where` option; builtin agents are always
workspace-less so the predicate always holds.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* 🔨 chore(database): use bare onConflictDoNothing in getBuiltinAgent for 0109 transition

Index-shape-agnostic upsert so the builtin-agent path works whether
agents_slug_user_id_unique is the legacy full unique or the 0109 partial,
removing the deploy-ordering coupling. Re-tighten to { target, where } in a
follow-up once 0109 has flipped the index everywhere.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 21:08:36 +08:00

20 lines
2.5 KiB
SQL

ALTER TABLE "rbac_roles" DROP CONSTRAINT IF EXISTS "rbac_roles_name_unique";--> statement-breakpoint
DROP INDEX IF EXISTS "agents_slug_user_id_unique";--> statement-breakpoint
DROP INDEX IF EXISTS "agent_eval_benchmarks_identifier_user_id_unique";--> statement-breakpoint
DROP INDEX IF EXISTS "agent_eval_datasets_identifier_user_id_unique";--> statement-breakpoint
DROP INDEX IF EXISTS "agent_skills_user_name_idx";--> statement-breakpoint
DROP INDEX IF EXISTS "documents_slug_user_id_unique";--> statement-breakpoint
DROP INDEX IF EXISTS "slug_user_id_unique";--> statement-breakpoint
DROP INDEX IF EXISTS "tasks_identifier_idx";--> statement-breakpoint
ALTER TABLE "rbac_user_roles" DROP CONSTRAINT IF EXISTS "rbac_user_roles_user_id_role_id_pk";--> statement-breakpoint
ALTER TABLE "rbac_user_roles" ADD COLUMN IF NOT EXISTS "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL;--> statement-breakpoint
CREATE UNIQUE INDEX IF NOT EXISTS "rbac_roles_name_workspace_unique" ON "rbac_roles" USING btree ("name",COALESCE("workspace_id", ''));--> statement-breakpoint
CREATE UNIQUE INDEX IF NOT EXISTS "rbac_user_roles_user_role_scope_unique" ON "rbac_user_roles" USING btree ("user_id","role_id",COALESCE("workspace_id", ''));--> statement-breakpoint
CREATE UNIQUE INDEX IF NOT EXISTS "agents_slug_user_id_unique" ON "agents" USING btree ("slug","user_id") WHERE "agents"."workspace_id" is null;--> statement-breakpoint
CREATE UNIQUE INDEX IF NOT EXISTS "agent_eval_benchmarks_identifier_user_id_unique" ON "agent_eval_benchmarks" USING btree ("identifier","user_id") WHERE "agent_eval_benchmarks"."workspace_id" is null;--> statement-breakpoint
CREATE UNIQUE INDEX IF NOT EXISTS "agent_eval_datasets_identifier_user_id_unique" ON "agent_eval_datasets" USING btree ("identifier","user_id") WHERE "agent_eval_datasets"."workspace_id" is null;--> statement-breakpoint
CREATE UNIQUE INDEX IF NOT EXISTS "agent_skills_user_name_idx" ON "agent_skills" USING btree ("user_id","name") WHERE "agent_skills"."workspace_id" is null;--> statement-breakpoint
CREATE UNIQUE INDEX IF NOT EXISTS "documents_slug_user_id_unique" ON "documents" USING btree ("slug","user_id") WHERE "documents"."workspace_id" IS NULL AND "documents"."slug" IS NOT NULL;--> statement-breakpoint
CREATE UNIQUE INDEX IF NOT EXISTS "slug_user_id_unique" ON "sessions" USING btree ("slug","user_id") WHERE "sessions"."workspace_id" is null;--> statement-breakpoint
CREATE UNIQUE INDEX IF NOT EXISTS "tasks_identifier_idx" ON "tasks" USING btree ("identifier","created_by_user_id") WHERE "tasks"."workspace_id" is null;