[Python-checkins] cpython (merge 3.5 -> default): Issue #27861: Fixed a crash in sqlite3.Connection.cursor() when a factory

serhiy.storchaka python-checkins at python.org
Mon Aug 29 08:12:25 EDT 2016


https://hg.python.org/cpython/rev/afcec2d11e9e
changeset:   102934:afcec2d11e9e
parent:      102931:8ec5a00e5d75
parent:      102932:c046cbb24f98
user:        Serhiy Storchaka <storchaka at gmail.com>
date:        Mon Aug 29 15:11:52 2016 +0300
summary:
  Issue #27861: Fixed a crash in sqlite3.Connection.cursor() when a factory
creates not a cursor.  Patch by Xiang Zhang.

files:
  Doc/library/sqlite3.rst      |   8 ++++----
  Lib/sqlite3/test/factory.py  |  20 +++++++++++++++++---
  Misc/NEWS                    |   3 +++
  Modules/_sqlite/connection.c |  13 +++++++++++--
  4 files changed, 35 insertions(+), 9 deletions(-)


diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst
--- a/Doc/library/sqlite3.rst
+++ b/Doc/library/sqlite3.rst
@@ -285,11 +285,11 @@
 
       .. 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
--- a/Lib/sqlite3/test/factory.py
+++ b/Lib/sqlite3/test/factory.py
@@ -58,8 +58,20 @@
         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):
@@ -183,10 +195,12 @@
     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
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -49,6 +49,9 @@
 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
--- a/Modules/_sqlite/connection.c
+++ b/Modules/_sqlite/connection.c
@@ -300,7 +300,7 @@
 
 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 @@
         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);
 

-- 
Repository URL: https://hg.python.org/cpython


More information about the Python-checkins mailing list