summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorHaki Benita <hakibenita@gmail.com>2025-12-25 11:42:58 +0200
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2026-01-10 08:12:05 +0100
commitd61838761f17d7c934c7be288fadfa14f471b598 (patch)
tree9a3bdb1288beafee649f5b154188bb6d7e458ce8 /tests
parent8a0315fab74d603813b2a64ab98d5a208a2eb6d1 (diff)
Fixed #36827 -- Added support for exclusion constraints using Hash indexes on PostgreSQL.
Diffstat (limited to 'tests')
-rw-r--r--tests/postgres_tests/test_constraints.py86
1 files changed, 84 insertions, 2 deletions
diff --git a/tests/postgres_tests/test_constraints.py b/tests/postgres_tests/test_constraints.py
index 331b23980d..39c2f9b1f1 100644
--- a/tests/postgres_tests/test_constraints.py
+++ b/tests/postgres_tests/test_constraints.py
@@ -27,7 +27,13 @@ from django.test.utils import isolate_apps
from django.utils import timezone
from . import PostgreSQLTestCase
-from .models import HotelReservation, IntegerArrayModel, RangesModel, Room, Scene
+from .models import (
+ HotelReservation,
+ IntegerArrayModel,
+ RangesModel,
+ Room,
+ Scene,
+)
try:
from django.contrib.postgres.constraints import ExclusionConstraint
@@ -299,7 +305,7 @@ class ExclusionConstraintTests(PostgreSQLTestCase):
)
def test_invalid_index_type(self):
- msg = "Exclusion constraints only support GiST or SP-GiST indexes."
+ msg = "Exclusion constraints only support GiST, Hash, or SP-GiST indexes."
with self.assertRaisesMessage(ValueError, msg):
ExclusionConstraint(
index_type="gin",
@@ -480,6 +486,18 @@ class ExclusionConstraintTests(PostgreSQLTestCase):
"(F(datespan), '-|-')] name='exclude_overlapping' "
"violation_error_code='overlapping_must_be_excluded'>",
)
+ constraint = ExclusionConstraint(
+ name="exclude_equal_hash",
+ index_type="hash",
+ expressions=[(F("room"), RangeOperators.EQUAL)],
+ violation_error_code="room_must_be_unique",
+ )
+ self.assertEqual(
+ repr(constraint),
+ "<ExclusionConstraint: index_type='hash' expressions=["
+ "(F(room), '=')] name='exclude_equal_hash' "
+ "violation_error_code='room_must_be_unique'>",
+ )
def test_eq(self):
constraint_1 = ExclusionConstraint(
@@ -581,6 +599,12 @@ class ExclusionConstraintTests(PostgreSQLTestCase):
violation_error_code="custom_code",
violation_error_message="other custom error",
)
+ constraint_13 = ExclusionConstraint(
+ name="exclude_equal_hash",
+ index_type="hash",
+ expressions=[(F("room"), RangeOperators.EQUAL)],
+ violation_error_code="room_must_be_unique",
+ )
self.assertEqual(constraint_1, constraint_1)
self.assertEqual(constraint_1, mock.ANY)
self.assertNotEqual(constraint_1, constraint_2)
@@ -597,8 +621,10 @@ class ExclusionConstraintTests(PostgreSQLTestCase):
self.assertNotEqual(constraint_1, object())
self.assertNotEqual(constraint_10, constraint_11)
self.assertNotEqual(constraint_11, constraint_12)
+ self.assertNotEqual(constraint_12, constraint_13)
self.assertEqual(constraint_10, constraint_10)
self.assertEqual(constraint_12, constraint_12)
+ self.assertEqual(constraint_13, constraint_13)
def test_deconstruct(self):
constraint = ExclusionConstraint(
@@ -1256,6 +1282,26 @@ class ExclusionConstraintTests(PostgreSQLTestCase):
editor.add_constraint(Room, constraint)
self.assertIn(constraint_name, self.get_constraints(Room._meta.db_table))
+ def test_hash_uniqueness(self):
+ constraint_name = "exclusion_equal_room_hash"
+ self.assertNotIn(constraint_name, self.get_constraints(Room._meta.db_table))
+ constraint = ExclusionConstraint(
+ name=constraint_name,
+ index_type="hash",
+ expressions=[(F("number"), RangeOperators.EQUAL)],
+ )
+ with connection.schema_editor() as editor:
+ editor.add_constraint(Room, constraint)
+ self.assertIn(constraint_name, self.get_constraints(Room._meta.db_table))
+ Room.objects.create(number=101)
+ with self.assertRaises(IntegrityError), transaction.atomic():
+ Room.objects.create(number=101)
+ Room.objects.create(number=102)
+ # Drop the constraint.
+ with connection.schema_editor() as editor:
+ editor.remove_constraint(Room, constraint)
+ self.assertNotIn(constraint.name, self.get_constraints(Room._meta.db_table))
+
@isolate_apps("postgres_tests")
def test_table_create(self):
constraint_name = "exclusion_equal_number_tc"
@@ -1287,3 +1333,39 @@ class ExclusionConstraintTests(PostgreSQLTestCase):
msg = "Constraint “ints_equal” is violated."
with self.assertRaisesMessage(ValidationError, msg):
constraint.validate(RangesModel, RangesModel())
+
+ def test_covering_hash_index_not_supported(self):
+ constraint_name = "covering_hash_index_not_supported"
+ msg = "Covering exclusion constraints using Hash indexes are not supported."
+ with self.assertRaisesMessage(ValueError, msg):
+ ExclusionConstraint(
+ name=constraint_name,
+ expressions=[("int1", RangeOperators.EQUAL)],
+ index_type="hash",
+ include=["int2"],
+ )
+
+ def test_composite_hash_index_not_supported(self):
+ constraint_name = "composite_hash_index_not_supported"
+ msg = "Composite exclusion constraints using Hash indexes are not supported."
+ with self.assertRaisesMessage(ValueError, msg):
+ ExclusionConstraint(
+ name=constraint_name,
+ expressions=[
+ ("int1", RangeOperators.EQUAL),
+ ("int2", RangeOperators.EQUAL),
+ ],
+ index_type="hash",
+ )
+
+ def test_non_equal_hash_index_not_supported(self):
+ constraint_name = "none_equal_hash_index_not_supported"
+ msg = (
+ "Exclusion constraints using Hash indexes only support the EQUAL operator."
+ )
+ with self.assertRaisesMessage(ValueError, msg):
+ ExclusionConstraint(
+ name=constraint_name,
+ expressions=[("int1", RangeOperators.NOT_EQUAL)],
+ index_type="hash",
+ )