> After studying the question a bit more, I realize that there is good
> reason for the C API to separate statement preparation from statement
> binding; preparation does all the parsing and compiling to bytecode,
> which is one of the more expensive parts of sending a query, whereas
> binding substitutes values for the parameters.
>
> As I don't want to expose the prepared-statement foreign objects in the
> API, a practical (as opposed to POC) implementation will need to provide
> a statement cache. Most SQLite interfaces do this, but it is not part
> of SQLite itself. The cache is basically a bounded-size mapping that
> maps SQL strings (or hashes of them) to the internal prepared-statement
> objects. When the cache is full, an element is kicked out and the
> corresponding prepared-statement object is freed.
>
> So when we call -exec/-eval, the cache is searched; on a miss, we
> prepare the statement and add it to the cache. Then it is bound and
> executed.
So a table where SQL strings are weak references pointing to sqlite_stmt
foreign objects? And it's probably good enough to compare the strings
using eq?.
If you want to optimize for speed, then (sql-do `("mumble @a @b @c
mumble" a ,a b ,b c ,c)) is a bit wasteful as well. SQLite uses
positional parameters internally, and the user must convert named
parameters to positional ones using sqlite3_bind_parameter_index().
If the statement stays the same, then the name->position mapping stays
the same as well and should be cached. Further, destructuring an
alist/plist on every call to unpack parameter values is unnecessary effort.