]> granicus.if.org Git - python/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 2b730e80928eaf34e8a4315afce31d44db013a1a..6b13172042b56f5cee6619310b150fefb07f7ab7 100644 (file)
@@ -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()
 
index 0e0196afec955164077ec2a32791f3cc33909507..b9e9cd7fd7f41318eb9aaabf5f5e77728aa183ed 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:")
@@ -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()
index e19cab42e556495ff6eaeeae3ed9f708da2e7179..1fd3d85e89cffc16f7b8fd907153bc7ac36d5cd4 100644 (file)
--- 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
index 78cc9be10eadb04c7e4a6c6e6127c2c8f1a2ed4f..24b39c159e8b5b2214b02628250790b6b2fff00a 100644 (file)
@@ -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);