summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/whatsnew/3.11.rst5
-rw-r--r--Lib/test/test_sqlite3/test_regression.py22
-rw-r--r--Lib/test/test_sqlite3/test_transactions.py45
-rw-r--r--Misc/NEWS.d/next/Library/2021-05-19-12-35-49.bpo-44092.hiSlI5.rst3
-rw-r--r--Modules/_sqlite/connection.c24
5 files changed, 47 insertions, 52 deletions
diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst
index faa63a9..4ddca74 100644
--- a/Doc/whatsnew/3.11.rst
+++ b/Doc/whatsnew/3.11.rst
@@ -302,6 +302,11 @@ sys
(Contributed by Irit Katriel in :issue:`45711`.)
+* Fetch across rollback no longer raises :exc:`~sqlite3.InterfaceError`.
+ Instead we leave it to the SQLite library to handle these cases.
+ (Contributed by Erlend E. Aasland in :issue:`44092`.)
+
+
threading
---------
diff --git a/Lib/test/test_sqlite3/test_regression.py b/Lib/test/test_sqlite3/test_regression.py
index eb34069..b527053 100644
--- a/Lib/test/test_sqlite3/test_regression.py
+++ b/Lib/test/test_sqlite3/test_regression.py
@@ -231,28 +231,6 @@ class RegressionTests(unittest.TestCase):
with self.assertRaises(sqlite.ProgrammingError):
cur = con.cursor()
- def test_cursor_registration(self):
- """
- Verifies that subclassed cursor classes are correctly registered with
- the connection object, too. (fetch-across-rollback problem)
- """
- class Connection(sqlite.Connection):
- def cursor(self):
- return Cursor(self)
-
- class Cursor(sqlite.Cursor):
- def __init__(self, con):
- sqlite.Cursor.__init__(self, con)
-
- con = Connection(":memory:")
- cur = con.cursor()
- cur.execute("create table foo(x)")
- cur.executemany("insert into foo(x) values (?)", [(3,), (4,), (5,)])
- cur.execute("select x from foo")
- con.rollback()
- with self.assertRaises(sqlite.InterfaceError):
- cur.fetchall()
-
def test_auto_commit(self):
"""
Verifies that creating a connection in autocommit mode works.
diff --git a/Lib/test/test_sqlite3/test_transactions.py b/Lib/test/test_sqlite3/test_transactions.py
index 3efa2c1..55cf8f1 100644
--- a/Lib/test/test_sqlite3/test_transactions.py
+++ b/Lib/test/test_sqlite3/test_transactions.py
@@ -131,10 +131,7 @@ class TransactionTests(unittest.TestCase):
self.con1.commit()
def test_rollback_cursor_consistency(self):
- """
- Checks if cursors on the connection are set into a "reset" state
- when a rollback is done on the connection.
- """
+ """Check that cursors behave correctly after rollback."""
con = sqlite.connect(":memory:")
cur = con.cursor()
cur.execute("create table test(x)")
@@ -142,8 +139,44 @@ class TransactionTests(unittest.TestCase):
cur.execute("select 1 union select 2 union select 3")
con.rollback()
- with self.assertRaises(sqlite.InterfaceError):
- cur.fetchall()
+ self.assertEqual(cur.fetchall(), [(1,), (2,), (3,)])
+
+
+class RollbackTests(unittest.TestCase):
+ """bpo-44092: sqlite3 now leaves it to SQLite to resolve rollback issues"""
+
+ def setUp(self):
+ self.con = sqlite.connect(":memory:")
+ self.cur1 = self.con.cursor()
+ self.cur2 = self.con.cursor()
+ with self.con:
+ self.con.execute("create table t(c)");
+ self.con.executemany("insert into t values(?)", [(0,), (1,), (2,)])
+ self.cur1.execute("begin transaction")
+ select = "select c from t"
+ self.cur1.execute(select)
+ self.con.rollback()
+ self.res = self.cur2.execute(select) # Reusing stmt from cache
+
+ def tearDown(self):
+ self.con.close()
+
+ def _check_rows(self):
+ for i, row in enumerate(self.res):
+ self.assertEqual(row[0], i)
+
+ def test_no_duplicate_rows_after_rollback_del_cursor(self):
+ del self.cur1
+ self._check_rows()
+
+ def test_no_duplicate_rows_after_rollback_close_cursor(self):
+ self.cur1.close()
+ self._check_rows()
+
+ def test_no_duplicate_rows_after_rollback_new_query(self):
+ self.cur1.execute("select c from t where c = 1")
+ self._check_rows()
+
class SpecialCommandTests(unittest.TestCase):
diff --git a/Misc/NEWS.d/next/Library/2021-05-19-12-35-49.bpo-44092.hiSlI5.rst b/Misc/NEWS.d/next/Library/2021-05-19-12-35-49.bpo-44092.hiSlI5.rst
new file mode 100644
index 0000000..6777781
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2021-05-19-12-35-49.bpo-44092.hiSlI5.rst
@@ -0,0 +1,3 @@
+Fetch across rollback no longer raises :exc:`~sqlite3.InterfaceError`. Instead
+we leave it to the SQLite library to handle these cases.
+Patch by Erlend E. Aasland.
diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c
index 4f0baa6..02f4ac4 100644
--- a/Modules/_sqlite/connection.c
+++ b/Modules/_sqlite/connection.c
@@ -274,28 +274,6 @@ pysqlite_connection_init_impl(pysqlite_Connection *self,
return 0;
}
-static void
-pysqlite_do_all_statements(pysqlite_Connection *self)
-{
- // Reset all statements
- sqlite3_stmt *stmt = NULL;
- while ((stmt = sqlite3_next_stmt(self->db, stmt))) {
- if (sqlite3_stmt_busy(stmt)) {
- (void)sqlite3_reset(stmt);
- }
- }
-
- // Reset all cursors
- for (int i = 0; i < PyList_Size(self->cursors); i++) {
- PyObject *weakref = PyList_GetItem(self->cursors, i);
- PyObject *object = PyWeakref_GetObject(weakref);
- if (object != Py_None) {
- pysqlite_Cursor *cursor = (pysqlite_Cursor *)object;
- cursor->reset = 1;
- }
- }
-}
-
#define VISIT_CALLBACK_CONTEXT(ctx) \
do { \
if (ctx) { \
@@ -549,8 +527,6 @@ pysqlite_connection_rollback_impl(pysqlite_Connection *self)
}
if (!sqlite3_get_autocommit(self->db)) {
- pysqlite_do_all_statements(self);
-
int rc;
Py_BEGIN_ALLOW_THREADS