mirror of
https://github.com/navidrome/navidrome.git
synced 2026-10-18 07:30:29 +00:00
fix(persistence): keep the new item's annotation when reassigning onto an item the user already annotated
ReassignAnnotation was a plain UPDATE; the annotation table is unique on (user_id, item_id, item_type), so when a user had annotated both items the statement aborted and none of the rows moved. In the scanner that surfaced as a warning; in the missing-file remap it rolled back the whole operation. UPDATE OR IGNORE moves what it can and leaves the conflicting rows for GC.
This commit is contained in:
parent
ed5fcd83fe
commit
b4addc59c7
@ -185,10 +185,9 @@ func (r sqlRepository) ReassignAnnotation(prevID string, newID string) error {
|
||||
if prevID == newID || prevID == "" || newID == "" {
|
||||
return nil
|
||||
}
|
||||
upd := Update(annotationTable).Where(And{
|
||||
Eq{annotationTable + ".item_type": r.tableName},
|
||||
Eq{annotationTable + ".item_id": prevID},
|
||||
}).Set("item_id", newID)
|
||||
// OR IGNORE keeps newID's own row where a user annotated both, instead of aborting the whole statement
|
||||
upd := Expr("update or ignore "+annotationTable+" set item_id = ? where item_type = ? and item_id = ?",
|
||||
newID, r.tableName, prevID)
|
||||
_, err := r.executeSQL(upd)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -30,6 +30,43 @@ var _ = Describe("Annotation Filters", func() {
|
||||
_, _ = albumRepo.executeSQL(squirrel.Delete("album").Where(squirrel.Eq{"id": albumWithoutAnnotation.ID}))
|
||||
})
|
||||
|
||||
Describe("ReassignAnnotation", func() {
|
||||
var prev, next model.Album
|
||||
|
||||
BeforeEach(func() {
|
||||
prev = model.Album{ID: "reassign-prev", Name: "Prev", LibraryID: 1}
|
||||
next = model.Album{ID: "reassign-next", Name: "Next", LibraryID: 1}
|
||||
Expect(albumRepo.Put(&prev)).To(Succeed())
|
||||
Expect(albumRepo.Put(&next)).To(Succeed())
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
_, _ = albumRepo.executeSQL(squirrel.Delete("annotation").Where(squirrel.Eq{"item_id": []string{prev.ID, next.ID}}))
|
||||
_, _ = albumRepo.executeSQL(squirrel.Delete("album").Where(squirrel.Eq{"id": []string{prev.ID, next.ID}}))
|
||||
})
|
||||
|
||||
It("moves the annotation when the new item has none", func() {
|
||||
Expect(albumRepo.SetRating(4, prev.ID)).To(Succeed())
|
||||
|
||||
Expect(albumRepo.ReassignAnnotation(prev.ID, next.ID)).To(Succeed())
|
||||
|
||||
got, err := albumRepo.Get(next.ID)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(got.Rating).To(Equal(4))
|
||||
})
|
||||
|
||||
It("keeps the new item's annotation when both exist", func() {
|
||||
Expect(albumRepo.SetRating(4, prev.ID)).To(Succeed())
|
||||
Expect(albumRepo.SetRating(2, next.ID)).To(Succeed())
|
||||
|
||||
Expect(albumRepo.ReassignAnnotation(prev.ID, next.ID)).To(Succeed())
|
||||
|
||||
got, err := albumRepo.Get(next.ID)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(got.Rating).To(Equal(2))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("annotationBoolFilter", func() {
|
||||
DescribeTable("creates correct SQL expressions",
|
||||
func(field, value string, expectedSQL string, expectedArgs []any) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user