From bff37a80545d5857789158d448694aa39eacf446 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 22 Aug 2023 12:28:13 +0530 Subject: [PATCH] Delete notes when deleting a custom column Also change the data dir name --- src/calibre/db/backend.py | 7 ++++--- src/calibre/db/constants.py | 2 +- src/calibre/db/notes/connect.py | 22 +++++++++++++++++++--- src/calibre/db/tests/notes.py | 13 +++++++++++++ 4 files changed, 37 insertions(+), 7 deletions(-) diff --git a/src/calibre/db/backend.py b/src/calibre/db/backend.py index d1b5ba3704..77e5004d6d 100644 --- a/src/calibre/db/backend.py +++ b/src/calibre/db/backend.py @@ -680,11 +680,11 @@ class DB: def initialize_custom_columns(self): # {{{ self.custom_columns_deleted = False + self.deleted_fields = [] with self.conn: # Delete previously marked custom columns - for record in self.conn.get( - 'SELECT id FROM custom_columns WHERE mark_for_delete=1'): - num = record[0] + for (num, label) in self.conn.get( + 'SELECT id,label FROM custom_columns WHERE mark_for_delete=1'): table, lt = self.custom_table_names(num) self.execute('''\ DROP INDEX IF EXISTS {table}_idx; @@ -703,6 +703,7 @@ class DB: '''.format(table=table, lt=lt) ) self.prefs.set('update_all_last_mod_dates_on_start', True) + self.deleted_fields.append('#'+label) self.execute('DELETE FROM custom_columns WHERE mark_for_delete=1') # Load metadata for custom columns diff --git a/src/calibre/db/constants.py b/src/calibre/db/constants.py index 07cda782a8..16d3189d3c 100644 --- a/src/calibre/db/constants.py +++ b/src/calibre/db/constants.py @@ -8,7 +8,7 @@ COVER_FILE_NAME = 'cover.jpg' METADATA_FILE_NAME = 'metadata.opf' DEFAULT_TRASH_EXPIRY_TIME_SECONDS = 14 * 86400 TRASH_DIR_NAME = '.caltrash' -NOTES_DIR_NAME = '.notes' +NOTES_DIR_NAME = '.calnotes' DATA_DIR_NAME = 'data' DATA_FILE_PATTERN = f'{DATA_DIR_NAME}/**/*' BOOK_ID_PATH_TEMPLATE = ' ({})' diff --git a/src/calibre/db/notes/connect.py b/src/calibre/db/notes/connect.py index ba51550f3a..0d63170e01 100644 --- a/src/calibre/db/notes/connect.py +++ b/src/calibre/db/notes/connect.py @@ -87,6 +87,22 @@ class Notes: ) SchemaUpgrade(conn, '\n'.join(triggers)) conn.notes_dbpath = dbpath + for cat in backend.deleted_fields: + self.delete_field(conn, cat) + + def delete_field(self, conn, field_name): + note_ids = conn.get('SELECT id from notes_db.notes WHERE colname=?', (field_name,)) + if note_ids: + conn.execute( + 'DELETE FROM notes_db.notes_resources_link WHERE note IN (SELECT id FROM notes_db.notes WHERE colname=?)', (field_name,) + ) + conn.execute('DELETE FROM notes_db.notes WHERE colname=?', (field_name,)) + self.remove_unreferenced_resources(conn) + + def remove_unreferenced_resources(self, conn): + for (rhash,) in conn.get('SELECT hash FROM notes_db.resources WHERE id NOT IN (SELECT resource FROM notes_db.notes_resources_link)'): + remove_with_retry(self.path_for_resource(conn, rhash)) + conn.execute('DELETE FROM notes_db.resources WHERE id NOT IN (SELECT resource FROM notes_db.notes_resources_link)') def path_for_resource(self, conn, resource_hash_or_resource_id: Union[str,int]) -> str: if isinstance(resource_hash_or_resource_id, str): @@ -162,7 +178,7 @@ class Notes: if x.startswith('res-'): rname = x.split('-', 1)[1] with open(os.path.join(srcdir, x), 'rb') as rsrc: - resources.add(self.add_resource(conn, rsrc, rname)) + resources.add(self.add_resource(conn, rsrc, rname, update_name=False)) note_id = self.set_note(conn, field_name, item_id, item_value, marked_up_text, resources, searchable_text) if note_id > -1: remove_with_retry(srcdir, is_dir=True) @@ -242,7 +258,7 @@ class Notes: for path in items[:extra]: remove_with_retry(path, is_dir=True) - def add_resource(self, conn, path_or_stream_or_data, name): + def add_resource(self, conn, path_or_stream_or_data, name, update_name=True): if isinstance(path_or_stream_or_data, bytes): data = path_or_stream_or_data elif isinstance(path_or_stream_or_data, str): @@ -271,7 +287,7 @@ class Notes: base_name, ext = os.path.splitext(name) c = 0 for (resource_id, existing_name) in conn.execute('SELECT id,name FROM notes_db.resources WHERE hash=?', (resource_hash,)): - if existing_name != name: + if existing_name != name and update_name: while True: try: conn.execute('UPDATE notes_db.resources SET name=? WHERE id=?', (name, resource_id)) diff --git a/src/calibre/db/tests/notes.py b/src/calibre/db/tests/notes.py index bca74657f4..94c9fb1ccf 100644 --- a/src/calibre/db/tests/notes.py +++ b/src/calibre/db/tests/notes.py @@ -87,6 +87,19 @@ def test_cache_api(self: 'NotesTest'): 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)) + # test delete custom column with notes + tags = cache.field_for('#tags', 1) + tag_id = cache.get_item_id('#tags', tags[0]) + h1 = cache.add_notes_resource(b'resource1t', 'r1.jpg') + h2 = cache.add_notes_resource(b'resource2t', 'r1.jpg') + cache.set_notes_for('#tags', tag_id, doc, resource_ids=(h1, h2)) + self.ae(cache.notes_for('#tags', tag_id), doc) + cache.delete_custom_column('tags') + cache.close() + cache = self.init_cache(cache.backend.library_path) + self.ae(cache.notes_for('#tags', tag_id), '') + self.assertIsNone(cache.get_notes_resource(h1)) + self.assertIsNone(cache.get_notes_resource(h2)) def test_fts(self: 'NotesTest'):