summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Bruhin <me@the-compiler.org>2021-03-18 10:28:53 +0100
committerFlorian Bruhin <me@the-compiler.org>2021-03-18 10:35:32 +0100
commit3acdb3288c9de923abb811c2c7385c5c9ff2c47d (patch)
tree6091c548397fe0c040ba4afbeb87b610afd9c709
parent9f67a763ef86805f0981f037ccd2fb5cb0e84b88 (diff)
downloadqutebrowser-3acdb3288c9de923abb811c2c7385c5c9ff2c47d.tar.gz
qutebrowser-3acdb3288c9de923abb811c2c7385c5c9ff2c47d.zip
Try to recover from CompletionMetaInfo with unexpected structure
Fixes #6302 (cherry picked from commit 254b21f3ecc43d4d844e6ded55378673b913b5c8)
-rw-r--r--qutebrowser/browser/history.py24
-rw-r--r--qutebrowser/misc/sql.py8
-rw-r--r--tests/unit/browser/test_history.py22
3 files changed, 47 insertions, 7 deletions
diff --git a/qutebrowser/browser/history.py b/qutebrowser/browser/history.py
index ef4650a35..9eaa88760 100644
--- a/qutebrowser/browser/history.py
+++ b/qutebrowser/browser/history.py
@@ -92,8 +92,11 @@ class CompletionMetaInfo(sql.SqlTable):
}
def __init__(self, parent=None):
- super().__init__("CompletionMetaInfo", ['key', 'value'],
- constraints={'key': 'PRIMARY KEY'})
+ self._fields = ['key', 'value']
+ self._constraints = {'key': 'PRIMARY KEY'}
+ super().__init__(
+ "CompletionMetaInfo", self._fields, constraints=self._constraints)
+
if sql.user_version_changed():
self._init_default_values()
@@ -101,6 +104,15 @@ class CompletionMetaInfo(sql.SqlTable):
if key not in self.KEYS:
raise KeyError(key)
+ def try_recover(self):
+ """Try recovering the table structure.
+
+ This should be called if getting a value via __getattr__ failed. In theory, this
+ should never happen, in practice, it does.
+ """
+ self._create_table(self._fields, constraints=self._constraints, force=True)
+ self._init_default_values()
+
def _init_default_values(self):
for key, default in self.KEYS.items():
if key not in self:
@@ -164,7 +176,13 @@ class WebHistory(sql.SqlTable):
self.completion = CompletionHistory(parent=self)
self.metainfo = CompletionMetaInfo(parent=self)
- rebuild_completion = self.metainfo['force_rebuild']
+ try:
+ rebuild_completion = self.metainfo['force_rebuild']
+ except sql.BugError:
+ log.sql.warning("Failed to access meta info, trying to recover...",
+ exc_info=True)
+ self.metainfo.try_recover()
+ rebuild_completion = self.metainfo['force_rebuild']
if sql.user_version_changed():
# If the DB user version changed, run a full cleanup and rebuild the
diff --git a/qutebrowser/misc/sql.py b/qutebrowser/misc/sql.py
index 7a3626f6e..68c0fd538 100644
--- a/qutebrowser/misc/sql.py
+++ b/qutebrowser/misc/sql.py
@@ -351,13 +351,13 @@ class SqlTable(QObject):
self._name = name
self._create_table(fields, constraints)
- def _create_table(self, fields, constraints):
+ def _create_table(self, fields, constraints, *, force=False):
"""Create the table if the database is uninitialized.
- If the table already exists, this does nothing, so it can e.g. be called on
- every user_version change.
+ If the table already exists, this does nothing (except with force=True), so it
+ can e.g. be called on every user_version change.
"""
- if not user_version_changed():
+ if not user_version_changed() and not force:
return
constraints = constraints or {}
diff --git a/tests/unit/browser/test_history.py b/tests/unit/browser/test_history.py
index 9b08de30d..1ca708ec8 100644
--- a/tests/unit/browser/test_history.py
+++ b/tests/unit/browser/test_history.py
@@ -508,6 +508,28 @@ class TestCompletionMetaInfo:
metainfo['excluded_patterns'] = value
assert metainfo['excluded_patterns'] == value
+ # FIXME: It'd be good to test those two things via WebHistory (and not just
+ # CompletionMetaInfo in isolation), but we can't do that right now - see the
+ # docstring of TestRebuild for details.
+
+ def test_recovery_no_key(self, metainfo):
+ metainfo.delete('key', 'force_rebuild')
+
+ with pytest.raises(sql.BugError, match='No result for single-result query'):
+ metainfo['force_rebuild']
+
+ metainfo.try_recover()
+ assert not metainfo['force_rebuild']
+
+ def test_recovery_no_table(self, metainfo):
+ sql.Query("DROP TABLE CompletionMetaInfo").run()
+
+ with pytest.raises(sql.BugError, match='no such table: CompletionMetaInfo'):
+ metainfo['force_rebuild']
+
+ metainfo.try_recover()
+ assert not metainfo['force_rebuild']
+
class TestHistoryProgress: