github.com/sean-/go@v0.0.0-20151219100004-97f854cd7bb6/src/database/sql/convert_test.go (about)

     1  // Copyright 2011 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package sql
     6  
     7  import (
     8  	"database/sql/driver"
     9  	"fmt"
    10  	"reflect"
    11  	"runtime"
    12  	"testing"
    13  	"time"
    14  )
    15  
    16  var someTime = time.Unix(123, 0)
    17  var answer int64 = 42
    18  
    19  type userDefined float64
    20  
    21  type userDefinedSlice []int
    22  
    23  type conversionTest struct {
    24  	s, d interface{} // source and destination
    25  
    26  	// following are used if they're non-zero
    27  	wantint    int64
    28  	wantuint   uint64
    29  	wantstr    string
    30  	wantbytes  []byte
    31  	wantraw    RawBytes
    32  	wantf32    float32
    33  	wantf64    float64
    34  	wanttime   time.Time
    35  	wantbool   bool // used if d is of type *bool
    36  	wanterr    string
    37  	wantiface  interface{}
    38  	wantptr    *int64 // if non-nil, *d's pointed value must be equal to *wantptr
    39  	wantnil    bool   // if true, *d must be *int64(nil)
    40  	wantusrdef userDefined
    41  }
    42  
    43  // Target variables for scanning into.
    44  var (
    45  	scanstr    string
    46  	scanbytes  []byte
    47  	scanraw    RawBytes
    48  	scanint    int
    49  	scanint8   int8
    50  	scanint16  int16
    51  	scanint32  int32
    52  	scanuint8  uint8
    53  	scanuint16 uint16
    54  	scanbool   bool
    55  	scanf32    float32
    56  	scanf64    float64
    57  	scantime   time.Time
    58  	scanptr    *int64
    59  	scaniface  interface{}
    60  )
    61  
    62  var conversionTests = []conversionTest{
    63  	// Exact conversions (destination pointer type matches source type)
    64  	{s: "foo", d: &scanstr, wantstr: "foo"},
    65  	{s: 123, d: &scanint, wantint: 123},
    66  	{s: someTime, d: &scantime, wanttime: someTime},
    67  
    68  	// To strings
    69  	{s: "string", d: &scanstr, wantstr: "string"},
    70  	{s: []byte("byteslice"), d: &scanstr, wantstr: "byteslice"},
    71  	{s: 123, d: &scanstr, wantstr: "123"},
    72  	{s: int8(123), d: &scanstr, wantstr: "123"},
    73  	{s: int64(123), d: &scanstr, wantstr: "123"},
    74  	{s: uint8(123), d: &scanstr, wantstr: "123"},
    75  	{s: uint16(123), d: &scanstr, wantstr: "123"},
    76  	{s: uint32(123), d: &scanstr, wantstr: "123"},
    77  	{s: uint64(123), d: &scanstr, wantstr: "123"},
    78  	{s: 1.5, d: &scanstr, wantstr: "1.5"},
    79  
    80  	// To []byte
    81  	{s: nil, d: &scanbytes, wantbytes: nil},
    82  	{s: "string", d: &scanbytes, wantbytes: []byte("string")},
    83  	{s: []byte("byteslice"), d: &scanbytes, wantbytes: []byte("byteslice")},
    84  	{s: 123, d: &scanbytes, wantbytes: []byte("123")},
    85  	{s: int8(123), d: &scanbytes, wantbytes: []byte("123")},
    86  	{s: int64(123), d: &scanbytes, wantbytes: []byte("123")},
    87  	{s: uint8(123), d: &scanbytes, wantbytes: []byte("123")},
    88  	{s: uint16(123), d: &scanbytes, wantbytes: []byte("123")},
    89  	{s: uint32(123), d: &scanbytes, wantbytes: []byte("123")},
    90  	{s: uint64(123), d: &scanbytes, wantbytes: []byte("123")},
    91  	{s: 1.5, d: &scanbytes, wantbytes: []byte("1.5")},
    92  
    93  	// To RawBytes
    94  	{s: nil, d: &scanraw, wantraw: nil},
    95  	{s: []byte("byteslice"), d: &scanraw, wantraw: RawBytes("byteslice")},
    96  	{s: 123, d: &scanraw, wantraw: RawBytes("123")},
    97  	{s: int8(123), d: &scanraw, wantraw: RawBytes("123")},
    98  	{s: int64(123), d: &scanraw, wantraw: RawBytes("123")},
    99  	{s: uint8(123), d: &scanraw, wantraw: RawBytes("123")},
   100  	{s: uint16(123), d: &scanraw, wantraw: RawBytes("123")},
   101  	{s: uint32(123), d: &scanraw, wantraw: RawBytes("123")},
   102  	{s: uint64(123), d: &scanraw, wantraw: RawBytes("123")},
   103  	{s: 1.5, d: &scanraw, wantraw: RawBytes("1.5")},
   104  
   105  	// Strings to integers
   106  	{s: "255", d: &scanuint8, wantuint: 255},
   107  	{s: "256", d: &scanuint8, wanterr: `converting string "256" to a uint8: strconv.ParseUint: parsing "256": value out of range`},
   108  	{s: "256", d: &scanuint16, wantuint: 256},
   109  	{s: "-1", d: &scanint, wantint: -1},
   110  	{s: "foo", d: &scanint, wanterr: `converting string "foo" to a int: strconv.ParseInt: parsing "foo": invalid syntax`},
   111  
   112  	// True bools
   113  	{s: true, d: &scanbool, wantbool: true},
   114  	{s: "True", d: &scanbool, wantbool: true},
   115  	{s: "TRUE", d: &scanbool, wantbool: true},
   116  	{s: "1", d: &scanbool, wantbool: true},
   117  	{s: 1, d: &scanbool, wantbool: true},
   118  	{s: int64(1), d: &scanbool, wantbool: true},
   119  	{s: uint16(1), d: &scanbool, wantbool: true},
   120  
   121  	// False bools
   122  	{s: false, d: &scanbool, wantbool: false},
   123  	{s: "false", d: &scanbool, wantbool: false},
   124  	{s: "FALSE", d: &scanbool, wantbool: false},
   125  	{s: "0", d: &scanbool, wantbool: false},
   126  	{s: 0, d: &scanbool, wantbool: false},
   127  	{s: int64(0), d: &scanbool, wantbool: false},
   128  	{s: uint16(0), d: &scanbool, wantbool: false},
   129  
   130  	// Not bools
   131  	{s: "yup", d: &scanbool, wanterr: `sql/driver: couldn't convert "yup" into type bool`},
   132  	{s: 2, d: &scanbool, wanterr: `sql/driver: couldn't convert 2 into type bool`},
   133  
   134  	// Floats
   135  	{s: float64(1.5), d: &scanf64, wantf64: float64(1.5)},
   136  	{s: int64(1), d: &scanf64, wantf64: float64(1)},
   137  	{s: float64(1.5), d: &scanf32, wantf32: float32(1.5)},
   138  	{s: "1.5", d: &scanf32, wantf32: float32(1.5)},
   139  	{s: "1.5", d: &scanf64, wantf64: float64(1.5)},
   140  
   141  	// Pointers
   142  	{s: interface{}(nil), d: &scanptr, wantnil: true},
   143  	{s: int64(42), d: &scanptr, wantptr: &answer},
   144  
   145  	// To interface{}
   146  	{s: float64(1.5), d: &scaniface, wantiface: float64(1.5)},
   147  	{s: int64(1), d: &scaniface, wantiface: int64(1)},
   148  	{s: "str", d: &scaniface, wantiface: "str"},
   149  	{s: []byte("byteslice"), d: &scaniface, wantiface: []byte("byteslice")},
   150  	{s: true, d: &scaniface, wantiface: true},
   151  	{s: nil, d: &scaniface},
   152  	{s: []byte(nil), d: &scaniface, wantiface: []byte(nil)},
   153  
   154  	// To a user-defined type
   155  	{s: 1.5, d: new(userDefined), wantusrdef: 1.5},
   156  	{s: int64(123), d: new(userDefined), wantusrdef: 123},
   157  	{s: "1.5", d: new(userDefined), wantusrdef: 1.5},
   158  	{s: []byte{1, 2, 3}, d: new(userDefinedSlice), wanterr: `unsupported driver -> Scan pair: []uint8 -> *sql.userDefinedSlice`},
   159  }
   160  
   161  func intPtrValue(intptr interface{}) interface{} {
   162  	return reflect.Indirect(reflect.Indirect(reflect.ValueOf(intptr))).Int()
   163  }
   164  
   165  func intValue(intptr interface{}) int64 {
   166  	return reflect.Indirect(reflect.ValueOf(intptr)).Int()
   167  }
   168  
   169  func uintValue(intptr interface{}) uint64 {
   170  	return reflect.Indirect(reflect.ValueOf(intptr)).Uint()
   171  }
   172  
   173  func float64Value(ptr interface{}) float64 {
   174  	return *(ptr.(*float64))
   175  }
   176  
   177  func float32Value(ptr interface{}) float32 {
   178  	return *(ptr.(*float32))
   179  }
   180  
   181  func timeValue(ptr interface{}) time.Time {
   182  	return *(ptr.(*time.Time))
   183  }
   184  
   185  func TestConversions(t *testing.T) {
   186  	for n, ct := range conversionTests {
   187  		err := convertAssign(ct.d, ct.s)
   188  		errstr := ""
   189  		if err != nil {
   190  			errstr = err.Error()
   191  		}
   192  		errf := func(format string, args ...interface{}) {
   193  			base := fmt.Sprintf("convertAssign #%d: for %v (%T) -> %T, ", n, ct.s, ct.s, ct.d)
   194  			t.Errorf(base+format, args...)
   195  		}
   196  		if errstr != ct.wanterr {
   197  			errf("got error %q, want error %q", errstr, ct.wanterr)
   198  		}
   199  		if ct.wantstr != "" && ct.wantstr != scanstr {
   200  			errf("want string %q, got %q", ct.wantstr, scanstr)
   201  		}
   202  		if ct.wantint != 0 && ct.wantint != intValue(ct.d) {
   203  			errf("want int %d, got %d", ct.wantint, intValue(ct.d))
   204  		}
   205  		if ct.wantuint != 0 && ct.wantuint != uintValue(ct.d) {
   206  			errf("want uint %d, got %d", ct.wantuint, uintValue(ct.d))
   207  		}
   208  		if ct.wantf32 != 0 && ct.wantf32 != float32Value(ct.d) {
   209  			errf("want float32 %v, got %v", ct.wantf32, float32Value(ct.d))
   210  		}
   211  		if ct.wantf64 != 0 && ct.wantf64 != float64Value(ct.d) {
   212  			errf("want float32 %v, got %v", ct.wantf64, float64Value(ct.d))
   213  		}
   214  		if bp, boolTest := ct.d.(*bool); boolTest && *bp != ct.wantbool && ct.wanterr == "" {
   215  			errf("want bool %v, got %v", ct.wantbool, *bp)
   216  		}
   217  		if !ct.wanttime.IsZero() && !ct.wanttime.Equal(timeValue(ct.d)) {
   218  			errf("want time %v, got %v", ct.wanttime, timeValue(ct.d))
   219  		}
   220  		if ct.wantnil && *ct.d.(**int64) != nil {
   221  			errf("want nil, got %v", intPtrValue(ct.d))
   222  		}
   223  		if ct.wantptr != nil {
   224  			if *ct.d.(**int64) == nil {
   225  				errf("want pointer to %v, got nil", *ct.wantptr)
   226  			} else if *ct.wantptr != intPtrValue(ct.d) {
   227  				errf("want pointer to %v, got %v", *ct.wantptr, intPtrValue(ct.d))
   228  			}
   229  		}
   230  		if ifptr, ok := ct.d.(*interface{}); ok {
   231  			if !reflect.DeepEqual(ct.wantiface, scaniface) {
   232  				errf("want interface %#v, got %#v", ct.wantiface, scaniface)
   233  				continue
   234  			}
   235  			if srcBytes, ok := ct.s.([]byte); ok {
   236  				dstBytes := (*ifptr).([]byte)
   237  				if len(srcBytes) > 0 && &dstBytes[0] == &srcBytes[0] {
   238  					errf("copy into interface{} didn't copy []byte data")
   239  				}
   240  			}
   241  		}
   242  		if ct.wantusrdef != 0 && ct.wantusrdef != *ct.d.(*userDefined) {
   243  			errf("want userDefined %f, got %f", ct.wantusrdef, *ct.d.(*userDefined))
   244  		}
   245  	}
   246  }
   247  
   248  func TestNullString(t *testing.T) {
   249  	var ns NullString
   250  	convertAssign(&ns, []byte("foo"))
   251  	if !ns.Valid {
   252  		t.Errorf("expecting not null")
   253  	}
   254  	if ns.String != "foo" {
   255  		t.Errorf("expecting foo; got %q", ns.String)
   256  	}
   257  	convertAssign(&ns, nil)
   258  	if ns.Valid {
   259  		t.Errorf("expecting null on nil")
   260  	}
   261  	if ns.String != "" {
   262  		t.Errorf("expecting blank on nil; got %q", ns.String)
   263  	}
   264  }
   265  
   266  type valueConverterTest struct {
   267  	c       driver.ValueConverter
   268  	in, out interface{}
   269  	err     string
   270  }
   271  
   272  var valueConverterTests = []valueConverterTest{
   273  	{driver.DefaultParameterConverter, NullString{"hi", true}, "hi", ""},
   274  	{driver.DefaultParameterConverter, NullString{"", false}, nil, ""},
   275  }
   276  
   277  func TestValueConverters(t *testing.T) {
   278  	for i, tt := range valueConverterTests {
   279  		out, err := tt.c.ConvertValue(tt.in)
   280  		goterr := ""
   281  		if err != nil {
   282  			goterr = err.Error()
   283  		}
   284  		if goterr != tt.err {
   285  			t.Errorf("test %d: %T(%T(%v)) error = %q; want error = %q",
   286  				i, tt.c, tt.in, tt.in, goterr, tt.err)
   287  		}
   288  		if tt.err != "" {
   289  			continue
   290  		}
   291  		if !reflect.DeepEqual(out, tt.out) {
   292  			t.Errorf("test %d: %T(%T(%v)) = %v (%T); want %v (%T)",
   293  				i, tt.c, tt.in, tt.in, out, out, tt.out, tt.out)
   294  		}
   295  	}
   296  }
   297  
   298  // Tests that assigning to RawBytes doesn't allocate (and also works).
   299  func TestRawBytesAllocs(t *testing.T) {
   300  	var tests = []struct {
   301  		name string
   302  		in   interface{}
   303  		want string
   304  	}{
   305  		{"uint64", uint64(12345678), "12345678"},
   306  		{"uint32", uint32(1234), "1234"},
   307  		{"uint16", uint16(12), "12"},
   308  		{"uint8", uint8(1), "1"},
   309  		{"uint", uint(123), "123"},
   310  		{"int", int(123), "123"},
   311  		{"int8", int8(1), "1"},
   312  		{"int16", int16(12), "12"},
   313  		{"int32", int32(1234), "1234"},
   314  		{"int64", int64(12345678), "12345678"},
   315  		{"float32", float32(1.5), "1.5"},
   316  		{"float64", float64(64), "64"},
   317  		{"bool", false, "false"},
   318  	}
   319  
   320  	buf := make(RawBytes, 10)
   321  	test := func(name string, in interface{}, want string) {
   322  		if err := convertAssign(&buf, in); err != nil {
   323  			t.Fatalf("%s: convertAssign = %v", name, err)
   324  		}
   325  		match := len(buf) == len(want)
   326  		if match {
   327  			for i, b := range buf {
   328  				if want[i] != b {
   329  					match = false
   330  					break
   331  				}
   332  			}
   333  		}
   334  		if !match {
   335  			t.Fatalf("%s: got %q (len %d); want %q (len %d)", name, buf, len(buf), want, len(want))
   336  		}
   337  	}
   338  
   339  	n := testing.AllocsPerRun(100, func() {
   340  		for _, tt := range tests {
   341  			test(tt.name, tt.in, tt.want)
   342  		}
   343  	})
   344  
   345  	// The numbers below are only valid for 64-bit interface word sizes,
   346  	// and gc. With 32-bit words there are more convT2E allocs, and
   347  	// with gccgo, only pointers currently go in interface data.
   348  	// So only care on amd64 gc for now.
   349  	measureAllocs := runtime.GOARCH == "amd64" && runtime.Compiler == "gc"
   350  
   351  	if n > 0.5 && measureAllocs {
   352  		t.Fatalf("allocs = %v; want 0", n)
   353  	}
   354  
   355  	// This one involves a convT2E allocation, string -> interface{}
   356  	n = testing.AllocsPerRun(100, func() {
   357  		test("string", "foo", "foo")
   358  	})
   359  	if n > 1.5 && measureAllocs {
   360  		t.Fatalf("allocs = %v; want max 1", n)
   361  	}
   362  }