summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--django/db/models/sql/datastructures.py3
-rw-r--r--django/db/models/sql/query.py11
-rw-r--r--django/db/models/sql/where.py55
-rw-r--r--tests/regressiontests/null_queries/models.py4
4 files changed, 47 insertions, 26 deletions
diff --git a/django/db/models/sql/datastructures.py b/django/db/models/sql/datastructures.py
index 59343aa515..ab03c72ea2 100644
--- a/django/db/models/sql/datastructures.py
+++ b/django/db/models/sql/datastructures.py
@@ -6,6 +6,9 @@ the SQL domain.
class EmptyResultSet(Exception):
pass
+class FullResultSet(Exception):
+ pass
+
class Aggregate(object):
"""
Base class for all aggregate-related classes (min, max, avg, count, sum).
diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py
index 2f7a08e4dd..f2d6b577f2 100644
--- a/django/db/models/sql/query.py
+++ b/django/db/models/sql/query.py
@@ -15,7 +15,7 @@ from django.utils.tree import Node
from django.utils.datastructures import SortedDict
from django.dispatch import dispatcher
from django.db.models import signals
-from django.db.models.sql.where import WhereNode, AND, OR
+from django.db.models.sql.where import WhereNode, EverythingNode, AND, OR
from django.db.models.sql.datastructures import Count, Date
from django.db.models.fields import FieldDoesNotExist, Field, related
from django.contrib.contenttypes import generic
@@ -311,19 +311,18 @@ class Query(object):
w.relabel_aliases(change_map)
if not self.where:
# Since 'self' matches everything, add an explicit "include
- # everything" (pk is not NULL) where-constraint so that
- # connections between the where clauses won't exclude valid
- # results.
+ # everything" where-constraint so that connections between the
+ # where clauses won't exclude valid results.
alias = self.join((None, self.model._meta.db_table, None, None))
pk = self.model._meta.pk
- self.where.add([alias, pk.column, pk, 'isnull', False], AND)
+ self.where.add(EverythingNode(), AND)
elif self.where:
# rhs has an empty where clause. Make it match everything (see
# above for reasoning).
w = WhereNode()
alias = self.join((None, self.model._meta.db_table, None, None))
pk = self.model._meta.pk
- w.add([alias, pk.column, pk, 'isnull', False], AND)
+ w.add(EverythingNode(), AND)
else:
w = WhereNode()
self.where.add(w, connector)
diff --git a/django/db/models/sql/where.py b/django/db/models/sql/where.py
index d361916953..198c6e2e40 100644
--- a/django/db/models/sql/where.py
+++ b/django/db/models/sql/where.py
@@ -5,7 +5,7 @@ import datetime
from django.utils import tree
from django.db import connection
-from datastructures import EmptyResultSet
+from datastructures import EmptyResultSet, FullResultSet
# Connection types
AND = 'AND'
@@ -43,26 +43,36 @@ class WhereNode(tree.Node):
result_params = []
empty = True
for child in node.children:
- if hasattr(child, 'as_sql'):
- sql, params = child.as_sql(qn=qn)
- format = '(%s)'
- elif isinstance(child, tree.Node):
- sql, params = self.as_sql(child, qn)
- if child.negated:
- format = 'NOT (%s)'
- else:
+ try:
+ if hasattr(child, 'as_sql'):
+ sql, params = child.as_sql(qn=qn)
format = '(%s)'
- else:
- try:
+ elif isinstance(child, tree.Node):
+ sql, params = self.as_sql(child, qn)
+ if child.negated:
+ format = 'NOT (%s)'
+ else:
+ format = '(%s)'
+ else:
sql, params = self.make_atom(child, qn)
format = '%s'
- except EmptyResultSet:
- if self.connector == AND and not node.negated:
- # We can bail out early in this particular case (only).
- raise
- elif node.negated:
- empty = False
- continue
+ except EmptyResultSet:
+ if self.connector == AND and not node.negated:
+ # We can bail out early in this particular case (only).
+ raise
+ elif node.negated:
+ empty = False
+ continue
+ except FullResultSet:
+ if self.connector == OR:
+ if node.negated:
+ empty = True
+ break
+ # We match everything. No need for any constraints.
+ return '', []
+ if node.negated:
+ empty = True
+ continue
empty = False
if sql:
result.append(format % sql)
@@ -156,3 +166,12 @@ class WhereNode(tree.Node):
val = child[0]
child[0] = change_map.get(val, val)
+class EverythingNode(object):
+ """
+ A node that matches everything.
+ """
+ def as_sql(self, qn=None):
+ raise FullResultSet
+
+ def relabel_aliases(self, change_map, node=None):
+ return
diff --git a/tests/regressiontests/null_queries/models.py b/tests/regressiontests/null_queries/models.py
index 739b75d96d..d6993b8be5 100644
--- a/tests/regressiontests/null_queries/models.py
+++ b/tests/regressiontests/null_queries/models.py
@@ -26,11 +26,11 @@ __test__ = {'API_TESTS':"""
# Exact query with value None returns nothing ("is NULL" in sql, but every 'id'
# field has a value).
->>> Choice.objects.filter(id__exact=None)
+>>> Choice.objects.filter(choice__exact=None)
[]
Excluding the previous result returns everything.
->>> Choice.objects.exclude(id=None).order_by('id')
+>>> Choice.objects.exclude(choice=None).order_by('id')
[<Choice: Choice: Because. in poll Q: Why? >, <Choice: Choice: Why Not? in poll Q: Why? >]
# Valid query, but fails because foo isn't a keyword