diff --git a/Lib/test/test_sqlite3/test_hooks.py b/Lib/test/test_sqlite3/test_hooks.py index 495ef97fa3c61c..7e5117771b379f 100644 --- a/Lib/test/test_sqlite3/test_hooks.py +++ b/Lib/test/test_sqlite3/test_hooks.py @@ -120,6 +120,21 @@ def test_collation_register_twice(self): self.assertEqual(result[0][0], 'b') self.assertEqual(result[1][0], 'a') + def test_collation_register_when_busy(self): + # See https://github.com/python/cpython/issues/146090. + con = self.con + con.create_collation("mycoll", lambda x, y: (x > y) - (x < y)) + con.execute("CREATE TABLE t(x TEXT)") + con.execute("INSERT INTO t VALUES (?)", ("a",)) + con.execute("INSERT INTO t VALUES (?)", ("b",)) + con.commit() + + cursor = self.con.execute("SELECT x FROM t ORDER BY x COLLATE mycoll") + next(cursor) + # Replace the collation while the statement is active -> SQLITE_BUSY. + with self.assertRaises(sqlite.OperationalError) as cm: + self.con.create_collation("mycoll", lambda a, b: 0) + def test_deregister_collation(self): """ Register a collation, then deregister it. Make sure an error is raised if we try diff --git a/Misc/NEWS.d/next/Library/2026-03-28-12-01-48.gh-issue-146090.wh1qJR.rst b/Misc/NEWS.d/next/Library/2026-03-28-12-01-48.gh-issue-146090.wh1qJR.rst new file mode 100644 index 00000000000000..a6d60d2c929304 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-03-28-12-01-48.gh-issue-146090.wh1qJR.rst @@ -0,0 +1,2 @@ +:mod:`sqlite3`: properly raise :exc:`MemoryError` instead of :exc:`SystemError` +when a context callback fails to be allocated. Patch by Bénédikt Tran. diff --git a/Misc/NEWS.d/next/Library/2026-03-28-12-05-34.gh-issue-146090.wf9_ef.rst b/Misc/NEWS.d/next/Library/2026-03-28-12-05-34.gh-issue-146090.wf9_ef.rst new file mode 100644 index 00000000000000..5b835b0271a604 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-03-28-12-05-34.gh-issue-146090.wf9_ef.rst @@ -0,0 +1,3 @@ +:mod:`sqlite3`: fix a crash when :meth:`sqlite3.Connection.create_collation` +fails with `SQLITE_BUSY `__. Patch by +Bénédikt Tran. diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index af63271b9fd971..bd44ff31b87c67 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -1059,13 +1059,16 @@ static callback_context * create_callback_context(PyTypeObject *cls, PyObject *callable) { callback_context *ctx = PyMem_Malloc(sizeof(callback_context)); - if (ctx != NULL) { - PyObject *module = PyType_GetModule(cls); - ctx->refcount = 1; - ctx->callable = Py_NewRef(callable); - ctx->module = Py_NewRef(module); - ctx->state = pysqlite_get_state(module); + if (ctx == NULL) { + PyErr_NoMemory(); + return NULL; } + + PyObject *module = PyType_GetModule(cls); + ctx->refcount = 1; + ctx->callable = Py_NewRef(callable); + ctx->module = Py_NewRef(module); + ctx->state = pysqlite_get_state(module); return ctx; } @@ -2198,7 +2201,7 @@ pysqlite_connection_create_collation_impl(pysqlite_Connection *self, * the context before returning. */ if (callable != Py_None) { - free_callback_context(ctx); + decref_callback_context(ctx); } set_error_from_db(self->state, self->db); return NULL;