]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- further optimize what get_all_pending() returns to reduce the work of receivers
authorMike Bayer <mike_mp@zzzcomputing.com>
Mon, 20 Dec 2010 00:55:35 +0000 (19:55 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 20 Dec 2010 00:55:35 +0000 (19:55 -0500)
lib/sqlalchemy/orm/attributes.py
lib/sqlalchemy/orm/dependency.py
lib/sqlalchemy/orm/dynamic.py
lib/sqlalchemy/orm/properties.py
lib/sqlalchemy/orm/state.py

index e7ab4c3a11120883dc095cf7f8716c19cc50e32b..5c2ae28e2223980f07f934b89f4da78f5a5660d3 100644 (file)
@@ -317,6 +317,21 @@ class AttributeImpl(object):
         raise NotImplementedError()
     
     def get_all_pending(self, state, dict_):
+        """Return a list of tuples of (state, obj) 
+        for all objects in this attribute's current state 
+        + history.
+        
+        Only applies to object-based attributes.
+
+        This is an inlining of existing functionality
+        which roughly correponds to:
+    
+            get_state_history(
+                        state, 
+                        key, 
+                        passive=PASSIVE_NO_INITIALIZE).sum()
+
+        """
         raise NotImplementedError()
         
     def initialize(self, state, dict_):
@@ -547,13 +562,20 @@ class ScalarObjectAttributeImpl(ScalarAttributeImpl):
             
             if self.key in state.committed_state:
                 original = state.committed_state[self.key]
-                if original not in (NEVER_SET, None) and \
+                if original not in (NEVER_SET, PASSIVE_NO_RESULT, None) and \
                     original is not current:
-                    return [current, original]
                     
-            return [current]
-        else:
-            return []
+                    if current is not None:
+                        return [
+                            (instance_state(current), current), 
+                            (instance_state(original), original)
+                        ]
+                    else:
+                        return [(instance_state(original), original)]
+            
+            if current is not None:
+                return [(instance_state(current), current)]
+        return []
 
     def set(self, state, dict_, value, initiator, passive=PASSIVE_OFF):
         """Set a value on the given InstanceState.
@@ -645,9 +667,6 @@ class CollectionAttributeImpl(AttributeImpl):
             return History.from_collection(self, state, current)
 
     def get_all_pending(self, state, dict_):
-        # this is basically an inline 
-        # of self.get_history().sum()
-        
         if self.key not in dict_:
             return []
         else:
@@ -656,22 +675,23 @@ class CollectionAttributeImpl(AttributeImpl):
         current = self.get_collection(state, dict_, current)
 
         if self.key not in state.committed_state:
-            return list(current)
+            return [(instance_state(o), o) for o in current]
 
         original = state.committed_state[self.key]
         
         if original is NO_VALUE:
-            return list(current)
+            return [(instance_state(o), o) for o in current]
         else:
-            # TODO: use the dict() of state, obj here
-            current_set = util.IdentitySet(current)
-            original_set = util.IdentitySet(original)
-
-            # ensure ordering is maintained
+            current_states = [(instance_state(c), c) for c in current]
+            original_states = [(instance_state(c), c) for c in original]
+            
+            current_set = dict(current_states)
+            original_set = dict(original_states)
+            
             return \
-                [x for x in current if x not in original_set] + \
-                [x for x in current if x in original_set] + \
-                [x for x in original if x not in current_set]
+                [(s, o) for s, o in current_states if s not in original_set] + \
+                [(s, o) for s, o in current_states if s in original_set] + \
+                [(s, o) for s, o in original_states if s not in current_set]
         
     def fire_append_event(self, state, dict_, value, initiator):
         for fn in self.dispatch.on_append:
@@ -1072,25 +1092,6 @@ def get_history(obj, key, **kwargs):
 def get_state_history(state, key, **kwargs):
     return state.get_history(key, **kwargs)
 
-def get_all_pending(state, dict_, key):
-    """Return a list of all objects currently in memory 
-    involving the given key on the given state.
-    
-    This should be equivalent to::
-    
-        get_state_history(
-                    state, 
-                    key, 
-                    passive=PASSIVE_NO_INITIALIZE).sum()
-                    
-    TODO: we'd like to more closely merge the "history" tuple
-    generation with "get_all_pending()", making the presence
-    of the "History" object optional.
-    
-    """
-    
-    return state.manager[key].impl.get_all_pending(state, dict_)
-    
     
 def has_parent(cls, obj, key, optimistic=False):
     """TODO"""
index 35b197f636c98146cefdc6dff6f1133a7b919901..2c4cc3effc1eebaf4cb6889635b276ce6481c4d5 100644 (file)
@@ -175,10 +175,7 @@ class DependencyProcessor(object):
                 
             if child_in_cycles:
                 child_actions = []
-                for child in sum_:
-                    if child is None:
-                        continue
-                    child_state = attributes.instance_state(child)
+                for child_state, child in sum_:
                     if child_state not in uow.states:
                         child_action = (None, None)
                     else:
index 4637bad7e5979fbf22d50d48e281cb5e3771f510..2094b1cb0dd7609c77620a3c049352d7d17015de 100644 (file)
@@ -144,9 +144,11 @@ class DynamicAttributeImpl(attributes.AttributeImpl):
     
     def get_all_pending(self, state, dict_):
         c = self._get_collection_history(state, True)
-        return (c.added_items or []) +\
-                (c.unchanged_items or []) +\
-                (c.deleted_items or [])
+        return [
+                (attributes.instance_state(x), x) 
+                for x in 
+                c.added_items + c.unchanged_items + c.deleted_items
+            ]
         
     def _get_collection_history(self, state, passive=False):
         if self.key in state.committed_state:
index da6d309e023d325269650c06ecfb324e6a93d1c3..d1b725c8e289b5e75b316fc2fdf1eaf8250515c2 100644 (file)
@@ -830,47 +830,42 @@ class RelationshipProperty(StrategizedProperty):
             passive = attributes.PASSIVE_OFF
 
         if type_ == 'save-update':
-            instances = state.manager[self.key].impl.get_all_pending(state, dict_)
+            tuples = state.manager[self.key].impl.\
+                        get_all_pending(state, dict_)
             
         else:
-            instances = state.value_as_iterable(dict_, self.key,
-                    passive=passive)
+            tuples = state.value_as_iterable(dict_, self.key,
+                            passive=passive)
+                        
         skip_pending = type_ == 'refresh-expire' and 'delete-orphan' \
             not in self.cascade
         
-        if instances:
-            for c in instances:
-                if c is not None and \
-                    c is not attributes.PASSIVE_NO_RESULT:
-
-                    instance_state = attributes.instance_state(c)
-                    if instance_state in visited_states:
-                        continue
-                        
-                    instance_dict = attributes.instance_dict(c)
-                    
-                    if halt_on and halt_on(instance_state):
-                        continue
-                    
-                    if skip_pending and not instance_state.key:
-                        continue
-                    
-                    instance_mapper = instance_state.manager.mapper
-                    
-                    if not instance_mapper.isa(self.mapper.class_manager.mapper):
-                        raise AssertionError("Attribute '%s' on class '%s' "
-                                            "doesn't handle objects "
-                                            "of type '%s'" % (
-                                                self.key, 
-                                                self.parent.class_, 
-                                                c.__class__
-                                            ))
-
-                    visited_states.add(instance_state)
-
-                    # cascade using the mapper local to this 
-                    # object, so that its individual properties are located
-                    yield c, instance_mapper, instance_state, instance_dict
+        for instance_state, c in tuples:
+            if instance_state in visited_states:
+                continue
+                
+            instance_dict = attributes.instance_dict(c)
+            
+            if halt_on and halt_on(instance_state):
+                continue
+            
+            if skip_pending and not instance_state.key:
+                continue
+            
+            instance_mapper = instance_state.manager.mapper
+            
+            if not instance_mapper.isa(self.mapper.class_manager.mapper):
+                raise AssertionError("Attribute '%s' on class '%s' "
+                                    "doesn't handle objects "
+                                    "of type '%s'" % (
+                                        self.key, 
+                                        self.parent.class_, 
+                                        c.__class__
+                                    ))
+
+            visited_states.add(instance_state)
+
+            yield c, instance_mapper, instance_state, instance_dict
             
 
     def _add_reverse_property(self, key):
index 392d83d94822c6d39f9d0f485853e2608eeb4ad8..fd55cfcb87bce90c81aa7326458623edbadbd55c 100644 (file)
@@ -128,22 +128,23 @@ class InstanceState(object):
         return self.pending[key]
 
     def value_as_iterable(self, dict_, key, passive=PASSIVE_OFF):
-        """return an InstanceState attribute as a list,
-        regardless of it being a scalar or collection-based
-        attribute.
+        """Return a list of tuples (state, obj) for the given
+        key.
 
-        returns None if passive is not PASSIVE_OFF and the getter returns
-        PASSIVE_NO_RESULT.
+        returns an empty list if the value is None/empty/PASSIVE_NO_RESULT
         """
 
-        impl = self.get_impl(key)
+        impl = self.manager[key].impl
         x = impl.get(self, dict_, passive=passive)
-        if x is PASSIVE_NO_RESULT:
-            return None
+        if x is PASSIVE_NO_RESULT or x is None:
+            return []
         elif hasattr(impl, 'get_collection'):
-            return impl.get_collection(self, dict_, x, passive=passive)
+            return [
+                (attributes.instance_state(o), o) for o in 
+                impl.get_collection(self, dict_, x, passive=passive)
+            ]
         else:
-            return [x]
+            return [(attributes.instance_state(x), x)]
 
     def __getstate__(self):
         d = {'instance':self.obj()}