https://github.com/python/cpython/commit/d6daeeeb2fe4db94fc8aa2fd1bc4dc201a4d1541
commit: d6daeeeb2fe4db94fc8aa2fd1bc4dc201a4d1541
branch: 3.13
author: Erlend E. Aasland <[email protected]>
committer: erlend-aasland <[email protected]>
date: 2025-02-10T00:21:33Z
summary:
[3.13] gh-129603: Don't segfault if sqlite3.Row description is None (#129604)
(#129923)
(cherry picked from commit 7e6ee50b6b8c760bcefb92ab4ddbc3d85d37a834)
files:
A Misc/NEWS.d/next/Library/2025-02-03-01-43-16.gh-issue-129603.xge9Tx.rst
M Lib/test/test_sqlite3/test_dbapi.py
M Modules/_sqlite/row.c
diff --git a/Lib/test/test_sqlite3/test_dbapi.py
b/Lib/test/test_sqlite3/test_dbapi.py
index 51ce095df41fc1..83134020c031c0 100644
--- a/Lib/test/test_sqlite3/test_dbapi.py
+++ b/Lib/test/test_sqlite3/test_dbapi.py
@@ -1947,5 +1947,70 @@ def wait():
self.assertEqual(proc.returncode, 0)
+class RowTests(unittest.TestCase):
+
+ def setUp(self):
+ self.cx = sqlite.connect(":memory:")
+ self.cx.row_factory = sqlite.Row
+
+ def tearDown(self):
+ self.cx.close()
+
+ def test_row_keys(self):
+ cu = self.cx.execute("SELECT 1 as first, 2 as second")
+ row = cu.fetchone()
+ self.assertEqual(row.keys(), ["first", "second"])
+
+ def test_row_length(self):
+ cu = self.cx.execute("SELECT 1, 2, 3")
+ row = cu.fetchone()
+ self.assertEqual(len(row), 3)
+
+ def test_row_getitem(self):
+ cu = self.cx.execute("SELECT 1 as a, 2 as b")
+ row = cu.fetchone()
+ self.assertEqual(row[0], 1)
+ self.assertEqual(row[1], 2)
+ self.assertEqual(row["a"], 1)
+ self.assertEqual(row["b"], 2)
+ for key in "nokey", 4, 1.2:
+ with self.subTest(key=key):
+ with self.assertRaises(IndexError):
+ row[key]
+
+ def test_row_equality(self):
+ c1 = self.cx.execute("SELECT 1 as a")
+ r1 = c1.fetchone()
+
+ c2 = self.cx.execute("SELECT 1 as a")
+ r2 = c2.fetchone()
+
+ self.assertIsNot(r1, r2)
+ self.assertEqual(r1, r2)
+
+ c3 = self.cx.execute("SELECT 1 as b")
+ r3 = c3.fetchone()
+
+ self.assertNotEqual(r1, r3)
+
+ def test_row_no_description(self):
+ cu = self.cx.cursor()
+ self.assertIsNone(cu.description)
+
+ row = sqlite.Row(cu, ())
+ self.assertEqual(row.keys(), [])
+ with self.assertRaisesRegex(IndexError, "nokey"):
+ row["nokey"]
+
+ def test_row_is_a_sequence(self):
+ from collections.abc import Sequence
+
+ cu = self.cx.execute("SELECT 1")
+ row = cu.fetchone()
+
+ self.assertTrue(issubclass(sqlite.Row, Sequence))
+ self.assertTrue(isinstance(row, Sequence))
+
+
if __name__ == "__main__":
unittest.main()
diff --git
a/Misc/NEWS.d/next/Library/2025-02-03-01-43-16.gh-issue-129603.xge9Tx.rst
b/Misc/NEWS.d/next/Library/2025-02-03-01-43-16.gh-issue-129603.xge9Tx.rst
new file mode 100644
index 00000000000000..0d0ec21bddd28f
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-02-03-01-43-16.gh-issue-129603.xge9Tx.rst
@@ -0,0 +1,3 @@
+Fix bugs where :class:`sqlite3.Row` objects could segfault if their
+inherited :attr:`~sqlite3.Cursor.description` was set to ``None``. Patch by
+Erlend Aasland.
diff --git a/Modules/_sqlite/row.c b/Modules/_sqlite/row.c
index 14555076a7e79a..3427fae373d4fc 100644
--- a/Modules/_sqlite/row.c
+++ b/Modules/_sqlite/row.c
@@ -132,7 +132,6 @@ static PyObject *
pysqlite_row_subscript(pysqlite_Row *self, PyObject *idx)
{
Py_ssize_t _idx;
- Py_ssize_t nitems, i;
if (PyLong_Check(idx)) {
_idx = PyNumber_AsSsize_t(idx, PyExc_IndexError);
@@ -144,9 +143,13 @@ pysqlite_row_subscript(pysqlite_Row *self, PyObject *idx)
PyObject *item = PyTuple_GetItem(self->data, _idx);
return Py_XNewRef(item);
} else if (PyUnicode_Check(idx)) {
- nitems = PyTuple_Size(self->description);
+ if (Py_IsNone(self->description)) {
+ PyErr_Format(PyExc_IndexError, "No item with key %R", idx);
+ return NULL;
+ }
+ Py_ssize_t nitems = PyTuple_GET_SIZE(self->description);
- for (i = 0; i < nitems; i++) {
+ for (Py_ssize_t i = 0; i < nitems; i++) {
PyObject *obj;
obj = PyTuple_GET_ITEM(self->description, i);
obj = PyTuple_GET_ITEM(obj, 0);
@@ -189,17 +192,19 @@ static PyObject *
pysqlite_row_keys_impl(pysqlite_Row *self)
/*[clinic end generated code: output=efe3dfb3af6edc07 input=7549a122827c5563]*/
{
- PyObject* list;
- Py_ssize_t nitems, i;
-
- list = PyList_New(0);
+ PyObject *list = PyList_New(0);
if (!list) {
return NULL;
}
- nitems = PyTuple_Size(self->description);
+ if (Py_IsNone(self->description)) {
+ return list;
+ }
- for (i = 0; i < nitems; i++) {
- if (PyList_Append(list,
PyTuple_GET_ITEM(PyTuple_GET_ITEM(self->description, i), 0)) != 0) {
+ Py_ssize_t nitems = PyTuple_GET_SIZE(self->description);
+ for (Py_ssize_t i = 0; i < nitems; i++) {
+ PyObject *descr = PyTuple_GET_ITEM(self->description, i);
+ PyObject *name = PyTuple_GET_ITEM(descr, 0);
+ if (PyList_Append(list, name) < 0) {
Py_DECREF(list);
return NULL;
}
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]