diff --git a/internal/db/migrations/0015_system_playlists.down.sql b/internal/db/migrations/0015_system_playlists.down.sql new file mode 100644 index 00000000..9e7ef182 --- /dev/null +++ b/internal/db/migrations/0015_system_playlists.down.sql @@ -0,0 +1,10 @@ +DROP TABLE IF EXISTS system_playlist_runs; +DROP INDEX IF EXISTS playlists_user_kind_idx; +ALTER TABLE playlists + DROP CONSTRAINT IF EXISTS playlists_seed_consistent; +ALTER TABLE playlists + DROP CONSTRAINT IF EXISTS playlists_kind_variant_consistent; +ALTER TABLE playlists + DROP COLUMN IF EXISTS seed_artist_id, + DROP COLUMN IF EXISTS system_variant, + DROP COLUMN IF EXISTS kind; diff --git a/internal/db/migrations/0015_system_playlists.up.sql b/internal/db/migrations/0015_system_playlists.up.sql new file mode 100644 index 00000000..32eb016f --- /dev/null +++ b/internal/db/migrations/0015_system_playlists.up.sql @@ -0,0 +1,46 @@ +-- M7 #352 slice 2: system-generated playlists. +-- Adds kind/system_variant/seed_artist_id columns to playlists and a +-- per-user run-tracking table. Backfills existing slice-1 playlists +-- to kind='user'. + +-- Discriminator on existing playlists table. +ALTER TABLE playlists + ADD COLUMN kind text NOT NULL DEFAULT 'user', + ADD COLUMN system_variant text, + ADD COLUMN seed_artist_id uuid REFERENCES artists(id) ON DELETE SET NULL; + +-- Belt-and-braces backfill in case the DEFAULT didn't apply on some +-- runtime branches; a no-op when the column metadata already filled. +UPDATE playlists SET kind = 'user' WHERE kind IS NULL OR kind = ''; + +-- Constraint: kind/system_variant pair is consistent. +ALTER TABLE playlists + ADD CONSTRAINT playlists_kind_variant_consistent + CHECK ( + (kind = 'user' AND system_variant IS NULL) + OR + (kind = 'system' AND system_variant IN ('for_you', 'songs_like_artist')) + ); + +-- Constraint: 'songs_like_artist' has a seed artist; nothing else does. +ALTER TABLE playlists + ADD CONSTRAINT playlists_seed_consistent + CHECK ( + (system_variant IS NULL AND seed_artist_id IS NULL) + OR + (system_variant = 'for_you' AND seed_artist_id IS NULL) + OR + (system_variant = 'songs_like_artist' AND seed_artist_id IS NOT NULL) + ); + +-- Filter index for kind=user vs kind=system queries on the user's row. +CREATE INDEX playlists_user_kind_idx ON playlists (user_id, kind); + +-- Per-user run tracking — one row per user. +CREATE TABLE system_playlist_runs ( + user_id uuid PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE, + last_run_at timestamptz, + last_run_date date, + in_flight boolean NOT NULL DEFAULT false, + last_error text +);