Skip to content

Commit 7e6ee50

Browse files
gh-129603: Don't segfault if sqlite3.Row description is None (#129604)
1 parent d05053a commit 7e6ee50

File tree

3 files changed

+83
-10
lines changed

3 files changed

+83
-10
lines changed

Lib/test/test_sqlite3/test_dbapi.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1924,5 +1924,70 @@ def wait():
19241924
self.assertEqual(proc.returncode, 0)
19251925

19261926

1927+
class RowTests(unittest.TestCase):
1928+
1929+
def setUp(self):
1930+
self.cx = sqlite.connect(":memory:")
1931+
self.cx.row_factory = sqlite.Row
1932+
1933+
def tearDown(self):
1934+
self.cx.close()
1935+
1936+
def test_row_keys(self):
1937+
cu = self.cx.execute("SELECT 1 as first, 2 as second")
1938+
row = cu.fetchone()
1939+
self.assertEqual(row.keys(), ["first", "second"])
1940+
1941+
def test_row_length(self):
1942+
cu = self.cx.execute("SELECT 1, 2, 3")
1943+
row = cu.fetchone()
1944+
self.assertEqual(len(row), 3)
1945+
1946+
def test_row_getitem(self):
1947+
cu = self.cx.execute("SELECT 1 as a, 2 as b")
1948+
row = cu.fetchone()
1949+
self.assertEqual(row[0], 1)
1950+
self.assertEqual(row[1], 2)
1951+
self.assertEqual(row["a"], 1)
1952+
self.assertEqual(row["b"], 2)
1953+
for key in "nokey", 4, 1.2:
1954+
with self.subTest(key=key):
1955+
with self.assertRaises(IndexError):
1956+
row[key]
1957+
1958+
def test_row_equality(self):
1959+
c1 = self.cx.execute("SELECT 1 as a")
1960+
r1 = c1.fetchone()
1961+
1962+
c2 = self.cx.execute("SELECT 1 as a")
1963+
r2 = c2.fetchone()
1964+
1965+
self.assertIsNot(r1, r2)
1966+
self.assertEqual(r1, r2)
1967+
1968+
c3 = self.cx.execute("SELECT 1 as b")
1969+
r3 = c3.fetchone()
1970+
1971+
self.assertNotEqual(r1, r3)
1972+
1973+
def test_row_no_description(self):
1974+
cu = self.cx.cursor()
1975+
self.assertIsNone(cu.description)
1976+
1977+
row = sqlite.Row(cu, ())
1978+
self.assertEqual(row.keys(), [])
1979+
with self.assertRaisesRegex(IndexError, "nokey"):
1980+
row["nokey"]
1981+
1982+
def test_row_is_a_sequence(self):
1983+
from collections.abc import Sequence
1984+
1985+
cu = self.cx.execute("SELECT 1")
1986+
row = cu.fetchone()
1987+
1988+
self.assertIsSubclass(sqlite.Row, Sequence)
1989+
self.assertIsInstance(row, Sequence)
1990+
1991+
19271992
if __name__ == "__main__":
19281993
unittest.main()
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix bugs where :class:`sqlite3.Row` objects could segfault if their
2+
inherited :attr:`~sqlite3.Cursor.description` was set to ``None``. Patch by
3+
Erlend Aasland.

Modules/_sqlite/row.c

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,6 @@ static PyObject *
138138
pysqlite_row_subscript(PyObject *op, PyObject *idx)
139139
{
140140
Py_ssize_t _idx;
141-
Py_ssize_t nitems, i;
142141
pysqlite_Row *self = _pysqlite_Row_CAST(op);
143142

144143
if (PyLong_Check(idx)) {
@@ -151,9 +150,13 @@ pysqlite_row_subscript(PyObject *op, PyObject *idx)
151150
PyObject *item = PyTuple_GetItem(self->data, _idx);
152151
return Py_XNewRef(item);
153152
} else if (PyUnicode_Check(idx)) {
154-
nitems = PyTuple_Size(self->description);
153+
if (Py_IsNone(self->description)) {
154+
PyErr_Format(PyExc_IndexError, "No item with key %R", idx);
155+
return NULL;
156+
}
157+
Py_ssize_t nitems = PyTuple_GET_SIZE(self->description);
155158

156-
for (i = 0; i < nitems; i++) {
159+
for (Py_ssize_t i = 0; i < nitems; i++) {
157160
PyObject *obj;
158161
obj = PyTuple_GET_ITEM(self->description, i);
159162
obj = PyTuple_GET_ITEM(obj, 0);
@@ -197,17 +200,19 @@ static PyObject *
197200
pysqlite_row_keys_impl(pysqlite_Row *self)
198201
/*[clinic end generated code: output=efe3dfb3af6edc07 input=7549a122827c5563]*/
199202
{
200-
PyObject* list;
201-
Py_ssize_t nitems, i;
202-
203-
list = PyList_New(0);
203+
PyObject *list = PyList_New(0);
204204
if (!list) {
205205
return NULL;
206206
}
207-
nitems = PyTuple_Size(self->description);
207+
if (Py_IsNone(self->description)) {
208+
return list;
209+
}
208210

209-
for (i = 0; i < nitems; i++) {
210-
if (PyList_Append(list, PyTuple_GET_ITEM(PyTuple_GET_ITEM(self->description, i), 0)) != 0) {
211+
Py_ssize_t nitems = PyTuple_GET_SIZE(self->description);
212+
for (Py_ssize_t i = 0; i < nitems; i++) {
213+
PyObject *descr = PyTuple_GET_ITEM(self->description, i);
214+
PyObject *name = PyTuple_GET_ITEM(descr, 0);
215+
if (PyList_Append(list, name) < 0) {
211216
Py_DECREF(list);
212217
return NULL;
213218
}

0 commit comments

Comments
 (0)