From: Serhiy Storchaka Date: Mon, 29 Aug 2016 11:29:55 +0000 (+0300) Subject: Issue #27861: Fixed a crash in sqlite3.Connection.cursor() when a factory X-Git-Tag: v2.7.13rc1~193 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=e63af905a494478e57f7f8272a7b384e7a29c0a0;p=python Issue #27861: Fixed a crash in sqlite3.Connection.cursor() when a factory creates not a cursor. Patch by Xiang Zhang. --- diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index 2b730e8092..6b13172042 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -256,11 +256,11 @@ Connection Objects :ref:`sqlite3-controlling-transactions` for a more detailed explanation. - .. method:: cursor([cursorClass]) + .. method:: cursor(factory=Cursor) - The cursor method accepts a single optional parameter *cursorClass*. If - supplied, this must be a custom cursor class that extends - :class:`sqlite3.Cursor`. + The cursor method accepts a single optional parameter *factory*. If + supplied, this must be a callable returning an instance of :class:`Cursor` + or its subclasses. .. method:: commit() diff --git a/Lib/sqlite3/test/factory.py b/Lib/sqlite3/test/factory.py index 0e0196afec..b9e9cd7fd7 100644 --- a/Lib/sqlite3/test/factory.py +++ b/Lib/sqlite3/test/factory.py @@ -58,9 +58,21 @@ class CursorFactoryTests(unittest.TestCase): self.con.close() 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) + 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): def setUp(self): self.con = sqlite.connect(":memory:") @@ -173,10 +185,12 @@ class RowFactoryTests(unittest.TestCase): def CheckFakeCursorClass(self): # Issue #24257: Incorrect use of PyObject_IsInstance() caused # segmentation fault. + # Issue #27861: Also applies for cursor factory. class FakeCursor(str): __class__ = sqlite.Cursor - cur = self.con.cursor(factory=FakeCursor) - self.assertRaises(TypeError, sqlite.Row, cur, ()) + self.con.row_factory = sqlite.Row + self.assertRaises(TypeError, self.con.cursor, FakeCursor) + self.assertRaises(TypeError, sqlite.Row, FakeCursor(), ()) def tearDown(self): self.con.close() diff --git a/Misc/NEWS b/Misc/NEWS index e19cab42e5..1fd3d85e89 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -33,6 +33,9 @@ Core and Builtins 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 #10513: Fix a regression in Connection.commit(). Statements should diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index 78cc9be10e..24b39c159e 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -324,7 +324,7 @@ error: 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* cursor; @@ -341,7 +341,16 @@ PyObject* pysqlite_connection_cursor(pysqlite_Connection* self, PyObject* args, 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);