diff --git a/resources/metadata_sqlite.sql b/resources/metadata_sqlite.sql index aa7e04c2ad..356695b1a3 100644 --- a/resources/metadata_sqlite.sql +++ b/resources/metadata_sqlite.sql @@ -97,6 +97,7 @@ CREATE TABLE identifiers ( id INTEGER PRIMARY KEY, ); CREATE TABLE languages ( id INTEGER PRIMARY KEY, lang_code TEXT NOT NULL COLLATE NOCASE, + link TEXT NOT NULL DEFAULT '', UNIQUE(lang_code) ); CREATE TABLE library_id ( id INTEGER PRIMARY KEY, @@ -116,19 +117,23 @@ CREATE TABLE preferences(id INTEGER PRIMARY KEY, CREATE TABLE publishers ( id INTEGER PRIMARY KEY, name TEXT NOT NULL COLLATE NOCASE, sort TEXT COLLATE NOCASE, + link TEXT NOT NULL DEFAULT '', UNIQUE(name) ); CREATE TABLE ratings ( id INTEGER PRIMARY KEY, rating INTEGER CHECK(rating > -1 AND rating < 11), + link TEXT NOT NULL DEFAULT '', UNIQUE (rating) ); CREATE TABLE series ( id INTEGER PRIMARY KEY, name TEXT NOT NULL COLLATE NOCASE, sort TEXT COLLATE NOCASE, + link TEXT NOT NULL DEFAULT '', UNIQUE (name) ); CREATE TABLE tags ( id INTEGER PRIMARY KEY, name TEXT NOT NULL COLLATE NOCASE, + link TEXT NOT NULL DEFAULT '', UNIQUE (name) ); CREATE TABLE last_read_positions ( id INTEGER PRIMARY KEY, @@ -633,4 +638,4 @@ CREATE TRIGGER series_update_trg BEGIN UPDATE series SET sort=title_sort(NEW.name) WHERE id=NEW.id; END; -pragma user_version=25; +pragma user_version=26; diff --git a/src/calibre/db/cache.py b/src/calibre/db/cache.py index e5f600ed16..949ae5c4e0 100644 --- a/src/calibre/db/cache.py +++ b/src/calibre/db/cache.py @@ -2341,10 +2341,7 @@ class Cache: @read_api def has_link_map(self, field): - if field not in self.fields: - raise ValueError(f'Lookup name {field} is not a valid name') - table = self.fields[field].table - return hasattr(table, 'link_map') + return hasattr(getattr(self.fields.get(field), 'table', None), 'link_map') @read_api def get_link_map(self, for_field): @@ -2358,11 +2355,12 @@ class Cache: if for_field not in self.fields: raise ValueError(f'Lookup name {for_field} is not a valid name') table = self.fields[for_field].table - if not hasattr(table, 'link_map'): + lm = getattr(table, 'link_map', None) + if lm is None: raise ValueError(f"Lookup name {for_field} doesn't have a link map") lm = table.link_map vm = table.id_map - return dict({vm.get(fid, None):v for fid,v in lm.items() if v}) + return {vm.get(fid):v for fid,v in lm.items() if v} @read_api def get_all_link_maps_for_book(self, book_id): @@ -2381,21 +2379,22 @@ class Cache: If book 2's author is neither A nor B and has no tags, this method returns {} ''' - if book_id in self.link_maps_cache: - return self.link_maps_cache[book_id] + cached = self.link_maps_cache.get(book_id) + if cached is not None: + return cached links = {} def add_links_for_field(f): field_ids = frozenset(self.field_ids_for(f, book_id)) table = self.fields[f].table lm = table.link_map vm = table.id_map - d = dict({vm.get(fid, None):v for fid,v in lm.items() if v and fid in field_ids}) + d = {vm.get(fid):v for fid,v in lm.items() if v and fid in field_ids} if d: links[f] = d for field in ('authors', 'publisher', 'series', 'tags'): add_links_for_field(field) for field in self.field_metadata.custom_field_keys(include_composites=False): - if self.has_link_map(field): + if self._has_link_map(field): add_links_for_field(field) self.link_maps_cache[book_id] = links return links @@ -2415,8 +2414,8 @@ class Cache: ''' if field not in self.fields: raise ValueError(f'Lookup name {field} is not a valid name') - table = self.fields[field].table - if not hasattr(table, 'link_map'): + table = getattr(self.fields[field], 'table', None) + if table is None: raise ValueError(f"Lookup name {field} doesn't have a link map") # Clear the links for book cache as we don't know what will be affected self.link_maps_cache = {} diff --git a/src/calibre/db/schema_upgrades.py b/src/calibre/db/schema_upgrades.py index 8f7f5c5b6c..4c395a8d3f 100644 --- a/src/calibre/db/schema_upgrades.py +++ b/src/calibre/db/schema_upgrades.py @@ -810,14 +810,13 @@ CREATE TRIGGER fkc_annot_update } if data['normalized']: tn = 'custom_column_{}'.format(data['num']) - alters.append(f'ALTER TABLE {tn} ADD COLUMN link TEXT NOT NULL DEFAULT "";') + alters.append(f"ALTER TABLE {tn} ADD COLUMN link TEXT NOT NULL DEFAULT '';") - alters.append('ALTER TABLE publishers ADD COLUMN link TEXT NOT NULL DEFAULT "";') - alters.append('ALTER TABLE series ADD COLUMN link TEXT NOT NULL DEFAULT "";') - alters.append('ALTER TABLE tags ADD COLUMN link TEXT NOT NULL DEFAULT "";') + alters.append("ALTER TABLE publishers ADD COLUMN link TEXT NOT NULL DEFAULT '';") + alters.append("ALTER TABLE series ADD COLUMN link TEXT NOT NULL DEFAULT '';") + alters.append("ALTER TABLE tags ADD COLUMN link TEXT NOT NULL DEFAULT '';") # These aren't necessary in that there is no UI to set links, but having them # makes the code uniform - alters.append('ALTER TABLE languages ADD COLUMN link TEXT NOT NULL DEFAULT "";') - alters.append('ALTER TABLE ratings ADD COLUMN link TEXT NOT NULL DEFAULT "";') + alters.append("ALTER TABLE languages ADD COLUMN link TEXT NOT NULL DEFAULT '';") + alters.append("ALTER TABLE ratings ADD COLUMN link TEXT NOT NULL DEFAULT '';") self.db.execute('\n'.join(alters)) - diff --git a/src/calibre/db/tables.py b/src/calibre/db/tables.py index 18658a3401..47e7c66f20 100644 --- a/src/calibre/db/tables.py +++ b/src/calibre/db/tables.py @@ -16,6 +16,9 @@ from calibre_extensions.speedup import parse_date as _c_speedup from polyglot.builtins import iteritems, itervalues +def identity(x): + return x + def c_parse(val): try: year, month, day, hour, minutes, seconds, tzsecs = _c_speedup(val) @@ -208,10 +211,7 @@ class ManyToOneTable(Table): def read_id_maps(self, db): query = db.execute('SELECT id, {}, link FROM {}'.format( self.metadata['column'], self.metadata['table'])) - if self.unserialize is None: - us = lambda x: x - else: - us = self.unserialize + us = identity if self.unserialize is None else self.unserialize for id_, val, link in query: self.id_map[id_] = us(val) self.link_map[id_] = link @@ -347,11 +347,12 @@ class ManyToOneTable(Table): return affected_books, new_id def set_links(self, link_map, db): - link_map = {id_:(l or '').strip() for id_, l in iteritems(link_map)} - link_map = {id_:l for id_, l in iteritems(link_map) if l != self.link_map.get(id_)} - self.link_map.update(link_map) - db.executemany(f'UPDATE {self.metadata["table"]} SET link=? WHERE id=?', - [(v, k) for k, v in iteritems(link_map)]) + link_map = {id_:(l or '').strip() for id_, l in link_map.items()} + link_map = {id_:l for id_, l in link_map.items() if l != self.link_map.get(id_)} + if link_map: + self.link_map.update(link_map) + db.executemany(f'UPDATE {self.metadata["table"]} SET link=? WHERE id=?', + tuple((v, k) for k, v in link_map.items())) return link_map diff --git a/src/calibre/db/tests/metadata.db b/src/calibre/db/tests/metadata.db index 57ae6ab43f..9e77a45f48 100644 Binary files a/src/calibre/db/tests/metadata.db and b/src/calibre/db/tests/metadata.db differ