summaryrefslogtreecommitdiff
path: root/test/engine/test_transaction.py
diff options
context:
space:
mode:
Diffstat (limited to 'test/engine/test_transaction.py')
-rw-r--r--test/engine/test_transaction.py142
1 files changed, 136 insertions, 6 deletions
diff --git a/test/engine/test_transaction.py b/test/engine/test_transaction.py
index ffc12b5b9..c373133d1 100644
--- a/test/engine/test_transaction.py
+++ b/test/engine/test_transaction.py
@@ -3,6 +3,7 @@ from sqlalchemy.testing import eq_, assert_raises, \
import sys
import time
import threading
+from sqlalchemy import event
from sqlalchemy.testing.engines import testing_engine
from sqlalchemy import create_engine, MetaData, INT, VARCHAR, Sequence, \
select, Integer, String, func, text, exc
@@ -29,7 +30,6 @@ class TransactionTest(fixtures.TestBase):
testing.db.execute(users.delete()).close()
@classmethod
- @testing.crashes('mysql+cymysql', 'deadlock')
def teardown_class(cls):
users.drop(testing.db)
@@ -342,7 +342,8 @@ class TransactionTest(fixtures.TestBase):
transaction = connection.begin_twophase()
connection.execute(users.insert(), user_id=1, user_name='user1')
transaction.prepare()
- connection.close()
+ connection.invalidate()
+
connection2 = testing.db.connect()
eq_(connection2.execute(select([users.c.user_id]).
order_by(users.c.user_id)).fetchall(),
@@ -379,6 +380,138 @@ class TransactionTest(fixtures.TestBase):
eq_(result.fetchall(), [('user1', ), ('user4', )])
conn.close()
+ @testing.requires.two_phase_transactions
+ def test_reset_rollback_two_phase_no_rollback(self):
+ # test [ticket:2907], essentially that the
+ # TwoPhaseTransaction is given the job of "reset on return"
+ # so that picky backends like MySQL correctly clear out
+ # their state when a connection is closed without handling
+ # the transaction explicitly.
+
+ eng = testing_engine()
+
+ # MySQL raises if you call straight rollback() on
+ # a connection with an XID present
+ @event.listens_for(eng, "invalidate")
+ def conn_invalidated(dbapi_con, con_record, exception):
+ dbapi_con.close()
+ raise exception
+
+ with eng.connect() as conn:
+ rec = conn.connection._connection_record
+ raw_dbapi_con = rec.connection
+ xa = conn.begin_twophase()
+ conn.execute(users.insert(), user_id=1, user_name='user1')
+
+ assert rec.connection is raw_dbapi_con
+
+ with eng.connect() as conn:
+ result = \
+ conn.execute(select([users.c.user_name]).
+ order_by(users.c.user_id))
+ eq_(result.fetchall(), [])
+
+class ResetAgentTest(fixtures.TestBase):
+ def test_begin_close(self):
+ with testing.db.connect() as connection:
+ trans = connection.begin()
+ assert connection.connection._reset_agent is trans
+ assert not trans.is_active
+
+ def test_begin_rollback(self):
+ with testing.db.connect() as connection:
+ trans = connection.begin()
+ assert connection.connection._reset_agent is trans
+ trans.rollback()
+ assert connection.connection._reset_agent is None
+
+ def test_begin_commit(self):
+ with testing.db.connect() as connection:
+ trans = connection.begin()
+ assert connection.connection._reset_agent is trans
+ trans.commit()
+ assert connection.connection._reset_agent is None
+
+ @testing.requires.savepoints
+ def test_begin_nested_close(self):
+ with testing.db.connect() as connection:
+ trans = connection.begin_nested()
+ assert connection.connection._reset_agent is trans
+ assert not trans.is_active
+
+ @testing.requires.savepoints
+ def test_begin_begin_nested_close(self):
+ with testing.db.connect() as connection:
+ trans = connection.begin()
+ trans2 = connection.begin_nested()
+ assert connection.connection._reset_agent is trans
+ assert trans2.is_active # was never closed
+ assert not trans.is_active
+
+ @testing.requires.savepoints
+ def test_begin_begin_nested_rollback_commit(self):
+ with testing.db.connect() as connection:
+ trans = connection.begin()
+ trans2 = connection.begin_nested()
+ assert connection.connection._reset_agent is trans
+ trans2.rollback()
+ assert connection.connection._reset_agent is trans
+ trans.commit()
+ assert connection.connection._reset_agent is None
+
+ @testing.requires.savepoints
+ def test_begin_begin_nested_rollback_rollback(self):
+ with testing.db.connect() as connection:
+ trans = connection.begin()
+ trans2 = connection.begin_nested()
+ assert connection.connection._reset_agent is trans
+ trans2.rollback()
+ assert connection.connection._reset_agent is trans
+ trans.rollback()
+ assert connection.connection._reset_agent is None
+
+ def test_begin_begin_rollback_rollback(self):
+ with testing.db.connect() as connection:
+ trans = connection.begin()
+ trans2 = connection.begin()
+ assert connection.connection._reset_agent is trans
+ trans2.rollback()
+ assert connection.connection._reset_agent is None
+ trans.rollback()
+ assert connection.connection._reset_agent is None
+
+ def test_begin_begin_commit_commit(self):
+ with testing.db.connect() as connection:
+ trans = connection.begin()
+ trans2 = connection.begin()
+ assert connection.connection._reset_agent is trans
+ trans2.commit()
+ assert connection.connection._reset_agent is trans
+ trans.commit()
+ assert connection.connection._reset_agent is None
+
+ @testing.requires.two_phase_transactions
+ def test_reset_via_agent_begin_twophase(self):
+ with testing.db.connect() as connection:
+ trans = connection.begin_twophase()
+ assert connection.connection._reset_agent is trans
+
+ @testing.requires.two_phase_transactions
+ def test_reset_via_agent_begin_twophase_commit(self):
+ with testing.db.connect() as connection:
+ trans = connection.begin_twophase()
+ assert connection.connection._reset_agent is trans
+ trans.commit()
+ assert connection.connection._reset_agent is None
+
+ @testing.requires.two_phase_transactions
+ def test_reset_via_agent_begin_twophase_rollback(self):
+ with testing.db.connect() as connection:
+ trans = connection.begin_twophase()
+ assert connection.connection._reset_agent is trans
+ trans.rollback()
+ assert connection.connection._reset_agent is None
+
class AutoRollbackTest(fixtures.TestBase):
@classmethod
@@ -504,7 +637,7 @@ class ExplicitAutoCommitTest(fixtures.TestBase):
conn2.close()
@testing.uses_deprecated(r'autocommit on select\(\) is deprecated',
- r'autocommit\(\) is deprecated')
+ r'``autocommit\(\)`` is deprecated')
def test_explicit_compiled_deprecated(self):
conn1 = testing.db.connect()
conn2 = testing.db.connect()
@@ -1036,7 +1169,6 @@ class ForUpdateTest(fixtures.TestBase):
@testing.crashes('mssql', 'FIXME: unknown')
@testing.crashes('firebird', 'FIXME: unknown')
@testing.crashes('sybase', 'FIXME: unknown')
- @testing.crashes('access', 'FIXME: unknown')
@testing.requires.independent_connections
def test_queued_update(self):
"""Test SELECT FOR UPDATE with concurrent modifications.
@@ -1101,7 +1233,6 @@ class ForUpdateTest(fixtures.TestBase):
@testing.crashes('mssql', 'FIXME: unknown')
@testing.crashes('firebird', 'FIXME: unknown')
@testing.crashes('sybase', 'FIXME: unknown')
- @testing.crashes('access', 'FIXME: unknown')
@testing.requires.independent_connections
def test_queued_select(self):
"""Simple SELECT FOR UPDATE conflict test"""
@@ -1113,7 +1244,6 @@ class ForUpdateTest(fixtures.TestBase):
@testing.fails_on('mysql', 'No support for NOWAIT')
@testing.crashes('firebird', 'FIXME: unknown')
@testing.crashes('sybase', 'FIXME: unknown')
- @testing.crashes('access', 'FIXME: unknown')
@testing.requires.independent_connections
def test_nowait_select(self):
"""Simple SELECT FOR UPDATE NOWAIT conflict test"""