Skip to content

FIX: escape '[' wildcard in F() expression pattern lookups - #575

Open
KHAN (Khan3K) wants to merge 1 commit into
microsoft:devfrom
Khan3K:fix/escape-brackets-in-f-expression-patterns
Open

FIX: escape '[' wildcard in F() expression pattern lookups#575
KHAN (Khan3K) wants to merge 1 commit into
microsoft:devfrom
Khan3K:fix/escape-brackets-in-f-expression-patterns

Conversation

@Khan3K

Copy link
Copy Markdown

Adds bracket escaping to DatabaseWrapper.pattern_esc, which is applied to __contains, __startswith, __endswith and their case-insensitive variants when the search term comes from an F() expression (column reference).

[ is the SQL Server LIKE character-class wildcard. Before this change, values containing [ were matched as a character class, so a row like firstname='[J]ohnny' matched firstname__contains=F("lastname") where lastname='[J]ohnny' — silently wrong results. ] outside a character class is already literal in SQL Server LIKE and needs no escaping.

# before
pattern_esc = r"REPLACE(REPLACE(REPLACE({}, '\', '[\]'), '%%', '[%%]'), '_', '[_]')"

# after
pattern_esc = r"REPLACE(REPLACE(REPLACE(REPLACE({}, '[', '[[]'), '\', '[\]'), '%%', '[%%]'), '_', '[_]')"

Mirrors the existing literal-term escaping in DatabaseOperations.prep_for_like_query, which already escapes [ as [[].

Adds regression tests for __contains and __startswith with an F() expression, and removes the expressions.tests.ExpressionsTests.test_patterns_escape exclusion from the test settings (it now passes).

Tested with:

python manage.py test testapp.tests.test_expressions.TestPatternLookupExpressionEscaping --noinput
python django/tests/runtests.py --settings=testapp.settings --noinput expressions.tests.ExpressionsTests.test_patterns_escape

Fixes #573

Pattern lookups (__contains, __startswith, __endswith and their
case-insensitive variants) whose search term comes from a column reference
(F()) escape backslash, percent, and underscore on the database side via
DatabaseWrapper.pattern_esc, but not the '[' character-class wildcard.
Values containing square brackets therefore match incorrectly and
silently (e.g. '[J]ohnny' acts as a character class matching 'J').

Escape '[' as '[[]' in pattern_esc, mirroring the existing literal-pattern
escaping in DatabaseOperations.prep_for_like_query. A bare ']' outside a
character class is already literal in SQL Server LIKE, so no additional
escaping is required.

Fixes microsoft#573
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes incorrect SQL Server LIKE behavior for pattern lookups (__contains, __startswith, __endswith and case-insensitive variants) when the pattern comes from an expression (notably F() column references). It updates the backend’s expression-side escaping to treat [ literally (matching SQL Server’s own literal-pattern escaping behavior), and adds regression tests plus re-enables the upstream Django test that was previously excluded.

Changes:

  • Escape [ in DatabaseWrapper.pattern_esc to prevent SQL Server LIKE character-class wildcard mis-matches for expression-sourced patterns.
  • Add regression tests covering __contains / __startswith with F() patterns containing bracketed values.
  • Remove the now-unnecessary exclusion of expressions.tests.ExpressionsTests.test_patterns_escape.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
mssql/base.py Escapes [ in expression-side LIKE pattern escaping (pattern_esc) to ensure literal bracket matching.
testapp/tests/test_expressions.py Adds regression tests for bracket escaping in F()-driven pattern lookups.
testapp/settings.py Removes the excluded Django test now that bracket escaping is implemented.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@Khan3K

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

@bewithgaurav

Copy link
Copy Markdown
Collaborator

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).

@github-actions

Copy link
Copy Markdown

📊 Code Coverage Report

🔥 Diff Coverage

100%


🎯 Overall Coverage

83.28%


📈 Total Lines Covered: 2531 out of 3039
📁 Project: mssql-django


Diff Coverage

Diff: dev...HEAD, staged and unstaged changes

No lines with coverage information in this diff.


📋 Files Needing Attention

📉 Files with overall lowest coverage (click to expand)
- mssql/client.py: 19.5%  (41 lines)
- mssql/__init__.py: 50.0%  (2 lines)
- mssql/creation.py: 56.8%  (74 lines)
- mssql/operations.py: 78.8%  (396 lines)
- mssql/compiler.py: 82.3%  (644 lines)
- mssql/functions.py: 84.1%  (428 lines)
- mssql/base.py: 84.8%  (514 lines)
- mssql/schema.py: 88.3%  (719 lines)
- mssql/introspection.py: 90.2%  (133 lines)
- mssql/features.py: 98.9%  (88 lines)

🔗 Quick Links

⚙️ Build Summary 📋 Coverage Details

View Azure DevOps Build

Browse Full Coverage Report

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Django 6.1] Pattern lookups with an F() expression are not escaped for [ ] wildcards on SQL Server

3 participants