aboutsummaryrefslogtreecommitdiff
path: root/src/database/sql/convert_test.go
diff options
context:
space:
mode:
authorBrad Fitzpatrick <bradfitz@golang.org>2016-10-11 08:34:58 -0700
committerBrad Fitzpatrick <bradfitz@golang.org>2016-10-17 15:26:25 +0000
commit0ce1d79a6a771f7449ec493b993ed2a720917870 (patch)
treefc1ef0ac4bd678c24236278e40ff8964a98d4c2f /src/database/sql/convert_test.go
parent237d7e34bc7579ec499a029191c33106dc5476a1 (diff)
downloadgo-0ce1d79a6a771f7449ec493b993ed2a720917870.tar.xz
database/sql: accept nil pointers to Valuers implemented on value receivers
The driver.Valuer interface lets types map their Go representation to a suitable database/sql/driver.Value. If a user defines the Value method with a value receiver, such as: type MyStr string func (s MyStr) Value() (driver.Value, error) { return strings.ToUpper(string(s)), nil } Then they can't use (*MyStr)(nil) as an argument to an SQL call via database/sql, because *MyStr also implements driver.Value, but via a compiler-generated wrapper which checks whether the pointer is nil and panics if so. We now accept (*MyStr)(nil) and map it to "nil" (an SQL "NULL") if the Valuer method is implemented on MyStr instead of *MyStr. If a user implements the driver.Value interface with a pointer receiver, they retain full control of what nil means: type MyStr string func (s *MyStr) Value() (driver.Value, error) { if s == nil { return "missing MyStr", nil } return strings.ToUpper(string(*s)), nil } Adds tests for both cases. Fixes #8415 Change-Id: I897d609d80d46e2354d2669a8a3e090688eee3ad Reviewed-on: https://go-review.googlesource.com/31259 Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Daniel Theophanes <kardianos@gmail.com> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Diffstat (limited to 'src/database/sql/convert_test.go')
-rw-r--r--src/database/sql/convert_test.go83
1 files changed, 83 insertions, 0 deletions
diff --git a/src/database/sql/convert_test.go b/src/database/sql/convert_test.go
index ab81f2f65a..4dfab1f6be 100644
--- a/src/database/sql/convert_test.go
+++ b/src/database/sql/convert_test.go
@@ -9,6 +9,7 @@ import (
"fmt"
"reflect"
"runtime"
+ "strings"
"testing"
"time"
)
@@ -389,3 +390,85 @@ func TestUserDefinedBytes(t *testing.T) {
t.Fatal("userDefinedBytes got potentially dirty driver memory")
}
}
+
+type Valuer_V string
+
+func (v Valuer_V) Value() (driver.Value, error) {
+ return strings.ToUpper(string(v)), nil
+}
+
+type Valuer_P string
+
+func (p *Valuer_P) Value() (driver.Value, error) {
+ if p == nil {
+ return "nil-to-str", nil
+ }
+ return strings.ToUpper(string(*p)), nil
+}
+
+func TestDriverArgs(t *testing.T) {
+ var nilValuerVPtr *Valuer_V
+ var nilValuerPPtr *Valuer_P
+ var nilStrPtr *string
+ tests := []struct {
+ args []interface{}
+ want []driver.NamedValue
+ }{
+ 0: {
+ args: []interface{}{Valuer_V("foo")},
+ want: []driver.NamedValue{
+ driver.NamedValue{
+ Ordinal: 1,
+ Value: "FOO",
+ },
+ },
+ },
+ 1: {
+ args: []interface{}{nilValuerVPtr},
+ want: []driver.NamedValue{
+ driver.NamedValue{
+ Ordinal: 1,
+ Value: nil,
+ },
+ },
+ },
+ 2: {
+ args: []interface{}{nilValuerPPtr},
+ want: []driver.NamedValue{
+ driver.NamedValue{
+ Ordinal: 1,
+ Value: "nil-to-str",
+ },
+ },
+ },
+ 3: {
+ args: []interface{}{"plain-str"},
+ want: []driver.NamedValue{
+ driver.NamedValue{
+ Ordinal: 1,
+ Value: "plain-str",
+ },
+ },
+ },
+ 4: {
+ args: []interface{}{nilStrPtr},
+ want: []driver.NamedValue{
+ driver.NamedValue{
+ Ordinal: 1,
+ Value: nil,
+ },
+ },
+ },
+ }
+ for i, tt := range tests {
+ ds := new(driverStmt)
+ got, err := driverArgs(ds, tt.args)
+ if err != nil {
+ t.Errorf("test[%d]: %v", i, err)
+ continue
+ }
+ if !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("test[%d]: got %v, want %v", i, got, tt.want)
+ }
+ }
+}