mirror of
https://github.com/python/cpython.git
synced 2025-11-03 03:22:27 +00:00
bpo-44092: Don't reset statements/cursors before rollback (GH-26026)
In SQLite versions pre 3.7.11, pending statements would block a rollback. This is no longer the case, so remove the workaround.
This commit is contained in:
parent
9d35dedc5e
commit
9d6a239a34
5 changed files with 47 additions and 52 deletions
|
|
@ -302,6 +302,11 @@ sys
|
||||||
(Contributed by Irit Katriel in :issue:`45711`.)
|
(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
|
threading
|
||||||
---------
|
---------
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -231,28 +231,6 @@ class RegressionTests(unittest.TestCase):
|
||||||
with self.assertRaises(sqlite.ProgrammingError):
|
with self.assertRaises(sqlite.ProgrammingError):
|
||||||
cur = con.cursor()
|
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):
|
def test_auto_commit(self):
|
||||||
"""
|
"""
|
||||||
Verifies that creating a connection in autocommit mode works.
|
Verifies that creating a connection in autocommit mode works.
|
||||||
|
|
|
||||||
|
|
@ -131,10 +131,7 @@ class TransactionTests(unittest.TestCase):
|
||||||
self.con1.commit()
|
self.con1.commit()
|
||||||
|
|
||||||
def test_rollback_cursor_consistency(self):
|
def test_rollback_cursor_consistency(self):
|
||||||
"""
|
"""Check that cursors behave correctly after rollback."""
|
||||||
Checks if cursors on the connection are set into a "reset" state
|
|
||||||
when a rollback is done on the connection.
|
|
||||||
"""
|
|
||||||
con = sqlite.connect(":memory:")
|
con = sqlite.connect(":memory:")
|
||||||
cur = con.cursor()
|
cur = con.cursor()
|
||||||
cur.execute("create table test(x)")
|
cur.execute("create table test(x)")
|
||||||
|
|
@ -142,8 +139,44 @@ class TransactionTests(unittest.TestCase):
|
||||||
cur.execute("select 1 union select 2 union select 3")
|
cur.execute("select 1 union select 2 union select 3")
|
||||||
|
|
||||||
con.rollback()
|
con.rollback()
|
||||||
with self.assertRaises(sqlite.InterfaceError):
|
self.assertEqual(cur.fetchall(), [(1,), (2,), (3,)])
|
||||||
cur.fetchall()
|
|
||||||
|
|
||||||
|
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):
|
class SpecialCommandTests(unittest.TestCase):
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -274,28 +274,6 @@ pysqlite_connection_init_impl(pysqlite_Connection *self,
|
||||||
return 0;
|
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) \
|
#define VISIT_CALLBACK_CONTEXT(ctx) \
|
||||||
do { \
|
do { \
|
||||||
if (ctx) { \
|
if (ctx) { \
|
||||||
|
|
@ -549,8 +527,6 @@ pysqlite_connection_rollback_impl(pysqlite_Connection *self)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sqlite3_get_autocommit(self->db)) {
|
if (!sqlite3_get_autocommit(self->db)) {
|
||||||
pysqlite_do_all_statements(self);
|
|
||||||
|
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
Py_BEGIN_ALLOW_THREADS
|
Py_BEGIN_ALLOW_THREADS
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue