Removed EverythingNode
At the same time, made sure that empty nodes in where clause match everything.
This commit is contained in:
parent
afe0bb7b13
commit
7145c8a62a
|
@ -1767,6 +1767,7 @@ class ForeignObject(RelatedField):
|
|||
root_constraint.add(lookup_class(targets[0].get_col(alias, sources[0]), value), AND)
|
||||
elif lookup_type == 'in':
|
||||
values = [get_normalized_value(value) for value in raw_value]
|
||||
root_constraint.connector = OR
|
||||
for value in values:
|
||||
value_constraint = constraint_class()
|
||||
for source, target, val in zip(sources, targets, value):
|
||||
|
|
|
@ -24,8 +24,8 @@ from django.db.models.sql.constants import (QUERY_TERMS, ORDER_DIR, SINGLE,
|
|||
ORDER_PATTERN, INNER, LOUTER)
|
||||
from django.db.models.sql.datastructures import (
|
||||
EmptyResultSet, Empty, MultiJoin, Join, BaseTable)
|
||||
from django.db.models.sql.where import (WhereNode, EverythingNode,
|
||||
ExtraWhere, AND, OR, NothingNode)
|
||||
from django.db.models.sql.where import (WhereNode, ExtraWhere, AND, OR,
|
||||
NothingNode)
|
||||
from django.utils import six
|
||||
from django.utils.deprecation import RemovedInDjango20Warning
|
||||
from django.utils.encoding import force_text
|
||||
|
@ -526,20 +526,8 @@ class Query(object):
|
|||
|
||||
# Now relabel a copy of the rhs where-clause and add it to the current
|
||||
# one.
|
||||
if rhs.where:
|
||||
w = rhs.where.clone()
|
||||
w.relabel_aliases(change_map)
|
||||
if not self.where:
|
||||
# Since 'self' matches everything, add an explicit "include
|
||||
# everything" where-constraint so that connections between the
|
||||
# where clauses won't exclude valid results.
|
||||
self.where.add(EverythingNode(), AND)
|
||||
elif self.where:
|
||||
# rhs has an empty where clause.
|
||||
w = self.where_class()
|
||||
w.add(EverythingNode(), AND)
|
||||
else:
|
||||
w = self.where_class()
|
||||
self.where.add(w, connector)
|
||||
|
||||
# Selection columns and extra extensions are those provided by 'rhs'.
|
||||
|
@ -1207,7 +1195,8 @@ class Query(object):
|
|||
# So, demotion is OK.
|
||||
existing_inner = set(
|
||||
(a for a in self.alias_map if self.alias_map[a].join_type == INNER))
|
||||
clause, require_inner = self._add_q(q_object, self.used_aliases)
|
||||
clause, _ = self._add_q(q_object, self.used_aliases)
|
||||
if clause:
|
||||
self.where.add(clause, AND)
|
||||
self.demote_joins(existing_inner)
|
||||
|
||||
|
@ -1233,6 +1222,7 @@ class Query(object):
|
|||
child, can_reuse=used_aliases, branch_negated=branch_negated,
|
||||
current_negated=current_negated, connector=connector, allow_joins=allow_joins)
|
||||
joinpromoter.add_votes(needed_inner)
|
||||
if child_clause:
|
||||
target_clause.add(child_clause, connector)
|
||||
needed_inner = joinpromoter.update_join_types(self)
|
||||
return target_clause, needed_inner
|
||||
|
|
|
@ -28,22 +28,22 @@ class WhereNode(tree.Node):
|
|||
the correct SQL).
|
||||
|
||||
A child is usually an expression producing boolean values. Most likely the
|
||||
expression is a Lookup instance, but other types of objects fulfilling the
|
||||
required API could be used too (for example, sql.where.EverythingNode).
|
||||
expression is a Lookup instance.
|
||||
|
||||
However, a child could also be any class with as_sql() and either
|
||||
relabeled_clone() method or relabel_aliases() and clone() methods. The
|
||||
second alternative should be used if the alias is not the only mutable
|
||||
variable.
|
||||
relabeled_clone() method or relabel_aliases() and clone() methods and
|
||||
contains_aggregate attribute.
|
||||
"""
|
||||
default = AND
|
||||
|
||||
def split_having(self, negated=False):
|
||||
"""
|
||||
Returns two possibly None nodes: one for those parts of self that
|
||||
should be pushed to having and one for those parts of self
|
||||
that should be in where.
|
||||
should be included in the WHERE clause and one for those parts of
|
||||
self that must be included in the HAVING clause.
|
||||
"""
|
||||
if not self.contains_aggregate:
|
||||
return self, None
|
||||
in_negated = negated ^ self.negated
|
||||
# If the effective connector is OR and this node contains an aggregate,
|
||||
# then we need to push the whole branch to HAVING clause.
|
||||
|
@ -57,9 +57,9 @@ class WhereNode(tree.Node):
|
|||
for c in self.children:
|
||||
if hasattr(c, 'split_having'):
|
||||
where_part, having_part = c.split_having(in_negated)
|
||||
if where_part:
|
||||
if where_part is not None:
|
||||
where_parts.append(where_part)
|
||||
if having_part:
|
||||
if having_part is not None:
|
||||
having_parts.append(having_part)
|
||||
elif c.contains_aggregate:
|
||||
having_parts.append(c)
|
||||
|
@ -76,55 +76,39 @@ class WhereNode(tree.Node):
|
|||
None, [] if this node is empty, and raises EmptyResultSet if this
|
||||
node can't match anything.
|
||||
"""
|
||||
# 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 = []
|
||||
everything_childs, nothing_childs = 0, 0
|
||||
non_empty_childs = len(self.children)
|
||||
if self.connector == AND:
|
||||
full_needed, empty_needed = len(self.children), 1
|
||||
else:
|
||||
full_needed, empty_needed = 1, len(self.children)
|
||||
|
||||
for child in self.children:
|
||||
try:
|
||||
sql, params = compiler.compile(child)
|
||||
except EmptyResultSet:
|
||||
nothing_childs += 1
|
||||
empty_needed -= 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
|
||||
full_needed -= 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 empty_needed == 0:
|
||||
if self.negated:
|
||||
return '', []
|
||||
else:
|
||||
raise EmptyResultSet
|
||||
if full_needed - everything_childs <= 0:
|
||||
if full_needed == 0:
|
||||
if self.negated:
|
||||
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:
|
||||
|
@ -186,16 +170,6 @@ class WhereNode(tree.Node):
|
|||
return self._contains_aggregate(self)
|
||||
|
||||
|
||||
class EverythingNode(object):
|
||||
"""
|
||||
A node that matches everything.
|
||||
"""
|
||||
contains_aggregate = False
|
||||
|
||||
def as_sql(self, compiler=None, connection=None):
|
||||
return '', []
|
||||
|
||||
|
||||
class NothingNode(object):
|
||||
"""
|
||||
A node that matches nothing.
|
||||
|
|
|
@ -9,7 +9,7 @@ import unittest
|
|||
from django.core.exceptions import FieldError
|
||||
from django.db import connection, DEFAULT_DB_ALIAS
|
||||
from django.db.models import Count, F, Q
|
||||
from django.db.models.sql.where import WhereNode, EverythingNode, NothingNode
|
||||
from django.db.models.sql.where import WhereNode, NothingNode
|
||||
from django.db.models.sql.constants import LOUTER
|
||||
from django.db.models.sql.datastructures import EmptyResultSet
|
||||
from django.test import TestCase, skipUnlessDBFeature
|
||||
|
@ -2851,20 +2851,10 @@ class WhereNodeTest(TestCase):
|
|||
|
||||
def test_empty_full_handling_conjunction(self):
|
||||
compiler = WhereNodeTest.MockCompiler()
|
||||
w = WhereNode(children=[EverythingNode()])
|
||||
self.assertEqual(w.as_sql(compiler, connection), ('', []))
|
||||
w.negate()
|
||||
self.assertRaises(EmptyResultSet, w.as_sql, compiler, connection)
|
||||
w = WhereNode(children=[NothingNode()])
|
||||
self.assertRaises(EmptyResultSet, w.as_sql, compiler, connection)
|
||||
w.negate()
|
||||
self.assertEqual(w.as_sql(compiler, connection), ('', []))
|
||||
w = WhereNode(children=[EverythingNode(), EverythingNode()])
|
||||
self.assertEqual(w.as_sql(compiler, connection), ('', []))
|
||||
w.negate()
|
||||
self.assertRaises(EmptyResultSet, w.as_sql, compiler, connection)
|
||||
w = WhereNode(children=[EverythingNode(), self.DummyNode()])
|
||||
self.assertEqual(w.as_sql(compiler, connection), ('dummy', []))
|
||||
w = WhereNode(children=[self.DummyNode(), self.DummyNode()])
|
||||
self.assertEqual(w.as_sql(compiler, connection), ('(dummy AND dummy)', []))
|
||||
w.negate()
|
||||
|
@ -2876,22 +2866,10 @@ class WhereNodeTest(TestCase):
|
|||
|
||||
def test_empty_full_handling_disjunction(self):
|
||||
compiler = WhereNodeTest.MockCompiler()
|
||||
w = WhereNode(children=[EverythingNode()], connector='OR')
|
||||
self.assertEqual(w.as_sql(compiler, connection), ('', []))
|
||||
w.negate()
|
||||
self.assertRaises(EmptyResultSet, w.as_sql, compiler, connection)
|
||||
w = WhereNode(children=[NothingNode()], connector='OR')
|
||||
self.assertRaises(EmptyResultSet, w.as_sql, compiler, connection)
|
||||
w.negate()
|
||||
self.assertEqual(w.as_sql(compiler, connection), ('', []))
|
||||
w = WhereNode(children=[EverythingNode(), EverythingNode()], connector='OR')
|
||||
self.assertEqual(w.as_sql(compiler, connection), ('', []))
|
||||
w.negate()
|
||||
self.assertRaises(EmptyResultSet, w.as_sql, compiler, connection)
|
||||
w = WhereNode(children=[EverythingNode(), self.DummyNode()], connector='OR')
|
||||
self.assertEqual(w.as_sql(compiler, connection), ('', []))
|
||||
w.negate()
|
||||
self.assertRaises(EmptyResultSet, w.as_sql, compiler, connection)
|
||||
w = WhereNode(children=[self.DummyNode(), self.DummyNode()], connector='OR')
|
||||
self.assertEqual(w.as_sql(compiler, connection), ('(dummy OR dummy)', []))
|
||||
w.negate()
|
||||
|
@ -2905,15 +2883,20 @@ class WhereNodeTest(TestCase):
|
|||
compiler = WhereNodeTest.MockCompiler()
|
||||
empty_w = WhereNode()
|
||||
w = WhereNode(children=[empty_w, empty_w])
|
||||
self.assertEqual(w.as_sql(compiler, connection), (None, []))
|
||||
self.assertEqual(w.as_sql(compiler, connection), ('', []))
|
||||
w.negate()
|
||||
self.assertEqual(w.as_sql(compiler, connection), (None, []))
|
||||
with self.assertRaises(EmptyResultSet):
|
||||
w.as_sql(compiler, connection)
|
||||
w.connector = 'OR'
|
||||
self.assertEqual(w.as_sql(compiler, connection), (None, []))
|
||||
with self.assertRaises(EmptyResultSet):
|
||||
w.as_sql(compiler, connection)
|
||||
w.negate()
|
||||
self.assertEqual(w.as_sql(compiler, connection), (None, []))
|
||||
self.assertEqual(w.as_sql(compiler, connection), ('', []))
|
||||
w = WhereNode(children=[empty_w, NothingNode()], connector='OR')
|
||||
self.assertRaises(EmptyResultSet, w.as_sql, compiler, connection)
|
||||
self.assertEqual(w.as_sql(compiler, connection), ('', []))
|
||||
w = WhereNode(children=[empty_w, NothingNode()], connector='AND')
|
||||
with self.assertRaises(EmptyResultSet):
|
||||
w.as_sql(compiler, connection)
|
||||
|
||||
|
||||
class IteratorExceptionsTest(TestCase):
|
||||
|
|
Loading…
Reference in New Issue