sqlite3 API considered harmful #248
Replies: 2 comments
-
Ha! Indeed, but the sentiment behind it is spot on. This is a topic which has consumed many of my nights, both with JS and Java/JNI, and my reason for sticking with, and promoting, "thin wrappers" boils down to precisely the points that you summarize:
We in the SQLite project collectively feel that there is no One Best Wrapper, and that an interested community is the best place to evolve "the best" wrapper for any given context. The low-level API provides a solid basis for exploring an endless variety of high-level wrappers, and that is a compelling reason (for me, anyway) to provide as-thin-as-possible bindings, as opposed to trying to find an ideal shape for any given high-level wrapper. More often than not, adding a level to a high-level wrapper causes a lower-level feature to be lost in the translation. (And that's fine, provided the target for that wrapper doesn't need that capability. WebSQL, for example, shaved off lots of "sharp edges" in its wrapper, but that also eliminated access to any and all power-user features. In its designers' defense, though, they were attempting to provide a generic SQL wrapper, not an SQLite wrapper.) In the cases of WASM and JNI, the criss-crossing across language/runtime boundaries certainly brings a performance hit with it, but my suspicion1 is that as WASM evolves, more and more of client-side code will end up in the WASM side of the constellation, reducing this implicit performance hit as code migrates to the other side of the runtime boundary. 0.02€ and all that. Footnotes
|
Beta Was this translation helpful? Give feedback.
-
Designing a good API for this is indeed challenging. I worked on a project that attempts to define "cross-platform" JS SQLite APIs last year, but never got around to get a polished version out. The work-in-progress is here, also with some outdated wa-sqlite support (builds on top of the wa-sqlite APIs, instead of re-defining them). The goal was a little different, since the idea was to get one small set of APIs that can be implemented by any SQLite driver. The tricky parts were:
It's a little different from the use case for wa-sqlite:
That said, I think there are a lot of similarities, so I can give you my findings so far:
I've found the same thing. In the JS world, there are many JS bindings out there for various platforms that leave so much to be desired in terms of low-level capabilities. For JS you unfortunately need higher-level wrappers to properly work with its asynchronous nature, which makes the balance very difficult. Many bindings go the wrong direction, exposing (often broken) APIs for transactions for example, instead of exposing better low-level functionality. There is another project attempting to standardize database APIs in JS here: halvardssm/stdext#6. The goal there is for cross-database support though, so I think it's a little less relevant here, although there are some interesting discussions. |
Beta Was this translation helpful? Give feedback.
-
The title is clickbait. This is not a criticism of SQLite but instead of how people (like me, like the official SQLite WASM library, and like the original sql.js we were inspired by) are exporting it from WebAssembly to Javascript. We basically just expose the sqlite3 C API as-is with some thin Javascript wrappers for type conversions. That works surprisingly well, minimizes design effort, minimizes documentation, and is familiar to anyone who has previously written a sqlite3 program. I also think it is slow.
sqlite3 is a very granular API - it is mostly a set of very low-level calls, and in most cases it takes a lot of calls to satisfy a typical user request. For example, unless you use the
sqlite3_exec()
convenience function1, it takes an API call to produce each row and an API call to retrieve each column. That can get expensive when all those calls have to cross the WebAssembly-Javascript language barrier.I find the original OpenGL API (for 3D graphics) to be a good analogy. You had to make a call to send each type of data for each vertex - so a call for position, a call for color, a call for texture coordinates, for every geometric vertex. As graphics systems evolved, the thousands of calls required to draw a single object severely impacted performance. So today's OpenGL and successor APIs are designed to function without this granularity while remaining low-level, i.e. still flexible and powerful but in a fraction of the calls. I think this needs to happen with WebAssembly SQLite to make it significantly faster.
What should such a less granular exported API look like? I don't know exactly, but here's a wish list:
I don't think this would be a huge effort but it is a challenging one. The hard part is designing a good API, which might take a few iterations. Then the implementation will take some expertise with a WebAssembly-compatible language as well as Javascript glue. It might be an opportunity for someone. I don't recommend waiting for "someone" to be me.
Footnotes
The wa-sqlite function exec() does not actually call
sqlite3_exec()
and so does not reduce the number of sqlite3 API calls. ↩Beta Was this translation helpful? Give feedback.
All reactions