#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.

Reply via email to