98ec64c658
FILTER-aggregate count over albums.cover_art_source and albums.mbid returning total, with_art, pending, settled, pending_no_mbid in a single round-trip. Sub-millisecond on realistic libraries; no index needed for v1. pending_no_mbid is a subset of pending — the UI tooltip uses it to show how many "pending" rows are blocked on missing MBID and won't be moved by another scan. Also bumps the Makefile sqlc pin from 1.27.0 to 1.31.1 to match the version that produced all existing committed dbq/*.go files. The prior pin was stale; running make generate against 1.27.0 silently rolled the codegen back (cosmetic version headers on most files plus a real codegen shape change in DeleteArtistIfEmpty).
143 lines
5.3 KiB
SQL
143 lines
5.3 KiB
SQL
-- name: UpsertAlbum :one
|
|
INSERT INTO albums (title, sort_title, artist_id, release_date, mbid, cover_art_path)
|
|
VALUES ($1, $2, $3, $4, $5, $6)
|
|
ON CONFLICT (mbid) WHERE mbid IS NOT NULL
|
|
DO UPDATE SET
|
|
title = EXCLUDED.title,
|
|
sort_title = EXCLUDED.sort_title,
|
|
artist_id = EXCLUDED.artist_id,
|
|
release_date = EXCLUDED.release_date,
|
|
cover_art_path = EXCLUDED.cover_art_path,
|
|
updated_at = now()
|
|
RETURNING *;
|
|
|
|
-- name: GetAlbumByID :one
|
|
SELECT * FROM albums WHERE id = $1;
|
|
|
|
-- name: GetAlbumByArtistAndTitle :one
|
|
-- Scanner uses this for the no-mbid dedupe path: resolve-or-create.
|
|
SELECT * FROM albums WHERE artist_id = $1 AND title = $2 LIMIT 1;
|
|
|
|
-- name: ListAlbumsByArtist :many
|
|
SELECT * FROM albums WHERE artist_id = $1 ORDER BY release_date NULLS LAST, sort_title;
|
|
|
|
-- name: ListAlbumsAlphaByName :many
|
|
SELECT * FROM albums ORDER BY sort_title LIMIT $1 OFFSET $2;
|
|
|
|
-- name: ListAlbumsAlphaByArtist :many
|
|
-- Sorted by the owning artist's sort_name. Needed by Subsonic's
|
|
-- alphabeticalByArtist album list type.
|
|
SELECT sqlc.embed(albums), artists.sort_name AS artist_sort_name
|
|
FROM albums
|
|
JOIN artists ON artists.id = albums.artist_id
|
|
ORDER BY artists.sort_name, albums.sort_title
|
|
LIMIT $1 OFFSET $2;
|
|
|
|
-- name: ListAlbumsNewest :many
|
|
SELECT * FROM albums ORDER BY created_at DESC LIMIT $1 OFFSET $2;
|
|
|
|
-- name: ListAlbumsRandom :many
|
|
SELECT * FROM albums ORDER BY random() LIMIT $1;
|
|
|
|
-- name: ListAlbumsByGenre :many
|
|
-- Album "belongs to" a genre if any of its tracks carry that genre.
|
|
SELECT DISTINCT ON (albums.id) albums.*
|
|
FROM albums
|
|
JOIN tracks ON tracks.album_id = albums.id
|
|
WHERE tracks.genre = $1
|
|
ORDER BY albums.id, albums.sort_title
|
|
LIMIT $2 OFFSET $3;
|
|
|
|
-- name: SearchAlbums :many
|
|
SELECT * FROM albums
|
|
WHERE title ILIKE '%' || $1 || '%'
|
|
ORDER BY sort_title
|
|
LIMIT $2 OFFSET $3;
|
|
|
|
-- name: CountAlbumsMatching :one
|
|
SELECT COUNT(*) FROM albums WHERE title ILIKE '%' || $1::text || '%';
|
|
|
|
-- name: ListRecentlyAddedAlbumsWithArtist :many
|
|
-- M6a: recently-added albums joined with artist_name + artist_id for the
|
|
-- home-page section. created_at is the row-insert timestamp from the
|
|
-- scanner — the closest proxy to "added to library". Stable secondary
|
|
-- ordering by id keeps pagination tie-break deterministic.
|
|
SELECT sqlc.embed(albums), artists.name AS artist_name
|
|
FROM albums
|
|
JOIN artists ON artists.id = albums.artist_id
|
|
ORDER BY albums.created_at DESC, albums.id
|
|
LIMIT $1;
|
|
|
|
-- name: ListAlbumsAlphaWithArtist :many
|
|
-- M6a: alpha-sorted album list joined with artist_name. Used by
|
|
-- /api/library/albums for the wrapping-grid page. Stable id-tiebreak.
|
|
SELECT sqlc.embed(albums), artists.name AS artist_name
|
|
FROM albums
|
|
JOIN artists ON artists.id = albums.artist_id
|
|
ORDER BY albums.sort_title, albums.id
|
|
LIMIT $1 OFFSET $2;
|
|
|
|
-- name: CountAlbums :one
|
|
-- M6a: total album count for the /api/library/albums envelope.
|
|
SELECT COUNT(*) FROM albums;
|
|
|
|
-- name: CountAlbumsByArtist :one
|
|
-- Used by request-progress reporting to count how many albums have been
|
|
-- ingested for a matched artist while a request is still in flight.
|
|
SELECT COUNT(*) FROM albums WHERE artist_id = $1;
|
|
|
|
-- name: DeleteAlbumIfEmpty :one
|
|
-- M7 #372: deletes the album row only when it has no remaining tracks.
|
|
-- Used after a track delete to tidy up orphaned albums. RETURNING gives
|
|
-- us the artist_id so the service can chain into the artist-empty check.
|
|
-- Returns no rows when the album still has tracks; the service treats
|
|
-- pgx.ErrNoRows as "album not orphaned, skip cascade".
|
|
DELETE FROM albums a
|
|
WHERE a.id = $1
|
|
AND NOT EXISTS (SELECT 1 FROM tracks t WHERE t.album_id = a.id)
|
|
RETURNING a.id, a.artist_id;
|
|
|
|
-- name: SetAlbumMbidIfNull :exec
|
|
-- M7 #379: heal MBID on existing album rows during rescan. Idempotent —
|
|
-- only updates when the existing mbid is NULL.
|
|
UPDATE albums
|
|
SET mbid = $2,
|
|
updated_at = now()
|
|
WHERE id = $1 AND mbid IS NULL;
|
|
|
|
-- name: ListAlbumsMissingMbidWithTrack :many
|
|
-- One-shot MBID backfill: returns each album where mbid IS NULL alongside
|
|
-- one of its tracks' file_path so the worker can re-read tags. LIMIT
|
|
-- supplied by caller for batching/progress purposes.
|
|
SELECT a.id AS album_id,
|
|
a.artist_id AS artist_id,
|
|
a.title AS title,
|
|
t.file_path AS track_file_path
|
|
FROM albums a
|
|
JOIN LATERAL (
|
|
SELECT file_path
|
|
FROM tracks
|
|
WHERE album_id = a.id
|
|
ORDER BY disc_number NULLS LAST, track_number NULLS LAST, id
|
|
LIMIT 1
|
|
) t ON true
|
|
WHERE a.mbid IS NULL
|
|
ORDER BY a.created_at ASC
|
|
LIMIT $1;
|
|
|
|
-- name: GetAlbumCoverageRollup :one
|
|
-- M7 coverage-gauge: library-wide cover-art coverage rollup for the
|
|
-- admin gauge. Single sequential scan with FILTER aggregates —
|
|
-- sub-millisecond on realistic libraries. pending_no_mbid is a SUBSET
|
|
-- of pending; the UI surfaces it separately so operators can see how
|
|
-- many "pending" albums are blocked on missing MBID and won't be
|
|
-- helped by another scan.
|
|
SELECT
|
|
COUNT(*) AS total,
|
|
COUNT(*) FILTER (WHERE cover_art_source IN ('sidecar','embedded','mbcaa')) AS with_art,
|
|
COUNT(*) FILTER (WHERE cover_art_source IS NULL) AS pending,
|
|
COUNT(*) FILTER (WHERE cover_art_source = 'none') AS settled,
|
|
COUNT(*) FILTER (WHERE cover_art_source IS NULL AND mbid IS NULL) AS pending_no_mbid
|
|
FROM albums;
|
|
|