--- /dev/null
+.. change::
+ :tags: bug, orm
+ :tickets: 4151
+ :versions: 1.2.1
+
+ Fixed bug where an object that is expunged during a rollback of
+ a nested or subtransaction which also had its primary key mutated
+ would not be correctly removed from the session, causing subsequent
+ issues in using the session.
"""
assert self._is_transaction_boundary
- self.session._expunge_states(
- set(self._new).union(self.session._new),
- to_transient=True)
+ to_expunge = set(self._new).union(self.session._new)
+ self.session._expunge_states(to_expunge, to_transient=True)
for s, (oldkey, newkey) in self._key_switches.items():
+ # we probably can do this conditionally based on
+ # if we expunged or not, but safe_discard does that anyway
self.session.identity_map.safe_discard(s)
+
+ # restore the old key
s.key = oldkey
- self.session.identity_map.replace(s)
+
+ # now restore the object, but only if we didn't expunge
+ if s not in to_expunge:
+ self.session.identity_map.replace(s)
for s in set(self._deleted).union(self.session._deleted):
self.session._update_impl(s, revert_deletion=True)
assert s.identity_map[(User, ('u1',))] is u1
assert s.identity_map[(User, ('u2',))] is u2
+ @testing.requires.savepoints
+ def test_key_replaced_by_update_nested(self):
+ users, User = self.tables.users, self.classes.User
+
+ mapper(User, users)
+
+ u1 = User(name='u1')
+
+ s = Session()
+ s.add(u1)
+ s.commit()
+
+ with s.begin_nested():
+ u2 = User(name='u2')
+ s.add(u2)
+ s.flush()
+
+ u2.name = 'u3'
+
+ s.rollback()
+
+ assert u1 in s
+ assert u2 not in s
+
+ u1.name = 'u5'
+
+ s.commit()
+
def test_multiple_key_replaced_by_update(self):
users, User = self.tables.users, self.classes.User