looks like most of the issues are because we're losing insert ordering

on cycles.  so lets reintroduce the organize as tree component, which
works here.   still need to make it meaningful by teaching the save/delete state
actions to receive a set of items to match up
This commit is contained in:
Mike Bayer
2010-04-05 17:24:00 -04:00
parent e0e60a6c94
commit f288339e95
3 changed files with 85 additions and 3 deletions
+15 -2
View File
@@ -219,8 +219,21 @@ class UOWTransaction(object):
print "------------------------"
print self.dependencies
print sort
for rec in sort:
rec.execute(self)
if cycles:
# organize into a tree so that groups of nodes can be
# merged together, allowing better maintenance of insert
# ordering and other things
(head, children) = topological.organize_as_tree(self.dependencies, sort)
stack = [(head, children)]
while stack:
node, children = stack.pop()
node.execute(self)
stack += children
else:
for rec in sort:
rec.execute(self)
def finalize_flush_changes(self):
+47
View File
@@ -114,3 +114,50 @@ def find_cycles(tuples, allitems):
else:
node = stack.pop()
return output
def organize_as_tree(tuples, allitems):
"""Given a list of sorted nodes from a topological sort, organize the
nodes into a tree structure, with as many non-dependent nodes
set as siblings to each other as possible.
returns nodes as tuples (item, children).
"""
nodes = allitems
edges = _EdgeCollection(tuples)
children = util.defaultdict(list)
if not nodes:
return None
# a list of all currently independent subtrees as a tuple of
# (root_node, set_of_all_tree_nodes, set_of_all_cycle_nodes_in_tree)
# order of the list has no semantics for the algorithmic
independents = []
# in reverse topological order
for node in reversed(nodes):
# nodes subtree and cycles contain the node itself
subtree = set([node])
# get a set of dependent nodes of node and its cycles
nodealldeps = edges.outgoing(node)
if nodealldeps:
# iterate over independent node indexes in reverse order so we can efficiently remove them
for index in xrange(len(independents) - 1, -1, -1):
child, childsubtree = independents[index]
# if there is a dependency between this node and an independent node
if (childsubtree.intersection(nodealldeps)):
# prepend child to nodes children
# (append should be fine, but previous implemetation used prepend)
children[node][0:0] = [(child, children[child])]
# merge childs subtree and cycles
subtree.update(childsubtree)
# remove the child from list of independent subtrees
independents[index:index+1] = []
# add node as a new independent subtree
independents.append((node, subtree))
# choose an arbitrary node from list of all independent subtrees
head = independents.pop()[0]
# add all other independent subtrees as a child of the chosen root
# used prepend [0:0] instead of extend to maintain exact behaviour of previous implementation
children[head][0:0] = [(i[0], children[i[0]]) for i in independents]
return (head, children[head])
+23 -1
View File
@@ -371,4 +371,26 @@ class SingleCycleTest(UOWTest):
sess.delete(n1)
sess.delete(n3)
sess.flush()
sess.flush()
def test_bidirectional_multilevel_save(self):
mapper(Node, nodes, properties={
'children':relationship(Node,
backref=backref('parent', remote_side=nodes.c.id)
)
})
sess = create_session()
n1 = Node(data='n1')
n1.children.append(Node(data='n11'))
n1.children.append(Node(data='n12'))
n1.children.append(Node(data='n13'))
n1.children[1].children.append(Node(data='n121'))
n1.children[1].children.append(Node(data='n122'))
n1.children[1].children.append(Node(data='n123'))
sess.add(n1)
sess.flush()
# self.assert_sql_execution(
# testing.db,
# sess.flush,
# )