Skip to content

Commit a412ffd

Browse files
[3.15] gh-152817: Prevent deletion of sqlite3 cursor.row_factory attr (GH-152818) (GH-156160)
(cherry picked from commit 8e96dd6) Co-authored-by: Steve Stagg <stestagg@gmail.com>
1 parent 54ffa19 commit a412ffd

3 files changed

Lines changed: 31 additions & 1 deletion

File tree

‎Lib/test/test_sqlite3/test_factory.py‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,14 @@ def test_delete_connection_text_factory(self):
156156
with self.assertRaises(AttributeError):
157157
del self.con.text_factory
158158

159+
def test_delete_cursor_row_factory(self):
160+
# gh-149738: deleting row_factory should raise an exception
161+
cur = self.con.cursor()
162+
with self.assertRaises(AttributeError):
163+
del cur.row_factory
164+
# Executing a query here should succeed.
165+
self.assertEqual(tuple(cur.execute("select 1").fetchone()), (1,))
166+
159167
def test_sqlite_row_index_unicode(self):
160168
row = self.con.execute("select 1 as \xff").fetchone()
161169
self.assertEqual(row["\xff"], 1)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
:mod:`sqlite3`: Disallow removing the ``row_factory`` attribute of a cursor
2+
to prevent a crash on a query.

‎Modules/_sqlite/cursor.c‎

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1402,13 +1402,33 @@ static struct PyMemberDef cursor_members[] =
14021402
{"description", _Py_T_OBJECT, offsetof(pysqlite_Cursor, description), Py_READONLY},
14031403
{"lastrowid", _Py_T_OBJECT, offsetof(pysqlite_Cursor, lastrowid), Py_READONLY},
14041404
{"rowcount", Py_T_LONG, offsetof(pysqlite_Cursor, rowcount), Py_READONLY},
1405-
{"row_factory", _Py_T_OBJECT, offsetof(pysqlite_Cursor, row_factory), 0},
14061405
{"__weaklistoffset__", Py_T_PYSSIZET, offsetof(pysqlite_Cursor, in_weakreflist), Py_READONLY},
14071406
{NULL}
14081407
};
14091408

1409+
static PyObject *
1410+
cursor_get_row_factory(PyObject *op, void *Py_UNUSED(closure))
1411+
{
1412+
pysqlite_Cursor *self = _pysqlite_Cursor_CAST(op);
1413+
return Py_NewRef(self->row_factory);
1414+
}
1415+
1416+
static int
1417+
cursor_set_row_factory(PyObject *op, PyObject *value, void *Py_UNUSED(closure))
1418+
{
1419+
pysqlite_Cursor *self = _pysqlite_Cursor_CAST(op);
1420+
if (value == NULL) {
1421+
PyErr_SetString(PyExc_AttributeError,
1422+
"cannot delete row_factory attribute");
1423+
return -1;
1424+
}
1425+
Py_XSETREF(self->row_factory, Py_NewRef(value));
1426+
return 0;
1427+
}
1428+
14101429
static struct PyGetSetDef cursor_getsets[] = {
14111430
_SQLITE3_CURSOR_ARRAYSIZE_GETSETDEF
1431+
{"row_factory", cursor_get_row_factory, cursor_set_row_factory},
14121432
{NULL},
14131433
};
14141434

0 commit comments

Comments
 (0)