Skip to content
5 changes: 5 additions & 0 deletions Lib/test/test_sqlite3/test_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,11 @@ def test_sqlite_row_index(self):
with self.assertRaises(IndexError):
row[complex()] # index must be int or string

def test_delete_connection_row_factory(self):
# gh-149738: deleting row_factory should raise an exception
with self.assertRaises(AttributeError):
del self.con.row_factory
Comment thread
sepehr-rs marked this conversation as resolved.

def test_sqlite_row_index_unicode(self):
row = self.con.execute("select 1 as \xff").fetchone()
self.assertEqual(row["\xff"], 1)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix a segmentation fault in :mod:`sqlite3` when ``row_factory`` is deleted
and a query is executed afterward.
Comment thread
sepehr-rs marked this conversation as resolved.
Outdated
43 changes: 40 additions & 3 deletions Modules/_sqlite/connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,41 @@ _sqlite3.Connection.cursor as pysqlite_connection_cursor

Return a cursor for the connection.
[clinic start generated code]*/
Comment thread
sepehr-rs marked this conversation as resolved.
static PyObject *
connection_get_row_factory(pysqlite_Connection *self, void *closure)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those getsets must be using a critical section as well.

{
return Py_NewRef(self->row_factory);
}

static int
connection_set_row_factory(pysqlite_Connection *self, PyObject *value, void *closure)
{
if (value == NULL) {
PyErr_SetString(PyExc_AttributeError,
"cannot delete row_factory attribute");
return -1;
}
Py_XSETREF(self->row_factory, Py_NewRef(value));
return 0;
}

static PyObject *
connection_get_text_factory(pysqlite_Connection *self, void *closure)
{
return Py_NewRef(self->text_factory);
}

static int
connection_set_text_factory(pysqlite_Connection *self, PyObject *value, void *closure)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The signature is incorrect, it must be (PyObject*, PyObject*, void*) and you must cast self in the body.

{
if (value == NULL) {
PyErr_SetString(PyExc_AttributeError,
"cannot delete text_factory attribute");
return -1;
}
Py_XSETREF(self->text_factory, Py_NewRef(value));
return 0;
}

static PyObject *
pysqlite_connection_cursor_impl(pysqlite_Connection *self, PyObject *factory)
Expand All @@ -549,7 +584,7 @@ pysqlite_connection_cursor_impl(pysqlite_Connection *self, PyObject *factory)
return NULL;
}

if (cursor && self->row_factory != Py_None) {
if (cursor && self->row_factory && self->row_factory != Py_None) {
Comment thread
sepehr-rs marked this conversation as resolved.
Outdated
Py_INCREF(self->row_factory);
Py_XSETREF(((pysqlite_Cursor *)cursor)->row_factory, self->row_factory);
}
Expand Down Expand Up @@ -2620,6 +2655,10 @@ static PyGetSetDef connection_getset[] = {
{"in_transaction", pysqlite_connection_get_in_transaction, NULL},
{"autocommit", get_autocommit, set_autocommit},
{"__text_signature__", get_sig, NULL},
{"row_factory", (getter)connection_get_row_factory,
(setter)connection_set_row_factory},
{"text_factory", (getter)connection_get_text_factory,
(setter)connection_set_text_factory},
{NULL}
};

Expand Down Expand Up @@ -2667,8 +2706,6 @@ static struct PyMemberDef connection_members[] =
{"InternalError", _Py_T_OBJECT, offsetof(pysqlite_Connection, InternalError), Py_READONLY},
{"ProgrammingError", _Py_T_OBJECT, offsetof(pysqlite_Connection, ProgrammingError), Py_READONLY},
{"NotSupportedError", _Py_T_OBJECT, offsetof(pysqlite_Connection, NotSupportedError), Py_READONLY},
{"row_factory", _Py_T_OBJECT, offsetof(pysqlite_Connection, row_factory)},
{"text_factory", _Py_T_OBJECT, offsetof(pysqlite_Connection, text_factory)},
{NULL}
};

Expand Down
Loading