mirror of
https://github.com/django/django.git
synced 2025-07-06 02:39:12 +00:00
queryset-refactor: Optimised some internal data structures in sql/query.py.
Mostly this involves changing them to a "copy on write" implementation, which speeds up cloning (and the relevant structures aren't updated very frequently). git-svn-id: http://code.djangoproject.com/svn/django/branches/queryset-refactor@7247 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
7534ef1f7e
commit
93baa3e417
@ -421,16 +421,7 @@ class _QuerySet(object):
|
|||||||
assert self.query.can_filter(), \
|
assert self.query.can_filter(), \
|
||||||
"Cannot change a query once a slice has been taken"
|
"Cannot change a query once a slice has been taken"
|
||||||
clone = self._clone()
|
clone = self._clone()
|
||||||
if select:
|
clone.query.add_extra(select, where, params, tables, order_by)
|
||||||
clone.query.extra_select.update(select)
|
|
||||||
if where:
|
|
||||||
clone.query.extra_where.extend(where)
|
|
||||||
if params:
|
|
||||||
clone.query.extra_params.extend(params)
|
|
||||||
if tables:
|
|
||||||
clone.query.extra_tables.extend(tables)
|
|
||||||
if order_by:
|
|
||||||
clone.query.extra_order_by = order_by
|
|
||||||
return clone
|
return clone
|
||||||
|
|
||||||
def reverse(self):
|
def reverse(self):
|
||||||
|
@ -22,11 +22,7 @@ JOIN_TYPE = 2
|
|||||||
LHS_ALIAS = 3
|
LHS_ALIAS = 3
|
||||||
LHS_JOIN_COL = 4
|
LHS_JOIN_COL = 4
|
||||||
RHS_JOIN_COL = 5
|
RHS_JOIN_COL = 5
|
||||||
# Alias map lists
|
NULLABLE = 6
|
||||||
ALIAS_TABLE = 0
|
|
||||||
ALIAS_REFCOUNT = 1
|
|
||||||
ALIAS_JOIN = 2
|
|
||||||
ALIAS_NULLABLE=3
|
|
||||||
|
|
||||||
# How many results to expect from a cursor.execute call
|
# How many results to expect from a cursor.execute call
|
||||||
MULTI = 'multi'
|
MULTI = 'multi'
|
||||||
|
@ -7,7 +7,7 @@ databases). The abstraction barrier only works one way: this module has to know
|
|||||||
all about the internals of models in order to get the information it needs.
|
all about the internals of models in order to get the information it needs.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import copy
|
from copy import deepcopy
|
||||||
|
|
||||||
from django.utils.tree import Node
|
from django.utils.tree import Node
|
||||||
from django.utils.datastructures import SortedDict
|
from django.utils.datastructures import SortedDict
|
||||||
@ -42,7 +42,8 @@ class Query(object):
|
|||||||
def __init__(self, model, connection, where=WhereNode):
|
def __init__(self, model, connection, where=WhereNode):
|
||||||
self.model = model
|
self.model = model
|
||||||
self.connection = connection
|
self.connection = connection
|
||||||
self.alias_map = {} # Maps alias to table name
|
self.alias_refcount = {}
|
||||||
|
self.alias_map = {} # Maps alias to join information
|
||||||
self.table_map = {} # Maps table names to list of aliases.
|
self.table_map = {} # Maps table names to list of aliases.
|
||||||
self.rev_join_map = {} # Reverse of join_map. (FIXME: Update comment)
|
self.rev_join_map = {} # Reverse of join_map. (FIXME: Update comment)
|
||||||
self.quote_cache = {}
|
self.quote_cache = {}
|
||||||
@ -69,11 +70,11 @@ class Query(object):
|
|||||||
|
|
||||||
# These are for extensions. The contents are more or less appended
|
# These are for extensions. The contents are more or less appended
|
||||||
# verbatim to the appropriate clause.
|
# verbatim to the appropriate clause.
|
||||||
self.extra_select = SortedDict() # Maps col_alias -> col_sql.
|
self.extra_select = {} # Maps col_alias -> col_sql.
|
||||||
self.extra_tables = []
|
self.extra_tables = ()
|
||||||
self.extra_where = []
|
self.extra_where = ()
|
||||||
self.extra_params = []
|
self.extra_params = ()
|
||||||
self.extra_order_by = []
|
self.extra_order_by = ()
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
"""
|
"""
|
||||||
@ -125,7 +126,8 @@ class Query(object):
|
|||||||
obj.__class__ = klass or self.__class__
|
obj.__class__ = klass or self.__class__
|
||||||
obj.model = self.model
|
obj.model = self.model
|
||||||
obj.connection = self.connection
|
obj.connection = self.connection
|
||||||
obj.alias_map = copy.deepcopy(self.alias_map)
|
obj.alias_refcount = self.alias_refcount.copy()
|
||||||
|
obj.alias_map = self.alias_map.copy()
|
||||||
obj.table_map = self.table_map.copy()
|
obj.table_map = self.table_map.copy()
|
||||||
obj.rev_join_map = self.rev_join_map.copy()
|
obj.rev_join_map = self.rev_join_map.copy()
|
||||||
obj.quote_cache = {}
|
obj.quote_cache = {}
|
||||||
@ -135,7 +137,7 @@ class Query(object):
|
|||||||
obj.start_meta = self.start_meta
|
obj.start_meta = self.start_meta
|
||||||
obj.select = self.select[:]
|
obj.select = self.select[:]
|
||||||
obj.tables = self.tables[:]
|
obj.tables = self.tables[:]
|
||||||
obj.where = copy.deepcopy(self.where)
|
obj.where = deepcopy(self.where)
|
||||||
obj.where_class = self.where_class
|
obj.where_class = self.where_class
|
||||||
obj.group_by = self.group_by[:]
|
obj.group_by = self.group_by[:]
|
||||||
obj.having = self.having[:]
|
obj.having = self.having[:]
|
||||||
@ -145,10 +147,10 @@ class Query(object):
|
|||||||
obj.select_related = self.select_related
|
obj.select_related = self.select_related
|
||||||
obj.max_depth = self.max_depth
|
obj.max_depth = self.max_depth
|
||||||
obj.extra_select = self.extra_select.copy()
|
obj.extra_select = self.extra_select.copy()
|
||||||
obj.extra_tables = self.extra_tables[:]
|
obj.extra_tables = self.extra_tables
|
||||||
obj.extra_where = self.extra_where[:]
|
obj.extra_where = self.extra_where
|
||||||
obj.extra_params = self.extra_params[:]
|
obj.extra_params = self.extra_params
|
||||||
obj.extra_order_by = self.extra_order_by[:]
|
obj.extra_order_by = self.extra_order_by
|
||||||
obj.__dict__.update(kwargs)
|
obj.__dict__.update(kwargs)
|
||||||
if hasattr(obj, '_setup_query'):
|
if hasattr(obj, '_setup_query'):
|
||||||
obj._setup_query()
|
obj._setup_query()
|
||||||
@ -179,7 +181,7 @@ class Query(object):
|
|||||||
obj = self.clone(CountQuery, _query=obj, where=self.where_class(),
|
obj = self.clone(CountQuery, _query=obj, where=self.where_class(),
|
||||||
distinct=False)
|
distinct=False)
|
||||||
obj.select = []
|
obj.select = []
|
||||||
obj.extra_select = SortedDict()
|
obj.extra_select = {}
|
||||||
obj.add_count_column()
|
obj.add_count_column()
|
||||||
data = obj.execute_sql(SINGLE)
|
data = obj.execute_sql(SINGLE)
|
||||||
if not data:
|
if not data:
|
||||||
@ -272,11 +274,10 @@ class Query(object):
|
|||||||
conjunction = (connector == AND)
|
conjunction = (connector == AND)
|
||||||
first = True
|
first = True
|
||||||
for alias in rhs.tables:
|
for alias in rhs.tables:
|
||||||
if not rhs.alias_map[alias][ALIAS_REFCOUNT]:
|
if not rhs.alias_refcount[alias]:
|
||||||
# An unused alias.
|
# An unused alias.
|
||||||
continue
|
continue
|
||||||
promote = (rhs.alias_map[alias][ALIAS_JOIN][JOIN_TYPE] ==
|
promote = (rhs.alias_map[alias][JOIN_TYPE] == self.LOUTER)
|
||||||
self.LOUTER)
|
|
||||||
new_alias = self.join(rhs.rev_join_map[alias],
|
new_alias = self.join(rhs.rev_join_map[alias],
|
||||||
(conjunction and not first), used, promote, not conjunction)
|
(conjunction and not first), used, promote, not conjunction)
|
||||||
used[new_alias] = None
|
used[new_alias] = None
|
||||||
@ -288,14 +289,14 @@ class Query(object):
|
|||||||
# to an outer join.
|
# to an outer join.
|
||||||
if not conjunction:
|
if not conjunction:
|
||||||
for alias in self.tables[1:]:
|
for alias in self.tables[1:]:
|
||||||
if self.alias_map[alias][ALIAS_REFCOUNT] == 1:
|
if self.alias_refcount[alias] == 1:
|
||||||
self.alias_map[alias][ALIAS_JOIN][JOIN_TYPE] = self.LOUTER
|
self.promote_alias(alias, True)
|
||||||
break
|
break
|
||||||
|
|
||||||
# Now relabel a copy of the rhs where-clause and add it to the current
|
# Now relabel a copy of the rhs where-clause and add it to the current
|
||||||
# one.
|
# one.
|
||||||
if rhs.where:
|
if rhs.where:
|
||||||
w = copy.deepcopy(rhs.where)
|
w = deepcopy(rhs.where)
|
||||||
w.relabel_aliases(change_map)
|
w.relabel_aliases(change_map)
|
||||||
if not self.where:
|
if not self.where:
|
||||||
# Since 'self' matches everything, add an explicit "include
|
# Since 'self' matches everything, add an explicit "include
|
||||||
@ -316,19 +317,18 @@ class Query(object):
|
|||||||
if isinstance(col, (list, tuple)):
|
if isinstance(col, (list, tuple)):
|
||||||
self.select.append((change_map.get(col[0], col[0]), col[1]))
|
self.select.append((change_map.get(col[0], col[0]), col[1]))
|
||||||
else:
|
else:
|
||||||
item = copy.deepcopy(col)
|
item = deepcopy(col)
|
||||||
item.relabel_aliases(change_map)
|
item.relabel_aliases(change_map)
|
||||||
self.select.append(item)
|
self.select.append(item)
|
||||||
self.extra_select = rhs.extra_select.copy()
|
self.extra_select = rhs.extra_select.copy()
|
||||||
self.extra_tables = rhs.extra_tables[:]
|
self.extra_tables = rhs.extra_tables
|
||||||
self.extra_where = rhs.extra_where[:]
|
self.extra_where = rhs.extra_where
|
||||||
self.extra_params = rhs.extra_params[:]
|
self.extra_params = rhs.extra_params
|
||||||
|
|
||||||
# Ordering uses the 'rhs' ordering, unless it has none, in which case
|
# Ordering uses the 'rhs' ordering, unless it has none, in which case
|
||||||
# the current ordering is used.
|
# the current ordering is used.
|
||||||
self.order_by = rhs.order_by and rhs.order_by[:] or self.order_by
|
self.order_by = rhs.order_by and rhs.order_by[:] or self.order_by
|
||||||
self.extra_order_by = (rhs.extra_order_by and rhs.extra_order_by[:] or
|
self.extra_order_by = rhs.extra_order_by or self.extra_order_by
|
||||||
self.extra_order_by)
|
|
||||||
|
|
||||||
def pre_sql_setup(self):
|
def pre_sql_setup(self):
|
||||||
"""
|
"""
|
||||||
@ -411,11 +411,11 @@ class Query(object):
|
|||||||
qn = self.quote_name_unless_alias
|
qn = self.quote_name_unless_alias
|
||||||
first = True
|
first = True
|
||||||
for alias in self.tables:
|
for alias in self.tables:
|
||||||
if not self.alias_map[alias][ALIAS_REFCOUNT]:
|
if not self.alias_refcount[alias]:
|
||||||
continue
|
continue
|
||||||
join = self.alias_map[alias][ALIAS_JOIN]
|
join = self.alias_map[alias]
|
||||||
if join:
|
if join:
|
||||||
name, alias, join_type, lhs, lhs_col, col = join
|
name, alias, join_type, lhs, lhs_col, col, nullable = join
|
||||||
alias_str = (alias != name and ' AS %s' % alias or '')
|
alias_str = (alias != name and ' AS %s' % alias or '')
|
||||||
else:
|
else:
|
||||||
join_type = None
|
join_type = None
|
||||||
@ -429,7 +429,6 @@ class Query(object):
|
|||||||
connector = not first and ', ' or ''
|
connector = not first and ', ' or ''
|
||||||
result.append('%s%s%s' % (connector, qn(name), alias_str))
|
result.append('%s%s%s' % (connector, qn(name), alias_str))
|
||||||
first = False
|
first = False
|
||||||
extra_tables = []
|
|
||||||
for t in self.extra_tables:
|
for t in self.extra_tables:
|
||||||
alias, created = self.table_alias(t)
|
alias, created = self.table_alias(t)
|
||||||
if created:
|
if created:
|
||||||
@ -554,7 +553,7 @@ class Query(object):
|
|||||||
# We have to do the same "final join" optimisation as in
|
# We have to do the same "final join" optimisation as in
|
||||||
# add_filter, since the final column might not otherwise be part of
|
# add_filter, since the final column might not otherwise be part of
|
||||||
# the select set (so we can't order on it).
|
# the select set (so we can't order on it).
|
||||||
join = self.alias_map[alias][ALIAS_JOIN]
|
join = self.alias_map[alias]
|
||||||
if col == join[RHS_JOIN_COL]:
|
if col == join[RHS_JOIN_COL]:
|
||||||
self.unref_alias(alias)
|
self.unref_alias(alias)
|
||||||
alias = join[LHS_ALIAS]
|
alias = join[LHS_ALIAS]
|
||||||
@ -571,7 +570,7 @@ class Query(object):
|
|||||||
"""
|
"""
|
||||||
if not create and table_name in self.table_map:
|
if not create and table_name in self.table_map:
|
||||||
alias = self.table_map[table_name][-1]
|
alias = self.table_map[table_name][-1]
|
||||||
self.alias_map[alias][ALIAS_REFCOUNT] += 1
|
self.alias_refcount[alias] += 1
|
||||||
return alias, False
|
return alias, False
|
||||||
|
|
||||||
# Create a new alias for this table.
|
# Create a new alias for this table.
|
||||||
@ -580,26 +579,32 @@ class Query(object):
|
|||||||
alias = table_name
|
alias = table_name
|
||||||
else:
|
else:
|
||||||
alias = '%s%d' % (self.alias_prefix, len(self.alias_map) + 1)
|
alias = '%s%d' % (self.alias_prefix, len(self.alias_map) + 1)
|
||||||
self.alias_map[alias] = [table_name, 1, None, False]
|
self.alias_refcount[alias] = 1
|
||||||
|
self.alias_map[alias] = None
|
||||||
self.table_map.setdefault(table_name, []).append(alias)
|
self.table_map.setdefault(table_name, []).append(alias)
|
||||||
self.tables.append(alias)
|
self.tables.append(alias)
|
||||||
return alias, True
|
return alias, True
|
||||||
|
|
||||||
def ref_alias(self, alias):
|
def ref_alias(self, alias):
|
||||||
""" Increases the reference count for this alias. """
|
""" Increases the reference count for this alias. """
|
||||||
self.alias_map[alias][ALIAS_REFCOUNT] += 1
|
self.alias_refcount[alias] += 1
|
||||||
|
|
||||||
def unref_alias(self, alias):
|
def unref_alias(self, alias):
|
||||||
""" Decreases the reference count for this alias. """
|
""" Decreases the reference count for this alias. """
|
||||||
self.alias_map[alias][ALIAS_REFCOUNT] -= 1
|
self.alias_refcount[alias] -= 1
|
||||||
|
|
||||||
def promote_alias(self, alias):
|
def promote_alias(self, alias, unconditional=False):
|
||||||
"""
|
"""
|
||||||
Promotes the join type of an alias to an outer join if it's possible
|
Promotes the join type of an alias to an outer join if it's possible
|
||||||
for the join to contain NULL values on the left.
|
for the join to contain NULL values on the left. If 'unconditional' is
|
||||||
|
False, the join is only promoted if it is nullable, otherwise it is
|
||||||
|
always promoted.
|
||||||
"""
|
"""
|
||||||
if self.alias_map[alias][ALIAS_NULLABLE]:
|
if ((unconditional or self.alias_map[alias][NULLABLE]) and
|
||||||
self.alias_map[alias][ALIAS_JOIN][JOIN_TYPE] = self.LOUTER
|
self.alias_map[alias] != self.LOUTER):
|
||||||
|
data = list(self.alias_map[alias])
|
||||||
|
data[JOIN_TYPE] = self.LOUTER
|
||||||
|
self.alias_map[alias] = tuple(data)
|
||||||
|
|
||||||
def change_aliases(self, change_map):
|
def change_aliases(self, change_map):
|
||||||
"""
|
"""
|
||||||
@ -619,27 +624,34 @@ class Query(object):
|
|||||||
|
|
||||||
# 2. Rename the alias in the internal table/alias datastructures.
|
# 2. Rename the alias in the internal table/alias datastructures.
|
||||||
for old_alias, new_alias in change_map.items():
|
for old_alias, new_alias in change_map.items():
|
||||||
alias_data = self.alias_map[old_alias]
|
alias_data = list(self.alias_map[old_alias])
|
||||||
alias_data[ALIAS_JOIN][RHS_ALIAS] = new_alias
|
alias_data[RHS_ALIAS] = new_alias
|
||||||
|
|
||||||
self.rev_join_map[new_alias] = self.rev_join_map[old_alias]
|
self.rev_join_map[new_alias] = self.rev_join_map[old_alias]
|
||||||
del self.rev_join_map[old_alias]
|
del self.rev_join_map[old_alias]
|
||||||
table_aliases = self.table_map[alias_data[ALIAS_TABLE]]
|
self.alias_refcount[new_alias] = self.alias_refcount[old_alias]
|
||||||
|
del self.alias_refcount[old_alias]
|
||||||
|
self.alias_map[new_alias] = tuple(alias_data)
|
||||||
|
del self.alias_map[old_alias]
|
||||||
|
|
||||||
|
table_aliases = self.table_map[alias_data[TABLE_NAME]]
|
||||||
for pos, alias in enumerate(table_aliases):
|
for pos, alias in enumerate(table_aliases):
|
||||||
if alias == old_alias:
|
if alias == old_alias:
|
||||||
table_aliases[pos] = new_alias
|
table_aliases[pos] = new_alias
|
||||||
break
|
break
|
||||||
self.alias_map[new_alias] = alias_data
|
|
||||||
del self.alias_map[old_alias]
|
|
||||||
for pos, alias in enumerate(self.tables):
|
for pos, alias in enumerate(self.tables):
|
||||||
if alias == old_alias:
|
if alias == old_alias:
|
||||||
self.tables[pos] = new_alias
|
self.tables[pos] = new_alias
|
||||||
break
|
break
|
||||||
|
|
||||||
# 3. Update any joins that refer to the old alias.
|
# 3. Update any joins that refer to the old alias.
|
||||||
for data in self.alias_map.values():
|
for alias, data in self.alias_map.items():
|
||||||
alias = data[ALIAS_JOIN][LHS_ALIAS]
|
lhs = data[LHS_ALIAS]
|
||||||
if alias in change_map:
|
if lhs in change_map:
|
||||||
data[ALIAS_JOIN][LHS_ALIAS] = change_map[alias]
|
data = list(data)
|
||||||
|
data[LHS_ALIAS] = change_map[lhs]
|
||||||
|
self.alias_map[alias] = tuple(data)
|
||||||
|
|
||||||
|
|
||||||
def bump_prefix(self):
|
def bump_prefix(self):
|
||||||
"""
|
"""
|
||||||
@ -678,7 +690,7 @@ class Query(object):
|
|||||||
Returns the number of tables in this query with a non-zero reference
|
Returns the number of tables in this query with a non-zero reference
|
||||||
count.
|
count.
|
||||||
"""
|
"""
|
||||||
return len([1 for o in self.alias_map.values() if o[ALIAS_REFCOUNT]])
|
return len([1 for count in self.alias_refcount.values() if count])
|
||||||
|
|
||||||
def join(self, connection, always_create=False, exclusions=(),
|
def join(self, connection, always_create=False, exclusions=(),
|
||||||
promote=False, outer_if_first=False, nullable=False):
|
promote=False, outer_if_first=False, nullable=False):
|
||||||
@ -717,34 +729,33 @@ class Query(object):
|
|||||||
lhs_table = lhs
|
lhs_table = lhs
|
||||||
is_table = True
|
is_table = True
|
||||||
else:
|
else:
|
||||||
lhs_table = self.alias_map[lhs][ALIAS_TABLE]
|
lhs_table = self.alias_map[lhs][TABLE_NAME]
|
||||||
is_table = False
|
is_table = False
|
||||||
t_ident = (lhs_table, table, lhs_col, col)
|
t_ident = (lhs_table, table, lhs_col, col)
|
||||||
if not always_create:
|
if not always_create:
|
||||||
for alias, row in self.rev_join_map.items():
|
for alias, row in self.rev_join_map.items():
|
||||||
if t_ident == row and alias not in exclusions:
|
if t_ident == row and alias not in exclusions:
|
||||||
self.ref_alias(alias)
|
self.ref_alias(alias)
|
||||||
if promote and self.alias_map[alias][ALIAS_NULLABLE]:
|
if promote:
|
||||||
self.alias_map[alias][ALIAS_JOIN][JOIN_TYPE] = self.LOUTER
|
self.promote_alias(alias)
|
||||||
return alias
|
return alias
|
||||||
# If we get to here (no non-excluded alias exists), we'll fall
|
# If we get to here (no non-excluded alias exists), we'll fall
|
||||||
# through to creating a new alias.
|
# through to creating a new alias.
|
||||||
|
|
||||||
# No reuse is possible, so we need a new alias.
|
# No reuse is possible, so we need a new alias.
|
||||||
assert not is_table, \
|
assert not is_table, \
|
||||||
"Must pass in lhs alias when creating a new join."
|
"Must pass in lhs alias when creating a new join."
|
||||||
alias, _ = self.table_alias(table, True)
|
alias, _ = self.table_alias(table, True)
|
||||||
if promote or outer_if_first:
|
|
||||||
join_type = self.LOUTER
|
|
||||||
else:
|
|
||||||
join_type = self.INNER
|
|
||||||
join = [table, alias, join_type, lhs, lhs_col, col]
|
|
||||||
if not lhs:
|
if not lhs:
|
||||||
# Not all tables need to be joined to anything. No join type
|
# Not all tables need to be joined to anything. No join type
|
||||||
# means the later columns are ignored.
|
# means the later columns are ignored.
|
||||||
join[JOIN_TYPE] = None
|
join_type = None
|
||||||
self.alias_map[alias][ALIAS_JOIN] = join
|
elif promote or outer_if_first:
|
||||||
self.alias_map[alias][ALIAS_NULLABLE] = nullable
|
join_type = self.LOUTER
|
||||||
|
else:
|
||||||
|
join_type = self.INNER
|
||||||
|
join = (table, alias, join_type, lhs, lhs_col, col, nullable)
|
||||||
|
self.alias_map[alias] = join
|
||||||
self.rev_join_map[alias] = t_ident
|
self.rev_join_map[alias] = t_ident
|
||||||
return alias
|
return alias
|
||||||
|
|
||||||
@ -850,7 +861,7 @@ class Query(object):
|
|||||||
if trim and len(join_list) > 1:
|
if trim and len(join_list) > 1:
|
||||||
extra = join_list[-1]
|
extra = join_list[-1]
|
||||||
join_list = join_list[:-1]
|
join_list = join_list[:-1]
|
||||||
col = self.alias_map[extra[0]][ALIAS_JOIN][LHS_JOIN_COL]
|
col = self.alias_map[extra[0]][LHS_JOIN_COL]
|
||||||
for alias in extra:
|
for alias in extra:
|
||||||
self.unref_alias(alias)
|
self.unref_alias(alias)
|
||||||
else:
|
else:
|
||||||
@ -862,7 +873,7 @@ class Query(object):
|
|||||||
# we are comparing against, we can go back one step in the join
|
# we are comparing against, we can go back one step in the join
|
||||||
# chain and compare against the lhs of the join instead. The result
|
# chain and compare against the lhs of the join instead. The result
|
||||||
# (potentially) involves one less table join.
|
# (potentially) involves one less table join.
|
||||||
join = self.alias_map[alias][ALIAS_JOIN]
|
join = self.alias_map[alias]
|
||||||
if col == join[RHS_JOIN_COL]:
|
if col == join[RHS_JOIN_COL]:
|
||||||
self.unref_alias(alias)
|
self.unref_alias(alias)
|
||||||
alias = join[LHS_ALIAS]
|
alias = join[LHS_ALIAS]
|
||||||
@ -891,7 +902,7 @@ class Query(object):
|
|||||||
join_it.next(), table_it.next()
|
join_it.next(), table_it.next()
|
||||||
for join in join_it:
|
for join in join_it:
|
||||||
table = table_it.next()
|
table = table_it.next()
|
||||||
if join == table and self.alias_map[join][ALIAS_REFCOUNT] > 1:
|
if join == table and self.alias_refcount[join] > 1:
|
||||||
continue
|
continue
|
||||||
self.promote_alias(join)
|
self.promote_alias(join)
|
||||||
if table != join:
|
if table != join:
|
||||||
@ -904,7 +915,7 @@ class Query(object):
|
|||||||
# that's harmless.
|
# that's harmless.
|
||||||
self.promote_alias(table)
|
self.promote_alias(table)
|
||||||
|
|
||||||
entry = [alias, col, field, lookup_type, value]
|
entry = (alias, col, field, lookup_type, value)
|
||||||
if merge_negated:
|
if merge_negated:
|
||||||
# This case is when we're doing the Q2 filter in exclude(Q1, Q2).
|
# This case is when we're doing the Q2 filter in exclude(Q1, Q2).
|
||||||
# It's different from exclude(Q1).exclude(Q2).
|
# It's different from exclude(Q1).exclude(Q2).
|
||||||
@ -926,8 +937,8 @@ class Query(object):
|
|||||||
if not merged:
|
if not merged:
|
||||||
self.where.negate()
|
self.where.negate()
|
||||||
if count > 1 and lookup_type != 'isnull':
|
if count > 1 and lookup_type != 'isnull':
|
||||||
j_col = self.alias_map[alias][ALIAS_JOIN][RHS_JOIN_COL]
|
j_col = self.alias_map[alias][RHS_JOIN_COL]
|
||||||
entry = Node([[alias, j_col, None, 'isnull', True]])
|
entry = Node([(alias, j_col, None, 'isnull', True)])
|
||||||
entry.negate()
|
entry.negate()
|
||||||
self.where.add(entry, AND)
|
self.where.add(entry, AND)
|
||||||
|
|
||||||
@ -1198,7 +1209,7 @@ class Query(object):
|
|||||||
no ordering in the resulting query (not even the model's default).
|
no ordering in the resulting query (not even the model's default).
|
||||||
"""
|
"""
|
||||||
self.order_by = []
|
self.order_by = []
|
||||||
self.extra_order_by = []
|
self.extra_order_by = ()
|
||||||
if force_empty:
|
if force_empty:
|
||||||
self.default_ordering = False
|
self.default_ordering = False
|
||||||
|
|
||||||
@ -1232,7 +1243,7 @@ class Query(object):
|
|||||||
# level.
|
# level.
|
||||||
self.distinct = False
|
self.distinct = False
|
||||||
self.select = [select]
|
self.select = [select]
|
||||||
self.extra_select = SortedDict()
|
self.extra_select = {}
|
||||||
|
|
||||||
def add_select_related(self, fields):
|
def add_select_related(self, fields):
|
||||||
"""
|
"""
|
||||||
@ -1247,6 +1258,27 @@ class Query(object):
|
|||||||
d = d.setdefault(part, {})
|
d = d.setdefault(part, {})
|
||||||
self.select_related = field_dict
|
self.select_related = field_dict
|
||||||
|
|
||||||
|
def add_extra(self, select, where, params, tables, order_by):
|
||||||
|
"""
|
||||||
|
Adds data to the various extra_* attributes for user-created additiosn
|
||||||
|
to the query.
|
||||||
|
"""
|
||||||
|
if select:
|
||||||
|
# The extra select might be ordered (because it will be accepting
|
||||||
|
# parameters).
|
||||||
|
if (isinstance(select, SortedDict) and
|
||||||
|
not isinstance(self.extra_select, SortedDict)):
|
||||||
|
self.extra_select = SortedDict(self.extra_select)
|
||||||
|
self.extra_select.update(select)
|
||||||
|
if where:
|
||||||
|
self.extra_where += tuple(where)
|
||||||
|
if params:
|
||||||
|
self.extra_params += tuple(params)
|
||||||
|
if tables:
|
||||||
|
self.extra_tables += tuple(tables)
|
||||||
|
if order_by:
|
||||||
|
self.extra_order_by = order_by
|
||||||
|
|
||||||
def set_start(self, start):
|
def set_start(self, start):
|
||||||
"""
|
"""
|
||||||
Sets the table from which to start joining. The start position is
|
Sets the table from which to start joining. The start position is
|
||||||
@ -1263,7 +1295,7 @@ class Query(object):
|
|||||||
field, col, opts, joins = self.setup_joins(start.split(LOOKUP_SEP),
|
field, col, opts, joins = self.setup_joins(start.split(LOOKUP_SEP),
|
||||||
opts, alias, False)
|
opts, alias, False)
|
||||||
alias = joins[-1][0]
|
alias = joins[-1][0]
|
||||||
self.select = [(alias, self.alias_map[alias][ALIAS_JOIN][RHS_JOIN_COL])]
|
self.select = [(alias, self.alias_map[alias][RHS_JOIN_COL])]
|
||||||
self.start_meta = opts
|
self.start_meta = opts
|
||||||
|
|
||||||
# The call to setup_joins add an extra reference to everything in
|
# The call to setup_joins add an extra reference to everything in
|
||||||
|
@ -175,7 +175,7 @@ class UpdateQuery(Query):
|
|||||||
else:
|
else:
|
||||||
self.add_filter(('pk__in', query))
|
self.add_filter(('pk__in', query))
|
||||||
for alias in self.tables[1:]:
|
for alias in self.tables[1:]:
|
||||||
self.alias_map[alias][ALIAS_REFCOUNT] = 0
|
self.alias_refcount[alias] = 0
|
||||||
|
|
||||||
def clear_related(self, related_field, pk_list):
|
def clear_related(self, related_field, pk_list):
|
||||||
"""
|
"""
|
||||||
|
@ -151,14 +151,14 @@ class WhereNode(tree.Node):
|
|||||||
"""
|
"""
|
||||||
if not node:
|
if not node:
|
||||||
node = self
|
node = self
|
||||||
for child in node.children:
|
for pos, child in enumerate(node.children):
|
||||||
if hasattr(child, 'relabel_aliases'):
|
if hasattr(child, 'relabel_aliases'):
|
||||||
child.relabel_aliases(change_map)
|
child.relabel_aliases(change_map)
|
||||||
elif isinstance(child, tree.Node):
|
elif isinstance(child, tree.Node):
|
||||||
self.relabel_aliases(change_map, child)
|
self.relabel_aliases(change_map, child)
|
||||||
else:
|
else:
|
||||||
val = child[0]
|
if child[0] in change_map:
|
||||||
child[0] = change_map.get(val, val)
|
node.children[pos] = (change_map[child[0]],) + child[1:]
|
||||||
|
|
||||||
class EverythingNode(object):
|
class EverythingNode(object):
|
||||||
"""
|
"""
|
||||||
|
@ -205,7 +205,7 @@ Bug #1801
|
|||||||
Bug #2306
|
Bug #2306
|
||||||
Checking that no join types are "left outer" joins.
|
Checking that no join types are "left outer" joins.
|
||||||
>>> query = Item.objects.filter(tags=t2).query
|
>>> query = Item.objects.filter(tags=t2).query
|
||||||
>>> query.LOUTER not in [x[2][2] for x in query.alias_map.values()]
|
>>> query.LOUTER not in [x[2] for x in query.alias_map.values()]
|
||||||
True
|
True
|
||||||
|
|
||||||
>>> Item.objects.filter(Q(tags=t1)).order_by('name')
|
>>> Item.objects.filter(Q(tags=t1)).order_by('name')
|
||||||
@ -332,12 +332,12 @@ Bug #5324, #6704
|
|||||||
|
|
||||||
# Excluding from a relation that cannot be NULL should not use outer joins.
|
# Excluding from a relation that cannot be NULL should not use outer joins.
|
||||||
>>> query = Item.objects.exclude(creator__in=[a1, a2]).query
|
>>> query = Item.objects.exclude(creator__in=[a1, a2]).query
|
||||||
>>> query.LOUTER not in [x[2][2] for x in query.alias_map.values()]
|
>>> query.LOUTER not in [x[2] for x in query.alias_map.values()]
|
||||||
True
|
True
|
||||||
|
|
||||||
Similarly, when one of the joins cannot possibly, ever, involve NULL values (Author -> ExtraInfo, in the following), it should never be promoted to a left outer join. So hte following query should only involve one "left outer" join (Author -> Item is 0-to-many).
|
Similarly, when one of the joins cannot possibly, ever, involve NULL values (Author -> ExtraInfo, in the following), it should never be promoted to a left outer join. So hte following query should only involve one "left outer" join (Author -> Item is 0-to-many).
|
||||||
>>> qs = Author.objects.filter(id=a1.id).filter(Q(extra__note=n1)|Q(item__note=n3))
|
>>> qs = Author.objects.filter(id=a1.id).filter(Q(extra__note=n1)|Q(item__note=n3))
|
||||||
>>> len([x[2][2] for x in qs.query.alias_map.values() if x[2][2] == query.LOUTER])
|
>>> len([x[2] for x in qs.query.alias_map.values() if x[2] == query.LOUTER])
|
||||||
1
|
1
|
||||||
|
|
||||||
The previous changes shouldn't affect nullable foreign key joins.
|
The previous changes shouldn't affect nullable foreign key joins.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user