From 27557a7a99ab1ad032c699dc01e114a5e6504b0a Mon Sep 17 00:00:00 2001 From: hayashi Date: Tue, 16 Jan 2018 03:11:20 +0900 Subject: [PATCH] Fixed #28857 -- Fixed invalid SQL when using Cast with complex expressions on PostgreSQL. --- django/db/models/functions/comparison.py | 4 +++- tests/db_functions/test_cast.py | 18 ++++++++++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/django/db/models/functions/comparison.py b/django/db/models/functions/comparison.py index 8bf4fdc8d7..69e9d09abb 100644 --- a/django/db/models/functions/comparison.py +++ b/django/db/models/functions/comparison.py @@ -21,7 +21,9 @@ class Cast(Func): def as_postgresql(self, compiler, connection): # CAST would be valid too, but the :: shortcut syntax is more readable. - return self.as_sql(compiler, connection, template='%(expressions)s::%(db_type)s') + # 'expressions' is wrapped in parentheses in case it's a complex + # expression. + return self.as_sql(compiler, connection, template='(%(expressions)s)::%(db_type)s') class Coalesce(Func): diff --git a/tests/db_functions/test_cast.py b/tests/db_functions/test_cast.py index 36220cf640..12932d95be 100644 --- a/tests/db_functions/test_cast.py +++ b/tests/db_functions/test_cast.py @@ -1,10 +1,14 @@ import datetime import decimal +import unittest -from django.db import models +from django.db import connection, models +from django.db.models import Avg from django.db.models.expressions import Value from django.db.models.functions import Cast -from django.test import TestCase, ignore_warnings, skipUnlessDBFeature +from django.test import ( + TestCase, ignore_warnings, override_settings, skipUnlessDBFeature, +) from .models import Author @@ -60,3 +64,13 @@ class CastTests(TestCase): cast_float = numbers.get().cast_float self.assertIsInstance(cast_float, float) self.assertEqual(cast_float, 0.125) + + @unittest.skipUnless(connection.vendor == 'postgresql', 'PostgreSQL test') + @override_settings(DEBUG=True) + 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. + """ + list(Author.objects.annotate(cast_float=Cast(Avg('age'), models.FloatField()))) + self.assertIn('(AVG("db_functions_author"."age"))::double precision', connection.queries[-1]['sql'])