From aabd29c571f8603b083863d6217001bc4647382a Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 30 Mar 2023 08:37:17 +0530 Subject: [PATCH] Clean up DB code from previous PR Note that string literals in SQLITE should be single quoted. https://sqlite.org/quirks.html#dblquote --- resources/metadata_sqlite.sql | 7 ++++++- src/calibre/db/cache.py | 23 +++++++++++------------ src/calibre/db/schema_upgrades.py | 13 ++++++------- src/calibre/db/tables.py | 19 ++++++++++--------- src/calibre/db/tests/metadata.db | Bin 264192 -> 264192 bytes 5 files changed, 33 insertions(+), 29 deletions(-) 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 57ae6ab43fadf3724e12761935d90f6de9d12789..9e77a45f482f00bb4ace8f55dff8d0500885ba02 100644 GIT binary patch delta 769 zcmZvZ-%FEW6vxjw&-T9gzRk9gy|W?POX}veUDk@RSlzv=P7Y5t2 z*>!{Ao|_=>W(8ezQ_n>dnO7k(|A3^1(Z%vcFugFS_sxl}_FSC@&iQ=5=X^7HcP8)7 z`#W3!NU;C5tkSXn1+?np1<&Dt(*@m=RX+Y|92dTrZGZvt(nWd)Z3nt}dR8@}QOxy- z&>FsW24|*jO^c0r@)d$!L%0*DdhTDFxi`_(#qT`WZ8gNiS1dJZmEj8h#NrcFO3F8M zYbn4%d}O&z3HK(R)_6L-r#7|KSgon1mV7AU6^zKoo4A6kpM&5gMda;m6sMFiz)2Xh znZwQ2GFjj*n@iRfHzFh(SyO9V$;0&71~%7#Q7VRY*a6tV-!wpPt<(L`E#k2~V#lS% zM3LCWrV@@&f0Qa#1SVjFo`K1h58QXR%sP2UH39+3jf(XmJ*_ur7BU`1ycFb@k$IA6 zr&+aZe3#3D5@F2;bcGgJ9(~gNo(`d&J;URwVVX2|LA>}t<^OSxzsVKOdE3yzhhgDX#v_9!so%g7Q-%q(|QSD^nAE*dcP!**R3(H^a zYz<{O#Cnf9VgNCr^|2Fr_9X9Bjn-D0i;K}d_NIP5B#sQSPI)=Wo-2+az#*xnSQ2%9 alP6W9rNxf!?pZclKNYaoP^2}wvG@nJUDBuk delta 679 zcmYk3Ur19?9LLY^ch;`lUG+-pW$M`GTw7bELa|`Z5-Nx!vXBasO_UMQAE}2y26;B$ z8{F*osQFU8g^}kWg4ABbhuL!vruNW-fq~MCVE4F&o~Q5O$LIU~@%@b#&GDjHYz`ZY z0kVU)=}6bXmw}_bcAeorVz5&;GGgT82+XV+1B?woJ3NH%+~-U$>!L5GS@AgJZ`1KL zctdxB*h1%aph_NHhu@%!%vgDzqsR}KqmE>GuFd@fx2X>^Hq2(ghiNjm;j|qv{95Xn z+v0(O;krw_r@*V8yV9hk=rX9$~?Dy$w9x)`URUqbb^dO4zV0S05 z=6_%uUqwC8#GncG@H~G0P-p0%(8dbt?mvvmy?(^*TGckh7{u@`uJL7uY62OXMCq_u z5z#Ea9}8YO@e-$KWdg!vCPAadNvxO4gvqF$V32^Pco$zw^9xpiYEGsX@E&;bZK6}N zfjK0g*F6MBRmz+Cp%FQs}aI)`No4$s5RF)zhume-4gCv>)pe^P1wGE;krACrSA{?6^n jGRR8(0#EHP>Je8&O0yaoRJGSH@-YSZ_95v&f9C%IxqiPZ