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

Lines changed: 39 additions & 0 deletions
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"""
Lines changed: 3 additions & 0 deletions
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

Lines changed: 3 additions & 0 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

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:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy