Rewrites the internal/db/queries comments to rule 32 and regenerates dbq, whose doc comments and embedded SQL carry them. The bugs behind the tiered mixes, the md5 order seed, the score bands and the round-robin arms are restated as the constraint each one enforces; counts from one library and stale cross-references are gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
529 lines
14 KiB
Go
529 lines
14 KiB
Go
// Code generated by sqlc. DO NOT EDIT.
|
|
// versions:
|
|
// sqlc v1.31.1
|
|
// source: requests.sql
|
|
|
|
package dbq
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/jackc/pgx/v5/pgtype"
|
|
)
|
|
|
|
const approveRequest = `-- name: ApproveRequest :one
|
|
UPDATE requests
|
|
SET status = 'approved',
|
|
decided_at = now(),
|
|
decided_by = $2,
|
|
updated_at = now()
|
|
WHERE id = $1 AND status = 'pending'
|
|
RETURNING id, user_id, status, kind, artist_mbid, album_mbid, track_mbid, artist_name, album_title, track_title, decided_at, decided_by, notes, completed_at, matched_track_id, matched_album_id, matched_artist_id, requested_at, updated_at
|
|
`
|
|
|
|
type ApproveRequestParams struct {
|
|
ID pgtype.UUID
|
|
DecidedBy pgtype.UUID
|
|
}
|
|
|
|
func (q *Queries) ApproveRequest(ctx context.Context, arg ApproveRequestParams) (Request, error) {
|
|
row := q.db.QueryRow(ctx, approveRequest, arg.ID, arg.DecidedBy)
|
|
var i Request
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.UserID,
|
|
&i.Status,
|
|
&i.Kind,
|
|
&i.ArtistMbid,
|
|
&i.AlbumMbid,
|
|
&i.TrackMbid,
|
|
&i.ArtistName,
|
|
&i.AlbumTitle,
|
|
&i.TrackTitle,
|
|
&i.DecidedAt,
|
|
&i.DecidedBy,
|
|
&i.Notes,
|
|
&i.CompletedAt,
|
|
&i.MatchedTrackID,
|
|
&i.MatchedAlbumID,
|
|
&i.MatchedArtistID,
|
|
&i.RequestedAt,
|
|
&i.UpdatedAt,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const cancelRequest = `-- name: CancelRequest :one
|
|
UPDATE 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 id, user_id, status, kind, artist_mbid, album_mbid, track_mbid, artist_name, album_title, track_title, decided_at, decided_by, notes, completed_at, matched_track_id, matched_album_id, matched_artist_id, requested_at, updated_at
|
|
`
|
|
|
|
type CancelRequestParams struct {
|
|
ID pgtype.UUID
|
|
DecidedBy pgtype.UUID
|
|
}
|
|
|
|
// $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.
|
|
func (q *Queries) CancelRequest(ctx context.Context, arg CancelRequestParams) (Request, error) {
|
|
row := q.db.QueryRow(ctx, cancelRequest, arg.ID, arg.DecidedBy)
|
|
var i Request
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.UserID,
|
|
&i.Status,
|
|
&i.Kind,
|
|
&i.ArtistMbid,
|
|
&i.AlbumMbid,
|
|
&i.TrackMbid,
|
|
&i.ArtistName,
|
|
&i.AlbumTitle,
|
|
&i.TrackTitle,
|
|
&i.DecidedAt,
|
|
&i.DecidedBy,
|
|
&i.Notes,
|
|
&i.CompletedAt,
|
|
&i.MatchedTrackID,
|
|
&i.MatchedAlbumID,
|
|
&i.MatchedArtistID,
|
|
&i.RequestedAt,
|
|
&i.UpdatedAt,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const completeRequest = `-- name: CompleteRequest :one
|
|
UPDATE 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 id, user_id, status, kind, artist_mbid, album_mbid, track_mbid, artist_name, album_title, track_title, decided_at, decided_by, notes, completed_at, matched_track_id, matched_album_id, matched_artist_id, requested_at, updated_at
|
|
`
|
|
|
|
type CompleteRequestParams struct {
|
|
ID pgtype.UUID
|
|
MatchedTrackID pgtype.UUID
|
|
MatchedAlbumID pgtype.UUID
|
|
MatchedArtistID pgtype.UUID
|
|
}
|
|
|
|
// 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.
|
|
func (q *Queries) CompleteRequest(ctx context.Context, arg CompleteRequestParams) (Request, error) {
|
|
row := q.db.QueryRow(ctx, completeRequest,
|
|
arg.ID,
|
|
arg.MatchedTrackID,
|
|
arg.MatchedAlbumID,
|
|
arg.MatchedArtistID,
|
|
)
|
|
var i Request
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.UserID,
|
|
&i.Status,
|
|
&i.Kind,
|
|
&i.ArtistMbid,
|
|
&i.AlbumMbid,
|
|
&i.TrackMbid,
|
|
&i.ArtistName,
|
|
&i.AlbumTitle,
|
|
&i.TrackTitle,
|
|
&i.DecidedAt,
|
|
&i.DecidedBy,
|
|
&i.Notes,
|
|
&i.CompletedAt,
|
|
&i.MatchedTrackID,
|
|
&i.MatchedAlbumID,
|
|
&i.MatchedArtistID,
|
|
&i.RequestedAt,
|
|
&i.UpdatedAt,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const createRequest = `-- name: CreateRequest :one
|
|
INSERT INTO requests (
|
|
user_id, kind,
|
|
artist_mbid, album_mbid, track_mbid,
|
|
artist_name, album_title, track_title
|
|
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
|
RETURNING id, user_id, status, kind, artist_mbid, album_mbid, track_mbid, artist_name, album_title, track_title, decided_at, decided_by, notes, completed_at, matched_track_id, matched_album_id, matched_artist_id, requested_at, updated_at
|
|
`
|
|
|
|
type CreateRequestParams struct {
|
|
UserID pgtype.UUID
|
|
Kind RequestKind
|
|
ArtistMbid string
|
|
AlbumMbid *string
|
|
TrackMbid *string
|
|
ArtistName string
|
|
AlbumTitle *string
|
|
TrackTitle *string
|
|
}
|
|
|
|
func (q *Queries) CreateRequest(ctx context.Context, arg CreateRequestParams) (Request, error) {
|
|
row := q.db.QueryRow(ctx, createRequest,
|
|
arg.UserID,
|
|
arg.Kind,
|
|
arg.ArtistMbid,
|
|
arg.AlbumMbid,
|
|
arg.TrackMbid,
|
|
arg.ArtistName,
|
|
arg.AlbumTitle,
|
|
arg.TrackTitle,
|
|
)
|
|
var i Request
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.UserID,
|
|
&i.Status,
|
|
&i.Kind,
|
|
&i.ArtistMbid,
|
|
&i.AlbumMbid,
|
|
&i.TrackMbid,
|
|
&i.ArtistName,
|
|
&i.AlbumTitle,
|
|
&i.TrackTitle,
|
|
&i.DecidedAt,
|
|
&i.DecidedBy,
|
|
&i.Notes,
|
|
&i.CompletedAt,
|
|
&i.MatchedTrackID,
|
|
&i.MatchedAlbumID,
|
|
&i.MatchedArtistID,
|
|
&i.RequestedAt,
|
|
&i.UpdatedAt,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const getNonTerminalRequestForMBID = `-- name: GetNonTerminalRequestForMBID :one
|
|
SELECT id, user_id, status, kind, artist_mbid, album_mbid, track_mbid, artist_name, album_title, track_title, decided_at, decided_by, notes, completed_at, matched_track_id, matched_album_id, matched_artist_id, requested_at, updated_at FROM requests
|
|
WHERE status IN ('pending', 'approved', 'completed')
|
|
AND ((kind = 'artist' AND artist_mbid = $1)
|
|
OR (kind = 'album' AND album_mbid = $1)
|
|
OR (kind = 'track' AND track_mbid = $1))
|
|
ORDER BY requested_at ASC
|
|
LIMIT 1
|
|
`
|
|
|
|
// 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.
|
|
func (q *Queries) GetNonTerminalRequestForMBID(ctx context.Context, mbid string) (Request, error) {
|
|
row := q.db.QueryRow(ctx, getNonTerminalRequestForMBID, mbid)
|
|
var i Request
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.UserID,
|
|
&i.Status,
|
|
&i.Kind,
|
|
&i.ArtistMbid,
|
|
&i.AlbumMbid,
|
|
&i.TrackMbid,
|
|
&i.ArtistName,
|
|
&i.AlbumTitle,
|
|
&i.TrackTitle,
|
|
&i.DecidedAt,
|
|
&i.DecidedBy,
|
|
&i.Notes,
|
|
&i.CompletedAt,
|
|
&i.MatchedTrackID,
|
|
&i.MatchedAlbumID,
|
|
&i.MatchedArtistID,
|
|
&i.RequestedAt,
|
|
&i.UpdatedAt,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const getRequestByID = `-- name: GetRequestByID :one
|
|
SELECT id, user_id, status, kind, artist_mbid, album_mbid, track_mbid, artist_name, album_title, track_title, decided_at, decided_by, notes, completed_at, matched_track_id, matched_album_id, matched_artist_id, requested_at, updated_at FROM requests WHERE id = $1
|
|
`
|
|
|
|
func (q *Queries) GetRequestByID(ctx context.Context, id pgtype.UUID) (Request, error) {
|
|
row := q.db.QueryRow(ctx, getRequestByID, id)
|
|
var i Request
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.UserID,
|
|
&i.Status,
|
|
&i.Kind,
|
|
&i.ArtistMbid,
|
|
&i.AlbumMbid,
|
|
&i.TrackMbid,
|
|
&i.ArtistName,
|
|
&i.AlbumTitle,
|
|
&i.TrackTitle,
|
|
&i.DecidedAt,
|
|
&i.DecidedBy,
|
|
&i.Notes,
|
|
&i.CompletedAt,
|
|
&i.MatchedTrackID,
|
|
&i.MatchedAlbumID,
|
|
&i.MatchedArtistID,
|
|
&i.RequestedAt,
|
|
&i.UpdatedAt,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const hasNonTerminalRequestForMBID = `-- name: HasNonTerminalRequestForMBID :one
|
|
SELECT (EXISTS (
|
|
SELECT 1 FROM requests r
|
|
WHERE r.status IN ('pending', 'approved', 'completed')
|
|
AND ((r.kind = 'artist' AND r.artist_mbid = $1)
|
|
OR (r.kind = 'album' AND r.album_mbid = $1)
|
|
OR (r.kind = 'track' AND r.track_mbid = $1))
|
|
) OR EXISTS (
|
|
SELECT 1 FROM wants w WHERE w.release_group_mbid = $1 AND w.fulfilled_at IS NULL
|
|
) OR EXISTS (
|
|
SELECT 1 FROM follows f WHERE f.artist_mbid = $1
|
|
))::bool AS requested
|
|
`
|
|
|
|
// Whether something is already on its way: a pending, approved or completed
|
|
// request at the given level, an open want for the release group, or a
|
|
// follow of the artist. Sets the `requested` flag on catalogue search.
|
|
// Terminal-status (rejected, failed) requests do not count. MBIDs are unique
|
|
// per entity type, so checking one id against every column cannot
|
|
// false-match.
|
|
func (q *Queries) HasNonTerminalRequestForMBID(ctx context.Context, mbid string) (bool, error) {
|
|
row := q.db.QueryRow(ctx, hasNonTerminalRequestForMBID, mbid)
|
|
var requested bool
|
|
err := row.Scan(&requested)
|
|
return requested, err
|
|
}
|
|
|
|
const listApprovedRequestsForReconcile = `-- name: ListApprovedRequestsForReconcile :many
|
|
SELECT id, user_id, status, kind, artist_mbid, album_mbid, track_mbid, artist_name, album_title, track_title, decided_at, decided_by, notes, completed_at, matched_track_id, matched_album_id, matched_artist_id, requested_at, updated_at FROM requests
|
|
WHERE status = 'approved'
|
|
ORDER BY decided_at ASC
|
|
LIMIT $1
|
|
`
|
|
|
|
func (q *Queries) ListApprovedRequestsForReconcile(ctx context.Context, limit int32) ([]Request, error) {
|
|
rows, err := q.db.Query(ctx, listApprovedRequestsForReconcile, limit)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []Request
|
|
for rows.Next() {
|
|
var i Request
|
|
if err := rows.Scan(
|
|
&i.ID,
|
|
&i.UserID,
|
|
&i.Status,
|
|
&i.Kind,
|
|
&i.ArtistMbid,
|
|
&i.AlbumMbid,
|
|
&i.TrackMbid,
|
|
&i.ArtistName,
|
|
&i.AlbumTitle,
|
|
&i.TrackTitle,
|
|
&i.DecidedAt,
|
|
&i.DecidedBy,
|
|
&i.Notes,
|
|
&i.CompletedAt,
|
|
&i.MatchedTrackID,
|
|
&i.MatchedAlbumID,
|
|
&i.MatchedArtistID,
|
|
&i.RequestedAt,
|
|
&i.UpdatedAt,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const listRequestsByStatus = `-- name: ListRequestsByStatus :many
|
|
SELECT id, user_id, status, kind, artist_mbid, album_mbid, track_mbid, artist_name, album_title, track_title, decided_at, decided_by, notes, completed_at, matched_track_id, matched_album_id, matched_artist_id, requested_at, updated_at FROM requests
|
|
WHERE status = $1
|
|
ORDER BY requested_at DESC
|
|
LIMIT $2
|
|
`
|
|
|
|
type ListRequestsByStatusParams struct {
|
|
Status RequestStatus
|
|
Limit int32
|
|
}
|
|
|
|
func (q *Queries) ListRequestsByStatus(ctx context.Context, arg ListRequestsByStatusParams) ([]Request, error) {
|
|
rows, err := q.db.Query(ctx, listRequestsByStatus, arg.Status, arg.Limit)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []Request
|
|
for rows.Next() {
|
|
var i Request
|
|
if err := rows.Scan(
|
|
&i.ID,
|
|
&i.UserID,
|
|
&i.Status,
|
|
&i.Kind,
|
|
&i.ArtistMbid,
|
|
&i.AlbumMbid,
|
|
&i.TrackMbid,
|
|
&i.ArtistName,
|
|
&i.AlbumTitle,
|
|
&i.TrackTitle,
|
|
&i.DecidedAt,
|
|
&i.DecidedBy,
|
|
&i.Notes,
|
|
&i.CompletedAt,
|
|
&i.MatchedTrackID,
|
|
&i.MatchedAlbumID,
|
|
&i.MatchedArtistID,
|
|
&i.RequestedAt,
|
|
&i.UpdatedAt,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const listRequestsForUser = `-- name: ListRequestsForUser :many
|
|
SELECT id, user_id, status, kind, artist_mbid, album_mbid, track_mbid, artist_name, album_title, track_title, decided_at, decided_by, notes, completed_at, matched_track_id, matched_album_id, matched_artist_id, requested_at, updated_at FROM requests
|
|
WHERE user_id = $1
|
|
ORDER BY requested_at DESC
|
|
LIMIT $2
|
|
`
|
|
|
|
type ListRequestsForUserParams struct {
|
|
UserID pgtype.UUID
|
|
Limit int32
|
|
}
|
|
|
|
func (q *Queries) ListRequestsForUser(ctx context.Context, arg ListRequestsForUserParams) ([]Request, error) {
|
|
rows, err := q.db.Query(ctx, listRequestsForUser, arg.UserID, arg.Limit)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []Request
|
|
for rows.Next() {
|
|
var i Request
|
|
if err := rows.Scan(
|
|
&i.ID,
|
|
&i.UserID,
|
|
&i.Status,
|
|
&i.Kind,
|
|
&i.ArtistMbid,
|
|
&i.AlbumMbid,
|
|
&i.TrackMbid,
|
|
&i.ArtistName,
|
|
&i.AlbumTitle,
|
|
&i.TrackTitle,
|
|
&i.DecidedAt,
|
|
&i.DecidedBy,
|
|
&i.Notes,
|
|
&i.CompletedAt,
|
|
&i.MatchedTrackID,
|
|
&i.MatchedAlbumID,
|
|
&i.MatchedArtistID,
|
|
&i.RequestedAt,
|
|
&i.UpdatedAt,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const rejectRequest = `-- name: RejectRequest :one
|
|
UPDATE requests
|
|
SET status = 'rejected',
|
|
notes = $2,
|
|
decided_at = now(),
|
|
decided_by = $3,
|
|
updated_at = now()
|
|
WHERE id = $1 AND status = 'pending'
|
|
RETURNING id, user_id, status, kind, artist_mbid, album_mbid, track_mbid, artist_name, album_title, track_title, decided_at, decided_by, notes, completed_at, matched_track_id, matched_album_id, matched_artist_id, requested_at, updated_at
|
|
`
|
|
|
|
type RejectRequestParams struct {
|
|
ID pgtype.UUID
|
|
Notes *string
|
|
DecidedBy pgtype.UUID
|
|
}
|
|
|
|
func (q *Queries) RejectRequest(ctx context.Context, arg RejectRequestParams) (Request, error) {
|
|
row := q.db.QueryRow(ctx, rejectRequest, arg.ID, arg.Notes, arg.DecidedBy)
|
|
var i Request
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.UserID,
|
|
&i.Status,
|
|
&i.Kind,
|
|
&i.ArtistMbid,
|
|
&i.AlbumMbid,
|
|
&i.TrackMbid,
|
|
&i.ArtistName,
|
|
&i.AlbumTitle,
|
|
&i.TrackTitle,
|
|
&i.DecidedAt,
|
|
&i.DecidedBy,
|
|
&i.Notes,
|
|
&i.CompletedAt,
|
|
&i.MatchedTrackID,
|
|
&i.MatchedAlbumID,
|
|
&i.MatchedArtistID,
|
|
&i.RequestedAt,
|
|
&i.UpdatedAt,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const repointRequestsAlbumMbid = `-- name: RepointRequestsAlbumMbid :exec
|
|
UPDATE requests
|
|
SET album_mbid = $1,
|
|
updated_at = now()
|
|
WHERE album_mbid = $2
|
|
`
|
|
|
|
type RepointRequestsAlbumMbidParams struct {
|
|
NewMbid *string
|
|
OldMbid *string
|
|
}
|
|
|
|
// A want moved from a release id to its group takes the requests
|
|
// that name it along, so the reconciler finds it.
|
|
func (q *Queries) RepointRequestsAlbumMbid(ctx context.Context, arg RepointRequestsAlbumMbidParams) error {
|
|
_, err := q.db.Exec(ctx, repointRequestsAlbumMbid, arg.NewMbid, arg.OldMbid)
|
|
return err
|
|
}
|