Description
A conditional (if-gated) .Having(...) on a GroupBy chain split across a reassigned local variable makes the generator lose the chain root's context type. When two contexts both expose an accessor for the same entity (CteDb and TestDbContext both have IEntityAccessor<Order> Orders()), the chain binds to the wrong context and renders wrong-dialect SQL.
Previously noted only as a prose comment ("Filed as follow-up") with no tracking issue — split out of #314's finding 5.
Location
- Trailing NOTE documenting the routing-around:
src/Quarry.Tests/SqlOutput/CrossDialectConditionalMaskTests.cs (end of file, "NOTE: A conditional-Having test ... currently triggers a generator misattribution")
- Chain-root context resolution during binding:
src/Quarry.Generator/IR/CallSiteBinder.cs / src/Quarry.Generator/Parsing/ChainAnalyzer.cs (context type lost across the GroupBy/Having variable split)
Diagnostics
Repro shape:
var ltG = lt.Orders().GroupBy(o => o.Status).Select(g => (g.Key, Total: Sql.Sum(g.Value.Amount)));
if (cond)
ltG = ltG.Having(g => Sql.Count() > 1);
var results = await ltG.ExecuteFetchAllAsync();
With both CteDb and TestDbContext in the compilation exposing Orders(), the chain binds to CteDb — SQL renders with CteDb's dialect/schema instead of the actual receiver's.
What Has Been Tried
Nothing — the test that would exercise this was deliberately omitted (single-line GroupBy chains work; see CrossDialectAggregateTests). Note in the test file records the behavior.
Gathered Information
Suggested Approach
During chain-root resolution, retain the receiver's context type through builder-variable reassignment (track the local's declared/inferred builder generic arguments rather than re-resolving by entity type). Failing that, detect the ambiguity (multiple contexts exposing the entity) and emit an actionable QRY diagnostic instead of silently binding to the wrong context.
Description
A conditional (
if-gated).Having(...)on aGroupBychain split across a reassigned local variable makes the generator lose the chain root's context type. When two contexts both expose an accessor for the same entity (CteDbandTestDbContextboth haveIEntityAccessor<Order> Orders()), the chain binds to the wrong context and renders wrong-dialect SQL.Previously noted only as a prose comment ("Filed as follow-up") with no tracking issue — split out of #314's finding 5.
Location
src/Quarry.Tests/SqlOutput/CrossDialectConditionalMaskTests.cs(end of file, "NOTE: A conditional-Having test ... currently triggers a generator misattribution")src/Quarry.Generator/IR/CallSiteBinder.cs/src/Quarry.Generator/Parsing/ChainAnalyzer.cs(context type lost across the GroupBy/Having variable split)Diagnostics
Repro shape:
With both
CteDbandTestDbContextin the compilation exposingOrders(), the chain binds toCteDb— SQL renders with CteDb's dialect/schema instead of the actual receiver's.What Has Been Tried
Nothing — the test that would exercise this was deliberately omitted (single-line GroupBy chains work; see CrossDialectAggregateTests). Note in the test file records the behavior.
Gathered Information
Suggested Approach
During chain-root resolution, retain the receiver's context type through builder-variable reassignment (track the local's declared/inferred builder generic arguments rather than re-resolving by entity type). Failing that, detect the ambiguity (multiple contexts exposing the entity) and emit an actionable QRY diagnostic instead of silently binding to the wrong context.