diff options
Diffstat (limited to 'src/pkg/database/sql/convert.go')
-rw-r--r-- | src/pkg/database/sql/convert.go | 83 |
1 files changed, 74 insertions, 9 deletions
diff --git a/src/pkg/database/sql/convert.go b/src/pkg/database/sql/convert.go index bfcb03ccf..853a7826c 100644 --- a/src/pkg/database/sql/convert.go +++ b/src/pkg/database/sql/convert.go @@ -14,53 +14,115 @@ import ( "strconv" ) -// subsetTypeArgs takes a slice of arguments from callers of the sql -// package and converts them into a slice of the driver package's -// "subset types". -func subsetTypeArgs(args []interface{}) ([]driver.Value, error) { - out := make([]driver.Value, len(args)) +var errNilPtr = errors.New("destination pointer is nil") // embedded in descriptive error + +// driverArgs converts arguments from callers of Stmt.Exec and +// Stmt.Query into driver Values. +// +// The statement si may be nil, if no statement is available. +func driverArgs(si driver.Stmt, args []interface{}) ([]driver.Value, error) { + dargs := make([]driver.Value, len(args)) + cc, ok := si.(driver.ColumnConverter) + + // Normal path, for a driver.Stmt that is not a ColumnConverter. + if !ok { + for n, arg := range args { + var err error + dargs[n], err = driver.DefaultParameterConverter.ConvertValue(arg) + if err != nil { + return nil, fmt.Errorf("sql: converting Exec argument #%d's type: %v", n, err) + } + } + return dargs, nil + } + + // Let the Stmt convert its own arguments. for n, arg := range args { + // 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 err != nil { + return nil, fmt.Errorf("sql: argument index %d from Value: %v", n, err) + } + if !driver.IsValue(sv) { + return nil, fmt.Errorf("sql: argument index %d: non-subset type %T returned from Value", n, sv) + } + arg = sv + } + + // Second, ask the column to sanity check itself. For + // example, drivers might use this to make sure that + // an int64 values being inserted into a 16-bit + // integer field is in range (before getting + // truncated), or that a nil can't go into a NOT NULL + // column before going across the network to get the + // same error. var err error - out[n], err = driver.DefaultParameterConverter.ConvertValue(arg) + dargs[n], err = cc.ColumnConverter(n).ConvertValue(arg) if err != nil { - return nil, fmt.Errorf("sql: converting argument #%d's type: %v", n+1, err) + return nil, fmt.Errorf("sql: converting argument #%d's type: %v", n, err) + } + if !driver.IsValue(dargs[n]) { + return nil, fmt.Errorf("sql: driver ColumnConverter error converted %T to unsupported type %T", + arg, dargs[n]) } } - return out, nil + + return dargs, nil } // convertAssign copies to dest the value in src, converting it if possible. // An error is returned if the copy would result in loss of information. // dest should be a pointer type. func convertAssign(dest, src interface{}) error { - // Common cases, without reflect. Fall through. + // Common cases, without reflect. switch s := src.(type) { case string: switch d := dest.(type) { case *string: + if d == nil { + return errNilPtr + } *d = s return nil case *[]byte: + if d == nil { + return errNilPtr + } *d = []byte(s) return nil } case []byte: switch d := dest.(type) { case *string: + if d == nil { + return errNilPtr + } *d = string(s) return nil case *interface{}: + if d == nil { + return errNilPtr + } bcopy := make([]byte, len(s)) copy(bcopy, s) *d = bcopy return nil case *[]byte: + if d == nil { + return errNilPtr + } *d = s return nil } case nil: switch d := dest.(type) { case *[]byte: + if d == nil { + return errNilPtr + } *d = nil return nil } @@ -98,6 +160,9 @@ func convertAssign(dest, src interface{}) error { if dpv.Kind() != reflect.Ptr { return errors.New("destination not a pointer") } + if dpv.IsNil() { + return errNilPtr + } if !sv.IsValid() { sv = reflect.ValueOf(src) |