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
|
has_select_for_update_of = True
|
||||||
select_for_update_of_column = True
|
select_for_update_of_column = True
|
||||||
can_return_id_from_insert = True
|
can_return_id_from_insert = True
|
||||||
allow_sliced_subqueries = False
|
|
||||||
can_introspect_autofield = True
|
can_introspect_autofield = True
|
||||||
supports_subqueries_in_group_by = False
|
supports_subqueries_in_group_by = False
|
||||||
supports_transactions = True
|
supports_transactions = True
|
||||||
|
|
|
@ -13,8 +13,6 @@ from .utils import BulkInsertMapper, InsertIdVar, Oracle_datetime
|
||||||
|
|
||||||
|
|
||||||
class DatabaseOperations(BaseDatabaseOperations):
|
class DatabaseOperations(BaseDatabaseOperations):
|
||||||
compiler_module = "django.db.backends.oracle.compiler"
|
|
||||||
|
|
||||||
# Oracle uses NUMBER(11) and NUMBER(19) for integer fields.
|
# Oracle uses NUMBER(11) and NUMBER(19) for integer fields.
|
||||||
integer_field_ranges = {
|
integer_field_ranges = {
|
||||||
'SmallIntegerField': (-99999999999, 99999999999),
|
'SmallIntegerField': (-99999999999, 99999999999),
|
||||||
|
@ -233,8 +231,15 @@ END;
|
||||||
else:
|
else:
|
||||||
return "%s"
|
return "%s"
|
||||||
|
|
||||||
|
def no_limit_value(self):
|
||||||
|
return None
|
||||||
|
|
||||||
def limit_offset_sql(self, low_mark, high_mark):
|
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):
|
def last_executed_query(self, cursor, sql, params):
|
||||||
# https://cx-oracle.readthedocs.io/en/latest/cursor.html#Cursor.statement
|
# https://cx-oracle.readthedocs.io/en/latest/cursor.html#Cursor.statement
|
||||||
|
|
|
@ -439,6 +439,8 @@ class SQLCompiler:
|
||||||
try:
|
try:
|
||||||
extra_select, order_by, group_by = self.pre_sql_setup()
|
extra_select, order_by, group_by = self.pre_sql_setup()
|
||||||
for_update_part = None
|
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
|
combinator = self.query.combinator
|
||||||
features = self.connection.features
|
features = self.connection.features
|
||||||
if combinator:
|
if combinator:
|
||||||
|
@ -479,7 +481,7 @@ class SQLCompiler:
|
||||||
if self.connection.get_autocommit():
|
if self.connection.get_autocommit():
|
||||||
raise TransactionManagementError('select_for_update cannot be used outside of a transaction.')
|
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(
|
raise NotSupportedError(
|
||||||
'LIMIT/OFFSET is not supported with '
|
'LIMIT/OFFSET is not supported with '
|
||||||
'select_for_update on this database backend.'
|
'select_for_update on this database backend.'
|
||||||
|
@ -531,10 +533,8 @@ class SQLCompiler:
|
||||||
params.extend(o_params)
|
params.extend(o_params)
|
||||||
result.append('ORDER BY %s' % ', '.join(ordering))
|
result.append('ORDER BY %s' % ', '.join(ordering))
|
||||||
|
|
||||||
if with_limits:
|
if with_limit_offset:
|
||||||
limit_offset_sql = self.connection.ops.limit_offset_sql(self.query.low_mark, self.query.high_mark)
|
result.append(self.connection.ops.limit_offset_sql(self.query.low_mark, self.query.high_mark))
|
||||||
if limit_offset_sql:
|
|
||||||
result.append(limit_offset_sql)
|
|
||||||
|
|
||||||
if for_update_part and not self.connection.features.for_update_after_from:
|
if for_update_part and not self.connection.features.for_update_after_from:
|
||||||
result.append(for_update_part)
|
result.append(for_update_part)
|
||||||
|
|
Loading…
Reference in New Issue