diff --git a/Lib/test/test_sqlite3/test_transactions.py b/Lib/test/test_sqlite3/test_transactions.py index 21acad8383c29e..a67d72709d39ab 100644 --- a/Lib/test/test_sqlite3/test_transactions.py +++ b/Lib/test/test_sqlite3/test_transactions.py @@ -139,6 +139,45 @@ def test_rollback_cursor_consistency(self): con.rollback() self.assertEqual(cur.fetchall(), [(1,), (2,), (3,)]) + def test_multiple_cursors_and_iternext(self): + # gh-94028: statements are cleared and reset in cursor iternext. + + # Provoke the gh-94028 by using a cursor cache. + CURSORS = {} + def sql(cx, sql, *args): + cu = cx.cursor() + cu.execute(sql, args) + CURSORS[id(sql)] = cu + return cu + + self.con1.execute("create table t(t)") + sql(self.con1, "insert into t values (?), (?), (?)", "u1", "u2", "u3") + self.con1.commit() + + # On second connection, verify rows are visible, then delete them. + count = sql(self.con2, "select count(*) from t").fetchone()[0] + self.assertEqual(count, 3) + changes = sql(self.con2, "delete from t").rowcount + self.assertEqual(changes, 3) + self.con2.commit() + + # Back in original connection, create 2 new users. + sql(self.con1, "insert into t values (?)", "u4") + sql(self.con1, "insert into t values (?)", "u5") + + # The second connection cannot see uncommitted changes. + count = sql(self.con2, "select count(*) from t").fetchone()[0] + self.assertEqual(count, 0) + + # First connection can see its own changes. + count = sql(self.con1, "select count(*) from t").fetchone()[0] + self.assertEqual(count, 2) + + # The second connection can now see the changes. + self.con1.commit() + count = sql(self.con2, "select count(*) from t").fetchone()[0] + self.assertEqual(count, 2) + class RollbackTests(unittest.TestCase): """bpo-44092: sqlite3 now leaves it to SQLite to resolve rollback issues""" diff --git a/Misc/NEWS.d/next/Library/2022-06-20-23-14-43.gh-issue-94028.UofEcX.rst b/Misc/NEWS.d/next/Library/2022-06-20-23-14-43.gh-issue-94028.UofEcX.rst new file mode 100644 index 00000000000000..5775b2276d70b4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-06-20-23-14-43.gh-issue-94028.UofEcX.rst @@ -0,0 +1,3 @@ +Fix a regression in the :mod:`sqlite3` where statement objects were not +properly cleared and reset after use in cursor iters. The regression was +introduced by PR 27884 in Python 3.11a1. Patch by Erlend E. Aasland. diff --git a/Modules/_sqlite/cursor.c b/Modules/_sqlite/cursor.c index 414584d8ee30e9..3f5cfef0c32589 100644 --- a/Modules/_sqlite/cursor.c +++ b/Modules/_sqlite/cursor.c @@ -1129,10 +1129,13 @@ pysqlite_cursor_iternext(pysqlite_Cursor *self) self->rowcount = (long)sqlite3_changes(self->connection->db); } (void)stmt_reset(self->statement); + Py_CLEAR(self->statement); } else if (rc != SQLITE_ROW) { (void)_pysqlite_seterror(self->connection->state, self->connection->db); + (void)stmt_reset(self->statement); + Py_CLEAR(self->statement); Py_DECREF(row); return NULL; }
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: