Skip to content

Escape LIKE wildcard chars correctly in SQL Server StartsWith(char) - #39076

Open
ilkertskn wants to merge 2 commits into
dotnet:mainfrom
ilkertskn:fix/sqlserver-startswith-char-like-pattern
Open

ilkertskn wants to merge 2 commits into
dotnet:mainfrom
ilkertskn:fix/sqlserver-startswith-char-like-pattern

Conversation

@ilkertskn

Copy link
Copy Markdown
Contributor

When string.StartsWith(char) is called with a LIKE wildcard ('%', '_' or '['), the SQL Server translator built the pattern with LikeEscapeChar + s + "%". Both operands of the first + are chars, so they are added numerically and the pattern becomes e.g. LIKE N'129%' instead of LIKE N'\%%' ESCAPE N'\'. The query runs but matches the wrong rows. EndsWith and Contains were already concatenating correctly.

Use string interpolation for both the constant and the parameter path, and add FunkyData tests covering the constant, parameter, bracket and negated cases.

Fixes #38923

  • [x ] I've read the guidelines for contributing and seen the walkthrough
  • [x ] I've posted a comment on an issue with a detailed description of how I am planning to contribute and got approval from a member of the team
  • [ x] The code builds and tests pass locally (also verified by our automated build checks)
  • [x ] Commit messages follow this format:
        Summary of the changes
        - Detail 1
        - Detail 2

        Fixes #bugnumber
  • [x ] Tests for the changes have been added (for bug fixes / features)
  • [ x] Code follows the same patterns and style as existing code in this repo

When string.StartsWith(char) is called with a LIKE wildcard ('%', '_' or '['),
the SQL Server translator built the pattern with `LikeEscapeChar + s + "%"`.
Both operands of the first `+` are chars, so they are added numerically and the
pattern becomes e.g. `LIKE N'129%'` instead of `LIKE N'\%%' ESCAPE N'\'`. The
query runs but matches the wrong rows. EndsWith and Contains were already
concatenating correctly.

Use string interpolation for both the constant and the parameter path, and add
FunkyData tests covering the constant, parameter, bracket and negated cases.

Fixes dotnet#38923

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@ilkertskn

Copy link
Copy Markdown
Contributor Author

The 7 failures are all in BatchingTest and cascade from a single connection-pool timeout in Deadlock_on_inserts_and_deletes_with_dependents_is_handled_correctly on the CI SQL Server. The deadlock test then aborted before Fixture.ReseedAsync(), leaving 4 extra blogs in the shared fixture database, which is exactly the off-by-4 (503 vs 499) and off-by-2 (4 vs 2) seen in the subsequent Inserts_* tests. None of these tests exercise LIKE translation; this PR only touches SqlServerSqlTranslatingExpressionVisitor and the FunkyData tests, which passed in the same run. Re-running the job should be enough. fyi

@AndriySvyryd AndriySvyryd self-assigned this Sep 23, 2026
methodType switch
{
StartsEndsWithContains.StartsWith => LikeEscapeChar + s + "%",
StartsEndsWithContains.StartsWith => $"{LikeEscapeChar}{s}%",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Also change this for EndsWith and for any other ambiguous use of the + operator with a char

@AndriySvyryd
AndriySvyryd requested review from a team and a lite review from Copilot September 23, 2026 21:04

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.

Copilot review overview

🟡 Changes recommended

The constant wildcard path still emits unsupported ESCAPE SQL on Azure Synapse.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 1 High severity · 1 Medium severity

Open (2)
What changed in this PR

Fixes SQL Server StartsWith(char) translation for LIKE wildcard characters and adds regression coverage.

Changes:

  • Corrects constant and parameterized pattern construction.
  • Adds shared wildcard and negated-case tests.
  • Adds SQL Server SQL baselines.
File Summary
test/​EFCore.SqlServer.FunctionalTests/​Query/​FunkyDataQuerySqlServerTest.cs Adds SQL Server baselines.
test/​EFCore.Specification.Tests/​Query/​FunkyDataQueryTestBase.cs Adds shared regression tests.
src/​EFCore.SqlServer/​Query/​Internal/​SqlServerSqlTranslatingExpressionVisitor.cs Fixes wildcard pattern construction.

Comment thread test/EFCore.Specification.Tests/Query/FunkyDataQueryTestBase.cs
…cards on Azure Synapse

Rewrite every StartsWith/EndsWith/Contains LIKE pattern arm in the SQL Server
and SQLite translators as an interpolated string, so no arm relies on operand
order to keep a char from being added numerically.

On Azure Synapse the constant char wildcard case still emitted LIKE ... ESCAPE,
which Synapse does not support. Route it through TranslateWithoutLike like the
string constant and parameter cases already do, passing a string constant so
the LEFT/RIGHT/CHARINDEX translation does not render a char-valued constant with
a string type mapping. Add Synapse baselines for the char wildcard tests.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

string.StartsWith(char) with wild char translates to incorrect LIKE pattern on SQL Server

3 participants