[1.0.X] Fixed .distinct() not working with slicing in Oracle, due to the

row numbers necessarily being distinct.

Backport of r9221 from trunk (since Ian doesn't have commit privileges at the
moment).


git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.0.X@9228 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Malcolm Tredinnick 2008-10-11 00:26:11 +00:00
parent 6cfbd52138
commit 909371e5e5
2 changed files with 17 additions and 35 deletions

View File

@ -26,8 +26,16 @@ def query_class(QueryClass, Database):
class OracleQuery(QueryClass):
def resolve_columns(self, row, fields=()):
index_start = len(self.extra_select.keys())
values = [self.convert_values(v, None) for v in row[:index_start]]
# If this query has limit/offset information, then we expect the
# first column to be an extra "_RN" column that we need to throw
# away.
if self.high_mark is not None or self.low_mark:
rn_offset = 1
else:
rn_offset = 0
index_start = rn_offset + len(self.extra_select.keys())
values = [self.convert_values(v, None)
for v in row[rn_offset:index_start]]
for value, field in map(None, row[index_start:], fields):
values.append(self.convert_values(value, field))
return values
@ -97,49 +105,17 @@ def query_class(QueryClass, Database):
sql, params = super(OracleQuery, self).as_sql(with_limits=False,
with_col_aliases=with_col_aliases)
else:
# `get_columns` needs to be called before `get_ordering` to
# populate `_select_alias`.
self.pre_sql_setup()
self.get_columns()
ordering = self.get_ordering()
# Oracle's ROW_NUMBER() function requires an ORDER BY clause.
if ordering:
rn_orderby = ', '.join(ordering)
else:
# Create a default ORDER BY since none was specified.
qn = self.quote_name_unless_alias
opts = self.model._meta
rn_orderby = '%s.%s' % (qn(opts.db_table),
qn(opts.fields[0].db_column or opts.fields[0].column))
# Ensure the base query SELECTs our special "_RN" column
self.extra_select['_RN'] = ('ROW_NUMBER() OVER (ORDER BY %s)'
% rn_orderby, '')
sql, params = super(OracleQuery, self).as_sql(with_limits=False,
with_col_aliases=True)
# Wrap the base query in an outer SELECT * with boundaries on
# the "_RN" column. This is the canonical way to emulate LIMIT
# and OFFSET on Oracle.
sql = 'SELECT * FROM (%s) WHERE "_RN" > %d' % (sql, self.low_mark)
sql = 'SELECT * FROM (SELECT ROW_NUMBER() OVER (ORDER BY 1) AS "_RN", "_SUB".* FROM (%s) "_SUB") WHERE "_RN" > %d' % (sql, self.low_mark)
if self.high_mark is not None:
sql = '%s AND "_RN" <= %d' % (sql, self.high_mark)
return sql, params
def set_limits(self, low=None, high=None):
super(OracleQuery, self).set_limits(low, high)
# We need to select the row number for the LIMIT/OFFSET sql.
# A placeholder is added to extra_select now, because as_sql is
# too late to be modifying extra_select. However, the actual sql
# depends on the ordering, so that is generated in as_sql.
self.extra_select['_RN'] = ('1', '')
def clear_limits(self):
super(OracleQuery, self).clear_limits()
if '_RN' in self.extra_select:
del self.extra_select['_RN']
_classes[QueryClass] = OracleQuery
return OracleQuery

View File

@ -334,6 +334,12 @@ Bug #4464
>>> Item.objects.filter(tags__in=[t1, t2]).filter(tags=t3)
[<Item: two>]
Make sure .distinct() works with slicing (this was broken in Oracle).
>>> Item.objects.filter(tags__in=[t1, t2]).order_by('name')[:3]
[<Item: one>, <Item: one>, <Item: two>]
>>> Item.objects.filter(tags__in=[t1, t2]).distinct().order_by('name')[:3]
[<Item: one>, <Item: two>]
Bug #2080, #3592
>>> Author.objects.filter(item__name='one') | Author.objects.filter(name='a3')
[<Author: a1>, <Author: a3>]