]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
don't mutate the statement in ORM compile
authorMike Bayer <mike_mp@zzzcomputing.com>
Wed, 28 Apr 2021 00:44:46 +0000 (20:44 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Wed, 28 Apr 2021 02:18:01 +0000 (22:18 -0400)
Fixed issue where using a :class:`_sql.Select` as a subquery in an ORM
context would modify the :class:`_sql.Select` in place to disable
eagerloads on that object, which would then cause that same
:class:`_sql.Select` to not eagerload if it were then re-used in a
top-level execution context.

Fixes: #6378
Change-Id: I945048c4c148587b933fb65a3fc83a05d05c052d

doc/build/changelog/unreleased_14/6378.rst [new file with mode: 0644]
lib/sqlalchemy/orm/context.py
test/orm/test_eager_relations.py

diff --git a/doc/build/changelog/unreleased_14/6378.rst b/doc/build/changelog/unreleased_14/6378.rst
new file mode 100644 (file)
index 0000000..bac2b0f
--- /dev/null
@@ -0,0 +1,10 @@
+.. change::
+    :tags: bug, orm
+    :tickets: 6378
+
+    Fixed issue where using a :class:`_sql.Select` as a subquery in an ORM
+    context would modify the :class:`_sql.Select` in place to disable
+    eagerloads on that object, which would then cause that same
+    :class:`_sql.Select` to not eagerload if it were then re-used in a
+    top-level execution context.
+
index 6cdad9f4171a25bf8ff0096e9211cb7310047c16..f62115fdaf3cae297d58d859106329951d7a7624 100644 (file)
@@ -517,15 +517,6 @@ class ORMSelectCompileState(ORMCompileState, SelectState):
             for_statement
         ) = select_statement._compile_options._for_statement
 
-        if not for_statement and not toplevel:
-            # for subqueries, turn off eagerloads.
-            # if "for_statement" mode is set, Query.subquery()
-            # would have set this flag to False already if that's what's
-            # desired
-            select_statement._compile_options += {
-                "_enable_eagerloads": False,
-            }
-
         # generally if we are from Query or directly from a select()
         self.use_legacy_query_style = (
             select_statement._compile_options._use_legacy_query_style
@@ -546,6 +537,15 @@ class ORMSelectCompileState(ORMCompileState, SelectState):
 
         self.compile_options = select_statement._compile_options
 
+        if not for_statement and not toplevel:
+            # for subqueries, turn off eagerloads.
+            # if "for_statement" mode is set, Query.subquery()
+            # would have set this flag to False already if that's what's
+            # desired
+            self.compile_options += {
+                "_enable_eagerloads": False,
+            }
+
         # determine label style.   we can make different decisions here.
         # at the moment, trying to see if we can always use DISAMBIGUATE_ONLY
         # rather than LABEL_STYLE_NONE, and if we can use disambiguate style
index de49e3d18f3eb3b2f568c8759c00d9e86d39b209..4e11f986378763c43b7e6a94208bad6cc6e0b1c1 100644 (file)
@@ -83,6 +83,43 @@ class EagerTest(_fixtures.FixtureTest, testing.AssertsCompiledSQL):
         )
         eq_(self.static.user_address_result, q.order_by(User.id).all())
 
+    def test_no_render_in_subquery(self):
+        """test #6378"""
+
+        users, Address, addresses, User = (
+            self.tables.users,
+            self.classes.Address,
+            self.tables.addresses,
+            self.classes.User,
+        )
+
+        mapper(
+            User,
+            users,
+            properties={
+                "addresses": relationship(
+                    mapper(Address, addresses),
+                    lazy="joined",
+                    order_by=Address.id,
+                )
+            },
+        )
+
+        stmt = select(User)
+        self.assert_compile(
+            select(stmt.subquery()),
+            "SELECT anon_1.id, anon_1.name FROM (SELECT users.id AS id, "
+            "users.name AS name FROM users) AS anon_1",
+        )
+
+        self.assert_compile(
+            stmt,
+            "SELECT users.id, users.name, addresses_1.id AS id_1, "
+            "addresses_1.user_id, addresses_1.email_address FROM users "
+            "LEFT OUTER JOIN addresses AS addresses_1 "
+            "ON users.id = addresses_1.user_id ORDER BY addresses_1.id",
+        )
+
     def test_late_compile(self):
         User, Address, addresses, users = (
             self.classes.User,