Skip to content

Commit 94eeac0

Browse files
gh-94028: Clear and reset sqlite3 statements properly in cursor iternext (GH-94042)
1 parent 5fcfdd8 commit 94eeac0

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
@@ -139,6 +139,45 @@ def test_rollback_cursor_consistency(self):
139139
con.rollback()
140140
self.assertEqual(cur.fetchall(), [(1,), (2,), (3,)])
141141

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

143182
class RollbackTests(unittest.TestCase):
144183
"""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
@@ -1129,10 +1129,13 @@ pysqlite_cursor_iternext(pysqlite_Cursor *self)
11291129
self->rowcount = (long)sqlite3_changes(self->connection->db);
11301130
}
11311131
(void)stmt_reset(self->statement);
1132+
Py_CLEAR(self->statement);
11321133
}
11331134
else if (rc != SQLITE_ROW) {
11341135
(void)_pysqlite_seterror(self->connection->state,
11351136
self->connection->db);
1137+
(void)stmt_reset(self->statement);
1138+
Py_CLEAR(self->statement);
11361139
Py_DECREF(row);
11371140
return NULL;
11381141
}

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