diff --git a/django/db/backends/oracle/features.py b/django/db/backends/oracle/features.py index 05632bb10e..002e03e2b5 100644 --- a/django/db/backends/oracle/features.py +++ b/django/db/backends/oracle/features.py @@ -37,7 +37,6 @@ class DatabaseFeatures(BaseDatabaseFeatures): requires_literal_defaults = True supports_default_keyword_in_bulk_insert = False closed_cursor_error_class = InterfaceError - bare_select_suffix = " FROM DUAL" # Select for update with limit can be achieved on Oracle, but not with the # current backend. supports_select_for_update_with_limit = False @@ -159,9 +158,10 @@ class DatabaseFeatures(BaseDatabaseFeatures): @cached_property def supports_collation_on_charfield(self): + sql = "SELECT CAST('a' AS VARCHAR2(4001))" + self.bare_select_suffix with self.connection.cursor() as cursor: try: - cursor.execute("SELECT CAST('a' AS VARCHAR2(4001)) FROM dual") + cursor.execute(sql) except DatabaseError as e: if e.args[0].code == 910: return False @@ -183,3 +183,7 @@ class DatabaseFeatures(BaseDatabaseFeatures): @cached_property def supports_aggregation_over_interval_types(self): return self.connection.oracle_version >= (23,) + + @cached_property + def bare_select_suffix(self): + return "" if self.connection.oracle_version >= (23,) else " FROM DUAL" diff --git a/django/db/backends/oracle/operations.py b/django/db/backends/oracle/operations.py index 11043a9b51..9e8172b80a 100644 --- a/django/db/backends/oracle/operations.py +++ b/django/db/backends/oracle/operations.py @@ -54,7 +54,7 @@ BEGIN SELECT NVL(last_number - cache_size, 0) INTO seq_value FROM user_sequences WHERE sequence_name = seq_name; WHILE table_value > seq_value LOOP - EXECUTE IMMEDIATE 'SELECT "'||seq_name||'".nextval FROM DUAL' + EXECUTE IMMEDIATE 'SELECT "'||seq_name||'".nextval%(suffix)s' INTO seq_value; END LOOP; END; @@ -527,6 +527,7 @@ END; "column": column, "table_name": strip_quotes(table), "column_name": strip_quotes(column), + "suffix": self.connection.features.bare_select_suffix, } sql.append(query) return sql @@ -550,6 +551,7 @@ END; "column": column, "table_name": strip_quotes(table), "column_name": strip_quotes(column), + "suffix": self.connection.features.bare_select_suffix, } ) # Only one AutoField is allowed per model, so don't @@ -683,7 +685,8 @@ END; if not query: placeholder = "%s col_%s" % (placeholder, i) select.append(placeholder) - query.append("SELECT %s FROM DUAL" % ", ".join(select)) + suffix = self.connection.features.bare_select_suffix + query.append(f"SELECT %s{suffix}" % ", ".join(select)) # Bulk insert to tables with Oracle identity columns causes Oracle to # add sequence.nextval to it. Sequence.nextval cannot be used with the # UNION operator. To prevent incorrect SQL, move UNION to a subquery. diff --git a/django/db/models/functions/text.py b/django/db/models/functions/text.py index 500fbea194..392061880c 100644 --- a/django/db/models/functions/text.py +++ b/django/db/models/functions/text.py @@ -261,13 +261,14 @@ class Reverse(Transform): def as_oracle(self, compiler, connection, **extra_context): # REVERSE in Oracle is undocumented and doesn't support multi-byte # strings. Use a special subquery instead. + suffix = connection.features.bare_select_suffix sql, params = super().as_sql( compiler, connection, template=( "(SELECT LISTAGG(s) WITHIN GROUP (ORDER BY n DESC) FROM " - "(SELECT LEVEL n, SUBSTR(%(expressions)s, LEVEL, 1) s " - "FROM DUAL CONNECT BY LEVEL <= LENGTH(%(expressions)s)) " + f"(SELECT LEVEL n, SUBSTR(%(expressions)s, LEVEL, 1) s{suffix} " + "CONNECT BY LEVEL <= LENGTH(%(expressions)s)) " "GROUP BY %(expressions)s)" ), **extra_context, diff --git a/tests/backends/oracle/tests.py b/tests/backends/oracle/tests.py index 1ad98a88cf..a4aa26cd2e 100644 --- a/tests/backends/oracle/tests.py +++ b/tests/backends/oracle/tests.py @@ -43,8 +43,9 @@ class Tests(TestCase): An 'almost right' datetime works with configured NLS parameters (#18465). """ + suffix = connection.features.bare_select_suffix with connection.cursor() as cursor: - query = "select 1 from dual where '1936-12-29 00:00' < sysdate" + query = f"SELECT 1{suffix} WHERE '1936-12-29 00:00' < SYSDATE" # The query succeeds without errors - pre #18465 this # wasn't the case. cursor.execute(query)