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.categories import get_categories
|
||||||
from calibre.db.locking import create_locks
|
from calibre.db.locking import create_locks
|
||||||
from calibre.db.errors import NoSuchFormat
|
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.search import Search
|
||||||
from calibre.db.tables import VirtualTable
|
from calibre.db.tables import VirtualTable
|
||||||
from calibre.db.write import get_series_values, uniq
|
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):
|
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)
|
return self._search_api(self, query, restriction, virtual_fields=virtual_fields, book_ids=book_ids)
|
||||||
|
|
||||||
@read_api
|
@api
|
||||||
def get_categories(self, sort='name', book_ids=None, icon_map=None):
|
def get_categories(self, sort='name', book_ids=None, icon_map=None, already_fixed=None):
|
||||||
return get_categories(self, sort=sort, book_ids=book_ids,
|
try:
|
||||||
icon_map=icon_map)
|
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
|
@write_api
|
||||||
def update_last_modified(self, book_ids, now=None):
|
def update_last_modified(self, book_ids, now=None):
|
||||||
|
@ -27,6 +27,12 @@ def bool_sort_key(bools_are_tristate):
|
|||||||
|
|
||||||
IDENTITY = lambda x: x
|
IDENTITY = lambda x: x
|
||||||
|
|
||||||
|
class InvalidLinkTable(Exception):
|
||||||
|
|
||||||
|
def __init__(self, name):
|
||||||
|
Exception.__init__(self, name)
|
||||||
|
self.field_name = name
|
||||||
|
|
||||||
class Field(object):
|
class Field(object):
|
||||||
|
|
||||||
is_many = False
|
is_many = False
|
||||||
@ -144,9 +150,15 @@ class Field(object):
|
|||||||
ratings = tuple(r for r in (book_rating_map.get(book_id, 0) for
|
ratings = tuple(r for r in (book_rating_map.get(book_id, 0) for
|
||||||
book_id in item_book_ids) if r > 0)
|
book_id in item_book_ids) if r > 0)
|
||||||
avg = sum(ratings)/len(ratings) if ratings else 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)
|
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,
|
c = tag_class(name, id=item_id, sort=sval, avg=avg,
|
||||||
id_set=item_book_ids, count=len(item_book_ids))
|
id_set=item_book_ids, count=len(item_book_ids))
|
||||||
ans.append(c)
|
ans.append(c)
|
||||||
|
@ -64,6 +64,9 @@ class Table(object):
|
|||||||
def remove_books(self, book_ids, db):
|
def remove_books(self, book_ids, db):
|
||||||
return set()
|
return set()
|
||||||
|
|
||||||
|
def fix_link_table(self, db):
|
||||||
|
pass
|
||||||
|
|
||||||
class VirtualTable(Table):
|
class VirtualTable(Table):
|
||||||
|
|
||||||
'''
|
'''
|
||||||
@ -201,6 +204,17 @@ class ManyToOneTable(Table):
|
|||||||
cbm[item_id].add(book)
|
cbm[item_id].add(book)
|
||||||
bcm[book] = item_id
|
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):
|
def remove_books(self, book_ids, db):
|
||||||
clean = set()
|
clean = set()
|
||||||
for book_id in book_ids:
|
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()}
|
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):
|
def remove_books(self, book_ids, db):
|
||||||
clean = set()
|
clean = set()
|
||||||
for book_id in book_ids:
|
for book_id in book_ids:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user