]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-146090: fix memory management of internal `sqlite3` callback contexts (#146569)
authorBénédikt Tran <10796600+picnixz@users.noreply.github.com>
Sun, 29 Mar 2026 12:21:37 +0000 (14:21 +0200)
committerGitHub <noreply@github.com>
Sun, 29 Mar 2026 12:21:37 +0000 (14:21 +0200)
Lib/test/test_sqlite3/test_hooks.py
Misc/NEWS.d/next/Library/2026-03-28-12-01-48.gh-issue-146090.wh1qJR.rst [new file with mode: 0644]
Misc/NEWS.d/next/Library/2026-03-28-12-05-34.gh-issue-146090.wf9_ef.rst [new file with mode: 0644]
Modules/_sqlite/connection.c

index 495ef97fa3c61c8ac029ca1bf224c8f36ae4e5d2..7e5117771b379f5f5fdfab74570d1b4a3330041c 100644 (file)
@@ -120,6 +120,21 @@ class CollationTests(MemoryDatabaseMixin, unittest.TestCase):
         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 (file)
index 0000000..a6d60d2
--- /dev/null
@@ -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 (file)
index 0000000..5b835b0
--- /dev/null
@@ -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.
index af63271b9fd9717ccad28bc43aed64452d403bca..bd44ff31b87c67be553e6b15bc09f1701065caa2 100644 (file)
@@ -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;