Code

Ticket #12252: 12252_1.2.diff

File 12252_1.2.diff, 5.7 KB (added by PhiR, 3 years ago)

patch for 1.2.5 in case anyone needs it (fix is commited in 1.2.X branch)

Line 
1diff -ur ../orig/Django-1.2.5/django/db/models/sql/query.py ./django/db/models/sql/query.py
2--- ../orig/Django-1.2.5/django/db/models/sql/query.py  2011-02-08 15:07:14.000000000 +0100
3+++ ./django/db/models/sql/query.py     2011-09-09 14:54:56.000000000 +0200
4@@ -446,6 +446,8 @@
5             "Cannot combine a unique query with a non-unique query."
6 
7         self.remove_inherited_models()
8+        l_tables = set([a for a in self.tables if self.alias_refcount[a]])
9+        r_tables = set([a for a in rhs.tables if rhs.alias_refcount[a]])
10         # Work out how to relabel the rhs aliases, if necessary.
11         change_map = {}
12         used = set()
13@@ -463,13 +465,19 @@
14             first = False
15 
16         # So that we don't exclude valid results in an "or" query combination,
17-        # the first join that is exclusive to the lhs (self) must be converted
18+        # all joins exclusive to either the lhs or the rhs must be converted
19         # to an outer join.
20         if not conjunction:
21-            for alias in self.tables[1:]:
22-                if self.alias_refcount[alias] == 1:
23-                    self.promote_alias(alias, True)
24-                    break
25+            # Update r_tables aliases.
26+            for alias in change_map:
27+                if alias in r_tables:
28+                    r_tables.remove(alias)
29+                    r_tables.add(change_map[alias])
30+            # Find aliases that are exclusive to rhs or lhs.
31+            # These are promoted to outer joins.
32+            outer_aliases = (l_tables | r_tables) - (l_tables & r_tables)
33+            for alias in outer_aliases:
34+                self.promote_alias(alias, True)
35 
36         # Now relabel a copy of the rhs where-clause and add it to the current
37         # one.
38diff -ur ../orig/Django-1.2.5/tests/regressiontests/queries/models.py ./tests/regressiontests/queries/models.py
39--- ../orig/Django-1.2.5/tests/regressiontests/queries/models.py        2011-01-25 04:39:38.000000000 +0100
40+++ ./tests/regressiontests/queries/models.py   2011-09-09 14:54:56.000000000 +0200
41@@ -294,3 +294,26 @@
42 
43     def __unicode__(self):
44         return u"%s" % self.num
45+
46+# Bug #12252
47+class ObjectA(models.Model):
48+    name = models.CharField(max_length=50)
49+
50+    def __unicode__(self):
51+        return self.name
52+
53+class ObjectB(models.Model):
54+    name = models.CharField(max_length=50)
55+    objecta = models.ForeignKey(ObjectA)
56+    number = models.PositiveSmallIntegerField()
57+
58+    def __unicode__(self):
59+        return self.name
60+
61+class ObjectC(models.Model):
62+    name = models.CharField(max_length=50)
63+    objecta = models.ForeignKey(ObjectA)
64+    objectb = models.ForeignKey(ObjectB)
65+
66+    def __unicode__(self):
67+       return self.name
68diff -ur ../orig/Django-1.2.5/tests/regressiontests/queries/tests.py ./tests/regressiontests/queries/tests.py
69--- ../orig/Django-1.2.5/tests/regressiontests/queries/tests.py 2011-01-25 04:39:38.000000000 +0100
70+++ ./tests/regressiontests/queries/tests.py    2011-09-09 14:56:58.000000000 +0200
71@@ -14,7 +14,8 @@
72 from models import (Annotation, Article, Author, Celebrity, Child, Cover, Detail,
73     DumbCategory, ExtraInfo, Fan, Item, LeafA, LoopX, LoopZ, ManagedModel,
74     Member, NamedCategory, Note, Number, Plaything, PointerA, Ranking, Related,
75-    Report, ReservedName, Tag, TvChef, Valid, X, Food, Eaten, Node)
76+    Report, ReservedName, Tag, TvChef, Valid, X, Food, Eaten, Node, ObjectA, ObjectB,
77+    ObjectC)
78 
79 
80 class BaseQuerysetTest(TestCase):
81@@ -1650,3 +1651,63 @@
82                 Number.objects.filter(num__in=numbers).count(),
83                 2500
84             )
85+
86+class UnionTests(unittest.TestCase):
87+    """
88+    Tests for the union of two querysets. Bug #12252.
89+    """
90+    def setUp(self):
91+        objectas = []
92+        objectbs = []
93+        objectcs = []
94+        a_info = ['one', 'two', 'three']
95+        for name in a_info:
96+            o = ObjectA(name=name)
97+            o.save()
98+            objectas.append(o)
99+        b_info = [('un', 1, objectas[0]), ('deux', 2, objectas[0]), ('trois', 3, objectas[2])]
100+        for name, number, objecta in b_info:
101+            o = ObjectB(name=name, number=number, objecta=objecta)
102+            o.save()
103+            objectbs.append(o)
104+        c_info = [('ein', objectas[2], objectbs[2]), ('zwei', objectas[1], objectbs[1])]
105+        for name, objecta, objectb in c_info:
106+            o = ObjectC(name=name, objecta=objecta, objectb=objectb)
107+            o.save()
108+            objectcs.append(o)
109+
110+    def check_union(self, model, Q1, Q2):
111+        filter = model.objects.filter
112+        self.assertEqual(set(filter(Q1) | filter(Q2)), set(filter(Q1 | Q2)))
113+        self.assertEqual(set(filter(Q2) | filter(Q1)), set(filter(Q1 | Q2)))
114+
115+    def test_A_AB(self):
116+        Q1 = Q(name='two')
117+        Q2 = Q(objectb__name='deux')
118+        self.check_union(ObjectA, Q1, Q2)
119+
120+    def test_A_AB2(self):
121+        Q1 = Q(name='two')
122+        Q2 = Q(objectb__name='deux', objectb__number=2)
123+        self.check_union(ObjectA, Q1, Q2)
124+
125+    def test_AB_ACB(self):
126+        Q1 = Q(objectb__name='deux')
127+        Q2 = Q(objectc__objectb__name='deux')
128+        self.check_union(ObjectA, Q1, Q2)
129+
130+    def test_BAB_BAC(self):
131+        Q1 = Q(objecta__objectb__name='deux')
132+        Q2 = Q(objecta__objectc__name='ein')
133+        self.check_union(ObjectB, Q1, Q2)
134+
135+    def test_BAB_BACB(self):
136+        Q1 = Q(objecta__objectb__name='deux')
137+        Q2 = Q(objecta__objectc__objectb__name='trois')
138+        self.check_union(ObjectB, Q1, Q2)
139+
140+    def test_BA_BCA__BAB_BAC_BCA(self):
141+        Q1 = Q(objecta__name='one', objectc__objecta__name='two')
142+        Q2 = Q(objecta__objectc__name='ein', objectc__objecta__name='three', objecta__objectb__name='trois')
143+        self.check_union(ObjectB, Q1, Q2)
144+