diff --git a/src/calibre/db/backend.py b/src/calibre/db/backend.py index fca576cf9d..72a25b4f78 100644 --- a/src/calibre/db/backend.py +++ b/src/calibre/db/backend.py @@ -16,6 +16,7 @@ import shutil import sys import time import uuid +from contextlib import suppress from functools import partial from calibre import as_unicode, force_unicode, isbytestring, prints @@ -377,10 +378,23 @@ class Connection(apsw.Connection): # {{{ ans = self.cursor().execute(*args) if kw.get('all', True): return ans.fetchall() - try: + with suppress(StopIteration, IndexError): return next(ans)[0] - except (StopIteration, IndexError): - return None + + def get_dict(self, *args, all=True): + ans = self.cursor().execute(*args) + desc = ans.getdescription() + field_names = tuple(x[0] for x in desc) + + def as_dict(row): + return dict(zip(field_names, row)) + + if all: + return tuple(map(as_dict, ans)) + ans = ans.fetchone() + if ans is not None: + ans = as_dict(ans) + return ans def execute(self, sql, bindings=None): cursor = self.cursor() diff --git a/src/calibre/db/fts/pool.py b/src/calibre/db/fts/pool.py index 356479d433..2a27f96aeb 100644 --- a/src/calibre/db/fts/pool.py +++ b/src/calibre/db/fts/pool.py @@ -62,12 +62,13 @@ class Worker(Thread): self.working = True try: res = self.run_job(x) - if res is not None: + if res is not None and self.keep_going: self.supervise_queue.put(res) except Exception: tb = traceback.format_exc() traceback.print_exc() - self.supervise_queue.put(Result(x, tb)) + if self.keep_going: + self.supervise_queue.put(Result(x, tb)) finally: self.working = False diff --git a/src/calibre/db/tests/fts_api.py b/src/calibre/db/tests/fts_api.py index 07d5ff2585..a6e4094f09 100644 --- a/src/calibre/db/tests/fts_api.py +++ b/src/calibre/db/tests/fts_api.py @@ -35,6 +35,9 @@ class FTSAPITest(BaseTest): while fts.all_currently_dirty() and time.monotonic() - st < timeout: fts.pool.supervisor_thread.join(0.01) + def text_records(self, fts): + return fts.get_connection().get_dict('SELECT * FROM fts_db.books_text') + def test_fts_pool(self): cache = self.init_cache() fts = cache.enable_fts() @@ -43,6 +46,24 @@ class FTSAPITest(BaseTest): cache.add_format(1, 'TXT', BytesIO(b'a test text')) self.wait_for_fts_to_finish(fts) + def q(rec, **kw): + self.ae({x: rec[x] for x in kw}, kw) + + def check(**kw): + tr = self.text_records(fts) + self.ae(len(tr), 1) + q(tr[0], **kw) + + check(id=1, book=1, format='TXT', searchable_text='a test text') + # check re-adding does not rescan + cache.add_format(1, 'TXT', BytesIO(b'a test text')) + self.wait_for_fts_to_finish(fts) + check(id=1, book=1, format='TXT', searchable_text='a test text') + # check updating rescans + cache.add_format(1, 'TXT', BytesIO(b'a test text2')) + self.wait_for_fts_to_finish(fts) + check(id=2, book=1, format='TXT', searchable_text='a test text2') + def test_fts_triggers(self): cache = self.init_cache() fts = cache.enable_fts(start_pool=False)