SQL on FHIR WG Meetings
SQL on FHIR WG Meeting — November 25, 2025
Arjun Sanyal
Arjun Sanyal
Principal Antidote Solutions
Nikolai Ryzhikov
Nikolai Ryzhikov
CTO at Health Samurai
John Grimes
John Grimes
Principal Research Consultant CSIRO
Nov 25, 2025

Topics discussed:

  • Nikolai's core complaint is that the draft reads as though it was written with an architecture in mind that never made it onto the page. All those redirects only earn their keep in a distributed system — a task server here, a status server there. If you aren't distributed, a monolithic implementer is simply made to build two endpoints where one would do, and clients pay an extra hop for nothing. Say the architecture out loud in the spec and people can at least agree it's justified; leave it out and the design looks unreasonable. He sent Josh that set of questions privately but wants the discussion to end up public, because everyone should be in it.
  • The hypermedia argument is the sharp one. If the thing is genuinely hypermedia-driven, Nikolai argued, the server should hand you both a status URL and a cancellation URL, because in a real distributed system those may be different components — the one running the job and the one tracking it. Instead there's a convention that you DELETE the status URL to cancel. And if the status endpoint can accept a cancellation, why can't it just return the result? In Richardson Maturity Model terms, FHIR sits at level 2 — resource-aware and method-aware but not hypermedia-driven — and the draft is pretending at level 3 while leaning on level-2 conventions. Pick one.
  • Progress despite all that: the redirect now returns 303 rather than 200 plus location; Josh isn't against a server that already has the answer replying 200 with a body; and Steve backed the idea that a poll may return a partial result — on a long export, if the first file is ready, why not tell the client. What's still unresolved is errors, and specifically how to tell a status-polling failure apart from a job failure. John's worry is broader: async schemes are proliferating — legacy bulk data, this draft, a new bulk submit proposal for import — and if the new one isn't technically compatible with what exists, everyone ends up implementing async twice. He'd like one pattern to rule them all, with switches rather than rivals.
  • A nice reversal on ordering. Arjun assumed row order would be non-deterministic, as it is in SQL. Nikolai's correction: a relation is a set, so SQL gives you no order — but a FHIR resource is made of arrays, and arrays are ordered, so unrolling one with forEach or repeat is deterministic, and sometimes the order carries meaning, as when you unroll a List. John's caveat from the Spark side is that his implementation is non-deterministic unless you explicitly sort, since rows may have been processed on another worker and simply chucked in at the end — which is exactly the argument for an explicit row index, SQL's row_number, rather than relying on luck.
  • John shipped repeat in Pathling with one caveat: unlimited nesting everywhere except nested extensions. The reason is that recursion knows when to stop by comparing structures, and level one genuinely differs from level two — but extensions aren't stored that way. Carrying every possible extension structure in every element would be an insane amount of structure and would wreck performance, so each element carries a field ID that joins out to a map holding the extension structure once. That trick doesn't survive the structure-comparison recursion, so extensions get a configurable limit. Nikolai's use case for repeat, meanwhile, is walking hierarchical terminologies where you need the parent code to join against — which John noted is almost everything except SNOMED, where hierarchy walking is nearly an anti-pattern, but a big deal for the likes of ICD.