https://github.com/python/cpython/commit/6fdf0846449064c2f5bec18296163e19ee09121e
commit: 6fdf0846449064c2f5bec18296163e19ee09121e
branch: 3.13
author: Miss Islington (bot) <[email protected]>
committer: picnixz <[email protected]>
date: 2026-03-29T15:14:58+02:00
summary:

[3.13] gh-146090: fix memory management of internal `sqlite3` callback contexts 
(GH-146569) (#146596)

gh-146090: fix memory management of internal `sqlite3` callback contexts 
(GH-146569)
(cherry picked from commit aa6680775d6d9ca571a675c3b2d655f4ade78c0c)

Co-authored-by: Bénédikt Tran <[email protected]>

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 e807cf786f89dd..066ec8257ebe5f 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 b83dc9ae0cca1b..5f888ac19ec24d 100644
--- a/Modules/_sqlite/connection.c
+++ b/Modules/_sqlite/connection.c
@@ -1103,13 +1103,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;
 }
 
@@ -2212,7 +2215,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);
         }
         _pysqlite_seterror(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]

Reply via email to