From b69b0c3fe871167a0ca01bb439508e335143801f Mon Sep 17 00:00:00 2001 From: Lucidiot Date: Thu, 17 Jun 2021 21:54:08 +0200 Subject: [PATCH] Fixed #32858 -- Fixed ExclusionConstraint crash with index transforms in expressions. --- AUTHORS | 1 + django/contrib/postgres/constraints.py | 3 +++ tests/postgres_tests/test_constraints.py | 17 ++++++++++++++++- 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index ef1e4ea095..84cbf17c27 100644 --- a/AUTHORS +++ b/AUTHORS @@ -561,6 +561,7 @@ answer newbie questions, and generally made Django that much better: Luan Pablo Lucas Connors Luciano Ramalho + Lucidiot Ludvig Ericson Luis C. Berrocal Ɓukasz Langa diff --git a/django/contrib/postgres/constraints.py b/django/contrib/postgres/constraints.py index 48dfd2bbc0..ddc9d23df9 100644 --- a/django/contrib/postgres/constraints.py +++ b/django/contrib/postgres/constraints.py @@ -2,6 +2,7 @@ from django.db import NotSupportedError from django.db.backends.ddl_references import Statement, Table from django.db.models import Deferrable, F, Q from django.db.models.constraints import BaseConstraint +from django.db.models.expressions import Col from django.db.models.sql import Query __all__ = ['ExclusionConstraint'] @@ -73,6 +74,8 @@ class ExclusionConstraint(BaseConstraint): expression = F(expression) expression = expression.resolve_expression(query=query) sql, params = compiler.compile(expression) + if not isinstance(expression, Col): + sql = f'({sql})' try: opclass = self.opclasses[idx] if opclass: diff --git a/tests/postgres_tests/test_constraints.py b/tests/postgres_tests/test_constraints.py index 7915bb226e..65402bd429 100644 --- a/tests/postgres_tests/test_constraints.py +++ b/tests/postgres_tests/test_constraints.py @@ -14,7 +14,9 @@ from django.test import modify_settings, skipUnlessDBFeature from django.utils import timezone from . import PostgreSQLTestCase -from .models import HotelReservation, RangesModel, Room, Scene +from .models import ( + HotelReservation, IntegerArrayModel, RangesModel, Room, Scene, +) try: from psycopg2.extras import DateRange, NumericRange @@ -670,6 +672,19 @@ class ExclusionConstraintTests(PostgreSQLTestCase): self.get_constraints(HotelReservation._meta.db_table), ) + def test_index_transform(self): + constraint_name = 'first_index_equal' + constraint = ExclusionConstraint( + name=constraint_name, + expressions=[('field__0', RangeOperators.EQUAL)], + ) + with connection.schema_editor() as editor: + editor.add_constraint(IntegerArrayModel, constraint) + self.assertIn( + constraint_name, + self.get_constraints(IntegerArrayModel._meta.db_table), + ) + def test_range_adjacent_initially_deferred(self): constraint_name = 'ints_adjacent_deferred' self.assertNotIn(constraint_name, self.get_constraints(RangesModel._meta.db_table))