Skip to content

Commit 029ca68

Browse files
stestaggserhiy-storchaka
authored andcommitted
gh-152817: Prevent deletion of sqlite3 cursor.row_factory attr, missed from: gh-149738 (GH-152818)
(cherry picked from commit 8e96dd6)
1 parent f71fbc5 commit 029ca68

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
@@ -1400,13 +1400,33 @@ static struct PyMemberDef cursor_members[] =
14001400
{"description", _Py_T_OBJECT, offsetof(pysqlite_Cursor, description), Py_READONLY},
14011401
{"lastrowid", _Py_T_OBJECT, offsetof(pysqlite_Cursor, lastrowid), Py_READONLY},
14021402
{"rowcount", Py_T_LONG, offsetof(pysqlite_Cursor, rowcount), Py_READONLY},
1403-
{"row_factory", _Py_T_OBJECT, offsetof(pysqlite_Cursor, row_factory), 0},
14041403
{"__weaklistoffset__", Py_T_PYSSIZET, offsetof(pysqlite_Cursor, in_weakreflist), Py_READONLY},
14051404
{NULL}
14061405
};
14071406

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

0 commit comments

Comments
 (0)