From e07609a0d1cac573890380ee32b85d7743632650 Mon Sep 17 00:00:00 2001 From: Mariusz Felisiak Date: Tue, 22 Jun 2021 06:10:54 +0200 Subject: [PATCH] Refs #32858, Refs #32392 -- Restored using :: shortcut syntax in Cast() on PostgreSQL. This partly reverts commit fdfbc66331292def201c9344e3cd29fbcbcd076a unnecessary since b69b0c3fe871167a0ca01bb439508e335143801f. --- django/db/models/functions/comparison.py | 6 ++++++ tests/db_functions/comparison/test_cast.py | 17 +++++++++++++++++ tests/postgres_tests/test_indexes.py | 2 +- 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/django/db/models/functions/comparison.py b/django/db/models/functions/comparison.py index 2976985db8..8a1c34430b 100644 --- a/django/db/models/functions/comparison.py +++ b/django/db/models/functions/comparison.py @@ -42,6 +42,12 @@ class Cast(Func): template = "JSON_EXTRACT(%(expressions)s, '$')" return self.as_sql(compiler, connection, template=template, **extra_context) + def as_postgresql(self, compiler, connection, **extra_context): + # CAST would be valid too, but the :: shortcut syntax is more readable. + # 'expressions' is wrapped in parentheses in case it's a complex + # expression. + return self.as_sql(compiler, connection, template='(%(expressions)s)::%(db_type)s', **extra_context) + def as_oracle(self, compiler, connection, **extra_context): if self.output_field.get_internal_type() == 'JSONField': # Oracle doesn't support explicit cast to JSON. diff --git a/tests/db_functions/comparison/test_cast.py b/tests/db_functions/comparison/test_cast.py index 687cf77656..ffba8af316 100644 --- a/tests/db_functions/comparison/test_cast.py +++ b/tests/db_functions/comparison/test_cast.py @@ -1,9 +1,11 @@ import datetime import decimal +import unittest from django.db import connection, models from django.db.models.functions import Cast from django.test import TestCase, ignore_warnings, skipUnlessDBFeature +from django.test.utils import CaptureQueriesContext from ..models import Author, DTModel, Fan, FloatModel @@ -125,5 +127,20 @@ class CastTests(TestCase): self.assertIsInstance(cast_float, float) self.assertEqual(cast_float, 0.125) + @unittest.skipUnless(connection.vendor == 'postgresql', 'PostgreSQL test') + def test_expression_wrapped_with_parentheses_on_postgresql(self): + """ + The SQL for the Cast expression is wrapped with parentheses in case + it's a complex expression. + """ + with CaptureQueriesContext(connection) as captured_queries: + list(Author.objects.annotate( + cast_float=Cast(models.Avg('age'), models.FloatField()), + )) + self.assertIn( + '(AVG("db_functions_author"."age"))::double precision', + captured_queries[0]['sql'], + ) + def test_cast_to_text_field(self): self.assertEqual(Author.objects.values_list(Cast('age', models.TextField()), flat=True).get(), '1') diff --git a/tests/postgres_tests/test_indexes.py b/tests/postgres_tests/test_indexes.py index 3578fb68f3..a212de2087 100644 --- a/tests/postgres_tests/test_indexes.py +++ b/tests/postgres_tests/test_indexes.py @@ -305,7 +305,7 @@ class SchemaTests(PostgreSQLTestCase): self.assertIn(index_name, constraints) self.assertIn(constraints[index_name]['type'], GinIndex.suffix) self.assertIs(sql.references_column(table, 'field'), True) - self.assertIn(' AS tsvector', str(sql)) + self.assertIn('::tsvector', str(sql)) with connection.schema_editor() as editor: editor.remove_index(TextFieldModel, index) self.assertNotIn(index_name, self.get_constraints(table))