From 5de00899e6e2240a1c05c66faff26e7b51f7e29d Mon Sep 17 00:00:00 2001 From: Arne Hormann Date: Thu, 23 Apr 2015 17:23:28 +0200 Subject: [PATCH 1/3] support uint64 parameters with high bit set --- driver_test.go | 43 +++++++++++++++++++++++++++++++++++++++++++ statement.go | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) diff --git a/driver_test.go b/driver_test.go index bda62eebc..cb0d5f5ec 100644 --- a/driver_test.go +++ b/driver_test.go @@ -780,6 +780,49 @@ func TestNULL(t *testing.T) { }) } +func TestUint64(t *testing.T) { + const ( + u0 = uint64(0) + uall = ^u0 + uhigh = uall >> 1 + utop = ^uhigh + s0 = int64(0) + sall = ^s0 + shigh = int64(uhigh) + stop = ^shigh + ) + runTests(t, dsn, func(dbt *DBTest) { + stmt, err := dbt.db.Prepare(`SELECT ?, ?, ? ,?, ?, ?, ?, ?`) + if err != nil { + dbt.Fatal(err) + } + defer stmt.Close() + row := stmt.QueryRow( + u0, uhigh, utop, uall, + s0, shigh, stop, sall, + ) + + var ua, ub, uc, ud uint64 + var sa, sb, sc, sd int64 + + err = row.Scan(&ua, &ub, &uc, &ud, &sa, &sb, &sc, &sd) + if err != nil { + dbt.Fatal(err) + } + switch { + case ua != u0, + ub != uhigh, + uc != utop, + ud != uall, + sa != s0, + sb != shigh, + sc != stop, + sd != sall: + dbt.Fatal("Unexpected result value") + } + }) +} + func TestLongData(t *testing.T) { runTests(t, dsn, func(dbt *DBTest) { var maxAllowedPacketSize int diff --git a/statement.go b/statement.go index 142ef5416..ed732afab 100644 --- a/statement.go +++ b/statement.go @@ -10,6 +10,8 @@ package mysql import ( "database/sql/driver" + "fmt" + "reflect" ) type mysqlStmt struct { @@ -34,6 +36,10 @@ func (stmt *mysqlStmt) NumInput() int { return stmt.paramCount } +func (stmt *mysqlStmt) ColumnConverter(idx int) driver.ValueConverter { + return converter{} +} + func (stmt *mysqlStmt) Exec(args []driver.Value) (driver.Result, error) { if stmt.mc.netConn == nil { errLog.Print(ErrInvalidConn) @@ -110,3 +116,45 @@ func (stmt *mysqlStmt) Query(args []driver.Value) (driver.Rows, error) { return rows, err } + +type converter struct{} + +func (converter) ConvertValue(v interface{}) (driver.Value, error) { + if driver.IsValue(v) { + return v, nil + } + + if svi, ok := v.(driver.Valuer); ok { + sv, err := svi.Value() + if err != nil { + return nil, err + } + if !driver.IsValue(sv) { + return nil, fmt.Errorf("non-Value type %T returned from Value", sv) + } + return sv, nil + } + + rv := reflect.ValueOf(v) + switch rv.Kind() { + case reflect.Ptr: + // indirect pointers + if rv.IsNil() { + return nil, nil + } + return driver.DefaultParameterConverter.ConvertValue(rv.Elem().Interface()) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return rv.Int(), nil + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32: + return int64(rv.Uint()), nil + case reflect.Uint64: + val := rv.Uint() + if val < (^uint64(0) >> 1) { + return int64(val), nil + } + return fmt.Sprintf("%d", rv.Uint()), nil + case reflect.Float32, reflect.Float64: + return rv.Float(), nil + } + return nil, fmt.Errorf("unsupported type %T, a %s", v, rv.Kind()) +} From e8631174644bb75fdaf9b55b7bb3dd0511e3a57a Mon Sep 17 00:00:00 2001 From: Arne Hormann Date: Thu, 23 Apr 2015 17:35:14 +0200 Subject: [PATCH 2/3] keep closer to go-source and avoid off-by-one messup --- statement.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/statement.go b/statement.go index ed732afab..ed0a6449e 100644 --- a/statement.go +++ b/statement.go @@ -148,11 +148,11 @@ func (converter) ConvertValue(v interface{}) (driver.Value, error) { case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32: return int64(rv.Uint()), nil case reflect.Uint64: - val := rv.Uint() - if val < (^uint64(0) >> 1) { - return int64(val), nil + u64 := rv.Uint() + if u64 >= 1<<63 { + return fmt.Sprintf("%d", rv.Uint()), nil } - return fmt.Sprintf("%d", rv.Uint()), nil + return int64(u64), nil case reflect.Float32, reflect.Float64: return rv.Float(), nil } From b2cd472f66fe211913456c03df005962a014cf3f Mon Sep 17 00:00:00 2001 From: Arne Hormann Date: Sat, 2 May 2015 22:58:16 +0200 Subject: [PATCH 3/3] fix review issues, replace call with known value --- statement.go | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/statement.go b/statement.go index ed0a6449e..f9dae03fa 100644 --- a/statement.go +++ b/statement.go @@ -124,17 +124,6 @@ func (converter) ConvertValue(v interface{}) (driver.Value, error) { return v, nil } - if svi, ok := v.(driver.Valuer); ok { - sv, err := svi.Value() - if err != nil { - return nil, err - } - if !driver.IsValue(sv) { - return nil, fmt.Errorf("non-Value type %T returned from Value", sv) - } - return sv, nil - } - rv := reflect.ValueOf(v) switch rv.Kind() { case reflect.Ptr: @@ -150,7 +139,7 @@ func (converter) ConvertValue(v interface{}) (driver.Value, error) { case reflect.Uint64: u64 := rv.Uint() if u64 >= 1<<63 { - return fmt.Sprintf("%d", rv.Uint()), nil + return fmt.Sprintf("%d", u64), nil } return int64(u64), nil case reflect.Float32, reflect.Float64: