Commit ef113cd4 authored by Serhiy Storchaka's avatar Serhiy Storchaka

Issue #27861: Fixed a crash in sqlite3.Connection.cursor() when a factory

creates not a cursor.  Patch by Xiang Zhang.
parent 5de141f1
...@@ -285,11 +285,11 @@ Connection Objects ...@@ -285,11 +285,11 @@ Connection Objects
.. versionadded:: 3.2 .. versionadded:: 3.2
.. method:: cursor([cursorClass]) .. method:: cursor(factory=Cursor)
The cursor method accepts a single optional parameter *cursorClass*. If The cursor method accepts a single optional parameter *factory*. If
supplied, this must be a custom cursor class that extends supplied, this must be a callable returning an instance of :class:`Cursor`
:class:`sqlite3.Cursor`. or its subclasses.
.. method:: commit() .. method:: commit()
......
...@@ -58,9 +58,21 @@ class CursorFactoryTests(unittest.TestCase): ...@@ -58,9 +58,21 @@ class CursorFactoryTests(unittest.TestCase):
self.con.close() self.con.close()
def CheckIsInstance(self): def CheckIsInstance(self):
cur = self.con.cursor(factory=MyCursor) cur = self.con.cursor()
self.assertIsInstance(cur, sqlite.Cursor)
cur = self.con.cursor(MyCursor)
self.assertIsInstance(cur, MyCursor)
cur = self.con.cursor(factory=lambda con: MyCursor(con))
self.assertIsInstance(cur, MyCursor) self.assertIsInstance(cur, MyCursor)
def CheckInvalidFactory(self):
# not a callable at all
self.assertRaises(TypeError, self.con.cursor, None)
# invalid callable with not exact one argument
self.assertRaises(TypeError, self.con.cursor, lambda: None)
# invalid callable returning non-cursor
self.assertRaises(TypeError, self.con.cursor, lambda con: None)
class RowFactoryTestsBackwardsCompat(unittest.TestCase): class RowFactoryTestsBackwardsCompat(unittest.TestCase):
def setUp(self): def setUp(self):
self.con = sqlite.connect(":memory:") self.con = sqlite.connect(":memory:")
...@@ -183,10 +195,12 @@ class RowFactoryTests(unittest.TestCase): ...@@ -183,10 +195,12 @@ class RowFactoryTests(unittest.TestCase):
def CheckFakeCursorClass(self): def CheckFakeCursorClass(self):
# Issue #24257: Incorrect use of PyObject_IsInstance() caused # Issue #24257: Incorrect use of PyObject_IsInstance() caused
# segmentation fault. # segmentation fault.
# Issue #27861: Also applies for cursor factory.
class FakeCursor(str): class FakeCursor(str):
__class__ = sqlite.Cursor __class__ = sqlite.Cursor
cur = self.con.cursor(factory=FakeCursor) self.con.row_factory = sqlite.Row
self.assertRaises(TypeError, sqlite.Row, cur, ()) self.assertRaises(TypeError, self.con.cursor, FakeCursor)
self.assertRaises(TypeError, sqlite.Row, FakeCursor(), ())
def tearDown(self): def tearDown(self):
self.con.close() self.con.close()
......
...@@ -50,6 +50,9 @@ Core and Builtins ...@@ -50,6 +50,9 @@ Core and Builtins
Library Library
------- -------
- Issue #27861: Fixed a crash in sqlite3.Connection.cursor() when a factory
creates not a cursor. Patch by Xiang Zhang.
- Issue #19884: Avoid spurious output on OS X with Gnu Readline. - Issue #19884: Avoid spurious output on OS X with Gnu Readline.
- Issue #10513: Fix a regression in Connection.commit(). Statements should - Issue #10513: Fix a regression in Connection.commit(). Statements should
......
...@@ -300,7 +300,7 @@ error: ...@@ -300,7 +300,7 @@ error:
PyObject* pysqlite_connection_cursor(pysqlite_Connection* self, PyObject* args, PyObject* kwargs) PyObject* pysqlite_connection_cursor(pysqlite_Connection* self, PyObject* args, PyObject* kwargs)
{ {
static char *kwlist[] = {"factory", NULL, NULL}; static char *kwlist[] = {"factory", NULL};
PyObject* factory = NULL; PyObject* factory = NULL;
PyObject* cursor; PyObject* cursor;
...@@ -317,7 +317,16 @@ PyObject* pysqlite_connection_cursor(pysqlite_Connection* self, PyObject* args, ...@@ -317,7 +317,16 @@ PyObject* pysqlite_connection_cursor(pysqlite_Connection* self, PyObject* args,
factory = (PyObject*)&pysqlite_CursorType; factory = (PyObject*)&pysqlite_CursorType;
} }
cursor = PyObject_CallFunction(factory, "O", self); cursor = PyObject_CallFunctionObjArgs(factory, (PyObject *)self, NULL);
if (cursor == NULL)
return NULL;
if (!PyObject_TypeCheck(cursor, &pysqlite_CursorType)) {
PyErr_Format(PyExc_TypeError,
"factory must return a cursor, not %.100s",
Py_TYPE(cursor)->tp_name);
Py_DECREF(cursor);
return NULL;
}
_pysqlite_drop_unused_cursor_references(self); _pysqlite_drop_unused_cursor_references(self);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment