Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to call the Scan variadic function using reflection

I'm looking to call the Rows.Scan() function using reflection. However it takes a variable number of pointers, but there are not a lot of source examples. I need to use reflection because I plan on filling a slice with the values from a Query call. So basically using rows.Columns() to get the length of the row and then make() a slice of []interface{} to fill with the data points that would normally be filled using the pointers passed to the Scan() function.

Basically something like this code:

col := rows.Columns()
vals := make([]interface{}, len(cols))
rows.Scan(&vals)

Anyone have an example of calling a variadic function that takes pointers using reflection that I can take a look at?

Edit: Sample code that doesn't appear to do what I'm after.

package main

import (
    _ "github.com/lib/pq"
    "database/sql"
    "fmt"
)


func main() {

    db, _ := sql.Open(
        "postgres",
        "user=postgres dbname=Go_Testing password=ssap sslmode=disable")

    rows, _ := db.Query("SELECT * FROM _users;")

    cols, _ := rows.Columns()

    for rows.Next() {

        data := make([]interface{}, len(cols))

        rows.Scan(data...)

        fmt.Println(data)
    }

}

The results:

[<nil> <nil> <nil> <nil> <nil>]
[<nil> <nil> <nil> <nil> <nil>]
[<nil> <nil> <nil> <nil> <nil>]
[<nil> <nil> <nil> <nil> <nil>]
[<nil> <nil> <nil> <nil> <nil>]
[<nil> <nil> <nil> <nil> <nil>]
like image 761
lucidquiet Avatar asked Jul 24 '13 22:07

lucidquiet


2 Answers

Here's the solution that I've arrived at. It doesn't get the Types before traversing the data, and so doesn't know before hand the type of each value before pulling the values out through Scan(), but the point really is to not have to know the types before hand.

The trick was to create 2 slices, one for the values, and one that holds pointers in parallel to the values slice. Then once the pointers are used to fill data, the values array is actually filled with the data, which can then be used to populate other data structures.

package main

import (
    "fmt"
    _ "github.com/lib/pq"
    "database/sql"
)

func main() {
    db, _ := sql.Open(
        "postgres",
        "user=postgres dbname=go_testing password=pass sslmode=disable")

    rows, _ := db.Query("SELECT * FROM _user;")

    columns, _ := rows.Columns()
    count := len(columns)
    values := make([]interface{}, count)
    valuePtrs := make([]interface{}, count)

    for rows.Next() {
        for i := range columns {
            valuePtrs[i] = &values[i]
        }

        rows.Scan(valuePtrs...)

        for i, col := range columns {
            val := values[i]

            b, ok := val.([]byte)
            var v interface{}
            if (ok) {
                v = string(b)
            } else {
                v = val
            }

            fmt.Println(col, v)
        }
    }
}
like image 97
lucidquiet Avatar answered Nov 06 '22 13:11

lucidquiet


To lucidquiet: you can also assign a interface instead of making a slice

The following code works good:

var sql = "select * from table"
rows, err := db.Query(sql)
columns, err = rows.Columns()
colNum := len(columns)

var values = make([]interface{}, colNum)
for i, _ := range values {
    var ii interface{}
    values[i] = &ii
}

for rows.Next() {
    err := rows.Scan(values...)
    for i, colName := range columns {
        var raw_value = *(values[i].(*interface{}))
        var raw_type = reflect.TypeOf(raw_value)

        fmt.Println(colName,raw_type,raw_value)
    }
}
like image 9
Tom Liu Avatar answered Nov 06 '22 12:11

Tom Liu