Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
|
|
|
"""
|
|
|
|
Code to manage the creation and SQL rendering of 'where' constraints.
|
|
|
|
"""
|
2011-10-18 00:56:18 +08:00
|
|
|
|
2013-07-14 01:29:11 +08:00
|
|
|
import collections
|
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
|
|
|
import datetime
|
2010-09-17 03:53:41 +08:00
|
|
|
from itertools import repeat
|
2014-01-18 17:09:43 +08:00
|
|
|
import warnings
|
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
|
|
|
|
2013-02-10 23:15:49 +08:00
|
|
|
from django.conf import settings
|
|
|
|
from django.db.models.fields import DateTimeField, Field
|
2012-06-02 09:13:36 +08:00
|
|
|
from django.db.models.sql.datastructures import EmptyResultSet, Empty
|
2014-02-27 05:48:20 +08:00
|
|
|
from django.utils.deprecation import RemovedInDjango19Warning
|
2015-01-02 09:39:31 +08:00
|
|
|
from django.utils.functional import cached_property
|
2014-12-13 21:04:36 +08:00
|
|
|
from django.utils.six.moves import range
|
2013-02-10 23:15:49 +08:00
|
|
|
from django.utils import timezone
|
|
|
|
from django.utils import tree
|
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
|
|
|
|
2013-07-08 08:39:54 +08:00
|
|
|
|
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
|
|
|
# Connection types
|
|
|
|
AND = 'AND'
|
|
|
|
OR = 'OR'
|
|
|
|
|
2013-07-08 08:39:54 +08:00
|
|
|
|
2009-01-05 19:47:14 +08:00
|
|
|
class EmptyShortCircuit(Exception):
|
|
|
|
"""
|
|
|
|
Internal exception used to indicate that a "matches nothing" node should be
|
|
|
|
added to the where-clause.
|
|
|
|
"""
|
|
|
|
pass
|
|
|
|
|
2013-07-08 08:39:54 +08:00
|
|
|
|
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
|
|
|
class WhereNode(tree.Node):
|
|
|
|
"""
|
|
|
|
Used to represent the SQL where-clause.
|
|
|
|
|
|
|
|
The class is tied to the Query class that created it (in order to create
|
2008-06-12 11:37:13 +08:00
|
|
|
the correct SQL).
|
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
|
|
|
|
2013-03-02 07:06:56 +08:00
|
|
|
A child is usually a tuple of:
|
|
|
|
(Constraint(alias, targetcol, field), lookup_type, value)
|
|
|
|
where value can be either raw Python value, or Query, ExpressionNode or
|
|
|
|
something else knowing how to turn itself into SQL.
|
|
|
|
|
|
|
|
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.
|
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
|
|
|
"""
|
|
|
|
default = AND
|
|
|
|
|
Refactored qs.add_q() and utils/tree.py
The sql/query.py add_q method did a lot of where/having tree hacking to
get complex queries to work correctly. The logic was refactored so that
it should be simpler to understand. The new logic should also produce
leaner WHERE conditions.
The changes cascade somewhat, as some other parts of Django (like
add_filter() and WhereNode) expect boolean trees in certain format or
they fail to work. So to fix the add_q() one must fix utils/tree.py,
some things in add_filter(), WhereNode and so on.
This commit also fixed add_filter to see negate clauses up the path.
A query like .exclude(Q(reversefk__in=a_list)) didn't work similarly to
.filter(~Q(reversefk__in=a_list)). The reason for this is that only
the immediate parent negate clauses were seen by add_filter, and thus a
tree like AND: (NOT AND: (AND: condition)) will not be handled
correctly, as there is one intermediary AND node in the tree. The
example tree is generated by .exclude(~Q(reversefk__in=a_list)).
Still, aggregation lost connectors in OR cases, and F() objects and
aggregates in same filter clause caused GROUP BY problems on some
databases.
Fixed #17600, fixed #13198, fixed #17025, fixed #17000, fixed #11293.
2012-05-25 05:27:24 +08:00
|
|
|
def _prepare_data(self, data):
|
2008-07-04 14:42:58 +08:00
|
|
|
"""
|
Refactored qs.add_q() and utils/tree.py
The sql/query.py add_q method did a lot of where/having tree hacking to
get complex queries to work correctly. The logic was refactored so that
it should be simpler to understand. The new logic should also produce
leaner WHERE conditions.
The changes cascade somewhat, as some other parts of Django (like
add_filter() and WhereNode) expect boolean trees in certain format or
they fail to work. So to fix the add_q() one must fix utils/tree.py,
some things in add_filter(), WhereNode and so on.
This commit also fixed add_filter to see negate clauses up the path.
A query like .exclude(Q(reversefk__in=a_list)) didn't work similarly to
.filter(~Q(reversefk__in=a_list)). The reason for this is that only
the immediate parent negate clauses were seen by add_filter, and thus a
tree like AND: (NOT AND: (AND: condition)) will not be handled
correctly, as there is one intermediary AND node in the tree. The
example tree is generated by .exclude(~Q(reversefk__in=a_list)).
Still, aggregation lost connectors in OR cases, and F() objects and
aggregates in same filter clause caused GROUP BY problems on some
databases.
Fixed #17600, fixed #13198, fixed #17025, fixed #17000, fixed #11293.
2012-05-25 05:27:24 +08:00
|
|
|
Prepare data for addition to the tree. If the data is a list or tuple,
|
|
|
|
it is expected to be of the form (obj, lookup_type, value), where obj
|
|
|
|
is a Constraint object, and is then slightly munged before being
|
|
|
|
stored (to avoid storing any reference to field objects). Otherwise,
|
|
|
|
the 'data' is stored unchanged and can be any class with an 'as_sql()'
|
|
|
|
method.
|
2008-07-04 14:42:58 +08:00
|
|
|
"""
|
|
|
|
if not isinstance(data, (list, tuple)):
|
Refactored qs.add_q() and utils/tree.py
The sql/query.py add_q method did a lot of where/having tree hacking to
get complex queries to work correctly. The logic was refactored so that
it should be simpler to understand. The new logic should also produce
leaner WHERE conditions.
The changes cascade somewhat, as some other parts of Django (like
add_filter() and WhereNode) expect boolean trees in certain format or
they fail to work. So to fix the add_q() one must fix utils/tree.py,
some things in add_filter(), WhereNode and so on.
This commit also fixed add_filter to see negate clauses up the path.
A query like .exclude(Q(reversefk__in=a_list)) didn't work similarly to
.filter(~Q(reversefk__in=a_list)). The reason for this is that only
the immediate parent negate clauses were seen by add_filter, and thus a
tree like AND: (NOT AND: (AND: condition)) will not be handled
correctly, as there is one intermediary AND node in the tree. The
example tree is generated by .exclude(~Q(reversefk__in=a_list)).
Still, aggregation lost connectors in OR cases, and F() objects and
aggregates in same filter clause caused GROUP BY problems on some
databases.
Fixed #17600, fixed #13198, fixed #17025, fixed #17000, fixed #11293.
2012-05-25 05:27:24 +08:00
|
|
|
return data
|
2009-01-05 19:47:14 +08:00
|
|
|
obj, lookup_type, value = data
|
2013-07-14 01:29:11 +08:00
|
|
|
if isinstance(value, collections.Iterator):
|
2009-03-08 11:32:16 +08:00
|
|
|
# Consume any generators immediately, so that we can determine
|
|
|
|
# emptiness and transform any non-empty values correctly.
|
|
|
|
value = list(value)
|
2009-01-05 19:47:14 +08:00
|
|
|
|
2014-04-27 01:18:45 +08:00
|
|
|
# The "value_annotation" parameter is used to pass auxiliary information
|
2009-01-05 19:47:14 +08:00
|
|
|
# about the value(s) to the query construction. Specifically, datetime
|
|
|
|
# and empty values need special handling. Other types could be used
|
|
|
|
# here in the future (using Python types is suggested for consistency).
|
2013-02-10 23:15:49 +08:00
|
|
|
if (isinstance(value, datetime.datetime)
|
2013-11-26 17:43:46 +08:00
|
|
|
or (isinstance(obj.field, DateTimeField) and lookup_type != 'isnull')):
|
2012-02-23 03:40:27 +08:00
|
|
|
value_annotation = datetime.datetime
|
2009-01-08 13:16:21 +08:00
|
|
|
elif hasattr(value, 'value_annotation'):
|
2012-02-23 03:40:27 +08:00
|
|
|
value_annotation = value.value_annotation
|
2008-07-04 14:42:58 +08:00
|
|
|
else:
|
2012-02-23 03:40:27 +08:00
|
|
|
value_annotation = bool(value)
|
2009-01-05 19:47:14 +08:00
|
|
|
|
2013-12-25 21:13:18 +08:00
|
|
|
if hasattr(obj, 'prepare'):
|
2009-12-22 23:18:51 +08:00
|
|
|
value = obj.prepare(lookup_type, value)
|
Refactored qs.add_q() and utils/tree.py
The sql/query.py add_q method did a lot of where/having tree hacking to
get complex queries to work correctly. The logic was refactored so that
it should be simpler to understand. The new logic should also produce
leaner WHERE conditions.
The changes cascade somewhat, as some other parts of Django (like
add_filter() and WhereNode) expect boolean trees in certain format or
they fail to work. So to fix the add_q() one must fix utils/tree.py,
some things in add_filter(), WhereNode and so on.
This commit also fixed add_filter to see negate clauses up the path.
A query like .exclude(Q(reversefk__in=a_list)) didn't work similarly to
.filter(~Q(reversefk__in=a_list)). The reason for this is that only
the immediate parent negate clauses were seen by add_filter, and thus a
tree like AND: (NOT AND: (AND: condition)) will not be handled
correctly, as there is one intermediary AND node in the tree. The
example tree is generated by .exclude(~Q(reversefk__in=a_list)).
Still, aggregation lost connectors in OR cases, and F() objects and
aggregates in same filter clause caused GROUP BY problems on some
databases.
Fixed #17600, fixed #13198, fixed #17025, fixed #17000, fixed #11293.
2012-05-25 05:27:24 +08:00
|
|
|
return (obj, lookup_type, value_annotation, value)
|
2008-07-04 14:42:58 +08:00
|
|
|
|
2014-11-16 09:56:42 +08:00
|
|
|
def as_sql(self, compiler, connection):
|
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
|
|
|
"""
|
|
|
|
Returns the SQL version of the where clause and the value to be
|
2012-05-26 10:55:33 +08:00
|
|
|
substituted in. Returns '', [] if this node matches everything,
|
|
|
|
None, [] if this node is empty, and raises EmptyResultSet if this
|
|
|
|
node can't match anything.
|
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
|
|
|
"""
|
2012-05-26 10:55:33 +08:00
|
|
|
# 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.
|
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
|
|
|
result = []
|
|
|
|
result_params = []
|
2012-05-26 10:55:33 +08:00
|
|
|
everything_childs, nothing_childs = 0, 0
|
|
|
|
non_empty_childs = len(self.children)
|
|
|
|
|
2008-07-04 14:42:58 +08:00
|
|
|
for child in self.children:
|
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
|
|
|
try:
|
|
|
|
if hasattr(child, 'as_sql'):
|
2014-11-16 09:56:42 +08:00
|
|
|
sql, params = compiler.compile(child)
|
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
|
|
|
else:
|
2008-07-04 14:42:58 +08:00
|
|
|
# A leaf node in the tree.
|
2014-11-16 09:56:42 +08:00
|
|
|
sql, params = self.make_atom(child, compiler, connection)
|
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
|
|
|
except EmptyResultSet:
|
2012-05-26 10:55:33 +08:00
|
|
|
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:
|
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
|
|
|
return '', []
|
2012-05-26 10:55:33 +08:00
|
|
|
else:
|
|
|
|
raise EmptyResultSet
|
|
|
|
if full_needed - everything_childs <= 0:
|
2008-07-04 14:42:58 +08:00
|
|
|
if self.negated:
|
2012-05-26 10:55:33 +08:00
|
|
|
raise EmptyResultSet
|
|
|
|
else:
|
|
|
|
return '', []
|
2008-07-04 14:42:58 +08:00
|
|
|
|
2012-05-26 10:55:33 +08:00
|
|
|
if non_empty_childs == 0:
|
|
|
|
# All the child nodes were empty, so this one is empty, too.
|
|
|
|
return None, []
|
2008-07-04 14:42:58 +08:00
|
|
|
conn = ' %s ' % self.connector
|
|
|
|
sql_string = conn.join(result)
|
|
|
|
if sql_string:
|
2012-05-26 10:55:33 +08:00
|
|
|
if self.negated:
|
2012-07-03 15:29:10 +08:00
|
|
|
# Some backends (Oracle at least) need parentheses
|
|
|
|
# around the inner SQL in the negated case, even if the
|
|
|
|
# inner SQL contains just a single expression.
|
2012-07-02 03:49:38 +08:00
|
|
|
sql_string = 'NOT (%s)' % sql_string
|
|
|
|
elif len(result) > 1:
|
|
|
|
sql_string = '(%s)' % sql_string
|
2008-07-04 14:42:58 +08:00
|
|
|
return sql_string, result_params
|
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
|
|
|
|
2014-01-18 17:09:43 +08:00
|
|
|
def get_group_by_cols(self):
|
Refactored qs.add_q() and utils/tree.py
The sql/query.py add_q method did a lot of where/having tree hacking to
get complex queries to work correctly. The logic was refactored so that
it should be simpler to understand. The new logic should also produce
leaner WHERE conditions.
The changes cascade somewhat, as some other parts of Django (like
add_filter() and WhereNode) expect boolean trees in certain format or
they fail to work. So to fix the add_q() one must fix utils/tree.py,
some things in add_filter(), WhereNode and so on.
This commit also fixed add_filter to see negate clauses up the path.
A query like .exclude(Q(reversefk__in=a_list)) didn't work similarly to
.filter(~Q(reversefk__in=a_list)). The reason for this is that only
the immediate parent negate clauses were seen by add_filter, and thus a
tree like AND: (NOT AND: (AND: condition)) will not be handled
correctly, as there is one intermediary AND node in the tree. The
example tree is generated by .exclude(~Q(reversefk__in=a_list)).
Still, aggregation lost connectors in OR cases, and F() objects and
aggregates in same filter clause caused GROUP BY problems on some
databases.
Fixed #17600, fixed #13198, fixed #17025, fixed #17000, fixed #11293.
2012-05-25 05:27:24 +08:00
|
|
|
cols = []
|
|
|
|
for child in self.children:
|
2014-01-18 17:09:43 +08:00
|
|
|
if hasattr(child, 'get_group_by_cols'):
|
|
|
|
cols.extend(child.get_group_by_cols())
|
Refactored qs.add_q() and utils/tree.py
The sql/query.py add_q method did a lot of where/having tree hacking to
get complex queries to work correctly. The logic was refactored so that
it should be simpler to understand. The new logic should also produce
leaner WHERE conditions.
The changes cascade somewhat, as some other parts of Django (like
add_filter() and WhereNode) expect boolean trees in certain format or
they fail to work. So to fix the add_q() one must fix utils/tree.py,
some things in add_filter(), WhereNode and so on.
This commit also fixed add_filter to see negate clauses up the path.
A query like .exclude(Q(reversefk__in=a_list)) didn't work similarly to
.filter(~Q(reversefk__in=a_list)). The reason for this is that only
the immediate parent negate clauses were seen by add_filter, and thus a
tree like AND: (NOT AND: (AND: condition)) will not be handled
correctly, as there is one intermediary AND node in the tree. The
example tree is generated by .exclude(~Q(reversefk__in=a_list)).
Still, aggregation lost connectors in OR cases, and F() objects and
aggregates in same filter clause caused GROUP BY problems on some
databases.
Fixed #17600, fixed #13198, fixed #17025, fixed #17000, fixed #11293.
2012-05-25 05:27:24 +08:00
|
|
|
else:
|
|
|
|
if isinstance(child[0], Constraint):
|
|
|
|
cols.append((child[0].alias, child[0].col))
|
2014-01-18 17:09:43 +08:00
|
|
|
if hasattr(child[3], 'get_group_by_cols'):
|
|
|
|
cols.extend(child[3].get_group_by_cols())
|
Refactored qs.add_q() and utils/tree.py
The sql/query.py add_q method did a lot of where/having tree hacking to
get complex queries to work correctly. The logic was refactored so that
it should be simpler to understand. The new logic should also produce
leaner WHERE conditions.
The changes cascade somewhat, as some other parts of Django (like
add_filter() and WhereNode) expect boolean trees in certain format or
they fail to work. So to fix the add_q() one must fix utils/tree.py,
some things in add_filter(), WhereNode and so on.
This commit also fixed add_filter to see negate clauses up the path.
A query like .exclude(Q(reversefk__in=a_list)) didn't work similarly to
.filter(~Q(reversefk__in=a_list)). The reason for this is that only
the immediate parent negate clauses were seen by add_filter, and thus a
tree like AND: (NOT AND: (AND: condition)) will not be handled
correctly, as there is one intermediary AND node in the tree. The
example tree is generated by .exclude(~Q(reversefk__in=a_list)).
Still, aggregation lost connectors in OR cases, and F() objects and
aggregates in same filter clause caused GROUP BY problems on some
databases.
Fixed #17600, fixed #13198, fixed #17025, fixed #17000, fixed #11293.
2012-05-25 05:27:24 +08:00
|
|
|
return cols
|
|
|
|
|
2014-11-16 09:56:42 +08:00
|
|
|
def make_atom(self, child, compiler, connection):
|
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
|
|
|
"""
|
2012-02-23 03:40:27 +08:00
|
|
|
Turn a tuple (Constraint(table_alias, column_name, db_type),
|
|
|
|
lookup_type, value_annotation, params) into valid SQL.
|
|
|
|
|
|
|
|
The first item of the tuple may also be an Aggregate.
|
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
|
|
|
|
|
|
|
Returns the string for the SQL fragment and the parameters to use for
|
|
|
|
it.
|
|
|
|
"""
|
2014-01-18 17:09:43 +08:00
|
|
|
warnings.warn(
|
|
|
|
"The make_atom() method will be removed in Django 1.9. Use Lookup class instead.",
|
2014-02-27 05:48:20 +08:00
|
|
|
RemovedInDjango19Warning)
|
2012-02-23 03:40:27 +08:00
|
|
|
lvalue, lookup_type, value_annotation, params_or_value = child
|
2013-05-19 19:28:09 +08:00
|
|
|
field_internal_type = lvalue.field.get_internal_type() if lvalue.field else None
|
2013-07-08 08:39:54 +08:00
|
|
|
|
2012-02-23 03:40:27 +08:00
|
|
|
if isinstance(lvalue, Constraint):
|
2009-12-22 23:18:51 +08:00
|
|
|
try:
|
|
|
|
lvalue, params = lvalue.process(lookup_type, params_or_value, connection)
|
|
|
|
except EmptyShortCircuit:
|
|
|
|
raise EmptyResultSet
|
|
|
|
else:
|
2013-12-25 21:13:18 +08:00
|
|
|
raise TypeError("'make_atom' expects a Constraint as the first "
|
|
|
|
"item of its 'child' argument.")
|
2012-02-23 03:40:27 +08:00
|
|
|
|
2009-01-05 19:47:14 +08:00
|
|
|
if isinstance(lvalue, tuple):
|
|
|
|
# A direct database column lookup.
|
2014-11-16 09:56:42 +08:00
|
|
|
field_sql, field_params = self.sql_for_columns(lvalue, compiler, connection, field_internal_type), []
|
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
|
|
|
else:
|
2009-01-05 19:47:14 +08:00
|
|
|
# A smart object with an as_sql() method.
|
2014-11-16 09:56:42 +08:00
|
|
|
field_sql, field_params = compiler.compile(lvalue)
|
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
|
|
|
|
2013-02-10 23:15:49 +08:00
|
|
|
is_datetime_field = value_annotation is datetime.datetime
|
|
|
|
cast_sql = connection.ops.datetime_cast_sql() if is_datetime_field else '%s'
|
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
|
|
|
|
2009-01-29 18:46:36 +08:00
|
|
|
if hasattr(params, 'as_sql'):
|
2014-11-16 09:56:42 +08:00
|
|
|
extra, params = compiler.compile(params)
|
2009-01-29 18:46:36 +08:00
|
|
|
cast_sql = ''
|
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
|
|
|
else:
|
|
|
|
extra = ''
|
|
|
|
|
2013-02-10 23:15:49 +08:00
|
|
|
params = field_params + params
|
|
|
|
|
2009-12-22 23:18:51 +08:00
|
|
|
if (len(params) == 1 and params[0] == '' and lookup_type == 'exact'
|
2013-11-26 17:43:46 +08:00
|
|
|
and connection.features.interprets_empty_strings_as_nulls):
|
2009-12-22 23:18:51 +08:00
|
|
|
lookup_type = 'isnull'
|
2012-02-23 03:40:27 +08:00
|
|
|
value_annotation = True
|
2009-12-22 23:18:51 +08:00
|
|
|
|
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
|
|
|
if lookup_type in connection.operators:
|
2009-02-08 19:14:56 +08:00
|
|
|
format = "%s %%s %%s" % (connection.ops.lookup_cast(lookup_type),)
|
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
|
|
|
return (format % (field_sql,
|
2009-02-08 19:14:56 +08:00
|
|
|
connection.operators[lookup_type] % cast_sql,
|
|
|
|
extra), params)
|
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
|
|
|
|
|
|
|
if lookup_type == 'in':
|
2012-02-23 03:40:27 +08:00
|
|
|
if not value_annotation:
|
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
|
|
|
raise EmptyResultSet
|
|
|
|
if extra:
|
|
|
|
return ('%s IN %s' % (field_sql, extra), params)
|
2010-09-17 03:53:41 +08:00
|
|
|
max_in_list_size = connection.ops.max_in_list_size()
|
|
|
|
if max_in_list_size and len(params) > max_in_list_size:
|
|
|
|
# Break up the params list into an OR of manageable chunks.
|
|
|
|
in_clause_elements = ['(']
|
2014-12-13 21:04:36 +08:00
|
|
|
for offset in range(0, len(params), max_in_list_size):
|
2010-09-17 03:53:41 +08:00
|
|
|
if offset > 0:
|
|
|
|
in_clause_elements.append(' OR ')
|
|
|
|
in_clause_elements.append('%s IN (' % field_sql)
|
|
|
|
group_size = min(len(params) - offset, max_in_list_size)
|
|
|
|
param_group = ', '.join(repeat('%s', group_size))
|
|
|
|
in_clause_elements.append(param_group)
|
|
|
|
in_clause_elements.append(')')
|
|
|
|
in_clause_elements.append(')')
|
|
|
|
return ''.join(in_clause_elements), params
|
|
|
|
else:
|
|
|
|
return ('%s IN (%s)' % (field_sql,
|
|
|
|
', '.join(repeat('%s', len(params)))),
|
|
|
|
params)
|
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
|
|
|
elif lookup_type in ('range', 'year'):
|
|
|
|
return ('%s BETWEEN %%s and %%s' % field_sql, params)
|
2013-02-10 23:15:49 +08:00
|
|
|
elif is_datetime_field and lookup_type in ('month', 'day', 'week_day',
|
|
|
|
'hour', 'minute', 'second'):
|
|
|
|
tzname = timezone.get_current_timezone_name() if settings.USE_TZ else None
|
|
|
|
sql, tz_params = connection.ops.datetime_extract_sql(lookup_type, field_sql, tzname)
|
|
|
|
return ('%s = %%s' % sql, tz_params + params)
|
2009-02-08 13:08:06 +08:00
|
|
|
elif lookup_type in ('month', 'day', 'week_day'):
|
2013-02-10 23:15:49 +08:00
|
|
|
return ('%s = %%s'
|
|
|
|
% connection.ops.date_extract_sql(lookup_type, field_sql), params)
|
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
|
|
|
elif lookup_type == 'isnull':
|
2013-02-11 17:28:46 +08:00
|
|
|
assert value_annotation in (True, False), "Invalid value_annotation for isnull"
|
|
|
|
return ('%s IS %sNULL' % (field_sql, ('' if value_annotation else 'NOT ')), ())
|
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
|
|
|
elif lookup_type == 'search':
|
|
|
|
return (connection.ops.fulltext_search_sql(field_sql), params)
|
|
|
|
elif lookup_type in ('regex', 'iregex'):
|
|
|
|
return connection.ops.regex_lookup(lookup_type) % (field_sql, cast_sql), params
|
|
|
|
|
|
|
|
raise TypeError('Invalid lookup_type: %r' % lookup_type)
|
|
|
|
|
2013-05-19 19:28:09 +08:00
|
|
|
def sql_for_columns(self, data, qn, connection, internal_type=None):
|
2009-01-05 19:47:14 +08:00
|
|
|
"""
|
|
|
|
Returns the SQL fragment used for the left-hand side of a column
|
|
|
|
constraint (for example, the "T1.foo" portion in the clause
|
2013-02-10 23:15:49 +08:00
|
|
|
"WHERE ... T1.foo = 6") and a list of parameters.
|
2009-01-05 19:47:14 +08:00
|
|
|
"""
|
|
|
|
table_alias, name, db_type = data
|
|
|
|
if table_alias:
|
|
|
|
lhs = '%s.%s' % (qn(table_alias), qn(name))
|
|
|
|
else:
|
|
|
|
lhs = qn(name)
|
2013-05-19 19:28:09 +08:00
|
|
|
return connection.ops.field_cast_sql(db_type, internal_type) % lhs
|
2009-01-05 19:47:14 +08:00
|
|
|
|
2013-03-02 07:06:56 +08:00
|
|
|
def relabel_aliases(self, change_map):
|
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
|
|
|
"""
|
|
|
|
Relabels the alias values of any children. 'change_map' is a dictionary
|
|
|
|
mapping old (current) alias values to the new values.
|
|
|
|
"""
|
2013-03-02 07:06:56 +08:00
|
|
|
for pos, child in enumerate(self.children):
|
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
|
|
|
if hasattr(child, 'relabel_aliases'):
|
2013-03-02 07:06:56 +08:00
|
|
|
# For example another WhereNode
|
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
|
|
|
child.relabel_aliases(change_map)
|
2014-01-18 17:09:43 +08:00
|
|
|
elif hasattr(child, 'relabeled_clone'):
|
|
|
|
self.children[pos] = child.relabeled_clone(change_map)
|
2010-02-23 12:39:39 +08:00
|
|
|
elif isinstance(child, (list, tuple)):
|
2013-03-02 07:06:56 +08:00
|
|
|
# tuple starting with Constraint
|
|
|
|
child = (child[0].relabeled_clone(change_map),) + child[1:]
|
|
|
|
if hasattr(child[3], 'relabeled_clone'):
|
|
|
|
child = (child[0], child[1], child[2]) + (
|
|
|
|
child[3].relabeled_clone(change_map),)
|
|
|
|
self.children[pos] = child
|
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
|
|
|
|
2012-06-02 09:13:36 +08:00
|
|
|
def clone(self):
|
|
|
|
"""
|
|
|
|
Creates a clone of the tree. Must only be called on root nodes (nodes
|
|
|
|
with empty subtree_parents). Childs must be either (Contraint, lookup,
|
|
|
|
value) tuples, or objects supporting .clone().
|
|
|
|
"""
|
|
|
|
clone = self.__class__._new_instance(
|
|
|
|
children=[], connector=self.connector, negated=self.negated)
|
|
|
|
for child in self.children:
|
2013-03-02 07:06:56 +08:00
|
|
|
if hasattr(child, 'clone'):
|
2012-06-02 09:13:36 +08:00
|
|
|
clone.children.append(child.clone())
|
2013-03-02 07:06:56 +08:00
|
|
|
else:
|
|
|
|
clone.children.append(child)
|
2012-06-02 09:13:36 +08:00
|
|
|
return clone
|
|
|
|
|
2015-01-02 09:39:31 +08:00
|
|
|
def relabeled_clone(self, change_map):
|
|
|
|
clone = self.clone()
|
|
|
|
clone.relabel_aliases(change_map)
|
|
|
|
return clone
|
|
|
|
|
2015-01-13 08:55:57 +08:00
|
|
|
@classmethod
|
|
|
|
def _contains_aggregate(cls, obj):
|
|
|
|
if not isinstance(obj, tree.Node):
|
|
|
|
return getattr(obj.lhs, 'contains_aggregate', False) or getattr(obj.rhs, 'contains_aggregate', False)
|
|
|
|
return any(cls._contains_aggregate(c) for c in obj.children)
|
|
|
|
|
2015-01-02 09:39:31 +08:00
|
|
|
@cached_property
|
|
|
|
def contains_aggregate(self):
|
2015-01-13 08:55:57 +08:00
|
|
|
return self._contains_aggregate(self)
|
2015-01-02 09:39:31 +08:00
|
|
|
|
2015-01-13 08:55:57 +08:00
|
|
|
@classmethod
|
|
|
|
def _refs_field(cls, obj, aggregate_types, field_types):
|
|
|
|
if not isinstance(obj, tree.Node):
|
|
|
|
if hasattr(obj.rhs, 'refs_field'):
|
|
|
|
return obj.rhs.refs_field(aggregate_types, field_types)
|
|
|
|
return False
|
|
|
|
return any(cls._refs_field(c, aggregate_types, field_types) for c in obj.children)
|
2015-01-02 09:39:31 +08:00
|
|
|
|
|
|
|
def refs_field(self, aggregate_types, field_types):
|
2015-01-13 08:55:57 +08:00
|
|
|
return self._refs_field(self, aggregate_types, field_types)
|
2015-01-02 09:39:31 +08:00
|
|
|
|
2012-10-24 05:04:37 +08:00
|
|
|
|
2013-07-08 08:39:54 +08:00
|
|
|
class EmptyWhere(WhereNode):
|
2012-10-24 05:04:37 +08:00
|
|
|
def add(self, data, connector):
|
|
|
|
return
|
|
|
|
|
2014-11-16 09:56:42 +08:00
|
|
|
def as_sql(self, compiler=None, connection=None):
|
2012-10-24 05:04:37 +08:00
|
|
|
raise EmptyResultSet
|
|
|
|
|
2013-07-08 08:39:54 +08:00
|
|
|
|
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
|
|
|
class EverythingNode(object):
|
|
|
|
"""
|
|
|
|
A node that matches everything.
|
|
|
|
"""
|
2009-12-22 23:18:51 +08:00
|
|
|
|
2014-11-16 09:56:42 +08:00
|
|
|
def as_sql(self, compiler=None, connection=None):
|
2012-05-26 10:55:33 +08:00
|
|
|
return '', []
|
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
|
|
|
|
2012-06-02 09:13:36 +08:00
|
|
|
|
2008-07-23 14:12:15 +08:00
|
|
|
class NothingNode(object):
|
|
|
|
"""
|
|
|
|
A node that matches nothing.
|
|
|
|
"""
|
2014-11-16 09:56:42 +08:00
|
|
|
def as_sql(self, compiler=None, connection=None):
|
2008-07-23 14:12:15 +08:00
|
|
|
raise EmptyResultSet
|
|
|
|
|
2012-06-02 09:13:36 +08:00
|
|
|
|
2010-02-23 12:39:39 +08:00
|
|
|
class ExtraWhere(object):
|
|
|
|
def __init__(self, sqls, params):
|
|
|
|
self.sqls = sqls
|
|
|
|
self.params = params
|
2010-04-22 00:34:33 +08:00
|
|
|
|
2014-11-16 09:56:42 +08:00
|
|
|
def as_sql(self, compiler=None, connection=None):
|
2012-04-09 08:43:08 +08:00
|
|
|
sqls = ["(%s)" % sql for sql in self.sqls]
|
2013-02-10 23:15:49 +08:00
|
|
|
return " AND ".join(sqls), list(self.params or ())
|
2010-02-23 12:39:39 +08:00
|
|
|
|
2012-06-02 09:13:36 +08:00
|
|
|
|
2009-01-05 19:47:14 +08:00
|
|
|
class Constraint(object):
|
|
|
|
"""
|
|
|
|
An object that can be passed to WhereNode.add() and knows how to
|
|
|
|
pre-process itself prior to including in the WhereNode.
|
|
|
|
"""
|
|
|
|
def __init__(self, alias, col, field):
|
2014-01-18 17:09:43 +08:00
|
|
|
warnings.warn(
|
|
|
|
"The Constraint class will be removed in Django 1.9. Use Lookup class instead.",
|
2014-02-27 05:48:20 +08:00
|
|
|
RemovedInDjango19Warning)
|
2009-01-05 19:47:14 +08:00
|
|
|
self.alias, self.col, self.field = alias, col, field
|
|
|
|
|
2009-12-22 23:18:51 +08:00
|
|
|
def prepare(self, lookup_type, value):
|
2014-01-18 17:09:43 +08:00
|
|
|
if self.field and not hasattr(value, 'as_sql'):
|
2009-12-22 23:18:51 +08:00
|
|
|
return self.field.get_prep_lookup(lookup_type, value)
|
|
|
|
return value
|
|
|
|
|
|
|
|
def process(self, lookup_type, value, connection):
|
2009-01-05 19:47:14 +08:00
|
|
|
"""
|
|
|
|
Returns a tuple of data suitable for inclusion in a WhereNode
|
|
|
|
instance.
|
|
|
|
"""
|
|
|
|
# Because of circular imports, we need to import this here.
|
|
|
|
from django.db.models.base import ObjectDoesNotExist
|
|
|
|
try:
|
|
|
|
if self.field:
|
2009-12-22 23:18:51 +08:00
|
|
|
params = self.field.get_db_prep_lookup(lookup_type, value,
|
|
|
|
connection=connection, prepared=True)
|
|
|
|
db_type = self.field.db_type(connection=connection)
|
2009-01-05 19:47:14 +08:00
|
|
|
else:
|
|
|
|
# This branch is used at times when we add a comparison to NULL
|
|
|
|
# (we don't really want to waste time looking up the associated
|
|
|
|
# field object at the calling location).
|
2009-12-22 23:18:51 +08:00
|
|
|
params = Field().get_db_prep_lookup(lookup_type, value,
|
|
|
|
connection=connection, prepared=True)
|
2009-01-05 19:47:14 +08:00
|
|
|
db_type = None
|
|
|
|
except ObjectDoesNotExist:
|
|
|
|
raise EmptyShortCircuit
|
|
|
|
|
|
|
|
return (self.alias, self.col, db_type), params
|
|
|
|
|
2013-03-02 07:06:56 +08:00
|
|
|
def relabeled_clone(self, change_map):
|
|
|
|
if self.alias not in change_map:
|
|
|
|
return self
|
|
|
|
else:
|
|
|
|
new = Empty()
|
|
|
|
new.__class__ = self.__class__
|
|
|
|
new.alias, new.col, new.field = change_map[self.alias], self.col, self.field
|
|
|
|
return new
|
2013-03-25 00:40:40 +08:00
|
|
|
|
2013-07-08 08:39:54 +08:00
|
|
|
|
2013-03-25 00:40:40 +08:00
|
|
|
class SubqueryConstraint(object):
|
|
|
|
def __init__(self, alias, columns, targets, query_object):
|
|
|
|
self.alias = alias
|
|
|
|
self.columns = columns
|
|
|
|
self.targets = targets
|
|
|
|
self.query_object = query_object
|
|
|
|
|
2014-11-16 09:56:42 +08:00
|
|
|
def as_sql(self, compiler, connection):
|
2013-03-25 00:40:40 +08:00
|
|
|
query = self.query_object
|
|
|
|
|
|
|
|
# QuerySet was sent
|
|
|
|
if hasattr(query, 'values'):
|
2013-03-25 04:55:30 +08:00
|
|
|
if query._db and connection.alias != query._db:
|
|
|
|
raise ValueError("Can't do subqueries with queries on different DBs.")
|
2013-05-20 19:24:48 +08:00
|
|
|
# Do not override already existing values.
|
|
|
|
if not hasattr(query, 'field_names'):
|
|
|
|
query = query.values(*self.targets)
|
|
|
|
else:
|
|
|
|
query = query._clone()
|
|
|
|
query = query.query
|
2014-04-15 04:11:52 +08:00
|
|
|
if query.can_filter():
|
|
|
|
# If there is no slicing in use, then we can safely drop all ordering
|
|
|
|
query.clear_ordering(True)
|
2013-03-25 00:40:40 +08:00
|
|
|
|
|
|
|
query_compiler = query.get_compiler(connection=connection)
|
2014-11-16 09:56:42 +08:00
|
|
|
return query_compiler.as_subquery_condition(self.alias, self.columns, compiler)
|
2013-03-25 00:40:40 +08:00
|
|
|
|
2013-05-27 16:29:43 +08:00
|
|
|
def relabel_aliases(self, change_map):
|
|
|
|
self.alias = change_map.get(self.alias, self.alias)
|
|
|
|
|
|
|
|
def clone(self):
|
2013-03-25 00:40:40 +08:00
|
|
|
return self.__class__(
|
2013-05-27 16:29:43 +08:00
|
|
|
self.alias, self.columns, self.targets,
|
|
|
|
self.query_object)
|