@John Nagle
Your description of why sql.Row[s] exists is very concise, I'll definitely
use it at some point in the future. I actually quite like database/sql
interface, since I think anything more sophisticated would have a worse
user-side interface. The only bags we have for holding a bag of stuff of
various types are []interface{} and structs, and only one of those can be
created at runtime.
I think people misunderstand QueryRow & Row. Their implementation is
strictly in terms of Query and Rows and is extremely short and easy to
understand:
http://golang.org/src/pkg/database/sql/sql.go?s=19667:19729#L768http://golang.org/src/pkg/database/sql/sql.go?s=34857:34974#L1379http://golang.org/src/pkg/database/sql/sql.go?s=35211:35256#L1389QueryRow uses Query and returns the rows & err wrapped in a Row.
Row.Scan() returns that error if set, otherwise scans once and throws the
rest away, returning any scanning error. The documentation for Row.Scan()
is completely correct, and Row.Scan() itself is written in terms of
Rows.Scan().
The source of confusion here is mostly in the naming: is that there is no
situation in which a Query or manipulation of its resultant Rows becomes a
Row. I'm sure most people expect Rows.Next() to give them a Row, or for
Row to be some kind of component of a Rows, where in fact the opposite is
true. There's nothing much that can be done about this after go1, but it's
merely awkward, not incorrect.
An additional comment, just in general, is that database/sql is very
protective about its interface. In my attempts to extend it (with sqlx), I
had to actually re-implement sql.Row & the various QueryRow functions to be
able to provide access to the underlying Rows.Columns() function when using
QueryRow:
https://github.com/jmoiron/sqlx/blob/master/sqlx.go#L80-L85I think requiring the use of reflect is absolutely fine, since this is what
it's for (runtime type manipulation). Requiring the use of unsafe on the
other hand...
@Arne
As for this proposal, I think it's an awkward way of doing it, but I agree
that it's beneficial to avoid adding public driver-only stuff to `sql.go`,
and that the drivers should have some way of accessing the information that
they put into an sql.Rows in the first place. I worry a bit about how much
database/sql is becoming fractured across driver lines (already DSNs and
query string interpolation will differ among drivers, even for the same
database), but wherever this is possible to fix it's probably a cultural
issue rather than a technical one. It's access to efficient APIs which
differ when needed rather than to pretend everything is the same and suffer
with a poor abstraction.
On Friday, May 31, 2013 3:05:17 PM UTC-4, John Nagle wrote:On 5/31/2013 6:07 AM, Arne Hormann wrote:Am Freitag, 31. Mai 2013 14:59:06 UTC+2 schrieb minux:
I have a feel that you're designing your solution against the
language....
just a feeling, though.
It's not just a feeling, you're right and this is not my preferred solution.
I need column type information from database/sql drivers and everything in
there is wrapped.
I proposed a safe workaround in an earlier post
<
https://groups.google.com/forum/#!topic/golang-nuts/2aLctcVyp6Q>,
but that requires changing the runtime.
There's a lot of dissatisfaction with Go's SQL interface.
That's why there are so many alternatives to it that do almost
the same thing. The Go SQL interface itself isn't a good fit
to Go; it's basically a warmed-over interface from another
language.
The fundamental problem is that what you get from a SQL
query is a sequence of typed values, each of which has a
type, a value, and a column name. In some languages,
like Python, this is easily represented as a tuple.
For Go, it would logically be a struct, but the form
of that struct is query dependent and not known to the SQL
package. So the Go SQL package returns an opaque type,
a "Row", for which a few functions are provided.
(The description for "func (*Row) Scan" at
"http://golang.org/pkg/database/sql/#Row" makes
no sense. It says: "Scan copies the columns from the matched row into
the values pointed at by dest. If more than one row matches the query,
Scan uses the first row and discards the rest. If no row matches the
query, Scan returns ErrNoRows." A "Row" only contains one row.
There's a type Rows, but that's different. This description may
have been modified from that for Scan() for type Rows.)
The original poster needs to convert a Row into a struct with
the matching fields, and there's no general way to do that
without reflection. Maybe there should be.
The "...T" syntax in function calls turns a list of
parameters into a slice of type []T. But you can only do
that in function calls. It would be useful to have a general
function that can turn a struct into a slice of interface{},
and the reverse operation. Those are safe operations, and
can be written (inefficiently) with reflection.
There some other simple generic operations like that which
would run fast if implemented at compile time but are
painfully slow when done via reflections. Deep copy
for nonrecursive types is an example.
John Nagle
--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to
[email protected].
For more options, visit
https://groups.google.com/groups/opt_out.