From 567175981193391809a896caf8684d1b14f63f07 Mon Sep 17 00:00:00 2001 From: Jay Kamat Date: Tue, 26 Mar 2019 18:19:33 -0700 Subject: Fix crash on long sql lines --- qutebrowser/completion/models/histcategory.py | 26 ++++++++++++++++---------- qutebrowser/misc/sql.py | 13 +++++++++++-- tests/unit/completion/test_histcategory.py | 14 ++++++++++++++ 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'), -- cgit v1.2.3-54-g00ecf