diff options
| author | Simon Charette <charette.s@gmail.com> | 2021-01-25 23:32:55 -0500 |
|---|---|---|
| committer | Mariusz Felisiak <felisiak.mariusz@gmail.com> | 2021-01-26 06:41:57 +0100 |
| commit | 42e8cf47c7ee2db238bf91197ea398126c546741 (patch) | |
| tree | 4a76c036b3ce3ffb02de1561092dde0588b9ea24 | |
| parent | 90ddf46ef7b3d775b124d81e1846bec7961c7f1f (diff) | |
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.
| -rw-r--r-- | django/db/backends/base/schema.py | 6 | ||||
| -rw-r--r-- | tests/migrations/test_operations.py | 14 |
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): |
