expected behavior, which changed as a result
of [ticket:2261]. [ticket:2319]
+- engine
+ - [bug] Fixed bug whereby transaction.rollback()
+ would throw an error on an invalidated
+ connection if the transaction were a
+ two-phase or savepoint transaction.
+ For plain transactions, rollback() is a no-op
+ if the connection is invalidated, so while
+ it wasn't 100% clear if it should be a no-op,
+ at least now the interface is consistent.
+ [ticket:2317]
+
- schema
- [feature] Added new support for remote "schemas":
- MetaData() accepts "schema" and "quote_schema"
return getattr(self.__connection, 'is_valid', False)
+ @property
+ def _still_open_and_connection_is_valid(self):
+ return \
+ not self.closed and \
+ not self.invalidated and \
+ getattr(self.__connection, 'is_valid', False)
+
@property
def info(self):
"""A collection of per-DB-API connection instance properties."""
if self._has_events:
self.engine.dispatch.rollback(self)
- if not self.closed and not self.invalidated and \
- self._connection_is_valid:
+ if self._still_open_and_connection_is_valid:
if self._echo:
self.engine.logger.info("ROLLBACK")
try:
if name is None:
self.__savepoint_seq += 1
name = 'sa_savepoint_%s' % self.__savepoint_seq
- if self._connection_is_valid:
+ if self._still_open_and_connection_is_valid:
self.engine.dialect.do_savepoint(self, name)
return name
if self._has_events:
self.engine.dispatch.rollback_savepoint(self, name, context)
- if self._connection_is_valid:
+ if self._still_open_and_connection_is_valid:
self.engine.dialect.do_rollback_to_savepoint(self, name)
self.__transaction = context
if self._has_events:
self.engine.dispatch.release_savepoint(self, name, context)
- if self._connection_is_valid:
+ if self._still_open_and_connection_is_valid:
self.engine.dialect.do_release_savepoint(self, name)
self.__transaction = context
if self._has_events:
self.engine.dispatch.begin_twophase(self, xid)
- if self._connection_is_valid:
+ if self._still_open_and_connection_is_valid:
self.engine.dialect.do_begin_twophase(self, xid)
def _prepare_twophase_impl(self, xid):
if self._has_events:
self.engine.dispatch.prepare_twophase(self, xid)
- if self._connection_is_valid:
+ if self._still_open_and_connection_is_valid:
assert isinstance(self.__transaction, TwoPhaseTransaction)
self.engine.dialect.do_prepare_twophase(self, xid)
if self._has_events:
self.engine.dispatch.rollback_twophase(self, xid, is_prepared)
- if self._connection_is_valid:
+ if self._still_open_and_connection_is_valid:
assert isinstance(self.__transaction, TwoPhaseTransaction)
self.engine.dialect.do_rollback_twophase(self, xid, is_prepared)
self.__transaction = None
if self._has_events:
self.engine.dispatch.commit_twophase(self, xid, is_prepared)
- if self._connection_is_valid:
+ if self._still_open_and_connection_is_valid:
assert isinstance(self.__transaction, TwoPhaseTransaction)
self.engine.dialect.do_commit_twophase(self, xid, is_prepared)
self.__transaction = None
conn.execute, select([1])
)
+ def test_rollback_on_invalid_plain(self):
+ conn = engine.connect()
+ trans = conn.begin()
+ conn.invalidate()
+ trans.rollback()
+
+ @testing.requires.two_phase_transactions
+ def test_rollback_on_invalid_twophase(self):
+ conn = engine.connect()
+ trans = conn.begin_twophase()
+ conn.invalidate()
+ trans.rollback()
+
+ @testing.requires.savepoints
+ def test_rollback_on_invalid_savepoint(self):
+ conn = engine.connect()
+ trans = conn.begin()
+ trans2 = conn.begin_nested()
+ conn.invalidate()
+ trans2.rollback()
+
def test_invalidate_twice(self):
conn = engine.connect()
conn.invalidate()