]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- Fixed bug which prevented two entities from mutually
authorMike Bayer <mike_mp@zzzcomputing.com>
Tue, 1 Sep 2009 22:55:59 +0000 (22:55 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Tue, 1 Sep 2009 22:55:59 +0000 (22:55 +0000)
replacing each other's primary key values within a single
flush() for some orderings of operations.  [ticket:1519]

CHANGES
lib/sqlalchemy/orm/session.py
test/orm/test_naturalpks.py

diff --git a/CHANGES b/CHANGES
index 432c9e2a3c3e9867629297eef55eba67fc208891..fe6fad202b05288c7ce98d8dbf9883f865ab1e33 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -379,6 +379,10 @@ CHANGES
       bidirectional reference to declare itself as "viewonly"
       [ticket:1507]
 
+    - Fixed bug which prevented two entities from mutually
+      replacing each other's primary key values within a single
+      flush() for some orderings of operations.  [ticket:1519]
+      
     - Fixed an obscure issue whereby a joined-table subclass
       with a self-referential eager load on the base class
       would populate the related object's "subclass" table with
index fdf679a4246592ca7f0522c2a036ea017e7ac369..002ccd50983da639612a0d94977de9f5c80cfeb4 100644 (file)
@@ -1017,8 +1017,10 @@ class Session(object):
             if state.key is None:
                 state.key = instance_key
             elif state.key != instance_key:
-                # primary key switch
-                self.identity_map.remove(state)
+                # primary key switch.
+                # use discard() in case another state has already replaced this
+                # one in the identity map (see test/orm/test_naturalpks.py ReversePKsTest)
+                self.identity_map.discard(state)
                 state.key = instance_key
             
             self.identity_map.replace(state)
index e99bfb794b0746ec785358fa107a4e3848e6fd0e..82a2f9e56e90b3ab7e421925e72fba808fe42777 100644 (file)
@@ -365,7 +365,63 @@ class NaturalPKTest(_base.MappedTest):
         r = sess.query(Item).with_parent(u2).all()
         eq_(Item(itemname='item2'), r[0])
 
+class ReversePKsTest(_base.MappedTest):
+    """reverse the primary keys of two entities and ensure bookkeeping succeeds."""
+    
+    
+    @classmethod
+    def define_tables(cls, metadata):
+        Table(
+            'user', metadata,
+            Column('code', Integer, primary_key=True),
+            Column('status', Integer, primary_key=True),
+            Column('username', String, nullable=False),
+            )
+    
+    @classmethod
+    def setup_classes(cls):
+        class User(_base.ComparableEntity):
+            def __init__(self, code, status, username):
+                self.code = code
+                self.status = status
+                self.username = username
+
+    @testing.resolve_artifact_names
+    def test_reverse(self):
+        PUBLISHED, EDITABLE, ARCHIVED = 1, 2, 3
+        
+        mapper(User, user)
+
+        session = sa.orm.sessionmaker()()
+        
+        a_published = User(0, PUBLISHED, u'a')
+        session.add(a_published)
+        session.commit()
+
+        a_editable = User(0, EDITABLE, u'a')
+
+        session.add(a_editable)
+        session.commit()
+
+        # do the switch in both directions - 
+        # one or the other should raise the error
+        # based on platform dictionary ordering
+        a_published.status = ARCHIVED
+        a_editable.status = PUBLISHED
+
+        session.commit()
+        assert session.query(User).get([0, PUBLISHED]) is a_editable
+        assert session.query(User).get([0, ARCHIVED]) is a_published
+
+        a_published.status = PUBLISHED
+        a_editable.status = EDITABLE
+
+        session.commit()
+
+        assert session.query(User).get([0, PUBLISHED]) is a_published
+        assert session.query(User).get([0, EDITABLE]) is a_editable
 
+    
 class SelfRefTest(_base.MappedTest):
     __unsupported_on__ = 'mssql' # mssql doesn't allow ON UPDATE on self-referential keys