]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
qualify compile_state updates for non-current entities
authorMike Bayer <mike_mp@zzzcomputing.com>
Mon, 23 Aug 2021 15:24:48 +0000 (11:24 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 23 Aug 2021 15:29:27 +0000 (11:29 -0400)
Fixed issue in recently repaired ``Query.with_entities()`` method where the
flag that determines automatic uniquing for legacy ORM ``Query`` objects
only would be set to ``True`` inappropriately in cases where the
``with_entities()`` call would be setting the ``Query`` to return
column-only rows, which are not uniqued.

Fixes: #6924
Change-Id: I81120823914c989bb7a4d13ef2ec08809d8e5a4d

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

diff --git a/doc/build/changelog/unreleased_14/6924.rst b/doc/build/changelog/unreleased_14/6924.rst
new file mode 100644 (file)
index 0000000..215dcc4
--- /dev/null
@@ -0,0 +1,9 @@
+.. change::
+    :tags: bug, orm, regression
+    :tickets: 6924
+
+    Fixed issue in recently repaired ``Query.with_entities()`` method where the
+    flag that determines automatic uniquing for legacy ORM ``Query`` objects
+    only would be set to ``True`` inappropriately in cases where the
+    ``with_entities()`` call would be setting the ``Query`` to return
+    column-only rows, which are not uniqued.
index 83b6586cc2e6bf5064dac406eeb98573d3973ed0..9318bb16346992f91b406e318d31424822c06cc1 100644 (file)
@@ -417,7 +417,10 @@ class ORMFromStatementCompileState(ORMCompileState):
         )
 
         _QueryEntity.to_compile_state(
-            self, statement_container._raw_columns, self._entities
+            self,
+            statement_container._raw_columns,
+            self._entities,
+            is_current_entities=True,
         )
 
         self.current_path = statement_container._compile_options._current_path
@@ -590,6 +593,7 @@ class ORMSelectCompileState(ORMCompileState, SelectState):
                     self,
                     memoized_entities._raw_columns,
                     [],
+                    is_current_entities=False,
                 )
                 for memoized_entities in (
                     select_statement._memoized_select_entities
@@ -597,7 +601,10 @@ class ORMSelectCompileState(ORMCompileState, SelectState):
             }
 
         _QueryEntity.to_compile_state(
-            self, select_statement._raw_columns, self._entities
+            self,
+            select_statement._raw_columns,
+            self._entities,
+            is_current_entities=True,
         )
 
         self.current_path = select_statement._compile_options._current_path
@@ -839,7 +846,9 @@ class ORMSelectCompileState(ORMCompileState, SelectState):
 
         # entities will also set up polymorphic adapters for mappers
         # that have with_polymorphic configured
-        _QueryEntity.to_compile_state(self, query._raw_columns, self._entities)
+        _QueryEntity.to_compile_state(
+            self, query._raw_columns, self._entities, is_current_entities=True
+        )
         return self
 
     @classmethod
@@ -2278,13 +2287,18 @@ class _QueryEntity(object):
     use_id_for_hash = False
 
     @classmethod
-    def to_compile_state(cls, compile_state, entities, entities_collection):
+    def to_compile_state(
+        cls, compile_state, entities, entities_collection, is_current_entities
+    ):
 
         for idx, entity in enumerate(entities):
             if entity._is_lambda_element:
                 if entity._is_sequence:
                     cls.to_compile_state(
-                        compile_state, entity._resolved, entities_collection
+                        compile_state,
+                        entity._resolved,
+                        entities_collection,
+                        is_current_entities,
                     )
                     continue
                 else:
@@ -2294,7 +2308,10 @@ class _QueryEntity(object):
                 if entity.is_selectable:
                     if "parententity" in entity._annotations:
                         _MapperEntity(
-                            compile_state, entity, entities_collection
+                            compile_state,
+                            entity,
+                            entities_collection,
+                            is_current_entities,
                         )
                     else:
                         _ColumnEntity._for_columns(
@@ -2343,12 +2360,15 @@ class _MapperEntity(_QueryEntity):
         "_polymorphic_discriminator",
     )
 
-    def __init__(self, compile_state, entity, entities_collection):
+    def __init__(
+        self, compile_state, entity, entities_collection, is_current_entities
+    ):
         entities_collection.append(self)
-        if compile_state._primary_entity is None:
-            compile_state._primary_entity = self
-        compile_state._has_mapper_entities = True
-        compile_state._has_orm_entities = True
+        if is_current_entities:
+            if compile_state._primary_entity is None:
+                compile_state._primary_entity = self
+            compile_state._has_mapper_entities = True
+            compile_state._has_orm_entities = True
 
         entity = entity._annotations["parententity"]
         entity._post_inspect
index 0562dcb269997ad37b4fb107818298483294df1a..f7ee1a94c1076e79f063449e32d5d7237b19102d 100644 (file)
@@ -29,6 +29,7 @@ from sqlalchemy.orm import joinedload
 from sqlalchemy.orm import mapper
 from sqlalchemy.orm import relationship
 from sqlalchemy.orm import Session
+from sqlalchemy.orm.context import ORMSelectCompileState
 from sqlalchemy.orm.util import join
 from sqlalchemy.sql import column
 from sqlalchemy.sql import table
@@ -1699,6 +1700,59 @@ class MixedEntitiesTest(QueryTest, AssertsCompiledSQL):
             ],
         )
 
+    @testing.combinations((True,), (False,))
+    def test_no_uniquing_cols_legacy(self, with_entities):
+        """test #6924"""
+        User = self.classes.User
+        Address = self.classes.Address
+
+        sess = fixture_session()
+
+        if with_entities:
+            q = (
+                sess.query(User)
+                .join(Address)
+                .filter(Address.user_id == 8)
+                .with_entities(User.id, User.name)
+                .order_by(User.id)
+            )
+        else:
+            q = (
+                sess.query(User.id, User.name)
+                .join(Address)
+                .filter(Address.user_id == 8)
+                .order_by(User.id)
+            )
+
+        is_(q._compile_state()._primary_entity, None)
+
+        eq_(q.all(), [(8, "ed"), (8, "ed"), (8, "ed")])
+
+    @testing.combinations((True,), (False,))
+    def test_no_uniquing_cols(self, with_entities):
+        """test #6924"""
+        User = self.classes.User
+        Address = self.classes.Address
+
+        if with_entities:
+            stmt = (
+                select(User)
+                .join(Address)
+                .filter(Address.user_id == 8)
+                .with_only_columns(User.id, User.name)
+                .order_by(User.id)
+            )
+        else:
+            stmt = (
+                select(User.id, User.name)
+                .join(Address)
+                .filter(Address.user_id == 8)
+                .order_by(User.id)
+            )
+
+        compile_state = ORMSelectCompileState.create_for_statement(stmt, None)
+        is_(compile_state._primary_entity, None)
+
     def test_column_queries_one(self):
         User = self.classes.User