Fold single-table filter subqueries into joins on SQLite#38692
Open
Aykuttonpc wants to merge 1 commit into
Open
Fold single-table filter subqueries into joins on SQLite#38692Aykuttonpc wants to merge 1 commit into
Aykuttonpc wants to merge 1 commit into
Conversation
EF translates a query that joins onto a filtered table (e.g. an optional navigation with a Where, or a SelectMany with a predicate) into a LEFT/INNER JOIN over a subquery: LEFT JOIN (SELECT ... WHERE ...) AS t ON .... When the subquery only filters a single table, that filter can move onto the join condition instead, dropping the subquery. Add SqliteSubqueryToJoinRewriter, run from SqliteQueryTranslationPostprocessor, which rewrites such joins into LEFT/INNER JOIN <table> ON <key> AND <filter>, inlining the subquery's projection into the containing SELECT. The rewrite is skipped whenever it could change results: when another join references the subquery's alias, when the join key depends on a sibling join, when a LEFT JOIN projection isn't made purely of columns (can't be made nullable), or when the predicate is only IS NOT NULL checks (an optional-entity existence test rather than a value filter). Part of dotnet#33745
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.
When a query filters a joined table (an optional navigation with a
Where, aSelectManywith a predicate, etc.), EF currently emits a join over a subquery even when the subquery does nothing but filter a single table:Since the filter references only the inner table, it can move onto the join condition and the subquery can go away:
This adds
SqliteSubqueryToJoinRewriter, run fromSqliteQueryTranslationPostprocessor, which does exactly that: rewrites a LEFT/INNER JOIN over a single-table filter subquery into a join over the table, folding the filter into the ON condition and inlining the subquery's projection.Where it deliberately does nothing
Working through the test suite surfaced several cases where folding the filter out would change results, so the rewrite skips them:
IS NOT NULLchecks — that's an optional-entity existence test (table splitting), not a value filter; the containing query uses it to decide whether the entity is present.Scope
This is implemented in the SQLite provider only. The underlying reasoning is provider-agnostic (it's about relational join semantics), so it could move to the relational layer to benefit all providers, but I kept it local to SQLite so it could be validated against the full SQLite functional suite. Happy to move it up if that's the preferred direction.
Tests
Full SQLite functional suite passes: 38367 tests, 0 failures. 97 query baselines lost their subqueries (net −334 lines in the affected test files, e.g.
GearsOfWarQuerySqliteTestshrank noticeably). No data or nullability regressions — each skip condition above was added specifically because a test showed the rewrite was otherwise changing results.Part of #33745