Opened 52 minutes ago
#37291 new Bug
Passing F("pk") to a tuple lookup can hang
| Reported by: | Jacob Walls | Owned by: | |
|---|---|---|---|
| Component: | Database layer (models, ORM) | Version: | 5.2 |
| Severity: | Release blocker | Keywords: | compositeprimarykey |
| Cc: | Triage Stage: | Unreviewed | |
| Has patch: | no | Needs documentation: | no |
| Needs tests: | no | Patch needs improvement: | no |
| Easy pickings: | no | UI/UX: | no |
Description
This test provides F("pk") directly to the TupleIn lookup. It hangs due to a sanity check in the composite PK logic:
diff --git a/tests/foreign_object/test_tuple_lookups.py b/tests/foreign_object/test_tuple_lookups.py index 008f118994..fb1965fa6a 100644 --- a/tests/foreign_object/test_tuple_lookups.py +++ b/tests/foreign_object/test_tuple_lookups.py @@ -168,6 +168,12 @@ class TupleLookupsTests(TestCase): with self.subTest(customer=customer.id, query=str(qs.query)): self.assertSequenceEqual(qs, contacts) + def test_tuple_in_subquery_f(self): + self.assertCountEqual( + Contact.objects.filter(TupleIn(F("pk"), Contact.objects.values("pk"))), + Contact.objects.all(), + ) + def test_tuple_in_rhs_must_be_collection_of_tuples_or_lists(self): test_cases = ( (1, 2, 3),
That sanity check iterates over the left-hand side expression, which is apparently not safe if the left-hand side is an F object, as when __iter__() missing, Python falls back to __getitem__(), and F.__getitem__() can perpetually iterate an index:
/Users/jwalls/django/tests/foreign_object/test_tuple_lookups.py(173)test_tuple_in_subquery_f() -> Contact.objects.filter(TupleIn(F("pk"), Contact.objects.values("pk"))), /Users/jwalls/django/django/db/models/lookups.py(35)__init__() -> self.rhs = self.get_prep_lookup() /Users/jwalls/django/django/db/models/fields/tuple_lookups.py(302)get_prep_lookup() -> self.check_rhs_is_tuple_or_list() /Users/jwalls/django/django/db/models/fields/tuple_lookups.py(63)check_rhs_is_tuple_or_list() -> lhs_str = self.get_lhs_str() /Users/jwalls/django/django/db/models/fields/tuple_lookups.py(89)get_lhs_str() -> names = ", ".join(repr(f.name) for f in self.lhs) /Users/jwalls/django/django/db/models/fields/tuple_lookups.py(89)<genexpr>()->"'pk'" -> names = ", ".join(repr(f.name) for f in self.lhs) > /Users/jwalls/django/django/db/models/expressions.py(898)__getitem__()
F.__getitem__():
def __getitem__(self, subscript): return Sliced(self, subscript)
Note:
See TracTickets
for help on using tickets.