Fixed #32392 -- Fixed ExclusionConstraint crash with Cast() in expressions.

This commit is contained in:
Tilman Koschnick 2021-01-28 17:50:54 +01:00 committed by Mariusz Felisiak
parent 135c800fe6
commit fdfbc66331
4 changed files with 15 additions and 23 deletions

View File

@ -42,12 +42,6 @@ class Cast(Func):
template = "JSON_EXTRACT(%(expressions)s, '$')" template = "JSON_EXTRACT(%(expressions)s, '$')"
return self.as_sql(compiler, connection, template=template, **extra_context) 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): def as_oracle(self, compiler, connection, **extra_context):
if self.output_field.get_internal_type() == 'JSONField': if self.output_field.get_internal_type() == 'JSONField':
# Oracle doesn't support explicit cast to JSON. # Oracle doesn't support explicit cast to JSON.

View File

@ -1,12 +1,9 @@
import datetime import datetime
import decimal import decimal
import unittest
from django.db import connection, models from django.db import connection, models
from django.db.models.functions import Cast from django.db.models.functions import Cast
from django.test import ( from django.test import TestCase, ignore_warnings, skipUnlessDBFeature
TestCase, ignore_warnings, override_settings, skipUnlessDBFeature,
)
from ..models import Author, DTModel, Fan, FloatModel from ..models import Author, DTModel, Fan, FloatModel
@ -128,15 +125,5 @@ class CastTests(TestCase):
self.assertIsInstance(cast_float, float) self.assertIsInstance(cast_float, float)
self.assertEqual(cast_float, 0.125) 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(models.Avg('age'), models.FloatField())))
self.assertIn('(AVG("db_functions_author"."age"))::double precision', connection.queries[-1]['sql'])
def test_cast_to_text_field(self): def test_cast_to_text_field(self):
self.assertEqual(Author.objects.values_list(Cast('age', models.TextField()), flat=True).get(), '1') self.assertEqual(Author.objects.values_list(Cast('age', models.TextField()), flat=True).get(), '1')

View File

@ -5,10 +5,10 @@ from django.db import (
IntegrityError, NotSupportedError, connection, transaction, IntegrityError, NotSupportedError, connection, transaction,
) )
from django.db.models import ( from django.db.models import (
CheckConstraint, Deferrable, F, Func, Q, UniqueConstraint, CheckConstraint, Deferrable, F, Func, IntegerField, Q, UniqueConstraint,
) )
from django.db.models.fields.json import KeyTextTransform from django.db.models.fields.json import KeyTextTransform
from django.db.models.functions import Left from django.db.models.functions import Cast, Left
from django.test import skipUnlessDBFeature from django.test import skipUnlessDBFeature
from django.utils import timezone from django.utils import timezone
@ -783,3 +783,14 @@ class ExclusionConstraintTests(PostgreSQLTestCase):
with connection.schema_editor() as editor: with connection.schema_editor() as editor:
editor.add_constraint(RangesModel, constraint) editor.add_constraint(RangesModel, constraint)
self.assertIn(constraint_name, self.get_constraints(RangesModel._meta.db_table)) self.assertIn(constraint_name, self.get_constraints(RangesModel._meta.db_table))
def test_range_equal_cast(self):
constraint_name = 'exclusion_equal_room_cast'
self.assertNotIn(constraint_name, self.get_constraints(Room._meta.db_table))
constraint = ExclusionConstraint(
name=constraint_name,
expressions=[(Cast('number', IntegerField()), RangeOperators.EQUAL)],
)
with connection.schema_editor() as editor:
editor.add_constraint(Room, constraint)
self.assertIn(constraint_name, self.get_constraints(Room._meta.db_table))

View File

@ -305,7 +305,7 @@ class SchemaTests(PostgreSQLTestCase):
self.assertIn(index_name, constraints) self.assertIn(index_name, constraints)
self.assertIn(constraints[index_name]['type'], GinIndex.suffix) self.assertIn(constraints[index_name]['type'], GinIndex.suffix)
self.assertIs(sql.references_column(table, 'field'), True) self.assertIs(sql.references_column(table, 'field'), True)
self.assertIn('::tsvector', str(sql)) self.assertIn(' AS tsvector', str(sql))
with connection.schema_editor() as editor: with connection.schema_editor() as editor:
editor.remove_index(TextFieldModel, index) editor.remove_index(TextFieldModel, index)
self.assertNotIn(index_name, self.get_constraints(table)) self.assertNotIn(index_name, self.get_constraints(table))