From de1712a2116d80a6771ad5475277cc85e87179e2 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Fri, 17 Aug 2007 21:20:49 +0000 Subject: [PATCH] - added close() method to Transaction. closes out a transaction using rollback if it's the outermost transaction, otherwise just ends without affecting the outer transaction. - transactional and non-transactional Session integrates better with bound connection; a close() will ensure that connection transactional state is the same as that which existed on it before being bound to the Session. --- CHANGES | 8 +++++++ lib/sqlalchemy/engine/base.py | 14 ++++++++++++ lib/sqlalchemy/orm/session.py | 2 +- test/engine/transaction.py | 43 +++++++++++++++++++++++++++++++++++ test/orm/session.py | 15 ++++++++++++ 5 files changed, 81 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 81505bf5ab..12ee19af48 100644 --- a/CHANGES +++ b/CHANGES @@ -11,6 +11,14 @@ CHANGES - Added session.prune(), trims away instances cached in a session that are no longer referenced elsewhere. (A utility for strong-ref identity maps). +- added close() method to Transaction. closes out a transaction using rollback + if it's the outermost transaction, otherwise just ends without affecting + the outer transaction. + +- transactional and non-transactional Session integrates better with bound + connection; a close() will ensure that connection transactional state is + the same as that which existed on it before being bound to the Session. + 0.4.0beta3 ---------- diff --git a/lib/sqlalchemy/engine/base.py b/lib/sqlalchemy/engine/base.py index 8fe34bf3f5..faeb00cc98 100644 --- a/lib/sqlalchemy/engine/base.py +++ b/lib/sqlalchemy/engine/base.py @@ -864,6 +864,20 @@ class Transaction(object): connection = property(lambda s:s._connection, doc="The Connection object referenced by this Transaction") is_active = property(lambda s:s._is_active) + def close(self): + """close this transaction. + + If this transaction is the base transaction in a begin/commit nesting, + the transaction will rollback(). Otherwise, the method returns. + + This is used to cancel a Transaction without affecting the scope of + an enclosign transaction. + """ + if not self._parent._is_active: + return + if self._parent is self: + self.rollback() + def rollback(self): if not self._parent._is_active: return diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py index f21d8d69bc..5a91f8fbfe 100644 --- a/lib/sqlalchemy/orm/session.py +++ b/lib/sqlalchemy/orm/session.py @@ -243,7 +243,7 @@ class SessionTransaction(object): if t[2]: t[0].close() else: - t[1].rollback() + t[1].close() self.session.transaction = None def __enter__(self): diff --git a/test/engine/transaction.py b/test/engine/transaction.py index 7adefbac2b..cf0be06596 100644 --- a/test/engine/transaction.py +++ b/test/engine/transaction.py @@ -116,6 +116,49 @@ class TransactionTest(PersistTest): result = connection.execute("select * from query_users") assert len(result.fetchall()) == 0 connection.close() + + @testing.exclude('mysql', '<', (5, 0, 3)) + def testclose(self): + connection = testbase.db.connect() + transaction = connection.begin() + connection.execute(users.insert(), user_id=1, user_name='user1') + connection.execute(users.insert(), user_id=2, user_name='user2') + connection.execute(users.insert(), user_id=3, user_name='user3') + trans2 = connection.begin() + connection.execute(users.insert(), user_id=4, user_name='user4') + connection.execute(users.insert(), user_id=5, user_name='user5') + assert connection.in_transaction() + trans2.close() + assert connection.in_transaction() + transaction.commit() + assert not connection.in_transaction() + self.assert_(connection.scalar("select count(1) from query_users") == 5) + + result = connection.execute("select * from query_users") + assert len(result.fetchall()) == 5 + connection.close() + + @testing.exclude('mysql', '<', (5, 0, 3)) + def testclose2(self): + connection = testbase.db.connect() + transaction = connection.begin() + connection.execute(users.insert(), user_id=1, user_name='user1') + connection.execute(users.insert(), user_id=2, user_name='user2') + connection.execute(users.insert(), user_id=3, user_name='user3') + trans2 = connection.begin() + connection.execute(users.insert(), user_id=4, user_name='user4') + connection.execute(users.insert(), user_id=5, user_name='user5') + assert connection.in_transaction() + trans2.close() + assert connection.in_transaction() + transaction.close() + assert not connection.in_transaction() + self.assert_(connection.scalar("select count(1) from query_users") == 0) + + result = connection.execute("select * from query_users") + assert len(result.fetchall()) == 0 + connection.close() + @testing.supported('postgres', 'mysql', 'oracle') @testing.exclude('mysql', '<', (5, 0, 3)) diff --git a/test/orm/session.py b/test/orm/session.py index e6db74baac..c4fa10f0a0 100644 --- a/test/orm/session.py +++ b/test/orm/session.py @@ -378,6 +378,21 @@ class SessionTest(AssertMixin): sess.commit() assert not c.in_transaction() assert c.scalar("select count(1) from users") == 1 + c.execute("delete from users") + assert c.scalar("select count(1) from users") == 0 + + c = testbase.db.connect() + + trans = c.begin() + sess = create_session(bind=c, transactional=False) + u = User() + sess.save(u) + sess.flush() + assert c.in_transaction() + trans.commit() + assert not c.in_transaction() + assert c.scalar("select count(1) from users") == 1 + @engines.close_open_connections def test_update(self): -- 2.47.3