summaryrefslogtreecommitdiff
path: root/django/db/models/sql
diff options
context:
space:
mode:
Diffstat (limited to 'django/db/models/sql')
-rw-r--r--django/db/models/sql/query.py21
-rw-r--r--django/db/models/sql/subqueries.py12
-rw-r--r--django/db/models/sql/where.py71
3 files changed, 59 insertions, 45 deletions
diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py
index 6eccaf997e..7944d0358f 100644
--- a/django/db/models/sql/query.py
+++ b/django/db/models/sql/query.py
@@ -1104,19 +1104,7 @@ class Query(object):
# that's harmless.
self.promote_alias(table)
- # To save memory and copying time, convert the value from the Python
- # object to the actual value used in the SQL query.
- if field:
- params = field.get_db_prep_lookup(lookup_type, value)
- else:
- params = Field().get_db_prep_lookup(lookup_type, value)
- if isinstance(value, datetime.datetime):
- annotation = datetime.datetime
- else:
- annotation = bool(value)
-
- self.where.add((alias, col, field.db_type(), lookup_type, annotation,
- params), connector)
+ self.where.add((alias, col, field, lookup_type, value), connector)
if negate:
for alias in join_list:
@@ -1126,8 +1114,8 @@ class Query(object):
for alias in join_list:
if self.alias_map[alias][JOIN_TYPE] == self.LOUTER:
j_col = self.alias_map[alias][RHS_JOIN_COL]
- entry = Node([(alias, j_col, None, 'isnull', True,
- [True])])
+ entry = self.where_class()
+ entry.add((alias, j_col, None, 'isnull', True), AND)
entry.negate()
self.where.add(entry, AND)
break
@@ -1135,7 +1123,8 @@ class Query(object):
# Leaky abstraction artifact: We have to specifically
# exclude the "foo__in=[]" case from this handling, because
# it's short-circuited in the Where class.
- entry = Node([(alias, col, None, 'isnull', True, [True])])
+ entry = self.where_class()
+ entry.add((alias, col, None, 'isnull', True), AND)
entry.negate()
self.where.add(entry, AND)
diff --git a/django/db/models/sql/subqueries.py b/django/db/models/sql/subqueries.py
index d8c5b074ec..0bb741d706 100644
--- a/django/db/models/sql/subqueries.py
+++ b/django/db/models/sql/subqueries.py
@@ -49,7 +49,7 @@ class DeleteQuery(Query):
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
where = self.where_class()
where.add((None, related.field.m2m_reverse_name(),
- related.field.db_type(), 'in', True,
+ related.field, 'in',
pk_list[offset : offset+GET_ITERATOR_CHUNK_SIZE]),
AND)
self.do_query(related.field.m2m_db_table(), where)
@@ -59,11 +59,11 @@ class DeleteQuery(Query):
if isinstance(f, generic.GenericRelation):
from django.contrib.contenttypes.models import ContentType
field = f.rel.to._meta.get_field(f.content_type_field_name)
- w1.add((None, field.column, field.db_type(), 'exact', True,
- [ContentType.objects.get_for_model(cls).id]), AND)
+ w1.add((None, field.column, field, 'exact',
+ ContentType.objects.get_for_model(cls).id), AND)
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
where = self.where_class()
- where.add((None, f.m2m_column_name(), f.db_type(), 'in', True,
+ where.add((None, f.m2m_column_name(), f, 'in',
pk_list[offset : offset + GET_ITERATOR_CHUNK_SIZE]),
AND)
if w1:
@@ -81,7 +81,7 @@ class DeleteQuery(Query):
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
where = self.where_class()
field = self.model._meta.pk
- where.add((None, field.column, field.db_type(), 'in', True,
+ where.add((None, field.column, field, 'in',
pk_list[offset : offset + GET_ITERATOR_CHUNK_SIZE]), AND)
self.do_query(self.model._meta.db_table, where)
@@ -204,7 +204,7 @@ class UpdateQuery(Query):
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
self.where = self.where_class()
f = self.model._meta.pk
- self.where.add((None, f.column, f.db_type(), 'in', True,
+ self.where.add((None, f.column, f, 'in',
pk_list[offset : offset + GET_ITERATOR_CHUNK_SIZE]),
AND)
self.values = [(related_field.column, None, '%s')]
diff --git a/django/db/models/sql/where.py b/django/db/models/sql/where.py
index bf45bceb4b..18e4bf2f7e 100644
--- a/django/db/models/sql/where.py
+++ b/django/db/models/sql/where.py
@@ -27,7 +27,36 @@ class WhereNode(tree.Node):
"""
default = AND
- def as_sql(self, node=None, qn=None):
+ def add(self, data, connector):
+ """
+ Add a node to the where-tree. If the data is a list or tuple, it is
+ expected to be of the form (alias, col_name, field_obj, lookup_type,
+ value), which is then slightly munged before being stored (to avoid
+ storing any reference to field objects). Otherwise, the 'data' is
+ stored unchanged and can be anything with an 'as_sql()' method.
+ """
+ if not isinstance(data, (list, tuple)):
+ super(WhereNode, self).add(data, connector)
+ return
+
+ alias, col, field, lookup_type, value = data
+ if field:
+ params = field.get_db_prep_lookup(lookup_type, value)
+ db_type = field.db_type()
+ else:
+ # This is possible when we add a comparison to NULL sometimes (we
+ # don't really need to waste time looking up the associated field
+ # object).
+ params = Field().get_db_prep_lookup(lookup_type, value)
+ db_type = None
+ if isinstance(value, datetime.datetime):
+ annotation = datetime.datetime
+ else:
+ annotation = bool(value)
+ super(WhereNode, self).add((alias, col, db_type, lookup_type,
+ annotation, params), connector)
+
+ def as_sql(self, qn=None):
"""
Returns the SQL version of the where clause and the value to be
substituted in. Returns None, None if this node is empty.
@@ -36,60 +65,56 @@ class WhereNode(tree.Node):
(generally not needed except by the internal implementation for
recursion).
"""
- if node is None:
- node = self
if not qn:
qn = connection.ops.quote_name
- if not node.children:
+ if not self.children:
return None, []
result = []
result_params = []
empty = True
- for child in node.children:
+ for child in self.children:
try:
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)'
- elif len(child.children) == 1:
- format = '%s'
- else:
- format = '(%s)'
else:
+ # A leaf node in the tree.
sql, params = self.make_atom(child, qn)
- format = '%s'
except EmptyResultSet:
- if node.connector == AND and not node.negated:
+ if self.connector == AND and not self.negated:
# We can bail out early in this particular case (only).
raise
- elif node.negated:
+ elif self.negated:
empty = False
continue
except FullResultSet:
if self.connector == OR:
- if node.negated:
+ if self.negated:
empty = True
break
# We match everything. No need for any constraints.
return '', []
- if node.negated:
+ if self.negated:
empty = True
continue
empty = False
if sql:
- result.append(format % sql)
+ result.append(sql)
result_params.extend(params)
if empty:
raise EmptyResultSet
- conn = ' %s ' % node.connector
- return conn.join(result), result_params
+
+ 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:
+ sql_string = '(%s)' % sql_string
+ return sql_string, result_params
def make_atom(self, child, qn):
"""
- Turn a tuple (table_alias, field_name, db_type, lookup_type,
+ Turn a tuple (table_alias, column_name, db_type, lookup_type,
value_annot, params) into valid SQL.
Returns the string for the SQL fragment and the parameters to use for