Source file src/database/sql/convert_test.go

     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  	"internal/asan"
    11  	"reflect"
    12  	"runtime"
    13  	"strings"
    14  	"sync"
    15  	"testing"
    16  	"time"
    17  	"uuid"
    18  )
    19  
    20  var someTime = time.Unix(123, 0)
    21  var answer int64 = 42
    22  
    23  type (
    24  	userDefined       float64
    25  	userDefinedSlice  []int
    26  	userDefinedString string
    27  )
    28  
    29  type conversionTest struct {
    30  	s, d any // source and destination
    31  
    32  	// following are used if they're non-zero
    33  	wantint    int64
    34  	wantuint   uint64
    35  	wantstr    string
    36  	wantbytes  []byte
    37  	wantraw    RawBytes
    38  	wantf32    float32
    39  	wantf64    float64
    40  	wanttime   time.Time
    41  	wantbool   bool // used if d is of type *bool
    42  	wantuuid   uuid.UUID
    43  	wanterr    string
    44  	wantiface  any
    45  	wantptr    *int64 // if non-nil, *d's pointed value must be equal to *wantptr
    46  	wantnil    bool   // if true, *d must be *int64(nil)
    47  	wantusrdef userDefined
    48  	wantusrstr userDefinedString
    49  }
    50  
    51  // Target variables for scanning into.
    52  var (
    53  	scanstr    string
    54  	scanbytes  []byte
    55  	scanraw    RawBytes
    56  	scanint    int
    57  	scanint64  int64
    58  	scanuint8  uint8
    59  	scanuint16 uint16
    60  	scanbool   bool
    61  	scanf32    float32
    62  	scanf64    float64
    63  	scantime   time.Time
    64  	scanuuid   uuid.UUID
    65  	scanptr    *int64
    66  	scaniface  any
    67  )
    68  
    69  func conversionTests() []conversionTest {
    70  	// Return a fresh instance to test so "go test -count 2" works correctly.
    71  	return []conversionTest{
    72  		// Exact conversions (destination pointer type matches source type)
    73  		{s: "foo", d: &scanstr, wantstr: "foo"},
    74  		{s: 123, d: &scanint, wantint: 123},
    75  		{s: int64(123), d: &scanint64, wantint: 123},
    76  		{s: int64(123), d: &scanint, wantint: 123},
    77  		{s: int64(-123), d: &scanint, wantint: -123},
    78  		{s: someTime, d: &scantime, wanttime: someTime},
    79  
    80  		// To strings
    81  		{s: "string", d: &scanstr, wantstr: "string"},
    82  		{s: []byte("byteslice"), d: &scanstr, wantstr: "byteslice"},
    83  		{s: 123, d: &scanstr, wantstr: "123"},
    84  		{s: int8(123), d: &scanstr, wantstr: "123"},
    85  		{s: int64(123), d: &scanstr, wantstr: "123"},
    86  		{s: uint8(123), d: &scanstr, wantstr: "123"},
    87  		{s: uint16(123), d: &scanstr, wantstr: "123"},
    88  		{s: uint32(123), d: &scanstr, wantstr: "123"},
    89  		{s: uint64(123), d: &scanstr, wantstr: "123"},
    90  		{s: 1.5, d: &scanstr, wantstr: "1.5"},
    91  
    92  		// From time.Time:
    93  		{s: time.Unix(1, 0).UTC(), d: &scanstr, wantstr: "1970-01-01T00:00:01Z"},
    94  		{s: time.Unix(1453874597, 0).In(time.FixedZone("here", -3600*8)), d: &scanstr, wantstr: "2016-01-26T22:03:17-08:00"},
    95  		{s: time.Unix(1, 2).UTC(), d: &scanstr, wantstr: "1970-01-01T00:00:01.000000002Z"},
    96  		{s: time.Time{}, d: &scanstr, wantstr: "0001-01-01T00:00:00Z"},
    97  		{s: time.Unix(1, 2).UTC(), d: &scanbytes, wantbytes: []byte("1970-01-01T00:00:01.000000002Z")},
    98  		{s: time.Unix(1, 2).UTC(), d: &scaniface, wantiface: time.Unix(1, 2).UTC()},
    99  
   100  		// To []byte
   101  		{s: nil, d: &scanbytes, wantbytes: nil},
   102  		{s: "string", d: &scanbytes, wantbytes: []byte("string")},
   103  		{s: []byte("byteslice"), d: &scanbytes, wantbytes: []byte("byteslice")},
   104  		{s: 123, d: &scanbytes, wantbytes: []byte("123")},
   105  		{s: int8(123), d: &scanbytes, wantbytes: []byte("123")},
   106  		{s: int64(123), d: &scanbytes, wantbytes: []byte("123")},
   107  		{s: uint8(123), d: &scanbytes, wantbytes: []byte("123")},
   108  		{s: uint16(123), d: &scanbytes, wantbytes: []byte("123")},
   109  		{s: uint32(123), d: &scanbytes, wantbytes: []byte("123")},
   110  		{s: uint64(123), d: &scanbytes, wantbytes: []byte("123")},
   111  		{s: 1.5, d: &scanbytes, wantbytes: []byte("1.5")},
   112  
   113  		// To RawBytes
   114  		{s: nil, d: &scanraw, wantraw: nil},
   115  		{s: []byte("byteslice"), d: &scanraw, wantraw: RawBytes("byteslice")},
   116  		{s: "string", d: &scanraw, wantraw: RawBytes("string")},
   117  		{s: 123, d: &scanraw, wantraw: RawBytes("123")},
   118  		{s: int8(123), d: &scanraw, wantraw: RawBytes("123")},
   119  		{s: int64(123), d: &scanraw, wantraw: RawBytes("123")},
   120  		{s: uint8(123), d: &scanraw, wantraw: RawBytes("123")},
   121  		{s: uint16(123), d: &scanraw, wantraw: RawBytes("123")},
   122  		{s: uint32(123), d: &scanraw, wantraw: RawBytes("123")},
   123  		{s: uint64(123), d: &scanraw, wantraw: RawBytes("123")},
   124  		{s: 1.5, d: &scanraw, wantraw: RawBytes("1.5")},
   125  		// time.Time has been placed here to check that the RawBytes slice gets
   126  		// correctly reset when calling time.Time.AppendFormat.
   127  		{s: time.Unix(2, 5).UTC(), d: &scanraw, wantraw: RawBytes("1970-01-01T00:00:02.000000005Z")},
   128  
   129  		// Strings to integers
   130  		{s: "255", d: &scanuint8, wantuint: 255},
   131  		{s: "256", d: &scanuint8, wanterr: "converting driver.Value type string (\"256\") to a uint8: value out of range"},
   132  		{s: "256", d: &scanuint16, wantuint: 256},
   133  		{s: "-1", d: &scanint, wantint: -1},
   134  		{s: "foo", d: &scanint, wanterr: "converting driver.Value type string (\"foo\") to a int: invalid syntax"},
   135  
   136  		// int64 to smaller integers
   137  		{s: int64(5), d: &scanuint8, wantuint: 5},
   138  		{s: int64(256), d: &scanuint8, wanterr: "converting driver.Value type int64 (\"256\") to a uint8: value out of range"},
   139  		{s: int64(256), d: &scanuint16, wantuint: 256},
   140  		{s: int64(65536), d: &scanuint16, wanterr: "converting driver.Value type int64 (\"65536\") to a uint16: value out of range"},
   141  
   142  		// True bools
   143  		{s: true, d: &scanbool, wantbool: true},
   144  		{s: "True", d: &scanbool, wantbool: true},
   145  		{s: "TRUE", d: &scanbool, wantbool: true},
   146  		{s: "1", d: &scanbool, wantbool: true},
   147  		{s: 1, d: &scanbool, wantbool: true},
   148  		{s: int64(1), d: &scanbool, wantbool: true},
   149  		{s: uint16(1), d: &scanbool, wantbool: true},
   150  
   151  		// False bools
   152  		{s: false, d: &scanbool, wantbool: false},
   153  		{s: "false", d: &scanbool, wantbool: false},
   154  		{s: "FALSE", d: &scanbool, wantbool: false},
   155  		{s: "0", d: &scanbool, wantbool: false},
   156  		{s: 0, d: &scanbool, wantbool: false},
   157  		{s: int64(0), d: &scanbool, wantbool: false},
   158  		{s: uint16(0), d: &scanbool, wantbool: false},
   159  
   160  		// Not bools
   161  		{s: "yup", d: &scanbool, wanterr: `sql/driver: couldn't convert "yup" into type bool`},
   162  		{s: 2, d: &scanbool, wanterr: `sql/driver: couldn't convert 2 into type bool`},
   163  
   164  		// Floats
   165  		{s: float64(1.5), d: &scanf64, wantf64: float64(1.5)},
   166  		{s: int64(1), d: &scanf64, wantf64: float64(1)},
   167  		{s: float64(1.5), d: &scanf32, wantf32: float32(1.5)},
   168  		{s: "1.5", d: &scanf32, wantf32: float32(1.5)},
   169  		{s: "1.5", d: &scanf64, wantf64: float64(1.5)},
   170  
   171  		// UUIDs
   172  		{s: "97b6a4e0-5323-43e9-82c8-88110d6686d6", d: &scanuuid, wantuuid: uuid.MustParse("97b6a4e0-5323-43e9-82c8-88110d6686d6")},
   173  		{s: "97B6A4E0-5323-43E9-82C8-88110D6686D6", d: &scanuuid, wantuuid: uuid.MustParse("97b6a4e0-5323-43e9-82c8-88110d6686d6")},
   174  		{s: "x", d: &scanuuid, wanterr: `converting driver.Value type string ("x") to a UUID: invalid uuid`},
   175  		{s: []byte("97b6a4e0-5323-43e9-82c8-88110d6686d6"), d: &scanuuid, wantuuid: uuid.MustParse("97b6a4e0-5323-43e9-82c8-88110d6686d6")},
   176  		{s: []byte("97B6A4E0-5323-43E9-82C8-88110D6686D6"), d: &scanuuid, wantuuid: uuid.MustParse("97b6a4e0-5323-43e9-82c8-88110d6686d6")},
   177  		{s: []byte{0x97, 0xb6, 0xa4, 0xe0, 0x53, 0x23, 0x43, 0xe9, 0x82, 0xc8, 0x88, 0x11, 0x0d, 0x66, 0x86, 0xd6}, d: &scanuuid, wantuuid: uuid.MustParse("97b6a4e0-5323-43e9-82c8-88110d6686d6")},
   178  		{s: []byte("x"), d: &scanuuid, wanterr: `converting driver.Value type []byte ("x") to a UUID: invalid uuid`},
   179  
   180  		// Pointers
   181  		{s: any(nil), d: &scanptr, wantnil: true},
   182  		{s: int64(42), d: &scanptr, wantptr: &answer},
   183  
   184  		// To interface{}
   185  		{s: float64(1.5), d: &scaniface, wantiface: float64(1.5)},
   186  		{s: int64(1), d: &scaniface, wantiface: int64(1)},
   187  		{s: "str", d: &scaniface, wantiface: "str"},
   188  		{s: []byte("byteslice"), d: &scaniface, wantiface: []byte("byteslice")},
   189  		{s: true, d: &scaniface, wantiface: true},
   190  		{s: nil, d: &scaniface},
   191  		{s: []byte(nil), d: &scaniface, wantiface: []byte(nil)},
   192  
   193  		// To a user-defined type
   194  		{s: 1.5, d: new(userDefined), wantusrdef: 1.5},
   195  		{s: int64(123), d: new(userDefined), wantusrdef: 123},
   196  		{s: "1.5", d: new(userDefined), wantusrdef: 1.5},
   197  		{s: []byte{1, 2, 3}, d: new(userDefinedSlice), wanterr: `unsupported Scan, storing driver.Value type []uint8 into type *sql.userDefinedSlice`},
   198  		{s: "str", d: new(userDefinedString), wantusrstr: "str"},
   199  
   200  		// Other errors
   201  		{s: complex(1, 2), d: &scanstr, wanterr: `unsupported Scan, storing driver.Value type complex128 into type *string`},
   202  	}
   203  }
   204  
   205  func intPtrValue(intptr any) any {
   206  	return reflect.Indirect(reflect.Indirect(reflect.ValueOf(intptr))).Int()
   207  }
   208  
   209  func intValue(intptr any) int64 {
   210  	return reflect.Indirect(reflect.ValueOf(intptr)).Int()
   211  }
   212  
   213  func uintValue(intptr any) uint64 {
   214  	return reflect.Indirect(reflect.ValueOf(intptr)).Uint()
   215  }
   216  
   217  func float64Value(ptr any) float64 {
   218  	return *(ptr.(*float64))
   219  }
   220  
   221  func float32Value(ptr any) float32 {
   222  	return *(ptr.(*float32))
   223  }
   224  
   225  func timeValue(ptr any) time.Time {
   226  	return *(ptr.(*time.Time))
   227  }
   228  
   229  func uuidValue(ptr any) uuid.UUID {
   230  	return *(ptr.(*uuid.UUID))
   231  }
   232  
   233  func TestConversions(t *testing.T) {
   234  	for n, ct := range conversionTests() {
   235  		err := convertAssign(ct.d, ct.s)
   236  		errstr := ""
   237  		if err != nil {
   238  			errstr = err.Error()
   239  		}
   240  		errf := func(format string, args ...any) {
   241  			base := fmt.Sprintf("convertAssign #%d: for %v (%T) -> %T, ", n, ct.s, ct.s, ct.d)
   242  			t.Errorf(base+format, args...)
   243  		}
   244  		if errstr != ct.wanterr {
   245  			errf("got error %q, want error %q", errstr, ct.wanterr)
   246  		}
   247  		if ct.wantstr != "" && ct.wantstr != scanstr {
   248  			errf("want string %q, got %q", ct.wantstr, scanstr)
   249  		}
   250  		if ct.wantbytes != nil && string(ct.wantbytes) != string(scanbytes) {
   251  			errf("want byte %q, got %q", ct.wantbytes, scanbytes)
   252  		}
   253  		if ct.wantraw != nil && string(ct.wantraw) != string(scanraw) {
   254  			errf("want RawBytes %q, got %q", ct.wantraw, scanraw)
   255  		}
   256  		if ct.wantint != 0 && ct.wantint != intValue(ct.d) {
   257  			errf("want int %d, got %d", ct.wantint, intValue(ct.d))
   258  		}
   259  		if ct.wantuint != 0 && ct.wantuint != uintValue(ct.d) {
   260  			errf("want uint %d, got %d", ct.wantuint, uintValue(ct.d))
   261  		}
   262  		if ct.wantf32 != 0 && ct.wantf32 != float32Value(ct.d) {
   263  			errf("want float32 %v, got %v", ct.wantf32, float32Value(ct.d))
   264  		}
   265  		if ct.wantf64 != 0 && ct.wantf64 != float64Value(ct.d) {
   266  			errf("want float32 %v, got %v", ct.wantf64, float64Value(ct.d))
   267  		}
   268  		if bp, boolTest := ct.d.(*bool); boolTest && *bp != ct.wantbool && ct.wanterr == "" {
   269  			errf("want bool %v, got %v", ct.wantbool, *bp)
   270  		}
   271  		if !ct.wanttime.IsZero() && !ct.wanttime.Equal(timeValue(ct.d)) {
   272  			errf("want time %v, got %v", ct.wanttime, timeValue(ct.d))
   273  		}
   274  		if ct.wantuuid != (uuid.UUID{}) && ct.wantuuid != uuidValue(ct.d) {
   275  			errf("want uuid %v, got %v", ct.wantuuid, uuidValue(ct.d))
   276  		}
   277  		if ct.wantnil && *ct.d.(**int64) != nil {
   278  			errf("want nil, got %v", intPtrValue(ct.d))
   279  		}
   280  		if ct.wantptr != nil {
   281  			if *ct.d.(**int64) == nil {
   282  				errf("want pointer to %v, got nil", *ct.wantptr)
   283  			} else if *ct.wantptr != intPtrValue(ct.d) {
   284  				errf("want pointer to %v, got %v", *ct.wantptr, intPtrValue(ct.d))
   285  			}
   286  		}
   287  		if ifptr, ok := ct.d.(*any); ok {
   288  			if !reflect.DeepEqual(ct.wantiface, scaniface) {
   289  				errf("want interface %#v, got %#v", ct.wantiface, scaniface)
   290  				continue
   291  			}
   292  			if srcBytes, ok := ct.s.([]byte); ok {
   293  				dstBytes := (*ifptr).([]byte)
   294  				if len(srcBytes) > 0 && &dstBytes[0] == &srcBytes[0] {
   295  					errf("copy into interface{} didn't copy []byte data")
   296  				}
   297  			}
   298  		}
   299  		if ct.wantusrdef != 0 && ct.wantusrdef != *ct.d.(*userDefined) {
   300  			errf("want userDefined %f, got %f", ct.wantusrdef, *ct.d.(*userDefined))
   301  		}
   302  		if len(ct.wantusrstr) != 0 && ct.wantusrstr != *ct.d.(*userDefinedString) {
   303  			errf("want userDefined %q, got %q", ct.wantusrstr, *ct.d.(*userDefinedString))
   304  		}
   305  	}
   306  }
   307  
   308  func TestNullString(t *testing.T) {
   309  	var ns NullString
   310  	convertAssign(&ns, []byte("foo"))
   311  	if !ns.Valid {
   312  		t.Errorf("expecting not null")
   313  	}
   314  	if ns.String != "foo" {
   315  		t.Errorf("expecting foo; got %q", ns.String)
   316  	}
   317  	convertAssign(&ns, nil)
   318  	if ns.Valid {
   319  		t.Errorf("expecting null on nil")
   320  	}
   321  	if ns.String != "" {
   322  		t.Errorf("expecting blank on nil; got %q", ns.String)
   323  	}
   324  }
   325  
   326  type valueConverterTest struct {
   327  	c       driver.ValueConverter
   328  	in, out any
   329  	err     string
   330  }
   331  
   332  var valueConverterTests = []valueConverterTest{
   333  	{driver.DefaultParameterConverter, NullString{"hi", true}, "hi", ""},
   334  	{driver.DefaultParameterConverter, NullString{"", false}, nil, ""},
   335  }
   336  
   337  func TestValueConverters(t *testing.T) {
   338  	for i, tt := range valueConverterTests {
   339  		out, err := tt.c.ConvertValue(tt.in)
   340  		goterr := ""
   341  		if err != nil {
   342  			goterr = err.Error()
   343  		}
   344  		if goterr != tt.err {
   345  			t.Errorf("test %d: %T(%T(%v)) error = %q; want error = %q",
   346  				i, tt.c, tt.in, tt.in, goterr, tt.err)
   347  		}
   348  		if tt.err != "" {
   349  			continue
   350  		}
   351  		if !reflect.DeepEqual(out, tt.out) {
   352  			t.Errorf("test %d: %T(%T(%v)) = %v (%T); want %v (%T)",
   353  				i, tt.c, tt.in, tt.in, out, out, tt.out, tt.out)
   354  		}
   355  	}
   356  }
   357  
   358  // Tests that assigning to RawBytes doesn't allocate (and also works).
   359  func TestRawBytesAllocs(t *testing.T) {
   360  	var tests = []struct {
   361  		name string
   362  		in   any
   363  		want string
   364  	}{
   365  		{"uint64", uint64(12345678), "12345678"},
   366  		{"uint32", uint32(1234), "1234"},
   367  		{"uint16", uint16(12), "12"},
   368  		{"uint8", uint8(1), "1"},
   369  		{"uint", uint(123), "123"},
   370  		{"int", int(123), "123"},
   371  		{"int8", int8(1), "1"},
   372  		{"int16", int16(12), "12"},
   373  		{"int32", int32(1234), "1234"},
   374  		{"int64", int64(12345678), "12345678"},
   375  		{"float32", float32(1.5), "1.5"},
   376  		{"float64", float64(64), "64"},
   377  		{"bool", false, "false"},
   378  		{"time", time.Unix(2, 5).UTC(), "1970-01-01T00:00:02.000000005Z"},
   379  	}
   380  	if asan.Enabled {
   381  		t.Skip("test allocates more with -asan; see #70079")
   382  	}
   383  
   384  	var buf RawBytes
   385  	rows := &Rows{}
   386  	test := func(name string, in any, want string) {
   387  		if err := convertAssignRows(&buf, in, rows); err != nil {
   388  			t.Fatalf("%s: convertAssign = %v", name, err)
   389  		}
   390  		match := len(buf) == len(want)
   391  		if match {
   392  			for i, b := range buf {
   393  				if want[i] != b {
   394  					match = false
   395  					break
   396  				}
   397  			}
   398  		}
   399  		if !match {
   400  			t.Fatalf("%s: got %q (len %d); want %q (len %d)", name, buf, len(buf), want, len(want))
   401  		}
   402  	}
   403  
   404  	n := testing.AllocsPerRun(100, func() {
   405  		for _, tt := range tests {
   406  			rows.raw = rows.raw[:0]
   407  			test(tt.name, tt.in, tt.want)
   408  		}
   409  	})
   410  
   411  	// The numbers below are only valid for 64-bit interface word sizes,
   412  	// and gc. With 32-bit words there are more convT2E allocs, and
   413  	// with gccgo, only pointers currently go in interface data.
   414  	// So only care on amd64 gc for now.
   415  	measureAllocs := false
   416  	switch runtime.GOARCH {
   417  	case "amd64", "arm64":
   418  		measureAllocs = runtime.Compiler == "gc"
   419  	}
   420  
   421  	if n > 0.5 && measureAllocs {
   422  		t.Fatalf("allocs = %v; want 0", n)
   423  	}
   424  
   425  	// This one involves a convT2E allocation, string -> interface{}
   426  	n = testing.AllocsPerRun(100, func() {
   427  		test("string", "foo", "foo")
   428  	})
   429  	if n > 1.5 && measureAllocs {
   430  		t.Fatalf("allocs = %v; want max 1", n)
   431  	}
   432  }
   433  
   434  // https://golang.org/issues/13905
   435  func TestUserDefinedBytes(t *testing.T) {
   436  	type userDefinedBytes []byte
   437  	var u userDefinedBytes
   438  	v := []byte("foo")
   439  
   440  	convertAssign(&u, v)
   441  	if &u[0] == &v[0] {
   442  		t.Fatal("userDefinedBytes got potentially dirty driver memory")
   443  	}
   444  }
   445  
   446  type Valuer_V string
   447  
   448  func (v Valuer_V) Value() (driver.Value, error) {
   449  	return strings.ToUpper(string(v)), nil
   450  }
   451  
   452  type Valuer_P string
   453  
   454  func (p *Valuer_P) Value() (driver.Value, error) {
   455  	if p == nil {
   456  		return "nil-to-str", nil
   457  	}
   458  	return strings.ToUpper(string(*p)), nil
   459  }
   460  
   461  func TestDriverArgs(t *testing.T) {
   462  	var nilValuerVPtr *Valuer_V
   463  	var nilValuerPPtr *Valuer_P
   464  	var nilStrPtr *string
   465  	tests := []struct {
   466  		args []any
   467  		want []driver.NamedValue
   468  	}{
   469  		0: {
   470  			args: []any{Valuer_V("foo")},
   471  			want: []driver.NamedValue{
   472  				{
   473  					Ordinal: 1,
   474  					Value:   "FOO",
   475  				},
   476  			},
   477  		},
   478  		1: {
   479  			args: []any{nilValuerVPtr},
   480  			want: []driver.NamedValue{
   481  				{
   482  					Ordinal: 1,
   483  					Value:   nil,
   484  				},
   485  			},
   486  		},
   487  		2: {
   488  			args: []any{nilValuerPPtr},
   489  			want: []driver.NamedValue{
   490  				{
   491  					Ordinal: 1,
   492  					Value:   "nil-to-str",
   493  				},
   494  			},
   495  		},
   496  		3: {
   497  			args: []any{"plain-str"},
   498  			want: []driver.NamedValue{
   499  				{
   500  					Ordinal: 1,
   501  					Value:   "plain-str",
   502  				},
   503  			},
   504  		},
   505  		4: {
   506  			args: []any{nilStrPtr},
   507  			want: []driver.NamedValue{
   508  				{
   509  					Ordinal: 1,
   510  					Value:   nil,
   511  				},
   512  			},
   513  		},
   514  	}
   515  	for i, tt := range tests {
   516  		ds := &driverStmt{Locker: &sync.Mutex{}, si: stubDriverStmt{nil}}
   517  		got, err := driverArgsConnLocked(nil, ds, tt.args)
   518  		if err != nil {
   519  			t.Errorf("test[%d]: %v", i, err)
   520  			continue
   521  		}
   522  		if !reflect.DeepEqual(got, tt.want) {
   523  			t.Errorf("test[%d]: got %v, want %v", i, got, tt.want)
   524  		}
   525  	}
   526  }
   527  
   528  type dec struct {
   529  	form        byte
   530  	neg         bool
   531  	coefficient [16]byte
   532  	exponent    int32
   533  }
   534  
   535  func (d dec) Decompose(buf []byte) (form byte, negative bool, coefficient []byte, exponent int32) {
   536  	coef := make([]byte, 16)
   537  	copy(coef, d.coefficient[:])
   538  	return d.form, d.neg, coef, d.exponent
   539  }
   540  
   541  func (d *dec) Compose(form byte, negative bool, coefficient []byte, exponent int32) error {
   542  	switch form {
   543  	default:
   544  		return fmt.Errorf("unknown form %d", form)
   545  	case 1, 2:
   546  		d.form = form
   547  		d.neg = negative
   548  		return nil
   549  	case 0:
   550  	}
   551  	d.form = form
   552  	d.neg = negative
   553  	d.exponent = exponent
   554  
   555  	// This isn't strictly correct, as the extra bytes could be all zero,
   556  	// ignore this for this test.
   557  	if len(coefficient) > 16 {
   558  		return fmt.Errorf("coefficient too large")
   559  	}
   560  	copy(d.coefficient[:], coefficient)
   561  
   562  	return nil
   563  }
   564  
   565  type decFinite struct {
   566  	neg         bool
   567  	coefficient [16]byte
   568  	exponent    int32
   569  }
   570  
   571  func (d decFinite) Decompose(buf []byte) (form byte, negative bool, coefficient []byte, exponent int32) {
   572  	coef := make([]byte, 16)
   573  	copy(coef, d.coefficient[:])
   574  	return 0, d.neg, coef, d.exponent
   575  }
   576  
   577  func (d *decFinite) Compose(form byte, negative bool, coefficient []byte, exponent int32) error {
   578  	switch form {
   579  	default:
   580  		return fmt.Errorf("unknown form %d", form)
   581  	case 1, 2:
   582  		return fmt.Errorf("unsupported form %d", form)
   583  	case 0:
   584  	}
   585  	d.neg = negative
   586  	d.exponent = exponent
   587  
   588  	// This isn't strictly correct, as the extra bytes could be all zero,
   589  	// ignore this for this test.
   590  	if len(coefficient) > 16 {
   591  		return fmt.Errorf("coefficient too large")
   592  	}
   593  	copy(d.coefficient[:], coefficient)
   594  
   595  	return nil
   596  }
   597  
   598  func TestDecimal(t *testing.T) {
   599  	list := []struct {
   600  		name string
   601  		in   decimalDecompose
   602  		out  dec
   603  		err  bool
   604  	}{
   605  		{name: "same", in: dec{exponent: -6}, out: dec{exponent: -6}},
   606  
   607  		// Ensure reflection is not used to assign the value by using different types.
   608  		{name: "diff", in: decFinite{exponent: -6}, out: dec{exponent: -6}},
   609  
   610  		{name: "bad-form", in: dec{form: 200}, err: true},
   611  	}
   612  	for _, item := range list {
   613  		t.Run(item.name, func(t *testing.T) {
   614  			out := dec{}
   615  			err := convertAssign(&out, item.in)
   616  			if item.err {
   617  				if err == nil {
   618  					t.Fatalf("unexpected nil error")
   619  				}
   620  				return
   621  			}
   622  			if err != nil {
   623  				t.Fatalf("unexpected error: %v", err)
   624  			}
   625  			if !reflect.DeepEqual(out, item.out) {
   626  				t.Fatalf("got %#v want %#v", out, item.out)
   627  			}
   628  		})
   629  	}
   630  }
   631  
   632  func TestConvertAssignNoContext(t *testing.T) {
   633  	const want = 42
   634  	var got int64
   635  	if err := ConvertAssign(driver.ScanContext{}, &got, want); err != nil {
   636  		t.Fatalf("ConvertAssign: %v", err)
   637  	}
   638  	if got != int64(want) {
   639  		t.Errorf("after ConvertAssign: got %v, want %v", got, want)
   640  	}
   641  }
   642  

View as plain text