]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- Fixed a regression from 0.7 where the contextmanager feature
authorMike Bayer <mike_mp@zzzcomputing.com>
Tue, 30 Apr 2013 14:02:49 +0000 (10:02 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Tue, 30 Apr 2013 14:02:49 +0000 (10:02 -0400)
of :meth:`.Session.begin_nested` would fail to correctly
roll back the transaction when a flush error occurred, instead
raising its own exception while leaving the session still
pending a rollback.  [ticket:2718]

doc/build/changelog/changelog_08.rst
lib/sqlalchemy/orm/session.py
test/orm/test_transaction.py

index ed953989110455db27c7e24bae8399ccb9b4880b..aa8f0f8786b168b4b83e4f74ff2f8c9c73e27b65 100644 (file)
@@ -6,6 +6,16 @@
 .. changelog::
     :version: 0.8.2
 
+    .. change::
+      :tags: bug, orm
+      :tickets: 2718
+
+      Fixed a regression from 0.7 where the contextmanager feature
+      of :meth:`.Session.begin_nested` would fail to correctly
+      roll back the transaction when a flush error occurred, instead
+      raising its own exception while leaving the session still
+      pending a rollback.
+
     .. change::
       :tags: bug, mysql
 
index f7a5558f17336cab250a4fb6e780c4b1d54529a5..ffb8a4e03960dcd66e2465abcf5bd2a361ccc3b4 100644 (file)
@@ -169,6 +169,7 @@ class SessionTransaction(object):
 
     def _assert_active(self, prepared_ok=False,
                         rollback_ok=False,
+                        deactive_ok=False,
                         closed_msg="This transaction is closed"):
         if self._state is COMMITTED:
             raise sa_exc.InvalidRequestError(
@@ -182,7 +183,7 @@ class SessionTransaction(object):
                         "SQL can be emitted within this transaction."
                     )
         elif self._state is DEACTIVE:
-            if not rollback_ok:
+            if not deactive_ok and not rollback_ok:
                 if self._rollback_exception:
                     raise sa_exc.InvalidRequestError(
                         "This Session's transaction has been rolled back "
@@ -192,7 +193,7 @@ class SessionTransaction(object):
                         " Original exception was: %s"
                         % self._rollback_exception
                     )
-                else:
+                elif not deactive_ok:
                     raise sa_exc.InvalidRequestError(
                         "This Session's transaction has been rolled back "
                         "by a nested rollback() call.  To begin a new "
@@ -435,7 +436,7 @@ class SessionTransaction(object):
         return self
 
     def __exit__(self, type, value, traceback):
-        self._assert_active(prepared_ok=True)
+        self._assert_active(deactive_ok=True, prepared_ok=True)
         if self.session.transaction is None:
             return
         if type is None:
index 64b05a131def0aa9ce3a2a6719cd1dbf3029b4a0..2866ab4ab66e2c21942b4a737e126cfe532b5a24 100644 (file)
@@ -1,4 +1,4 @@
-
+from __future__ import with_statement
 from sqlalchemy.testing import eq_, assert_raises, \
     assert_raises_message, assert_warnings
 from sqlalchemy import *
@@ -83,6 +83,8 @@ class SessionTransactionTest(FixtureTest):
             conn.close()
             raise
 
+
+
     @testing.requires.savepoints
     def test_heavy_nesting(self):
         users = self.tables.users
@@ -620,6 +622,66 @@ class CleanSavepointTest(FixtureTest):
                                     synchronize_session='fetch')
         self._run_test(update_fn)
 
+class ContextManagerTest(FixtureTest):
+    run_inserts = None
+
+    @testing.requires.savepoints
+    @engines.close_open_connections
+    def test_contextmanager_nested_rollback(self):
+        users, User = self.tables.users, self.classes.User
+
+        mapper(User, users)
+
+        sess = Session()
+        def go():
+            with sess.begin_nested():
+                sess.add(User())   # name can't be null
+                sess.flush()
+
+        # and not InvalidRequestError
+        assert_raises(
+            sa_exc.DBAPIError,
+            go
+        )
+
+        with sess.begin_nested():
+            sess.add(User(name='u1'))
+
+        eq_(sess.query(User).count(), 1)
+
+    def test_contextmanager_commit(self):
+        users, User = self.tables.users, self.classes.User
+
+        mapper(User, users)
+
+        sess = Session(autocommit=True)
+        with sess.begin():
+            sess.add(User(name='u1'))
+
+        sess.rollback()
+        eq_(sess.query(User).count(), 1)
+
+    def test_contextmanager_rollback(self):
+        users, User = self.tables.users, self.classes.User
+
+        mapper(User, users)
+
+        sess = Session(autocommit=True)
+        def go():
+            with sess.begin():
+                sess.add(User())  # name can't be null
+        assert_raises(
+            sa_exc.DBAPIError,
+            go
+        )
+
+        eq_(sess.query(User).count(), 0)
+
+        with sess.begin():
+            sess.add(User(name='u1'))
+        eq_(sess.query(User).count(), 1)
+
+
 class AutoExpireTest(_LocalFixture):
 
     def test_expunge_pending_on_rollback(self):