summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJay Kamat <jaygkamat@gmail.com>2019-03-26 18:19:33 -0700
committerFlorian Bruhin <me@the-compiler.org>2019-04-03 08:54:00 +0200
commit567175981193391809a896caf8684d1b14f63f07 (patch)
tree6e5c7f6a3b451b7078ca82e9b75be0792279b494
parent055e8270914397cc1ea3b4d051b839777bb2a8bb (diff)
downloadqutebrowser-567175981193391809a896caf8684d1b14f63f07.tar.gz
qutebrowser-567175981193391809a896caf8684d1b14f63f07.zip
Fix crash on long sql lines
-rw-r--r--qutebrowser/completion/models/histcategory.py26
-rw-r--r--qutebrowser/misc/sql.py13
-rw-r--r--tests/unit/completion/test_histcategory.py14
3 files changed, 41 insertions, 12 deletions
diff --git a/qutebrowser/completion/models/histcategory.py b/qutebrowser/completion/models/histcategory.py
index 8b1be213a..175b458fd 100644
--- a/qutebrowser/completion/models/histcategory.py
+++ b/qutebrowser/completion/models/histcategory.py
@@ -22,7 +22,7 @@
from PyQt5.QtSql import QSqlQueryModel
from qutebrowser.misc import sql
-from qutebrowser.utils import debug
+from qutebrowser.utils import debug, message
from qutebrowser.config import config
@@ -88,15 +88,21 @@ class HistoryCategory(QSqlQueryModel):
if not self._query or len(words) != len(self._query.bound_values()):
# if the number of words changed, we need to generate a new query
# otherwise, we can reuse the prepared query for performance
- self._query = sql.Query(' '.join([
- "SELECT url, title, {}".format(timefmt),
- "FROM CompletionHistory",
- # the incoming pattern will have literal % and _ escaped
- # we need to tell sql to treat '\' as an escape character
- 'WHERE ({})'.format(where_clause),
- self._atime_expr(),
- "ORDER BY last_atime DESC",
- ]), forward_only=False)
+ try:
+ self._query = sql.Query(' '.join([
+ "SELECT url, title, {}".format(timefmt),
+ "FROM CompletionHistory",
+ # the incoming pattern will have literal % and _ escaped
+ # we need to tell sql to treat '\' as an escape character
+ 'WHERE ({})'.format(where_clause),
+ self._atime_expr(),
+ "ORDER BY last_atime DESC",
+ ]), forward_only=False)
+ except sql.SqlLongQueryError as e:
+ # Sometimes, the query we built up was invalid, for example,
+ # due to a large amount of words.
+ message.error("Error building SQL Query: {}".format(e.text()))
+ return
with debug.log_time('sql', 'Running completion query'):
self._query.run(**{
diff --git a/qutebrowser/misc/sql.py b/qutebrowser/misc/sql.py
index e3540e082..dd33621f6 100644
--- a/qutebrowser/misc/sql.py
+++ b/qutebrowser/misc/sql.py
@@ -36,6 +36,7 @@ class SqliteErrorCode:
"""
UNKNOWN = '-1'
+ ERROR = '1' # generic error code
BUSY = '5' # database is locked
READONLY = '8' # attempt to write a readonly database
IOERR = '10' # disk I/O error
@@ -73,6 +74,11 @@ class SqlEnvironmentError(SqlError):
"""
+class SqlLongQueryError(SqlError):
+
+ """Raised when a query is too long."""
+
+
class SqlBugError(SqlError):
"""Raised on an error interacting with the SQL database.
@@ -112,8 +118,11 @@ def raise_sqlite_error(msg, error):
if error_code in environmental_errors or qtbug_70506:
raise SqlEnvironmentError(msg, error)
- else:
- raise SqlBugError(msg, error)
+ if (error_code == SqliteErrorCode.ERROR and
+ driver_text == "Unable to execute statement" and
+ database_text.startswith("Expression tree is too large")):
+ raise SqlLongQueryError(msg, error)
+ raise SqlBugError(msg, error)
def init(db_path):
diff --git a/tests/unit/completion/test_histcategory.py b/tests/unit/completion/test_histcategory.py
index 8fd48f429..7dfda3ea6 100644
--- a/tests/unit/completion/test_histcategory.py
+++ b/tests/unit/completion/test_histcategory.py
@@ -20,11 +20,13 @@
"""Test the web history completion category."""
import datetime
+import logging
import pytest
from qutebrowser.misc import sql
from qutebrowser.completion.models import histcategory
+from qutebrowser.utils import usertypes
@pytest.fixture
@@ -133,6 +135,18 @@ def test_set_pattern_repeated(model_validator, hist):
])
+def test_set_pattern_long(hist, message_mock, caplog):
+ hist.insert({'url': 'example.com/foo', 'title': 'title1', 'last_atime': 1})
+ cat = histcategory.HistoryCategory()
+ with caplog.at_level(logging.ERROR):
+ # pylint: disable=bad-builtin
+ cat.set_pattern(" ".join(map(str, range(10000))))
+ # pylint: enable=bad-builtin
+ msg = message_mock.getmsg(usertypes.MessageLevel.error)
+ assert msg.text.startswith(
+ "Error building SQL Query: Expression tree is too large")
+
+
@pytest.mark.parametrize('max_items, before, after', [
(-1, [
('a', 'a', '2017-04-16'),