summaryrefslogtreecommitdiff
path: root/src/pkg/database/sql/convert.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/database/sql/convert.go')
-rw-r--r--src/pkg/database/sql/convert.go83
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)