]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
re-apply right memo for nested ORMJoin when splicing
authorMike Bayer <mike_mp@zzzcomputing.com>
Tue, 8 Oct 2024 14:29:34 +0000 (10:29 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Wed, 9 Oct 2024 13:17:31 +0000 (09:17 -0400)
Fixed regression caused by fixes to joined eager loading in
:ticket:`11449`, where a particular joinedload case could not be asserted
correctly.   We now have an example of that case so the assertion has been
repaired to allow for it.

Fixes: #11965
Change-Id: I2e0a594981534f4aaeff361a2f8cf1a0fba8de8f
(cherry picked from commit 43b974a34957f22963e7faf44f0798c8179adcfc)

doc/build/changelog/unreleased_20/11965.rst [new file with mode: 0644]
lib/sqlalchemy/orm/strategies.py
lib/sqlalchemy/orm/util.py
test/orm/test_eager_relations.py

diff --git a/doc/build/changelog/unreleased_20/11965.rst b/doc/build/changelog/unreleased_20/11965.rst
new file mode 100644 (file)
index 0000000..1f9294c
--- /dev/null
@@ -0,0 +1,9 @@
+.. change::
+    :tags: bug, orm
+    :tickets: 11965
+
+    Fixed regression caused by fixes to joined eager loading in
+    :ticket:`11449`, where a particular joinedload case could not be asserted
+    correctly.   We now have an example of that case so the assertion has been
+    repaired to allow for it.
+
index 996bdbc1d97046025460a011133a48759b49b641..3f947a8d7438f2d51bb73f3211425fa79cd80903 100644 (file)
@@ -2694,7 +2694,8 @@ class JoinedLoader(AbstractRelationshipLoader):
         # lets look at our path we are satisfying and see if we're in the
         # wrong place.  This is specifically for when our entity may
         # appear more than once in the path, issue #11449
-        if detected_existing_path:
+        # updated in issue #11965.
+        if detected_existing_path and len(detected_existing_path) > 2:
             # this assertion is currently based on how this call is made,
             # where given a join_obj, the call will have these parameters as
             # entity_inside_join_structure=join_obj._left_memo
index 2b4ac3c9d7c9dd6c792f398551f36a18bd3ba652..c9fa79c804bfc52e5420fb3f32cfa846698d06eb 100644 (file)
@@ -1944,7 +1944,7 @@ class _ORMJoin(expression.Join):
             self.onclause,
             isouter=self.isouter,
             _left_memo=self._left_memo,
-            _right_memo=None,
+            _right_memo=other._left_memo._path_registry,
         )
 
         return _ORMJoin(
index bc3d8f10c2c609dbdef091e77d93d59fd018c786..7e0eca62c6532f141f2942e48e09d2cb8152aa4f 100644 (file)
@@ -26,6 +26,8 @@ from sqlalchemy.orm import joinedload
 from sqlalchemy.orm import lazyload
 from sqlalchemy.orm import Load
 from sqlalchemy.orm import load_only
+from sqlalchemy.orm import Mapped
+from sqlalchemy.orm import mapped_column
 from sqlalchemy.orm import relationship
 from sqlalchemy.orm import Session
 from sqlalchemy.orm import undefer
@@ -7110,3 +7112,94 @@ class SingletonConstantSubqTest(_fixtures.FixtureTest):
             )
 
         self.assert_sql_count(testing.db, go, 1)
+
+
+class NestedInnerjoinTestIssue11965(
+    fixtures.DeclarativeMappedTest, testing.AssertsCompiledSQL
+):
+    """test for issue #11965, regression from #11449"""
+
+    __dialect__ = "default"
+
+    @classmethod
+    def setup_classes(cls):
+        Base = cls.DeclarativeBasic
+
+        class Source(Base):
+            __tablename__ = "source"
+            id: Mapped[int] = mapped_column(primary_key=True)
+
+        class Day(Base):
+            __tablename__ = "day"
+            id: Mapped[int] = mapped_column(primary_key=True)
+
+        class Run(Base):
+            __tablename__ = "run"
+            id: Mapped[int] = mapped_column(primary_key=True)
+
+            source_id: Mapped[int] = mapped_column(
+                ForeignKey(Source.id), nullable=False
+            )
+            source = relationship(Source, lazy="joined", innerjoin=True)
+
+            day = relationship(
+                Day,
+                lazy="joined",
+                innerjoin=True,
+            )
+            day_id: Mapped[int] = mapped_column(
+                ForeignKey(Day.id), nullable=False
+            )
+
+        class Event(Base):
+            __tablename__ = "event"
+
+            id: Mapped[int] = mapped_column(primary_key=True)
+            run_id: Mapped[int] = mapped_column(
+                ForeignKey(Run.id), nullable=False
+            )
+            run = relationship(Run, lazy="joined", innerjoin=True)
+
+        class Room(Base):
+            __tablename__ = "room"
+
+            id: Mapped[int] = mapped_column(primary_key=True)
+            event_id: Mapped[int] = mapped_column(
+                ForeignKey(Event.id), nullable=False
+            )
+            event = relationship(Event, foreign_keys=event_id, lazy="joined")
+
+    @classmethod
+    def insert_data(cls, connection):
+        Room, Run, Source, Event, Day = cls.classes(
+            "Room", "Run", "Source", "Event", "Day"
+        )
+        run = Run(source=Source(), day=Day())
+        event = Event(run=run)
+        room = Room(event=event)
+        with Session(connection) as session:
+            session.add(room)
+            session.commit()
+
+    def test_compile(self):
+        Room = self.classes.Room
+        self.assert_compile(
+            select(Room),
+            "SELECT room.id, room.event_id, source_1.id AS id_1, "
+            "day_1.id AS id_2, run_1.id AS id_3, run_1.source_id, "
+            "run_1.day_id, event_1.id AS id_4, event_1.run_id "
+            "FROM room LEFT OUTER JOIN "
+            "(event AS event_1 "
+            "JOIN run AS run_1 ON run_1.id = event_1.run_id "
+            "JOIN day AS day_1 ON day_1.id = run_1.day_id "
+            "JOIN source AS source_1 ON source_1.id = run_1.source_id) "
+            "ON event_1.id = room.event_id",
+        )
+
+    def test_roundtrip(self):
+        Room = self.classes.Room
+        session = fixture_session()
+        rooms = session.scalars(select(Room)).unique().all()
+        session.close()
+        # verify eager-loaded correctly
+        assert rooms[0].event.run.day