From: Mike Bayer Date: Thu, 19 Jan 2017 16:41:09 +0000 (-0500) Subject: Dont set _set_select_from() for alias object X-Git-Tag: rel_1_1_6~29^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4ae02f46e944ac11af5ad77e5ff5f06963b63b3d;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Dont set _set_select_from() for alias object 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 --- diff --git a/doc/build/changelog/changelog_11.rst b/doc/build/changelog/changelog_11.rst index c240515c47..63cdd484ec 100644 --- a/doc/build/changelog/changelog_11.rst +++ b/doc/build/changelog/changelog_11.rst @@ -21,6 +21,15 @@ .. 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 diff --git a/lib/sqlalchemy/orm/strategies.py b/lib/sqlalchemy/orm/strategies.py index 4e3574b540..ffd47d17d2 100644 --- a/lib/sqlalchemy/orm/strategies.py +++ b/lib/sqlalchemy/orm/strategies.py @@ -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 diff --git a/test/orm/test_subquery_relations.py b/test/orm/test_subquery_relations.py index ce99260587..03b7985c77 100644 --- a/test/orm/test_subquery_relations.py +++ b/test/orm/test_subquery_relations.py @@ -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):