Skip to content

Commit 8e96dd6

Browse files
authored
gh-152817: Prevent deletion of sqlite3 cursor.row_factory attr, missed from: gh-149738 (GH-152818)
1 parent b9d9c3f commit 8e96dd6

3 files changed

Lines changed: 30 additions & 2 deletions

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: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
:mod:`sqlite3`: Disallow removing ``row_factory`` and ``text_factory`` attributes
2-
of a connection to prevent a crash on a query.
2+
of a connection or cursor 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
@@ -1405,13 +1405,33 @@ static struct PyMemberDef cursor_members[] =
14051405
{"description", _Py_T_OBJECT, offsetof(pysqlite_Cursor, description), Py_READONLY},
14061406
{"lastrowid", _Py_T_OBJECT, offsetof(pysqlite_Cursor, lastrowid), Py_READONLY},
14071407
{"rowcount", Py_T_LONG, offsetof(pysqlite_Cursor, rowcount), Py_READONLY},
1408-
{"row_factory", _Py_T_OBJECT, offsetof(pysqlite_Cursor, row_factory), 0},
14091408
{"__weaklistoffset__", Py_T_PYSSIZET, offsetof(pysqlite_Cursor, in_weakreflist), Py_READONLY},
14101409
{NULL}
14111410
};
14121411

1412+
static PyObject *
1413+
cursor_get_row_factory(PyObject *op, void *Py_UNUSED(closure))
1414+
{
1415+
pysqlite_Cursor *self = _pysqlite_Cursor_CAST(op);
1416+
return Py_NewRef(self->row_factory);
1417+
}
1418+
1419+
static int
1420+
cursor_set_row_factory(PyObject *op, PyObject *value, void *Py_UNUSED(closure))
1421+
{
1422+
pysqlite_Cursor *self = _pysqlite_Cursor_CAST(op);
1423+
if (value == NULL) {
1424+
PyErr_SetString(PyExc_AttributeError,
1425+
"cannot delete row_factory attribute");
1426+
return -1;
1427+
}
1428+
Py_XSETREF(self->row_factory, Py_NewRef(value));
1429+
return 0;
1430+
}
1431+
14131432
static struct PyGetSetDef cursor_getsets[] = {
14141433
_SQLITE3_CURSOR_ARRAYSIZE_GETSETDEF
1434+
{"row_factory", cursor_get_row_factory, cursor_set_row_factory},
14151435
{NULL},
14161436
};
14171437

0 commit comments

Comments
 (0)