def test_one_to_many_delete_parent(self):
        mapper(Node, nodes, properties={'children': relationship(Node)})
        sess = create_session()

        n2, n3 = Node(data='n2', children=[]), Node(data='n3', children=[])
        n1 = Node(data='n1', children=[n2, n3])

        sess.add(n1)
        sess.flush()

        sess.delete(n1)
        self.assert_sql_execution(
            testing.db, sess.flush,
            AllOf(
                CompiledSQL(
                    "UPDATE nodes SET parent_id=:parent_id "
                    "WHERE nodes.id = :nodes_id", lambda ctx: {
                        'nodes_id': n3.id,
                        'parent_id': None
                    }),
                CompiledSQL(
                    "UPDATE nodes SET parent_id=:parent_id "
                    "WHERE nodes.id = :nodes_id", lambda ctx: {
                        'nodes_id': n2.id,
                        'parent_id': None
                    }),
            ),
            CompiledSQL("DELETE FROM nodes WHERE nodes.id = :id",
                        lambda ctx: {'id': n1.id}))
    def test_one_to_many_delete_all(self):
        mapper(User, users, properties={
            'addresses': relationship(Address),
        })
        mapper(Address, addresses)
        sess = create_session()
        a1, a2 = Address(email_address='a1'), Address(email_address='a2')
        u1 = User(name='u1', addresses=[a1, a2])
        sess.add(u1)
        sess.flush()

        sess.delete(u1)
        sess.delete(a1)
        sess.delete(a2)
        self.assert_sql_execution(
            testing.db,
            sess.flush,
            CompiledSQL("DELETE FROM addresses WHERE addresses.id = :id",
                        [{
                            'id': a1.id
                        }, {
                            'id': a2.id
                        }]),
            CompiledSQL("DELETE FROM users WHERE users.id = :id",
                        {'id': u1.id}),
        )
    def test_one_to_many_save(self):
        mapper(User, users, properties={
            'addresses': relationship(Address),
        })
        mapper(Address, addresses)
        sess = create_session()

        a1, a2 = Address(email_address='a1'), Address(email_address='a2')
        u1 = User(name='u1', addresses=[a1, a2])
        sess.add(u1)

        self.assert_sql_execution(
            testing.db,
            sess.flush,
            CompiledSQL("INSERT INTO users (name) VALUES (:name)",
                        {'name': 'u1'}),
            CompiledSQL(
                "INSERT INTO addresses (user_id, email_address) "
                "VALUES (:user_id, :email_address)", lambda ctx: {
                    'email_address': 'a1',
                    'user_id': u1.id
                }),
            CompiledSQL(
                "INSERT INTO addresses (user_id, email_address) "
                "VALUES (:user_id, :email_address)", lambda ctx: {
                    'email_address': 'a2',
                    'user_id': u1.id
                }),
        )
    def test_many_to_one_delete_unloaded(self):
        mapper(User, users)
        mapper(Address, addresses, properties={'parent': relationship(User)})

        parent = User(name='p1')
        c1, c2 = Address(email_address='c1', parent=parent), \
                    Address(email_address='c2', parent=parent)

        session = Session()
        session.add_all([c1, c2])
        session.add(parent)

        session.flush()

        pid = parent.id
        c1id = c1.id
        c2id = c2.id

        session.expire(parent)
        session.expire(c1)
        session.expire(c2)

        session.delete(c1)
        session.delete(c2)
        session.delete(parent)

        # testing that relationships
        # are loaded even if all ids/references are
        # expired
        self.assert_sql_execution(
            testing.db,
            session.flush,
            AllOf(
                # ensure all three m2os are loaded.
                # the selects here are in fact unexpiring
                # each row - the m2o comes from the identity map.
                CompiledSQL(
                    "SELECT addresses.id AS addresses_id, addresses.user_id AS "
                    "addresses_user_id, addresses.email_address AS "
                    "addresses_email_address FROM addresses WHERE addresses.id = "
                    ":param_1", lambda ctx: {'param_1': c1id}),
                CompiledSQL(
                    "SELECT addresses.id AS addresses_id, addresses.user_id AS "
                    "addresses_user_id, addresses.email_address AS "
                    "addresses_email_address FROM addresses WHERE addresses.id = "
                    ":param_1", lambda ctx: {'param_1': c2id}),
                CompiledSQL(
                    "SELECT users.id AS users_id, users.name AS users_name "
                    "FROM users WHERE users.id = :param_1",
                    lambda ctx: {'param_1': pid}),
            ),
            CompiledSQL("DELETE FROM addresses WHERE addresses.id = :id",
                        lambda ctx: [{
                            'id': c1id
                        }, {
                            'id': c2id
                        }]),
            CompiledSQL("DELETE FROM users WHERE users.id = :id",
                        lambda ctx: {'id': pid}),
        )
    def test_many_to_one_delete_target(self):
        mapper(User, users)
        mapper(Address, addresses, properties={'user': relationship(User)})
        sess = create_session()

        u1 = User(name='u1')
        a1, a2 = Address(email_address='a1', user=u1), \
                    Address(email_address='a2', user=u1)
        sess.add_all([a1, a2])
        sess.flush()

        sess.delete(u1)
        a1.user = a2.user = None
        self.assert_sql_execution(
            testing.db,
            sess.flush,
            CompiledSQL(
                "UPDATE addresses SET user_id=:user_id WHERE "
                "addresses.id = :addresses_id",
                lambda ctx: [{
                    u'addresses_id': a1.id,
                    'user_id': None
                }]),
            CompiledSQL(
                "UPDATE addresses SET user_id=:user_id WHERE "
                "addresses.id = :addresses_id",
                lambda ctx: [{
                    u'addresses_id': a2.id,
                    'user_id': None
                }]),
            CompiledSQL("DELETE FROM users WHERE users.id = :id",
                        {'id': u1.id}),
        )
    def test_one_to_many_delete_parent(self):
        mapper(User, users, properties={
            'addresses': relationship(Address),
        })
        mapper(Address, addresses)
        sess = create_session()
        a1, a2 = Address(email_address='a1'), Address(email_address='a2')
        u1 = User(name='u1', addresses=[a1, a2])
        sess.add(u1)
        sess.flush()

        sess.delete(u1)
        self.assert_sql_execution(
            testing.db,
            sess.flush,
            CompiledSQL(
                "UPDATE addresses SET user_id=:user_id WHERE "
                "addresses.id = :addresses_id",
                lambda ctx: [{
                    u'addresses_id': a1.id,
                    'user_id': None
                }]),
            CompiledSQL(
                "UPDATE addresses SET user_id=:user_id WHERE "
                "addresses.id = :addresses_id",
                lambda ctx: [{
                    u'addresses_id': a2.id,
                    'user_id': None
                }]),
            CompiledSQL("DELETE FROM users WHERE users.id = :id",
                        {'id': u1.id}),
        )
    def test_many_to_one_delete_all(self):
        mapper(
            Node,
            nodes,
            properties={'parent': relationship(Node, remote_side=nodes.c.id)})
        sess = create_session()

        n1 = Node(data='n1')
        n2, n3 = Node(data='n2', parent=n1), Node(data='n3', parent=n1)

        sess.add_all([n2, n3])
        sess.flush()

        sess.delete(n1)
        sess.delete(n2)
        sess.delete(n3)
        self.assert_sql_execution(
            testing.db, sess.flush,
            CompiledSQL("DELETE FROM nodes WHERE nodes.id = :id",
                        lambda ctx: [{
                            'id': n2.id
                        }, {
                            'id': n3.id
                        }]),
            CompiledSQL("DELETE FROM nodes WHERE nodes.id = :id",
                        lambda ctx: {'id': n1.id}))
    def test_many_to_one_save(self):
        mapper(
            Node,
            nodes,
            properties={'parent': relationship(Node, remote_side=nodes.c.id)})
        sess = create_session()

        n1 = Node(data='n1')
        n2, n3 = Node(data='n2', parent=n1), Node(data='n3', parent=n1)

        sess.add_all([n2, n3])

        self.assert_sql_execution(
            testing.db, sess.flush,
            CompiledSQL(
                "INSERT INTO nodes (parent_id, data) VALUES "
                "(:parent_id, :data)", {
                    'parent_id': None,
                    'data': 'n1'
                }),
            AllOf(
                CompiledSQL(
                    "INSERT INTO nodes (parent_id, data) VALUES "
                    "(:parent_id, :data)", lambda ctx: {
                        'parent_id': n1.id,
                        'data': 'n2'
                    }),
                CompiledSQL(
                    "INSERT INTO nodes (parent_id, data) VALUES "
                    "(:parent_id, :data)", lambda ctx: {
                        'parent_id': n1.id,
                        'data': 'n3'
                    }),
            ))
    def test_one(self):
        mapper(Parent, parent, properties={
            'c1s':relationship(Child1, primaryjoin=child1.c.parent_id==parent.c.id),
            'c2s':relationship(Child2, primaryjoin=child2.c.parent_id==parent.c.id),
            'c3s':relationship(Child3, primaryjoin=child3.c.parent_id==parent.c.id),
            
            'c1':relationship(Child1, primaryjoin=child1.c.id==parent.c.c1_id, post_update=True),
            'c2':relationship(Child2, primaryjoin=child2.c.id==parent.c.c2_id, post_update=True),
            'c3':relationship(Child3, primaryjoin=child3.c.id==parent.c.c3_id, post_update=True),
        })
        mapper(Child1, child1)
        mapper(Child2, child2)
        mapper(Child3, child3)
        
        sess = create_session()
        
        p1 = Parent('p1')
        c11, c12, c13 = Child1('c1'), Child1('c2'), Child1('c3')
        c21, c22, c23 = Child2('c1'), Child2('c2'), Child2('c3')
        c31, c32, c33 = Child3('c1'), Child3('c2'), Child3('c3')
        
        p1.c1s = [c11, c12, c13]
        p1.c2s = [c21, c22, c23]
        p1.c3s = [c31, c32, c33]
        sess.add(p1)
        sess.flush()
        
        p1.c1 = c12
        p1.c2 = c23
        p1.c3 = c31

        self.assert_sql_execution(
            testing.db, 
            sess.flush,
            CompiledSQL(
                "UPDATE parent SET c1_id=:c1_id, c2_id=:c2_id, "
                "c3_id=:c3_id WHERE parent.id = :parent_id",
                lambda ctx: {'c2_id': c23.id, 'parent_id': p1.id, 'c1_id': c12.id, 'c3_id': c31.id}
            )
        )

        p1.c1 = p1.c2 = p1.c3 = None

        self.assert_sql_execution(
            testing.db, 
            sess.flush,
            CompiledSQL(
                "UPDATE parent SET c1_id=:c1_id, c2_id=:c2_id, "
                "c3_id=:c3_id WHERE parent.id = :parent_id",
                lambda ctx: {'c2_id': None, 'parent_id': p1.id, 'c1_id': None, 'c3_id': None}
            )
        )
        
Beispiel #10
0
    def test_rowswitch_doesntfire(self):
        mapper(User, users)
        mapper(Address, addresses, properties={
            'user':relationship(User, passive_updates=True)
        })

        sess = create_session()
        u1 = User(username='******')
        a1 = Address(user=u1, email='ed@host1')
        
        sess.add(u1)
        sess.add(a1)
        sess.flush()
        
        sess.delete(u1)
        sess.delete(a1)

        u2 = User(username='******')
        a2 = Address(user=u2, email='ed@host1', etc='foo')
        sess.add(u2)
        sess.add(a2)

        from sqlalchemy.test.assertsql import CompiledSQL
        
        # test that the primary key columns of addresses are not
        # being updated as well, since this is a row switch.
        self.assert_sql_execution(testing.db,
                        sess.flush,
                        CompiledSQL(
                                "UPDATE addresses SET etc=:etc WHERE "
                                "addresses.username = :addresses_username AND"
                                " addresses.email = :addresses_email",
                         {'etc': 'foo', 'addresses_username':'******',
                            'addresses_email':'ed@host1'} ),
                    )
Beispiel #11
0
 def test_many_to_many(self):
     mapper(Item, items, properties={
         'keywords':relationship(Keyword, secondary=item_keywords)
     })
     mapper(Keyword, keywords)
     
     sess = create_session()
     k1 = Keyword(name='k1')
     i1 = Item(description='i1', keywords=[k1])
     sess.add(i1)
     self.assert_sql_execution(
             testing.db,
             sess.flush,
             AllOf(
                 CompiledSQL(
                 "INSERT INTO keywords (name) VALUES (:name)",
                 {'name':'k1'}
                 ),
                 CompiledSQL(
                 "INSERT INTO items (description) VALUES (:description)",
                 {'description':'i1'}
                 ),
             ),
             CompiledSQL(
                 "INSERT INTO item_keywords (item_id, keyword_id) "
                 "VALUES (:item_id, :keyword_id)",
                 lambda ctx:{'item_id':i1.id, 'keyword_id':k1.id}
             )
     )
     
     # test that keywords collection isn't loaded
     sess.expire(i1, ['keywords'])
     i1.description = 'i2'
     self.assert_sql_execution(
             testing.db,
             sess.flush,
             CompiledSQL("UPDATE items SET description=:description "
                         "WHERE items.id = :items_id", 
                         lambda ctx:{'description':'i2', 'items_id':i1.id})
     )
    def test_one_to_many_delete_all(self):
        mapper(Node, nodes, properties={'children': relationship(Node)})
        sess = create_session()

        n2, n3 = Node(data='n2', children=[]), Node(data='n3', children=[])
        n1 = Node(data='n1', children=[n2, n3])

        sess.add(n1)
        sess.flush()

        sess.delete(n1)
        sess.delete(n2)
        sess.delete(n3)
        self.assert_sql_execution(
            testing.db, sess.flush,
            CompiledSQL("DELETE FROM nodes WHERE nodes.id = :id",
                        lambda ctx: [{
                            'id': n2.id
                        }, {
                            'id': n3.id
                        }]),
            CompiledSQL("DELETE FROM nodes WHERE nodes.id = :id",
                        lambda ctx: {'id': n1.id}))
Beispiel #13
0
    def testbasic(self):
        """Post_update only fires off when needed.

        This test case used to produce many superfluous update statements,
        particularly upon delete

        """

        mapper(Node, node, properties={
            'children': relation(
                Node,
                primaryjoin=node.c.id==node.c.parent_id,
                lazy=True,
                cascade="all",
                backref=backref("parent", remote_side=node.c.id)
            ),
            'prev_sibling': relation(
                Node,
                primaryjoin=node.c.prev_sibling_id==node.c.id,
                remote_side=node.c.id,
                lazy=True,
                uselist=False),
            'next_sibling': relation(
                Node,
                primaryjoin=node.c.next_sibling_id==node.c.id,
                remote_side=node.c.id,
                lazy=True,
                uselist=False,
                post_update=True)})

        session = create_session()

        def append_child(parent, child):
            if parent.children:
                parent.children[-1].next_sibling = child
                child.prev_sibling = parent.children[-1]
            parent.children.append(child)

        def remove_child(parent, child):
            child.parent = None
            node = child.next_sibling
            node.prev_sibling = child.prev_sibling
            child.prev_sibling.next_sibling = node
            session.delete(child)
        root = Node('root')

        about = Node('about')
        cats = Node('cats')
        stories = Node('stories')
        bruce = Node('bruce')

        append_child(root, about)
        assert(about.prev_sibling is None)
        append_child(root, cats)
        assert(cats.prev_sibling is about)
        assert(cats.next_sibling is None)
        assert(about.next_sibling is cats)
        assert(about.prev_sibling is None)
        append_child(root, stories)
        append_child(root, bruce)
        session.add(root)
        session.flush()

        remove_child(root, cats)
        # pre-trigger lazy loader on 'cats' to make the test easier
        cats.children
        self.assert_sql_execution(
            testing.db, 
            session.flush,
            CompiledSQL("UPDATE node SET prev_sibling_id=:prev_sibling_id "
             "WHERE node.id = :node_id",
             lambda ctx:{'prev_sibling_id':about.id, 'node_id':stories.id}),

            CompiledSQL("UPDATE node SET next_sibling_id=:next_sibling_id "
             "WHERE node.id = :node_id",
             lambda ctx:{'next_sibling_id':stories.id, 'node_id':about.id}),

            CompiledSQL("UPDATE node SET next_sibling_id=:next_sibling_id "
             "WHERE node.id = :node_id",
             lambda ctx:{'next_sibling_id':None, 'node_id':cats.id}),
             
            CompiledSQL("DELETE FROM node WHERE node.id = :id",
             lambda ctx:[{'id':cats.id}])
        )
Beispiel #14
0
    def testpostupdate_o2m(self):
        """A cycle between two rows, with a post_update on the one-to-many"""

        mapper(Ball, ball)
        mapper(Person, person, properties=dict(
            balls=relation(Ball,
                           primaryjoin=ball.c.person_id == person.c.id,
                           remote_side=ball.c.person_id,
                           cascade="all, delete-orphan",
                           post_update=True,
                           backref='person'),
            favorite=relation(Ball,
                              primaryjoin=person.c.favorite_ball_id == ball.c.id,
                              remote_side=person.c.favorite_ball_id)))

        b = Ball(data='some data')
        p = Person(data='some data')
        p.balls.append(b)
        b2 = Ball(data='some data')
        p.balls.append(b2)
        b3 = Ball(data='some data')
        p.balls.append(b3)
        b4 = Ball(data='some data')
        p.balls.append(b4)
        p.favorite = b
        sess = create_session()
        sess.add_all((b,p,b2,b3,b4))

        self.assert_sql_execution(
            testing.db,
            sess.flush,
            CompiledSQL("INSERT INTO ball (person_id, data) "
             "VALUES (:person_id, :data)",
             {'person_id':None, 'data':'some data'}),

            CompiledSQL("INSERT INTO ball (person_id, data) "
             "VALUES (:person_id, :data)",
             {'person_id':None, 'data':'some data'}),

            CompiledSQL("INSERT INTO ball (person_id, data) "
             "VALUES (:person_id, :data)",
             {'person_id':None, 'data':'some data'}),

            CompiledSQL("INSERT INTO ball (person_id, data) "
             "VALUES (:person_id, :data)",
             {'person_id':None, 'data':'some data'}),

            CompiledSQL("INSERT INTO person (favorite_ball_id, data) "
             "VALUES (:favorite_ball_id, :data)",
             lambda ctx:{'favorite_ball_id':b.id, 'data':'some data'}),

            AllOf(
            CompiledSQL("UPDATE ball SET person_id=:person_id "
             "WHERE ball.id = :ball_id",
             lambda ctx:{'person_id':p.id,'ball_id':b.id}),

            CompiledSQL("UPDATE ball SET person_id=:person_id "
             "WHERE ball.id = :ball_id",
             lambda ctx:{'person_id':p.id,'ball_id':b2.id}),

            CompiledSQL("UPDATE ball SET person_id=:person_id "
             "WHERE ball.id = :ball_id",
             lambda ctx:{'person_id':p.id,'ball_id':b3.id}),

            CompiledSQL("UPDATE ball SET person_id=:person_id "
             "WHERE ball.id = :ball_id",
             lambda ctx:{'person_id':p.id,'ball_id':b4.id})
            ),
        )
        
        sess.delete(p)
        
        self.assert_sql_execution(testing.db, sess.flush, 
            AllOf(CompiledSQL("UPDATE ball SET person_id=:person_id "
             "WHERE ball.id = :ball_id",
             lambda ctx:{'person_id': None, 'ball_id': b.id}),

            CompiledSQL("UPDATE ball SET person_id=:person_id "
             "WHERE ball.id = :ball_id",
             lambda ctx:{'person_id': None, 'ball_id': b2.id}),

            CompiledSQL("UPDATE ball SET person_id=:person_id "
             "WHERE ball.id = :ball_id",
             lambda ctx:{'person_id': None, 'ball_id': b3.id}),

            CompiledSQL("UPDATE ball SET person_id=:person_id "
             "WHERE ball.id = :ball_id",
             lambda ctx:{'person_id': None, 'ball_id': b4.id})),

            CompiledSQL("DELETE FROM person WHERE person.id = :id",
             lambda ctx:[{'id':p.id}]),

            CompiledSQL("DELETE FROM ball WHERE ball.id = :id",
             lambda ctx:[{'id': b.id},
                         {'id': b2.id},
                         {'id': b3.id},
                         {'id': b4.id}])
        )
    def test_many_to_many_one(self):
        class Node(Base):
            pass

        mapper(
            Node,
            nodes,
            properties={
                'children':
                relationship(
                    Node,
                    secondary=node_to_nodes,
                    primaryjoin=nodes.c.id == node_to_nodes.c.left_node_id,
                    secondaryjoin=nodes.c.id == node_to_nodes.c.right_node_id,
                    backref='parents'),
                'favorite':
                relationship(Node, remote_side=nodes.c.id)
            })

        sess = create_session()
        n1 = Node(data='n1')
        n2 = Node(data='n2')
        n3 = Node(data='n3')
        n4 = Node(data='n4')
        n5 = Node(data='n5')

        n4.favorite = n3
        n1.favorite = n5
        n5.favorite = n2

        n1.children = [n2, n3, n4]
        n2.children = [n3, n5]
        n3.children = [n5, n4]

        sess.add_all([n1, n2, n3, n4, n5])

        # can't really assert the SQL on this easily
        # since there's too many ways to insert the rows.
        # so check the end result
        sess.flush()
        eq_(
            sess.query(node_to_nodes.c.left_node_id,
                            node_to_nodes.c.right_node_id).\
                    order_by(node_to_nodes.c.left_node_id,
                            node_to_nodes.c.right_node_id).\
                    all(),
            sorted([
                    (n1.id, n2.id), (n1.id, n3.id), (n1.id, n4.id),
                    (n2.id, n3.id), (n2.id, n5.id),
                    (n3.id, n5.id), (n3.id, n4.id)
                ])
        )

        sess.delete(n1)

        self.assert_sql_execution(
            testing.db,
            sess.flush,
            # this is n1.parents firing off, as it should, since
            # passive_deletes is False for n1.parents
            CompiledSQL(
                "SELECT nodes.id AS nodes_id, nodes.data AS nodes_data, "
                "nodes.favorite_node_id AS nodes_favorite_node_id FROM "
                "nodes, node_to_nodes WHERE :param_1 = "
                "node_to_nodes.right_node_id AND nodes.id = "
                "node_to_nodes.left_node_id",
                lambda ctx: {u'param_1': n1.id},
            ),
            CompiledSQL(
                "DELETE FROM node_to_nodes WHERE "
                "node_to_nodes.left_node_id = :left_node_id AND "
                "node_to_nodes.right_node_id = :right_node_id",
                lambda ctx: [{
                    'right_node_id': n2.id,
                    'left_node_id': n1.id
                }, {
                    'right_node_id': n3.id,
                    'left_node_id': n1.id
                }, {
                    'right_node_id': n4.id,
                    'left_node_id': n1.id
                }]),
            CompiledSQL("DELETE FROM nodes WHERE nodes.id = :id",
                        lambda ctx: {'id': n1.id}),
        )

        for n in [n2, n3, n4, n5]:
            sess.delete(n)

        # load these collections
        # outside of the flush() below
        n4.children
        n5.children

        self.assert_sql_execution(
            testing.db,
            sess.flush,
            CompiledSQL(
                "DELETE FROM node_to_nodes WHERE node_to_nodes.left_node_id "
                "= :left_node_id AND node_to_nodes.right_node_id = "
                ":right_node_id", lambda ctx: [{
                    'right_node_id': n5.id,
                    'left_node_id': n3.id
                }, {
                    'right_node_id': n4.id,
                    'left_node_id': n3.id
                }, {
                    'right_node_id': n3.id,
                    'left_node_id': n2.id
                }, {
                    'right_node_id': n5.id,
                    'left_node_id': n2.id
                }]),
            CompiledSQL("DELETE FROM nodes WHERE nodes.id = :id",
                        lambda ctx: [{
                            'id': n4.id
                        }, {
                            'id': n5.id
                        }]),
            CompiledSQL("DELETE FROM nodes WHERE nodes.id = :id",
                        lambda ctx: [{
                            'id': n2.id
                        }, {
                            'id': n3.id
                        }]),
        )
    def test_delete_unloaded_m2o(self):
        mapper(
            Node,
            nodes,
            properties={'parent': relationship(Node, remote_side=nodes.c.id)})

        parent = Node()
        c1, c2 = Node(parent=parent), Node(parent=parent)

        session = Session()
        session.add_all([c1, c2])
        session.add(parent)

        session.flush()

        pid = parent.id
        c1id = c1.id
        c2id = c2.id

        session.expire(parent)
        session.expire(c1)
        session.expire(c2)

        session.delete(c1)
        session.delete(c2)
        session.delete(parent)

        # testing that relationships
        # are loaded even if all ids/references are
        # expired
        self.assert_sql_execution(
            testing.db,
            session.flush,
            AllOf(
                # ensure all three m2os are loaded.
                # the selects here are in fact unexpiring
                # each row - the m2o comes from the identity map.
                CompiledSQL(
                    "SELECT nodes.id AS nodes_id, nodes.parent_id AS "
                    "nodes_parent_id, "
                    "nodes.data AS nodes_data FROM nodes "
                    "WHERE nodes.id = :param_1", lambda ctx: {'param_1': pid}),
                CompiledSQL(
                    "SELECT nodes.id AS nodes_id, nodes.parent_id AS "
                    "nodes_parent_id, "
                    "nodes.data AS nodes_data FROM nodes "
                    "WHERE nodes.id = :param_1",
                    lambda ctx: {'param_1': c1id}),
                CompiledSQL(
                    "SELECT nodes.id AS nodes_id, nodes.parent_id AS "
                    "nodes_parent_id, "
                    "nodes.data AS nodes_data FROM nodes "
                    "WHERE nodes.id = :param_1",
                    lambda ctx: {'param_1': c2id}),
            ),
            CompiledSQL("DELETE FROM nodes WHERE nodes.id = :id",
                        lambda ctx: [{
                            'id': c1id
                        }, {
                            'id': c2id
                        }]),
            CompiledSQL("DELETE FROM nodes WHERE nodes.id = :id",
                        lambda ctx: {'id': pid}),
        )
 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'))
     n12 = Node(data='n12')
     n1.children.append(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)
     self.assert_sql_execution(
         testing.db,
         sess.flush,
         CompiledSQL(
             "INSERT INTO nodes (parent_id, data) VALUES "
             "(:parent_id, :data)", lambda ctx: {
                 'parent_id': None,
                 'data': 'n1'
             }),
         CompiledSQL(
             "INSERT INTO nodes (parent_id, data) VALUES "
             "(:parent_id, :data)", lambda ctx: {
                 'parent_id': n1.id,
                 'data': 'n11'
             }),
         CompiledSQL(
             "INSERT INTO nodes (parent_id, data) VALUES "
             "(:parent_id, :data)", lambda ctx: {
                 'parent_id': n1.id,
                 'data': 'n12'
             }),
         CompiledSQL(
             "INSERT INTO nodes (parent_id, data) VALUES "
             "(:parent_id, :data)", lambda ctx: {
                 'parent_id': n1.id,
                 'data': 'n13'
             }),
         CompiledSQL(
             "INSERT INTO nodes (parent_id, data) VALUES "
             "(:parent_id, :data)", lambda ctx: {
                 'parent_id': n12.id,
                 'data': 'n121'
             }),
         CompiledSQL(
             "INSERT INTO nodes (parent_id, data) VALUES "
             "(:parent_id, :data)", lambda ctx: {
                 'parent_id': n12.id,
                 'data': 'n122'
             }),
         CompiledSQL(
             "INSERT INTO nodes (parent_id, data) VALUES "
             "(:parent_id, :data)", lambda ctx: {
                 'parent_id': n12.id,
                 'data': 'n123'
             }),
     )