// Convert videos.id and slides.id (plus every FK that references them) from // serial integers to UUID (gen_random_uuid). // // Safe for dev: truncates all video data first. Re-run the push script after // migrating to re-ingest videos. shared_assets (asset library) is unchanged. // // This is a one-way migration. There is no meaningful down path. const CHILD_TABLES = [ // table FK constraint name UNIQUE constraint name UNIQUE expression ['narration_segments', 'narration_segments_project_id_fkey', 'narration_segments_project_key_unique', '(project_id, segment_key)'], ['cutouts', 'cutouts_project_id_fkey', 'cutouts_project_name_unique', '(project_id, name)'], ['filter_chains', 'filter_chains_project_id_fkey', null, null], ['video_assets', 'video_assets_project_id_fkey', 'video_assets_project_key_unique', '(project_id, asset_key)'], ['audio_tracks', 'audio_tracks_project_id_fkey', 'audio_tracks_project_key_unique', '(project_id, track_key)'], ['citations', 'citations_project_id_fkey', null, null], ['project_sync', 'project_sync_project_id_fkey', 'project_sync_project_id_key', '(project_id)'], ['transcripts', 'transcripts_project_id_fkey', 'transcripts_project_source_unique', '(project_id, source_name)'], ]; exports.up = (pgm) => { // ── Clear all video data ───────────────────────────────────────────────── pgm.sql('TRUNCATE videos CASCADE'); // ── 1. Drop all FKs pointing at videos.id BEFORE touching the PK ──────── // slides pgm.sql('ALTER TABLE slides DROP CONSTRAINT IF EXISTS slides_video_id_fkey'); pgm.sql('ALTER TABLE slides DROP CONSTRAINT IF EXISTS slides_video_id_gnommo_slide_id_unique'); // all other child tables for (const [table, fkName, uniqueName] of CHILD_TABLES) { pgm.sql(`ALTER TABLE ${table} DROP CONSTRAINT IF EXISTS ${fkName}`); if (uniqueName) pgm.sql(`ALTER TABLE ${table} DROP CONSTRAINT IF EXISTS ${uniqueName}`); } // ── 2. videos.id: serial → uuid ───────────────────────────────────────── pgm.sql('ALTER TABLE videos ALTER COLUMN id DROP DEFAULT'); pgm.sql('ALTER TABLE videos DROP CONSTRAINT videos_pkey'); pgm.sql('ALTER TABLE videos ALTER COLUMN id TYPE uuid USING gen_random_uuid()'); pgm.sql('ALTER TABLE videos ALTER COLUMN id SET DEFAULT gen_random_uuid()'); pgm.sql('ALTER TABLE videos ADD PRIMARY KEY (id)'); // ── 3. slides: video_id FK column, then id PK ─────────────────────────── pgm.sql('ALTER TABLE slides ALTER COLUMN video_id TYPE uuid USING gen_random_uuid()'); pgm.sql('ALTER TABLE slides ADD CONSTRAINT slides_video_id_gnommo_slide_id_unique UNIQUE (video_id, gnommo_slide_id)'); pgm.sql('ALTER TABLE slides ADD CONSTRAINT slides_video_id_fkey FOREIGN KEY (video_id) REFERENCES videos(id) ON DELETE CASCADE'); pgm.sql('ALTER TABLE slides ALTER COLUMN id DROP DEFAULT'); pgm.sql('ALTER TABLE slides DROP CONSTRAINT slides_pkey'); pgm.sql('ALTER TABLE slides ALTER COLUMN id TYPE uuid USING gen_random_uuid()'); pgm.sql('ALTER TABLE slides ALTER COLUMN id SET DEFAULT gen_random_uuid()'); pgm.sql('ALTER TABLE slides ADD PRIMARY KEY (id)'); // ── 4. All other child tables: project_id integer → uuid ──────────────── for (const [table, fkName, uniqueName, uniqueExpr] of CHILD_TABLES) { pgm.sql(`ALTER TABLE ${table} ALTER COLUMN project_id TYPE uuid USING gen_random_uuid()`); if (uniqueName && uniqueExpr) { pgm.sql(`ALTER TABLE ${table} ADD CONSTRAINT ${uniqueName} UNIQUE ${uniqueExpr}`); } pgm.sql(`ALTER TABLE ${table} ADD CONSTRAINT ${fkName} FOREIGN KEY (project_id) REFERENCES videos(id) ON DELETE CASCADE`); } }; exports.down = () => { // Intentionally empty — reversing uuid → serial would require re-sequencing // all IDs and is not worth implementing for a dev-only migration. };