2 Code to manage the creation and SQL rendering of 'where' constraints.
6 from django
.utils
import tree
7 from django
.db
import connection
8 from django
.db
.models
.fields
import Field
9 from django
.db
.models
.query_utils
import QueryWrapper
10 from datastructures
import EmptyResultSet
, FullResultSet
16 class WhereNode(tree
.Node
):
18 Used to represent the SQL where-clause.
20 The class is tied to the Query class that created it (in order to create
23 The children in this tree are usually either Q-like objects or lists of
24 [table_alias, field_name, field_class, lookup_type, value]. However, a
25 child could also be any class with as_sql() and relabel_aliases() methods.
29 def as_sql(self
, node
=None, qn
=None):
31 Returns the SQL version of the where clause and the value to be
32 substituted in. Returns None, None if this node is empty.
34 If 'node' is provided, that is the root of the SQL generation
35 (generally not needed except by the internal implementation for
41 qn
= connection
.ops
.quote_name
47 for child
in node
.children
:
49 if hasattr(child
, 'as_sql'):
50 sql
, params
= child
.as_sql(qn
=qn
)
52 elif isinstance(child
, tree
.Node
):
53 sql
, params
= self
.as_sql(child
, qn
)
54 if len(child
.children
) == 1:
59 format
= 'NOT %s' % format
61 sql
, params
= self
.make_atom(child
, qn
)
63 except EmptyResultSet
:
64 if node
.connector
== AND
and not node
.negated
:
65 # We can bail out early in this particular case (only).
71 if self
.connector
== OR
:
75 # We match everything. No need for any constraints.
82 result
.append(format
% sql
)
83 result_params
.extend(params
)
86 conn
= ' %s ' % node
.connector
87 return conn
.join(result
), result_params
89 def make_atom(self
, child
, qn
):
91 Turn a tuple (table_alias, field_name, field_class, lookup_type, value)
94 Returns the string for the SQL fragment and the parameters to use for
97 table_alias
, name
, field
, lookup_type
, value
= child
99 lhs
= '%s.%s' % (qn(table_alias
), qn(name
))
102 db_type
= field
and field
.db_type() or None
103 field_sql
= connection
.ops
.field_cast_sql(db_type
) % lhs
105 if isinstance(value
, datetime
.datetime
):
106 cast_sql
= connection
.ops
.datetime_cast_sql()
111 params
= field
.get_db_prep_lookup(lookup_type
, value
)
113 params
= Field().get_db_prep_lookup(lookup_type
, value
)
114 if isinstance(params
, QueryWrapper
):
115 extra
, params
= params
.data
119 if lookup_type
in connection
.operators
:
120 format
= "%s %%s %s" % (connection
.ops
.lookup_cast(lookup_type
),
122 return (format
% (field_sql
,
123 connection
.operators
[lookup_type
] % cast_sql
), params
)
125 if lookup_type
== 'in':
129 return ('%s IN %s' % (field_sql
, extra
), params
)
130 return ('%s IN (%s)' % (field_sql
, ', '.join(['%s'] * len(value
))),
132 elif lookup_type
in ('range', 'year'):
133 return ('%s BETWEEN %%s and %%s' % field_sql
, params
)
134 elif lookup_type
in ('month', 'day'):
135 return ('%s = %%s' % connection
.ops
.date_extract_sql(lookup_type
,
137 elif lookup_type
== 'isnull':
138 return ('%s IS %sNULL' % (field_sql
, (not value
and 'NOT ' or '')),
140 elif lookup_type
== 'search':
141 return (connection
.ops
.fulltext_search_sql(field_sql
), params
)
142 elif lookup_type
in ('regex', 'iregex'):
143 return connection
.ops
.regex_lookup(lookup_type
) % (field_sql
, cast_sql
), params
145 raise TypeError('Invalid lookup_type: %r' % lookup_type
)
147 def relabel_aliases(self
, change_map
, node
=None):
149 Relabels the alias values of any children. 'change_map' is a dictionary
150 mapping old (current) alias values to the new values.
154 for pos
, child
in enumerate(node
.children
):
155 if hasattr(child
, 'relabel_aliases'):
156 child
.relabel_aliases(change_map
)
157 elif isinstance(child
, tree
.Node
):
158 self
.relabel_aliases(change_map
, child
)
160 if child
[0] in change_map
:
161 node
.children
[pos
] = (change_map
[child
[0]],) + child
[1:]
163 class EverythingNode(object):
165 A node that matches everything.
167 def as_sql(self
, qn
=None):
170 def relabel_aliases(self
, change_map
, node
=None):