Skip to content

Backport LIKE escape mapping fix to 10.0 - #3891

Merged
roji merged 3 commits into
hotfix/10.0.3from
roji-backport-like-escape-10.0.3
Jul 23, 2026
Merged

Backport LIKE escape mapping fix to 10.0#3891
roji merged 3 commits into
hotfix/10.0.3from
roji-backport-like-escape-10.0.3

Conversation

@roji

@roji roji commented Jul 23, 2026

Copy link
Copy Markdown
Member

Backports #3889 and #3890 to the hotfix/10.0.3 branch.

A value converter on a LIKE/ILIKE match property could be applied to the escape expression, converting the provider-generated empty escape into ESCAPE NULL and causing matching predicates to evaluate as unknown. This keeps escape-character type mapping independent from match/pattern inference for both LIKE and ILIKE, while explicitly preserving an absent optional escape.

The regression test is adapted to the 10.0 branch's InitializeAsync test infrastructure.

Tests:

  • NorthwindDbFunctionsQueryNpgsqlTest
  • AdHocMiscellaneousQueryNpgsqlTest.Like_with_implicit_escape_does_not_apply_value_converter
  • 42 tests passed

Fixes: #3888

roji added 3 commits July 23, 2026 10:21
Keep LIKE and ILIKE escape expressions independent from match and pattern type mapping so property converters cannot rewrite provider-generated escape values.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: e5b861c2-ea5b-411c-8efe-3d925d29c52e
Preserve null escape expressions before applying default type mapping for LIKE and ILIKE.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: e5b861c2-ea5b-411c-8efe-3d925d29c52e
Use the hotfix branch's InitializeAsync and CreateContext helpers for the backported regression test.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: e5b861c2-ea5b-411c-8efe-3d925d29c52e
Copilot AI review requested due to automatic review settings July 23, 2026 17:23
@roji
roji changed the base branch from main to hotfix/10.0.3 July 23, 2026 17:23
@roji
roji merged commit 2d14bd7 into hotfix/10.0.3 Jul 23, 2026
6 checks passed
@roji
roji deleted the roji-backport-like-escape-10.0.3 branch July 23, 2026 17:28

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Note

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Backports the fix for #3888 to prevent a LIKE/ILIKE match property’s value converter from being applied to the provider-generated escape expression, which could lead to ESCAPE NULL and “unknown” predicate results.

Changes:

  • Adjust type-mapping application for LikeExpression and PgILikeExpression so the escape character mapping is independent from match/pattern inference.
  • Add a regression test verifying implicit escape does not apply the value converter for both LIKE and ILIKE.
Show a summary per file
File Description
test/EFCore.PG.FunctionalTests/Query/AdHocMiscellaneousQueryNpgsqlTest.cs Adds regression coverage for implicit escape handling with a value-converted property.
src/EFCore.PG/Query/NpgsqlSqlExpressionFactory.cs Updates type mapping logic to avoid propagating converters onto escape expressions for LIKE/ILIKE.

Review details

  • Files reviewed: 2/2 changed files
  • Comments generated: 1
  • Review effort level: Low

Comment on lines +714 to 739
// The base implementation is private, so duplicate it here to keep the escape character's type mapping independent.
private SqlExpression ApplyTypeMappingOnLike(LikeExpression likeExpression)
{
var inferredTypeMapping = ExpressionExtensions.InferTypeMapping(likeExpression.Match, likeExpression.Pattern)
?? _typeMappingSource.FindMapping(likeExpression.Match.Type, Dependencies.Model);

return new LikeExpression(
ApplyTypeMapping(likeExpression.Match, inferredTypeMapping),
ApplyTypeMapping(likeExpression.Pattern, inferredTypeMapping),
// The escape character must not inherit the match or pattern's value converter (#3888).
likeExpression.EscapeChar is null ? null : ApplyDefaultTypeMapping(likeExpression.EscapeChar),
_boolTypeMapping);
}

private SqlExpression ApplyTypeMappingOnILike(PgILikeExpression ilikeExpression)
{
var inferredTypeMapping = (ilikeExpression.EscapeChar is null
? ExpressionExtensions.InferTypeMapping(
ilikeExpression.Match, ilikeExpression.Pattern)
: ExpressionExtensions.InferTypeMapping(
ilikeExpression.Match, ilikeExpression.Pattern,
ilikeExpression.EscapeChar))
var inferredTypeMapping = ExpressionExtensions.InferTypeMapping(ilikeExpression.Match, ilikeExpression.Pattern)
?? _typeMappingSource.FindMapping(ilikeExpression.Match.Type, Dependencies.Model);

return new PgILikeExpression(
ApplyTypeMapping(ilikeExpression.Match, inferredTypeMapping),
ApplyTypeMapping(ilikeExpression.Pattern, inferredTypeMapping),
ApplyTypeMapping(ilikeExpression.EscapeChar, inferredTypeMapping),
// The escape character must not inherit the match or pattern's value converter (#3888).
ilikeExpression.EscapeChar is null ? null : ApplyDefaultTypeMapping(ilikeExpression.EscapeChar),
_boolTypeMapping);
}
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.

ILike implicit empty escape applies property ValueConverter, producing ESCAPE NULL

2 participants