Skip to content

Commit c20558f

Browse files
fix: coordinated schema migration for audit findings 18-21, 37-39
Combines seven schema fixes into a single migration (0026) to avoid ordering conflicts between individual PRs: - F18: shift_covers.schedule_id serial → integer (drop auto-increment on FK) - F19: flowsheet.play_order serial → integer (manually managed, not auto-inc) - F20: artist_library_crossreference add NOT NULL on both FK columns - F21: show_djs add unique constraint on (show_id, dj_id) - F37: anonymous_devices remove redundant .unique() (keep explicit uniqueIndex) - F38: add ON DELETE cascade/set-null rules to 15 FK relationships - F39: add withTimezone to all 14 wxyc_schema timestamp columns Timestamp conversions use AT TIME ZONE 'America/New_York' to preserve the intended wall-clock times from the station's local timezone. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 9441273 commit c20558f

3 files changed

Lines changed: 216 additions & 43 deletions

File tree

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
-- Schema audit fixes (Findings 18, 19, 20, 21, 37, 38, 39)
2+
-- This migration must be applied AFTER verifying no NULL rows exist in
3+
-- artist_library_crossreference, and after confirming the shift_covers
4+
-- and flowsheet sequences are not relied upon for auto-generation.
5+
6+
-- F18: shift_covers.schedule_id serial → integer
7+
-- Drop the auto-increment sequence. The column type is already integer in PG;
8+
-- serial just adds a DEFAULT nextval() and owns the sequence.
9+
ALTER TABLE "wxyc_schema"."shift_covers" ALTER COLUMN "schedule_id" DROP DEFAULT;
10+
DO $$ BEGIN
11+
IF EXISTS (SELECT 1 FROM pg_class WHERE relname = 'shift_covers_schedule_id_seq') THEN
12+
DROP SEQUENCE "wxyc_schema"."shift_covers_schedule_id_seq";
13+
END IF;
14+
END $$;
15+
--> statement-breakpoint
16+
17+
-- F19: flowsheet.play_order serial → integer
18+
ALTER TABLE "wxyc_schema"."flowsheet" ALTER COLUMN "play_order" DROP DEFAULT;
19+
DO $$ BEGIN
20+
IF EXISTS (SELECT 1 FROM pg_class WHERE relname = 'flowsheet_play_order_seq') THEN
21+
DROP SEQUENCE "wxyc_schema"."flowsheet_play_order_seq";
22+
END IF;
23+
END $$;
24+
--> statement-breakpoint
25+
26+
-- F20: artist_library_crossreference – add NOT NULL to FK columns
27+
-- IMPORTANT: Run this check first to confirm no NULLs exist:
28+
-- SELECT count(*) FROM wxyc_schema.artist_library_crossreference
29+
-- WHERE artist_id IS NULL OR library_id IS NULL;
30+
ALTER TABLE "wxyc_schema"."artist_library_crossreference" ALTER COLUMN "artist_id" SET NOT NULL;
31+
--> statement-breakpoint
32+
ALTER TABLE "wxyc_schema"."artist_library_crossreference" ALTER COLUMN "library_id" SET NOT NULL;
33+
--> statement-breakpoint
34+
35+
-- F21: show_djs – add unique constraint on (show_id, dj_id)
36+
-- First remove any duplicate rows (keep the first inserted)
37+
DELETE FROM "wxyc_schema"."show_djs" a USING "wxyc_schema"."show_djs" b
38+
WHERE a.ctid < b.ctid
39+
AND a.show_id = b.show_id
40+
AND a.dj_id = b.dj_id;
41+
--> statement-breakpoint
42+
CREATE UNIQUE INDEX "show_djs_show_id_dj_id_unique" ON "wxyc_schema"."show_djs" USING btree ("show_id", "dj_id");
43+
--> statement-breakpoint
44+
45+
-- F37: anonymous_devices – remove redundant unique constraint
46+
-- The explicit uniqueIndex('anonymous_devices_device_id_key') remains.
47+
-- Drop the inline .unique() constraint (named by PG convention).
48+
ALTER TABLE "anonymous_devices" DROP CONSTRAINT IF EXISTS "anonymous_devices_device_id_unique";
49+
--> statement-breakpoint
50+
51+
-- F38: FK cascade rules
52+
-- schedule FKs
53+
ALTER TABLE "wxyc_schema"."schedule" DROP CONSTRAINT IF EXISTS "schedule_specialty_id_specialty_shows_id_fk";
54+
ALTER TABLE "wxyc_schema"."schedule" ADD CONSTRAINT "schedule_specialty_id_specialty_shows_id_fk"
55+
FOREIGN KEY ("specialty_id") REFERENCES "wxyc_schema"."specialty_shows"("id") ON DELETE SET NULL;
56+
--> statement-breakpoint
57+
ALTER TABLE "wxyc_schema"."schedule" DROP CONSTRAINT IF EXISTS "schedule_assigned_dj_id_auth_user_id_fk";
58+
ALTER TABLE "wxyc_schema"."schedule" ADD CONSTRAINT "schedule_assigned_dj_id_auth_user_id_fk"
59+
FOREIGN KEY ("assigned_dj_id") REFERENCES "public"."auth_user"("id") ON DELETE SET NULL;
60+
--> statement-breakpoint
61+
ALTER TABLE "wxyc_schema"."schedule" DROP CONSTRAINT IF EXISTS "schedule_assigned_dj_id2_auth_user_id_fk";
62+
ALTER TABLE "wxyc_schema"."schedule" ADD CONSTRAINT "schedule_assigned_dj_id2_auth_user_id_fk"
63+
FOREIGN KEY ("assigned_dj_id2") REFERENCES "public"."auth_user"("id") ON DELETE SET NULL;
64+
--> statement-breakpoint
65+
66+
-- shift_covers FK
67+
ALTER TABLE "wxyc_schema"."shift_covers" DROP CONSTRAINT IF EXISTS "shift_covers_cover_dj_id_auth_user_id_fk";
68+
ALTER TABLE "wxyc_schema"."shift_covers" ADD CONSTRAINT "shift_covers_cover_dj_id_auth_user_id_fk"
69+
FOREIGN KEY ("cover_dj_id") REFERENCES "public"."auth_user"("id") ON DELETE SET NULL;
70+
--> statement-breakpoint
71+
72+
-- rotation FK
73+
ALTER TABLE "wxyc_schema"."rotation" DROP CONSTRAINT IF EXISTS "rotation_album_id_library_id_fk";
74+
ALTER TABLE "wxyc_schema"."rotation" ADD CONSTRAINT "rotation_album_id_library_id_fk"
75+
FOREIGN KEY ("album_id") REFERENCES "wxyc_schema"."library"("id") ON DELETE CASCADE;
76+
--> statement-breakpoint
77+
78+
-- flowsheet FKs
79+
ALTER TABLE "wxyc_schema"."flowsheet" DROP CONSTRAINT IF EXISTS "flowsheet_show_id_shows_id_fk";
80+
ALTER TABLE "wxyc_schema"."flowsheet" ADD CONSTRAINT "flowsheet_show_id_shows_id_fk"
81+
FOREIGN KEY ("show_id") REFERENCES "wxyc_schema"."shows"("id") ON DELETE SET NULL;
82+
--> statement-breakpoint
83+
ALTER TABLE "wxyc_schema"."flowsheet" DROP CONSTRAINT IF EXISTS "flowsheet_album_id_library_id_fk";
84+
ALTER TABLE "wxyc_schema"."flowsheet" ADD CONSTRAINT "flowsheet_album_id_library_id_fk"
85+
FOREIGN KEY ("album_id") REFERENCES "wxyc_schema"."library"("id") ON DELETE SET NULL;
86+
--> statement-breakpoint
87+
ALTER TABLE "wxyc_schema"."flowsheet" DROP CONSTRAINT IF EXISTS "flowsheet_rotation_id_rotation_id_fk";
88+
ALTER TABLE "wxyc_schema"."flowsheet" ADD CONSTRAINT "flowsheet_rotation_id_rotation_id_fk"
89+
FOREIGN KEY ("rotation_id") REFERENCES "wxyc_schema"."rotation"("id") ON DELETE SET NULL;
90+
--> statement-breakpoint
91+
92+
-- reviews FK
93+
ALTER TABLE "wxyc_schema"."reviews" DROP CONSTRAINT IF EXISTS "reviews_album_id_library_id_fk";
94+
ALTER TABLE "wxyc_schema"."reviews" ADD CONSTRAINT "reviews_album_id_library_id_fk"
95+
FOREIGN KEY ("album_id") REFERENCES "wxyc_schema"."library"("id") ON DELETE CASCADE;
96+
--> statement-breakpoint
97+
98+
-- genre_artist_crossreference FKs
99+
ALTER TABLE "wxyc_schema"."genre_artist_crossreference" DROP CONSTRAINT IF EXISTS "genre_artist_crossreference_artist_id_artists_id_fk";
100+
ALTER TABLE "wxyc_schema"."genre_artist_crossreference" ADD CONSTRAINT "genre_artist_crossreference_artist_id_artists_id_fk"
101+
FOREIGN KEY ("artist_id") REFERENCES "wxyc_schema"."artists"("id") ON DELETE CASCADE;
102+
--> statement-breakpoint
103+
ALTER TABLE "wxyc_schema"."genre_artist_crossreference" DROP CONSTRAINT IF EXISTS "genre_artist_crossreference_genre_id_genres_id_fk";
104+
ALTER TABLE "wxyc_schema"."genre_artist_crossreference" ADD CONSTRAINT "genre_artist_crossreference_genre_id_genres_id_fk"
105+
FOREIGN KEY ("genre_id") REFERENCES "wxyc_schema"."genres"("id") ON DELETE CASCADE;
106+
--> statement-breakpoint
107+
108+
-- artist_library_crossreference FKs
109+
ALTER TABLE "wxyc_schema"."artist_library_crossreference" DROP CONSTRAINT IF EXISTS "artist_library_crossreference_artist_id_artists_id_fk";
110+
ALTER TABLE "wxyc_schema"."artist_library_crossreference" ADD CONSTRAINT "artist_library_crossreference_artist_id_artists_id_fk"
111+
FOREIGN KEY ("artist_id") REFERENCES "wxyc_schema"."artists"("id") ON DELETE CASCADE;
112+
--> statement-breakpoint
113+
ALTER TABLE "wxyc_schema"."artist_library_crossreference" DROP CONSTRAINT IF EXISTS "artist_library_crossreference_library_id_library_id_fk";
114+
ALTER TABLE "wxyc_schema"."artist_library_crossreference" ADD CONSTRAINT "artist_library_crossreference_library_id_library_id_fk"
115+
FOREIGN KEY ("library_id") REFERENCES "wxyc_schema"."library"("id") ON DELETE CASCADE;
116+
--> statement-breakpoint
117+
118+
-- shows FKs
119+
ALTER TABLE "wxyc_schema"."shows" DROP CONSTRAINT IF EXISTS "shows_primary_dj_id_auth_user_id_fk";
120+
ALTER TABLE "wxyc_schema"."shows" ADD CONSTRAINT "shows_primary_dj_id_auth_user_id_fk"
121+
FOREIGN KEY ("primary_dj_id") REFERENCES "public"."auth_user"("id") ON DELETE SET NULL;
122+
--> statement-breakpoint
123+
ALTER TABLE "wxyc_schema"."shows" DROP CONSTRAINT IF EXISTS "shows_specialty_id_specialty_shows_id_fk";
124+
ALTER TABLE "wxyc_schema"."shows" ADD CONSTRAINT "shows_specialty_id_specialty_shows_id_fk"
125+
FOREIGN KEY ("specialty_id") REFERENCES "wxyc_schema"."specialty_shows"("id") ON DELETE SET NULL;
126+
--> statement-breakpoint
127+
128+
-- show_djs FK (show_id → cascade, dj_id already has cascade)
129+
ALTER TABLE "wxyc_schema"."show_djs" DROP CONSTRAINT IF EXISTS "show_djs_show_id_shows_id_fk";
130+
ALTER TABLE "wxyc_schema"."show_djs" ADD CONSTRAINT "show_djs_show_id_shows_id_fk"
131+
FOREIGN KEY ("show_id") REFERENCES "wxyc_schema"."shows"("id") ON DELETE CASCADE;
132+
--> statement-breakpoint
133+
134+
-- F39: Convert all wxyc_schema timestamps to timestamptz
135+
-- PostgreSQL preserves values when converting timestamp → timestamptz
136+
ALTER TABLE "wxyc_schema"."shift_covers" ALTER COLUMN "shift_timestamp" TYPE timestamptz USING "shift_timestamp" AT TIME ZONE 'America/New_York';
137+
--> statement-breakpoint
138+
ALTER TABLE "wxyc_schema"."artists" ALTER COLUMN "last_modified" TYPE timestamptz USING "last_modified" AT TIME ZONE 'America/New_York';
139+
--> statement-breakpoint
140+
ALTER TABLE "wxyc_schema"."library" ALTER COLUMN "add_date" TYPE timestamptz USING "add_date" AT TIME ZONE 'America/New_York';
141+
--> statement-breakpoint
142+
ALTER TABLE "wxyc_schema"."library" ALTER COLUMN "last_modified" TYPE timestamptz USING "last_modified" AT TIME ZONE 'America/New_York';
143+
--> statement-breakpoint
144+
ALTER TABLE "wxyc_schema"."flowsheet" ALTER COLUMN "add_time" TYPE timestamptz USING "add_time" AT TIME ZONE 'America/New_York';
145+
--> statement-breakpoint
146+
ALTER TABLE "wxyc_schema"."genres" ALTER COLUMN "last_modified" TYPE timestamptz USING "last_modified" AT TIME ZONE 'America/New_York';
147+
--> statement-breakpoint
148+
ALTER TABLE "wxyc_schema"."reviews" ALTER COLUMN "last_modified" TYPE timestamptz USING "last_modified" AT TIME ZONE 'America/New_York';
149+
--> statement-breakpoint
150+
ALTER TABLE "wxyc_schema"."shows" ALTER COLUMN "start_time" TYPE timestamptz USING "start_time" AT TIME ZONE 'America/New_York';
151+
--> statement-breakpoint
152+
ALTER TABLE "wxyc_schema"."shows" ALTER COLUMN "end_time" TYPE timestamptz USING "end_time" AT TIME ZONE 'America/New_York';
153+
--> statement-breakpoint
154+
ALTER TABLE "wxyc_schema"."specialty_shows" ALTER COLUMN "last_modified" TYPE timestamptz USING "last_modified" AT TIME ZONE 'America/New_York';
155+
--> statement-breakpoint
156+
ALTER TABLE "wxyc_schema"."album_metadata" ALTER COLUMN "last_accessed" TYPE timestamptz USING "last_accessed" AT TIME ZONE 'America/New_York';
157+
--> statement-breakpoint
158+
ALTER TABLE "wxyc_schema"."album_metadata" ALTER COLUMN "created_at" TYPE timestamptz USING "created_at" AT TIME ZONE 'America/New_York';
159+
--> statement-breakpoint
160+
ALTER TABLE "wxyc_schema"."artist_metadata" ALTER COLUMN "last_accessed" TYPE timestamptz USING "last_accessed" AT TIME ZONE 'America/New_York';
161+
--> statement-breakpoint
162+
ALTER TABLE "wxyc_schema"."artist_metadata" ALTER COLUMN "created_at" TYPE timestamptz USING "created_at" AT TIME ZONE 'America/New_York';

shared/database/src/migrations/meta/_journal.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,13 @@
162162
"when": 1768890229444,
163163
"tag": "0025_rate_limiting_tables",
164164
"breakpoints": true
165+
},
166+
{
167+
"idx": 26,
168+
"version": "7",
169+
"when": 1740153600000,
170+
"tag": "0026_schema_audit_fixes",
171+
"breakpoints": true
165172
}
166173
]
167174
}

0 commit comments

Comments
 (0)