From: Mike Bayer Date: Tue, 24 Apr 2012 14:55:47 +0000 (-0400) Subject: - Backported the fix for [ticket:2317] introduced X-Git-Tag: rel_0_6_9~4 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=a6535d17fb41ec5c6bfed07687d9c1e05ff5e91e;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - Backported the fix for [ticket:2317] introduced in 0.7.4, which ensures that the connection is in a valid state before attempting to call rollback()/prepare()/release() on savepoint and two-phase transactions. --- diff --git a/CHANGES b/CHANGES index c5dc7ab834..2a41fac507 100644 --- a/CHANGES +++ b/CHANGES @@ -72,6 +72,13 @@ CHANGES a tuple is inadvertently passed to session.query() [ticket:2297]. +- engine + - Backported the fix for [ticket:2317] introduced + in 0.7.4, which ensures that the connection + is in a valid state before attempting to call + rollback()/prepare()/release() on savepoint + and two-phase transactions. + - sql - Fixed two subtle bugs involving column correspondence in a selectable, diff --git a/lib/sqlalchemy/engine/base.py b/lib/sqlalchemy/engine/base.py index a3ed5bac16..78058bb6c1 100644 --- a/lib/sqlalchemy/engine/base.py +++ b/lib/sqlalchemy/engine/base.py @@ -929,6 +929,13 @@ class Connection(Connectable): 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.""" @@ -1077,8 +1084,7 @@ class Connection(Connectable): raise def _rollback_impl(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: @@ -1109,32 +1115,32 @@ class Connection(Connectable): return name def _rollback_to_savepoint_impl(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 def _release_savepoint_impl(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 def _begin_twophase_impl(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._connection_is_valid: + if self._still_open_and_connection_is_valid: assert isinstance(self.__transaction, TwoPhaseTransaction) self.engine.dialect.do_prepare_twophase(self, xid) def _rollback_twophase_impl(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 def _commit_twophase_impl(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 @@ -1341,6 +1347,7 @@ class Connection(Connectable): context.handle_dbapi_exception(e) is_disconnect = self.dialect.is_disconnect(e) + if is_disconnect: self.invalidate(e) self.engine.dispose() diff --git a/test/engine/test_reconnect.py b/test/engine/test_reconnect.py index e380682a65..09997a4658 100644 --- a/test/engine/test_reconnect.py +++ b/test/engine/test_reconnect.py @@ -259,6 +259,27 @@ class RealReconnectTest(TestBase): conn.close() + 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()