diff --git a/django/db/backends/oracle/features.py b/django/db/backends/oracle/features.py index 47bdf37efaae..4d7ca7dd1fcc 100644 --- a/django/db/backends/oracle/features.py +++ b/django/db/backends/oracle/features.py @@ -178,6 +178,10 @@ def supports_boolean_expr_in_select_clause(self): def supports_aggregation_over_interval_types(self): return self.connection.oracle_version >= (23,) + @cached_property + def supports_bulk_insert_with_multiple_rows(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 15a1b3335bbb..15edb292b82c 100644 --- a/django/db/backends/oracle/operations.py +++ b/django/db/backends/oracle/operations.py @@ -676,6 +676,24 @@ def bulk_insert_sql(self, fields, placeholder_rows): for field in fields if field ] + if ( + self.connection.features.supports_bulk_insert_with_multiple_rows + # A workaround with UNION of SELECTs is required for models without + # any fields. + and field_placeholders + ): + placeholder_rows_sql = [] + for row in placeholder_rows: + placeholders_row = ( + field_placeholder % placeholder + for field_placeholder, placeholder in zip( + field_placeholders, row, strict=True + ) + ) + placeholder_rows_sql.append(placeholders_row) + return super().bulk_insert_sql(fields, placeholder_rows_sql) + # Oracle < 23c doesn't support inserting multiple rows in a single + # statement, use UNION of SELECTs as a workaround. query = [] for row in placeholder_rows: select = []