From 602c3d22a55f32aafaa3c377a86de2e67246ee17 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 5 Aug 2013 21:43:29 +0530 Subject: [PATCH] newdb: Handle duplicate values when setting many-many fields Fixes an error when merging book records that have the same tags. --- src/calibre/db/tests/writing.py | 3 +++ src/calibre/db/write.py | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/src/calibre/db/tests/writing.py b/src/calibre/db/tests/writing.py index 4f2bacf921..96e2bb0c34 100644 --- a/src/calibre/db/tests/writing.py +++ b/src/calibre/db/tests/writing.py @@ -292,6 +292,9 @@ class WritingTest(BaseTest): ae(c.field_for('sort', 1), 'Moose, The') ae(c.field_for('sort', 2), 'Cat') + # Test setting with the same value repeated + ae(sf('tags', {3: ('a', 'b', 'a')}), {3}) + # }}} def test_dirtied(self): # {{{ diff --git a/src/calibre/db/write.py b/src/calibre/db/write.py index 58c642e59c..5f486445db 100644 --- a/src/calibre/db/write.py +++ b/src/calibre/db/write.py @@ -337,6 +337,14 @@ def many_one(book_id_val_map, db, field, allow_case_change, *args): # }}} # Many-Many fields {{{ + +def uniq(vals): + ' Remove all duplicates from vals, while preserving order. Items in vals must be hashable ' + vals = vals or () + seen = set() + seen_add = seen.add + return tuple(x for x in vals if x not in seen and not seen_add(x)) + def many_many(book_id_val_map, db, field, allow_case_change, *args): dirtied = set() m = field.metadata @@ -349,6 +357,7 @@ def many_many(book_id_val_map, db, field, allow_case_change, *args): rid_map = {kmap(item):item_id for item_id, item in table.id_map.iteritems()} val_map = {} case_changes = {} + book_id_val_map = {k:uniq(vals) for k, vals in book_id_val_map.iteritems()} for vals in book_id_val_map.itervalues(): for val in vals: get_db_id(val, db, m, table, kmap, rid_map, allow_case_change,