From 0d6831448b55e56297b021518aae48dd9d64ff0b Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Tue, 1 Jul 2014 12:12:27 -0400 Subject: [PATCH] - Fixed bug where items that were persisted, deleted, or had a primary key change within a savepoint block would not participate in being restored to their former state (not in session, in session, previous PK) after the outer transaction were rolled back. fixes #3108 --- doc/build/changelog/changelog_09.rst | 11 +++++++++ lib/sqlalchemy/orm/session.py | 5 +++- test/orm/test_naturalpks.py | 30 ++++++++++++++++++++++ test/orm/test_transaction.py | 37 ++++++++++++++++++++++++++++ 4 files changed, 82 insertions(+), 1 deletion(-) diff --git a/doc/build/changelog/changelog_09.rst b/doc/build/changelog/changelog_09.rst index fbf0aa1f73..61aeb64f97 100644 --- a/doc/build/changelog/changelog_09.rst +++ b/doc/build/changelog/changelog_09.rst @@ -14,6 +14,17 @@ :version: 0.9.7 :released: + .. change:: + :tags: bug, orm + :tickets: 3108 + :versions: 1.0.0 + + Fixed bug where items that were persisted, deleted, or had a + primary key change within a savepoint block would not + participate in being restored to their former state (not in + session, in session, previous PK) after the outer transaction + were rolled back. + .. change:: :tags: bug, orm :tickets: 3106 diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py index 89d9946ee6..ba8dcf653f 100644 --- a/lib/sqlalchemy/orm/session.py +++ b/lib/sqlalchemy/orm/session.py @@ -292,7 +292,10 @@ class SessionTransaction(object): for s in self._deleted: s.session_id = None self._deleted.clear() - + elif self.nested: + self._parent._new.update(self._new) + self._parent._deleted.update(self._deleted) + self._parent._key_switches.update(self._key_switches) def _connection_for_bind(self, bind): self._assert_active() diff --git a/test/orm/test_naturalpks.py b/test/orm/test_naturalpks.py index 011667651c..b394b46ca0 100644 --- a/test/orm/test_naturalpks.py +++ b/test/orm/test_naturalpks.py @@ -546,6 +546,36 @@ class ReversePKsTest(fixtures.MappedTest): assert session.query(User).get([1, PUBLISHED]) is a_published assert session.query(User).get([1, EDITABLE]) is a_editable + @testing.requires.savepoints + def test_reverse_savepoint(self): + user, User = self.tables.user, self.classes.User + + PUBLISHED, EDITABLE, ARCHIVED = 1, 2, 3 + + mapper(User, user) + + session = sa.orm.sessionmaker()() + + a_published = User(1, PUBLISHED, 'a') + session.add(a_published) + session.commit() + + a_editable = User(1, EDITABLE, 'a') + + session.add(a_editable) + session.commit() + + # testing #3108 + session.begin_nested() + + a_published.status = ARCHIVED + a_editable.status = PUBLISHED + + session.commit() + + session.rollback() + eq_(a_published.status, PUBLISHED) + eq_(a_editable.status, EDITABLE) class SelfReferentialTest(fixtures.MappedTest): # mssql, mysql don't allow diff --git a/test/orm/test_transaction.py b/test/orm/test_transaction.py index 22d759bb8e..32bc27c92d 100644 --- a/test/orm/test_transaction.py +++ b/test/orm/test_transaction.py @@ -85,6 +85,43 @@ class SessionTransactionTest(FixtureTest): raise + @testing.requires.savepoints + def test_nested_accounting_new_items_removed(self): + User, users = self.classes.User, self.tables.users + + mapper(User, users) + + session = create_session(bind=testing.db) + session.begin() + session.begin_nested() + u1 = User(name='u1') + session.add(u1) + session.commit() + assert u1 in session + session.rollback() + assert u1 not in session + + @testing.requires.savepoints + def test_nested_accounting_deleted_items_restored(self): + User, users = self.classes.User, self.tables.users + + mapper(User, users) + + session = create_session(bind=testing.db) + session.begin() + u1 = User(name='u1') + session.add(u1) + session.commit() + + session.begin() + u1 = session.query(User).first() + + session.begin_nested() + session.delete(u1) + session.commit() + assert u1 not in session + session.rollback() + assert u1 in session @testing.requires.savepoints def test_heavy_nesting(self): -- 2.47.3