mirror of
https://github.com/golang/go
synced 2024-11-23 08:20:05 -07:00
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
This commit is contained in:
parent
5833c96b0a
commit
bca3f5fca0
@ -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) {
|
|||||||
// 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 {
|
|||||||
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) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user