Skip to content

Commit a3b9665

Browse files
erlend-aaslandhrnciar
authored andcommitted
00384: pythongh-94028: Clear and reset sqlite3 statements properly in cursor iternext (pythonGH-94042)
1 parent e6a1ac7 commit a3b9665

File tree

3 files changed

+45
-0
lines changed

3 files changed

+45
-0
lines changed

Lib/test/test_sqlite3/test_transactions.py

+39
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,45 @@ def test_rollback_cursor_consistency(self):
141141
con.rollback()
142142
self.assertEqual(cur.fetchall(), [(1,), (2,), (3,)])
143143

144+
def test_multiple_cursors_and_iternext(self):
145+
# gh-94028: statements are cleared and reset in cursor iternext.
146+
147+
# Provoke the gh-94028 by using a cursor cache.
148+
CURSORS = {}
149+
def sql(cx, sql, *args):
150+
cu = cx.cursor()
151+
cu.execute(sql, args)
152+
CURSORS[id(sql)] = cu
153+
return cu
154+
155+
self.con1.execute("create table t(t)")
156+
sql(self.con1, "insert into t values (?), (?), (?)", "u1", "u2", "u3")
157+
self.con1.commit()
158+
159+
# On second connection, verify rows are visible, then delete them.
160+
count = sql(self.con2, "select count(*) from t").fetchone()[0]
161+
self.assertEqual(count, 3)
162+
changes = sql(self.con2, "delete from t").rowcount
163+
self.assertEqual(changes, 3)
164+
self.con2.commit()
165+
166+
# Back in original connection, create 2 new users.
167+
sql(self.con1, "insert into t values (?)", "u4")
168+
sql(self.con1, "insert into t values (?)", "u5")
169+
170+
# The second connection cannot see uncommitted changes.
171+
count = sql(self.con2, "select count(*) from t").fetchone()[0]
172+
self.assertEqual(count, 0)
173+
174+
# First connection can see its own changes.
175+
count = sql(self.con1, "select count(*) from t").fetchone()[0]
176+
self.assertEqual(count, 2)
177+
178+
# The second connection can now see the changes.
179+
self.con1.commit()
180+
count = sql(self.con2, "select count(*) from t").fetchone()[0]
181+
self.assertEqual(count, 2)
182+
144183

145184
class RollbackTests(unittest.TestCase):
146185
"""bpo-44092: sqlite3 now leaves it to SQLite to resolve rollback issues"""
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix a regression in the :mod:`sqlite3` where statement objects were not
2+
properly cleared and reset after use in cursor iters. The regression was
3+
introduced by PR 27884 in Python 3.11a1. Patch by Erlend E. Aasland.

Modules/_sqlite/cursor.c

+3
Original file line numberDiff line numberDiff line change
@@ -1126,10 +1126,13 @@ pysqlite_cursor_iternext(pysqlite_Cursor *self)
11261126
int rc = stmt_step(stmt);
11271127
if (rc == SQLITE_DONE) {
11281128
(void)stmt_reset(self->statement);
1129+
Py_CLEAR(self->statement);
11291130
}
11301131
else if (rc != SQLITE_ROW) {
11311132
(void)_pysqlite_seterror(self->connection->state,
11321133
self->connection->db);
1134+
(void)stmt_reset(self->statement);
1135+
Py_CLEAR(self->statement);
11331136
Py_DECREF(row);
11341137
return NULL;
11351138
}

0 commit comments

Comments
 (0)