From e07609a0d1cac573890380ee32b85d7743632650 Mon Sep 17 00:00:00 2001
From: Mariusz Felisiak <felisiak.mariusz@gmail.com>
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))