#22014: `prefetch_related` recursion protection does not cover all cases
-------------------------------------+-------------------------------------
Reporter: StillNewb | Owner: nobody
Type: Bug | Status: closed
Component: Database layer | Version: master
(models, ORM) |
Severity: Normal | Resolution: needsinfo
Keywords: prefetch | Triage Stage: Accepted
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Baptiste Mispelon):
* status: new => closed
* resolution: => needsinfo
Comment:
Using a modified version of the `prefetch_recursion_protection_fail.diff​`
patch attached at the top of the ticket, I was able to run the provided
test against the latest master and the problem seems to have been fixed.
Using `git bisect`, I found that the commit that fixed the attached
failing test was bdbe50a491ca41e7d4ebace47bfe8abe50a58211.
Sadly, link from comment:2 is now dead so I can't reproduce any infinite
recursion error so I'm going to draw the same conclusion as I did in
comment:1:
I'm going to close this ticket as `needsinfo`. Please reopen if you can
provide some steps to reproduce the issue (ideally in the form of a unit
test against a current version of Django).
Thanks.
For reference, here's the modified testcase I used:
{{{#!python
class RecursionProtectionTests(TestCase):
def setUp(self):
self.book1 = Book.objects.create()
self.book2 = Book.objects.create()
self.author1 = Author.objects.create(first_book=self.book1,
name='Author #1')
self.author2 = Author.objects.create(first_book=self.book1,
name='Author #2')
self.publisher = Publisher.objects.create()
self.author1.books.add(self.book1)
self.author2.books.add(self.book2)
self.book1.publishers.add(self.publisher)
self.publisher.authors.add(self.author1, self.author2)
def walk(self, authors):
fetched_books = []
for author in authors:
for book in author.books.all():
for publisher in book.publishers.all():
for published_author in publisher.authors.all():
for fetched_book in published_author.books.all():
fetched_books.append(fetched_book)
return fetched_books
@override_settings(DEBUG=True)
def test_descriptors_storing_protection1(self):
qs1 =
Author.objects.filter(pk=self.author1.pk).prefetch_related(Prefetch('books',
Book.objects.prefetch_related('publishers__authors__books')),
'books__publishers__authors__books')
qs2 =
Author.objects.filter(pk=self.author1.pk).prefetch_related(Prefetch('books',
Book.objects.prefetch_related('publishers__authors__books')),
'books__publishers__authors__books', 'books__publishers__authors__books')
def count_queries(qs):
offset = len(connection.queries)
results = self.walk(qs)
return results, len(connection.queries[offset:])
results1, queries1 = count_queries(qs1)
results2, queries2 = count_queries(qs2)
self.assertEqual(len(results1), 2)
self.assertEqual(len(results2), 2)
self.assertEqual(queries1, queries2)
@override_settings(DEBUG=True)
def test_only_unique_queries(self):
qs =
Author.objects.filter(pk=self.author1.pk).prefetch_related(Prefetch('books',
Book.objects.prefetch_related('publishers__authors__books')),
'books__publishers__authors__books')
offset = len(connection.queries)
self.walk(qs)
queries = [q['sql'] for q in connection.queries[offset:]]
self.assertEqual(len(queries), len(set(queries)))
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/22014#comment:5>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
--
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-updates/067.2a7f2f628aeb189827359580ba8756de%40djangoproject.com.