diff --git a/Lib/sqlite3/test/regression.py b/Lib/sqlite3/test/regression.py index 417a53109c87c7..b206e2e48f0493 100644 --- a/Lib/sqlite3/test/regression.py +++ b/Lib/sqlite3/test/regression.py @@ -415,9 +415,44 @@ def test_return_empty_bytestring(self): self.assertEqual(val, b'') +class ConverterProgrammingErrorTestCase(unittest.TestCase): + def setUp(self): + self.con = sqlite.connect(':memory:', detect_types=sqlite.PARSE_COLNAMES) + self.cur = self.con.cursor() + self.cur.execute('create table test(x foo)') + + sqlite.converters['CURSOR_INIT'] = lambda x: self.cur.__init__(self.con) + sqlite.converters['CURSOR_CLOSE'] = lambda x: self.cur.close() + sqlite.converters['CURSOR_ITER'] = lambda x, l=[]: self.cur.fetchone() if l else l.append(None) + + def tearDown(self): + del sqlite.converters['CURSOR_INIT'] + del sqlite.converters['CURSOR_CLOSE'] + del sqlite.converters['CURSOR_ITER'] + self.cur.close() + self.con.close() + + def test_cursor_init(self): + self.cur.execute('insert into test(x) values (?)', ('foo',)) + with self.assertRaises(sqlite.ProgrammingError): + self.cur.execute('select x as "x [CURSOR_INIT]", x from test') + + def test_cursor_close(self): + self.cur.execute('insert into test(x) values (?)', ('foo',)) + with self.assertRaises(sqlite.ProgrammingError): + self.cur.execute('select x as "x [CURSOR_CLOSE]", x from test') + + def test_cursor_iter(self): + self.cur.executemany('insert into test(x) values (?)', (('foo',),) * 2) + self.cur.execute('select x as "x [CURSOR_ITER]", x from test') + with self.assertRaises(sqlite.ProgrammingError): + self.cur.fetchone() + + def suite(): tests = [ - RegressionTests + RegressionTests, + ConverterProgrammingErrorTestCase, ] return unittest.TestSuite( [unittest.TestLoader().loadTestsFromTestCase(t) for t in tests] diff --git a/Misc/NEWS.d/next/Library/2019-06-22-11-01-45.bpo-36073.ED8mB9.rst b/Misc/NEWS.d/next/Library/2019-06-22-11-01-45.bpo-36073.ED8mB9.rst new file mode 100644 index 00000000000000..bee0168dd52318 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-06-22-11-01-45.bpo-36073.ED8mB9.rst @@ -0,0 +1,2 @@ +Raise :exc:`~sqlite3.ProgrammingError` on recursive usage of cursors in +:mod:`sqlite3` converters. Patch by Sergey Fedoseev. diff --git a/Modules/_sqlite/cursor.c b/Modules/_sqlite/cursor.c index dfaa5577ab4081..31698ac47ea4b5 100644 --- a/Modules/_sqlite/cursor.c +++ b/Modules/_sqlite/cursor.c @@ -26,6 +26,19 @@ #include "util.h" #include "clinic/cursor.c.h" +static int +check_cursor_locked(pysqlite_Cursor* cur) +{ + if (cur->locked) { + PyErr_SetString( + pysqlite_ProgrammingError, + "Recursive use of cursors not allowed." + ); + return 0; + } + return 1; +} + /*[clinic input] module _sqlite3 class _sqlite3.Cursor "pysqlite_Cursor *" "pysqlite_CursorType" @@ -47,6 +60,10 @@ pysqlite_cursor_init_impl(pysqlite_Cursor *self, pysqlite_Connection *connection) /*[clinic end generated code: output=ac59dce49a809ca8 input=a8a4f75ac90999b2]*/ { + if (!check_cursor_locked(self)) { + return -1; + } + Py_INCREF(connection); Py_XSETREF(self->connection, connection); Py_CLEAR(self->statement); @@ -384,12 +401,9 @@ static int check_cursor(pysqlite_Cursor* cur) return 0; } - if (cur->locked) { - PyErr_SetString(pysqlite_ProgrammingError, "Recursive use of cursors not allowed."); - return 0; - } - - return pysqlite_check_thread(cur->connection) && pysqlite_check_connection(cur->connection); + return check_cursor_locked(cur) && + pysqlite_check_thread(cur->connection) && + pysqlite_check_connection(cur->connection); } static PyObject * @@ -811,7 +825,9 @@ pysqlite_cursor_iternext(pysqlite_Cursor *self) } if (rc == SQLITE_ROW) { + self->locked = 1; self->next_row = _pysqlite_fetch_one_row(self); + self->locked = 0; if (self->next_row == NULL) { (void)pysqlite_statement_reset(self->statement); return NULL; @@ -962,6 +978,10 @@ static PyObject * pysqlite_cursor_close_impl(pysqlite_Cursor *self) /*[clinic end generated code: output=b6055e4ec6fe63b6 input=08b36552dbb9a986]*/ { + if (!check_cursor_locked(self)) { + return NULL; + } + if (!self->connection) { PyErr_SetString(pysqlite_ProgrammingError, "Base Cursor.__init__ not called.");