https://github.com/python/cpython/commit/aa6680775d6d9ca571a675c3b2d655f4ade78c0c
commit: aa6680775d6d9ca571a675c3b2d655f4ade78c0c
branch: main
author: Bénédikt Tran <[email protected]>
committer: picnixz <[email protected]>
date: 2026-03-29T14:21:37+02:00
summary:
gh-146090: fix memory management of internal `sqlite3` callback contexts
(#146569)
files:
A Misc/NEWS.d/next/Library/2026-03-28-12-01-48.gh-issue-146090.wh1qJR.rst
A Misc/NEWS.d/next/Library/2026-03-28-12-05-34.gh-issue-146090.wf9_ef.rst
M Lib/test/test_sqlite3/test_hooks.py
M Modules/_sqlite/connection.c
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 <https://sqlite.org/rescode.html#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;
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]