diff options
author | Erlend Egeberg Aasland <erlend.aasland@protonmail.com> | 2022-05-04 13:16:01 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-05-04 13:16:01 (GMT) |
commit | 090819ec5faea2d0c2cdf6bcfdbbc9df144a8aad (patch) | |
tree | 71881b44e7ffb8eaa92dcdfc88a6ad037bc057d9 | |
parent | d716a0dfe2d1029111db393afaecdb04cc4093de (diff) | |
download | cpython-090819ec5faea2d0c2cdf6bcfdbbc9df144a8aad.zip cpython-090819ec5faea2d0c2cdf6bcfdbbc9df144a8aad.tar.gz cpython-090819ec5faea2d0c2cdf6bcfdbbc9df144a8aad.tar.bz2 |
gh-89022: Improve sqlite3 exceptions related to binding params and API misuse (#91572)
* Map SQLITE_MISUSE to sqlite3.InterfaceError
SQLITE_MISUSE implies misuse of the SQLite C API, which, if it happens,
is _not_ a user error; it is an sqlite3 extension module error.
* Raise better errors when binding parameters fail.
Instead of always raising InterfaceError, guessing what went wrong,
raise accurate exceptions with more accurate error messages.
-rw-r--r-- | Lib/test/test_sqlite3/test_dbapi.py | 2 | ||||
-rw-r--r-- | Lib/test/test_sqlite3/test_types.py | 8 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Library/2022-04-15-17-06-09.gh-issue-89022.DgdQCa.rst | 4 | ||||
-rw-r--r-- | Modules/_sqlite/cursor.c | 33 | ||||
-rw-r--r-- | Modules/_sqlite/util.c | 1 |
5 files changed, 28 insertions, 20 deletions
diff --git a/Lib/test/test_sqlite3/test_dbapi.py b/Lib/test/test_sqlite3/test_dbapi.py index 7e675a9..e132fcd 100644 --- a/Lib/test/test_sqlite3/test_dbapi.py +++ b/Lib/test/test_sqlite3/test_dbapi.py @@ -743,7 +743,7 @@ class CursorTests(unittest.TestCase): self.assertEqual(row[0], "Hu\x00go") def test_execute_non_iterable(self): - with self.assertRaises(ValueError) as cm: + with self.assertRaises(sqlite.ProgrammingError) as cm: self.cu.execute("insert into test(id) values (?)", 42) self.assertEqual(str(cm.exception), 'parameters are of unsupported type') diff --git a/Lib/test/test_sqlite3/test_types.py b/Lib/test/test_sqlite3/test_types.py index 0cfb72c..177cd10 100644 --- a/Lib/test/test_sqlite3/test_types.py +++ b/Lib/test/test_sqlite3/test_types.py @@ -255,9 +255,9 @@ class DeclTypesTests(unittest.TestCase): def test_error_in_conform(self): val = DeclTypesTests.BadConform(TypeError) - with self.assertRaises(sqlite.InterfaceError): + with self.assertRaises(sqlite.ProgrammingError): self.cur.execute("insert into test(bad) values (?)", (val,)) - with self.assertRaises(sqlite.InterfaceError): + with self.assertRaises(sqlite.ProgrammingError): self.cur.execute("insert into test(bad) values (:val)", {"val": val}) val = DeclTypesTests.BadConform(KeyboardInterrupt) @@ -269,13 +269,13 @@ class DeclTypesTests(unittest.TestCase): def test_unsupported_seq(self): class Bar: pass val = Bar() - with self.assertRaises(sqlite.InterfaceError): + with self.assertRaises(sqlite.ProgrammingError): self.cur.execute("insert into test(f) values (?)", (val,)) def test_unsupported_dict(self): class Bar: pass val = Bar() - with self.assertRaises(sqlite.InterfaceError): + with self.assertRaises(sqlite.ProgrammingError): self.cur.execute("insert into test(f) values (:val)", {"val": val}) def test_blob(self): diff --git a/Misc/NEWS.d/next/Library/2022-04-15-17-06-09.gh-issue-89022.DgdQCa.rst b/Misc/NEWS.d/next/Library/2022-04-15-17-06-09.gh-issue-89022.DgdQCa.rst new file mode 100644 index 0000000..4392f29 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-04-15-17-06-09.gh-issue-89022.DgdQCa.rst @@ -0,0 +1,4 @@ +In :mod:`sqlite3`, ``SQLITE_MISUSE`` result codes are now mapped to +:exc:`~sqlite3.InterfaceError` instead of :exc:`~sqlite3.ProgrammingError`. +Also, more accurate exceptions are raised when binding parameters fail. +Patch by Erlend E. Aasland. diff --git a/Modules/_sqlite/cursor.c b/Modules/_sqlite/cursor.c index f72bf30..c58def5 100644 --- a/Modules/_sqlite/cursor.c +++ b/Modules/_sqlite/cursor.c @@ -527,7 +527,8 @@ stmt_step(sqlite3_stmt *statement) } static int -bind_param(pysqlite_Statement *self, int pos, PyObject *parameter) +bind_param(pysqlite_state *state, pysqlite_Statement *self, int pos, + PyObject *parameter) { int rc = SQLITE_OK; const char *string; @@ -603,6 +604,9 @@ bind_param(pysqlite_Statement *self, int pos, PyObject *parameter) break; } case TYPE_UNKNOWN: + PyErr_Format(state->ProgrammingError, + "Error binding parameter %d: type '%s' is not supported", + pos, Py_TYPE(parameter)->tp_name); rc = -1; } @@ -688,15 +692,15 @@ bind_parameters(pysqlite_state *state, pysqlite_Statement *self, } } - rc = bind_param(self, i + 1, adapted); + rc = bind_param(state, self, i + 1, adapted); Py_DECREF(adapted); if (rc != SQLITE_OK) { - if (!PyErr_Occurred()) { - PyErr_Format(state->InterfaceError, - "Error binding parameter %d - " - "probably unsupported type.", i); - } + PyObject *exc, *val, *tb; + PyErr_Fetch(&exc, &val, &tb); + sqlite3 *db = sqlite3_db_handle(self->st); + _pysqlite_seterror(state, db); + _PyErr_ChainExceptions(exc, val, tb); return; } } @@ -748,20 +752,21 @@ bind_parameters(pysqlite_state *state, pysqlite_Statement *self, } } - rc = bind_param(self, i, adapted); + rc = bind_param(state, self, i, adapted); Py_DECREF(adapted); if (rc != SQLITE_OK) { - if (!PyErr_Occurred()) { - PyErr_Format(state->InterfaceError, - "Error binding parameter :%s - " - "probably unsupported type.", binding_name); - } + PyObject *exc, *val, *tb; + PyErr_Fetch(&exc, &val, &tb); + sqlite3 *db = sqlite3_db_handle(self->st); + _pysqlite_seterror(state, db); + _PyErr_ChainExceptions(exc, val, tb); return; } } } else { - PyErr_SetString(PyExc_ValueError, "parameters are of unsupported type"); + PyErr_SetString(state->ProgrammingError, + "parameters are of unsupported type"); } } diff --git a/Modules/_sqlite/util.c b/Modules/_sqlite/util.c index 37b2dd6..2b3bbfe 100644 --- a/Modules/_sqlite/util.c +++ b/Modules/_sqlite/util.c @@ -59,7 +59,6 @@ get_exception_class(pysqlite_state *state, int errorcode) case SQLITE_MISMATCH: return state->IntegrityError; case SQLITE_MISUSE: - return state->ProgrammingError; case SQLITE_RANGE: return state->InterfaceError; default: |