You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
RIGHT [OUTER] JOIN and FULL [OUTER] JOIN against base / view / system-table right sides (parser cases for both keyword spellings; lateral / derived-table right raises NotSupportedException since real SQL Server's Msg 4104 rejection of correlated-right can't be statically distinguished from non-correlated). JoinDriver refactored from level-recursive to an operator-pipeline fold over joins[]: INNER/CROSS/LEFT/CROSS-APPLY/OUTER-APPLY stream, RIGHT/FULL materialize right rows + track a matched bitmap across the upstream iteration, then emit unmatched-right rows with NULL-filled left slots after upstream exhausts. EF Core 10 LINQ LeftJoin/RightJoin operators covered end-to-end; FullJoin has no .NET 10 LINQ counterpart so FULL OUTER stays raw-SQL-only.
`Selection.cs` + `Selection.Execution.cs` are a partial-class pair. `Parse → Selection`, `Execute → SimulatedSqlResultSet`. Correlated subqueries re-run the same plan per outer row via `outerResolver: Func<MultiPartName, SqlValue>?` (execute) and `outerTypeResolver: Func<MultiPartName, SqlType>?` (parse). Both walk arbitrary nesting depth via `ParserContext.OuterTypeResolver` + the runtime arg. **Derived tables in FROM are always deferred** (`FromSource.LateralPlan` is re-executed per outer row), matching SQL Server's "any FROM derived table can correlate" rule — required because outer references in WHERE/ON resolve through `Run`, not `GetSqlType`.
47
47
48
48
### Multi-source rows
49
-
`FromSource[]`; rows during enumeration are `byte[]?[]`, one slot per source, null = unmatched LEFT JOIN right side. Column resolution is qualifier-aware via `FindSourceColumn` / `ResolveAcrossTuple`; ambiguous unqualified name → Msg 209.
49
+
`FromSource[]`; rows during enumeration are `byte[]?[]`, one slot per source, null = NULL-filled outer-join side (LEFT/RIGHT/FULL/OUTER APPLY). Column resolution is qualifier-aware via `FindSourceColumn` / `ResolveAcrossTuple`; ambiguous unqualified name → Msg 209.
50
50
51
51
### `MultiPartName`
52
52
Readonly struct, up to 4 inline slots (SQL Server's grammar limit). API: `Leaf`, `ImmediateQualifier` (null when unqualified — pair with `Collation.Default.Equals(name.ImmediateQualifier, "INSERTED")`, the equality folds null into `false`), `Count`, `ToString()`. 5th segment → Msg 4104.
@@ -95,7 +95,9 @@ Five scopes, one home each. **Add new state to whichever class matches its true
95
95
The `*.Tests` and `*.Tests.EFCore` suites are the authoritative behavior contract. Notes below cover only probe-confirmed quirks, deviations from SQL Server, and non-obvious implementation rules. Per-feature deep-dives live under `docs/claude/` (see [Feature reference](#feature-reference) for the trigger-phrased index); short cross-cutting sections stay inline below.
96
96
97
97
### JOINs / APPLY
98
-
INNER / bare JOIN / LEFT [OUTER] / CROSS / CROSS APPLY / OUTER APPLY. Multi-table chains compose left-to-right. ON-predicate UNKNOWN excludes. APPLY = lateral form (right side re-executed per outer row, no ON clause).
98
+
INNER / bare JOIN / LEFT [OUTER] / RIGHT [OUTER] / FULL [OUTER] / CROSS / CROSS APPLY / OUTER APPLY. Multi-table chains compose left-to-right. ON-predicate UNKNOWN excludes. APPLY = lateral form (right side re-executed per outer row, no ON clause).
99
+
100
+
`JoinDriver` is a fold over `joins[]`: leftmost rowset → wrap with each join's operator → final enumerator (`Selection.Execution.Joins.cs`). INNER / CROSS / LEFT / CROSS APPLY / OUTER APPLY stream one upstream tuple at a time; RIGHT / FULL materialize `sources[level].Rows` into a list and track a `matched[]` bitmap across the entire upstream iteration so unmatched right rows can be emitted (with all prior slots NULL-filled) after upstream is exhausted. **RIGHT / FULL with a derived-table or lateral right side raise `NotSupportedException`** — every derived table is deferred regardless of actual correlation, and real SQL Server rejects correlated subqueries on the right of RIGHT/FULL with Msg 4104. EF Core 10's LINQ `LeftJoin` / `RightJoin` operators translate to LEFT / RIGHT JOIN respectively and route through this pipeline; .NET 10 LINQ doesn't expose a `FullJoin` operator, so FULL OUTER JOIN is reachable only via raw SQL.
99
101
100
102
### Subqueries
101
103
`EXISTS` / `NOT EXISTS` (multi-column inner allowed); `expr [NOT] IN (SELECT ...)` (single inner column, Msg 116); scalar `(SELECT col FROM ...)` (single column, single-row Msg 512 per outer row, empty → typed NULL). All forms work correlated and non-correlated, arbitrary nesting depth.
@@ -151,7 +153,7 @@ Per-feature deep-dives live under `docs/claude/`. Each entry below is a trigger:
-`RIGHT JOIN`(rewrite as LEFT with sources swapped) and `FULL OUTER JOIN` — both raise `NotSupportedException`at parse.
156
+
-`RIGHT JOIN`/ `FULL OUTER JOIN`with a derived-table or lateral right side — base tables / views / system tables on the right ship (see JOINs / APPLY); a derived-table right (always-deferred in this simulator) raises `NotSupportedException`since real SQL Server's Msg 4104 rejection of correlated subqueries on the right of RIGHT/FULL can't be statically distinguished from non-correlated ones.
155
157
- Comma-separated FROM (legacy ANSI-89 join syntax).
0 commit comments