summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Charette <charette.s@gmail.com>2021-01-25 23:32:55 -0500
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2021-01-26 08:20:40 +0100
commit9607e3a0cc4b812a4a1ce0e693357902408fc0b2 (patch)
tree1c0405bb9f56d40a398912eff1a3985720d758ab
parent5d9374b9fb12843ab897355fd319fef898a95d01 (diff)
[3.2.x] Fixed #32369 -- Fixed adding check constraints with pattern lookups and expressions as rhs.
This disables interpolation of constraint creation statements. Since Constraint.create_sql interpolates its parameters instead of deferring this responsibility to the backend connection it must disable connection level parameters interpolation. Backport of 42e8cf47c7ee2db238bf91197ea398126c546741 from master
-rw-r--r--django/db/backends/base/schema.py6
-rw-r--r--tests/migrations/test_operations.py14
2 files changed, 19 insertions, 1 deletions
diff --git a/django/db/backends/base/schema.py b/django/db/backends/base/schema.py
index be33ab3e4d..f879d59fa9 100644
--- a/django/db/backends/base/schema.py
+++ b/django/db/backends/base/schema.py
@@ -360,6 +360,8 @@ class BaseDatabaseSchemaEditor:
not self.connection.features.supports_expression_indexes
):
return None
+ # Index.create_sql returns interpolated SQL which makes params=None a
+ # necessity to avoid escaping attempts on execution.
self.execute(index.create_sql(model, self), params=None)
def remove_index(self, model, index):
@@ -375,7 +377,9 @@ class BaseDatabaseSchemaEditor:
"""Add a constraint to a model."""
sql = constraint.create_sql(model, self)
if sql:
- self.execute(sql)
+ # Constraint.create_sql returns interpolated SQL which makes
+ # params=None a necessity to avoid escaping attempts on execution.
+ self.execute(sql, params=None)
def remove_constraint(self, model, constraint):
"""Remove a constraint from a model."""
diff --git a/tests/migrations/test_operations.py b/tests/migrations/test_operations.py
index 897808f75b..984aefa23d 100644
--- a/tests/migrations/test_operations.py
+++ b/tests/migrations/test_operations.py
@@ -2145,6 +2145,7 @@ class OperationTests(OperationTestBase):
fields=[
('id', models.AutoField(primary_key=True)),
('name', models.CharField(max_length=100)),
+ ('surname', models.CharField(max_length=100, default='')),
('rebate', models.CharField(max_length=100)),
],
),
@@ -2178,6 +2179,19 @@ class OperationTests(OperationTestBase):
Author.objects.create(name='Albert', rebate='10$')
author = Author.objects.create(name='Albert', rebate='10%')
self.assertEqual(Author.objects.get(), author)
+ # Right-hand-side baked "%" literals should not be used for parameters
+ # interpolation.
+ check = ~models.Q(surname__startswith=models.F('name'))
+ constraint = models.CheckConstraint(check=check, name='name_constraint_rhs')
+ operation = migrations.AddConstraint('Author', constraint)
+ from_state = to_state
+ to_state = from_state.clone()
+ operation.state_forwards(app_label, to_state)
+ with connection.schema_editor() as editor:
+ operation.database_forwards(app_label, editor, from_state, to_state)
+ Author = to_state.apps.get_model(app_label, 'Author')
+ with self.assertRaises(IntegrityError), transaction.atomic():
+ Author.objects.create(name='Albert', surname='Alberto')
@skipUnlessDBFeature('supports_table_check_constraints')
def test_add_or_constraint(self):