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>
334 lines
10 KiB
Go
334 lines
10 KiB
Go
// Code generated by sqlc. DO NOT EDIT.
|
|
// versions:
|
|
// sqlc v1.31.1
|
|
// source: merge.sql
|
|
|
|
package dbq
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/jackc/pgx/v5/pgtype"
|
|
)
|
|
|
|
const listDuplicateGroupMergeMembers = `-- name: ListDuplicateGroupMergeMembers :many
|
|
SELECT t.id, t.file_path, t.file_format, t.file_size, t.added_at, t.album_id
|
|
FROM duplicate_group_members m
|
|
JOIN tracks t ON t.id = m.track_id
|
|
WHERE m.group_id = $1
|
|
ORDER BY t.id
|
|
`
|
|
|
|
type ListDuplicateGroupMergeMembersRow struct {
|
|
ID pgtype.UUID
|
|
FilePath string
|
|
FileFormat string
|
|
FileSize int64
|
|
AddedAt pgtype.Timestamptz
|
|
AlbumID pgtype.UUID
|
|
}
|
|
|
|
func (q *Queries) ListDuplicateGroupMergeMembers(ctx context.Context, groupID pgtype.UUID) ([]ListDuplicateGroupMergeMembersRow, error) {
|
|
rows, err := q.db.Query(ctx, listDuplicateGroupMergeMembers, groupID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []ListDuplicateGroupMergeMembersRow
|
|
for rows.Next() {
|
|
var i ListDuplicateGroupMergeMembersRow
|
|
if err := rows.Scan(
|
|
&i.ID,
|
|
&i.FilePath,
|
|
&i.FileFormat,
|
|
&i.FileSize,
|
|
&i.AddedAt,
|
|
&i.AlbumID,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const lockDuplicateGroupForMerge = `-- name: LockDuplicateGroupForMerge :one
|
|
|
|
SELECT id, tier, status
|
|
FROM duplicate_groups
|
|
WHERE id = $1
|
|
FOR UPDATE
|
|
`
|
|
|
|
type LockDuplicateGroupForMergeRow struct {
|
|
ID pgtype.UUID
|
|
Tier string
|
|
Status string
|
|
}
|
|
|
|
// Duplicate merge. Every statement here runs inside the one
|
|
// transaction library.MergeDuplicateGroup opens, after the removed copy's file
|
|
// is already gone. The loser's own track row is deleted last with DeleteTrack;
|
|
// what these do is move everything it carries onto the survivor first, so that
|
|
// delete's CASCADE finds nothing left to destroy.
|
|
// Locks the group for the rest of the transaction, so two merges of one group
|
|
// cannot run at once.
|
|
func (q *Queries) LockDuplicateGroupForMerge(ctx context.Context, id pgtype.UUID) (LockDuplicateGroupForMergeRow, error) {
|
|
row := q.db.QueryRow(ctx, lockDuplicateGroupForMerge, id)
|
|
var i LockDuplicateGroupForMergeRow
|
|
err := row.Scan(&i.ID, &i.Tier, &i.Status)
|
|
return i, err
|
|
}
|
|
|
|
const markDuplicateGroupMerged = `-- name: MarkDuplicateGroupMerged :execrows
|
|
UPDATE duplicate_groups
|
|
SET status = 'merged', resolved_at = now()
|
|
WHERE id = $1 AND status = 'pending'
|
|
`
|
|
|
|
func (q *Queries) MarkDuplicateGroupMerged(ctx context.Context, id pgtype.UUID) (int64, error) {
|
|
result, err := q.db.Exec(ctx, markDuplicateGroupMerged, id)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return result.RowsAffected(), nil
|
|
}
|
|
|
|
const mergeCopyGeneralLikes = `-- name: MergeCopyGeneralLikes :many
|
|
|
|
INSERT INTO general_likes (user_id, track_id, liked_at)
|
|
SELECT user_id, $1::uuid, liked_at
|
|
FROM general_likes
|
|
WHERE track_id = $2::uuid
|
|
ON CONFLICT (user_id, track_id) DO UPDATE
|
|
SET liked_at = LEAST(general_likes.liked_at, EXCLUDED.liked_at)
|
|
RETURNING user_id
|
|
`
|
|
|
|
type MergeCopyGeneralLikesParams struct {
|
|
SurvivorID pgtype.UUID
|
|
LoserID pgtype.UUID
|
|
}
|
|
|
|
// Collision-safe merges: a unique key includes track_id, so the survivor may
|
|
// already hold a matching row. Copy what it lacks; DeleteTrack's CASCADE then
|
|
// removes the loser's originals.
|
|
// One like per user. A user who liked both copies keeps a single like, dated to
|
|
// the earlier of the two.
|
|
func (q *Queries) MergeCopyGeneralLikes(ctx context.Context, arg MergeCopyGeneralLikesParams) ([]pgtype.UUID, error) {
|
|
rows, err := q.db.Query(ctx, mergeCopyGeneralLikes, arg.SurvivorID, arg.LoserID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []pgtype.UUID
|
|
for rows.Next() {
|
|
var user_id pgtype.UUID
|
|
if err := rows.Scan(&user_id); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, user_id)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const mergeCopyTrackSimilarity = `-- name: MergeCopyTrackSimilarity :execrows
|
|
INSERT INTO track_similarity (track_a_id, track_b_id, score, source, fetched_at)
|
|
SELECT CASE WHEN track_a_id = $1::uuid THEN $2::uuid ELSE track_a_id END,
|
|
CASE WHEN track_b_id = $1::uuid THEN $2::uuid ELSE track_b_id END,
|
|
score, source, fetched_at
|
|
FROM track_similarity
|
|
WHERE (track_a_id = $1::uuid OR track_b_id = $1::uuid)
|
|
AND (CASE WHEN track_a_id = $1::uuid THEN $2::uuid ELSE track_a_id END)
|
|
<> (CASE WHEN track_b_id = $1::uuid THEN $2::uuid ELSE track_b_id END)
|
|
ON CONFLICT (track_a_id, track_b_id, source) DO NOTHING
|
|
`
|
|
|
|
type MergeCopyTrackSimilarityParams struct {
|
|
LoserID pgtype.UUID
|
|
SurvivorID pgtype.UUID
|
|
}
|
|
|
|
// Rewrites the loser to the survivor on either side of an edge. An edge between
|
|
// the two copies would become a track similar to itself — the table forbids
|
|
// that, and it means nothing — so it is dropped. An edge the survivor already
|
|
// has from the same source is kept as it is.
|
|
func (q *Queries) MergeCopyTrackSimilarity(ctx context.Context, arg MergeCopyTrackSimilarityParams) (int64, error) {
|
|
result, err := q.db.Exec(ctx, mergeCopyTrackSimilarity, arg.LoserID, arg.SurvivorID)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return result.RowsAffected(), nil
|
|
}
|
|
|
|
const mergeCopyTrackTags = `-- name: MergeCopyTrackTags :execrows
|
|
INSERT INTO track_tags (track_id, tag, weight)
|
|
SELECT $1::uuid, tag, weight
|
|
FROM track_tags
|
|
WHERE track_id = $2::uuid
|
|
ON CONFLICT (track_id, tag) DO NOTHING
|
|
`
|
|
|
|
type MergeCopyTrackTagsParams struct {
|
|
SurvivorID pgtype.UUID
|
|
LoserID pgtype.UUID
|
|
}
|
|
|
|
func (q *Queries) MergeCopyTrackTags(ctx context.Context, arg MergeCopyTrackTagsParams) (int64, error) {
|
|
result, err := q.db.Exec(ctx, mergeCopyTrackTags, arg.SurvivorID, arg.LoserID)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return result.RowsAffected(), nil
|
|
}
|
|
|
|
const mergeInheritTrackMbid = `-- name: MergeInheritTrackMbid :exec
|
|
UPDATE tracks AS survivor
|
|
SET mbid = loser.mbid, mbid_source = loser.mbid_source
|
|
FROM tracks AS loser
|
|
WHERE survivor.id = $1::uuid
|
|
AND loser.id = $2::uuid
|
|
AND survivor.mbid IS NULL
|
|
AND loser.mbid IS NOT NULL
|
|
`
|
|
|
|
type MergeInheritTrackMbidParams struct {
|
|
SurvivorID pgtype.UUID
|
|
LoserID pgtype.UUID
|
|
}
|
|
|
|
// A recording MBID is what the similarity pipeline keys on. If only the removed
|
|
// copy carried one, the survivor takes it rather than going dark to similarity.
|
|
func (q *Queries) MergeInheritTrackMbid(ctx context.Context, arg MergeInheritTrackMbidParams) error {
|
|
_, err := q.db.Exec(ctx, mergeInheritTrackMbid, arg.SurvivorID, arg.LoserID)
|
|
return err
|
|
}
|
|
|
|
const mergeRepointContextualLikes = `-- name: MergeRepointContextualLikes :execrows
|
|
UPDATE contextual_likes SET track_id = $1::uuid WHERE track_id = $2::uuid
|
|
`
|
|
|
|
type MergeRepointContextualLikesParams struct {
|
|
SurvivorID pgtype.UUID
|
|
LoserID pgtype.UUID
|
|
}
|
|
|
|
func (q *Queries) MergeRepointContextualLikes(ctx context.Context, arg MergeRepointContextualLikesParams) (int64, error) {
|
|
result, err := q.db.Exec(ctx, mergeRepointContextualLikes, arg.SurvivorID, arg.LoserID)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return result.RowsAffected(), nil
|
|
}
|
|
|
|
const mergeRepointPlayEvents = `-- name: MergeRepointPlayEvents :execrows
|
|
|
|
UPDATE play_events SET track_id = $1::uuid WHERE track_id = $2::uuid
|
|
`
|
|
|
|
type MergeRepointPlayEventsParams struct {
|
|
SurvivorID pgtype.UUID
|
|
LoserID pgtype.UUID
|
|
}
|
|
|
|
// Plain repoints: no unique key involves track_id, so moving rows cannot collide.
|
|
func (q *Queries) MergeRepointPlayEvents(ctx context.Context, arg MergeRepointPlayEventsParams) (int64, error) {
|
|
result, err := q.db.Exec(ctx, mergeRepointPlayEvents, arg.SurvivorID, arg.LoserID)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return result.RowsAffected(), nil
|
|
}
|
|
|
|
const mergeRepointPlaybackErrors = `-- name: MergeRepointPlaybackErrors :execrows
|
|
UPDATE playback_errors SET track_id = $1::uuid WHERE track_id = $2::uuid
|
|
`
|
|
|
|
type MergeRepointPlaybackErrorsParams struct {
|
|
SurvivorID pgtype.UUID
|
|
LoserID pgtype.UUID
|
|
}
|
|
|
|
func (q *Queries) MergeRepointPlaybackErrors(ctx context.Context, arg MergeRepointPlaybackErrorsParams) (int64, error) {
|
|
result, err := q.db.Exec(ctx, mergeRepointPlaybackErrors, arg.SurvivorID, arg.LoserID)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return result.RowsAffected(), nil
|
|
}
|
|
|
|
const mergeRepointPlaylistTracks = `-- name: MergeRepointPlaylistTracks :many
|
|
UPDATE playlist_tracks SET track_id = $1::uuid
|
|
WHERE track_id = $2::uuid
|
|
RETURNING playlist_id
|
|
`
|
|
|
|
type MergeRepointPlaylistTracksParams struct {
|
|
SurvivorID pgtype.UUID
|
|
LoserID pgtype.UUID
|
|
}
|
|
|
|
// playlist_tracks is keyed by (playlist_id, position), so repointing keeps every
|
|
// entry exactly where it was. A playlist that held both copies simply holds the
|
|
// survivor twice — the user put two entries there, and both stay.
|
|
func (q *Queries) MergeRepointPlaylistTracks(ctx context.Context, arg MergeRepointPlaylistTracksParams) ([]pgtype.UUID, error) {
|
|
rows, err := q.db.Query(ctx, mergeRepointPlaylistTracks, arg.SurvivorID, arg.LoserID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []pgtype.UUID
|
|
for rows.Next() {
|
|
var playlist_id pgtype.UUID
|
|
if err := rows.Scan(&playlist_id); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, playlist_id)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const mergeRepointRequests = `-- name: MergeRepointRequests :execrows
|
|
UPDATE requests SET matched_track_id = $1::uuid
|
|
WHERE matched_track_id = $2::uuid
|
|
`
|
|
|
|
type MergeRepointRequestsParams struct {
|
|
SurvivorID pgtype.UUID
|
|
LoserID pgtype.UUID
|
|
}
|
|
|
|
func (q *Queries) MergeRepointRequests(ctx context.Context, arg MergeRepointRequestsParams) (int64, error) {
|
|
result, err := q.db.Exec(ctx, mergeRepointRequests, arg.SurvivorID, arg.LoserID)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return result.RowsAffected(), nil
|
|
}
|
|
|
|
const mergeRepointSkipEvents = `-- name: MergeRepointSkipEvents :execrows
|
|
UPDATE skip_events SET track_id = $1::uuid WHERE track_id = $2::uuid
|
|
`
|
|
|
|
type MergeRepointSkipEventsParams struct {
|
|
SurvivorID pgtype.UUID
|
|
LoserID pgtype.UUID
|
|
}
|
|
|
|
func (q *Queries) MergeRepointSkipEvents(ctx context.Context, arg MergeRepointSkipEventsParams) (int64, error) {
|
|
result, err := q.db.Exec(ctx, mergeRepointSkipEvents, arg.SurvivorID, arg.LoserID)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return result.RowsAffected(), nil
|
|
}
|