https://github.com/python/cpython/commit/60fdb3192b897168ec0418fb0ea6c8d2d49ea513
commit: 60fdb3192b897168ec0418fb0ea6c8d2d49ea513
branch: main
author: Sepehr Rasouli <[email protected]>
committer: vstinner <[email protected]>
date: 2026-06-02T11:07:08+02:00
summary:
gh-149738: Fix segmentation fault bug in sqllite3 (#149754)
Deleting the `row_factory` or `text_factory` attribute is no longer allowed.
files:
A
Misc/NEWS.d/next/Core_and_Builtins/2026-05-13-06-54-41.gh-issue-149738.4BLFoH.rst
M Doc/library/sqlite3.rst
M Lib/test/test_sqlite3/test_factory.py
M Modules/_sqlite/connection.c
diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst
index 484260e63dd5f2f..3a75d44f3f7d21b 100644
--- a/Doc/library/sqlite3.rst
+++ b/Doc/library/sqlite3.rst
@@ -1417,6 +1417,9 @@ Connection objects
See :ref:`sqlite3-howto-row-factory` for more details.
+ .. versionchanged:: next
+ Deleting the ``row_factory`` attribute is no longer allowed.
+
.. attribute:: text_factory
A :term:`callable` that accepts a :class:`bytes` parameter
@@ -1426,6 +1429,9 @@ Connection objects
See :ref:`sqlite3-howto-encoding` for more details.
+ .. versionchanged:: next
+ Deleting the ``text_factory`` attribute is no longer allowed.
+
.. attribute:: total_changes
Return the total number of database rows that have been modified,
inserted, or
@@ -1709,6 +1715,9 @@ Cursor objects
See :ref:`sqlite3-howto-row-factory` for more details.
+ .. versionchanged:: next
+ Deleting the ``row_factory`` attribute is no longer allowed.
+
.. The sqlite3.Row example used to be a how-to. It has now been incorporated
into the Row reference. We keep the anchor here in order not to break
diff --git a/Lib/test/test_sqlite3/test_factory.py
b/Lib/test/test_sqlite3/test_factory.py
index 776659e3b161089..a9abeab31936880 100644
--- a/Lib/test/test_sqlite3/test_factory.py
+++ b/Lib/test/test_sqlite3/test_factory.py
@@ -146,6 +146,16 @@ def test_sqlite_row_index(self):
with self.assertRaises(IndexError):
row[complex()] # index must be int or string
+ def test_delete_connection_row_factory(self):
+ # gh-149738: deleting row_factory should raise an exception
+ with self.assertRaises(AttributeError):
+ del self.con.row_factory
+
+ def test_delete_connection_text_factory(self):
+ # gh-149738: deleting text_factory should raise an exception
+ with self.assertRaises(AttributeError):
+ del self.con.text_factory
+
def test_sqlite_row_index_unicode(self):
row = self.con.execute("select 1 as \xff").fetchone()
self.assertEqual(row["\xff"], 1)
diff --git
a/Misc/NEWS.d/next/Core_and_Builtins/2026-05-13-06-54-41.gh-issue-149738.4BLFoH.rst
b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-13-06-54-41.gh-issue-149738.4BLFoH.rst
new file mode 100644
index 000000000000000..e62b681d716650b
--- /dev/null
+++
b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-13-06-54-41.gh-issue-149738.4BLFoH.rst
@@ -0,0 +1,2 @@
+:mod:`sqlite3`: Disallow removing ``row_factory`` and ``text_factory``
attributes
+of a connection to prevent a crash on a query.
diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c
index f596cc1ab36a19c..892740b05e55c98 100644
--- a/Modules/_sqlite/connection.c
+++ b/Modules/_sqlite/connection.c
@@ -557,6 +557,47 @@ pysqlite_connection_cursor_impl(pysqlite_Connection *self,
PyObject *factory)
return cursor;
}
+static PyObject *
+connection_get_row_factory(PyObject *op, void *closure)
+{
+ pysqlite_Connection *self = (pysqlite_Connection *)op;
+ return Py_NewRef(self->row_factory);
+}
+
+static int
+connection_set_row_factory(PyObject *op, PyObject *value, void *closure)
+{
+ pysqlite_Connection *self = (pysqlite_Connection *)op;
+ if (value == NULL) {
+ PyErr_SetString(PyExc_AttributeError,
+ "cannot delete row_factory attribute");
+ return -1;
+ }
+ Py_XSETREF(self->row_factory, Py_NewRef(value));
+ return 0;
+}
+
+static PyObject *
+connection_get_text_factory(PyObject *op, void *closure)
+{
+ pysqlite_Connection *self = (pysqlite_Connection *)op;
+ return Py_NewRef(self->text_factory);
+}
+
+static int
+connection_set_text_factory(PyObject *op, PyObject *value, void *closure)
+{
+ pysqlite_Connection *self = (pysqlite_Connection *)op;
+ if (value == NULL) {
+ PyErr_SetString(PyExc_AttributeError,
+ "cannot delete text_factory attribute");
+ return -1;
+ }
+ Py_XSETREF(self->text_factory, Py_NewRef(value));
+ return 0;
+}
+
+
/*[clinic input]
_sqlite3.Connection.blobopen as blobopen
@@ -2620,6 +2661,10 @@ static PyGetSetDef connection_getset[] = {
{"in_transaction", pysqlite_connection_get_in_transaction, NULL},
{"autocommit", get_autocommit, set_autocommit},
{"__text_signature__", get_sig, NULL},
+ {"row_factory", connection_get_row_factory,
+ connection_set_row_factory},
+ {"text_factory", connection_get_text_factory,
+ connection_set_text_factory},
{NULL}
};
@@ -2667,8 +2712,6 @@ static struct PyMemberDef connection_members[] =
{"InternalError", _Py_T_OBJECT, offsetof(pysqlite_Connection,
InternalError), Py_READONLY},
{"ProgrammingError", _Py_T_OBJECT, offsetof(pysqlite_Connection,
ProgrammingError), Py_READONLY},
{"NotSupportedError", _Py_T_OBJECT, offsetof(pysqlite_Connection,
NotSupportedError), Py_READONLY},
- {"row_factory", _Py_T_OBJECT, offsetof(pysqlite_Connection, row_factory)},
- {"text_factory", _Py_T_OBJECT, offsetof(pysqlite_Connection,
text_factory)},
{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]