2

In the Go SQL docs they give an example here of a query that only returns 1 column (poor example in my opinion, at least return 2...)

age := 27
rows, err := db.Query("SELECT name FROM users WHERE age=?", age)
if err != nil {
        log.Fatal(err)
}
for rows.Next() {
        var name string
        if err := rows.Scan(&name); err != nil {
                log.Fatal(err)
        }
        fmt.Printf("%s is %d\n", name, age)
}
if err := rows.Err(); err != nil {
        log.Fatal(err)
}

The docs state here that Scan copies the columns in the current row into the values pointed at by dest.

How does this work with a struct, lets say I have a struct

type User struct{
    Name string
    Age int
}

and I modify my query to SELECT name, age from users where age=?

How do I unpack *Rows into my struct? I did find this example, but it didn't deal with structs. I will be following Active Record pattern conventions so my structs will map to my database via snake case conversion.

Community
  • 1
  • 1
Lee
  • 8,354
  • 14
  • 55
  • 90

1 Answers1

4

Looking at the source, it seems the copy is done with ... syntax on destination pointers:

func (rs *Rows) Scan(dest ...interface{}) error

So in your example, you can do for instance:

for rows.Next() {
    u := User{} // An empty user
    ...
    if err := rows.Scan(&u.Name, &u.Age); err != nil {
        ...
    }
 }

As long as you pass the exact number of pointers, this should work, whether they are from a struct or not.

val
  • 8,459
  • 30
  • 34
  • If I first selected age and then name e.g. select age, name would the above fall over, i.e is it ordinal? – Lee Jan 06 '14 at 10:36
  • I believe so, yes. If you were to invert the query, the call to `err := convertAssign(dest[i], sv)` would fail and the `row.Scan` method would return an error, as it cannot convert `int` to `string` – val Jan 06 '14 at 10:39
  • That makes it impossible to do this with reflection as you cannot guarantee that the model fields are in ordinal sync with the columns of the database (well not without being pedantic and impractical) – Lee Jan 06 '14 at 10:41
  • I think the idea is to have it as low-level as possible for efficiency reasons. Nothing prevents you to implement a more user-friendly/flexible access to it on top of it (e.g. building a `map[string]interface{}` or even building the query from your `reflect`ed struct fields...) – val Jan 06 '14 at 11:01