diff options
Diffstat (limited to 'libgo/go/database/sql/convert.go')
-rw-r--r-- | libgo/go/database/sql/convert.go | 91 |
1 files changed, 76 insertions, 15 deletions
diff --git a/libgo/go/database/sql/convert.go b/libgo/go/database/sql/convert.go index 99aed2398e2..ea2f377810e 100644 --- a/libgo/go/database/sql/convert.go +++ b/libgo/go/database/sql/convert.go @@ -13,16 +13,36 @@ import ( "reflect" "strconv" "time" + "unicode" + "unicode/utf8" ) var errNilPtr = errors.New("destination pointer is nil") // embedded in descriptive error +func describeNamedValue(nv *driver.NamedValue) string { + if len(nv.Name) == 0 { + return fmt.Sprintf("$%d", nv.Ordinal) + } + return fmt.Sprintf("with name %q", nv.Name) +} + +func validateNamedValueName(name string) error { + if len(name) == 0 { + return nil + } + r, _ := utf8.DecodeRuneInString(name) + if unicode.IsLetter(r) { + return nil + } + return fmt.Errorf("name %q does not begin with a letter", name) +} + // driverArgs converts arguments from callers of Stmt.Exec and // Stmt.Query into driver Values. // // The statement ds may be nil, if no statement is available. -func driverArgs(ds *driverStmt, args []interface{}) ([]driver.Value, error) { - dargs := make([]driver.Value, len(args)) +func driverArgs(ds *driverStmt, args []interface{}) ([]driver.NamedValue, error) { + nvargs := make([]driver.NamedValue, len(args)) var si driver.Stmt if ds != nil { si = ds.si @@ -33,26 +53,45 @@ func driverArgs(ds *driverStmt, args []interface{}) ([]driver.Value, error) { if !ok { for n, arg := range args { var err error - dargs[n], err = driver.DefaultParameterConverter.ConvertValue(arg) + nv := &nvargs[n] + nv.Ordinal = n + 1 + if np, ok := arg.(NamedArg); ok { + if err := validateNamedValueName(np.Name); err != nil { + return nil, err + } + arg = np.Value + nvargs[n].Name = np.Name + } + nv.Value, err = driver.DefaultParameterConverter.ConvertValue(arg) + if err != nil { - return nil, fmt.Errorf("sql: converting Exec argument #%d's type: %v", n, err) + return nil, fmt.Errorf("sql: converting Exec argument %s type: %v", describeNamedValue(nv), err) } } - return dargs, nil + return nvargs, nil } // Let the Stmt convert its own arguments. for n, arg := range args { + nv := &nvargs[n] + nv.Ordinal = n + 1 + if np, ok := arg.(NamedArg); ok { + if err := validateNamedValueName(np.Name); err != nil { + return nil, err + } + arg = np.Value + nv.Name = np.Name + } // First, see if the value itself knows how to convert // itself to a driver type. For example, a NullString // struct changing into a string or nil. - if svi, ok := arg.(driver.Valuer); ok { - sv, err := svi.Value() + if vr, ok := arg.(driver.Valuer); ok { + sv, err := callValuerValue(vr) if err != nil { - return nil, fmt.Errorf("sql: argument index %d from Value: %v", n, err) + return nil, fmt.Errorf("sql: argument %s from Value: %v", describeNamedValue(nv), err) } if !driver.IsValue(sv) { - return nil, fmt.Errorf("sql: argument index %d: non-subset type %T returned from Value", n, sv) + return nil, fmt.Errorf("sql: argument %s: non-subset type %T returned from Value", describeNamedValue(nv), sv) } arg = sv } @@ -66,18 +105,18 @@ func driverArgs(ds *driverStmt, args []interface{}) ([]driver.Value, error) { // same error. var err error ds.Lock() - dargs[n], err = cc.ColumnConverter(n).ConvertValue(arg) + nv.Value, err = cc.ColumnConverter(n).ConvertValue(arg) ds.Unlock() if err != nil { - return nil, fmt.Errorf("sql: converting argument #%d's type: %v", n, err) + return nil, fmt.Errorf("sql: converting argument %s type: %v", describeNamedValue(nv), err) } - if !driver.IsValue(dargs[n]) { - return nil, fmt.Errorf("sql: driver ColumnConverter error converted %T to unsupported type %T", - arg, dargs[n]) + if !driver.IsValue(nv.Value) { + return nil, fmt.Errorf("sql: for argument %s, driver ColumnConverter error converted %T to unsupported type %T", + describeNamedValue(nv), arg, nv.Value) } } - return dargs, nil + return nvargs, nil } // convertAssign copies to dest the value in src, converting it if possible. @@ -330,3 +369,25 @@ func asBytes(buf []byte, rv reflect.Value) (b []byte, ok bool) { } return } + +var valuerReflectType = reflect.TypeOf((*driver.Valuer)(nil)).Elem() + +// callValuerValue returns vr.Value(), with one exception: +// If vr.Value is an auto-generated method on a pointer type and the +// pointer is nil, it would panic at runtime in the panicwrap +// method. Treat it like nil instead. +// Issue 8415. +// +// This is so people can implement driver.Value on value types and +// still use nil pointers to those types to mean nil/NULL, just like +// string/*string. +// +// This function is mirrored in the database/sql/driver package. +func callValuerValue(vr driver.Valuer) (v driver.Value, err error) { + if rv := reflect.ValueOf(vr); rv.Kind() == reflect.Ptr && + rv.IsNil() && + rv.Type().Elem().Implements(valuerReflectType) { + return nil, nil + } + return vr.Value() +} |