https://github.com/python/cpython/commit/65e149a15f31def0b7c44bf087b69428b615bcc3
commit: 65e149a15f31def0b7c44bf087b69428b615bcc3
branch: main
author: cui fliter <[email protected]>
committer: kumaraditya303 <[email protected]>
date: 2026-08-21T20:42:29+05:30
summary:

gh-155781: Hold strong references to sqlite3 converters (#155784)

files:
A Lib/test/test_free_threading/test_sqlite3.py
A 
Misc/NEWS.d/next/Library/2026-08-14-11-12-18.gh-issue-155781.sqlite-converter.rst
M Modules/_sqlite/cursor.c

diff --git a/Lib/test/test_free_threading/test_sqlite3.py 
b/Lib/test/test_free_threading/test_sqlite3.py
new file mode 100644
index 00000000000000..456771726f7a8f
--- /dev/null
+++ b/Lib/test/test_free_threading/test_sqlite3.py
@@ -0,0 +1,54 @@
+import unittest
+
+from test import support
+from test.support import import_helper, threading_helper
+from test.support.threading_helper import run_concurrently
+
+
+sqlite3 = import_helper.import_module("sqlite3")
+
+
+NAME = "FREE_THREADING_RACE"
+NCOLUMNS = 64
+NITER = 3000
+QUERY = "select " + ", ".join(
+    f"'value' as 'c{i} [{NAME}]'" for i in range(NCOLUMNS)
+)
+
+
+@threading_helper.requires_working_threading()
+class TestSQLite3(unittest.TestCase):
+    def test_concurrent_converter_replacement(self):
+        # gh-155781: Converter lookups must retain a strong reference while
+        # another thread updates the public converter registry.
+        class Converter:
+            __slots__ = ("value",)
+
+            def __init__(self, value):
+                self.value = value
+
+            def __call__(self, value):
+                return self.value
+
+        def reader():
+            con = sqlite3.connect(":memory:",
+                                  detect_types=sqlite3.PARSE_COLNAMES)
+            try:
+                for _ in range(NITER):
+                    row = con.execute(QUERY).fetchone()
+                    self.assertEqual(len(row), NCOLUMNS)
+            finally:
+                con.close()
+
+        def mutator():
+            for i in range(NITER * NCOLUMNS):
+                sqlite3.register_converter(NAME, Converter(i))
+                if i % 3 == 0:
+                    sqlite3.converters.pop(NAME, None)
+
+        with support.swap_item(sqlite3.converters, NAME, Converter(0)):
+            run_concurrently([reader] * 8 + [mutator] * 2)
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git 
a/Misc/NEWS.d/next/Library/2026-08-14-11-12-18.gh-issue-155781.sqlite-converter.rst
 
b/Misc/NEWS.d/next/Library/2026-08-14-11-12-18.gh-issue-155781.sqlite-converter.rst
new file mode 100644
index 00000000000000..cd6655899d5955
--- /dev/null
+++ 
b/Misc/NEWS.d/next/Library/2026-08-14-11-12-18.gh-issue-155781.sqlite-converter.rst
@@ -0,0 +1,3 @@
+Fix a possible crash in free-threaded builds when the :mod:`sqlite3`
+converter registry is modified concurrently while a cursor builds its row
+cast map.
diff --git a/Modules/_sqlite/cursor.c b/Modules/_sqlite/cursor.c
index 96fb3dc9e42d3a..dd4204ff267d0a 100644
--- a/Modules/_sqlite/cursor.c
+++ b/Modules/_sqlite/cursor.c
@@ -223,8 +223,11 @@ _pysqlite_get_converter(pysqlite_state *state, const char 
*keystr,
         return NULL;
     }
 
-    retval = PyDict_GetItemWithError(state->converters, upcase_key);
+    int rc = PyDict_GetItemRef(state->converters, upcase_key, &retval);
     Py_DECREF(upcase_key);
+    if (rc < 0) {
+        return NULL;
+    }
 
     return retval;
 }
@@ -296,11 +299,10 @@ pysqlite_build_row_cast_map(pysqlite_Cursor* self)
             }
         }
 
-        if (!converter) {
-            converter = Py_None;
-        }
-
-        if (PyList_Append(self->row_cast_map, converter) != 0) {
+        int rc = PyList_Append(self->row_cast_map,
+                               converter ? converter : Py_None);
+        Py_XDECREF(converter);
+        if (rc != 0) {
             Py_CLEAR(self->row_cast_map);
             return -1;
         }

_______________________________________________
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