Commit bca3f5fc authored by Brad Fitzpatrick's avatar Brad Fitzpatrick

database/sql: check for nil Scan pointers

Return nice errors and don't panic.

Fixes #4859

R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/7383046
parent 5833c96b
...@@ -14,6 +14,8 @@ import ( ...@@ -14,6 +14,8 @@ import (
"strconv" "strconv"
) )
var errNilPtr = errors.New("destination pointer is nil") // embedded in descriptive error
// driverArgs converts arguments from callers of Stmt.Exec and // driverArgs converts arguments from callers of Stmt.Exec and
// Stmt.Query into driver Values. // Stmt.Query into driver Values.
// //
...@@ -75,34 +77,52 @@ func driverArgs(si driver.Stmt, args []interface{}) ([]driver.Value, error) { ...@@ -75,34 +77,52 @@ func driverArgs(si driver.Stmt, args []interface{}) ([]driver.Value, error) {
// An error is returned if the copy would result in loss of information. // An error is returned if the copy would result in loss of information.
// dest should be a pointer type. // dest should be a pointer type.
func convertAssign(dest, src interface{}) error { func convertAssign(dest, src interface{}) error {
// Common cases, without reflect. Fall through. // Common cases, without reflect.
switch s := src.(type) { switch s := src.(type) {
case string: case string:
switch d := dest.(type) { switch d := dest.(type) {
case *string: case *string:
if d == nil {
return errNilPtr
}
*d = s *d = s
return nil return nil
case *[]byte: case *[]byte:
if d == nil {
return errNilPtr
}
*d = []byte(s) *d = []byte(s)
return nil return nil
} }
case []byte: case []byte:
switch d := dest.(type) { switch d := dest.(type) {
case *string: case *string:
if d == nil {
return errNilPtr
}
*d = string(s) *d = string(s)
return nil return nil
case *interface{}: case *interface{}:
if d == nil {
return errNilPtr
}
bcopy := make([]byte, len(s)) bcopy := make([]byte, len(s))
copy(bcopy, s) copy(bcopy, s)
*d = bcopy *d = bcopy
return nil return nil
case *[]byte: case *[]byte:
if d == nil {
return errNilPtr
}
*d = s *d = s
return nil return nil
} }
case nil: case nil:
switch d := dest.(type) { switch d := dest.(type) {
case *[]byte: case *[]byte:
if d == nil {
return errNilPtr
}
*d = nil *d = nil
return nil return nil
} }
...@@ -140,6 +160,9 @@ func convertAssign(dest, src interface{}) error { ...@@ -140,6 +160,9 @@ func convertAssign(dest, src interface{}) error {
if dpv.Kind() != reflect.Ptr { if dpv.Kind() != reflect.Ptr {
return errors.New("destination not a pointer") return errors.New("destination not a pointer")
} }
if dpv.IsNil() {
return errNilPtr
}
if !sv.IsValid() { if !sv.IsValid() {
sv = reflect.ValueOf(src) sv = reflect.ValueOf(src)
......
...@@ -696,3 +696,15 @@ func nullTestRun(t *testing.T, spec nullTestSpec) { ...@@ -696,3 +696,15 @@ func nullTestRun(t *testing.T, spec nullTestSpec) {
} }
} }
} }
// golang.org/issue/4859
func TestQueryRowNilScanDest(t *testing.T) {
db := newTestDB(t, "people")
defer closeDB(t, db)
var name *string // nil pointer
err := db.QueryRow("SELECT|people|name|").Scan(name)
want := "sql: Scan error on column index 0: destination pointer is nil"
if err == nil || err.Error() != want {
t.Errorf("error = %q; want %q", err.Error(), want)
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment