From 93d1fdb1304ed5b2e5ea2e0f1e84db7fff5eb7fa Mon Sep 17 00:00:00 2001 From: Julien Phalip Date: Mon, 9 Apr 2012 00:43:08 +0000 Subject: [PATCH] =?UTF-8?q?Fixed=20#17877=20--=20Ensured=20that=20extra=20?= =?UTF-8?q?WHERE=20clauses=20get=20correctly=20ANDed=20when=20they=20conta?= =?UTF-8?q?in=20OR=20operations.=20Thanks=20to=20Marek=20Brzo=CC=81ska=20f?= =?UTF-8?q?or=20the=20report,=20to=20eleather=20for=20the=20test=20case=20?= =?UTF-8?q?and=20to=20Adrien=20Lemaire=20for=20the=20patch.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git-svn-id: http://code.djangoproject.com/svn/django/trunk@17880 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- AUTHORS | 1 + django/db/models/sql/where.py | 3 +- docs/ref/models/querysets.txt | 4 +-- tests/regressiontests/extra_regress/tests.py | 31 ++++++++++++++++++++ 4 files changed, 36 insertions(+), 3 deletions(-) diff --git a/AUTHORS b/AUTHORS index 0af0c7961e..fc3cb93406 100644 --- a/AUTHORS +++ b/AUTHORS @@ -326,6 +326,7 @@ answer newbie questions, and generally made Django that much better: lcordier@point45.com Jeong-Min Lee Tai Lee + Adrien Lemaire Christopher Lenz lerouxb@gmail.com Piotr Lewandowski diff --git a/django/db/models/sql/where.py b/django/db/models/sql/where.py index 2bd705dd60..0bc3638b80 100644 --- a/django/db/models/sql/where.py +++ b/django/db/models/sql/where.py @@ -281,7 +281,8 @@ class ExtraWhere(object): self.params = params def as_sql(self, qn=None, connection=None): - return " AND ".join(self.sqls), tuple(self.params or ()) + sqls = ["(%s)" % sql for sql in self.sqls] + return " AND ".join(sqls), tuple(self.params or ()) class Constraint(object): """ diff --git a/docs/ref/models/querysets.txt b/docs/ref/models/querysets.txt index e25bea0e69..b20c6e34e5 100644 --- a/docs/ref/models/querysets.txt +++ b/docs/ref/models/querysets.txt @@ -968,11 +968,11 @@ of the arguments is required, but you should use at least one of them. Example:: - Entry.objects.extra(where=['id IN (3, 4, 5, 20)']) + Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"]) ...translates (roughly) into the following SQL:: - SELECT * FROM blog_entry WHERE id IN (3, 4, 5, 20); + SELECT * FROM blog_entry WHERE (foo='a' OR bar='a') AND (baz='a') Be careful when using the ``tables`` parameter if you're specifying tables that are already used in the query. When you add extra tables diff --git a/tests/regressiontests/extra_regress/tests.py b/tests/regressiontests/extra_regress/tests.py index 67efb428dd..3fcafef5de 100644 --- a/tests/regressiontests/extra_regress/tests.py +++ b/tests/regressiontests/extra_regress/tests.py @@ -313,3 +313,34 @@ class ExtraRegressTests(TestCase): TestObject.objects.extra(where=["id > %s"], params=[obj.pk]), [''] ) + + def test_regression_17877(self): + """ + Ensure that extra WHERE clauses get correctly ANDed, even when they + contain OR operations. + """ + # Test Case 1: should appear in queryset. + t = TestObject(first='a', second='a', third='a') + t.save() + # Test Case 2: should appear in queryset. + t = TestObject(first='b', second='a', third='a') + t.save() + # Test Case 3: should not appear in queryset, bug case. + t = TestObject(first='a', second='a', third='b') + t.save() + # Test Case 4: should not appear in queryset. + t = TestObject(first='b', second='a', third='b') + t.save() + # Test Case 5: should not appear in queryset. + t = TestObject(first='b', second='b', third='a') + t.save() + # Test Case 6: should not appear in queryset, bug case. + t = TestObject(first='a', second='b', third='b') + t.save() + + self.assertQuerysetEqual( + TestObject.objects.extra( + where=["first = 'a' OR second = 'a'", "third = 'a'"], + ), + ['', ''] + )