summaryrefslogtreecommitdiff
path: root/django/db/models/sql/query.py
diff options
context:
space:
mode:
Diffstat (limited to 'django/db/models/sql/query.py')
-rw-r--r--django/db/models/sql/query.py43
1 files changed, 33 insertions, 10 deletions
diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py
index 1c22593293..2dba2bdb3c 100644
--- a/django/db/models/sql/query.py
+++ b/django/db/models/sql/query.py
@@ -52,6 +52,7 @@ class Query(object):
self.default_cols = True
self.default_ordering = True
self.standard_ordering = True
+ self.ordering_aliases = []
self.start_meta = None
# SQL-related attributes
@@ -139,6 +140,7 @@ class Query(object):
obj.default_cols = self.default_cols
obj.default_ordering = self.default_ordering
obj.standard_ordering = self.standard_ordering
+ obj.ordering_aliases = []
obj.start_meta = self.start_meta
obj.select = self.select[:]
obj.tables = self.tables[:]
@@ -215,16 +217,18 @@ class Query(object):
self.pre_sql_setup()
out_cols = self.get_columns()
ordering = self.get_ordering()
+
# This must come after 'select' and 'ordering' -- see docstring of
# get_from_clause() for details.
from_, f_params = self.get_from_clause()
+
where, w_params = self.where.as_sql(qn=self.quote_name_unless_alias)
params = list(self.extra_select_params)
result = ['SELECT']
if self.distinct:
result.append('DISTINCT')
- result.append(', '.join(out_cols))
+ result.append(', '.join(out_cols + self.ordering_aliases))
result.append('FROM')
result.extend(from_)
@@ -465,15 +469,13 @@ class Query(object):
def get_ordering(self):
"""
- Returns a tuple representing the SQL elements in the "order by" clause.
+ Returns list representing the SQL elements in the "order by" clause.
+ Also sets the ordering_aliases attribute on this instance to a list of
+ extra aliases needed in the select.
Determining the ordering SQL can change the tables we need to include,
so this should be run *before* get_from_clause().
"""
- # FIXME: It's an SQL-92 requirement that all ordering columns appear as
- # output columns in the query (in the select statement) or be ordinals.
- # We don't enforce that here, but we should (by adding to the select
- # columns), for portability.
if self.extra_order_by:
ordering = self.extra_order_by
elif not self.default_ordering:
@@ -485,6 +487,7 @@ class Query(object):
distinct = self.distinct
select_aliases = self._select_aliases
result = []
+ ordering_aliases = []
if self.standard_ordering:
asc, desc = ORDER_DIR['ASC']
else:
@@ -515,13 +518,16 @@ class Query(object):
for table, col, order in self.find_ordering_name(field,
self.model._meta, default_order=asc):
elt = '%s.%s' % (qn(table), qn2(col))
- if not distinct or elt in select_aliases:
- result.append('%s %s' % (elt, order))
+ if distinct and elt not in select_aliases:
+ ordering_aliases.append(elt)
+ result.append('%s %s' % (elt, order))
else:
col, order = get_order_dir(field, asc)
elt = qn(col)
- if not distinct or elt in select_aliases:
- result.append('%s %s' % (elt, order))
+ if distinct and elt not in select_aliases:
+ ordering_aliases.append(elt)
+ result.append('%s %s' % (elt, order))
+ self.ordering_aliases = ordering_aliases
return result
def find_ordering_name(self, name, opts, alias=None, default_order='ASC',
@@ -1379,8 +1385,14 @@ class Query(object):
if not result_type:
return cursor
if result_type == SINGLE:
+ if self.ordering_aliases:
+ return cursor.fetchone()[:-len(results.ordering_aliases)]
return cursor.fetchone()
+
# The MULTI case.
+ if self.ordering_aliases:
+ return order_modified_iter(cursor, len(self.ordering_aliases),
+ self.connection.features.empty_fetchmany_value)
return iter((lambda: cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE)),
self.connection.features.empty_fetchmany_value)
@@ -1408,6 +1420,17 @@ def empty_iter():
"""
yield iter([]).next()
+def order_modified_iter(cursor, trim, sentinel):
+ """
+ Yields blocks of rows from a cursor. We use this iterator in the special
+ case when extra output columns have been added to support ordering
+ requirements. We must trim those extra columns before anything else can use
+ the results, since they're only needed to make the SQL valid.
+ """
+ for rows in iter((lambda: cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE)),
+ sentinel):
+ yield [r[:-trim] for r in rows]
+
def setup_join_cache(sender):
"""
The information needed to join between model fields is something that is