]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Issue #27861: Fixed a crash in sqlite3.Connection.cursor() when a factory
authorSerhiy Storchaka <storchaka@gmail.com>
Mon, 29 Aug 2016 11:29:55 +0000 (14:29 +0300)
committerSerhiy Storchaka <storchaka@gmail.com>
Mon, 29 Aug 2016 11:29:55 +0000 (14:29 +0300)
creates not a cursor.  Patch by Xiang Zhang.

Doc/library/sqlite3.rst
Lib/sqlite3/test/factory.py
Misc/NEWS
Modules/_sqlite/connection.c

index d8fd6e764c7e22aa61054f2cdbcf0ae73274baa3..5b222c7aba3ecef5a1dc22830de30c92eaf2984b 100644 (file)
@@ -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()
 
index f587596f644880cab5b3d11f0e228014937319b6..ced8445536e3c69ddabf9283aaa62d546e859e2a 100644 (file)
@@ -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()
index e85587ef20d1b4d1808ab9a81dd9688f4dc3487e..129e124c928b0ac86b1fd268be54db5bf3c2809b 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -50,6 +50,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
index fcc1695ed06fa28ba6fe1faa47e9a9762fe428c5..bea930d129ac3c1513f411dda48ef29614680deb 100644 (file)
@@ -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);