mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
newdb: Automatically fix broken link tables
Fixes #1218783 [Beim Starten von Calibre bleibt das Startlogo bestehen und Pythons meldet einen Fehler.](https://bugs.launchpad.net/calibre/+bug/1218783)
This commit is contained in:
parent
e35a7bd6fb
commit
5b1ba794b1
@ -20,7 +20,7 @@ from calibre.db import SPOOL_SIZE, _get_next_series_num_for_list
|
||||
from calibre.db.categories import get_categories
|
||||
from calibre.db.locking import create_locks
|
||||
from calibre.db.errors import NoSuchFormat
|
||||
from calibre.db.fields import create_field, IDENTITY
|
||||
from calibre.db.fields import create_field, IDENTITY, InvalidLinkTable
|
||||
from calibre.db.search import Search
|
||||
from calibre.db.tables import VirtualTable
|
||||
from calibre.db.write import get_series_values, uniq
|
||||
@ -866,10 +866,18 @@ class Cache(object):
|
||||
def search(self, query, restriction='', virtual_fields=None, book_ids=None):
|
||||
return self._search_api(self, query, restriction, virtual_fields=virtual_fields, book_ids=book_ids)
|
||||
|
||||
@read_api
|
||||
def get_categories(self, sort='name', book_ids=None, icon_map=None):
|
||||
return get_categories(self, sort=sort, book_ids=book_ids,
|
||||
icon_map=icon_map)
|
||||
@api
|
||||
def get_categories(self, sort='name', book_ids=None, icon_map=None, already_fixed=None):
|
||||
try:
|
||||
with self.read_lock:
|
||||
return get_categories(self, sort=sort, book_ids=book_ids, icon_map=icon_map)
|
||||
except InvalidLinkTable as err:
|
||||
bad_field = err.field_name
|
||||
if bad_field == already_fixed:
|
||||
raise
|
||||
with self.write_lock:
|
||||
self.fields[bad_field].table.fix_link_table(self.backend)
|
||||
return self.get_categories(sort=sort, book_ids=book_ids, icon_map=icon_map, already_fixed=bad_field)
|
||||
|
||||
@write_api
|
||||
def update_last_modified(self, book_ids, now=None):
|
||||
|
@ -27,6 +27,12 @@ def bool_sort_key(bools_are_tristate):
|
||||
|
||||
IDENTITY = lambda x: x
|
||||
|
||||
class InvalidLinkTable(Exception):
|
||||
|
||||
def __init__(self, name):
|
||||
Exception.__init__(self, name)
|
||||
self.field_name = name
|
||||
|
||||
class Field(object):
|
||||
|
||||
is_many = False
|
||||
@ -144,9 +150,15 @@ class Field(object):
|
||||
ratings = tuple(r for r in (book_rating_map.get(book_id, 0) for
|
||||
book_id in item_book_ids) if r > 0)
|
||||
avg = sum(ratings)/len(ratings) if ratings else 0
|
||||
name = self.category_formatter(id_map[item_id])
|
||||
try:
|
||||
name = self.category_formatter(id_map[item_id])
|
||||
except KeyError:
|
||||
# db has entries in the link table without entries in the
|
||||
# id table, for example, see
|
||||
# https://bugs.launchpad.net/bugs/1218783
|
||||
raise InvalidLinkTable(self.name)
|
||||
sval = (self.category_sort_value(item_id, item_book_ids, lang_map)
|
||||
if special_sort else name)
|
||||
if special_sort else name)
|
||||
c = tag_class(name, id=item_id, sort=sval, avg=avg,
|
||||
id_set=item_book_ids, count=len(item_book_ids))
|
||||
ans.append(c)
|
||||
|
@ -64,6 +64,9 @@ class Table(object):
|
||||
def remove_books(self, book_ids, db):
|
||||
return set()
|
||||
|
||||
def fix_link_table(self, db):
|
||||
pass
|
||||
|
||||
class VirtualTable(Table):
|
||||
|
||||
'''
|
||||
@ -201,6 +204,17 @@ class ManyToOneTable(Table):
|
||||
cbm[item_id].add(book)
|
||||
bcm[book] = item_id
|
||||
|
||||
def fix_link_table(self, db):
|
||||
linked_item_ids = {item_id for item_id in self.book_col_map.itervalues()}
|
||||
extra_item_ids = linked_item_ids - set(self.id_map)
|
||||
if extra_item_ids:
|
||||
for item_id in extra_item_ids:
|
||||
book_ids = self.col_book_map.pop(item_id, ())
|
||||
for book_id in book_ids:
|
||||
self.book_col_map.pop(book_id, None)
|
||||
db.conn.executemany('DELETE FROM {0} WHERE {1}=?'.format(
|
||||
self.link_table, self.metadata['link_column']), tuple((x,) for x in extra_item_ids))
|
||||
|
||||
def remove_books(self, book_ids, db):
|
||||
clean = set()
|
||||
for book_id in book_ids:
|
||||
@ -284,6 +298,17 @@ class ManyToManyTable(ManyToOneTable):
|
||||
|
||||
self.book_col_map = {k:tuple(v) for k, v in bcm.iteritems()}
|
||||
|
||||
def fix_link_table(self, db):
|
||||
linked_item_ids = {item_id for item_ids in self.book_col_map.itervalues() for item_id in item_ids}
|
||||
extra_item_ids = linked_item_ids - set(self.id_map)
|
||||
if extra_item_ids:
|
||||
for item_id in extra_item_ids:
|
||||
book_ids = self.col_book_map.pop(item_id, ())
|
||||
for book_id in book_ids:
|
||||
self.book_col_map[book_id] = tuple(iid for iid in self.book_col_map.pop(book_id, ()) if iid not in extra_item_ids)
|
||||
db.conn.executemany('DELETE FROM {0} WHERE {1}=?'.format(
|
||||
self.link_table, self.metadata['link_column']), tuple((x,) for x in extra_item_ids))
|
||||
|
||||
def remove_books(self, book_ids, db):
|
||||
clean = set()
|
||||
for book_id in book_ids:
|
||||
|
Loading…
x
Reference in New Issue
Block a user