Fixed #28670 -- Added FETCH/OFFSET support on Oracle.
Thanks Tim Graham for the review.
This commit is contained in:
parent
81d5320db5
commit
0899d583bd
|
@ -1,62 +0,0 @@
|
|||
from django.db import NotSupportedError
|
||||
from django.db.models.sql import compiler
|
||||
|
||||
|
||||
class SQLCompiler(compiler.SQLCompiler):
|
||||
def as_sql(self, with_limits=True, with_col_aliases=False):
|
||||
"""
|
||||
Create the SQL for this query. Return the SQL string and list
|
||||
of parameters. This is overridden from the original Query class
|
||||
to handle the additional SQL Oracle requires to emulate LIMIT
|
||||
and OFFSET.
|
||||
|
||||
If 'with_limits' is False, any limit/offset information is not
|
||||
included in the query.
|
||||
"""
|
||||
# The `do_offset` flag indicates whether we need to construct
|
||||
# the SQL needed to use limit/offset with Oracle.
|
||||
do_offset = with_limits and (self.query.high_mark is not None or self.query.low_mark)
|
||||
if not do_offset:
|
||||
sql, params = super().as_sql(with_limits=False, with_col_aliases=with_col_aliases)
|
||||
elif not self.connection.features.supports_select_for_update_with_limit and self.query.select_for_update:
|
||||
raise NotSupportedError(
|
||||
'LIMIT/OFFSET is not supported with select_for_update on this '
|
||||
'database backend.'
|
||||
)
|
||||
else:
|
||||
sql, params = super().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.
|
||||
high_where = ''
|
||||
if self.query.high_mark is not None:
|
||||
high_where = 'WHERE ROWNUM <= %d' % (self.query.high_mark,)
|
||||
|
||||
if self.query.low_mark:
|
||||
sql = (
|
||||
'SELECT * FROM (SELECT "_SUB".*, ROWNUM AS "_RN" FROM (%s) '
|
||||
'"_SUB" %s) WHERE "_RN" > %d' % (sql, high_where, self.query.low_mark)
|
||||
)
|
||||
else:
|
||||
# Simplify the query to support subqueries if there's no offset.
|
||||
sql = (
|
||||
'SELECT * FROM (SELECT "_SUB".* FROM (%s) "_SUB" %s)' % (sql, high_where)
|
||||
)
|
||||
|
||||
return sql, params
|
||||
|
||||
|
||||
class SQLInsertCompiler(compiler.SQLInsertCompiler, SQLCompiler):
|
||||
pass
|
||||
|
||||
|
||||
class SQLDeleteCompiler(compiler.SQLDeleteCompiler, SQLCompiler):
|
||||
pass
|
||||
|
||||
|
||||
class SQLUpdateCompiler(compiler.SQLUpdateCompiler, SQLCompiler):
|
||||
pass
|
||||
|
||||
|
||||
class SQLAggregateCompiler(compiler.SQLAggregateCompiler, SQLCompiler):
|
||||
pass
|
|
@ -12,7 +12,6 @@ class DatabaseFeatures(BaseDatabaseFeatures):
|
|||
has_select_for_update_of = True
|
||||
select_for_update_of_column = True
|
||||
can_return_id_from_insert = True
|
||||
allow_sliced_subqueries = False
|
||||
can_introspect_autofield = True
|
||||
supports_subqueries_in_group_by = False
|
||||
supports_transactions = True
|
||||
|
|
|
@ -13,8 +13,6 @@ from .utils import BulkInsertMapper, InsertIdVar, Oracle_datetime
|
|||
|
||||
|
||||
class DatabaseOperations(BaseDatabaseOperations):
|
||||
compiler_module = "django.db.backends.oracle.compiler"
|
||||
|
||||
# Oracle uses NUMBER(11) and NUMBER(19) for integer fields.
|
||||
integer_field_ranges = {
|
||||
'SmallIntegerField': (-99999999999, 99999999999),
|
||||
|
@ -233,8 +231,15 @@ END;
|
|||
else:
|
||||
return "%s"
|
||||
|
||||
def no_limit_value(self):
|
||||
return None
|
||||
|
||||
def limit_offset_sql(self, low_mark, high_mark):
|
||||
return ''
|
||||
fetch, offset = self._get_limit_offset_params(low_mark, high_mark)
|
||||
return '%s%s' % (
|
||||
(' OFFSET %d ROWS' % offset) if offset else '',
|
||||
(' FETCH FIRST %d ROWS ONLY' % fetch) if fetch else '',
|
||||
)
|
||||
|
||||
def last_executed_query(self, cursor, sql, params):
|
||||
# https://cx-oracle.readthedocs.io/en/latest/cursor.html#Cursor.statement
|
||||
|
|
|
@ -439,6 +439,8 @@ class SQLCompiler:
|
|||
try:
|
||||
extra_select, order_by, group_by = self.pre_sql_setup()
|
||||
for_update_part = None
|
||||
# Is a LIMIT/OFFSET clause needed?
|
||||
with_limit_offset = with_limits and (self.query.high_mark is not None or self.query.low_mark)
|
||||
combinator = self.query.combinator
|
||||
features = self.connection.features
|
||||
if combinator:
|
||||
|
@ -479,7 +481,7 @@ class SQLCompiler:
|
|||
if self.connection.get_autocommit():
|
||||
raise TransactionManagementError('select_for_update cannot be used outside of a transaction.')
|
||||
|
||||
if with_limits and not self.connection.features.supports_select_for_update_with_limit:
|
||||
if with_limit_offset and not self.connection.features.supports_select_for_update_with_limit:
|
||||
raise NotSupportedError(
|
||||
'LIMIT/OFFSET is not supported with '
|
||||
'select_for_update on this database backend.'
|
||||
|
@ -531,10 +533,8 @@ class SQLCompiler:
|
|||
params.extend(o_params)
|
||||
result.append('ORDER BY %s' % ', '.join(ordering))
|
||||
|
||||
if with_limits:
|
||||
limit_offset_sql = self.connection.ops.limit_offset_sql(self.query.low_mark, self.query.high_mark)
|
||||
if limit_offset_sql:
|
||||
result.append(limit_offset_sql)
|
||||
if with_limit_offset:
|
||||
result.append(self.connection.ops.limit_offset_sql(self.query.low_mark, self.query.high_mark))
|
||||
|
||||
if for_update_part and not self.connection.features.for_update_after_from:
|
||||
result.append(for_update_part)
|
||||
|
|
Loading…
Reference in New Issue