Skip to content

[release/11.0] Lift mixed outer-reference aggregate arguments - #38925

Open
AndriySvyryd with Copilot wants to merge 1 commit into
release/11.0from
copilot/servicing-pr-38855
Open

[release/11.0] Lift mixed outer-reference aggregate arguments#38925
AndriySvyryd with Copilot wants to merge 1 commit into
release/11.0from
copilot/servicing-pr-38855

Conversation

CopilotAI commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Fixes#38834
Port of #38855

SQL Server rejects aggregate expressions combining an outer reference with a local column, causing valid LINQ queries to fail with error 8124.

  • Query translation: Detect mixed local and outer references in aggregate arguments and lift the expression through OUTER APPLY or CROSS JOIN.
  • Postprocessor: Rename and extend the aggregate postprocessor while preserving existing subquery lifting behavior.
  • Regression coverage: Add tests for direct mixed-reference aggregates and aggregates over projected single-result subqueries.
vartotals=context.Orders.Select(o =>o.OrderDetails.Sum(d =>d.ProductId*o.OrderId));

- Track, per aggregate invocation, whether the argument references a column of
the SELECT the aggregate is evaluated in (local) and whether it references a
column from further out (outer), and lift the argument when both are present.
The postprocessor only lifted arguments containing a subquery, so these
expressions reached the server unmodified and failed with "Multiple columns
are specified in an aggregated expression containing an outer reference"
- Rename SqlServerAggregateOverSubqueryPostprocessor to
SqlServerAggregateArgumentPostprocessor, since a subquery in the argument is
no longer the only thing that triggers a lift
- Compare against the aggregating SELECT's own table aliases rather than
_tableAliasesInScope: that set deliberately excludes those tables, since a
lifted subquery reaches them through APPLY rather than CROSS JOIN
- Leave an aggregate whose argument is purely an outer reference alone; SQL
Server evaluates it in the outer query, which is how aggregates over an outer
grouping are meant to translate
- Restore the parent visitor state before building the lifted subquery, and
capture _isCorrelatedSubquery for the OUTER APPLY/CROSS JOIN choice, so the
state saved by an enclosing aggregate is no longer clobbered
- Add specification tests for a Sum over an expression with an outer reference
and for Sums over members of a single-result subquery, plus SQL Server
baselines
Fixes#38834
Co-authored-by: AndriySvyryd <6539701+AndriySvyryd@users.noreply.github.com>
CopilotAI changed the title [release/11.0] Lift aggregate arguments that mix an outer reference with a local column[release/11.0] Lift mixed outer-reference aggregate argumentsSep 5, 2026
@AndriySvyryd
AndriySvyryd requested a lite review from CopilotSeptember 5, 2026 01:15
@AndriySvyryd
AndriySvyryd marked this pull request as ready for review September 5, 2026 01:15
@AndriySvyryd
AndriySvyryd requested a review from a team as a code ownerSeptember 5, 2026 01:15

CopilotAI 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.

🟡 Changes recommended

The new relational spec test dereferences a nullable FirstOrDefault() result (d.OrderID) and will fail the build due to warnings-as-errors.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR addresses a SQL Server limitation where aggregate arguments that mix an outer reference with a local column (SQL error 8124) produce invalid SQL, by detecting that pattern during postprocessing and lifting the aggregate argument via OUTER APPLY/CROSS JOIN. It also adds regression coverage for both the direct mixed-reference case and the single-result-subquery projection case.

Changes:

  • Extend/rename the SQL Server aggregate argument postprocessor to lift arguments that contain both local and outer references (in addition to the existing “contains subquery” trigger).
  • Fix visitor state handling for nested aggregates by restoring parent state before performing the lift while capturing correlation state.
  • Add relational spec tests and SQL Server baselines covering mixed outer/local aggregate arguments and the single-result-subquery member projection regression.
File summaries
FileDescription
test/EFCore.SqlServer.FunctionalTests/Query/NorthwindAggregateOperatorsQuerySqlServerTest.csAdds SQL Server baselines asserting the lifted OUTER APPLY shape for the new regression tests.
test/EFCore.Relational.Specification.Tests/Query/NorthwindAggregateOperatorsQueryRelationalTestBase.csAdds two new relational spec tests covering mixed outer-reference aggregate arguments (direct and via projected single-result subquery).
src/EFCore.SqlServer/Query/Internal/SqlServerQueryTranslationPostprocessor.csSwitches to the renamed/extended aggregate argument postprocessor.
src/EFCore.SqlServer/Query/Internal/SqlServerAggregateArgumentPostprocessor.csImplements mixed local/outer reference detection and lifts aggregate arguments to avoid SQL Server error 8124.
Review details
  • Files reviewed: 4/4 changed files
  • Comments generated: 1
  • Review effort level: Lite

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

select new
{
Products = d!.ProductID * c.CustomerID.Length,
Orders = d.OrderID * c.CustomerID.Length
Sign up for freeto 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.

3 participants

@AndriySvyryd