diff options
author | Simon Charette <charette.s@gmail.com> | 2023-05-09 22:34:43 -0400 |
---|---|---|
committer | Mariusz Felisiak <felisiak.mariusz@gmail.com> | 2023-05-10 17:25:57 +0200 |
commit | ffff17d4b0117cce59f65c9f56fa164694eafd23 (patch) | |
tree | f0a756b34808b12bceab28cad2cbcb4dc69b612a | |
parent | e0f8104a968d316925f736e961a63bef73a686b4 (diff) | |
download | django-ffff17d4b0117cce59f65c9f56fa164694eafd23.tar.gz |
Fixed #34553 -- Fixed improper % escaping of literal in constraints.
Proper escaping of % in string literals used when defining constaints
was attempted (a8b3f96f6) by overriding quote_value of Postgres and
Oracle schema editor. The same approach was used when adding support for
constraints to the MySQL/MariaDB backend (1fc2c70).
Later on it was discovered that this approach was not appropriate and
that a preferable one was to pass params=None when executing the
constraint creation DDL to avoid any form of interpolation in the first
place (42e8cf47).
When the second patch was applied the corrective of the first were not
removed which caused % literals to be unnecessary doubled. This flew
under the radar because the existings test were crafted in a way that
consecutive %% didn't catch regressions.
This commit introduces an extra test for __exact lookups which
highlights more adequately % doubling problems but also adjust a
previous __endswith test to cover % doubling problems (%\% -> %%\%%).
Thanks Thomas Kolar for the report.
Refs #32369, #30408, #30593.
-rw-r--r-- | django/db/backends/mysql/schema.py | 2 | ||||
-rw-r--r-- | django/db/backends/oracle/schema.py | 2 | ||||
-rw-r--r-- | django/db/backends/postgresql/schema.py | 2 | ||||
-rw-r--r-- | tests/migrations/test_operations.py | 8 |
4 files changed, 8 insertions, 6 deletions
diff --git a/django/db/backends/mysql/schema.py b/django/db/backends/mysql/schema.py index 2ac287328b..31829506c1 100644 --- a/django/db/backends/mysql/schema.py +++ b/django/db/backends/mysql/schema.py @@ -56,8 +56,6 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): def quote_value(self, value): self.connection.ensure_connection() - if isinstance(value, str): - value = value.replace("%", "%%") # MySQLdb escapes to string, PyMySQL to bytes. quoted = self.connection.connection.escape( value, self.connection.connection.encoders diff --git a/django/db/backends/oracle/schema.py b/django/db/backends/oracle/schema.py index aca6d83962..0d70522a2a 100644 --- a/django/db/backends/oracle/schema.py +++ b/django/db/backends/oracle/schema.py @@ -32,7 +32,7 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): elif isinstance(value, datetime.timedelta): return "'%s'" % duration_iso_string(value) elif isinstance(value, str): - return "'%s'" % value.replace("'", "''").replace("%", "%%") + return "'%s'" % value.replace("'", "''") elif isinstance(value, (bytes, bytearray, memoryview)): return "'%s'" % value.hex() elif isinstance(value, bool): diff --git a/django/db/backends/postgresql/schema.py b/django/db/backends/postgresql/schema.py index 18899a6a2b..40fef6660e 100644 --- a/django/db/backends/postgresql/schema.py +++ b/django/db/backends/postgresql/schema.py @@ -56,8 +56,6 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): ) def quote_value(self, value): - if isinstance(value, str): - value = value.replace("%", "%%") return sql.quote(value, self.connection.connection) def _field_indexes_sql(self, model, field): diff --git a/tests/migrations/test_operations.py b/tests/migrations/test_operations.py index 0135dbc538..b67a871bc8 100644 --- a/tests/migrations/test_operations.py +++ b/tests/migrations/test_operations.py @@ -3662,7 +3662,7 @@ class OperationTests(OperationTestBase): ), # Literal "%" should be escaped in a way that is not a considered a # wildcard. - (models.Q(rebate__endswith="%"), {"rebate": "10%"}, {"rebate": "10$"}), + (models.Q(rebate__endswith="%"), {"rebate": "10%"}, {"rebate": "10%$"}), # Right-hand-side baked "%" literals should not be used for # parameters interpolation. ( @@ -3670,6 +3670,12 @@ class OperationTests(OperationTestBase): {"name": "Albert"}, {"name": "Albert", "surname": "Alberto"}, ), + # Exact matches against "%" literals should also be supported. + ( + models.Q(name="%"), + {"name": "%"}, + {"name": "Albert"}, + ), ] for check, valid, invalid in checks: with self.subTest(check=check, valid=valid, invalid=invalid): |