summaryrefslogtreecommitdiff
path: root/django/db/models/sql/where.py
diff options
context:
space:
mode:
Diffstat (limited to 'django/db/models/sql/where.py')
-rw-r--r--django/db/models/sql/where.py83
1 files changed, 48 insertions, 35 deletions
diff --git a/django/db/models/sql/where.py b/django/db/models/sql/where.py
index 5515bc4f83..70ff5310f7 100644
--- a/django/db/models/sql/where.py
+++ b/django/db/models/sql/where.py
@@ -10,7 +10,7 @@ from itertools import repeat
from django.utils import tree
from django.db.models.fields import Field
-from django.db.models.sql.datastructures import EmptyResultSet, FullResultSet
+from django.db.models.sql.datastructures import EmptyResultSet
from django.db.models.sql.aggregates import Aggregate
# Connection types
@@ -75,17 +75,21 @@ class WhereNode(tree.Node):
def as_sql(self, qn, connection):
"""
Returns the SQL version of the where clause and the value to be
- substituted in. Returns None, None if this node is empty.
-
- If 'node' is provided, that is the root of the SQL generation
- (generally not needed except by the internal implementation for
- recursion).
+ substituted in. Returns '', [] if this node matches everything,
+ None, [] if this node is empty, and raises EmptyResultSet if this
+ node can't match anything.
"""
- if not self.children:
- return None, []
+ # Note that the logic here is made slightly more complex than
+ # necessary because there are two kind of empty nodes: Nodes
+ # containing 0 children, and nodes that are known to match everything.
+ # A match-everything node is different than empty node (which also
+ # technically matches everything) for backwards compatibility reasons.
+ # Refs #5261.
result = []
result_params = []
- empty = True
+ everything_childs, nothing_childs = 0, 0
+ non_empty_childs = len(self.children)
+
for child in self.children:
try:
if hasattr(child, 'as_sql'):
@@ -93,39 +97,48 @@ class WhereNode(tree.Node):
else:
# A leaf node in the tree.
sql, params = self.make_atom(child, qn, connection)
-
except EmptyResultSet:
- if self.connector == AND and not self.negated:
- # We can bail out early in this particular case (only).
- raise
- elif self.negated:
- empty = False
- continue
- except FullResultSet:
- if self.connector == OR:
- if self.negated:
- empty = True
- break
- # We match everything. No need for any constraints.
+ nothing_childs += 1
+ else:
+ if sql:
+ result.append(sql)
+ result_params.extend(params)
+ else:
+ if sql is None:
+ # Skip empty childs totally.
+ non_empty_childs -= 1
+ continue
+ everything_childs += 1
+ # Check if this node matches nothing or everything.
+ # First check the amount of full nodes and empty nodes
+ # to make this node empty/full.
+ if self.connector == AND:
+ full_needed, empty_needed = non_empty_childs, 1
+ else:
+ full_needed, empty_needed = 1, non_empty_childs
+ # Now, check if this node is full/empty using the
+ # counts.
+ if empty_needed - nothing_childs <= 0:
+ if self.negated:
return '', []
+ else:
+ raise EmptyResultSet
+ if full_needed - everything_childs <= 0:
if self.negated:
- empty = True
- continue
-
- empty = False
- if sql:
- result.append(sql)
- result_params.extend(params)
- if empty:
- raise EmptyResultSet
+ raise EmptyResultSet
+ else:
+ return '', []
+ if non_empty_childs == 0:
+ # All the child nodes were empty, so this one is empty, too.
+ return None, []
conn = ' %s ' % self.connector
sql_string = conn.join(result)
if sql_string:
- if self.negated:
- sql_string = 'NOT (%s)' % sql_string
- elif len(self.children) != 1:
+ if len(result) > 1:
sql_string = '(%s)' % sql_string
+ if self.negated:
+ sql_string = 'NOT %s' % sql_string
return sql_string, result_params
def make_atom(self, child, qn, connection):
@@ -261,7 +274,7 @@ class EverythingNode(object):
"""
def as_sql(self, qn=None, connection=None):
- raise FullResultSet
+ return '', []
def relabel_aliases(self, change_map, node=None):
return