SQL on FHIR WG Meetings

Default type hints so two runners produce columns you can SQL over — plus Nikolai on why instant should map to timestamp — Jul 15, 2025

Nikolai Ryzhikov
Nikolai Ryzhikov
CTO at Health Samurai
Arjun Sanyal
Arjun Sanyal
Principal Antidote Solutions
John Grimes
John Grimes
Principal Research Consultant CSIRO
Steve Munini
Steve Munini
CEO and CTO, Helios Software
Bashir Sadjad
Bashir Sadjad
Software Engineer at Google
Ivan Bagrov
Ivan Bagrov
Senior Software Engineer at Health Samurai
Jul 15, 2025

Ivan — $run working in Aidbox

View by reference, view definition in the body, resources passed inline, group and patient filters, _since and _limit, JSON out for now. Took about four days to build.

Nikolai Ryzhikov
Nikolai Ryzhikov
CTO at Health Samurai

Four days and you have the API.

Paging is on the to-do list and is about to be deleted from it.

John's problem — the same view, different columns on different runners

You can run one view definition on several runners and get output you can't query, because each runner quietly picked its own column types.

There is a way to hint ANSI SQL types, but nothing requires it and there's no default, so implementations have all mapped independently — which you can see in the test runners, where everyone has to be tolerant of almost anything in the expectations because the reference implementation's choices are the only de facto answer.

His fix: a default. What hint is assumed when no hint is given.

Arjun — how useful can a default be, given how differently SQL treats types?

Decimal is a problem, strict ANSI makes you pick a size for a VARCHAR — and he worried the model might be more work for implementers than the alternative.

John's answer: anyone who already supports type hinting has done this work; the only question is what's assumed in the absence of a hint. He'd defaulted nearly everything to the FHIR string form, on the argument that a good default shouldn't be lossy, and only integers, booleans and binary are genuinely safe.

Nikolai — fight for instant → timestamp

Nikolai Ryzhikov
Nikolai Ryzhikov
CTO at Health Samurai

If you call it a string, people will cast it to a timestamp anyway, and every query the group ever distributes will carry that cast.

One decent time type also unlocks the rest: lowBoundary and highBoundary plus toInstant turns a dateTime into two properly typed instants.

Bashir reframed it as a choice of goal:

Bashir Sadjad
Bashir Sadjad
Software Engineer at Google

Losslessness gives you one list of types, reducing casts gives you another. If every implementation already models something a given way in practice, a slightly imprecise default may beat a pure one.

John conceded the point:

John Grimes
John Grimes
Principal Research Consultant CSIRO

You've made a good argument.

Where it settled

  • FHIR dateTime is a range, not a point — so it can't honestly map to a SQL date, and the spec should say how to handle that rather than leave everyone to rediscover it
  • Hints can't be binding anyway, since not everything has a concrete ANSI mapping — and requiring a hint on every column was rejected as the cure being worse
  • The plan: publish as a recommendation, let other implementers beat it up, tighten later. John took the pull request, reckoning it's a table in the type hints section plus words

Smaller things settled

  • GET only works for views the server already knows — with no body you can't send a view definition or resources
  • Accept carries MIME types while _format takes the short codes, as in FHIR
  • A partitioned export reports multiple location values under one output name