-
Notifications
You must be signed in to change notification settings - Fork 18.4k
Description
Please answer these questions before submitting your issue. Thanks!
What version of Go are you using (go version
)?
go version devel +f56f853 Tue Oct 31 01:23:40 2017 -0700 darwin/amd64
Does this issue reproduce with the latest release?
Yes
What operating system and processor architecture are you using (go env
)?
GOARCH="amd64"
GOBIN="/Users/emmanuelodeke/go/bin"
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOOS="darwin"
GOPATH="/Users/emmanuelodeke/go"
GORACE=""
GOROOT="/Users/emmanuelodeke/go/src/go.googlesource.com/go"
GOTOOLDIR="/Users/emmanuelodeke/go/src/go.googlesource.com/go/pkg/tool/darwin_amd64"
GCCGO="gccgo"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/v3/7z434qpx5v3bw0wh8h2myfpw0000gn/T/go-build329208117=/tmp/go-build -gno-record-gcc-switches -fno-common"
What did you do?
You'll need a MySQL server and then run the code below.
package main
import (
"database/sql"
"flag"
"log"
// MySQL driver
_ "github.com/go-sql-driver/mysql"
)
type trade struct {
DBID uint64
Buy float64
Sell float64
Exch string
}
func main() {
var dbURL string
flag.StringVar(&dbURL, "db-url", "user:@/repro", "the URL to connect to MySQL")
flag.Parse()
db, err := sql.Open("mysql", dbURL)
if err != nil {
log.Fatalf("openDB: %v", err)
}
setup := []string{
"create database if not exists repro",
"use repro",
`create table if not exists repro(
id integer not null AUTO_INCREMENT,
buy float(53),
sell float(53),
exch varchar(128),
primary key(id)
);`,
"insert into repro (buy, exch) values(10, 'gophexch')",
"insert into repro (sell, exch) values(-97.6, 'goos')",
}
for i, line := range setup {
if _, err := db.Exec(line); err != nil {
log.Fatalf("line: #%d err: %v", i, err)
}
}
rows, err := db.Query("select buy, sell, exch from repro")
if err != nil {
log.Fatalf("row err: %v", err)
}
for rows.Next() {
curTrade := new(trade)
err := rows.Scan(&curTrade.Buy, &curTrade.Sell, &curTrade.Exch)
if err != nil {
log.Printf("error: %v\n", err)
}
}
}
and the respective MySQL table looks like this
mysql> select * from repro;
+----+------+-------+----------+
| id | buy | sell | exch |
+----+------+-------+----------+
| 1 | 10 | NULL | gophexch |
| 2 | NULL | -97.6 | goos |
+----+------+-------+----------+
2 rows in set (0.00 sec)
If possible, provide a recipe for reproducing the error.
A complete runnable program is good.
A link, sure: https://gist.github.com/odeke-em/78a237c1b4788ad36162c47e5903217a
What did you expect to see?
Successfully ran
What did you see instead?
2017/11/02 02:37:13 error: sql: Scan error on column index 1: converting driver.Value type ("") to a float64: invalid syntax
2017/11/02 02:37:13 error: sql: Scan error on column index 0: converting driver.Value type ("") to a float64: invalid syntax
This issue arises because we assume that data will never be nil and automatically convert it to a string to be parsed by strconv as in
go/src/database/sql/convert.go
Lines 404 to 405 in 0d101d1
case reflect.Float32, reflect.Float64: | |
s := asString(src) |
The above can be fixed if we detect that the byte slice of the representative data is nil and automatically set that to zero like in this patch
diff --git a/src/database/sql/convert.go b/src/database/sql/convert.go
index b79ec3f..bb384a9 100644
--- a/src/database/sql/convert.go
+++ b/src/database/sql/convert.go
@@ -402,11 +402,15 @@ func convertAssign(dest, src interface{}) error {
dv.SetUint(u64)
return nil
case reflect.Float32, reflect.Float64:
- s := asString(src)
- f64, err := strconv.ParseFloat(s, dv.Type().Bits())
- if err != nil {
- err = strconvErr(err)
- return fmt.Errorf("converting driver.Value type %T (%q) to a %s: %v", src, s, dv.Kind(), err)
+ var f64 float64
+ if src != nil {
+ var err error
+ s := asString(src)
+ f64, err = strconv.ParseFloat(s, dv.Type().Bits())
+ if err != nil {
+ err = strconvErr(err)
+ return fmt.Errorf("converting driver.Value type %T (%q) to a %s: %v", src, s, dv.Kind(), err)
+ }
}
dv.SetFloat(f64)
return nil
or a similar patch that avoids passing in "", "", "NULL" into strconv.
Could we recognize the special case when the column value is NULL thus a nil byte slice, and instead of passing it into strconv, just set that to the zero value?
This issue kept me up as I thought there was some other issue with my database and code so I went on a digging expedition, it has bitten me in the past as well and I suspect that other folks have experienced it before.
I believe that this is a bug, but since it has been this behavior has existed for ages, I'll title this as a proposal.