]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- Fixed bug regarding "subqueryload" strategy whereby
authorMike Bayer <mike_mp@zzzcomputing.com>
Thu, 6 Jan 2011 17:25:17 +0000 (12:25 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Thu, 6 Jan 2011 17:25:17 +0000 (12:25 -0500)
the join would fail if using a multi-level load
of the form from A->joined-subclass->C [ticket:2014]

CHANGES
lib/sqlalchemy/orm/strategies.py
test/orm/inheritance/test_query.py

diff --git a/CHANGES b/CHANGES
index 617e825d3acb55621a7fc2f85aad4c10d2263276..e055c75986911f386ba714a65f16521650b533dc 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -69,6 +69,10 @@ CHANGES
     strategy would fail if the entity was an aliased()
     construct.  [ticket:1964]
 
+  - Fixed bug regarding "subqueryload" strategy whereby
+    the join would fail if using a multi-level load
+    of the form from A->joined-subclass->C [ticket:2014]
+
   - Fixed indexing of Query objects by -1. It was erroneously
     transformed to the empty slice -1:0 that resulted in
     IndexError. [ticket:1968]
index 7d3e563f41b6565939c806943d0cf152acc308dd..333650ec4d90248646d9b5f4c37674ed3a5a66df 100644 (file)
@@ -746,9 +746,23 @@ class SubqueryLoader(AbstractRelationshipLoader):
                     for i in xrange(0, len(subq_path), 2)
                 ]
 
+        # determine the immediate parent class we are joining from,
+        # which needs to be aliased.
+
         if len(to_join) < 2:
+            # in the case of a one level eager load, this is the
+            # leftmost "left_alias".
             parent_alias = left_alias
+        elif subq_path[-2].isa(self.parent):
+            # In the case of multiple levels, retrieve
+            # it from subq_path[-2]. This is the same as self.parent 
+            # in the vast majority of cases, and [ticket:2014] 
+            # illustrates a case where sub_path[-2] is a subclass
+            # of self.parent
+            parent_alias = mapperutil.AliasedClass(subq_path[-2])
         else:
+            # if of_type() were used leading to this relationship, 
+            # self.parent is more specific than subq_path[-2]
             parent_alias = mapperutil.AliasedClass(self.parent)
 
         local_cols, remote_cols = \
index 61727eb4e107dcf305d44f65a853a514438b610c..b1769294526c2f501bd8f90c723c6bb12b786156 100644 (file)
@@ -1249,7 +1249,7 @@ class SelfReferentialM2MTest(_base.MappedTest, AssertsCompiledSQL):
             assert row.left_child2
 
 class EagerToSubclassTest(_base.MappedTest):
-    """Test joinedloads to subclass mappers"""
+    """Test eager loads to subclass mappers"""
 
     run_setup_classes = 'once'
     run_setup_mappers = 'once'
@@ -1259,19 +1259,29 @@ class EagerToSubclassTest(_base.MappedTest):
     @classmethod
     def define_tables(cls, metadata):
         Table('parent', metadata,
-            Column('id', Integer, primary_key=True, test_needs_autoincrement=True),
+            Column('id', Integer, primary_key=True, 
+                            test_needs_autoincrement=True),
             Column('data', String(10)),
         )
 
         Table('base', metadata,
-            Column('id', Integer, primary_key=True, test_needs_autoincrement=True),
+            Column('id', Integer, primary_key=True, 
+                            test_needs_autoincrement=True),
             Column('type', String(10)),
+            Column('related_id', Integer, ForeignKey('related.id'))
         )
 
         Table('sub', metadata,
             Column('id', Integer, ForeignKey('base.id'), primary_key=True),
             Column('data', String(10)),
-            Column('parent_id', Integer, ForeignKey('parent.id'), nullable=False)
+            Column('parent_id', Integer, 
+                                ForeignKey('parent.id'), nullable=False)
+        )
+
+        Table('related', metadata,
+            Column('id', Integer, primary_key=True,
+                                test_needs_autoincrement=True),
+            Column('data', String(10)),
         )
 
     @classmethod
@@ -1286,52 +1296,109 @@ class EagerToSubclassTest(_base.MappedTest):
         class Sub(Base):
             pass
 
+        class Related(_base.ComparableEntity):
+            pass
+
     @classmethod
     @testing.resolve_artifact_names
     def setup_mappers(cls):
         mapper(Parent, parent, properties={
-            'children':relationship(Sub)
+            'children':relationship(Sub, order_by=sub.c.data)
         })
-        mapper(Base, base, polymorphic_on=base.c.type, polymorphic_identity='b')
-        mapper(Sub, sub, inherits=Base, polymorphic_identity='s')
+        mapper(Base, base, 
+                    polymorphic_on=base.c.type, 
+                    polymorphic_identity='b', 
+                    properties={
+                        'related':relationship(Related)
+                    })
+        mapper(Sub, sub, inherits=Base, 
+                    polymorphic_identity='s')
+        mapper(Related, related)
 
     @classmethod
     @testing.resolve_artifact_names
     def insert_data(cls):
-        sess = create_session()
-        p1 = Parent(data='p1', children=[Sub(data='s1'), Sub(data='s2'), Sub(data='s3')])
-        p2 = Parent(data='p2', children=[Sub(data='s4'), Sub(data='s5')])
+        sess = Session()
+        r1, r2 = Related(data='r1'), Related(data='r2')
+        s1, s2, s3 = Sub(data='s1', related=r1), \
+                        Sub(data='s2', related=r2), \
+                        Sub(data='s3')
+        s4, s5 = Sub(data='s4', related=r2), Sub(data='s5')
+
+        p1 = Parent(data='p1', children=[s1, s2, s3])
+        p2 = Parent(data='p2', children=[s4, s5])
         sess.add(p1)
         sess.add(p2)
-        sess.flush()
+        sess.commit()
 
     @testing.resolve_artifact_names
     def test_joinedload(self):
-        sess = create_session()
+        sess = Session()
         def go():
             eq_(
-                sess.query(Parent).options(joinedload(Parent.children)).all(), 
+                sess.query(Parent).\
+                        options(joinedload(Parent.children)).all(), 
                 [
-                    Parent(data='p1', children=[Sub(data='s1'), Sub(data='s2'), Sub(data='s3')]),
-                    Parent(data='p2', children=[Sub(data='s4'), Sub(data='s5')])
+                    Parent(data='p1', children=[
+                                        Sub(data='s1'), 
+                                        Sub(data='s2'), 
+                                        Sub(data='s3')
+                                    ]),
+                    Parent(data='p2', children=[
+                                        Sub(data='s4'), 
+                                        Sub(data='s5')
+                                    ])
                 ]
             )
         self.assert_sql_count(testing.db, go, 1)
 
     @testing.resolve_artifact_names
     def test_contains_eager(self):
-        sess = create_session()
+        sess = Session()
         def go():
             eq_(
-                sess.query(Parent).join(Parent.children).options(contains_eager(Parent.children)).\
+                sess.query(Parent).join(Parent.children).\
+                                options(contains_eager(Parent.children)).\
                                 order_by(Parent.data, Sub.data).all(), 
                 [
-                    Parent(data='p1', children=[Sub(data='s1'), Sub(data='s2'), Sub(data='s3')]),
-                    Parent(data='p2', children=[Sub(data='s4'), Sub(data='s5')])
+                    Parent(data='p1', children=[
+                                        Sub(data='s1'), 
+                                        Sub(data='s2'), 
+                                        Sub(data='s3')
+                                    ]),
+                    Parent(data='p2', children=[
+                                        Sub(data='s4'), 
+                                        Sub(data='s5')
+                                    ])
                 ]
             )
         self.assert_sql_count(testing.db, go, 1)
 
+    @testing.resolve_artifact_names
+    def test_subq_through_related(self):
+        sess = Session()
+        def go():
+            eq_(
+                sess.query(Parent).\
+                        options(
+                            subqueryload_all(Parent.children, Sub.related)
+                        ).\
+                        order_by(Parent.data).\
+                        all(),
+                [
+                    Parent(data='p1', children=[
+                                Sub(data='s1', related=Related(data='r1')), 
+                                Sub(data='s2', related=Related(data='r2')), 
+                                Sub(data='s3', related=None)
+                            ]),
+                    Parent(data='p2', children=[
+                                Sub(data='s4',related=Related(data='r2')), 
+                                Sub(data='s5',related=None)
+                            ])
+                ]
+            )
+        self.assert_sql_count(testing.db, go, 3)
+
 class SubClassEagerToSubClassTest(_base.MappedTest):
     """Test joinedloads from subclass to subclass mappers"""