From 6ffc5174eade1d99f3069e4128af7026e69f9ab1 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 26 Aug 2022 17:15:04 +0200 Subject: sql: Handle sqlite extended error codes Qt 6.1 switched to using extended sqlite error codes: https://codereview.qt-project.org/c/qt/qtbase/+/329472 This is neat because it gives us more granularity for errors, but it also means that we now need to account for that by masking the error codes accordingly for checking their category. See unhandled I/O error here: https://crashes.qutebrowser.org/view/d9b26c2f --- qutebrowser/misc/sql.py | 32 +++++++++++++++++++------------- tests/unit/misc/test_sql.py | 10 ++++++++-- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/qutebrowser/misc/sql.py b/qutebrowser/misc/sql.py index d7b3dda0d..6c588c980 100644 --- a/qutebrowser/misc/sql.py +++ b/qutebrowser/misc/sql.py @@ -77,16 +77,16 @@ class SqliteErrorCode: in qutebrowser here. """ - ERROR = '1' # generic error code - BUSY = '5' # database is locked - READONLY = '8' # attempt to write a readonly database - IOERR = '10' # disk I/O error - CORRUPT = '11' # database disk image is malformed - FULL = '13' # database or disk is full - CANTOPEN = '14' # unable to open database file - PROTOCOL = '15' # locking protocol error - CONSTRAINT = '19' # UNIQUE constraint failed - NOTADB = '26' # file is not a database + ERROR = 1 # generic error code + BUSY = 5 # database is locked + READONLY = 8 # attempt to write a readonly database + IOERR = 10 # disk I/O error + CORRUPT = 11 # database disk image is malformed + FULL = 13 # database or disk is full + CANTOPEN = 14 # unable to open database file + PROTOCOL = 15 # locking protocol error + CONSTRAINT = 19 # UNIQUE constraint failed + NOTADB = 26 # file is not a database class Error(Exception): @@ -128,6 +128,12 @@ class BugError(Error): def raise_sqlite_error(msg: str, error: QSqlError) -> None: """Raise either a BugError or KnownError.""" error_code = error.nativeErrorCode() + try: + # https://sqlite.org/rescode.html#pve + primary_error_code = int(error_code) & 0xff + except ValueError: + primary_error_code = None + database_text = error.databaseText() driver_text = error.driverText() @@ -135,7 +141,7 @@ def raise_sqlite_error(msg: str, error: QSqlError) -> None: log.sql.debug(f"type: {debug.qenum_key(QSqlError, error.type())}") log.sql.debug(f"database text: {database_text}") log.sql.debug(f"driver text: {driver_text}") - log.sql.debug(f"error code: {error_code}") + log.sql.debug(f"error code: {error_code} -> {primary_error_code}") known_errors = [ SqliteErrorCode.BUSY, @@ -151,12 +157,12 @@ def raise_sqlite_error(msg: str, error: QSqlError) -> None: # https://github.com/qutebrowser/qutebrowser/issues/4681 # If the query we built was too long too_long_err = ( - error_code == SqliteErrorCode.ERROR and + primary_error_code == SqliteErrorCode.ERROR and (database_text.startswith("Expression tree is too large") or database_text in ["too many SQL variables", "LIKE or GLOB pattern too complex"])) - if error_code in known_errors or too_long_err: + if primary_error_code in known_errors or too_long_err: raise KnownError(msg, error) raise BugError(msg, error) diff --git a/tests/unit/misc/test_sql.py b/tests/unit/misc/test_sql.py index da72c5bb1..3e16ff337 100644 --- a/tests/unit/misc/test_sql.py +++ b/tests/unit/misc/test_sql.py @@ -93,10 +93,16 @@ class TestSqlError: @pytest.mark.parametrize('error_code, exception', [ (sql.SqliteErrorCode.BUSY, sql.KnownError), (sql.SqliteErrorCode.CONSTRAINT, sql.BugError), + # extended error codes + (sql.SqliteErrorCode.IOERR | (1<<8), sql.KnownError), # SQLITE_IOERR_READ + ( + sql.SqliteErrorCode.CONSTRAINT | (1<<8), # SQLITE_CONSTRAINT_CHECK + sql.BugError + ), ]) def test_known(self, error_code, exception): sql_err = QSqlError("driver text", "db text", QSqlError.ErrorType.UnknownError, - error_code) + str(error_code)) with pytest.raises(exception): sql.raise_sqlite_error("Message", sql_err) @@ -109,7 +115,7 @@ class TestSqlError: 'type: UnknownError', 'database text: db text', 'driver text: driver text', - 'error code: 23'] + 'error code: 23 -> 23'] assert caplog.messages == expected -- cgit v1.2.3-54-g00ecf