From 2868daf3d0be10aae45f47b2b3d093ecbccc6acf Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Fri, 15 Apr 2022 16:59:29 +0200 Subject: [PATCH 1/6] gh-89022: 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. --- Modules/_sqlite/util.c | 1 - 1 file changed, 1 deletion(-) diff --git a/Modules/_sqlite/util.c b/Modules/_sqlite/util.c index 113b581bfac735..b2443e2f73c94d 100644 --- a/Modules/_sqlite/util.c +++ b/Modules/_sqlite/util.c @@ -71,7 +71,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: From cfba3331077fad5007bd08ae4d093fe6e5abcb32 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Fri, 15 Apr 2022 17:06:15 +0200 Subject: [PATCH 2/6] Add NEWS --- .../next/Library/2022-04-15-17-06-09.gh-issue-89022.DgdQCa.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2022-04-15-17-06-09.gh-issue-89022.DgdQCa.rst 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 00000000000000..cb92781d251e8a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-04-15-17-06-09.gh-issue-89022.DgdQCa.rst @@ -0,0 +1,2 @@ +In :mod:`sqlite3`, ``SQLITE_MISUSE`` result codes are now mapped to +:exc:`~sqlite3.InterfaceError`. Patch by Erlend E. Aasland. From c32e2e6dee13f94360ab6eb3cf938d6f7fc021b5 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sun, 1 May 2022 20:40:35 -0600 Subject: [PATCH 3/6] Raise better errors when binding parameters fail. Instead of always raising InterfaceError, guessing what went wrong, raise accurate exceptions with more accurate error messages. --- Lib/test/test_sqlite3/test_types.py | 8 ++++---- Modules/_sqlite/statement.c | 32 +++++++++++++++++------------ Modules/_sqlite/statement.h | 4 +++- 3 files changed, 26 insertions(+), 18 deletions(-) diff --git a/Lib/test/test_sqlite3/test_types.py b/Lib/test/test_sqlite3/test_types.py index 0cfb72c5f0999e..177cd102350397 100644 --- a/Lib/test/test_sqlite3/test_types.py +++ b/Lib/test/test_sqlite3/test_types.py @@ -255,9 +255,9 @@ def test_foo(self): 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 @@ def test_error_in_conform(self): 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/Modules/_sqlite/statement.c b/Modules/_sqlite/statement.c index baa1b71a8daa41..1f2fe0b0c05fb6 100644 --- a/Modules/_sqlite/statement.c +++ b/Modules/_sqlite/statement.c @@ -127,7 +127,10 @@ pysqlite_statement_create(pysqlite_Connection *connection, PyObject *sql) return NULL; } -int pysqlite_statement_bind_parameter(pysqlite_Statement* self, int pos, PyObject* parameter) +int +pysqlite_statement_bind_parameter(pysqlite_state *state, + pysqlite_Statement *self, int pos, + PyObject *parameter) { int rc = SQLITE_OK; const char *string; @@ -203,6 +206,9 @@ int pysqlite_statement_bind_parameter(pysqlite_Statement* self, int pos, PyObjec break; } case TYPE_UNKNOWN: + PyErr_Format(state->ProgrammingError, + "Error binding parameter %d: type '%s' in not supported", + pos, Py_TYPE(parameter)->tp_name); rc = -1; } @@ -289,15 +295,15 @@ pysqlite_statement_bind_parameters(pysqlite_state *state, } } - rc = pysqlite_statement_bind_parameter(self, i + 1, adapted); + rc = pysqlite_statement_bind_parameter(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; } } @@ -349,15 +355,15 @@ pysqlite_statement_bind_parameters(pysqlite_state *state, } } - rc = pysqlite_statement_bind_parameter(self, i, adapted); + rc = pysqlite_statement_bind_parameter(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; } } diff --git a/Modules/_sqlite/statement.h b/Modules/_sqlite/statement.h index b901c43c479ae2..619aec8228f814 100644 --- a/Modules/_sqlite/statement.h +++ b/Modules/_sqlite/statement.h @@ -39,7 +39,9 @@ typedef struct pysqlite_Statement *pysqlite_statement_create(pysqlite_Connection *connection, PyObject *sql); -int pysqlite_statement_bind_parameter(pysqlite_Statement* self, int pos, PyObject* parameter); +int pysqlite_statement_bind_parameter(pysqlite_state *state, + pysqlite_Statement *self, int pos, + PyObject *parameter); void pysqlite_statement_bind_parameters(pysqlite_state *state, pysqlite_Statement *self, PyObject *parameters); From 268e4e3b28d300456573bed56e96cd09189f6cb9 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sun, 1 May 2022 20:45:28 -0600 Subject: [PATCH 4/6] Expand NEWS entry --- .../Library/2022-04-15-17-06-09.gh-issue-89022.DgdQCa.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 index cb92781d251e8a..4392f29b376f48 100644 --- 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 @@ -1,2 +1,4 @@ In :mod:`sqlite3`, ``SQLITE_MISUSE`` result codes are now mapped to -:exc:`~sqlite3.InterfaceError`. Patch by Erlend E. Aasland. +:exc:`~sqlite3.InterfaceError` instead of :exc:`~sqlite3.ProgrammingError`. +Also, more accurate exceptions are raised when binding parameters fail. +Patch by Erlend E. Aasland. From c83172a961213ec8f29e6050f562b5b42120c0b5 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sun, 1 May 2022 20:57:51 -0600 Subject: [PATCH 5/6] Improve more bind exceptions --- Lib/test/test_sqlite3/test_dbapi.py | 2 +- Modules/_sqlite/statement.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_sqlite3/test_dbapi.py b/Lib/test/test_sqlite3/test_dbapi.py index 7e675a91b5d1fd..e132fcdfb0e658 100644 --- a/Lib/test/test_sqlite3/test_dbapi.py +++ b/Lib/test/test_sqlite3/test_dbapi.py @@ -743,7 +743,7 @@ def test_execute_arg_string_with_zero_byte(self): 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/Modules/_sqlite/statement.c b/Modules/_sqlite/statement.c index 1f2fe0b0c05fb6..76327bdec6fd7a 100644 --- a/Modules/_sqlite/statement.c +++ b/Modules/_sqlite/statement.c @@ -368,7 +368,8 @@ pysqlite_statement_bind_parameters(pysqlite_state *state, } } } else { - PyErr_SetString(PyExc_ValueError, "parameters are of unsupported type"); + PyErr_SetString(state->ProgrammingError, + "parameters are of unsupported type"); } } From 76f7ed12fddf07180cd683757f0eb0b969bb77c9 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sun, 1 May 2022 21:02:48 -0600 Subject: [PATCH 6/6] pysqlite_statement_bind_parameter is local to statement.c, so make it static --- Modules/_sqlite/statement.c | 2 +- Modules/_sqlite/statement.h | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/Modules/_sqlite/statement.c b/Modules/_sqlite/statement.c index 76327bdec6fd7a..20e468744719ae 100644 --- a/Modules/_sqlite/statement.c +++ b/Modules/_sqlite/statement.c @@ -127,7 +127,7 @@ pysqlite_statement_create(pysqlite_Connection *connection, PyObject *sql) return NULL; } -int +static int pysqlite_statement_bind_parameter(pysqlite_state *state, pysqlite_Statement *self, int pos, PyObject *parameter) diff --git a/Modules/_sqlite/statement.h b/Modules/_sqlite/statement.h index 619aec8228f814..74ddfe59ce54f8 100644 --- a/Modules/_sqlite/statement.h +++ b/Modules/_sqlite/statement.h @@ -39,9 +39,6 @@ typedef struct pysqlite_Statement *pysqlite_statement_create(pysqlite_Connection *connection, PyObject *sql); -int pysqlite_statement_bind_parameter(pysqlite_state *state, - pysqlite_Statement *self, int pos, - PyObject *parameter); void pysqlite_statement_bind_parameters(pysqlite_state *state, pysqlite_Statement *self, PyObject *parameters);