From: John Cowan <xxxxxx@ccil.org>Date: Thursday, September 19, 2019 2:46 PMOn Wed, Sep 18, 2019 at 10:32 PM <xxxxxx@ancell-ent.com> wrote:(open-sql-database filename mode) -> dbmode is symbol: read, write, create
This is still SQLite specific. I've been thinking a more general open should return a "connection", since that's what it generally is, and you may find it useful to open more than one connection to a non-embedded database. For that matter, you can do the same with SQLite, can't you? Won't it do the right thing, while only allowing one writer or write at a time?Thus filename should be something like connect-string, magic-string, whatever, it's content is by convention and for the lowest level of the stack to understand. mode should be optional and something like the SRFI-167 Ordered Key Value Store (OKVS) config, there an association list, which for SQLite would have one item, 'mode, being 'read, 'write, or 'create.Fixed. However, I have added a database-type symbol that specifies which driver to use.
Are the following uses of timespecs intervals, instead of a point in time since an epoch? The latter is the only thing they're currently defined to be in SRFI-174.Well, the epoch begins when the database call begins. Yes, it's hacky.
[...](sql-in-transaction db thunk) -> what thunk returns
When proc returns, roll back unless sql-commit has been called.
Why not assume success and commit on the completion of the thunk, and require an explicit rollback? That's what the PostgreSQL egg does, and what I'm planning on doing for my DBI. See this modification of an example supplied by Peter Bex:I can't make up my mind what's best here. Can you provide some arguments?
(with-transaction db(lambda ()(query db "INSERT INTO foo (bar, qux) VALUES (1, 2)")(with-transaction(lambda ()(query db "INSERT INTO foo (bar, qux) VALUES (3, 4)");; Oops, decide to roll this back(rollback)))(query db "INSERT INTO foo (bar, qux) VALUES (5, 6)")))
(transaction(if-not (valid?)(rollback)(do-complicated-query))(when-not (is-rollback?)(println "success!")))
Per the above, I don't think this is needed. Rollback is definitely needed, and if you make commit the default, you can reduce the API surface by remove this manual commit.I think it's better to allow being explicit.
(sql-rollback db) -> unspecified
Per the above, a call to the rollback procedure gets you to the containing with-transaction, which already has db.You might be inside transactions on two connections at the same time, as when you are copying data from one db to another.
(sql-statement db code bindings) -> statement
It is an error to mutate either code or bindings; this enables caching of either compiled but unbound statements, fully bound statements, or both.
I've been thinking that while there should be a sane default, you ought to be able to provide a perhaps per-database type (SQLite, PostgreSQL, etc.) hint, suggestion, or order to prepare the supplied statement.I don't understand. I think what you are saying is what this is already meant to do. This is preparation as part of the per-db API, as opposed to the SQL PREPARE statement.
I assume the specifics of code and bindings are still up in the air based on the in progress discussions?Well, code is meant to be SQL, at least so far, possibly with question marks, since named parameters are so disappointing.
(sql-exec db statement) -> implementation-dependent
Use this procedure when statement does not return a result set.
(sql-result-set db statement thunk) -> whatever thunk returns
Executes statement and calls thunk, passing a result-set object.
And here you ought to be able to provide a hint, suggestion or order to get the full result set, or play the cursor game underneath the API.If you want to process the whole result set, use sql-for-each or sql-map->list. APIs providing access to the whole result set (apparently all except SQLite) can use their bulk API here.
[ Null is 'null, "which is what consensus JSON parsers use." ][...]One thought I've had is that to the extent you can't introspect this information, the API can provide a facility for keeping a (hopefully in-sync) copy of what [ column type data is ] missing.You should be able to read it from some database-type-dependent table(s). In SQLite it's sqlite_master.
[...]