Opened 4 weeks ago

Closed 12 days ago

Last modified 12 days ago

#36149 closed Bug (fixed)

Composite primary key subquery lookup prevent usage or specify fields and are not implemented for exact

Reported by: Simon Charette Owned by: Simon Charette
Component: Database layer (models, ORM) Version: 5.2
Severity: Release blocker Keywords:
Cc: Csirmaz Bendegúz 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

I started playing with tuple lookup support for composite primary key and I noticed two main problems.

First pk__in=query lookups completely disallow specifying which fields should be used in the select clause of the right-hand-side and implicitly set them to the left-hand-side name which assumes the same set of left-hand-side field name is shared by the right-hand-side which is a bad assumption

  • tests/composite_pk/models/tenant.py

    diff --git a/tests/composite_pk/models/tenant.py b/tests/composite_pk/models/tenant.py
    index 6286ed2354..c85869afa7 100644
    a b class Comment(models.Model):  
    4444        related_name="comments",
    4545    )
    4646    text = models.TextField(default="", blank=True)
     47    integer = models.IntegerField(default=0)
    4748
    4849
    4950class Post(models.Model):
  • tests/composite_pk/test_filter.py

    diff --git a/tests/composite_pk/test_filter.py b/tests/composite_pk/test_filter.py
    index fe942b9e5b..78383655a0 100644
    a b def test_filter_comments_by_pk_in(self):  
    182182                    Comment.objects.filter(pk__in=pks).order_by("pk"), objs
    183183                )
    184184
     185    def test_filter_comments_by_pk_in_subquery(self):
     186        self.assertSequenceEqual(
     187            Comment.objects.filter(
     188                pk__in=Comment.objects.filter(pk=self.comment_1.pk),
     189            ),
     190            [self.comment_1],
     191        )
     192        self.assertSequenceEqual(
     193            Comment.objects.filter(
     194                pk__in=Comment.objects.filter(pk=self.comment_1.pk).values(
     195                    "tenant_id", "id"
     196                ),
     197            ),
     198            [self.comment_1],
     199        )
     200        self.comment_2.integer = self.comment_1.id
     201        self.comment_2.save()
     202        self.assertSequenceEqual(
     203            Comment.objects.filter(
     204                pk__in=Comment.objects.values("tenant_id", "integer"),
     205            ),
     206            [self.comment_1],
     207        )
     208
    185209    def test_filter_comments_by_user_and_order_by_pk_asc(self):
    186210        self.assertSequenceEqual(
    187211            Comment.objects.filter(user=self.user_1).order_by("pk"),

Allowing fields to be specified requires lifting a QuerySet resolving time constraint which was entirely by-passable by passing queryset.query instead as the right-hand-side.

The tuple exact lookup should also allow querysets with a single element to be specified as right-hand-side which is currently disallowed

  • tests/composite_pk/test_filter.py

    diff --git a/tests/composite_pk/test_filter.py b/tests/composite_pk/test_filter.py
    index fe942b9e5b..78383655a0 100644
    a b def test_non_outer_ref_subquery(self):  
    450474        with self.assertRaisesMessage(ValueError, msg):
    451475            Comment.objects.filter(pk=pk)
    452476
     477    def test_filter_comments_by_pk_exact_subquery(self):
     478        self.assertSequenceEqual(
     479            Comment.objects.filter(
     480                pk=Comment.objects.filter(pk=self.comment_1.pk)[:1],
     481            ),
     482            [self.comment_1],
     483        )
     484        self.assertSequenceEqual(
     485            Comment.objects.filter(
     486                pk__in=Comment.objects.filter(pk=self.comment_1.pk).values(
     487                    "tenant_id", "id"
     488                )[:1],
     489            ),
     490            [self.comment_1],
     491        )
     492        self.comment_2.integer = self.comment_1.id
     493        self.comment_2.save()
     494        self.assertSequenceEqual(
     495            Comment.objects.filter(
     496                pk__in=Comment.objects.values("tenant_id", "integer"),
     497            )[:1],
     498            [self.comment_1],
     499        )
     500
    453501    def test_outer_ref_not_composite_pk(self):
    454502        subquery = Comment.objects.filter(pk=OuterRef("id")).values("id")
    455503        queryset = Comment.objects.filter(id=Subquery(subquery))

Lastly lifting the constraint that prevented subqueries returning more than one column from being used as a right-hand-side required adjusting the existing exact and in lookup logic to disallow left-hand-side and right-hand-side with mismatching number of fields.

Change History (7)

comment:1 by Sarah Boyce, 4 weeks ago

Cc: Csirmaz Bendegúz added
Has patch: unset
Owner: set to Simon Charette
Triage Stage: UnreviewedAccepted

Thank you!

comment:2 by Sarah Boyce, 4 weeks ago

Has patch: set

comment:3 by Sarah Boyce, 4 weeks ago

Patch needs improvement: set

comment:4 by Simon Charette, 2 weeks ago

Patch needs improvement: unset

comment:5 by Sarah Boyce, 12 days ago

Triage Stage: AcceptedReady for checkin

comment:6 by Sarah Boyce <42296566+sarahboyce@…>, 12 days ago

Resolution: fixed
Status: assignedclosed

In 41239fe3:

Fixed #36149 -- Allowed subquery values against tuple exact and in lookups.

Non-tuple exact and in lookups have specialized logic for subqueries that can
be adapted to properly assign select mask if unspecified and ensure the number
of involved members are matching on both side of the operator.

comment:7 by Sarah Boyce <42296566+sarahboyce@…>, 12 days ago

In dc1c9b4:

[5.2.x] Fixed #36149 -- Allowed subquery values against tuple exact and in lookups.

Non-tuple exact and in lookups have specialized logic for subqueries that can
be adapted to properly assign select mask if unspecified and ensure the number
of involved members are matching on both side of the operator.

Backport of 41239fe34d64e801212dccaa4585e4802d0fac68 from main.

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