mirror of
https://github.com/django/django.git
synced 2024-12-23 01:25:58 +00:00
Fixed #20950 -- Instantiate OrderedDict() only when needed
The use of OrderedDict (even an empty one) was surprisingly slow. By initializing OrderedDict only when needed it is possible to save non-trivial amount of computing time (Model.save() is around 30% faster for example). This commit targetted sql.Query only, there are likely other places which could use similar optimizations.
This commit is contained in:
parent
886bb9d878
commit
ff723d894d
@ -391,7 +391,7 @@ class SQLCompiler(object):
|
|||||||
if not distinct or elt in select_aliases:
|
if not distinct or elt in select_aliases:
|
||||||
result.append('%s %s' % (elt, order))
|
result.append('%s %s' % (elt, order))
|
||||||
group_by.append((elt, []))
|
group_by.append((elt, []))
|
||||||
elif get_order_dir(field)[0] not in self.query.extra:
|
elif not self.query._extra or get_order_dir(field)[0] not in self.query._extra:
|
||||||
# 'col' is of the form 'field' or 'field1__field2' or
|
# 'col' is of the form 'field' or 'field1__field2' or
|
||||||
# '-field1__field2__field', etc.
|
# '-field1__field2__field', etc.
|
||||||
for table, cols, order in self.find_ordering_name(field,
|
for table, cols, order in self.find_ordering_name(field,
|
||||||
@ -987,7 +987,7 @@ class SQLUpdateCompiler(SQLCompiler):
|
|||||||
# We need to use a sub-select in the where clause to filter on things
|
# We need to use a sub-select in the where clause to filter on things
|
||||||
# from other tables.
|
# from other tables.
|
||||||
query = self.query.clone(klass=Query)
|
query = self.query.clone(klass=Query)
|
||||||
query.extra = {}
|
query._extra = {}
|
||||||
query.select = []
|
query.select = []
|
||||||
query.add_fields([query.get_meta().pk.name])
|
query.add_fields([query.get_meta().pk.name])
|
||||||
# Recheck the count - it is possible that fiddling with the select
|
# Recheck the count - it is possible that fiddling with the select
|
||||||
|
@ -143,7 +143,10 @@ class Query(object):
|
|||||||
self.select_related = False
|
self.select_related = False
|
||||||
|
|
||||||
# SQL aggregate-related attributes
|
# SQL aggregate-related attributes
|
||||||
self.aggregates = OrderedDict() # Maps alias -> SQL aggregate function
|
# The _aggregates will be an OrderedDict when used. Due to the cost
|
||||||
|
# of creating OrderedDict this attribute is created lazily (in
|
||||||
|
# self.aggregates property).
|
||||||
|
self._aggregates = None # Maps alias -> SQL aggregate function
|
||||||
self.aggregate_select_mask = None
|
self.aggregate_select_mask = None
|
||||||
self._aggregate_select_cache = None
|
self._aggregate_select_cache = None
|
||||||
|
|
||||||
@ -153,7 +156,9 @@ 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 = OrderedDict() # Maps col_alias -> (col_sql, params).
|
# The _extra attribute is an OrderedDict, lazily created similarly to
|
||||||
|
# .aggregates
|
||||||
|
self._extra = None # Maps col_alias -> (col_sql, params).
|
||||||
self.extra_select_mask = None
|
self.extra_select_mask = None
|
||||||
self._extra_select_cache = None
|
self._extra_select_cache = None
|
||||||
|
|
||||||
@ -165,6 +170,18 @@ class Query(object):
|
|||||||
# load.
|
# load.
|
||||||
self.deferred_loading = (set(), True)
|
self.deferred_loading = (set(), True)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def extra(self):
|
||||||
|
if self._extra is None:
|
||||||
|
self._extra = OrderedDict()
|
||||||
|
return self._extra
|
||||||
|
|
||||||
|
@property
|
||||||
|
def aggregates(self):
|
||||||
|
if self._aggregates is None:
|
||||||
|
self._aggregates = OrderedDict()
|
||||||
|
return self._aggregates
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
"""
|
"""
|
||||||
Returns the query as a string of SQL with the parameter values
|
Returns the query as a string of SQL with the parameter values
|
||||||
@ -245,7 +262,7 @@ class Query(object):
|
|||||||
obj.select_for_update_nowait = self.select_for_update_nowait
|
obj.select_for_update_nowait = self.select_for_update_nowait
|
||||||
obj.select_related = self.select_related
|
obj.select_related = self.select_related
|
||||||
obj.related_select_cols = []
|
obj.related_select_cols = []
|
||||||
obj.aggregates = self.aggregates.copy()
|
obj._aggregates = self._aggregates.copy() if self._aggregates is not None else None
|
||||||
if self.aggregate_select_mask is None:
|
if self.aggregate_select_mask is None:
|
||||||
obj.aggregate_select_mask = None
|
obj.aggregate_select_mask = None
|
||||||
else:
|
else:
|
||||||
@ -257,7 +274,7 @@ class Query(object):
|
|||||||
# used.
|
# used.
|
||||||
obj._aggregate_select_cache = None
|
obj._aggregate_select_cache = None
|
||||||
obj.max_depth = self.max_depth
|
obj.max_depth = self.max_depth
|
||||||
obj.extra = self.extra.copy()
|
obj._extra = self._extra.copy() if self._extra is not None else None
|
||||||
if self.extra_select_mask is None:
|
if self.extra_select_mask is None:
|
||||||
obj.extra_select_mask = None
|
obj.extra_select_mask = None
|
||||||
else:
|
else:
|
||||||
@ -344,7 +361,7 @@ class Query(object):
|
|||||||
# and move them to the outer AggregateQuery.
|
# and move them to the outer AggregateQuery.
|
||||||
for alias, aggregate in self.aggregate_select.items():
|
for alias, aggregate in self.aggregate_select.items():
|
||||||
if aggregate.is_summary:
|
if aggregate.is_summary:
|
||||||
query.aggregate_select[alias] = aggregate.relabeled_clone(relabels)
|
query.aggregates[alias] = aggregate.relabeled_clone(relabels)
|
||||||
del obj.aggregate_select[alias]
|
del obj.aggregate_select[alias]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -358,7 +375,7 @@ class Query(object):
|
|||||||
query = self
|
query = self
|
||||||
self.select = []
|
self.select = []
|
||||||
self.default_cols = False
|
self.default_cols = False
|
||||||
self.extra = {}
|
self._extra = {}
|
||||||
self.remove_inherited_models()
|
self.remove_inherited_models()
|
||||||
|
|
||||||
query.clear_ordering(True)
|
query.clear_ordering(True)
|
||||||
@ -527,7 +544,7 @@ class Query(object):
|
|||||||
# It would be nice to be able to handle this, but the queries don't
|
# It would be nice to be able to handle this, but the queries don't
|
||||||
# really make sense (or return consistent value sets). Not worth
|
# really make sense (or return consistent value sets). Not worth
|
||||||
# the extra complexity when you can write a real query instead.
|
# the extra complexity when you can write a real query instead.
|
||||||
if self.extra and rhs.extra:
|
if self._extra and rhs._extra:
|
||||||
raise ValueError("When merging querysets using 'or', you "
|
raise ValueError("When merging querysets using 'or', you "
|
||||||
"cannot have extra(select=...) on both sides.")
|
"cannot have extra(select=...) on both sides.")
|
||||||
self.extra.update(rhs.extra)
|
self.extra.update(rhs.extra)
|
||||||
@ -756,8 +773,9 @@ class Query(object):
|
|||||||
self.group_by = [relabel_column(col) for col in self.group_by]
|
self.group_by = [relabel_column(col) for col in self.group_by]
|
||||||
self.select = [SelectInfo(relabel_column(s.col), s.field)
|
self.select = [SelectInfo(relabel_column(s.col), s.field)
|
||||||
for s in self.select]
|
for s in self.select]
|
||||||
self.aggregates = OrderedDict(
|
if self._aggregates:
|
||||||
(key, relabel_column(col)) for key, col in self.aggregates.items())
|
self._aggregates = OrderedDict(
|
||||||
|
(key, relabel_column(col)) for key, col in self._aggregates.items())
|
||||||
|
|
||||||
# 2. Rename the alias in the internal table/alias datastructures.
|
# 2. Rename the alias in the internal table/alias datastructures.
|
||||||
for ident, aliases in self.join_map.items():
|
for ident, aliases in self.join_map.items():
|
||||||
@ -967,7 +985,7 @@ class Query(object):
|
|||||||
"""
|
"""
|
||||||
opts = model._meta
|
opts = model._meta
|
||||||
field_list = aggregate.lookup.split(LOOKUP_SEP)
|
field_list = aggregate.lookup.split(LOOKUP_SEP)
|
||||||
if len(field_list) == 1 and aggregate.lookup in self.aggregates:
|
if len(field_list) == 1 and self._aggregates and aggregate.lookup in self.aggregates:
|
||||||
# Aggregate is over an annotation
|
# Aggregate is over an annotation
|
||||||
field_name = field_list[0]
|
field_name = field_list[0]
|
||||||
col = field_name
|
col = field_name
|
||||||
@ -1049,7 +1067,7 @@ class Query(object):
|
|||||||
lookup_parts = lookup.split(LOOKUP_SEP)
|
lookup_parts = lookup.split(LOOKUP_SEP)
|
||||||
num_parts = len(lookup_parts)
|
num_parts = len(lookup_parts)
|
||||||
if (len(lookup_parts) > 1 and lookup_parts[-1] in self.query_terms
|
if (len(lookup_parts) > 1 and lookup_parts[-1] in self.query_terms
|
||||||
and lookup not in self.aggregates):
|
and (not self._aggregates or lookup not in self._aggregates)):
|
||||||
# Traverse the lookup query to distinguish related fields from
|
# Traverse the lookup query to distinguish related fields from
|
||||||
# lookup types.
|
# lookup types.
|
||||||
lookup_model = self.model
|
lookup_model = self.model
|
||||||
@ -1108,10 +1126,11 @@ class Query(object):
|
|||||||
value, lookup_type = self.prepare_lookup_value(value, lookup_type, can_reuse)
|
value, lookup_type = self.prepare_lookup_value(value, lookup_type, can_reuse)
|
||||||
|
|
||||||
clause = self.where_class()
|
clause = self.where_class()
|
||||||
for alias, aggregate in self.aggregates.items():
|
if self._aggregates:
|
||||||
if alias in (parts[0], LOOKUP_SEP.join(parts)):
|
for alias, aggregate in self.aggregates.items():
|
||||||
clause.add((aggregate, lookup_type, value), AND)
|
if alias in (parts[0], LOOKUP_SEP.join(parts)):
|
||||||
return clause
|
clause.add((aggregate, lookup_type, value), AND)
|
||||||
|
return clause
|
||||||
|
|
||||||
opts = self.get_meta()
|
opts = self.get_meta()
|
||||||
alias = self.get_initial_alias()
|
alias = self.get_initial_alias()
|
||||||
@ -1170,6 +1189,8 @@ class Query(object):
|
|||||||
Returns whether or not all elements of this q_object need to be put
|
Returns whether or not all elements of this q_object need to be put
|
||||||
together in the HAVING clause.
|
together in the HAVING clause.
|
||||||
"""
|
"""
|
||||||
|
if not self._aggregates:
|
||||||
|
return False
|
||||||
if not isinstance(obj, Node):
|
if not isinstance(obj, Node):
|
||||||
return (refs_aggregate(obj[0].split(LOOKUP_SEP), self.aggregates)
|
return (refs_aggregate(obj[0].split(LOOKUP_SEP), self.aggregates)
|
||||||
or (hasattr(obj[1], 'contains_aggregate')
|
or (hasattr(obj[1], 'contains_aggregate')
|
||||||
@ -1632,7 +1653,7 @@ class Query(object):
|
|||||||
|
|
||||||
# Set only aggregate to be the count column.
|
# Set only aggregate to be the count column.
|
||||||
# Clear out the select cache to reflect the new unmasked aggregates.
|
# Clear out the select cache to reflect the new unmasked aggregates.
|
||||||
self.aggregates = {None: count}
|
self._aggregates = {None: count}
|
||||||
self.set_aggregate_mask(None)
|
self.set_aggregate_mask(None)
|
||||||
self.group_by = None
|
self.group_by = None
|
||||||
|
|
||||||
@ -1781,7 +1802,8 @@ class Query(object):
|
|||||||
self.extra_select_mask = set(names)
|
self.extra_select_mask = set(names)
|
||||||
self._extra_select_cache = None
|
self._extra_select_cache = None
|
||||||
|
|
||||||
def _aggregate_select(self):
|
@property
|
||||||
|
def aggregate_select(self):
|
||||||
"""The OrderedDict of aggregate columns that are not masked, and should
|
"""The OrderedDict of aggregate columns that are not masked, and should
|
||||||
be used in the SELECT clause.
|
be used in the SELECT clause.
|
||||||
|
|
||||||
@ -1789,6 +1811,8 @@ class Query(object):
|
|||||||
"""
|
"""
|
||||||
if self._aggregate_select_cache is not None:
|
if self._aggregate_select_cache is not None:
|
||||||
return self._aggregate_select_cache
|
return self._aggregate_select_cache
|
||||||
|
elif not self._aggregates:
|
||||||
|
return {}
|
||||||
elif self.aggregate_select_mask is not None:
|
elif self.aggregate_select_mask is not None:
|
||||||
self._aggregate_select_cache = OrderedDict(
|
self._aggregate_select_cache = OrderedDict(
|
||||||
(k, v) for k, v in self.aggregates.items()
|
(k, v) for k, v in self.aggregates.items()
|
||||||
@ -1797,11 +1821,13 @@ class Query(object):
|
|||||||
return self._aggregate_select_cache
|
return self._aggregate_select_cache
|
||||||
else:
|
else:
|
||||||
return self.aggregates
|
return self.aggregates
|
||||||
aggregate_select = property(_aggregate_select)
|
|
||||||
|
|
||||||
def _extra_select(self):
|
@property
|
||||||
|
def extra_select(self):
|
||||||
if self._extra_select_cache is not None:
|
if self._extra_select_cache is not None:
|
||||||
return self._extra_select_cache
|
return self._extra_select_cache
|
||||||
|
if not self._extra:
|
||||||
|
return {}
|
||||||
elif self.extra_select_mask is not None:
|
elif self.extra_select_mask is not None:
|
||||||
self._extra_select_cache = OrderedDict(
|
self._extra_select_cache = OrderedDict(
|
||||||
(k, v) for k, v in self.extra.items()
|
(k, v) for k, v in self.extra.items()
|
||||||
@ -1810,7 +1836,6 @@ class Query(object):
|
|||||||
return self._extra_select_cache
|
return self._extra_select_cache
|
||||||
else:
|
else:
|
||||||
return self.extra
|
return self.extra
|
||||||
extra_select = property(_extra_select)
|
|
||||||
|
|
||||||
def trim_start(self, names_with_path):
|
def trim_start(self, names_with_path):
|
||||||
"""
|
"""
|
||||||
|
Loading…
Reference in New Issue
Block a user