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).
674 lines
18 KiB
Go
674 lines
18 KiB
Go
// Code generated by sqlc. DO NOT EDIT.
|
|
// versions:
|
|
// sqlc v1.31.1
|
|
// source: albums.sql
|
|
|
|
package dbq
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/jackc/pgx/v5/pgtype"
|
|
)
|
|
|
|
const countAlbums = `-- name: CountAlbums :one
|
|
SELECT COUNT(*) FROM albums
|
|
`
|
|
|
|
// M6a: total album count for the /api/library/albums envelope.
|
|
func (q *Queries) CountAlbums(ctx context.Context) (int64, error) {
|
|
row := q.db.QueryRow(ctx, countAlbums)
|
|
var count int64
|
|
err := row.Scan(&count)
|
|
return count, err
|
|
}
|
|
|
|
const countAlbumsByArtist = `-- name: CountAlbumsByArtist :one
|
|
SELECT COUNT(*) FROM albums WHERE artist_id = $1
|
|
`
|
|
|
|
// Used by request-progress reporting to count how many albums have been
|
|
// ingested for a matched artist while a request is still in flight.
|
|
func (q *Queries) CountAlbumsByArtist(ctx context.Context, artistID pgtype.UUID) (int64, error) {
|
|
row := q.db.QueryRow(ctx, countAlbumsByArtist, artistID)
|
|
var count int64
|
|
err := row.Scan(&count)
|
|
return count, err
|
|
}
|
|
|
|
const countAlbumsMatching = `-- name: CountAlbumsMatching :one
|
|
SELECT COUNT(*) FROM albums WHERE title ILIKE '%' || $1::text || '%'
|
|
`
|
|
|
|
func (q *Queries) CountAlbumsMatching(ctx context.Context, dollar_1 string) (int64, error) {
|
|
row := q.db.QueryRow(ctx, countAlbumsMatching, dollar_1)
|
|
var count int64
|
|
err := row.Scan(&count)
|
|
return count, err
|
|
}
|
|
|
|
const deleteAlbumIfEmpty = `-- name: DeleteAlbumIfEmpty :one
|
|
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
|
|
`
|
|
|
|
type DeleteAlbumIfEmptyRow struct {
|
|
ID pgtype.UUID
|
|
ArtistID pgtype.UUID
|
|
}
|
|
|
|
// 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".
|
|
func (q *Queries) DeleteAlbumIfEmpty(ctx context.Context, id pgtype.UUID) (DeleteAlbumIfEmptyRow, error) {
|
|
row := q.db.QueryRow(ctx, deleteAlbumIfEmpty, id)
|
|
var i DeleteAlbumIfEmptyRow
|
|
err := row.Scan(&i.ID, &i.ArtistID)
|
|
return i, err
|
|
}
|
|
|
|
const getAlbumByArtistAndTitle = `-- name: GetAlbumByArtistAndTitle :one
|
|
SELECT id, title, sort_title, artist_id, release_date, mbid, cover_art_path, created_at, updated_at, cover_art_source FROM albums WHERE artist_id = $1 AND title = $2 LIMIT 1
|
|
`
|
|
|
|
type GetAlbumByArtistAndTitleParams struct {
|
|
ArtistID pgtype.UUID
|
|
Title string
|
|
}
|
|
|
|
// Scanner uses this for the no-mbid dedupe path: resolve-or-create.
|
|
func (q *Queries) GetAlbumByArtistAndTitle(ctx context.Context, arg GetAlbumByArtistAndTitleParams) (Album, error) {
|
|
row := q.db.QueryRow(ctx, getAlbumByArtistAndTitle, arg.ArtistID, arg.Title)
|
|
var i Album
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.Title,
|
|
&i.SortTitle,
|
|
&i.ArtistID,
|
|
&i.ReleaseDate,
|
|
&i.Mbid,
|
|
&i.CoverArtPath,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
&i.CoverArtSource,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const getAlbumByID = `-- name: GetAlbumByID :one
|
|
SELECT id, title, sort_title, artist_id, release_date, mbid, cover_art_path, created_at, updated_at, cover_art_source FROM albums WHERE id = $1
|
|
`
|
|
|
|
func (q *Queries) GetAlbumByID(ctx context.Context, id pgtype.UUID) (Album, error) {
|
|
row := q.db.QueryRow(ctx, getAlbumByID, id)
|
|
var i Album
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.Title,
|
|
&i.SortTitle,
|
|
&i.ArtistID,
|
|
&i.ReleaseDate,
|
|
&i.Mbid,
|
|
&i.CoverArtPath,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
&i.CoverArtSource,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const getAlbumCoverageRollup = `-- name: GetAlbumCoverageRollup :one
|
|
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
|
|
`
|
|
|
|
type GetAlbumCoverageRollupRow struct {
|
|
Total int64
|
|
WithArt int64
|
|
Pending int64
|
|
Settled int64
|
|
PendingNoMbid int64
|
|
}
|
|
|
|
// 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.
|
|
func (q *Queries) GetAlbumCoverageRollup(ctx context.Context) (GetAlbumCoverageRollupRow, error) {
|
|
row := q.db.QueryRow(ctx, getAlbumCoverageRollup)
|
|
var i GetAlbumCoverageRollupRow
|
|
err := row.Scan(
|
|
&i.Total,
|
|
&i.WithArt,
|
|
&i.Pending,
|
|
&i.Settled,
|
|
&i.PendingNoMbid,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const listAlbumsAlphaByArtist = `-- name: ListAlbumsAlphaByArtist :many
|
|
SELECT albums.id, albums.title, albums.sort_title, albums.artist_id, albums.release_date, albums.mbid, albums.cover_art_path, albums.created_at, albums.updated_at, albums.cover_art_source, 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
|
|
`
|
|
|
|
type ListAlbumsAlphaByArtistParams struct {
|
|
Limit int32
|
|
Offset int32
|
|
}
|
|
|
|
type ListAlbumsAlphaByArtistRow struct {
|
|
Album Album
|
|
ArtistSortName string
|
|
}
|
|
|
|
// Sorted by the owning artist's sort_name. Needed by Subsonic's
|
|
// alphabeticalByArtist album list type.
|
|
func (q *Queries) ListAlbumsAlphaByArtist(ctx context.Context, arg ListAlbumsAlphaByArtistParams) ([]ListAlbumsAlphaByArtistRow, error) {
|
|
rows, err := q.db.Query(ctx, listAlbumsAlphaByArtist, arg.Limit, arg.Offset)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []ListAlbumsAlphaByArtistRow
|
|
for rows.Next() {
|
|
var i ListAlbumsAlphaByArtistRow
|
|
if err := rows.Scan(
|
|
&i.Album.ID,
|
|
&i.Album.Title,
|
|
&i.Album.SortTitle,
|
|
&i.Album.ArtistID,
|
|
&i.Album.ReleaseDate,
|
|
&i.Album.Mbid,
|
|
&i.Album.CoverArtPath,
|
|
&i.Album.CreatedAt,
|
|
&i.Album.UpdatedAt,
|
|
&i.Album.CoverArtSource,
|
|
&i.ArtistSortName,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const listAlbumsAlphaByName = `-- name: ListAlbumsAlphaByName :many
|
|
SELECT id, title, sort_title, artist_id, release_date, mbid, cover_art_path, created_at, updated_at, cover_art_source FROM albums ORDER BY sort_title LIMIT $1 OFFSET $2
|
|
`
|
|
|
|
type ListAlbumsAlphaByNameParams struct {
|
|
Limit int32
|
|
Offset int32
|
|
}
|
|
|
|
func (q *Queries) ListAlbumsAlphaByName(ctx context.Context, arg ListAlbumsAlphaByNameParams) ([]Album, error) {
|
|
rows, err := q.db.Query(ctx, listAlbumsAlphaByName, arg.Limit, arg.Offset)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []Album
|
|
for rows.Next() {
|
|
var i Album
|
|
if err := rows.Scan(
|
|
&i.ID,
|
|
&i.Title,
|
|
&i.SortTitle,
|
|
&i.ArtistID,
|
|
&i.ReleaseDate,
|
|
&i.Mbid,
|
|
&i.CoverArtPath,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
&i.CoverArtSource,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const listAlbumsAlphaWithArtist = `-- name: ListAlbumsAlphaWithArtist :many
|
|
SELECT albums.id, albums.title, albums.sort_title, albums.artist_id, albums.release_date, albums.mbid, albums.cover_art_path, albums.created_at, albums.updated_at, albums.cover_art_source, 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
|
|
`
|
|
|
|
type ListAlbumsAlphaWithArtistParams struct {
|
|
Limit int32
|
|
Offset int32
|
|
}
|
|
|
|
type ListAlbumsAlphaWithArtistRow struct {
|
|
Album Album
|
|
ArtistName string
|
|
}
|
|
|
|
// M6a: alpha-sorted album list joined with artist_name. Used by
|
|
// /api/library/albums for the wrapping-grid page. Stable id-tiebreak.
|
|
func (q *Queries) ListAlbumsAlphaWithArtist(ctx context.Context, arg ListAlbumsAlphaWithArtistParams) ([]ListAlbumsAlphaWithArtistRow, error) {
|
|
rows, err := q.db.Query(ctx, listAlbumsAlphaWithArtist, arg.Limit, arg.Offset)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []ListAlbumsAlphaWithArtistRow
|
|
for rows.Next() {
|
|
var i ListAlbumsAlphaWithArtistRow
|
|
if err := rows.Scan(
|
|
&i.Album.ID,
|
|
&i.Album.Title,
|
|
&i.Album.SortTitle,
|
|
&i.Album.ArtistID,
|
|
&i.Album.ReleaseDate,
|
|
&i.Album.Mbid,
|
|
&i.Album.CoverArtPath,
|
|
&i.Album.CreatedAt,
|
|
&i.Album.UpdatedAt,
|
|
&i.Album.CoverArtSource,
|
|
&i.ArtistName,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const listAlbumsByArtist = `-- name: ListAlbumsByArtist :many
|
|
SELECT id, title, sort_title, artist_id, release_date, mbid, cover_art_path, created_at, updated_at, cover_art_source FROM albums WHERE artist_id = $1 ORDER BY release_date NULLS LAST, sort_title
|
|
`
|
|
|
|
func (q *Queries) ListAlbumsByArtist(ctx context.Context, artistID pgtype.UUID) ([]Album, error) {
|
|
rows, err := q.db.Query(ctx, listAlbumsByArtist, artistID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []Album
|
|
for rows.Next() {
|
|
var i Album
|
|
if err := rows.Scan(
|
|
&i.ID,
|
|
&i.Title,
|
|
&i.SortTitle,
|
|
&i.ArtistID,
|
|
&i.ReleaseDate,
|
|
&i.Mbid,
|
|
&i.CoverArtPath,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
&i.CoverArtSource,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const listAlbumsByGenre = `-- name: ListAlbumsByGenre :many
|
|
SELECT DISTINCT ON (albums.id) albums.id, albums.title, albums.sort_title, albums.artist_id, albums.release_date, albums.mbid, albums.cover_art_path, albums.created_at, albums.updated_at, albums.cover_art_source
|
|
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
|
|
`
|
|
|
|
type ListAlbumsByGenreParams struct {
|
|
Genre *string
|
|
Limit int32
|
|
Offset int32
|
|
}
|
|
|
|
// Album "belongs to" a genre if any of its tracks carry that genre.
|
|
func (q *Queries) ListAlbumsByGenre(ctx context.Context, arg ListAlbumsByGenreParams) ([]Album, error) {
|
|
rows, err := q.db.Query(ctx, listAlbumsByGenre, arg.Genre, arg.Limit, arg.Offset)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []Album
|
|
for rows.Next() {
|
|
var i Album
|
|
if err := rows.Scan(
|
|
&i.ID,
|
|
&i.Title,
|
|
&i.SortTitle,
|
|
&i.ArtistID,
|
|
&i.ReleaseDate,
|
|
&i.Mbid,
|
|
&i.CoverArtPath,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
&i.CoverArtSource,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const listAlbumsMissingMbidWithTrack = `-- name: ListAlbumsMissingMbidWithTrack :many
|
|
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
|
|
`
|
|
|
|
type ListAlbumsMissingMbidWithTrackRow struct {
|
|
AlbumID pgtype.UUID
|
|
ArtistID pgtype.UUID
|
|
Title string
|
|
TrackFilePath string
|
|
}
|
|
|
|
// 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.
|
|
func (q *Queries) ListAlbumsMissingMbidWithTrack(ctx context.Context, limit int32) ([]ListAlbumsMissingMbidWithTrackRow, error) {
|
|
rows, err := q.db.Query(ctx, listAlbumsMissingMbidWithTrack, limit)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []ListAlbumsMissingMbidWithTrackRow
|
|
for rows.Next() {
|
|
var i ListAlbumsMissingMbidWithTrackRow
|
|
if err := rows.Scan(
|
|
&i.AlbumID,
|
|
&i.ArtistID,
|
|
&i.Title,
|
|
&i.TrackFilePath,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const listAlbumsNewest = `-- name: ListAlbumsNewest :many
|
|
SELECT id, title, sort_title, artist_id, release_date, mbid, cover_art_path, created_at, updated_at, cover_art_source FROM albums ORDER BY created_at DESC LIMIT $1 OFFSET $2
|
|
`
|
|
|
|
type ListAlbumsNewestParams struct {
|
|
Limit int32
|
|
Offset int32
|
|
}
|
|
|
|
func (q *Queries) ListAlbumsNewest(ctx context.Context, arg ListAlbumsNewestParams) ([]Album, error) {
|
|
rows, err := q.db.Query(ctx, listAlbumsNewest, arg.Limit, arg.Offset)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []Album
|
|
for rows.Next() {
|
|
var i Album
|
|
if err := rows.Scan(
|
|
&i.ID,
|
|
&i.Title,
|
|
&i.SortTitle,
|
|
&i.ArtistID,
|
|
&i.ReleaseDate,
|
|
&i.Mbid,
|
|
&i.CoverArtPath,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
&i.CoverArtSource,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const listAlbumsRandom = `-- name: ListAlbumsRandom :many
|
|
SELECT id, title, sort_title, artist_id, release_date, mbid, cover_art_path, created_at, updated_at, cover_art_source FROM albums ORDER BY random() LIMIT $1
|
|
`
|
|
|
|
func (q *Queries) ListAlbumsRandom(ctx context.Context, limit int32) ([]Album, error) {
|
|
rows, err := q.db.Query(ctx, listAlbumsRandom, limit)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []Album
|
|
for rows.Next() {
|
|
var i Album
|
|
if err := rows.Scan(
|
|
&i.ID,
|
|
&i.Title,
|
|
&i.SortTitle,
|
|
&i.ArtistID,
|
|
&i.ReleaseDate,
|
|
&i.Mbid,
|
|
&i.CoverArtPath,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
&i.CoverArtSource,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const listRecentlyAddedAlbumsWithArtist = `-- name: ListRecentlyAddedAlbumsWithArtist :many
|
|
SELECT albums.id, albums.title, albums.sort_title, albums.artist_id, albums.release_date, albums.mbid, albums.cover_art_path, albums.created_at, albums.updated_at, albums.cover_art_source, 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
|
|
`
|
|
|
|
type ListRecentlyAddedAlbumsWithArtistRow struct {
|
|
Album Album
|
|
ArtistName string
|
|
}
|
|
|
|
// 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.
|
|
func (q *Queries) ListRecentlyAddedAlbumsWithArtist(ctx context.Context, limit int32) ([]ListRecentlyAddedAlbumsWithArtistRow, error) {
|
|
rows, err := q.db.Query(ctx, listRecentlyAddedAlbumsWithArtist, limit)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []ListRecentlyAddedAlbumsWithArtistRow
|
|
for rows.Next() {
|
|
var i ListRecentlyAddedAlbumsWithArtistRow
|
|
if err := rows.Scan(
|
|
&i.Album.ID,
|
|
&i.Album.Title,
|
|
&i.Album.SortTitle,
|
|
&i.Album.ArtistID,
|
|
&i.Album.ReleaseDate,
|
|
&i.Album.Mbid,
|
|
&i.Album.CoverArtPath,
|
|
&i.Album.CreatedAt,
|
|
&i.Album.UpdatedAt,
|
|
&i.Album.CoverArtSource,
|
|
&i.ArtistName,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const searchAlbums = `-- name: SearchAlbums :many
|
|
SELECT id, title, sort_title, artist_id, release_date, mbid, cover_art_path, created_at, updated_at, cover_art_source FROM albums
|
|
WHERE title ILIKE '%' || $1 || '%'
|
|
ORDER BY sort_title
|
|
LIMIT $2 OFFSET $3
|
|
`
|
|
|
|
type SearchAlbumsParams struct {
|
|
Column1 *string
|
|
Limit int32
|
|
Offset int32
|
|
}
|
|
|
|
func (q *Queries) SearchAlbums(ctx context.Context, arg SearchAlbumsParams) ([]Album, error) {
|
|
rows, err := q.db.Query(ctx, searchAlbums, arg.Column1, arg.Limit, arg.Offset)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []Album
|
|
for rows.Next() {
|
|
var i Album
|
|
if err := rows.Scan(
|
|
&i.ID,
|
|
&i.Title,
|
|
&i.SortTitle,
|
|
&i.ArtistID,
|
|
&i.ReleaseDate,
|
|
&i.Mbid,
|
|
&i.CoverArtPath,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
&i.CoverArtSource,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const setAlbumMbidIfNull = `-- name: SetAlbumMbidIfNull :exec
|
|
UPDATE albums
|
|
SET mbid = $2,
|
|
updated_at = now()
|
|
WHERE id = $1 AND mbid IS NULL
|
|
`
|
|
|
|
type SetAlbumMbidIfNullParams struct {
|
|
ID pgtype.UUID
|
|
Mbid *string
|
|
}
|
|
|
|
// M7 #379: heal MBID on existing album rows during rescan. Idempotent —
|
|
// only updates when the existing mbid is NULL.
|
|
func (q *Queries) SetAlbumMbidIfNull(ctx context.Context, arg SetAlbumMbidIfNullParams) error {
|
|
_, err := q.db.Exec(ctx, setAlbumMbidIfNull, arg.ID, arg.Mbid)
|
|
return err
|
|
}
|
|
|
|
const upsertAlbum = `-- 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 id, title, sort_title, artist_id, release_date, mbid, cover_art_path, created_at, updated_at, cover_art_source
|
|
`
|
|
|
|
type UpsertAlbumParams struct {
|
|
Title string
|
|
SortTitle string
|
|
ArtistID pgtype.UUID
|
|
ReleaseDate pgtype.Date
|
|
Mbid *string
|
|
CoverArtPath *string
|
|
}
|
|
|
|
func (q *Queries) UpsertAlbum(ctx context.Context, arg UpsertAlbumParams) (Album, error) {
|
|
row := q.db.QueryRow(ctx, upsertAlbum,
|
|
arg.Title,
|
|
arg.SortTitle,
|
|
arg.ArtistID,
|
|
arg.ReleaseDate,
|
|
arg.Mbid,
|
|
arg.CoverArtPath,
|
|
)
|
|
var i Album
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.Title,
|
|
&i.SortTitle,
|
|
&i.ArtistID,
|
|
&i.ReleaseDate,
|
|
&i.Mbid,
|
|
&i.CoverArtPath,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
&i.CoverArtSource,
|
|
)
|
|
return i, err
|
|
}
|