#37291: Passing F("pk") to a tuple lookup can hang
-------------------------------------+-------------------------------------
Reporter: Jacob Walls | Type: Bug
Status: new | Component: Database
| layer (models, ORM)
Version: 5.2 | Severity: Release
| blocker
Keywords: compositeprimarykey | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
This test provides `F("pk")` directly to the `TupleIn` lookup. It hangs
due to a sanity check in the composite PK logic:
{{{#!py
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:
{{{#!py
/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__()`:
{{{#!py
def __getitem__(self, subscript):
return Sliced(self, subscript)
}}}
--
Ticket URL: <
https://code.djangoproject.com/ticket/37291>
Django <
https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.