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.
|
|
|
|
"""
|
2014-12-23 21:16:56 +08:00
|
|
|
|
2016-05-26 03:00:20 +08:00
|
|
|
from django.core.exceptions import EmptyResultSet
|
2013-02-10 23:15:49 +08:00
|
|
|
from django.utils import tree
|
2014-12-23 21:16:56 +08:00
|
|
|
from django.utils.functional import cached_property
|
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
|
|
|
|
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):
|
|
|
|
"""
|
2017-01-25 07:04:12 +08:00
|
|
|
An SQL WHERE clause.
|
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
|
|
|
|
|
|
|
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
|
|
|
|
2015-01-19 03:29:09 +08:00
|
|
|
A child is usually an expression producing boolean values. Most likely the
|
2015-02-04 16:21:29 +08:00
|
|
|
expression is a Lookup instance.
|
2013-03-02 07:06:56 +08:00
|
|
|
|
|
|
|
However, a child could also be any class with as_sql() and either
|
2015-02-04 16:21:29 +08:00
|
|
|
relabeled_clone() method or relabel_aliases() and clone() methods and
|
|
|
|
contains_aggregate attribute.
|
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
|
2017-12-02 10:48:49 +08:00
|
|
|
resolved = False
|
2017-12-08 23:59:49 +08:00
|
|
|
conditional = True
|
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-12-23 21:16:56 +08:00
|
|
|
def split_having(self, negated=False):
|
|
|
|
"""
|
2017-01-25 07:04:12 +08:00
|
|
|
Return two possibly None nodes: one for those parts of self that
|
2015-02-04 16:21:29 +08:00
|
|
|
should be included in the WHERE clause and one for those parts of
|
|
|
|
self that must be included in the HAVING clause.
|
2014-12-23 21:16:56 +08:00
|
|
|
"""
|
2015-02-04 16:21:29 +08:00
|
|
|
if not self.contains_aggregate:
|
|
|
|
return self, None
|
2014-12-23 21:16:56 +08:00
|
|
|
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.
|
|
|
|
may_need_split = (
|
|
|
|
(in_negated and self.connector == AND) or
|
|
|
|
(not in_negated and self.connector == OR))
|
|
|
|
if may_need_split and self.contains_aggregate:
|
|
|
|
return None, self
|
|
|
|
where_parts = []
|
|
|
|
having_parts = []
|
|
|
|
for c in self.children:
|
|
|
|
if hasattr(c, 'split_having'):
|
|
|
|
where_part, having_part = c.split_having(in_negated)
|
2015-02-04 16:21:29 +08:00
|
|
|
if where_part is not None:
|
2014-12-23 21:16:56 +08:00
|
|
|
where_parts.append(where_part)
|
2015-02-04 16:21:29 +08:00
|
|
|
if having_part is not None:
|
2014-12-23 21:16:56 +08:00
|
|
|
having_parts.append(having_part)
|
|
|
|
elif c.contains_aggregate:
|
|
|
|
having_parts.append(c)
|
|
|
|
else:
|
|
|
|
where_parts.append(c)
|
|
|
|
having_node = self.__class__(having_parts, self.connector, self.negated) if having_parts else None
|
|
|
|
where_node = self.__class__(where_parts, self.connector, self.negated) if where_parts else None
|
|
|
|
return where_node, having_node
|
|
|
|
|
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
|
|
|
"""
|
2017-01-25 07:04:12 +08:00
|
|
|
Return the SQL version of the where clause and the value to be
|
|
|
|
substituted in. Return '', [] if this node matches everything,
|
|
|
|
None, [] if this node is empty, and raise EmptyResultSet if this
|
2012-05-26 10:55:33 +08:00
|
|
|
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
|
|
|
"""
|
|
|
|
result = []
|
|
|
|
result_params = []
|
2015-02-04 16:21:29 +08:00
|
|
|
if self.connector == AND:
|
|
|
|
full_needed, empty_needed = len(self.children), 1
|
|
|
|
else:
|
|
|
|
full_needed, empty_needed = 1, len(self.children)
|
2012-05-26 10:55:33 +08:00
|
|
|
|
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:
|
2015-01-19 03:29:09 +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
|
|
|
except EmptyResultSet:
|
2015-02-04 16:21:29 +08:00
|
|
|
empty_needed -= 1
|
2012-05-26 10:55:33 +08:00
|
|
|
else:
|
|
|
|
if sql:
|
|
|
|
result.append(sql)
|
|
|
|
result_params.extend(params)
|
|
|
|
else:
|
2015-02-04 16:21:29 +08:00
|
|
|
full_needed -= 1
|
2012-05-26 10:55:33 +08:00
|
|
|
# Check if this node matches nothing or everything.
|
|
|
|
# First check the amount of full nodes and empty nodes
|
|
|
|
# to make this node empty/full.
|
|
|
|
# Now, check if this node is full/empty using the
|
|
|
|
# counts.
|
2015-02-04 16:21:29 +08:00
|
|
|
if empty_needed == 0:
|
2012-05-26 10:55:33 +08:00
|
|
|
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
|
2015-02-04 16:21:29 +08:00
|
|
|
if full_needed == 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
|
|
|
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
|
2017-12-02 10:48:49 +08:00
|
|
|
elif len(result) > 1 or self.resolved:
|
2012-07-02 03:49:38 +08:00
|
|
|
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
|
|
|
|
2019-03-19 13:05:47 +08:00
|
|
|
def get_group_by_cols(self, alias=None):
|
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:
|
2015-01-19 03:29:09 +08:00
|
|
|
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
|
|
|
return cols
|
|
|
|
|
2017-01-16 23:03:15 +08:00
|
|
|
def get_source_expressions(self):
|
|
|
|
return self.children[:]
|
|
|
|
|
|
|
|
def set_source_expressions(self, children):
|
|
|
|
assert len(children) == len(self.children)
|
|
|
|
self.children = children
|
|
|
|
|
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
|
|
|
"""
|
2017-01-25 07:04:12 +08:00
|
|
|
Relabel the alias values of any children. 'change_map' is a dictionary
|
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
|
|
|
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)
|
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):
|
|
|
|
"""
|
2017-01-25 07:04:12 +08:00
|
|
|
Create a clone of the tree. Must only be called on root nodes (nodes
|
2018-08-02 00:55:53 +08:00
|
|
|
with empty subtree_parents). Childs must be either (Constraint, lookup,
|
2012-06-02 09:13:36 +08:00
|
|
|
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):
|
2014-12-23 21:16:56 +08:00
|
|
|
if isinstance(obj, tree.Node):
|
|
|
|
return any(cls._contains_aggregate(c) for c in obj.children)
|
|
|
|
return obj.contains_aggregate
|
2015-01-13 08:55:57 +08:00
|
|
|
|
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
|
|
|
|
2017-09-18 21:42:29 +08:00
|
|
|
@classmethod
|
|
|
|
def _contains_over_clause(cls, obj):
|
|
|
|
if isinstance(obj, tree.Node):
|
|
|
|
return any(cls._contains_over_clause(c) for c in obj.children)
|
|
|
|
return obj.contains_over_clause
|
|
|
|
|
|
|
|
@cached_property
|
|
|
|
def contains_over_clause(self):
|
|
|
|
return self._contains_over_clause(self)
|
|
|
|
|
2017-01-16 23:03:15 +08:00
|
|
|
@property
|
|
|
|
def is_summary(self):
|
|
|
|
return any(child.is_summary for child in self.children)
|
|
|
|
|
2019-03-06 14:05:55 +08:00
|
|
|
@staticmethod
|
2019-08-16 11:20:57 +08:00
|
|
|
def _resolve_leaf(expr, query, *args, **kwargs):
|
|
|
|
if hasattr(expr, 'resolve_expression'):
|
|
|
|
expr = expr.resolve_expression(query, *args, **kwargs)
|
|
|
|
return expr
|
2019-03-06 14:05:55 +08:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def _resolve_node(cls, node, query, *args, **kwargs):
|
|
|
|
if hasattr(node, 'children'):
|
|
|
|
for child in node.children:
|
|
|
|
cls._resolve_node(child, query, *args, **kwargs)
|
2019-08-16 11:20:57 +08:00
|
|
|
if hasattr(node, 'lhs'):
|
|
|
|
node.lhs = cls._resolve_leaf(node.lhs, query, *args, **kwargs)
|
2019-03-06 14:05:55 +08:00
|
|
|
if hasattr(node, 'rhs'):
|
2019-08-16 11:20:57 +08:00
|
|
|
node.rhs = cls._resolve_leaf(node.rhs, query, *args, **kwargs)
|
2019-03-06 14:05:55 +08:00
|
|
|
|
2017-12-02 10:48:49 +08:00
|
|
|
def resolve_expression(self, *args, **kwargs):
|
|
|
|
clone = self.clone()
|
2019-03-06 14:05:55 +08:00
|
|
|
clone._resolve_node(clone, *args, **kwargs)
|
2017-12-02 10:48:49 +08:00
|
|
|
clone.resolved = True
|
|
|
|
return clone
|
|
|
|
|
2012-10-24 05:04:37 +08:00
|
|
|
|
2017-01-19 15:39:46 +08:00
|
|
|
class NothingNode:
|
2017-01-25 07:04:12 +08:00
|
|
|
"""A node that matches nothing."""
|
2014-12-23 21:16:56 +08:00
|
|
|
contains_aggregate = False
|
|
|
|
|
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
|
|
|
|
2017-01-19 15:39:46 +08:00
|
|
|
class ExtraWhere:
|
2014-12-23 21:16:56 +08:00
|
|
|
# The contents are a black box - assume no aggregates are used.
|
|
|
|
contains_aggregate = False
|
|
|
|
|
2010-02-23 12:39:39 +08:00
|
|
|
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
|
|
|
|
2017-01-19 15:39:46 +08:00
|
|
|
class SubqueryConstraint:
|
2014-12-23 21:16:56 +08:00
|
|
|
# Even if aggregates would be used in a subquery, the outer query isn't
|
|
|
|
# interested about those.
|
|
|
|
contains_aggregate = False
|
|
|
|
|
2013-03-25 00:40:40 +08:00
|
|
|
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
|
2016-10-28 23:20:23 +08:00
|
|
|
query.set_values(self.targets)
|
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)
|