Skip to content

Implement nested repeat directive (#8)#9

Open
piotrszul wants to merge 7 commits intomainfrom
issue/8_nested_repeat
Open

Implement nested repeat directive (#8)#9
piotrszul wants to merge 7 commits intomainfrom
issue/8_nested_repeat

Conversation

@piotrszul
Copy link
Copy Markdown
Collaborator

Summary

Closes #8.

Implements the repeat directive with no positional constraints beyond the spec's per-select-node mutual exclusion of forEach / forEachOrNull / repeat. Wherever a select node is structurally legal, a select node carrying repeat is now handled correctly by the query generator.

Changes

  • Tree-walker query generator — replaces the processor-based architecture (ForEachProcessor, RepeatProcessor, SelectClauseBuilder, SelectCombinationExpander) with a recursive tree-walker in src/queryGenerator/treeWalker/. Context (current JSON source, partition keys, accumulated APPLY chain) threads through descent, eliminating the previous post-hoc rewiring step that couldn't represent complex nesting relationships.
  • Submodule bumpsqlonfhir advances to aehrc/sql-on-fhir-v2:repeat-updates (commit 27ee2c6), which adds twelve test cases covering the full structural surface of the operator: linear nestings, sibling combinations, triple-nested mixed combinations, and operator-pairing coverage (unionAll inside repeat, multi-path repeat inside forEach, etc.). The submodule URL is repointed to the aehrc fork pending the upstream PR to FHIR/sql-on-fhir-v2.

Test plan

  • All 19 tests in sqlonfhir/tests/repeat.json pass against the MSSQL implementation.
  • All 19 tests pass against the sof-js reference implementation.
  • Open upstream PR on FHIR/sql-on-fhir-v2 for the new test cases.

piotrszul and others added 4 commits May 1, 2026 18:56
Points the sqlonfhir submodule at the new repeat-updates branch on
aehrc/sql-on-fhir-v2 (commit 27ee2c6) which adds twelve test cases
expanding repeat-operator coverage. Sets the submodule URL to the aehrc
fork so this branch tracks against the staging remote pending the
upstream PR to FHIR/sql-on-fhir-v2.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace the processor-based query generator (ForEachProcessor, RepeatProcessor,
SelectClauseBuilder, SelectCombinationExpander) with a new tree-walker compiler
in src/queryGenerator/treeWalker/. The new approach walks the ViewDefinition
select tree recursively, classifying nodes, generating CTE templates, and
rendering T-SQL, making the repeat/forEach nesting logic clearer and easier
to extend.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Drops fromClause, rowOrigin/RowOrigin, fromAlias, nullableHere, and the
ScalarColumn/Context.scalarColumns machinery that was never populated.
Also removes the stale USE_TREE_WALKER gate reference from index.ts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@piotrszul
Copy link
Copy Markdown
Collaborator Author

Code review

Found 1 issue:

  1. Context.nullable is tracked but never consumed in repeat.tsrepeat nodes nested under forEachOrNull always use INNER JOIN to the CTE, which will drop the null rows that OUTER APPLY from forEachOrNull preserves. The nullable field exists on Context precisely to drive this join-type decision, and it is correctly propagated in forEach.ts (line 81), but buildJoinClause in repeat.ts hardcodes INNER JOIN regardless of ctx.nullable.

}
/**
* Outer-SELECT join condition aligning this CTE's rows with the enclosing
* partition (resource id plus any forEach/repeat keys above this scope).
*/
function buildJoinClause(cteAlias: string, ctx: Context): string {
const joinConditions = ctx.partitionKeys

See also the declaration in types.ts:

resourceAlias: string;
/** Current JSON source expression, e.g. "r.json", "forEach_0.value". */
source: string;
/** Ordered, monotonically appended as the walker descends. */

🤖 Generated with Claude Code

- If this code review was useful, please react with 👍. Otherwise, react with 👎.

piotrszul and others added 2 commits May 4, 2026 20:33
…ers, trim barrel exports

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-project-automation github-project-automation Bot moved this to Backlog in Pathling May 4, 2026
@piotrszul piotrszul moved this from Backlog to In progress in Pathling May 4, 2026
@piotrszul piotrszul requested a review from johngrimes May 4, 2026 23:29
Copy link
Copy Markdown
Member

@johngrimes johngrimes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @piotrszul!

I noticed a few minor issues.

Comment thread src/queryGenerator/treeWalker/operators/repeat.ts
Comment thread src/queryGenerator/treeWalker/render.ts Outdated
Comment thread src/queryGenerator/treeWalker/mergeSiblings.ts Outdated
Comment thread src/queryGenerator/treeWalker/operators/unionAll.ts Outdated
Comment thread src/queryGenerator/treeWalker/compile.ts
- Fix `joins` → `fromExtensions` in repeat.ts module comment
- Fix render.ts template comment to show single `<fromExtensions>` slot
- Rewrite mergeSiblings.ts header to describe actual behaviour
- Fix unionAll.ts seed alias comment (_seed_X → _seed) with note on why
  no unique suffix is needed (UNION ALL branches have independent scopes)
- Add @param/@returns/@throws JSDoc to all 12 exported treeWalker functions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In progress

Development

Successfully merging this pull request may close these issues.

Implement nested repeat directive

2 participants