summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMariusz Felisiak <felisiak.mariusz@gmail.com>2017-10-09 18:07:03 +0200
committerGitHub <noreply@github.com>2017-10-09 18:07:03 +0200
commit0899d583bdb140910698d00d17f5f1abc8774b07 (patch)
tree78bb4ad64476636f4ac5dad34c1c9338b1f99836
parent81d5320db56f28514c346edfec42559f5da4b343 (diff)
Fixed #28670 -- Added FETCH/OFFSET support on Oracle.
Thanks Tim Graham for the review.
-rw-r--r--django/db/backends/oracle/compiler.py62
-rw-r--r--django/db/backends/oracle/features.py1
-rw-r--r--django/db/backends/oracle/operations.py11
-rw-r--r--django/db/models/sql/compiler.py10
4 files changed, 13 insertions, 71 deletions
diff --git a/django/db/backends/oracle/compiler.py b/django/db/backends/oracle/compiler.py
deleted file mode 100644
index b568e59e9e..0000000000
--- a/django/db/backends/oracle/compiler.py
+++ /dev/null
@@ -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
diff --git a/django/db/backends/oracle/features.py b/django/db/backends/oracle/features.py
index 71421f0df8..cb2fa7d558 100644
--- a/django/db/backends/oracle/features.py
+++ b/django/db/backends/oracle/features.py
@@ -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
diff --git a/django/db/backends/oracle/operations.py b/django/db/backends/oracle/operations.py
index 5c0f6accae..fa29a8925a 100644
--- a/django/db/backends/oracle/operations.py
+++ b/django/db/backends/oracle/operations.py
@@ -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
diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py
index 078a8477af..93ba60638a 100644
--- a/django/db/models/sql/compiler.py
+++ b/django/db/models/sql/compiler.py
@@ -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)