Fixed Oracle backend limit/offset SQL to again use extra_select, properly this time. This cleans up a test case failure, and hopefully gets contrib.gis working again.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@8471 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Matt Boersma 2008-08-22 20:26:20 +00:00
parent 1b8b9686fa
commit 6e36ce1429
1 changed files with 37 additions and 28 deletions

View File

@ -82,55 +82,64 @@ def query_class(QueryClass, Database):
""" """
Creates the SQL for this query. Returns the SQL string and list Creates the SQL for this query. Returns the SQL string and list
of parameters. This is overriden from the original Query class of parameters. This is overriden from the original Query class
to accommodate Oracle's limit/offset SQL. to handle the additional SQL Oracle requires to emulate LIMIT
and OFFSET.
If 'with_limits' is False, any limit/offset information is not If 'with_limits' is False, any limit/offset information is not
included in the query. included in the query.
""" """
# The `do_offset` flag indicates whether we need to construct # The `do_offset` flag indicates whether we need to construct
# the SQL needed to use limit/offset w/Oracle. # the SQL needed to use limit/offset with Oracle.
do_offset = with_limits and (self.high_mark is not None do_offset = with_limits and (self.high_mark is not None
or self.low_mark) or self.low_mark)
sql, params = super(OracleQuery, self).as_sql(with_limits=False, if not do_offset:
with_col_aliases=with_col_aliases or do_offset) sql, params = super(OracleQuery, self).as_sql(with_limits=False,
if do_offset: with_col_aliases=with_col_aliases)
# Get the "ORDER BY" SQL for the ROW_NUMBER() result. 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() ordering = self.get_ordering()
# Oracle's ROW_NUMBER() function requires an ORDER BY clause.
if ordering: if ordering:
rn_orderby = ', '.join(ordering) rn_orderby = ', '.join(ordering)
else: else:
# Oracle's ROW_NUMBER() function always requires an # Create a default ORDER BY since none was specified.
# order-by clause. So we need to define a default
# order-by, since none was provided.
qn = self.quote_name_unless_alias qn = self.quote_name_unless_alias
opts = self.model._meta opts = self.model._meta
rn_orderby = '%s.%s' % (qn(opts.db_table), rn_orderby = '%s.%s' % (qn(opts.db_table),
qn(opts.fields[0].db_column or opts.fields[0].column)) qn(opts.fields[0].db_column or opts.fields[0].column))
# Collect all the selected column names or aliases. # Ensure the base query SELECTs our special "_RN" column
outer_cols = [] self.extra_select['_RN'] = ('ROW_NUMBER() OVER (ORDER BY %s)'
for col in self.get_columns(True): % rn_orderby, '')
if ' AS ' in col: sql, params = super(OracleQuery, self).as_sql(with_limits=False,
outer_cols.append(col.split(' AS ', 1)[1]) with_col_aliases=True)
else:
outer_cols.append(col.rsplit('.', 1)[1])
# Rewrite the original SQL query to select ROW_NUMBER() and involve # Wrap the base query in an outer SELECT * with boundaries on
# it in the WHERE clause, then wrap everything in an outer SELECT # the "_RN" column. This is the canonical way to emulate LIMIT
# statement that omits the "rn" column. This is the canonical way # and OFFSET on Oracle.
# to emulate LIMIT and OFFSET on Oracle. sql = 'SELECT * FROM (%s) WHERE "_RN" > %d' % (sql, self.low_mark)
sql = 'SELECT ROW_NUMBER() OVER (ORDER BY %s) rn, %s' % (rn_orderby, sql[7:])
result = ['SELECT %s FROM (%s)' % (', '.join(outer_cols), sql)]
# Place WHERE condition on `rn` for the desired range.
result.append('WHERE rn > %d' % self.low_mark)
if self.high_mark is not None: if self.high_mark is not None:
result.append('AND rn <= %d' % self.high_mark) sql = '%s AND "_RN" <= %d' % (sql, self.high_mark)
sql = ' '.join(result)
return sql, params 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 _classes[QueryClass] = OracleQuery
return OracleQuery return OracleQuery