Code

Ticket #10113: aggr_joins.diff

File aggr_joins.diff, 5.9 KB (added by Koen Biermans <koen.biermans@…>, 5 years ago)

small testcase demonstrating the problem

Line 
1Index: tests/regressiontests/aggr_joins/__init__.py
2===================================================================
3Index: tests/regressiontests/aggr_joins/fixtures/initial_data.json
4===================================================================
5--- tests/regressiontests/aggr_joins/fixtures/initial_data.json (revision 0)
6+++ tests/regressiontests/aggr_joins/fixtures/initial_data.json (revision 0)
7@@ -0,0 +1,104 @@
8+[
9+    {
10+        "pk": 1,
11+        "model": "aggr_joins.category",
12+        "fields": {
13+            "name": "Cat 1",
14+            "order": 2
15+        }
16+    },
17+    {
18+        "pk": 2,
19+        "model": "aggr_joins.category",
20+        "fields": {
21+            "name": "Cat 2",
22+            "order": 1
23+        }
24+    },
25+    {
26+        "pk": 1,
27+        "model": "aggr_joins.group",
28+        "fields": {
29+            "name": "Group 1",
30+            "category": 1
31+        }
32+    },
33+    {
34+        "pk": 2,
35+        "model": "aggr_joins.group",
36+        "fields": {
37+            "name": "Group 2",
38+            "category": 2
39+        }
40+    },
41+    {
42+        "pk": 3,
43+        "model": "aggr_joins.group",
44+        "fields": {
45+            "name": "Group 3",
46+            "category": 1
47+        }
48+    },
49+    {
50+        "pk": 4,
51+        "model": "aggr_joins.group",
52+        "fields": {
53+            "name": "Group 4",
54+            "category": 2
55+        }
56+    },
57+    {
58+        "pk": 1,
59+        "model": "aggr_joins.registration",
60+        "fields": {
61+            "group": 1,
62+            "type": 1,
63+            "time": 1
64+        }
65+    },
66+    {
67+        "pk": 2,
68+        "model": "aggr_joins.registration",
69+        "fields": {
70+            "group": 1,
71+            "type": 2,
72+            "time": 2
73+        }
74+    },
75+    {
76+        "pk": 3,
77+        "model": "aggr_joins.registration",
78+        "fields": {
79+            "group": 2,
80+            "type": 1,
81+            "time": 4
82+        }
83+    },
84+    {
85+        "pk": 4,
86+        "model": "aggr_joins.registration",
87+        "fields": {
88+            "group": 2,
89+            "type": 2,
90+            "time": 8
91+        }
92+    },
93+    {
94+        "pk": 5,
95+        "model": "aggr_joins.registration",
96+        "fields": {
97+            "group": 3,
98+            "type": 1,
99+            "time": 16
100+        }
101+    },
102+    {
103+        "pk": 6,
104+        "model": "aggr_joins.registration",
105+        "fields": {
106+            "group": 3,
107+            "type": 2,
108+            "time": 32
109+        }
110+    }
111+]
112Index: tests/regressiontests/aggr_joins/models.py
113===================================================================
114--- tests/regressiontests/aggr_joins/models.py  (revision 0)
115+++ tests/regressiontests/aggr_joins/models.py  (revision 0)
116@@ -0,0 +1,94 @@
117+# coding: utf-8
118+# test for fault in joins with aggregates
119+from django.db import models
120+
121+class Category(models.Model):
122+    name = models.CharField(max_length=100)
123+    order = models.IntegerField()
124+
125+    def __unicode__(self):
126+        return self.name
127+
128+class Group(models.Model):
129+    category = models.ForeignKey(Category)
130+    name = models.CharField(max_length=300)
131+
132+    def __unicode__(self):
133+        return self.name
134+
135+class Registration(models.Model):
136+    group = models.ForeignKey(Group)
137+    type = models.SmallIntegerField()
138+    time = models.IntegerField()
139+
140+__test__ = {'API_TESTS': """
141+>>> from django.db.models import Sum
142+
143+# the categories are reversely ordered:
144+>>> Category.objects.order_by('order')
145+[<Category: Cat 2>, <Category: Cat 1>]
146+
147+# all groups order by category.order
148+>>> qs = Group.objects.order_by('category__order').distinct()
149+>>> [(group.id, group.category.id) for group in qs]
150+[(2, 2), (4, 2), (1, 1), (3, 1)]
151+
152+# annotated
153+>>> qs = Group.objects.annotate(sum_time = Sum('registration__time') \
154+    ).order_by('id')
155+>>> [(group.id, group.category.id, group.sum_time) for group in qs]
156+[(1, 1, 3), (2, 2, 12), (3, 1, 48), (4, 2, None)]
157+
158+# annotated and ordered
159+# this fails on PostGres because the ordering column is not included in the
160+# GROUP BY
161+>>> qs = Group.objects.annotate(sum_time = Sum('registration__time') \
162+    ).order_by('category__order')
163+>>> [(group.id, group.category.id, group.sum_time) for group in qs]
164+[(2, 2, 12), (4, 2, None), (1, 1, 3), (3, 1, 48)]
165+
166+# filtered and ordered
167+# this is a dummy filter, but it joins the tables so only the ones
168+# with registrations return
169+# requires distinct
170+# also fails on postgres
171+>>> qs = Group.objects.filter(registration__type__gt=0 \
172+    ).order_by('category__order').distinct()
173+>>> [(group.id, group.category.id) for group in qs]
174+[(2, 2), (1, 1), (3, 1)]
175+
176+# filtered, annotated and ordered
177+# this fails on postgres (missing in group by)
178+# but also on sqlite: wrong result
179+# I get this: [(4, 2, 256), (2, 2, 12), (1, 1, 3), (3, 1, 240)]
180+# the result for group 4 includes all registrations
181+>>> qs = Group.objects.filter(registration__type__gt=0 \
182+    ).annotate(sum_time = Sum('registration__time') \
183+    ).order_by('category__order')
184+>>> [(group.id, group.category.id, group.sum_time) for group in qs]
185+[(2, 2, 12), (1, 1, 3), (3, 1, 48)]
186+>>> qs.query.as_sql()
187+
188+# notice how the inner join for the order_by comes after the left outer joins
189+# putting the inner join first gives the correct result
190+
191+# filtered, annotated, but unordered
192+>>> qs = Group.objects.filter(registration__type__gt=0 \
193+    ).annotate(sum_time = Sum('registration__time') \
194+    ).order_by('id')
195+>>> [(group.id, group.category.id, group.sum_time) for group in qs]
196+[(1, 1, 3), (2, 2, 12), (3, 1, 48)]
197+
198+# annotated but unordered and unfiltered
199+>>> qs = Group.objects.annotate(sum_time = Sum('registration__time') \
200+    ).order_by('id')
201+>>> [(group.id, group.category.id, group.sum_time) for group in qs]
202+[(1, 1, 3), (2, 2, 12), (3, 1, 48), (4, 2, None)]
203+
204+# filtered but unordered
205+>>> qs = Group.objects.filter(registration__type__gt=0 \
206+    ).order_by('id').distinct()
207+>>> [(group.id, group.category.id) for group in qs]
208+[(1, 1), (2, 2), (3, 1)]
209+
210+"""}