Skip to content

Commit 24639eb

Browse files
Disable check constraints via feature flag
1 parent 8bd8b10 commit 24639eb

File tree

5 files changed

+131
-15
lines changed

5 files changed

+131
-15
lines changed

aurora_dsql_django/features.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,10 @@ class DatabaseFeatures(features.DatabaseFeatures):
3131
# at the end of each save operation?
3232
supports_forward_references = True
3333

34-
# Does it support foreign keys?
3534
supports_foreign_keys = False
3635

36+
supports_table_check_constraints = False
37+
3738
# Can it create foreign key constraints inline when adding columns?
3839
can_create_inline_fk = False
3940

aurora_dsql_django/schema.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,6 @@ class DatabaseSchemaEditor(schema.DatabaseSchemaEditor):
4949

5050
# These "ALTER TABLE" operations are not supported.
5151
sql_create_pk = ""
52-
sql_create_check = ""
53-
sql_delete_check = ""
5452
sql_delete_constraint = ""
5553
sql_delete_column = ""
5654

@@ -67,12 +65,19 @@ def __enter__(self):
6765
def add_index(self, model, index, concurrently=False):
6866
if index.contains_expressions and not self.connection.features.supports_expression_indexes:
6967
return None
70-
super().add_index(model, index, concurrently)
68+
return super().add_index(model, index, concurrently)
7169

7270
def remove_index(self, model, index, concurrently=False):
7371
if index.contains_expressions and not self.connection.features.supports_expression_indexes:
7472
return None
75-
super().remove_index(model, index, concurrently)
73+
return super().remove_index(model, index, concurrently)
74+
75+
def _check_sql(self, name, check):
76+
# There is no feature check in the upstream implementation when creating
77+
# a model, so we add our own check.
78+
if not self.connection.features.supports_table_check_constraints:
79+
return None
80+
return super()._check_sql(name, check)
7681

7782
def _index_columns(self, table, columns, col_suffixes, opclasses):
7883
# Aurora DSQL doesn't support PostgreSQL opclasses.

aurora_dsql_django/tests/unit/test_features.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ def test_supports_forward_references(self):
1818
def test_supports_foreign_keys(self):
1919
self.assertFalse(self.features.supports_foreign_keys)
2020

21+
def test_supports_check_constraints(self):
22+
self.assertFalse(self.features.supports_table_check_constraints)
23+
2124
def test_can_create_inline_fk(self):
2225
self.assertFalse(self.features.can_create_inline_fk)
2326

aurora_dsql_django/tests/unit/test_schema.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@ def test_sql_attributes(self):
2323
self.schema_editor.sql_update_with_default,
2424
"UPDATE %(table)s SET %(column)s = %(default)s WHERE %(column)s IS NULL"
2525
)
26-
self.assertEqual(self.schema_editor.sql_create_check, "")
27-
self.assertEqual(self.schema_editor.sql_delete_check, "")
2826
self.assertEqual(self.schema_editor.sql_delete_constraint, "")
2927
self.assertEqual(self.schema_editor.sql_delete_column, "")
3028

@@ -93,6 +91,25 @@ def test_create_like_index_sql(self):
9391

9492
self.assertIsNone(result)
9593

94+
@patch('aurora_dsql_django.schema.schema.DatabaseSchemaEditor._check_sql')
95+
def test_check_sql_feature_disabled(self, mock_super_check_sql):
96+
self.connection.features.supports_table_check_constraints = False
97+
98+
result = self.schema_editor._check_sql("test_check", "age >= 0")
99+
100+
self.assertIsNone(result)
101+
mock_super_check_sql.assert_not_called()
102+
103+
@patch('aurora_dsql_django.schema.schema.DatabaseSchemaEditor._check_sql')
104+
def test_check_sql_feature_enabled(self, mock_super_check_sql):
105+
self.connection.features.supports_table_check_constraints = True
106+
mock_super_check_sql.return_value = "CHECK (age >= 0)"
107+
108+
result = self.schema_editor._check_sql("test_check", "age >= 0")
109+
110+
mock_super_check_sql.assert_called_once_with("test_check", "age >= 0")
111+
self.assertEqual(result, "CHECK (age >= 0)")
112+
96113

97114
if __name__ == '__main__':
98115
unittest.main()

aurora_dsql_django/tests/unit/test_wrapper.py

Lines changed: 98 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import django
44
from django.conf import settings
55
from django.db import models
6+
from django.db.models import CheckConstraint, Q
67
from aurora_dsql_django.base import DatabaseWrapper
78
from aurora_dsql_django.features import DatabaseFeatures
89
from aurora_dsql_django.schema import DatabaseSchemaEditor
@@ -16,20 +17,46 @@
1617
django.setup()
1718

1819

20+
def simple_quote_value(value):
21+
return f"'{value}'"
22+
23+
1924
class TestWrapper(unittest.TestCase):
2025
"""Test Aurora DSQL wrapper behavior when all parts are working together"""
2126

2227
def setUp(self):
2328
self.connection = DatabaseWrapper({})
2429
self.connection.connection = MagicMock()
25-
self.connection.connection.encoding = 'utf8'
2630

2731
# Configure mock to use real components.
2832
self.connection.features = DatabaseFeatures(self.connection)
2933
self.schema_editor = DatabaseSchemaEditor(self.connection)
3034

31-
def test_foreign_key_sql_generation(self):
32-
"""Ensure foreign key SQL is not generated when the feature is disabled"""
35+
def _assert_sql_not_generated(self, operation_func, sql_patterns, message):
36+
"""Helper method to verify SQL patterns are not generated"""
37+
executed_sql = []
38+
39+
def mock_execute(sql, params=None):
40+
executed_sql.append((sql, params))
41+
42+
# Capture SQL statements without running anything against a real DB.
43+
execute_patch = patch.object(self.schema_editor, 'execute', side_effect=mock_execute)
44+
45+
# Work around issue caused by missing encoding configuration in test environment.
46+
quote_patch = patch.object(self.schema_editor, 'quote_value', side_effect=simple_quote_value)
47+
48+
with execute_patch, quote_patch:
49+
operation_func()
50+
51+
all_sql = [str(sql) for sql, _ in executed_sql]
52+
if hasattr(self.schema_editor, 'deferred_sql'):
53+
all_sql += [str(sql) for sql in self.schema_editor.deferred_sql]
54+
55+
matching_statements = [sql for sql in all_sql if any(pattern in sql for pattern in sql_patterns)]
56+
self.assertListEqual([], matching_statements, message)
57+
58+
def test_foreign_key_operations_ignored(self):
59+
"""Ensure foreign key constraint operations are ignored for model creation when the feature is disabled"""
3360

3461
class ParentModel(models.Model):
3562
class Meta:
@@ -41,14 +68,77 @@ class ChildModel(models.Model):
4168
class Meta:
4269
app_label = 'test_app'
4370

44-
# Mock execute to capture SQL without actually running it.
45-
with patch.object(self.schema_editor, 'execute'):
71+
def operation():
4672
with self.schema_editor:
4773
self.schema_editor.create_model(ChildModel)
4874

49-
# Check that no foreign key SQL was deferred.
50-
foreign_key_statements = [sql for sql in self.schema_editor.deferred_sql if 'FOREIGN KEY' in str(sql)]
51-
self.assertListEqual([], foreign_key_statements, "Should not generate foreign key SQL")
75+
self._assert_sql_not_generated(
76+
operation,
77+
['FOREIGN KEY', 'REFERENCES'],
78+
"Should not generate foreign key SQL"
79+
)
80+
81+
def test_check_constraint_create_model_ignored(self):
82+
"""Ensure check constraint operations are ignored for model creation when the feature is disabled"""
83+
84+
class CheckConstraintModel(models.Model):
85+
age = models.IntegerField()
86+
87+
class Meta:
88+
app_label = 'test_app'
89+
constraints = [
90+
CheckConstraint(condition=Q(age__gte=0), name='age_gte_0')
91+
]
92+
93+
def operation():
94+
with self.schema_editor:
95+
self.schema_editor.create_model(CheckConstraintModel)
96+
97+
self._assert_sql_not_generated(
98+
operation,
99+
['CHECK'],
100+
"Should not generate check constraint SQL"
101+
)
102+
103+
def test_check_constraint_add_constraint_ignored(self):
104+
"""Ensure add_constraint operations ignore check constraints when the feature is disabled"""
105+
106+
class AddCheckConstraintModel(models.Model):
107+
age = models.IntegerField()
108+
109+
class Meta:
110+
app_label = 'test_app'
111+
112+
constraint = CheckConstraint(condition=Q(age__gte=0), name='age_gte_0')
113+
114+
def operation():
115+
self.schema_editor.add_constraint(AddCheckConstraintModel, constraint)
116+
117+
self._assert_sql_not_generated(
118+
operation,
119+
['CHECK'],
120+
"Should not execute check constraint SQL"
121+
)
122+
123+
def test_check_constraint_remove_constraint_ignored(self):
124+
"""Ensure remove_constraint operations ignore check constraints when the feature is disabled"""
125+
126+
class RemoveCheckConstraintModel(models.Model):
127+
age = models.IntegerField()
128+
129+
class Meta:
130+
app_label = 'test_app'
131+
132+
constraint = CheckConstraint(condition=Q(age__gte=0), name='age_gte_0')
133+
134+
def operation():
135+
self.schema_editor.remove_constraint(RemoveCheckConstraintModel, constraint)
136+
137+
self._assert_sql_not_generated(
138+
operation,
139+
['CONSTRAINT'],
140+
"Should not execute check constraint removal SQL"
141+
)
52142

53143

54144
if __name__ == '__main__':

0 commit comments

Comments
 (0)