Fix #1927179 [VL set to wildcard crashes Calibre](https://bugs.launchpad.net/calibre/+bug/1927179)
This commit is contained in:
Kovid Goyal 2021-05-05 21:43:52 +05:30
commit d8918f1dd8
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C

View File

@ -142,6 +142,7 @@ class Cache(object):
self.formatter_template_cache = {} self.formatter_template_cache = {}
self.dirtied_cache = {} self.dirtied_cache = {}
self.vls_for_books_cache = None self.vls_for_books_cache = None
self.vls_for_books_cache_is_loading = False
self.dirtied_sequence = 0 self.dirtied_sequence = 0
self.cover_caches = set() self.cover_caches = set()
self.clear_search_cache_count = 0 self.clear_search_cache_count = 0
@ -251,6 +252,7 @@ class Cache(object):
self.clear_search_cache_count += 1 self.clear_search_cache_count += 1
self._search_api.update_or_clear(self, book_ids) self._search_api.update_or_clear(self, book_ids)
self.vls_for_books_cache = None self.vls_for_books_cache = None
self.vls_for_books_cache_is_loading = False
@read_api @read_api
def last_modified(self): def last_modified(self):
@ -672,8 +674,26 @@ class Cache(object):
cover_as_data is True then as mi.cover_data. cover_as_data is True then as mi.cover_data.
''' '''
# Check if virtual_libraries_for_books rebuilt its cache. If it did then
# we must clear the composite caches so the new data can be taken into
# account. Clearing the caches requires getting a write lock, so it must
# be done outside of the closure of _get_metadata().
composite_cache_needs_to_be_cleared = False
with self.safe_read_lock: with self.safe_read_lock:
vl_cache_was_none = self.vls_for_books_cache is None
mi = self._get_metadata(book_id, get_user_categories=get_user_categories) mi = self._get_metadata(book_id, get_user_categories=get_user_categories)
if vl_cache_was_none and self.vls_for_books_cache is not None:
composite_cache_needs_to_be_cleared = True
if composite_cache_needs_to_be_cleared:
try:
with self.write_lock:
self.clear_composite_caches()
except DowngradeLockError:
# We can't clear the composite caches because a read lock is set.
# As a consequence the value of a composite column that calls
# virtual_libraries() might be wrong. Oh well. Log and keep running.
print("Couldn't get write lock after vls_for_books_cache was loaded", file=sys.stderr)
traceback.print_exc()
if get_cover: if get_cover:
if cover_as_data: if cover_as_data:
@ -2213,13 +2233,25 @@ class Cache(object):
if self.vls_for_books_cache is None: if self.vls_for_books_cache is None:
# Using a list is slightly faster than a set. # Using a list is slightly faster than a set.
c = defaultdict(list) c = defaultdict(list)
if self.vls_for_books_cache_is_loading:
# We get here if resolving the books in a VL triggers another VL
# calculation. This can be 'real' recursion, in which case the
# eventual answer will be wrong. It can also be a search using
# a location of 'all' that causes evaluation of a composite that
# references virtual libraries. If the composite isn't used in a
# VL then the eventual answer will be correct because get_metadata
# will clear the caches.
return c
self.vls_for_books_cache_is_loading = True
libraries = self._pref('virtual_libraries', {}) libraries = self._pref('virtual_libraries', {})
for lib, expr in libraries.items(): for lib, expr in libraries.items():
book = None
try: try:
for book in self._search(expr, virtual_fields=virtual_fields): for book in self._search(expr, virtual_fields=virtual_fields):
c[book].append(lib) c[book].append(lib)
except Exception as e: except Exception as e:
c[book].append(_('[Error in Virtual library {0}: {1}]').format(lib, str(e))) if book:
c[book].append(_('[Error in Virtual library {0}: {1}]').format(lib, str(e)))
self.vls_for_books_cache = {b:tuple(sorted(libs, key=sort_key)) for b, libs in c.items()} self.vls_for_books_cache = {b:tuple(sorted(libs, key=sort_key)) for b, libs in c.items()}
if not book_ids: if not book_ids:
book_ids = self._all_book_ids() book_ids = self._all_book_ids()