Opened 5 weeks ago

Closed 4 weeks ago

Last modified 4 weeks ago

#36109 closed Bug (fixed)

Three or more stacked FilteredRelations can create a RecursionError

Reported by: Peter DeVita Owned by: Peter DeVita
Component: Database layer (models, ORM) Version: 5.1
Severity: Normal Keywords: FilteredRelation
Cc: Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Example test case, this belongs to FilteredRelationTests

    def test_three_level_nested_chained_relations(self):
        borrower1 = Borrower.objects.create(name="Jenny")
        # borrower 1 reserves, rents, and returns book1.
        Reservation.objects.create(
            borrower=borrower1,
            book=self.book1,
            state=Reservation.STOPPED,
        )
        qs = (
            Author.objects.annotate(
                my_books=FilteredRelation(
                    "book"
                ),
                my_reserved_books=FilteredRelation(
                    "my_books__reservation",
                    condition=Q(my_books__reservation__state=Reservation.STOPPED)
                ),
                my_readers=FilteredRelation(
                    "my_reserved_books__borrower",
                    condition=Q(my_reserved_books__borrower=borrower1)
                )
            )
        )
        qs = qs.filter(
            my_readers=borrower1
        )
        self.assertQuerySetEqual(
            qs,
            [
                ("Alice",)
            ],
            lambda x: (x.name,),
        )

This will result in a RecursionError when the filter is applied. From what I can understand, this is because on Query when it gets to setup_joins and attempts to setup the join for the my_readers FilteredRelation, it is not allowed to reuse the aliases from the first two FilteredRelations. It then constantly recurses, trying to setup the joins and then not being able to reuse the joins.

I'm no ORM expert but the fix might be as simple as just adding the FilteredRelation's join alias to the can_reuse set. Any joins that were going to that table but not through the FilteredRelation should be unaffected since they weren't going to reference its alias anyways. Any joins that do reference that FilteredRelation should always want to reuse this too. The fix is in my diff/PR and tests all pass with it.

This bug might also be only because we're filtering on the last join in the set as the first filter condition, it might be possible to force the aliases to be reusable if you filtered one at a time on each FilteredRelation leading up to it.

Attachments (1)

filteredrelation_stack.diff (2.1 KB ) - added by Peter DeVita 5 weeks ago.

Download all attachments as: .zip

Change History (7)

by Peter DeVita, 5 weeks ago

Attachment: filteredrelation_stack.diff added

comment:1 by Anirudh Konidala, 5 weeks ago

Owner: set to Anirudh Konidala
Status: newassigned

comment:2 by Peter DeVita, 5 weeks ago

Owner: changed from Anirudh Konidala to Peter DeVita

Ah I didn't realize how the ownership system works, my bad. Sorry Anirudh, I already have a PR open for this so I'm going to reassign ownership to myself.

comment:3 by Tim Graham, 5 weeks ago

Triage Stage: UnreviewedAccepted

comment:4 by Sarah Boyce, 5 weeks ago

Triage Stage: AcceptedReady for checkin

comment:5 by Sarah Boyce <42296566+sarahboyce@…>, 4 weeks ago

Resolution: fixed
Status: assignedclosed

In 8eca4077:

Fixed #36109 -- Fixed RecursionError when stacking FilteredRelation joins.

comment:6 by Sarah Boyce <42296566+sarahboyce@…>, 4 weeks ago

In 720ef7a:

[5.2.x] Fixed #36109 -- Fixed RecursionError when stacking FilteredRelation joins.

Backport of 8eca4077f60fa0705ecfd9437c9ceaeef7a3808b from main.

Note: See TracTickets for help on using tickets.
Back to Top