]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
apply loader criteria more specifically when refresh is true
authorMike Bayer <mike_mp@zzzcomputing.com>
Mon, 28 Mar 2022 22:39:19 +0000 (18:39 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 28 Mar 2022 22:41:12 +0000 (18:41 -0400)
Fixed bug in :func:`_orm.with_loader_criteria` function where loader
criteria would not be applied to a joined eager load that were invoked
within the scope of a refresh operation for the parent object.

Fixes: #7862
Change-Id: If1ac86eaa95880b5ec5bdeee292d6e8000aac705

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

diff --git a/doc/build/changelog/unreleased_14/7862.rst b/doc/build/changelog/unreleased_14/7862.rst
new file mode 100644 (file)
index 0000000..00252ec
--- /dev/null
@@ -0,0 +1,7 @@
+.. change::
+    :tags: bug, orm
+    :tickets: 7862
+
+    Fixed bug in :func:`_orm.with_loader_criteria` function where loader
+    criteria would not be applied to a joined eager load that were invoked
+    within the scope of a refresh operation for the parent object.
index 63ed10d501b141b11dfca7ffea85d5550121f0d4..bcd3e7d2334b3ee0d6ffc5e3d49bef30590ae8f7 100644 (file)
@@ -1957,7 +1957,10 @@ class ORMSelectCompileState(ORMCompileState, SelectState):
 
             single_crit = ext_info.mapper._single_table_criterion
 
-            additional_entity_criteria = self._get_extra_criteria(ext_info)
+            if self.compile_options._for_refresh_state:
+                additional_entity_criteria = []
+            else:
+                additional_entity_criteria = self._get_extra_criteria(ext_info)
 
             if single_crit is not None:
                 additional_entity_criteria += (single_crit,)
index 980002776ee0aaa968911bbbb1e6ddecdb7b8a69..2b49d440045c7f3caffa715ab6b5d0393db35097 100644 (file)
@@ -1184,8 +1184,7 @@ class LoaderCriteriaOption(CriteriaOption):
         # if options to limit the criteria to immediate query only,
         # use compile_state.attributes instead
 
-        if not compile_state.compile_options._for_refresh_state:
-            self.get_global_criteria(compile_state.global_attributes)
+        self.get_global_criteria(compile_state.global_attributes)
 
     def get_global_criteria(self, attributes):
         for mp in self._all_mappers():
index 90d8aba2d32183c7e2fea2bf11603a564aa7ef2f..4c89a69ca476629c86ed75c9f50f4ec3cd0f7951 100644 (file)
@@ -55,6 +55,33 @@ class _Fixtures(_fixtures.FixtureTest):
         )
         return User, Address
 
+    @testing.fixture
+    def user_address_custom_strat_fixture(self):
+        users, Address, addresses, User = (
+            self.tables.users,
+            self.classes.Address,
+            self.tables.addresses,
+            self.classes.User,
+        )
+
+        def go(strat):
+            self.mapper_registry.map_imperatively(
+                User,
+                users,
+                properties={
+                    "addresses": relationship(
+                        self.mapper_registry.map_imperatively(
+                            Address, addresses
+                        ),
+                        lazy=strat,
+                        order_by=Address.id,
+                    )
+                },
+            )
+            return User, Address
+
+        return go
+
     @testing.fixture
     def order_item_fixture(self):
         Order, Item = self.classes("Order", "Item")
@@ -220,6 +247,40 @@ class LoaderCriteriaTest(_Fixtures, testing.AssertsCompiledSQL):
             "WHERE users.name != :name_1",
         )
 
+    @testing.combinations(
+        "select",
+        "joined",
+        "subquery",
+        "selectin",
+        "immediate",
+        argnames="loader_strategy",
+    )
+    def test_loader_strategy_on_refresh(
+        self, loader_strategy, user_address_custom_strat_fixture
+    ):
+        User, Address = user_address_custom_strat_fixture(loader_strategy)
+
+        sess = fixture_session()
+
+        @event.listens_for(sess, "do_orm_execute")
+        def add_criteria(orm_context):
+            orm_context.statement = orm_context.statement.options(
+                with_loader_criteria(
+                    Address,
+                    ~Address.id.in_([5, 3]),
+                )
+            )
+
+        u1 = sess.get(User, 7)
+        u2 = sess.get(User, 8)
+        eq_(u1.addresses, [Address(id=1)])
+        eq_(u2.addresses, [Address(id=2), Address(id=4)])
+
+        for i in range(3):
+            sess.expire_all()
+            eq_(u1.addresses, [Address(id=1)])
+            eq_(u2.addresses, [Address(id=2), Address(id=4)])
+
     def test_criteria_post_replace_legacy(self, user_address_fixture):
         User, Address = user_address_fixture