summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSerhiy Storchaka <storchaka@gmail.com>2016-08-29 12:11:52 (GMT)
committerSerhiy Storchaka <storchaka@gmail.com>2016-08-29 12:11:52 (GMT)
commit8631da64bb1f163026b7be82884f8e5b506e0e66 (patch)
tree8e038d5acc352dfa2d74fa8f476e3017d0c98c97
parentd0f5bab21b9828f4764bd45e4afe24f23c1a55b0 (diff)
parentef113cd4cc2c16fa0ce59e325ee587698d62e1ae (diff)
downloadcpython-8631da64bb1f163026b7be82884f8e5b506e0e66.zip
cpython-8631da64bb1f163026b7be82884f8e5b506e0e66.tar.gz
cpython-8631da64bb1f163026b7be82884f8e5b506e0e66.tar.bz2
Issue #27861: Fixed a crash in sqlite3.Connection.cursor() when a factory
creates not a cursor. Patch by Xiang Zhang.
-rw-r--r--Doc/library/sqlite3.rst8
-rw-r--r--Lib/sqlite3/test/factory.py20
-rw-r--r--Misc/NEWS3
-rw-r--r--Modules/_sqlite/connection.c13
4 files changed, 35 insertions, 9 deletions
diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst
index 1498ead..c4d4d78 100644
--- a/Doc/library/sqlite3.rst
+++ b/Doc/library/sqlite3.rst
@@ -285,11 +285,11 @@ Connection Objects
.. versionadded:: 3.2
- .. 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 f587596..ced8445 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:")
@@ -183,10 +195,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 8ae30e2..519a787 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -49,6 +49,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 1841c768..1889dcd 100644
--- a/Modules/_sqlite/connection.c
+++ b/Modules/_sqlite/connection.c
@@ -300,7 +300,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;
@@ -317,7 +317,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);