Skip to content

Commit

Permalink
[1.11.x] Fixed #26522 -- Fixed a nondeterministic AssertionError in Q…
Browse files Browse the repository at this point in the history
…uerySet combining.

Thanks Andrew Brown for the test case.

Backport of 9bbb6e2 from master
  • Loading branch information
rlmv authored and timgraham committed Sep 1, 2017
1 parent 511dfb3 commit 8d66bff
Show file tree
Hide file tree
Showing 5 changed files with 25 additions and 4 deletions.
1 change: 1 addition & 0 deletions AUTHORS
Expand Up @@ -110,6 +110,7 @@ answer newbie questions, and generally made Django that much better:
berto
Bill Fenner <fenner@gmail.com>
Bjørn Stabell <bjorn@exoweb.net>
Bo Marchman <bo.marchman@gmail.com>
Bojan Mihelac <bmihelac@mihelac.org>
Bouke Haarsma <bouke@haarsma.eu>
Božidar Benko <bbenko@gmail.com>
Expand Down
2 changes: 1 addition & 1 deletion django/db/models/sql/query.py
Expand Up @@ -133,7 +133,7 @@ def __init__(self, model, where=WhereNode):
# types they are. The key is the alias of the joined table (possibly
# the table name) and the value is a Join-like object (see
# sql.datastructures.Join for more information).
self.alias_map = {}
self.alias_map = OrderedDict()
# Sometimes the query contains references to aliases in outer queries (as
# a result of split_exclude). Correct alias quoting needs to know these
# aliases too.
Expand Down
3 changes: 3 additions & 0 deletions docs/releases/1.11.5.txt
Expand Up @@ -40,3 +40,6 @@ Bugfixes
weren't logged in the admin change history (:ticket:`27998`) and prevented
``ManyToManyField`` initial data in model forms from being affected by
subsequent model changes (:ticket:`28543`).

* Fixed non-deterministic results or an ``AssertionError`` crash in some
queries with multiple joins (:ticket:`26522`).
5 changes: 5 additions & 0 deletions tests/queries/models.py
Expand Up @@ -709,6 +709,11 @@ class Classroom(models.Model):
students = models.ManyToManyField(Student, related_name='classroom')


class Teacher(models.Model):
schools = models.ManyToManyField(School)
friends = models.ManyToManyField('self')


class Ticket23605AParent(models.Model):
pass

Expand Down
18 changes: 15 additions & 3 deletions tests/queries/tests.py
Expand Up @@ -28,9 +28,9 @@
ProxyCategory, ProxyObjectA, ProxyObjectB, Ranking, Related,
RelatedIndividual, RelatedObject, Report, ReportComment, ReservedName,
Responsibility, School, SharedConnection, SimpleCategory, SingleObject,
SpecialCategory, Staff, StaffUser, Student, Tag, Task, Ticket21203Child,
Ticket21203Parent, Ticket23605A, Ticket23605B, Ticket23605C, TvChef, Valid,
X,
SpecialCategory, Staff, StaffUser, Student, Tag, Task, Teacher,
Ticket21203Child, Ticket21203Parent, Ticket23605A, Ticket23605B,
Ticket23605C, TvChef, Valid, X,
)


Expand Down Expand Up @@ -1391,6 +1391,18 @@ def test_combine_join_reuse(self):
self.assertEqual(len(combined), 1)
self.assertEqual(combined[0].name, 'a1')

def test_join_reuse_order(self):
# Join aliases are reused in order. This shouldn't raise AssertionError
# because change_map contains a circular reference (#26522).
s1 = School.objects.create()
s2 = School.objects.create()
s3 = School.objects.create()
t1 = Teacher.objects.create()
otherteachers = Teacher.objects.exclude(pk=t1.pk).exclude(friends=t1)
qs1 = otherteachers.filter(schools=s1).filter(schools=s2)
qs2 = otherteachers.filter(schools=s1).filter(schools=s3)
self.assertQuerysetEqual(qs1 | qs2, [])

def test_ticket7095(self):
# Updates that are filtered on the model being updated are somewhat
# tricky in MySQL.
Expand Down

0 comments on commit 8d66bff

Please sign in to comment.