gh-152817: Prevent deletion of sqlite3 `cursor.row_factory` attr, missed from: gh-149738 (GH-152818)
serhiy-storchaka <[email protected]>
| Newsgroups | gmane.comp.python.cvs |
|---|---|
| Message-ID | <[email protected]> |
https://github.com/python/cpython/commit/8e96dd6f25b53f0773d3f3f2a898eaa1501ee92e commit: 8e96dd6f25b53f0773d3f3f2a898eaa1501ee92e branch: main author: Steve Stagg <[email protected]> committer: serhiy-storchaka <[email protected]> date: 2026-08-21T07:58:49Z summary: gh-152817: Prevent deletion of sqlite3 `cursor.row_factory` attr, missed from: gh-149738 (GH-152818) files: M Lib/test/test_sqlite3/test_factory.py M Misc/NEWS.d/next/Core_and_Builtins/2026-05-13-06-54-41.gh-issue-149738.4BLFoH.rst M Modules/_sqlite/cursor.c diff --git a/Lib/test/test_sqlite3/test_factory.py b/Lib/test/test_sqlite3/test_factory.py index b9b18fdee87226..2dd42921d31ddd 100644 --- a/Lib/test/test_sqlite3/test_factory.py +++ b/Lib/test/test_sqlite3/test_factory.py @@ -156,6 +156,14 @@ def test_delete_connection_text_factory(self): with self.assertRaises(AttributeError): del self.con.text_factory + def test_delete_cursor_row_factory(self): + # gh-149738: deleting row_factory should raise an exception + cur = self.con.cursor() + with self.assertRaises(AttributeError): + del cur.row_factory + # Executing a query here should succeed. + self.assertEqual(tuple(cur.execute("select 1").fetchone()), (1,)) + 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 index e62b681d716650..e1935555b09174 100644 --- 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 @@ -1,2 +1,2 @@ :mod:`sqlite3`: Disallow removing ``row_factory`` and ``text_factory`` attributes -of a connection to prevent a crash on a query. +of a connection or cursor to prevent a crash on a query. diff --git a/Modules/_sqlite/cursor.c b/Modules/_sqlite/cursor.c index 3778ccd32fe4c4..96fb3dc9e42d3a 100644 --- a/Modules/_sqlite/cursor.c +++ b/Modules/_sqlite/cursor.c @@ -1405,13 +1405,33 @@ static struct PyMemberDef cursor_members[] = {"description", _Py_T_OBJECT, offsetof(pysqlite_Cursor, description), Py_READONLY}, {"lastrowid", _Py_T_OBJECT, offsetof(pysqlite_Cursor, lastrowid), Py_READONLY}, {"rowcount", Py_T_LONG, offsetof(pysqlite_Cursor, rowcount), Py_READONLY}, - {"row_factory", _Py_T_OBJECT, offsetof(pysqlite_Cursor, row_factory), 0}, {"__weaklistoffset__", Py_T_PYSSIZET, offsetof(pysqlite_Cursor, in_weakreflist), Py_READONLY}, {NULL} }; +static PyObject * +cursor_get_row_factory(PyObject *op, void *Py_UNUSED(closure)) +{ + pysqlite_Cursor *self = _pysqlite_Cursor_CAST(op); + return Py_NewRef(self->row_factory); +} + +static int +cursor_set_row_factory(PyObject *op, PyObject *value, void *Py_UNUSED(closure)) +{ + pysqlite_Cursor *self = _pysqlite_Cursor_CAST(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 struct PyGetSetDef cursor_getsets[] = { _SQLITE3_CURSOR_ARRAYSIZE_GETSETDEF + {"row_factory", cursor_get_row_factory, cursor_set_row_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]