-- name: CreateLidarrRequest :one INSERT INTO lidarr_requests ( user_id, kind, lidarr_artist_mbid, lidarr_album_mbid, lidarr_track_mbid, artist_name, album_title, track_title ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING *; -- name: GetLidarrRequestByID :one SELECT * FROM lidarr_requests WHERE id = $1; -- name: ListLidarrRequestsForUser :many SELECT * FROM lidarr_requests WHERE user_id = $1 ORDER BY requested_at DESC LIMIT $2; -- name: ListLidarrRequestsByStatus :many SELECT * FROM lidarr_requests WHERE status = $1 ORDER BY requested_at DESC LIMIT $2; -- name: ListApprovedLidarrRequestsForReconcile :many SELECT * FROM lidarr_requests WHERE status = 'approved' ORDER BY decided_at ASC LIMIT $1; -- name: ApproveLidarrRequest :one UPDATE lidarr_requests SET status = 'approved', quality_profile_id = $2, root_folder_path = $3, decided_at = now(), decided_by = $4, updated_at = now() WHERE id = $1 AND status = 'pending' RETURNING *; -- name: RejectLidarrRequest :one UPDATE lidarr_requests SET status = 'rejected', notes = $2, decided_at = now(), decided_by = $3, updated_at = now() WHERE id = $1 AND status = 'pending' RETURNING *; -- name: CancelLidarrRequest :one -- $2 serves as both the ownership guard (user_id = $2) and the -- decided_by audit field. The generated parameter struct names it -- DecidedBy — callers must pass the cancelling user's UUID, and -- a wrong UUID will silently no-op (zero rows updated) rather than -- erroring at the SQL layer. UPDATE lidarr_requests SET status = 'rejected', notes = 'cancelled by user', decided_at = now(), decided_by = $2, updated_at = now() WHERE id = $1 AND user_id = $2 AND status = 'pending' RETURNING *; -- name: CompleteLidarrRequest :one -- Reconciler transitions an approved request to completed when its -- target track/album/artist has appeared in the library. Callers -- should set ONLY the matched_*_id corresponding to the request's -- kind and pass pgtype.UUID{} (Valid: false) for the others — -- per-kind one-of-three is the contract, NULL for the others -- preserves auditability if the matched row is later evicted. UPDATE lidarr_requests SET status = 'completed', matched_track_id = $2, matched_album_id = $3, matched_artist_id = $4, completed_at = now(), updated_at = now() WHERE id = $1 AND status = 'approved' RETURNING *; -- name: HasNonTerminalRequestForMBID :one -- Returns true if any user has a pending/approved/completed request -- whose MBID matches at the given level. Used to set the `requested` -- flag on /api/lidarr/search responses. Terminal-status (rejected, -- failed) rows do not count. The same MBID parameter is checked -- against artist/album/track columns based on kind. SELECT EXISTS ( SELECT 1 FROM lidarr_requests WHERE status IN ('pending', 'approved', 'completed') AND ((kind = 'artist' AND lidarr_artist_mbid = @mbid) OR (kind = 'album' AND lidarr_album_mbid = @mbid) OR (kind = 'track' AND lidarr_track_mbid = @mbid)) ); -- name: GetNonTerminalRequestForMBID :one -- Returns the oldest non-terminal (pending/approved/completed) request -- whose own-kind MBID matches @mbid, or pgx.ErrNoRows when none. Create() -- uses this to dedupe: a re-request for something already in flight -- returns the existing row instead of inserting a duplicate. MBIDs are -- globally unique per entity type, so the cross-column OR cannot -- false-match (an artist MBID never collides with an album/track MBID). SELECT * FROM lidarr_requests WHERE status IN ('pending', 'approved', 'completed') AND ((kind = 'artist' AND lidarr_artist_mbid = @mbid) OR (kind = 'album' AND lidarr_album_mbid = @mbid) OR (kind = 'track' AND lidarr_track_mbid = @mbid)) ORDER BY requested_at ASC LIMIT 1; -- name: MarkLidarrRequestAddConfirmed :exec -- Idempotently records that Lidarr accepted the add for an approved -- request. The lidarr_add_confirmed_at IS NULL guard makes a redundant -- reconciler retry a no-op rather than bumping updated_at every tick. UPDATE lidarr_requests SET lidarr_add_confirmed_at = now(), updated_at = now() WHERE id = $1 AND status = 'approved' AND lidarr_add_confirmed_at IS NULL;