From f59a2b730685fc62c5cb44101f54faf8921d9bc7 Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Mon, 18 May 2020 02:12:20 -0400 Subject: [PATCH] Refs #12990 -- Added DatabaseFeatures.has_json_operators. CockroachDB also has them. --- django/db/backends/base/features.py | 2 ++ django/db/backends/postgresql/features.py | 1 + tests/model_fields/test_jsonfield.py | 8 +++++--- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/django/db/backends/base/features.py b/django/db/backends/base/features.py index 67bfe77e7c..35ce3ba299 100644 --- a/django/db/backends/base/features.py +++ b/django/db/backends/base/features.py @@ -309,6 +309,8 @@ class BaseDatabaseFeatures: supports_primitives_in_json_field = True # Is there a true datatype for JSON? has_native_json_field = False + # Does the backend use PostgreSQL-style JSON operators like '->'? + has_json_operators = False def __init__(self, connection): self.connection = connection diff --git a/django/db/backends/postgresql/features.py b/django/db/backends/postgresql/features.py index 80cee8e8da..a4ba0b99fc 100644 --- a/django/db/backends/postgresql/features.py +++ b/django/db/backends/postgresql/features.py @@ -58,6 +58,7 @@ class DatabaseFeatures(BaseDatabaseFeatures): supported_explain_formats = {'JSON', 'TEXT', 'XML', 'YAML'} validates_explain_options = False # A query will error on invalid options. supports_deferrable_unique_constraints = True + has_json_operators = True @cached_property def is_postgresql_10(self): diff --git a/tests/model_fields/test_jsonfield.py b/tests/model_fields/test_jsonfield.py index 464cf163d4..1e92b34791 100644 --- a/tests/model_fields/test_jsonfield.py +++ b/tests/model_fields/test_jsonfield.py @@ -17,7 +17,9 @@ from django.db.models.fields.json import ( KeyTransformTextLookupMixin, ) from django.db.models.functions import Cast -from django.test import SimpleTestCase, TestCase, skipUnlessDBFeature +from django.test import ( + SimpleTestCase, TestCase, skipIfDBFeature, skipUnlessDBFeature, +) from django.test.utils import CaptureQueriesContext from .models import CustomJSONDecoder, JSONModel, NullableJSONModel @@ -607,7 +609,7 @@ class TestQuerying(TestCase): def test_key_iregex(self): self.assertIs(NullableJSONModel.objects.filter(value__foo__iregex=r'^bAr$').exists(), True) - @skipUnless(connection.vendor == 'postgresql', 'kwargs are crafted for PostgreSQL.') + @skipUnlessDBFeature('has_json_operators') def test_key_sql_injection(self): with CaptureQueriesContext(connection) as queries: self.assertIs( @@ -621,7 +623,7 @@ class TestQuerying(TestCase): queries[0]['sql'], ) - @skipIf(connection.vendor == 'postgresql', 'PostgreSQL uses operators not functions.') + @skipIfDBFeature('has_json_operators') def test_key_sql_injection_escape(self): query = str(JSONModel.objects.filter(**{ """value__test") = '"a"' OR 1 = 1 OR ("d""": 'x',