Skip to content

Commit 8288442

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

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
@@ -165,6 +165,14 @@ def test_delete_connection_text_factory(self):
165165
with self.assertRaises(AttributeError):
166166
del self.con.text_factory
167167

168+
def test_delete_cursor_row_factory(self):
169+
# gh-149738: deleting row_factory should raise an exception
170+
cur = self.con.cursor()
171+
with self.assertRaises(AttributeError):
172+
del cur.row_factory
173+
# Executing a query here should succeed.
174+
self.assertEqual(tuple(cur.execute("select 1").fetchone()), (1,))
175+
168176
def test_sqlite_row_index_unicode(self):
169177
row = self.con.execute("select 1 as \xff").fetchone()
170178
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
@@ -1348,13 +1348,33 @@ static struct PyMemberDef cursor_members[] =
13481348
{"description", _Py_T_OBJECT, offsetof(pysqlite_Cursor, description), Py_READONLY},
13491349
{"lastrowid", _Py_T_OBJECT, offsetof(pysqlite_Cursor, lastrowid), Py_READONLY},
13501350
{"rowcount", Py_T_LONG, offsetof(pysqlite_Cursor, rowcount), Py_READONLY},
1351-
{"row_factory", _Py_T_OBJECT, offsetof(pysqlite_Cursor, row_factory), 0},
13521351
{"__weaklistoffset__", Py_T_PYSSIZET, offsetof(pysqlite_Cursor, in_weakreflist), Py_READONLY},
13531352
{NULL}
13541353
};
13551354

1355+
static PyObject *
1356+
cursor_get_row_factory(PyObject *op, void *Py_UNUSED(closure))
1357+
{
1358+
pysqlite_Cursor *self = _pysqlite_Cursor_CAST(op);
1359+
return Py_NewRef(self->row_factory);
1360+
}
1361+
1362+
static int
1363+
cursor_set_row_factory(PyObject *op, PyObject *value, void *Py_UNUSED(closure))
1364+
{
1365+
pysqlite_Cursor *self = _pysqlite_Cursor_CAST(op);
1366+
if (value == NULL) {
1367+
PyErr_SetString(PyExc_AttributeError,
1368+
"cannot delete row_factory attribute");
1369+
return -1;
1370+
}
1371+
Py_XSETREF(self->row_factory, Py_NewRef(value));
1372+
return 0;
1373+
}
1374+
13561375
static struct PyGetSetDef cursor_getsets[] = {
13571376
_SQLITE3_CURSOR_ARRAYSIZE_GETSETDEF
1377+
{"row_factory", cursor_get_row_factory, cursor_set_row_factory},
13581378
{NULL},
13591379
};
13601380

0 commit comments

Comments
 (0)