diff --git a/Lib/sqlite3/test/hooks.py b/Lib/sqlite3/test/hooks.py index 43e3810d13df18..6d61b2c2e46e94 100644 --- a/Lib/sqlite3/test/hooks.py +++ b/Lib/sqlite3/test/hooks.py @@ -289,6 +289,15 @@ def trace(statement): con2.close() self.assertEqual(traced_statements, queries) + @with_tracebacks(["ZeroDivisionError", "division by zero"]) + def test_trace_bad_handler(self): + cx = sqlite.connect(":memory:") + cx.set_trace_callback(lambda stmt: 5/0) + self.assertRaisesRegex( + sqlite.OperationalError, "trace callback raised an exception", + cx.execute, "select 1" + ) + def suite(): tests = [ diff --git a/Misc/NEWS.d/next/Library/2021-09-02-23-06-56.bpo-45089.ZeU_ie.rst b/Misc/NEWS.d/next/Library/2021-09-02-23-06-56.bpo-45089.ZeU_ie.rst new file mode 100644 index 00000000000000..904c35843e1a43 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2021-09-02-23-06-56.bpo-45089.ZeU_ie.rst @@ -0,0 +1,2 @@ +The :mod:`sqlite3` trace callback now raise :exc:`~sqlite3.DataError` or +:exc:`~sqlite3.OperationalError` on error. Patch by Erlend E. Aasland. diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index 0780d41c5a0c39..ae1a534ff98146 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -1011,27 +1011,33 @@ trace_callback(void *ctx, const char *statement_string) PyGILState_STATE gilstate = PyGILState_Ensure(); - PyObject *py_statement = NULL; - PyObject *ret = NULL; - py_statement = PyUnicode_DecodeUTF8(statement_string, + PyObject *py_statement = PyUnicode_DecodeUTF8(statement_string, strlen(statement_string), "replace"); - if (py_statement) { - ret = PyObject_CallOneArg((PyObject*)ctx, py_statement); - Py_DECREF(py_statement); + if (py_statement == NULL) { + pysqlite_state *state = pysqlite_get_state(NULL); + if (state->enable_callback_tracebacks) { + PyErr_Print(); + } + PyErr_SetString(state->DataError, + "trace callback unable to fetch statement string"); + goto exit; } - if (ret) { - Py_DECREF(ret); - } else { + PyObject *ret = PyObject_CallOneArg((PyObject *)ctx, py_statement); + Py_DECREF(py_statement); + if (ret == NULL) { pysqlite_state *state = pysqlite_get_state(NULL); if (state->enable_callback_tracebacks) { PyErr_Print(); } - else { - PyErr_Clear(); - } + PyErr_SetString(state->OperationalError, + "trace callback raised an exception"); + } + else { + Py_DECREF(ret); } +exit: PyGILState_Release(gilstate); #ifdef HAVE_TRACE_V2 return 0;