summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorAnssi Kääriäinen <akaariai@gmail.com>2012-08-21 18:43:08 +0300
committerAnssi Kääriäinen <akaariai@gmail.com>2012-08-21 21:23:57 +0300
commita193372753ad9d1d15ad5e2d6d06bbc07ca3f433 (patch)
tree2e0b3c6165a161c7b403c3e3bcb65fb538f31e59 /tests
parentfd58d6c258058f8ac54e241b21b949bdbe50059b (diff)
Fixed #17886 -- Fixed join promotion in ORed nullable queries
The ORM generated a query with INNER JOIN instead of LEFT OUTER JOIN in a somewhat complicated case. The main issue was that there was a chain of nullable FK -> non-nullble FK, and the join promotion logic didn't see the need to promote the non-nullable FK even if the previous nullable FK was already promoted to LOUTER JOIN. This resulted in a query like a LOUTER b INNER c, which incorrectly prunes results.
Diffstat (limited to 'tests')
-rw-r--r--tests/regressiontests/queries/models.py15
-rw-r--r--tests/regressiontests/queries/tests.py25
2 files changed, 39 insertions, 1 deletions
diff --git a/tests/regressiontests/queries/models.py b/tests/regressiontests/queries/models.py
index 45a48ee77c..f0178a0256 100644
--- a/tests/regressiontests/queries/models.py
+++ b/tests/regressiontests/queries/models.py
@@ -385,3 +385,18 @@ class NullableName(models.Model):
class Meta:
ordering = ['id']
+
+class ModelD(models.Model):
+ name = models.TextField()
+
+class ModelC(models.Model):
+ name = models.TextField()
+
+class ModelB(models.Model):
+ name = models.TextField()
+ c = models.ForeignKey(ModelC)
+
+class ModelA(models.Model):
+ name = models.TextField()
+ b = models.ForeignKey(ModelB, null=True)
+ d = models.ForeignKey(ModelD)
diff --git a/tests/regressiontests/queries/tests.py b/tests/regressiontests/queries/tests.py
index 85ea4aa452..005aa9650b 100644
--- a/tests/regressiontests/queries/tests.py
+++ b/tests/regressiontests/queries/tests.py
@@ -23,7 +23,7 @@ from .models import (Annotation, Article, Author, Celebrity, Child, Cover,
Ranking, Related, Report, ReservedName, Tag, TvChef, Valid, X, Food, Eaten,
Node, ObjectA, ObjectB, ObjectC, CategoryItem, SimpleCategory,
SpecialCategory, OneToOneCategory, NullableName, ProxyCategory,
- SingleObject, RelatedObject)
+ SingleObject, RelatedObject, ModelA, ModelD)
class BaseQuerysetTest(TestCase):
@@ -2105,3 +2105,26 @@ class WhereNodeTest(TestCase):
self.assertEqual(w.as_sql(qn, connection), (None, []))
w = WhereNode(children=[empty_w, NothingNode()], connector='OR')
self.assertRaises(EmptyResultSet, w.as_sql, qn, connection)
+
+class NullJoinPromotionOrTest(TestCase):
+ def setUp(self):
+ d = ModelD.objects.create(name='foo')
+ ModelA.objects.create(name='bar', d=d)
+
+ def test_ticket_17886(self):
+ # The first Q-object is generating the match, the rest of the filters
+ # should not remove the match even if they do not match anything. The
+ # problem here was that b__name generates a LOUTER JOIN, then
+ # b__c__name generates join to c, which the ORM tried to promote but
+ # failed as that join isn't nullable.
+ q_obj = (
+ Q(d__name='foo')|
+ Q(b__name='foo')|
+ Q(b__c__name='foo')
+ )
+ qset = ModelA.objects.filter(q_obj)
+ self.assertEqual(len(qset), 1)
+ # We generate one INNER JOIN to D. The join is direct and not nullable
+ # so we can use INNER JOIN for it. However, we can NOT use INNER JOIN
+ # for the b->c join, as a->b is nullable.
+ self.assertEqual(str(qset.query).count('INNER JOIN'), 1)