/** * Reconcile-historical-add-scripts convoy — Brief 2. * * Backfills the `collections`-table columns that historical scripts added to * prod but which `migrations/1779853647564_initial-schema.js` never captured: * * - `visibility VARCHAR(20) DEFAULT 'private'` (from `scripts/add-collaboration-features.js`, lines 15-21 — collections half only) * - `tcg VARCHAR(50) DEFAULT 'MTG'` (from `scripts/add-collaboration-features.js`, lines 15-21 — collections half only) * - `tags TEXT` (from `scripts/add-collaboration-features.js`, lines 15-21 — collections half only) * - `slug VARCHAR(100) UNIQUE` (from `scripts/add-collection-slugs.js`, lines 17-20) * - `idx_collections_slug` UNIQUE INDEX (from `scripts/add-collection-slugs.js`, line 79) * - `check_slug_format` CHECK constraint (from `scripts/add-collection-slugs.js`, line 91) * - `image TEXT` (from `scripts/add-image-column.js`, lines 14-17) * * Excluded from B2 (out of scope per architect inventory): * - `collection_permissions`, `collection_activity`, `users.is_pending`, * `idx_collection_permissions_*`, `idx_collection_activity_collection_id` * — B3 (`reconcile-collaboration-tables`). * `idx_collections_visibility` — ships in * `1786757312884_add-collections-visibility-index` (B3's timestamp * precedes this file's `visibility` column add). * - `is_system_collection` — already captured by * `migrations/1780378340194_system-collection-description.js`. * * Excluded DML: per-row UPDATE that backfills slugs from `name` via * `lib/slug-utils.js::generateUniqueSlug` (script lines 41-74). Generating * slugs on a fresh env is moot — there are no pre-existing collections to * backfill — and replicating the runtime helper inside a migration would * couple the schema timeline to application code. Operators of long-lived * envs already ran the backfill historically; this migration does NOT * re-run it. * * Idempotency strategy (D2): every statement is `IF NOT EXISTS`-guarded. * * - `ADD COLUMN IF NOT EXISTS` — Postgres native; the `UNIQUE` inline * clause on `slug` is ignored when the column already exists, so prod * (which already has the autogen `collections_slug_key` constraint) * and fresh envs (where the autogen constraint will be added) both * converge correctly. * - `CREATE UNIQUE INDEX IF NOT EXISTS` — Postgres native. * - `ALTER TABLE ... ADD CONSTRAINT` for CHECK — Postgres has no * `IF NOT EXISTS` clause for named constraints, so we wrap in a * `DO $$ BEGIN ... END $$` block that checks `pg_constraint` first. * * Down-migration is a hard stub. See `down()` below — matches * `1779853647564_initial-schema.js` style. * * @type {import('node-pg-migrate').ColumnDefinitions | undefined} */ export const shorthands = undefined; /** * @param {import('node-pg-migrate').MigrationBuilder} pgm * @returns {void} */ export const up = (pgm) => { pgm.sql(` ALTER TABLE collections ADD COLUMN IF NOT EXISTS visibility VARCHAR(20) DEFAULT 'private', ADD COLUMN IF NOT EXISTS tcg VARCHAR(50) DEFAULT 'MTG', ADD COLUMN IF NOT EXISTS tags TEXT `); pgm.sql(` ALTER TABLE collections ADD COLUMN IF NOT EXISTS image TEXT `); pgm.sql(` ALTER TABLE collections ADD COLUMN IF NOT EXISTS slug VARCHAR(100) UNIQUE `); pgm.sql(` CREATE UNIQUE INDEX IF NOT EXISTS idx_collections_slug ON collections(slug) `); pgm.sql(` DO $$ BEGIN IF NOT EXISTS ( SELECT 1 FROM pg_constraint WHERE conname = 'check_slug_format' AND conrelid = 'collections'::regclass ) THEN ALTER TABLE collections ADD CONSTRAINT check_slug_format CHECK (slug ~ '^[a-z0-9]([a-z0-9-]*[a-z0-9])?$' AND length(slug) <= 50); END IF; END $$; `); }; /** * Down-migration is a hard stub. This is a reconciliation migration: the * prod schema state is the source of truth, and dropping these columns * would discard slugs / visibility flags / tcg labels / tags / images that * application code reads on every collection page render. If you need a * clean schema for testing, branch the Neon database and run * `npm run migrate up` against the branch instead of rolling this back. * * @returns {void} */ export const down = () => { throw new Error( '[migration:1781000000002_reconcile-collections-columns] Refusing to drop reconciled columns. ' + 'Rolling back this migration would erase visibility/tcg/tags/slug/image data on every ' + 'collections row in the database. If you need a clean schema for testing, branch the ' + 'Neon database and run `npm run migrate up` against the branch instead. See ' + 'migrations/1781000000002_reconcile-collections-columns.js up() docstring for the ' + 'reconciliation rationale.' ); };