diff --git a/interpolate.go b/interpolate.go index 372164f..2a948dc 100644 --- a/interpolate.go +++ b/interpolate.go @@ -119,6 +119,12 @@ func (i *interpolator) encodePlaceholder(value interface{}, topLevel bool) error return nil } + v := reflect.ValueOf(value) + if value == nil || (v.Kind() == reflect.Ptr && v.IsNil()) { + i.WriteString("NULL") + return nil + } + if valuer, ok := value.(driver.Valuer); ok { // get driver.Valuer's data var err error @@ -126,13 +132,9 @@ func (i *interpolator) encodePlaceholder(value interface{}, topLevel bool) error if err != nil { return err } + v = reflect.ValueOf(value) } - if value == nil { - i.WriteString("NULL") - return nil - } - v := reflect.ValueOf(value) switch v.Kind() { case reflect.String: i.WriteString(i.EncodeString(v.String())) diff --git a/interpolate_test.go b/interpolate_test.go index bb45aed..4123ba3 100644 --- a/interpolate_test.go +++ b/interpolate_test.go @@ -1,6 +1,7 @@ package dbr import ( + "database/sql/driver" "strings" "testing" "time" @@ -177,6 +178,56 @@ func TestCommonSQLInjections(t *testing.T) { } } +// testValuer is a simple implementation of driver.Value, for testing purposes +type testValuer struct{} + +func (tv testValuer) Value() (driver.Value, error) { + return "ok", nil +} + +// Ensures that nil driver.Valuer values are handler correctly. +func TestEncodePlaceholderHandlesNilValuers(t *testing.T) { + i := interpolator{ + Buffer: NewBuffer(), + Dialect: dialect.MySQL, + IgnoreBinary: true, + } + + for _, test := range []struct { + name string + value func() interface{} + }{ + { + name: "nil value", + value: func() interface{} { return nil }, + }, + { + name: "nil valuer pointer", + value: func() interface{} { + var v *testValuer + return v + }, + }, + { + name: "not nil valuer pointer", + value: func() interface{} { return &testValuer{} }, + }, + { + name: "value", + value: func() interface{} { return 123 }, + }, + { + name: "valuer", + value: func() interface{} { return testValuer{} }, + }, + } { + t.Run(test.name, func(t *testing.T) { + err := i.encodePlaceholder(test.value(), true) + require.NoError(t, err) + }) + } +} + // InjectionAttempts is a newline separated list of common SQL injection exploits // taken from https://wfuzz.googlecode.com/svn/trunk/wordlist/Injections/SQL.txt