Background
During code review of PR #9, it was noted that Context.nullable is tracked and propagated correctly in forEach.ts (line 81) when a forEachOrNull node is encountered, but it is never consumed — operators such as repeat and potentially forEach itself always emit INNER JOIN / CROSS APPLY regardless of whether a forEachOrNull ancestor is in scope.
Problem
When a repeat (or forEach) node is nested inside a forEachOrNull, the outer OUTER APPLY may produce null rows for resources that have no matching items. Any inner INNER JOIN or CROSS APPLY generated by the nested operator will silently drop those null rows, producing incorrect results for nullable paths.
Investigation needed
- Confirm the scope of the issue — does it affect only
repeat, or also nested forEach inside forEachOrNull?
- Check the reference implementation — does the SQL-on-FHIR reference implementation (
sqlonfhir submodule / sof-js) exhibit the same behaviour for these cases? If so, it may be a known limitation or intentional spec behaviour rather than a bug.
- Check existing test cases — do the current
forEachOrNull test fixtures in the test suite cover nested repeat/forEach inside forEachOrNull? If the reference implementation and the current code agree, the tests may be passing with the same (potentially incorrect) output.
- Clarify expected SQL-on-FHIR behaviour — the spec should define whether rows with no matching nested items must appear as null rows or may be omitted entirely.
Related
Background
During code review of PR #9, it was noted that
Context.nullableis tracked and propagated correctly inforEach.ts(line 81) when aforEachOrNullnode is encountered, but it is never consumed — operators such asrepeatand potentiallyforEachitself always emitINNER JOIN/CROSS APPLYregardless of whether aforEachOrNullancestor is in scope.Problem
When a
repeat(orforEach) node is nested inside aforEachOrNull, the outerOUTER APPLYmay produce null rows for resources that have no matching items. Any innerINNER JOINorCROSS APPLYgenerated by the nested operator will silently drop those null rows, producing incorrect results for nullable paths.Investigation needed
repeat, or also nestedforEachinsideforEachOrNull?sqlonfhirsubmodule /sof-js) exhibit the same behaviour for these cases? If so, it may be a known limitation or intentional spec behaviour rather than a bug.forEachOrNulltest fixtures in the test suite cover nestedrepeat/forEachinsideforEachOrNull? If the reference implementation and the current code agree, the tests may be passing with the same (potentially incorrect) output.Related
Context.nullabledeclaration:src/queryGenerator/treeWalker/types.ts:53src/queryGenerator/treeWalker/operators/forEach.ts:81repeat:src/queryGenerator/treeWalker/operators/repeat.ts:71