From: Mike Bayer Date: Mon, 20 Dec 2010 00:55:35 +0000 (-0500) Subject: - further optimize what get_all_pending() returns to reduce the work of receivers X-Git-Tag: rel_0_7b1~148 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d94c1d1ac30821572c7c5cf864825d5f552b81b3;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - further optimize what get_all_pending() returns to reduce the work of receivers --- diff --git a/lib/sqlalchemy/orm/attributes.py b/lib/sqlalchemy/orm/attributes.py index e7ab4c3a11..5c2ae28e22 100644 --- a/lib/sqlalchemy/orm/attributes.py +++ b/lib/sqlalchemy/orm/attributes.py @@ -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""" diff --git a/lib/sqlalchemy/orm/dependency.py b/lib/sqlalchemy/orm/dependency.py index 35b197f636..2c4cc3effc 100644 --- a/lib/sqlalchemy/orm/dependency.py +++ b/lib/sqlalchemy/orm/dependency.py @@ -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: diff --git a/lib/sqlalchemy/orm/dynamic.py b/lib/sqlalchemy/orm/dynamic.py index 4637bad7e5..2094b1cb0d 100644 --- a/lib/sqlalchemy/orm/dynamic.py +++ b/lib/sqlalchemy/orm/dynamic.py @@ -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: diff --git a/lib/sqlalchemy/orm/properties.py b/lib/sqlalchemy/orm/properties.py index da6d309e02..d1b725c8e2 100644 --- a/lib/sqlalchemy/orm/properties.py +++ b/lib/sqlalchemy/orm/properties.py @@ -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): diff --git a/lib/sqlalchemy/orm/state.py b/lib/sqlalchemy/orm/state.py index 392d83d948..fd55cfcb87 100644 --- a/lib/sqlalchemy/orm/state.py +++ b/lib/sqlalchemy/orm/state.py @@ -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()}