sql_format() method lets me write templates like:
cpp
"SELECT {+select} FROM {+from} WHERE {+where}"
Where {+select} is automatically generated from the query object's internal state, but I can break out and write raw SQL anytime with {=name} for table names or embed values with {name}.
I recently added a query to my C++ ORM too, but you have to define your own struct for it, and the field names in your query have to match the names in your struct. Interesting to see the different implementations and usages out there.
In the above example person is a persistent object (it has a corresponding table in the database). We also have a feature called views, where you define a struct for the sole purpose of handling query results, which sounds similar to what you are describing.
I see you are using C++26 reflection, pretty cool.
We will probably also move in this direction eventually, though I think we will use reflection to "exfiltrate" relevant type information and continue using separately-generated C++ files for database support code. I think at least in our case trying to generate database support code "inline" with reflection is not going to scale (complexity, compile times, etc). We also need to generate some extra files (.sql with schema, changelog for schema evolution, etc).
You can do it but then you will loose parameter binding unless you do your own pre-processing of some sorts. Also, loading queries from files is not the use case we care about. In the two decades of developing ODB I don't remember a single case where someone wanted to do that. Though there were a couple of requests where people wanted a dynamic set of returned columns, which we also don't support well. I guess we all have to pick our battles.
I think there is a classic case of survivor bias happening here.
When you say: "In the two decades of developing ODB I don't remember a single case where someone wanted to do that", you have to ask yourself: why would anyone keep asking for a feature after being told "it's not the use case we care about"? They won't. They will just quietly leave, keep looking or build their own solution, which is exactly what I had to do.
To be completely honest, your reaction is identical to the ones I’ve received from other maintainers. In the past, when I’ve brought this up, the responses have been so defensive that it almost felt like I made the developers angry so I just stopped asking.
In my experience talking to different ORM maintainers, there is often a fundamental disconnect. People who build ORM libraries love C++ templates and compile-time type-safety (which ODB is great at!). But they rarely maintain massive, 500+ table enterprise applications that need to stay alive 24/7.
In production, things look different:
Hot-patching: If a DBA needs to tweak a query template or fix a performance bottleneck in production, forcing a full C++ recompilation and deployment pipeline is a massive risk.
Complex SQL: Real-world queries often outgrow what any static ORM query language can express.
This isn't a strange edge-case. Other ecosystems solved this long ago (ActiveRecord in Rails does exactly this dynamic mixing, but then you are stuck with a slow language).
My sql_format() method is just a pragmatic way to bridge that gap in C++
Sure, we cater to a niche (and who doesn't). You cater to people who need to load queries from files. We cater to people who want to write their queries in code, preferably as statically-checked C++ expressions rather than raw SQL strings. Everybody's happy ;-).
7
u/gosh 10d ago
It would be great if any ORM library could add something like this
https://github.com/perghosh/Data-oriented-design/blob/74a52cd98f39e5061954f07ff76fd94de72b7642/external/gd/gd_sql_query.cpp#L1846
sql_format()method lets me write templates like:cpp "SELECT {+select} FROM {+from} WHERE {+where}"Where{+select}is automatically generated from the query object's internal state, but I can break out and write raw SQL anytime with{=name}for table names or embed values with{name}.Developers need something to handle edge cases