Keep nullable outer-key null check in compound APPLY→JOIN predicates#38449
Open
Copilot wants to merge 6 commits into
Open
Keep nullable outer-key null check in compound APPLY→JOIN predicates#38449Copilot wants to merge 6 commits into
Copilot wants to merge 6 commits into
Conversation
… APPLY to JOIN Co-authored-by: AndriySvyryd <6539701+AndriySvyryd@users.noreply.github.com>
…Server baselines, add regression test Co-authored-by: AndriySvyryd <6539701+AndriySvyryd@users.noreply.github.com>
…n fix Co-authored-by: AndriySvyryd <6539701+AndriySvyryd@users.noreply.github.com>
Copilot
AI
changed the title
[WIP] Fix nullable navigation properties leading to unexpected outer join
Keep nullable outer-key null check in compound APPLY→JOIN predicates
Jun 18, 2026
There was a problem hiding this comment.
Pull request overview
Fixes incorrect results for collection navigations reached via an optional key when the correlated APPLY is converted to a JOIN with a compound predicate (e.g., additional filters), by ensuring outer nullable-key null checks aren’t stripped in cases where SqlNullabilityProcessor applies C# null-semantics expansion.
Changes:
- Updated
SelectExpression.RemoveRedundantNullChecksto retain nullable outer-key null checks when the extracted join predicate is compound. - Added a regression test covering filtered collections through an optional navigation, with provider-specific SQL assertions (SqlServer/Sqlite).
- Updated affected SQL baselines across GearsOfWar suites (SqlServer + Sqlite) to reflect the new join predicate shape.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs | Keeps nullable outer-key null checks for compound extracted join predicates to prevent incorrect null matching. |
| test/EFCore.Specification.Tests/Query/AdHocNavigationsQueryTestBase.cs | Adds regression test for filtered collection through optional navigation not matching on null keys. |
| test/EFCore.SqlServer.FunctionalTests/Query/AdHocNavigationsQuerySqlServerTest.cs | Adds SQL assertion baseline for the new regression test (SqlServer). |
| test/EFCore.Sqlite.FunctionalTests/Query/AdHocNavigationsQuerySqliteTest.cs | Adds SQL assertion baseline for the new regression test (Sqlite). |
| test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs | Updates SQL baselines to include retained outer-key null checks / simplified predicates. |
| test/EFCore.SqlServer.FunctionalTests/Query/TemporalGearsOfWarQuerySqlServerTest.cs | Updates temporal SQL baselines to include retained outer-key null checks / simplified predicates. |
| test/EFCore.SqlServer.FunctionalTests/Query/Inheritance/TPTGearsOfWarQuerySqlServerTest.cs | Updates TPT SQL baselines for the adjusted join predicates. |
| test/EFCore.SqlServer.FunctionalTests/Query/Inheritance/TPCGearsOfWarQuerySqlServerTest.cs | Updates TPC SQL baselines for the adjusted join predicates. |
| test/EFCore.Sqlite.FunctionalTests/Query/GearsOfWarQuerySqliteTest.cs | Updates Sqlite SQL baselines for the adjusted join predicates. |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: AndriySvyryd <6539701+AndriySvyryd@users.noreply.github.com>
cincuranet
approved these changes
Jun 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #35706
A collection navigation reached through an optional (nullable) key returned wrong results when combined with an additional filter: entities with a
NULLkey matched each other instead of yielding an empty collection.Root cause:
SelectExpression.RemoveRedundantNullChecksalways stripped the outer-keyIS NOT NULLcheck. For a single-comparison join that's safe (SqlNullabilityProcessor.ProcessJoinPredicatekeeps SQL null semantics), but for a compound predicate the processor expands the equality with C# null semantics — addingOR (both sides NULL)— which then matches rows where both keys areNULL.Changes
SelectExpression.RemoveRedundantNullChecks: retain the null check on a nullable outer key when the join predicate is compound (more than one key comparison). Single-comparison joins are unchanged. A smallCountKeyComparisonshelper distinguishes the two cases.outerKey IS NOT NULL AND ...instead of(outerKey = innerKey OR (both NULL)) AND ....Filtered_collection_through_optional_navigation_does_not_match_on_null_keys(specification base + SqlServer/SQLite SQL assertions).Notes for review