summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2014-07-01 12:12:27 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2014-07-01 12:12:51 -0400
commit427ee3a61e9732fe41c10d2f8ab6650f24bb3d7c (patch)
treee480ee7e5f51aaa998e32fb9c15fa3fa81c61307
parent2d8d1dcf60e023275b8dcfea015ec16cad69d266 (diff)
downloadsqlalchemy-427ee3a61e9732fe41c10d2f8ab6650f24bb3d7c.tar.gz
- 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
-rw-r--r--doc/build/changelog/changelog_09.rst11
-rw-r--r--lib/sqlalchemy/orm/session.py5
-rw-r--r--test/orm/test_naturalpks.py30
-rw-r--r--test/orm/test_transaction.py37
4 files changed, 82 insertions, 1 deletions
diff --git a/doc/build/changelog/changelog_09.rst b/doc/build/changelog/changelog_09.rst
index fa83c4b34..f3389ffda 100644
--- a/doc/build/changelog/changelog_09.rst
+++ b/doc/build/changelog/changelog_09.rst
@@ -16,6 +16,17 @@
.. 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
:versions: 1.0.0
diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py
index 3cc03a2d4..714c42a84 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 011667651..b394b46ca 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 22d759bb8..32bc27c92 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):