diff --git a/src/calibre/db/backend.py b/src/calibre/db/backend.py index 9ba5a3e348..5bba130b48 100644 --- a/src/calibre/db/backend.py +++ b/src/calibre/db/backend.py @@ -991,6 +991,9 @@ class DB: if note_id is not None: yield from self.notes.resources_used_by(conn, note_id) + def unretire_note(self, field, item_id, item_val): + return self.notes.unretire(self.conn, field, item_id, item_val) + def initialize_fts(self, dbref): self.fts = None if not self.prefs['fts_enabled']: diff --git a/src/calibre/db/notes/connect.py b/src/calibre/db/notes/connect.py index dc8958afd9..fddb2a7c6b 100644 --- a/src/calibre/db/notes/connect.py +++ b/src/calibre/db/notes/connect.py @@ -13,6 +13,7 @@ from typing import Optional, Union from calibre.constants import iswindows from calibre.utils.copy_files import WINDOWS_SLEEP_FOR_RETRY_TIME from calibre.utils.filenames import copyfile_using_links, make_long_path_useable +from calibre.utils.icu import lower as icu_lower from ..constants import NOTES_DIR_NAME from .schema_upgrade import SchemaUpgrade @@ -130,7 +131,7 @@ class Notes: def retire_entry(self, field_name, item_id, item_value, resources, note_id): path = make_long_path_useable(os.path.join(self.backup_dir, field_name, str(item_id))) if os.path.exists(path): - key = (item_value or '').lower() + key = icu_lower(item_value or '') destdir = os.path.join(self.retired_dir, hash_key(f'{field_name} {key}')) os.makedirs(make_long_path_useable(destdir), exist_ok=True) dest = os.path.join(destdir, DOC_NAME) @@ -145,7 +146,7 @@ class Notes: self.trim_retired_dir() def unretire(self, conn, field_name, item_id, item_value) -> int: - key = (item_value or '').lower() + key = icu_lower(item_value or '') srcdir = make_long_path_useable(os.path.join(self.retired_dir, hash_key(f'{field_name} {key}'))) note_id = -1 if not os.path.exists(srcdir) or self.note_id_for(conn, field_name, item_id) is not None: diff --git a/src/calibre/db/tests/notes.py b/src/calibre/db/tests/notes.py index 0b9e3c60fa..68be4dde47 100644 --- a/src/calibre/db/tests/notes.py +++ b/src/calibre/db/tests/notes.py @@ -79,8 +79,14 @@ def test_cache_api(self): # test removing author from db retires notes cache.set_field('authors', {bid:('New Author',) for bid in cache.all_book_ids()}) self.ae(len(cache.all_field_ids('authors')), 1) - before = os.listdir(notes.retired_dir) - self.ae(len(before), 1) + self.ae(len(os.listdir(notes.retired_dir)), 1) + # test re-using of retired note + cache.set_field('authors', {1:'Author One'}) + author_id = cache.get_item_id('authors', 'Author One') + self.ae(cache.notes_resources_used_by('authors', author_id), frozenset({h1, h2})) + self.ae(cache.get_notes_resource(h1)['data'], b'resource1') + self.ae(cache.get_notes_resource(h2)['data'], b'resource2') + self.assertFalse(os.listdir(notes.retired_dir)) class NotesTest(BaseTest): diff --git a/src/calibre/db/write.py b/src/calibre/db/write.py index 27bf6879fc..d0442caf53 100644 --- a/src/calibre/db/write.py +++ b/src/calibre/db/write.py @@ -297,6 +297,8 @@ def get_db_id(val, db, m, table, kmap, rid_map, allow_case_change, table.asort_map[item_id] = aus if hasattr(table, 'link_map'): table.link_map[item_id] = '' + if table.supports_notes and isinstance(val, str): + db.unretire_note(table.name, item_id, val) elif allow_case_change and val != table.id_map[item_id]: case_changes[item_id] = val val_map[val] = item_id @@ -380,7 +382,8 @@ def many_one(book_id_val_map, db, field, allow_case_change, *args): # Remove no longer used items remove = {item_id:item_val for item_id, item_val in table.id_map.items() if not table.col_book_map.get(item_id, False)} if remove: - db.clear_notes_for_category_items(table.name, remove) + if table.supports_notes: + db.clear_notes_for_category_items(table.name, remove) db.executemany('DELETE FROM %s WHERE id=?'%m['table'], ((item_id,) for item_id in remove)) for item_id in remove: @@ -484,7 +487,8 @@ def many_many(book_id_val_map, db, field, allow_case_change, *args): # Remove no longer used items remove = {item_id:item_val for item_id, item_val in table.id_map.items() if not table.col_book_map.get(item_id, False)} if remove: - db.clear_notes_for_category_items(table.name, remove) + if table.supports_notes: + db.clear_notes_for_category_items(table.name, remove) db.executemany('DELETE FROM %s WHERE id=?'%m['table'], ((item_id,) for item_id in remove)) for item_id in remove: