diff --git a/django/db/backends/oracle/features.py b/django/db/backends/oracle/features.py index c07d9f1ed0..688a2a3d0c 100644 --- a/django/db/backends/oracle/features.py +++ b/django/db/backends/oracle/features.py @@ -76,7 +76,6 @@ class DatabaseFeatures(BaseDatabaseFeatures): requires_compound_order_by_subquery = True allows_multiple_constraints_on_same_fields = False supports_json_field_contains = False - supports_json_negative_indexing = False supports_collation_on_textfield = False supports_on_delete_db_default = False test_now_utc_template = "CURRENT_TIMESTAMP AT TIME ZONE 'UTC'" @@ -90,6 +89,10 @@ class DatabaseFeatures(BaseDatabaseFeatures): "INSERT INTO {} VALUES (DEFAULT, DEFAULT, DEFAULT)" ) + @cached_property + def supports_json_negative_indexing(self): + return self.connection.oracle_version >= (21,) + @cached_property def django_test_skips(self): skips = { diff --git a/django/db/backends/oracle/operations.py b/django/db/backends/oracle/operations.py index 1da4fe6127..b3e28b102f 100644 --- a/django/db/backends/oracle/operations.py +++ b/django/db/backends/oracle/operations.py @@ -729,3 +729,8 @@ END; if isinstance(expression, RawSQL) and expression.conditional: return True return False + + def format_json_path_numeric_index(self, num): + if num < 0: + return "[last-%s]" % abs(num + 1) # Indexing is zero-based. + return super().format_json_path_numeric_index(num) diff --git a/docs/releases/6.1.txt b/docs/releases/6.1.txt index 036fee53cf..52f53de212 100644 --- a/docs/releases/6.1.txt +++ b/docs/releases/6.1.txt @@ -372,6 +372,9 @@ Miscellaneous used as the top-level value. :lookup:`Key and index lookups ` are unaffected by this deprecation. +* Support for negative JSON array indices was added for Oracle versions 21c + and higher. + Features removed in 6.1 ======================= diff --git a/docs/topics/db/queries.txt b/docs/topics/db/queries.txt index b3b6ec125d..e57a205c29 100644 --- a/docs/topics/db/queries.txt +++ b/docs/topics/db/queries.txt @@ -1182,7 +1182,7 @@ directly, but you can still use dictionary unpacking to use it in a query: >>> Dog.objects.filter(**{"data__owner__other_pets__-1__name": "Fishy"}) ]> -.. admonition:: MySQL, MariaDB, and Oracle +.. admonition:: MySQL, MariaDB, and Oracle < 21c Negative JSON array indices are not supported. @@ -1190,6 +1190,11 @@ directly, but you can still use dictionary unpacking to use it in a query: SQLite support for negative JSON array indices was added. +.. versionchanged:: 6.1 + + Support for negative JSON array indices was added for Oracle versions 21c + and higher. + If the key you wish to query by clashes with the name of another lookup, use the :lookup:`contains ` lookup instead.