]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Dont set _set_select_from() for alias object
authorMike Bayer <mike_mp@zzzcomputing.com>
Thu, 19 Jan 2017 16:41:09 +0000 (11:41 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Thu, 19 Jan 2017 18:49:08 +0000 (13:49 -0500)
Fixed bug first introduced in 0.9.7 as a result of :ticket:`3106`
which would cause an incorrect query in some forms of multi-level
subqueryload against aliased entities, with an unnecessary extra
FROM entity in the innermost subquery.

Fixes: #3893
Change-Id: Ic4003c2c1c0206bd22a098fd497a7375c2758305

doc/build/changelog/changelog_11.rst
lib/sqlalchemy/orm/strategies.py
test/orm/test_subquery_relations.py

index c240515c47b9217f4b3f2de2eb0b6791ab96ffb3..63cdd484ec1d49a864cdc82e76711b78075b7dce 100644 (file)
 .. changelog::
     :version: 1.1.6
 
+    .. change:: 3893
+        :tags: bug, orm
+        :tickets: 3893
+
+        Fixed bug first introduced in 0.9.7 as a result of :ticket:`3106`
+        which would cause an incorrect query in some forms of multi-level
+        subqueryload against aliased entities, with an unnecessary extra
+        FROM entity in the innermost subquery.
+
 .. changelog::
     :version: 1.1.5
     :released: January 17, 2017
index 4e3574b5406a9c23e7b8eb8531d7b08b8df1e471..ffd47d17d28b4ec3610ac9b47bddd89b81dd6205 100644 (file)
@@ -851,11 +851,14 @@ class SubqueryLoader(AbstractRelationshipLoader):
 
         # set a real "from" if not present, as this is more
         # accurate than just going off of the column expression
-        if not q._from_obj and orig_entity.mapper.isa(leftmost_mapper):
+        if not q._from_obj and orig_entity.is_mapper and \
+                orig_entity.mapper.isa(leftmost_mapper):
             q._set_select_from([orig_entity], False)
         target_cols = q._adapt_col_list(leftmost_attr)
 
-        # select from the identity columns of the outer
+        # select from the identity columns of the outer.  This will remove
+        # other columns from the query that might suggest the right entity
+        # which is why we try to _set_select_from above.
         q._set_entities(target_cols)
 
         distinct_target_key = leftmost_relationship.distinct_target_key
index ce99260587772a42b03ce30bcc1b6887640ef5dc..03b7985c77255e2c6fb9e4b9e0a205eaed676092 100644 (file)
@@ -2073,6 +2073,85 @@ class JoinedNoLoadConflictTest(fixtures.DeclarativeMappedTest):
         )
 
 
+class SelfRefInheritanceAliasedTest(
+        fixtures.DeclarativeMappedTest,
+        testing.AssertsCompiledSQL):
+    __dialect__ = 'default'
+
+    @classmethod
+    def setup_classes(cls):
+        Base = cls.DeclarativeBasic
+
+        class Foo(Base):
+            __tablename__ = "foo"
+            id = Column(Integer, primary_key=True)
+            type = Column(String(50))
+
+            foo_id = Column(Integer, ForeignKey("foo.id"))
+            foo = relationship(
+                lambda: Foo, foreign_keys=foo_id, remote_side=id)
+
+            __mapper_args__ = {
+                "polymorphic_on": type,
+                "polymorphic_identity": "foo",
+            }
+
+        class Bar(Foo):
+            __mapper_args__ = {
+                "polymorphic_identity": "bar",
+            }
+
+    @classmethod
+    def insert_data(cls):
+        Foo, Bar = cls.classes('Foo', 'Bar')
+
+        session = Session()
+        target = Bar(id=1)
+        b1 = Bar(id=2, foo=Foo(id=3, foo=target))
+        session.add(b1)
+        session.commit()
+
+    def test_twolevel_subquery_w_polymorphic(self):
+        Foo, Bar = self.classes('Foo', 'Bar')
+
+        I = with_polymorphic(Foo, "*", aliased=True)
+        attr1 = Foo.foo.of_type(I)
+        attr2 = I.foo
+
+        s = Session()
+        q = s.query(Foo).filter(Foo.id == 2).options(
+            subqueryload(attr1).subqueryload(attr2))
+
+        self.assert_sql_execution(
+            testing.db,
+            q.all,
+            CompiledSQL(
+                "SELECT foo.id AS foo_id_1, foo.type AS foo_type, "
+                "foo.foo_id AS foo_foo_id FROM foo WHERE foo.id = :id_1",
+                [{'id_1': 2}]
+            ),
+            CompiledSQL(
+                "SELECT foo_1.id AS foo_1_id, foo_1.type AS foo_1_type, "
+                "foo_1.foo_id AS foo_1_foo_id, "
+                "anon_1.foo_foo_id AS anon_1_foo_foo_id "
+                "FROM (SELECT DISTINCT foo.foo_id AS foo_foo_id "
+                "FROM foo WHERE foo.id = :id_1) AS anon_1 "
+                "JOIN foo AS foo_1 ON foo_1.id = anon_1.foo_foo_id "
+                "ORDER BY anon_1.foo_foo_id",
+                {'id_1': 2}
+            ),
+            CompiledSQL(
+                "SELECT foo.id AS foo_id_1, foo.type AS foo_type, "
+                "foo.foo_id AS foo_foo_id, foo_1.foo_id AS foo_1_foo_id "
+                "FROM (SELECT DISTINCT foo.foo_id AS foo_foo_id FROM foo "
+                "WHERE foo.id = :id_1) AS anon_1 "
+                "JOIN foo AS foo_1 ON foo_1.id = anon_1.foo_foo_id "
+                "JOIN foo ON foo.id = foo_1.foo_id ORDER BY foo_1.foo_id",
+                {'id_1': 2}
+            ),
+        )
+
+
 class TestExistingRowPopulation(fixtures.DeclarativeMappedTest):
     @classmethod
     def setup_classes(cls):