# TODO: use correct API for this
return self._get_instrumented_attribute().hasparent(state)
- def per_property_flush_actions(self, uow):
+ def per_property_preprocessors(self, uow):
"""establish actions and dependencies related to a flush.
These actions will operate on all relevant states in
if self.post_update and self._check_reverse(uow):
return
- unitofwork.GetDependentObjects(uow, self, False, True)
- unitofwork.GetDependentObjects(uow, self, True, True)
+ uow.register_preprocessor(self, True)
- def _has_flush_activity(self, uow):
+ def per_property_flush_actions(self, uow):
if self.post_update and self._check_reverse(uow):
return
# assertions to ensure this method isn't being
# called unnecessarily. can comment these out when
# code is stable
- assert ('has_flush_activity', self) in uow.attributes
+ #assert ('has_flush_activity', self) in uow.attributes
assert not self.post_update or not self._check_reverse(uow)
def process_saves(self, uowcommit, states):
pass
- def _prop_has_changes(self, uowcommit, states):
+ def prop_has_changes(self, uowcommit, states, isdelete):
for s in states:
# TODO: add a high speed method
# to InstanceState which returns: attribute
"""
- def per_property_flush_actions(self, uow):
+ def per_property_preprocessors(self, uow):
if self.prop._reverse_property:
return
- unitofwork.GetDependentObjects(uow, self, False, False)
+ uow.register_preprocessor(self, False)
- def _has_flush_activity(self, uow):
- pass
+ def per_property_flush_actions(self, uow):
+ parent_saves = unitofwork.SaveUpdateAll(
+ uow,
+ self.parent.base_mapper)
+ after_save = unitofwork.ProcessAll(uow, self, False, False)
+ uow.dependencies.update([
+ (parent_saves, after_save)
+ ])
def per_state_flush_actions(self, uow, states, isdelete):
pass
def presort_deletes(self, uowcommit, states):
- assert False
+ pass
def presort_saves(self, uow, states):
- if self.passive_updates:
- # for passive updates, register objects in the process stage
- # so that we avoid ManyToOneDP's registering the object without
- # the listonly flag in its own preprocess stage (results in UPDATE)
- # statements being emitted
- for s in states:
- if self._pks_changed(uow, s):
- parent_saves = unitofwork.SaveUpdateAll(
- uow,
- self.parent.base_mapper)
- after_save = unitofwork.ProcessAll(uow, self, False, False)
- uow.dependencies.update([
- (parent_saves, after_save)
- ])
- return
-
- else:
+ if not self.passive_updates:
# for non-passive updates, register in the preprocess stage
# so that mapper save_obj() gets a hold of changes
self._process_key_switches(states, uow)
+
+ def prop_has_changes(self, uow, states, isdelete):
+ if not isdelete and self.passive_updates:
+ for s in states:
+ if self._pks_changed(uow, s):
+ return True
+
+ return False
def process_deletes(self, uowcommit, states):
assert False
def process_saves(self, uowcommit, states):
+ # for passive updates, register objects in the process stage
+ # so that we avoid ManyToOneDP's registering the object without
+ # the listonly flag in its own preprocess stage (results in UPDATE)
+ # statements being emitted
assert self.passive_updates
self._process_key_switches(states, uowcommit)
-
+
def _process_key_switches(self, deplist, uowcommit):
switchers = set(s for s in deplist if self._pks_changed(uowcommit, s))
if switchers:
class ManyToManyDP(DependencyProcessor):
- def per_property_flush_actions(self, uow):
+ def per_property_preprocessors(self, uow):
if self._check_reverse(uow):
return
- DependencyProcessor.per_property_flush_actions(self, uow)
+ DependencyProcessor.per_property_preprocessors(self, uow)
def per_property_dependencies(self, uow, parent_saves,
child_saves,
self.session = session
self.mapper_flush_opts = session._mapper_flush_opts
- # dictionary used by external actors to store arbitrary state
- # information.
+ # dictionary used by external actors to
+ # store arbitrary state information.
self.attributes = {}
+ # dictionary of mappers to sets of
+ # DependencyProcessors which have that mapper
+ # as a parent.
self.deps = util.defaultdict(set)
+
+ # dictionary of mappers to sets of InstanceState
+ # items pending for flush which have that mapper
+ # as a parent.
self.mappers = util.defaultdict(set)
- self.presort_actions = {}
+
+ # a set of Preprocess objects, which gather
+ # additional states impacted by the flush
+ # and determine if a flush action is needed
+ self.presort_actions = set()
+
+ # dictionary of PostSortRec objects, each
+ # one issues work during the flush within
+ # a certain ordering.
self.postsort_actions = {}
- self.states = {}
+
+ # a set of 2-tuples, each containing two
+ # PostSortRec objects where the second
+ # is dependent on the first being executed
+ # first
self.dependencies = set()
+
+ # dictionary of InstanceState-> (isdelete, listonly)
+ # tuples, indicating if this state is to be deleted
+ # or insert/updated, or just refreshed
+ self.states = {}
@property
def has_work(self):
def is_deleted(self, state):
"""return true if the given state is marked as deleted
- within this UOWTransaction."""
+ within this uowtransaction."""
+
return state in self.states and self.states[state][0]
def remove_state_actions(self, state):
self.states[state] = (False, True)
def get_attribute_history(self, state, key, passive=True):
+ """facade to attributes.get_state_history(), including caching of results."""
+
hashkey = ("history", state, key)
# cache the objects, not the states; the strong reference here
return history
else:
return history.as_state()
-
+
+ def register_preprocessor(self, processor, fromparent):
+ self.presort_actions.add(Preprocess(processor, fromparent))
+ self.deps[processor.parent.base_mapper].add(processor)
+
def register_object(self, state, isdelete=False, listonly=False):
if not self.session._contains_state(state):
return
mapper_for_dep = self._mapper_for_dep
return [s for s in states if mapper_for_dep[(s.manager.mapper, dep)]]
- def states_for_mapper(self, mapper, isdelete, listonly):
- checktup = (isdelete, listonly)
- for state in self.mappers[mapper]:
- if self.states[state] == checktup:
- yield state
-
def states_for_mapper_hierarchy(self, mapper, isdelete, listonly):
checktup = (isdelete, listonly)
for mapper in mapper.base_mapper.polymorphic_iterator():
# add new states to the uow.
while True:
ret = False
- for action in self.presort_actions.values():
+ for action in list(self.presort_actions):
if action.execute(self):
ret = True
if not ret:
for dep in convert[edge[1]]:
self.dependencies.add((edge[0], dep))
- return set(
- [a for a in self.postsort_actions.values()
- if not a.disabled
- ]
- ).difference(cycles)
+ return set([a for a in self.postsort_actions.values()
+ if not a.disabled
+ ]
+ ).difference(cycles)
def execute(self):
postsort_actions = self._generate_actions()
#sort = topological.sort(self.dependencies, postsort_actions)
#print "--------------"
#print self.dependencies
- print postsort_actions
- print "COUNT OF POSTSORT ACTIONS", len(postsort_actions)
+ #print postsort_actions
+ #print "COUNT OF POSTSORT ACTIONS", len(postsort_actions)
# execute
if self.cycles:
log.class_logger(UOWTransaction)
-class PreSortRec(object):
- def __new__(cls, uow, *args):
- key = (cls, ) + args
- if key in uow.presort_actions:
- return uow.presort_actions[key]
+class IterateMappersMixin(object):
+ def _mappers(self, uow):
+ if self.fromparent:
+ return iter(
+ m for m in self.dependency_processor.parent.polymorphic_iterator()
+ if uow._mapper_for_dep[(m, self.dependency_processor)]
+ )
else:
- uow.presort_actions[key] = \
- ret = \
- object.__new__(cls)
- return ret
+ return self.dependency_processor.mapper.polymorphic_iterator()
+
+class Preprocess(IterateMappersMixin):
+ def __init__(self, dependency_processor, fromparent):
+ self.dependency_processor = dependency_processor
+ self.fromparent = fromparent
+ self.processed = set()
+ self.setup_flush_actions = False
+
+ def execute(self, uow):
+ delete_states = set()
+ save_states = set()
+
+ for mapper in self._mappers(uow):
+ for state in uow.mappers[mapper].difference(self.processed):
+ (isdelete, listonly) = uow.states[state]
+ if not listonly:
+ if isdelete:
+ delete_states.add(state)
+ else:
+ save_states.add(state)
+
+ if delete_states:
+ self.dependency_processor.presort_deletes(uow, delete_states)
+ self.processed.update(delete_states)
+ if save_states:
+ self.dependency_processor.presort_saves(uow, save_states)
+ self.processed.update(save_states)
+
+ if (delete_states or save_states):
+ if not self.setup_flush_actions and (
+ self.dependency_processor.\
+ prop_has_changes(uow, delete_states, True) or
+ self.dependency_processor.\
+ prop_has_changes(uow, save_states, False)
+ ):
+ self.dependency_processor.per_property_flush_actions(uow)
+ self.setup_flush_actions = True
+ return True
+ else:
+ return False
class PostSortRec(object):
disabled = False
",".join(str(x) for x in self.__dict__.values())
)
-class PropertyRecMixin(object):
+class ProcessAll(IterateMappersMixin, PostSortRec):
def __init__(self, uow, dependency_processor, delete, fromparent):
self.dependency_processor = dependency_processor
self.delete = delete
self.fromparent = fromparent
- prop = dependency_processor.prop
- if fromparent:
- self._mappers = set(
- m for m in dependency_processor.parent.polymorphic_iterator()
- if m._props[prop.key] is prop
- )
- else:
- self._mappers = set(
- dependency_processor.mapper.polymorphic_iterator()
- )
-
- def __repr__(self):
- return "%s(%s, delete=%s)" % (
- self.__class__.__name__,
- self.dependency_processor,
- self.delete
- )
-
- processed = ()
- def _elements(self, uow):
- for mapper in self._mappers:
- for state in uow.mappers[mapper]:
- if state in self.processed:
- continue
- (isdelete, listonly) = uow.states[state]
- if isdelete == self.delete and not listonly:
- yield state
-
-class GetDependentObjects(PropertyRecMixin, PreSortRec):
- def __init__(self, *args):
- super(GetDependentObjects, self).__init__(*args)
- self.processed = set()
-
- def execute(self, uow):
-
- states = list(self._elements(uow))
- if states:
- if self.delete:
- self.dependency_processor.presort_deletes(uow, states)
- else:
- self.dependency_processor.presort_saves(uow, states)
- self.processed.update(states)
-
- if ('has_flush_activity', self.dependency_processor) not in uow.attributes:
- # TODO: wont be calling self.fromparent here once detectkeyswitch works
- if not self.fromparent or self.dependency_processor._prop_has_changes(uow, states):
- self.dependency_processor._has_flush_activity(uow)
- uow.attributes[('has_flush_activity', self.dependency_processor)] = True
-
- return True
- else:
- return False
-
-class ProcessAll(PropertyRecMixin, PostSortRec):
- def __init__(self, uow, *args):
- super(ProcessAll, self).__init__(uow, *args)
- uow.deps[self.dependency_processor.parent.base_mapper].add(self.dependency_processor)
-
def execute(self, uow):
states = self._elements(uow)
if self.delete:
# into per-state if either the parent/child mappers
# are part of a cycle
return iter([])
+
+ def __repr__(self):
+ return "%s(%s, delete=%s)" % (
+ self.__class__.__name__,
+ self.dependency_processor,
+ self.delete
+ )
+
+ def _elements(self, uow):
+ for mapper in self._mappers(uow):
+ for state in uow.mappers[mapper]:
+ (isdelete, listonly) = uow.states[state]
+ if isdelete == self.delete and not listonly:
+ yield state
class SaveUpdateAll(PostSortRec):
def __init__(self, uow, mapper):