]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
clear new Query._memoized_select_entities in _from_selectable
authorMike Bayer <mike_mp@zzzcomputing.com>
Wed, 30 Jun 2021 20:04:07 +0000 (16:04 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Wed, 30 Jun 2021 20:05:34 +0000 (16:05 -0400)
Fixed regression caused in 1.4.19 due to #6503 and related involving
:meth:`_orm.Query.with_entities` where the new structure used would be
inappropriately transferred to an enclosing :class:`_orm.Query` when making
use of set operations such as :meth:`_orm.Query.union`, causing the JOIN
instructions within to be applied to the outside query as well.

Fixes: #6698
Change-Id: Ia9f294215ebc01330d142a0a3e5be9d02be9380f

doc/build/changelog/unreleased_14/6698.rst [new file with mode: 0644]
lib/sqlalchemy/orm/query.py
test/orm/test_joins.py
test/sql/test_select.py

diff --git a/doc/build/changelog/unreleased_14/6698.rst b/doc/build/changelog/unreleased_14/6698.rst
new file mode 100644 (file)
index 0000000..8ed8711
--- /dev/null
@@ -0,0 +1,9 @@
+.. change::
+    :tags: bug, regression, orm
+    :tickets: 6698
+
+    Fixed regression caused in 1.4.19 due to #6503 and related involving
+    :meth:`_orm.Query.with_entities` where the new structure used would be
+    inappropriately transferred to an enclosing :class:`_orm.Query` when making
+    use of set operations such as :meth:`_orm.Query.union`, causing the JOIN
+    instructions within to be applied to the outside query as well.
index d8f4b4ea7c8db2fdee69ce29d16925fc55c00739..a89b86c7935f91255684cb385c95220d6ed88e40 100644 (file)
@@ -1359,6 +1359,7 @@ class Query(
             "_offset_clause",
             "_last_joined_entity",
             "_legacy_setup_joins",
+            "_memoized_select_entities",
             "_distinct",
             "_distinct_on",
             "_having_criteria",
index 25fa7e6615c1340ca11da4da592df9cffc279c1f..5391892830ed78a20bc64f50c512c31fc601a789 100644 (file)
@@ -18,6 +18,7 @@ from sqlalchemy import String
 from sqlalchemy import Table
 from sqlalchemy import testing
 from sqlalchemy import true
+from sqlalchemy import union
 from sqlalchemy.engine import default
 from sqlalchemy.orm import aliased
 from sqlalchemy.orm import backref
@@ -364,6 +365,73 @@ class JoinTest(QueryTest, AssertsCompiledSQL):
                 "JOIN addresses ON users.id = addresses.user_id",
             )
 
+    @testing.combinations((True,), (False,), argnames="legacy")
+    @testing.combinations((True,), (False,), argnames="threelevel")
+    def test_join_and_union_with_entities(self, legacy, threelevel):
+        """test issue #6698, regression caused by #6503"""
+
+        User, Address, Dingaling = self.classes("User", "Address", "Dingaling")
+
+        if legacy:
+            sess = fixture_session()
+            stmt = sess.query(User).join(Address).with_entities(Address.id)
+        else:
+            stmt = select(User).join(Address).with_only_columns(Address.id)
+
+            stmt = stmt.set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL)
+
+        if threelevel:
+            if legacy:
+                stmt = stmt.join(Address.dingaling).with_entities(Dingaling.id)
+
+                to_union = sess.query(Dingaling.id)
+            else:
+                stmt = stmt.join(Address.dingaling).with_only_columns(
+                    Dingaling.id
+                )
+                to_union = select(Dingaling.id).set_label_style(
+                    LABEL_STYLE_TABLENAME_PLUS_COL
+                )
+        else:
+            if legacy:
+                to_union = sess.query(Address.id)
+            else:
+                to_union = select(Address.id).set_label_style(
+                    LABEL_STYLE_TABLENAME_PLUS_COL
+                )
+
+        if legacy:
+            stmt = stmt.union(to_union)
+        else:
+            stmt = (
+                union(stmt, to_union)
+                .subquery()
+                .select()
+                .set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL)
+            )
+
+        if threelevel:
+            self.assert_compile(
+                stmt,
+                "SELECT anon_1.dingalings_id AS anon_1_dingalings_id FROM "
+                "(SELECT dingalings.id AS dingalings_id "
+                "FROM users JOIN addresses ON users.id = addresses.user_id "
+                "JOIN dingalings ON addresses.id = dingalings.address_id "
+                "UNION "
+                "SELECT dingalings.id AS dingalings_id FROM dingalings) "
+                "AS anon_1",
+            )
+        else:
+            self.assert_compile(
+                stmt,
+                "SELECT anon_1.addresses_id AS anon_1_addresses_id FROM "
+                "(SELECT addresses.id AS addresses_id FROM users "
+                "JOIN addresses ON users.id = addresses.user_id "
+                "UNION "
+                "SELECT addresses.id AS addresses_id FROM addresses) "
+                "AS anon_1",
+            )
+
     def test_invalid_kwarg_join(self):
         User = self.classes.User
         sess = fixture_session()
index d1f9e381f9da824a8edb48e7f438f2e06a197aad..37d43f89f84f1aadb4412ca9be18e5bf6303f844 100644 (file)
@@ -9,6 +9,7 @@ from sqlalchemy import select
 from sqlalchemy import String
 from sqlalchemy import Table
 from sqlalchemy import tuple_
+from sqlalchemy import union
 from sqlalchemy.sql import column
 from sqlalchemy.sql import table
 from sqlalchemy.testing import assert_raises_message
@@ -276,6 +277,21 @@ class FutureSelectTest(fixtures.TestBase, AssertsCompiledSQL):
             "JOIN child ON parent.id = child.parent_id",
         )
 
+    def test_join_implicit_left_side_wo_cols_onelevel_union(self):
+        """test issue #6698, regression from #6503.
+
+        this issue didn't affect Core but testing it here anyway."""
+        stmt = select(parent).join(child).with_only_columns(child.c.id)
+
+        stmt = stmt.union(select(child.c.id))
+        self.assert_compile(
+            stmt,
+            "SELECT child.id FROM parent "
+            "JOIN child ON parent.id = child.parent_id "
+            "UNION "
+            "SELECT child.id FROM child",
+        )
+
     def test_join_implicit_left_side_wo_cols_twolevel(self):
         """test issue #6503"""
         stmt = (
@@ -293,6 +309,28 @@ class FutureSelectTest(fixtures.TestBase, AssertsCompiledSQL):
             "JOIN grandchild ON child.id = grandchild.child_id",
         )
 
+    def test_join_implicit_left_side_wo_cols_twolevel_union(self):
+        """test issue #6698, regression from #6503.
+
+        this issue didn't affect Core but testing it here anyway."""
+        stmt = (
+            select(parent)
+            .join(child)
+            .with_only_columns(child.c.id)
+            .join(grandchild)
+            .with_only_columns(grandchild.c.id)
+        )
+
+        stmt = union(stmt, select(grandchild.c.id))
+        self.assert_compile(
+            stmt,
+            "SELECT grandchild.id FROM parent "
+            "JOIN child ON parent.id = child.parent_id "
+            "JOIN grandchild ON child.id = grandchild.child_id "
+            "UNION "
+            "SELECT grandchild.id FROM grandchild",
+        )
+
     def test_right_nested_inner_join(self):
         inner = child.join(grandchild)