PhoenixKit.Migrations.Postgres.V106 (phoenix_kit v1.7.111)

Copy Markdown View Source

V106: Split phoenix_kit_projects.name uniqueness across templates and real projects.

V101 created a single global unique index (phoenix_kit_projects_name_index) on lower(name) over the phoenix_kit_projects table. Templates and real projects share that table — distinguished only by the is_template boolean — so they also shared one name namespace, which made Projects.create_project_from_template/2 collide whenever a real project should reuse the source template's name (the common, expected case).

This migration replaces that single index with two partial unique indexes — one per is_template value — so a template "Onboarding" and a real project "Onboarding" can coexist freely.

Indexes

  • phoenix_kit_projects_name_template_index UNIQUE (lower(name)) WHERE is_template = true
  • phoenix_kit_projects_name_project_index UNIQUE (lower(name)) WHERE is_template = false

Both are idempotent (CREATE INDEX IF NOT EXISTS / DROP INDEX IF EXISTS).

Schema-side change only — changeset half lives downstream

Core owns the SQL. The matching unique_constraint(...) swap on the Project schema lives in the downstream phoenix_kit_projects package, not in this repo (no phoenix_kit_projects schema exists under lib/ here — rg phoenix_kit_projects_name_template_index lib/ will only return this migration).

The downstream changeset picks :phoenix_kit_projects_name_template_index vs :phoenix_kit_projects_name_project_index based on the is_template field at validate time, so unique-name violations surface as a clean name has already been taken form error instead of a generic FK error path. Without the changeset half shipped alongside this migration, the V106 split would be invisible to end users — they'd still see the legacy single constraint name in error tuples.

Summary

Functions

Reverts to V101's single global unique index.

Functions

down(opts)

Reverts to V101's single global unique index.

Lossy rollback: if the post-V106 state has both a template and a real project with the same name, recreating the single phoenix_kit_projects_name_index will fail with a uniqueness violation. Resolve duplicates before rolling back in production.

The down step pre-checks for cross-mode duplicates and raises an actionable message (naming one offending row) BEFORE dropping the partial indexes. Without the pre-check, operators would hit a generic Postgres duplicate key value violates unique constraint during the CREATE UNIQUE INDEX step — same end result but the error message wouldn't tell them which name to resolve, and by then the partial indexes have already been dropped, leaving the table with no name uniqueness at all.

up(opts)