From 691221affe053d404f3c8a2f77779d9ec6bf25ff Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Sun, 2 Oct 2005 20:24:04 +0000 Subject: [PATCH] --- lib/sqlalchemy/mapper.py | 6 -- lib/sqlalchemy/objectstore.py | 136 +++++++--------------------------- lib/sqlalchemy/util.py | 93 +++++++++++++++++++++++ 3 files changed, 119 insertions(+), 116 deletions(-) diff --git a/lib/sqlalchemy/mapper.py b/lib/sqlalchemy/mapper.py index b7e87eb049..7feb24439b 100644 --- a/lib/sqlalchemy/mapper.py +++ b/lib/sqlalchemy/mapper.py @@ -778,15 +778,11 @@ class PropertyLoader(MapperProperty): clearkeys = True for child in childlist.deleted_items(): self.primaryjoin.accept_visitor(setter) - - #print self.mapper.table.name + " postdep " + repr([str(v) for v in deplist.map.values()]) + " process_dep isdelete " + repr(delete) def _sync_foreign_keys(self, binary, obj, child, associationrow, clearkeys): """given a binary clause with an = operator joining two table columns, synchronizes the values of the corresponding attributes within a parent object and a child object, or the attributes within an an "association row" that represents an association link between the 'parent' and 'child' object.""" - if obj is child: - raise "wha?" if binary.operator == '=': if binary.left.table == binary.right.table: if binary.right is self.foreignkey: @@ -796,8 +792,6 @@ class PropertyLoader(MapperProperty): else: raise "Cant determine direction for relationship %s = %s" % (binary.left.fullname, binary.right.fullname) self.mapper._setattrbycolumn(child, self.foreignkey, self.parent._getattrbycolumn(obj, source)) - print "set " + repr(id(child)) + child.__dict__['name'] + ":" + self.foreignkey.key + " to " + repr(id(obj)) + obj.__dict__['name'] + ":" + source.key - #+ "\n" + repr(child.__dict__) else: colmap = {binary.left.table : binary.left, binary.right.table : binary.right} if colmap.has_key(self.parent.primarytable) and colmap.has_key(self.target): diff --git a/lib/sqlalchemy/objectstore.py b/lib/sqlalchemy/objectstore.py index 0b5311f4fc..c3c3c8f956 100644 --- a/lib/sqlalchemy/objectstore.py +++ b/lib/sqlalchemy/objectstore.py @@ -348,7 +348,7 @@ class UOWTransaction(object): mappers.append(task.mapper) bymapper[(task.mapper, task.isdelete)] = task - head = TupleSorter(self.dependencies, mappers).sort() + head = util.DependencySorter(self.dependencies, mappers).sort() res = [] tasklist = sort(head, False, res) @@ -359,10 +359,6 @@ class UOWTransaction(object): assert(len(self.tasks.values()) == len(tasklist)) # "sorted task list not the same size as original task list" - import string,sys - #print string.join([str(t) for t in tasklist], ',') - #sys.exit(0) - return tasklist class UOWTask(object): @@ -377,7 +373,7 @@ class UOWTask(object): def execute(self, trans): if self.iscircular: - task = self.sort_circular_dependencies(trans) + task = self._sort_circular_dependencies(trans) if task is not None: task.execute_circular(trans) return @@ -392,12 +388,15 @@ class UOWTask(object): self.mapper.delete_obj(obj_list, trans) def execute_circular(self, trans): - self.execute(trans) + if not self.isdelete: + self.execute(trans) for obj in self.objects: childtask = self.taskhash[obj] childtask.execute_circular(trans) - - def sort_circular_dependencies(self, trans): + if self.isdelete: + self.execute(trans) + + def _sort_circular_dependencies(self, trans): allobjects = self.objects tuples = [] d = {} @@ -426,20 +425,27 @@ class UOWTask(object): for obj in self.objects: parenttask = get_task(obj) + # TODO: we are doing this dependency sort which uses a lot of the + # concepts in mapper.PropertyLoader's more coarse-grained version. + # should consolidate the concept of "childlist/added/deleted/unchanged" "left/right" + # in one place for dep in self.dependencies: (processor, targettask) = dep - if targettask is self: - childlist = processor.get_object_dependencies(obj, trans, passive = True) - for o in childlist.added_items() + childlist.deleted_items(): - whosdep = processor.whose_dependent_on_who(obj, o, trans) - if whosdep is not None: - tuples.append(whosdep) - if whosdep[0] is obj: - get_dependency_task(whosdep[0], processor).objects.append(whosdep[0]) - else: - get_dependency_task(whosdep[0], processor).objects.append(whosdep[1]) + childlist = processor.get_object_dependencies(obj, trans, passive = True) + if self.isdelete: + childlist = childlist.unchanged_items() + childlist.deleted_items() + else: + childlist = childlist.added_items() + childlist.deleted_items() + for o in childlist: + whosdep = processor.whose_dependent_on_who(obj, o, trans) + if whosdep is not None: + tuples.append(whosdep) + if whosdep[0] is obj: + get_dependency_task(whosdep[0], processor).objects.append(whosdep[0]) + else: + get_dependency_task(whosdep[0], processor).objects.append(whosdep[1]) - head = TupleSorter(tuples, allobjects).sort() + head = util.DependencySorter(tuples, allobjects).sort() if head is None: return None @@ -475,96 +481,6 @@ class UOWTask(object): else: return self.mapper.primarytable.name + " saves " + repr(self.listonly) -class TupleSorter(object): - - class Node: - def __init__(self, item): - #print "new node on " + str(item) - self.item = item - self.children = util.HashSet() - self.parent = None - self.circular = False - def __str__(self): - return self.safestr({}) - def safestr(self, hash, indent = 0): - if hash.has_key(self): - return (' ' * indent) + "RECURSIVE:%s(%s, %s)" % (str(self.item), repr(id(self)), self.parent and repr(id(self.parent)) or 'None') - hash[self] = True - return (' ' * indent) + "%s(%s, %s)" % (str(self.item), repr(id(self)), self.parent and repr(id(self.parent)) or "None") + "\n" + string.join([n.safestr(hash, indent + 1) for n in self.children], '') - - def __init__(self, tuples, allitems): - self.tuples = tuples - self.allitems = allitems - def sort(self): - (tuples, allitems) = (self.tuples, self.allitems) - nodes = {} - head = None - for tup in tuples: - (parent, child) = (tup[0], tup[1]) - #print "tuple: " + str(parent) + " " + str(child) - - # get parent node - try: - parentnode = nodes[parent] - except KeyError: - parentnode = TupleSorter.Node(parent) - nodes[parent] = parentnode - - # if parent is child, mark "circular" attribute on the node - if parent is child: - parentnode.circular = True - # set head if its nothing - if head is None: - head = parentnode - # nothing more to do for this one - continue - - # get child node - try: - childnode = nodes[child] - except KeyError: - childnode = TupleSorter.Node(child) - nodes[child] = childnode - - # set head if its nothing, move it up to the parent - # if its the child node - if head is None: - head = parentnode - elif head is childnode: - head = parentnode - - # now see, if the parent is an ancestor of the child - c = childnode - while c is not None and c is not parentnode: - c = c.parent - - # nope, so we have to move the child down from whereever - # it currently is to a child of the parent - if c is None: - if childnode.parent is not None: - del childnode.parent.children[childnode] - childnode.parent.children.append(parentnode) - parentnode.children.append(childnode) - childnode.parent = parentnode - #print str(head) - - # go through the total list of items. for those - # that had no dependency tuples, and therefore are not - # in the tree, add them as head nodes in a line - newhead = None - for item in allitems: - if not nodes.has_key(item): - if newhead is None: - newhead = TupleSorter.Node(item) - if head is not None: - head.parent = newhead - newhead.children.append(head) - head = newhead - else: - n = TupleSorter.Node(item) - head.children.append(n) - n.parent = head - return head uow = util.ScopedRegistry(lambda: UnitOfWork(), "thread") diff --git a/lib/sqlalchemy/util.py b/lib/sqlalchemy/util.py index 446b87cb95..682b9fe323 100644 --- a/lib/sqlalchemy/util.py +++ b/lib/sqlalchemy/util.py @@ -318,4 +318,97 @@ class ScopedRegistry(object): def _clear_application(self): self.application = createfunc() +class DependencySorter(object): + """creates a "dependency tree" across a list of objects, using a series of 'dependency relationships' + expressed as a list of tuples to determine its shape. the tuples are of the form (x,y) which indicate + 'y is dependent on x', as well as a list of + elements which represent the full collection that x and y originate from""" + class Node: + def __init__(self, item): + #print "new node on " + str(item) + self.item = item + self.children = HashSet() + self.parent = None + self.circular = False + def __str__(self): + return self.safestr({}) + def safestr(self, hash, indent = 0): + if hash.has_key(self): + return (' ' * indent) + "RECURSIVE:%s(%s, %s)" % (str(self.item), repr(id(self)), self.parent and repr(id(self.parent)) or 'None') + hash[self] = True + return (' ' * indent) + "%s(%s, %s)" % (str(self.item), repr(id(self)), self.parent and repr(id(self.parent)) or "None") + "\n" + string.join([n.safestr(hash, indent + 1) for n in self.children], '') + + def __init__(self, tuples, allitems): + self.tuples = tuples + self.allitems = allitems + def sort(self): + (tuples, allitems) = (self.tuples, self.allitems) + nodes = {} + head = None + for tup in tuples: + (parent, child) = (tup[0], tup[1]) + #print "tuple: " + str(parent) + " " + str(child) + + # get parent node + try: + parentnode = nodes[parent] + except KeyError: + parentnode = DependencySorter.Node(parent) + nodes[parent] = parentnode + + # if parent is child, mark "circular" attribute on the node + if parent is child: + parentnode.circular = True + # set head if its nothing + if head is None: + head = parentnode + # nothing more to do for this one + continue + + # get child node + try: + childnode = nodes[child] + except KeyError: + childnode = DependencySorter.Node(child) + nodes[child] = childnode + + # set head if its nothing, move it up to the parent + # if its the child node + if head is None: + head = parentnode + elif head is childnode: + head = parentnode + + # now see, if the parent is an ancestor of the child + c = childnode + while c is not None and c is not parentnode: + c = c.parent + + # nope, so we have to move the child down from whereever + # it currently is to a child of the parent + if c is None: + if childnode.parent is not None: + del childnode.parent.children[childnode] + childnode.parent.children.append(parentnode) + parentnode.children.append(childnode) + childnode.parent = parentnode + #print str(head) + + # go through the total list of items. for those + # that had no dependency tuples, and therefore are not + # in the tree, add them as head nodes in a line + newhead = None + for item in allitems: + if not nodes.has_key(item): + if newhead is None: + newhead = DependencySorter.Node(item) + if head is not None: + head.parent = newhead + newhead.children.append(head) + head = newhead + else: + n = TupleSorter.Node(item) + head.children.append(n) + n.parent = head + return head \ No newline at end of file -- 2.47.2