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