On Nov 19, 5:39 pm, Peter Froehlich <
peter.hans.froehl...@gmail.com>
wrote:
> Hi all,
>
> On Thu, Nov 19, 2009 at 7:19 AM, Nathan Baum
>
> <
nat...@parenthephobia.org.uk> wrote:
> > Open() should be simple. You've actually listed the components of a
> > URI as what will go in the argument array.
>
> > Open("mysql://phf:somepassword@localhost/mydb")
> > Open("sqlite3:my.db?vfs=whatever")
>
> I come from Python, that's where I've done the most with databases. At
> least there, it seems that ORMs offer the syntax you suggest, while
> database interfaces take separate parameters. I am not sure how much
> I'd like to parse URIs at this level, seems that I am already pushing
> it by requiring each database interface to parse through a map. But I
> am flexible. :-D
An advantage of URIs is that they're easy to make configurable. Almost
any configuration format can accept strings, whilst not all can easily
accept an arbitrary map[string] string.
I don't see an obvious benefit to _not_ using URIs, unless there are
esoteric database engines which require a parameter which is an array
or map.
OTOH, since it's obvious how to convert a URI to a map of the sort the
API uses, you could have an OpenURI function. (Using http.URL for
parsing.)
> > The TransactionalConnection is missing Begin, I think.
>
> If so then it's missing from the Python API we started from as well. I
> think the idea is that all statements you execute on a connection are
> potentially part of a transaction, so you really only have to decide
> how things "end" and not how they "begin"?
I'm used to database engines which default to "auto-commit" mode,
where each statement is implicitly followed by a commit. It's useful
if you need to be able to see side effects of database changes in one
program as you make them in another. I have one program at the moment
which communicates with a (proprietary) daemon on another computer by
inserting jobs into a table in a shared database.
I think it might also be useful to require people to explicitly use
Begin() to start a transaction, if that involves a
TransactionalConnection type assertion. It means they are less likely
to be in a situation where their program is designed to use
transactions, is later connected to a non-transactional database, but
doesn't find that out until it tries to rollback the transaction.
Requiring Begin() before the transaction ensures* that if transactions
are not available on the engine, no work will be done that assumes
they are.
* Except in MySQL, where not all table formats are transactional, and
attempting to begin, commit or rollback on those formats doesn't even
result in an error being reported. Great.
> > I don't like the name of PythonicCursor!
>
> Better name? Another example of where I just needed a name to group a
> bunch of signatures under.
You could name them after how they behave.
To me, "cursor" describes something which can go backward and forward,
and used for both reading and writing. This is a result of having used
Microsoft's ADO a long time ago, and having used many text editors
which have things they call cursors.
Your API's cursors aren't like that. You can only read-and-go-forward
(possibly multiple times). They map rather directly to iterators in
Python. Perhaps they should be called ArrayIterator and MapIterator.
Some might consider Generator to be a nicer term.
There are some even simpler kinds of thing which have the behaviour
specified.
Execute could return a function which returns a row for each
invocation. If one chooses whether to use arrays or maps when
executing or preparing the statement, the function need accept no
arguments. (I imagine that one never wants to fetch some rows as
arrays and some as maps.)
Another option is for Execute to return a channel. This has the
advantage that one can use it directly with for range, which looks
nice:
for x := range query.Execute() {
print(x["foobar"])
}
It has the possibly significant downside that you can't conveniently
propagate errors down the same channel, so the API would presumably
also have to provide a way of reporting fetch errors to the program.
(Perhaps via another channel.)
FetchMany and FetchAll can be implemented as generic channel
operations. That has the advantage that chanops.ReceiveMany(<-chan
template{}, int) []template{} and chanops.ReceiveAll(<-chan template
{}) []template{} can also be used with channels from elsewhere, making
them useful to people who aren't even using the DBAPI.
If the cursors remain cursors, channels are still nice. The idiomatic
Go way to produce a channel from a container seems to be with an Iter
method; that could be added to the Cursor interface.