~ubuntu-branches/ubuntu/quantal/python-django/quantal

« back to all changes in this revision

Viewing changes to django/db/models/sql/where.py

  • Committer: Bazaar Package Importer
  • Author(s): Chris Lamb
  • Date: 2009-07-29 11:26:28 UTC
  • mfrom: (1.1.8 upstream) (4.1.5 sid)
  • Revision ID: james.westby@ubuntu.com-20090729112628-pg09ino8sz0sj21t
Tags: 1.1-1
* New upstream release.
* Merge from experimental:
  - Ship FastCGI initscript and /etc/default file in python-django's examples
    directory (Closes: #538863)
  - Drop "05_10539-sphinx06-compatibility.diff"; it has been applied
    upstream.
  - Bump Standards-Version to 3.8.2.

Show diffs side-by-side

added added

removed removed

Lines of Context:
13
13
AND = 'AND'
14
14
OR = 'OR'
15
15
 
 
16
class EmptyShortCircuit(Exception):
 
17
    """
 
18
    Internal exception used to indicate that a "matches nothing" node should be
 
19
    added to the where-clause.
 
20
    """
 
21
    pass
 
22
 
16
23
class WhereNode(tree.Node):
17
24
    """
18
25
    Used to represent the SQL where-clause.
35
42
        storing any reference to field objects). Otherwise, the 'data' is
36
43
        stored unchanged and can be anything with an 'as_sql()' method.
37
44
        """
38
 
        # Because of circular imports, we need to import this here.
39
 
        from django.db.models.base import ObjectDoesNotExist
40
 
 
41
45
        if not isinstance(data, (list, tuple)):
42
46
            super(WhereNode, self).add(data, connector)
43
47
            return
44
48
 
45
 
        alias, col, field, lookup_type, value = data
46
 
        try:
47
 
            if field:
48
 
                params = field.get_db_prep_lookup(lookup_type, value)
49
 
                db_type = field.db_type()
50
 
            else:
51
 
                # This is possible when we add a comparison to NULL sometimes
52
 
                # (we don't really need to waste time looking up the associated
53
 
                # field object).
54
 
                params = Field().get_db_prep_lookup(lookup_type, value)
55
 
                db_type = None
56
 
        except ObjectDoesNotExist:
57
 
            # This can happen when trying to insert a reference to a null pk.
58
 
            # We break out of the normal path and indicate there's nothing to
59
 
            # match.
60
 
            super(WhereNode, self).add(NothingNode(), connector)
61
 
            return
 
49
        obj, lookup_type, value = data
 
50
        if hasattr(value, '__iter__') and hasattr(value, 'next'):
 
51
            # Consume any generators immediately, so that we can determine
 
52
            # emptiness and transform any non-empty values correctly.
 
53
            value = list(value)
 
54
        if hasattr(obj, "process"):
 
55
            try:
 
56
                obj, params = obj.process(lookup_type, value)
 
57
            except (EmptyShortCircuit, EmptyResultSet):
 
58
                # There are situations where we want to short-circuit any
 
59
                # comparisons and make sure that nothing is returned. One
 
60
                # example is when checking for a NULL pk value, or the
 
61
                # equivalent.
 
62
                super(WhereNode, self).add(NothingNode(), connector)
 
63
                return
 
64
        else:
 
65
            params = Field().get_db_prep_lookup(lookup_type, value)
 
66
 
 
67
        # The "annotation" parameter is used to pass auxilliary information
 
68
        # about the value(s) to the query construction. Specifically, datetime
 
69
        # and empty values need special handling. Other types could be used
 
70
        # here in the future (using Python types is suggested for consistency).
62
71
        if isinstance(value, datetime.datetime):
63
72
            annotation = datetime.datetime
 
73
        elif hasattr(value, 'value_annotation'):
 
74
            annotation = value.value_annotation
64
75
        else:
65
76
            annotation = bool(value)
66
 
        super(WhereNode, self).add((alias, col, db_type, lookup_type,
67
 
                annotation, params), connector)
 
77
 
 
78
        super(WhereNode, self).add((obj, lookup_type, annotation, params),
 
79
                connector)
68
80
 
69
81
    def as_sql(self, qn=None):
70
82
        """
89
101
                else:
90
102
                    # A leaf node in the tree.
91
103
                    sql, params = self.make_atom(child, qn)
 
104
 
92
105
            except EmptyResultSet:
93
106
                if self.connector == AND and not self.negated:
94
107
                    # We can bail out early in this particular case (only).
106
119
                if self.negated:
107
120
                    empty = True
108
121
                continue
 
122
 
109
123
            empty = False
110
124
            if sql:
111
125
                result.append(sql)
130
144
        Returns the string for the SQL fragment and the parameters to use for
131
145
        it.
132
146
        """
133
 
        table_alias, name, db_type, lookup_type, value_annot, params = child
134
 
        if table_alias:
135
 
            lhs = '%s.%s' % (qn(table_alias), qn(name))
 
147
        lvalue, lookup_type, value_annot, params = child
 
148
        if isinstance(lvalue, tuple):
 
149
            # A direct database column lookup.
 
150
            field_sql = self.sql_for_columns(lvalue, qn)
136
151
        else:
137
 
            lhs = qn(name)
138
 
        field_sql = connection.ops.field_cast_sql(db_type) % lhs
 
152
            # A smart object with an as_sql() method.
 
153
            field_sql = lvalue.as_sql(quote_func=qn)
139
154
 
140
155
        if value_annot is datetime.datetime:
141
156
            cast_sql = connection.ops.datetime_cast_sql()
142
157
        else:
143
158
            cast_sql = '%s'
144
159
 
145
 
        if isinstance(params, QueryWrapper):
146
 
            extra, params = params.data
 
160
        if hasattr(params, 'as_sql'):
 
161
            extra, params = params.as_sql(qn)
 
162
            cast_sql = ''
147
163
        else:
148
164
            extra = ''
149
165
 
150
166
        if lookup_type in connection.operators:
151
 
            format = "%s %%s %s" % (connection.ops.lookup_cast(lookup_type),
152
 
                    extra)
 
167
            format = "%s %%s %%s" % (connection.ops.lookup_cast(lookup_type),)
153
168
            return (format % (field_sql,
154
 
                    connection.operators[lookup_type] % cast_sql), params)
 
169
                              connection.operators[lookup_type] % cast_sql,
 
170
                              extra), params)
155
171
 
156
172
        if lookup_type == 'in':
157
173
            if not value_annot:
162
178
                    params)
163
179
        elif lookup_type in ('range', 'year'):
164
180
            return ('%s BETWEEN %%s and %%s' % field_sql, params)
165
 
        elif lookup_type in ('month', 'day'):
166
 
            return ('%s = %%s' % connection.ops.date_extract_sql(lookup_type,
167
 
                    field_sql), params)
 
181
        elif lookup_type in ('month', 'day', 'week_day'):
 
182
            return ('%s = %%s' % connection.ops.date_extract_sql(lookup_type, field_sql),
 
183
                    params)
168
184
        elif lookup_type == 'isnull':
169
185
            return ('%s IS %sNULL' % (field_sql,
170
186
                (not value_annot and 'NOT ' or '')), ())
175
191
 
176
192
        raise TypeError('Invalid lookup_type: %r' % lookup_type)
177
193
 
 
194
    def sql_for_columns(self, data, qn):
 
195
        """
 
196
        Returns the SQL fragment used for the left-hand side of a column
 
197
        constraint (for example, the "T1.foo" portion in the clause
 
198
        "WHERE ... T1.foo = 6").
 
199
        """
 
200
        table_alias, name, db_type = data
 
201
        if table_alias:
 
202
            lhs = '%s.%s' % (qn(table_alias), qn(name))
 
203
        else:
 
204
            lhs = qn(name)
 
205
        return connection.ops.field_cast_sql(db_type) % lhs
 
206
 
178
207
    def relabel_aliases(self, change_map, node=None):
179
208
        """
180
209
        Relabels the alias values of any children. 'change_map' is a dictionary
188
217
            elif isinstance(child, tree.Node):
189
218
                self.relabel_aliases(change_map, child)
190
219
            else:
191
 
                if child[0] in change_map:
192
 
                    node.children[pos] = (change_map[child[0]],) + child[1:]
 
220
                if isinstance(child[0], (list, tuple)):
 
221
                    elt = list(child[0])
 
222
                    if elt[0] in change_map:
 
223
                        elt[0] = change_map[elt[0]]
 
224
                        node.children[pos] = (tuple(elt),) + child[1:]
 
225
                else:
 
226
                    child[0].relabel_aliases(change_map)
 
227
 
 
228
                # Check if the query value also requires relabelling
 
229
                if hasattr(child[3], 'relabel_aliases'):
 
230
                    child[3].relabel_aliases(change_map)
193
231
 
194
232
class EverythingNode(object):
195
233
    """
211
249
    def relabel_aliases(self, change_map, node=None):
212
250
        return
213
251
 
 
252
class Constraint(object):
 
253
    """
 
254
    An object that can be passed to WhereNode.add() and knows how to
 
255
    pre-process itself prior to including in the WhereNode.
 
256
    """
 
257
    def __init__(self, alias, col, field):
 
258
        self.alias, self.col, self.field = alias, col, field
 
259
 
 
260
    def process(self, lookup_type, value):
 
261
        """
 
262
        Returns a tuple of data suitable for inclusion in a WhereNode
 
263
        instance.
 
264
        """
 
265
        # Because of circular imports, we need to import this here.
 
266
        from django.db.models.base import ObjectDoesNotExist
 
267
        try:
 
268
            if self.field:
 
269
                params = self.field.get_db_prep_lookup(lookup_type, value)
 
270
                db_type = self.field.db_type()
 
271
            else:
 
272
                # This branch is used at times when we add a comparison to NULL
 
273
                # (we don't really want to waste time looking up the associated
 
274
                # field object at the calling location).
 
275
                params = Field().get_db_prep_lookup(lookup_type, value)
 
276
                db_type = None
 
277
        except ObjectDoesNotExist:
 
278
            raise EmptyShortCircuit
 
279
 
 
280
        return (self.alias, self.col, db_type), params
 
281