World's simplest Scheme interface to sqlite3 John Cowan (29 Aug 2020 03:00 UTC)
(missing)
(missing)
Fwd: World's simplest Scheme interface to sqlite3 John Cowan (29 Aug 2020 17:57 UTC)
Re: Fwd: World's simplest Scheme interface to sqlite3 Lassi Kortela (29 Aug 2020 18:44 UTC)
Re: Fwd: World's simplest Scheme interface to sqlite3 John Cowan (29 Aug 2020 22:37 UTC)
SQL API sketch with some code Lassi Kortela (30 Aug 2020 13:24 UTC)
Re: SQL API sketch with some code Lassi Kortela (30 Aug 2020 13:46 UTC)
Re: SQL API sketch with some code John Cowan (30 Aug 2020 20:47 UTC)
Re: SQL API sketch with some code Lassi Kortela (31 Aug 2020 05:02 UTC)
Re: SQL API sketch with some code Lassi Kortela (31 Aug 2020 05:14 UTC)
Re: SQL API sketch with some code John Cowan (31 Aug 2020 15:38 UTC)
Re: SQL API sketch with some code Lassi Kortela (31 Aug 2020 15:54 UTC)
Re: SQL API sketch with some code John Cowan (31 Aug 2020 17:12 UTC)
Re: SQL API sketch with some code Lassi Kortela (31 Aug 2020 19:20 UTC)
Reflection on the database schema Lassi Kortela (30 Aug 2020 13:35 UTC)
Re: Reflection on the database schema John Cowan (30 Aug 2020 19:51 UTC)
Re: Fwd: World's simplest Scheme interface to sqlite3 Alaric Snell-Pym (31 Aug 2020 22:03 UTC)
Re: Fwd: World's simplest Scheme interface to sqlite3 Lassi Kortela (02 Sep 2020 08:38 UTC)
Re: Fwd: World's simplest Scheme interface to sqlite3 John Cowan (02 Sep 2020 17:09 UTC)
SQL statement caching Lassi Kortela (02 Sep 2020 17:21 UTC)
Re: SQL statement caching John Cowan (02 Sep 2020 18:13 UTC)
Re: SQL statement caching Lassi Kortela (02 Sep 2020 18:53 UTC)
Re: World's simplest Scheme interface to sqlite3 Lassi Kortela (29 Aug 2020 11:40 UTC)
Re: World's simplest Scheme interface to sqlite3 Ivan Raikov (29 Aug 2020 04:06 UTC)
Re: World's simplest Scheme interface to sqlite3 Lassi Kortela (29 Aug 2020 11:24 UTC)
Re: World's simplest Scheme interface to sqlite3 John Cowan (29 Aug 2020 17:47 UTC)
Re: World's simplest Scheme interface to sqlite3 Lassi Kortela (29 Aug 2020 18:23 UTC)
Re: World's simplest Scheme interface to sqlite3 John Cowan (29 Aug 2020 22:15 UTC)
Re: World's simplest Scheme interface to sqlite3 Amirouche Boubekki (29 Aug 2020 07:33 UTC)
Re: World's simplest Scheme interface to sqlite3 Lassi Kortela (29 Aug 2020 11:37 UTC)

Re: SQL API sketch with some code Lassi Kortela 31 Aug 2020 05:02 UTC

>     (sql-prepare    sql-string . params) -> statement
>
> I'd rather let the implementation do the prepared-statement caching, and
> put string . params into the below examples, so (sql-do database string
> . params).  It bumps up the ease of use a lot, and you have only one
> opaque object to keep track of, the database connection.

Caching is not really an issue since the implementation hopefully should
not build an escaped SQL string out of sql-string and params. The
wrapper object would just keep sql-string and params in one object, to
be sent to DB engine later as separate objects.

Maybe we should just use (cons sql-string params) in which case we don't
need sql-prepare.

Using (sql-do-something database sql-string . params) is nice until you
start adding more arguments to sql-do-something. It's nice for the
mapfun and accumulator arguments to be optional, which means they have
to be at the end. But it's also nice for params to be optional. The
arglist gets a bit hard to read.

You'd have to pass params as one argument (which we'd probably have as
an alist) instead of a rest argument which is a plist. And you'd have to
pass '() every time you want to apply a custom mapfun or accumulator
with no params:

(sql-get-all database "select a,b,c from foo" '() list)

>     (sql-do         database statement) -> #f
>
> Unspecified value, I think.

Yes.

>     (sql-do/row-id  database statement) -> integer
>
>     (sql-get-one  database statement [mapfun]) -> result
>     (sql-get-all  database statement [mapfun accumulator]) -> state
>
>     ;; sql-query-one expects exactly one row back and does (apply mapfun
>     ;; row). The mapfun may return multiple values, which are returned
>     ;; from sql-query-one. It raises an exception if it gets no rows, or if
>     ;; it gets more than one row.
>
> The only thing I don't like about this is that it's column-positional,
> but I suppose we are stuck with that, since some queries result in
> columns named things like "(a+b)/c".

You can name those columns with "select (a+b)/c as foo", but there may
be some situations where that syntax doesn't work.

> Is it worth having a special case for "one row, one column" that gives
> you a scalar directly?

If we use (sql-get-one db "select count(*) from foo" values) it gives us
a scalar because we wrote a select that only gives one column. And
values is the default, so that can be shortened to (sql-get-one db
"select count(*) from foo").

The idea of feeding each row to a mapfun is straight from Bigloo's
sqlite module which you linked. I think it's brilliant because it makes
the one-column case and the multi-column case have the same calling
convention. Just (lambda (a) ...) vs (lambda (a b c) ...). If they were
passed to a list or vector instead of a lambda, it would be awkward for
users to unpack the one-column case manually.

And thanks to `values`, the one-row and multi-row cases can also have
the same calling convention. Multi-row just adds the accumulator and
uses `vector` instead of `values` as the default mapfun. It would be
nice if Scheme came with a standard `identity` function for (lambda (x)
x) which we could use to accumulate single columns from multiple rows.

All of this relies on either:

1) real multiple values in the Scheme implementation
2) a `values` implementation where the single-value case (values a)
    returns `a` with no wrapper object.

>     ;; Each return value
>     ;; is fed to the accumulator. Finally the accumulator is fed
>     ;; (eof-object) and its final state is returned from sql-query-all.
>
> I love this use of an accumulator!

Me too! It's basically your idea. Thanks for advocating for generators
and accumulators on the lists.