]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
looks like most of the issues are because we're losing insert ordering
authorMike Bayer <mike_mp@zzzcomputing.com>
Mon, 5 Apr 2010 21:24:00 +0000 (17:24 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 5 Apr 2010 21:24:00 +0000 (17:24 -0400)
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

lib/sqlalchemy/orm/unitofwork.py
lib/sqlalchemy/topological.py
test/orm/test_unitofworkv2.py

index 432e2e238817074c9dc33287a75f6ec32bac505a..9f888f8672408d6025f248e301e8ea9f3e6995f9 100644 (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):
index bcf47bd6492bf4a4c2b3236d61690b24dddc32bc..7253cdc4d89d1ad632e2f1faed2bef330e4b64d0 100644 (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])
index 5dd334d6e860a23f3d85abf355702fc4734aed47..e532b14fe6bc63c655873960336cd5690830b615 100644 (file)
@@ -371,4 +371,26 @@ class SingleCycleTest(UOWTest):
         
         sess.delete(n1)
         sess.delete(n3)
-        sess.flush()
\ No newline at end of file
+        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,
+ #       )
+        
\ No newline at end of file