Nick Pope 2023-12-12 16:07:31 +00:00 committed by Mariusz Felisiak
parent a816efe238
commit c72001644f
4 changed files with 16 additions and 7 deletions

View File

@ -37,7 +37,6 @@ class DatabaseFeatures(BaseDatabaseFeatures):
requires_literal_defaults = True requires_literal_defaults = True
supports_default_keyword_in_bulk_insert = False supports_default_keyword_in_bulk_insert = False
closed_cursor_error_class = InterfaceError closed_cursor_error_class = InterfaceError
bare_select_suffix = " FROM DUAL"
# Select for update with limit can be achieved on Oracle, but not with the # Select for update with limit can be achieved on Oracle, but not with the
# current backend. # current backend.
supports_select_for_update_with_limit = False supports_select_for_update_with_limit = False
@ -159,9 +158,10 @@ class DatabaseFeatures(BaseDatabaseFeatures):
@cached_property @cached_property
def supports_collation_on_charfield(self): def supports_collation_on_charfield(self):
sql = "SELECT CAST('a' AS VARCHAR2(4001))" + self.bare_select_suffix
with self.connection.cursor() as cursor: with self.connection.cursor() as cursor:
try: try:
cursor.execute("SELECT CAST('a' AS VARCHAR2(4001)) FROM dual") cursor.execute(sql)
except DatabaseError as e: except DatabaseError as e:
if e.args[0].code == 910: if e.args[0].code == 910:
return False return False
@ -183,3 +183,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
@cached_property @cached_property
def supports_aggregation_over_interval_types(self): def supports_aggregation_over_interval_types(self):
return self.connection.oracle_version >= (23,) return self.connection.oracle_version >= (23,)
@cached_property
def bare_select_suffix(self):
return "" if self.connection.oracle_version >= (23,) else " FROM DUAL"

View File

@ -54,7 +54,7 @@ BEGIN
SELECT NVL(last_number - cache_size, 0) INTO seq_value FROM user_sequences SELECT NVL(last_number - cache_size, 0) INTO seq_value FROM user_sequences
WHERE sequence_name = seq_name; WHERE sequence_name = seq_name;
WHILE table_value > seq_value LOOP 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; INTO seq_value;
END LOOP; END LOOP;
END; END;
@ -527,6 +527,7 @@ END;
"column": column, "column": column,
"table_name": strip_quotes(table), "table_name": strip_quotes(table),
"column_name": strip_quotes(column), "column_name": strip_quotes(column),
"suffix": self.connection.features.bare_select_suffix,
} }
sql.append(query) sql.append(query)
return sql return sql
@ -550,6 +551,7 @@ END;
"column": column, "column": column,
"table_name": strip_quotes(table), "table_name": strip_quotes(table),
"column_name": strip_quotes(column), "column_name": strip_quotes(column),
"suffix": self.connection.features.bare_select_suffix,
} }
) )
# Only one AutoField is allowed per model, so don't # Only one AutoField is allowed per model, so don't
@ -683,7 +685,8 @@ END;
if not query: if not query:
placeholder = "%s col_%s" % (placeholder, i) placeholder = "%s col_%s" % (placeholder, i)
select.append(placeholder) 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 # Bulk insert to tables with Oracle identity columns causes Oracle to
# add sequence.nextval to it. Sequence.nextval cannot be used with the # add sequence.nextval to it. Sequence.nextval cannot be used with the
# UNION operator. To prevent incorrect SQL, move UNION to a subquery. # UNION operator. To prevent incorrect SQL, move UNION to a subquery.

View File

@ -261,13 +261,14 @@ class Reverse(Transform):
def as_oracle(self, compiler, connection, **extra_context): def as_oracle(self, compiler, connection, **extra_context):
# REVERSE in Oracle is undocumented and doesn't support multi-byte # REVERSE in Oracle is undocumented and doesn't support multi-byte
# strings. Use a special subquery instead. # strings. Use a special subquery instead.
suffix = connection.features.bare_select_suffix
sql, params = super().as_sql( sql, params = super().as_sql(
compiler, compiler,
connection, connection,
template=( template=(
"(SELECT LISTAGG(s) WITHIN GROUP (ORDER BY n DESC) FROM " "(SELECT LISTAGG(s) WITHIN GROUP (ORDER BY n DESC) FROM "
"(SELECT LEVEL n, SUBSTR(%(expressions)s, LEVEL, 1) s " f"(SELECT LEVEL n, SUBSTR(%(expressions)s, LEVEL, 1) s{suffix} "
"FROM DUAL CONNECT BY LEVEL <= LENGTH(%(expressions)s)) " "CONNECT BY LEVEL <= LENGTH(%(expressions)s)) "
"GROUP BY %(expressions)s)" "GROUP BY %(expressions)s)"
), ),
**extra_context, **extra_context,

View File

@ -43,8 +43,9 @@ class Tests(TestCase):
An 'almost right' datetime works with configured NLS parameters An 'almost right' datetime works with configured NLS parameters
(#18465). (#18465).
""" """
suffix = connection.features.bare_select_suffix
with connection.cursor() as cursor: 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 # The query succeeds without errors - pre #18465 this
# wasn't the case. # wasn't the case.
cursor.execute(query) cursor.execute(query)