I'm running a SQL
query using the sample from denisenkom but coupled with a http.ResponseWriter
and I'm struggling with interface{}
type conversion. There are a few posts that are close to what I'm doing but the solution seems kind of heavy handed and always use fmt
(which I'm not using).
Note that my query works and returns a result. I'm just trying to display that result.
Here is my code that I think is relatively close but doesn't work. I've tried a couple other things but none even compile.
vals := make([]interface{}, len(cols))
for i := 0; i < len(cols); i++ {
vals[i] = new(interface{})
if i != 0 {
w.Write([]byte("\t"))
}
w.Write([]byte(cols[i]))
}
for rows.Next() {
err = rows.Scan(vals...)
if err != nil {
w.Write([]byte("Row problem: " + err.Error()))
continue
}
for i := 0; i < len(vals); i++ {
if i != 0 {
w.Write([]byte("\t"))
}
//THIS IS THE PART I'M STUCK ON
switch v := vals[i].(type) {
case int:
w.Write([]byte("int!
" + string(v)))
case int8:
w.Write([]byte("int8!
" + string(v)))
//etc, etc
//more types
//etc, etc
case float64:
w.Write([]byte("float64!
" + string(v))) //This fails, can't convert, will need something else
case string:
w.Write([]byte("string!
" + v))
default:
w.Write([]byte("something else!
"))
}
}
}
But isn't there a better way to dynamically check the underlying type and convert it to something readable? All I want to do is spit out the results of a query, this seems like I'm doing something wrong.
Note that it always hits the default
case, even when it's explicitly the same type.
The example at denisekom prints to stdout using the fmt.Print family of functions. Change the example to print to a response writer by using the fmt.Fprint family of functions.
The fmt.Fprint functions write to the io.Writer specified as the first argument. The response writer satisfies the io.Writer interface. Here's the modified code from the original example where w
is the http.ResponseWriter.
vals := make([]interface{}, len(cols))
for i := 0; i < len(cols); i++ {
vals[i] = new(interface{})
if i != 0 {
fmt.Fprint(w, "\t")
}
fmt.Fprint(w, cols[i])
}
fmt.Fprintln(w)
for rows.Next() {
err = rows.Scan(vals...)
if err != nil {
fmt.Fprintln(w, err)
continue
}
for i := 0; i < len(vals); i++ {
if i != 0 {
fmt.Fprint(w, "\t")
}
printValue(w, vals[i].(*interface{}))
}
fmt.Fprintln(w)
}
...
func printValue(w io.Writer, pval *interface{}) {
switch v := (*pval).(type) {
case nil:
fmt.Fprint(w, "NULL")
case bool:
if v {
fmt.Fprint(w, "1")
} else {
fmt.Fprint(w, "0")
}
case []byte:
fmt.Fprint(w, string(v))
case time.Time:
fmt.Fprint(w, v.Format("2006-01-02 15:04:05.999"))
default:
fmt.Fprint(w, v)
}
}
Regarding the code in the question: the expression string(v)
is a string conversion. A string conversion does not convert numbers to a decimal representation as the code is assuming. See the spec for details on string conversions. Use either the fmt
package as above or the strconv
package to convert numbers to decimal strings. The type switch should be switch v := (*(vals[i].(*interface{})).(type) {
. That's a bit of mess, and that leads to my next point.
The original example uses more indirection than needed. Here's a simplified version ready to be called with a response writer:
func exec(w io.Writer, db *sql.DB, cmd string) error {
rows, err := db.Query(cmd)
if err != nil {
return err
}
defer rows.Close()
cols, err := rows.Columns()
if err != nil {
return err
}
if cols == nil {
return nil
}
vals := make([]interface{}, len(cols))
args := make([]interface{}, len(cols))
for i := 0; i < len(cols); i++ {
args[i] = &vals[i]
if i != 0 {
fmt.Fprint(w, "\t")
}
fmt.Fprint(w, cols[i])
}
for rows.Next() {
err = rows.Scan(args...)
if err != nil {
fmt.Fprintln(w, err)
continue
}
for i := 0; i < len(vals); i++ {
if i != 0 {
fmt.Print("\t")
}
printValue(w, vals[i])
}
fmt.Fprintln(w)
}
if rows.Err() != nil {
return rows.Err()
}
return nil
}
func printValue(w io.Writer, v interface{}) {
switch v := v.(type) {
case nil:
fmt.Fprint(w, "NULL")
case bool:
if v {
fmt.Fprint(w, "1")
} else {
fmt.Fprint(w, "0")
}
case []byte:
fmt.Fprint(w, string(v))
case time.Time:
fmt.Fprint(w, v.Format("2006-01-02 15:04:05.999"))
default:
fmt.Fprint(w, v)
}
}