github.com/XiaoMi/Gaea@v1.2.5/parser/tidb-types/datum_test.go (about)

     1  // Copyright 2016 PingCAP, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package types
    15  
    16  import (
    17  	"reflect"
    18  	"testing"
    19  	"time"
    20  
    21  	. "github.com/pingcap/check"
    22  
    23  	"github.com/XiaoMi/Gaea/mysql"
    24  	"github.com/XiaoMi/Gaea/parser/stmtctx"
    25  	"github.com/XiaoMi/Gaea/parser/tidb-types/json"
    26  )
    27  
    28  var _ = Suite(&testDatumSuite{})
    29  
    30  type testDatumSuite struct {
    31  }
    32  
    33  func (ts *testDatumSuite) TestDatum(c *C) {
    34  	values := []interface{}{
    35  		int64(1),
    36  		uint64(1),
    37  		1.1,
    38  		"abc",
    39  		[]byte("abc"),
    40  		[]int{1},
    41  	}
    42  	for _, val := range values {
    43  		var d Datum
    44  		d.SetValue(val)
    45  		x := d.GetValue()
    46  		c.Assert(x, DeepEquals, val)
    47  	}
    48  }
    49  
    50  func testDatumToBool(c *C, in interface{}, res int) {
    51  	datum := NewDatum(in)
    52  	res64 := int64(res)
    53  	sc := new(stmtctx.StatementContext)
    54  	sc.IgnoreTruncate = true
    55  	b, err := datum.ToBool(sc)
    56  	c.Assert(err, IsNil)
    57  	c.Assert(b, Equals, res64)
    58  }
    59  
    60  func (ts *testDatumSuite) TestToBool(c *C) {
    61  	testDatumToBool(c, int(0), 0)
    62  	testDatumToBool(c, int64(0), 0)
    63  	testDatumToBool(c, uint64(0), 0)
    64  	testDatumToBool(c, float32(0.1), 0)
    65  	testDatumToBool(c, float64(0.1), 0)
    66  	testDatumToBool(c, "", 0)
    67  	testDatumToBool(c, "0.1", 0)
    68  	testDatumToBool(c, []byte{}, 0)
    69  	testDatumToBool(c, []byte("0.1"), 0)
    70  	testDatumToBool(c, NewBinaryLiteralFromUint(0, -1), 0)
    71  	testDatumToBool(c, Enum{Name: "a", Value: 1}, 1)
    72  	testDatumToBool(c, Set{Name: "a", Value: 1}, 1)
    73  
    74  	t, err := ParseTime(&stmtctx.StatementContext{TimeZone: time.UTC}, "2011-11-10 11:11:11.999999", mysql.TypeTimestamp, 6)
    75  	c.Assert(err, IsNil)
    76  	testDatumToBool(c, t, 1)
    77  
    78  	td, err := ParseDuration(nil, "11:11:11.999999", 6)
    79  	c.Assert(err, IsNil)
    80  	testDatumToBool(c, td, 1)
    81  
    82  	ft := NewFieldType(mysql.TypeNewDecimal)
    83  	ft.Decimal = 5
    84  	v, err := Convert(0.1415926, ft)
    85  	c.Assert(err, IsNil)
    86  	testDatumToBool(c, v, 0)
    87  	d := NewDatum(&invalidMockType{})
    88  	sc := new(stmtctx.StatementContext)
    89  	sc.IgnoreTruncate = true
    90  	_, err = d.ToBool(sc)
    91  	c.Assert(err, NotNil)
    92  }
    93  
    94  func (ts *testDatumSuite) TestEqualDatums(c *C) {
    95  	tests := []struct {
    96  		a    []interface{}
    97  		b    []interface{}
    98  		same bool
    99  	}{
   100  		// Positive cases
   101  		{[]interface{}{1}, []interface{}{1}, true},
   102  		{[]interface{}{1, "aa"}, []interface{}{1, "aa"}, true},
   103  		{[]interface{}{1, "aa", 1}, []interface{}{1, "aa", 1}, true},
   104  
   105  		// negative cases
   106  		{[]interface{}{1}, []interface{}{2}, false},
   107  		{[]interface{}{1, "a"}, []interface{}{1, "aaaaaa"}, false},
   108  		{[]interface{}{1, "aa", 3}, []interface{}{1, "aa", 2}, false},
   109  
   110  		// Corner cases
   111  		{[]interface{}{}, []interface{}{}, true},
   112  		{[]interface{}{nil}, []interface{}{nil}, true},
   113  		{[]interface{}{}, []interface{}{1}, false},
   114  		{[]interface{}{1}, []interface{}{1, 1}, false},
   115  		{[]interface{}{nil}, []interface{}{1}, false},
   116  	}
   117  	for _, tt := range tests {
   118  		testEqualDatums(c, tt.a, tt.b, tt.same)
   119  	}
   120  }
   121  
   122  func testEqualDatums(c *C, a []interface{}, b []interface{}, same bool) {
   123  	sc := new(stmtctx.StatementContext)
   124  	sc.IgnoreTruncate = true
   125  	res, err := EqualDatums(sc, MakeDatums(a...), MakeDatums(b...))
   126  	c.Assert(err, IsNil)
   127  	c.Assert(res, Equals, same, Commentf("a: %v, b: %v", a, b))
   128  }
   129  
   130  func testDatumToInt64(c *C, val interface{}, expect int64) {
   131  	d := NewDatum(val)
   132  	sc := new(stmtctx.StatementContext)
   133  	sc.IgnoreTruncate = true
   134  	b, err := d.ToInt64(sc)
   135  	c.Assert(err, IsNil)
   136  	c.Assert(b, Equals, expect)
   137  }
   138  
   139  func (ts *testTypeConvertSuite) TestToInt64(c *C) {
   140  	testDatumToInt64(c, "0", int64(0))
   141  	testDatumToInt64(c, int(0), int64(0))
   142  	testDatumToInt64(c, int64(0), int64(0))
   143  	testDatumToInt64(c, uint64(0), int64(0))
   144  	testDatumToInt64(c, float32(3.1), int64(3))
   145  	testDatumToInt64(c, float64(3.1), int64(3))
   146  	testDatumToInt64(c, NewBinaryLiteralFromUint(100, -1), int64(100))
   147  	testDatumToInt64(c, Enum{Name: "a", Value: 1}, int64(1))
   148  	testDatumToInt64(c, Set{Name: "a", Value: 1}, int64(1))
   149  	testDatumToInt64(c, json.CreateBinary(int64(3)), int64(3))
   150  
   151  	t, err := ParseTime(&stmtctx.StatementContext{
   152  		TimeZone: time.UTC,
   153  	}, "2011-11-10 11:11:11.999999", mysql.TypeTimestamp, 0)
   154  	c.Assert(err, IsNil)
   155  	testDatumToInt64(c, t, int64(20111110111112))
   156  
   157  	td, err := ParseDuration(nil, "11:11:11.999999", 6)
   158  	c.Assert(err, IsNil)
   159  	testDatumToInt64(c, td, int64(111112))
   160  
   161  	ft := NewFieldType(mysql.TypeNewDecimal)
   162  	ft.Decimal = 5
   163  	v, err := Convert(3.1415926, ft)
   164  	c.Assert(err, IsNil)
   165  	testDatumToInt64(c, v, int64(3))
   166  
   167  	binLit, err := ParseHexStr("0x9999999999999999999999999999999999999999999")
   168  	c.Assert(err, IsNil)
   169  	testDatumToInt64(c, binLit, -1)
   170  }
   171  
   172  func (ts *testTypeConvertSuite) TestToFloat32(c *C) {
   173  	ft := NewFieldType(mysql.TypeFloat)
   174  	var datum = NewFloat64Datum(281.37)
   175  	sc := new(stmtctx.StatementContext)
   176  	sc.IgnoreTruncate = true
   177  	converted, err := datum.ConvertTo(sc, ft)
   178  	c.Assert(err, IsNil)
   179  	c.Assert(converted.Kind(), Equals, KindFloat32)
   180  	c.Assert(converted.GetFloat32(), Equals, float32(281.37))
   181  
   182  	datum.SetString("281.37")
   183  	converted, err = datum.ConvertTo(sc, ft)
   184  	c.Assert(err, IsNil)
   185  	c.Assert(converted.Kind(), Equals, KindFloat32)
   186  	c.Assert(converted.GetFloat32(), Equals, float32(281.37))
   187  
   188  	ft = NewFieldType(mysql.TypeDouble)
   189  	datum = NewFloat32Datum(281.37)
   190  	converted, err = datum.ConvertTo(sc, ft)
   191  	c.Assert(err, IsNil)
   192  	c.Assert(converted.Kind(), Equals, KindFloat64)
   193  	// Convert to float32 and convert back to float64, we will get a different value.
   194  	c.Assert(converted.GetFloat64(), Not(Equals), 281.37)
   195  	c.Assert(converted.GetFloat64(), Equals, datum.GetFloat64())
   196  }
   197  
   198  // mustParseTimeIntoDatum is similar to ParseTime but panic if any error occurs.
   199  func mustParseTimeIntoDatum(s string, tp byte, fsp int) (d Datum) {
   200  	t, err := ParseTime(&stmtctx.StatementContext{TimeZone: time.UTC}, s, tp, fsp)
   201  	if err != nil {
   202  		panic("ParseTime fail")
   203  	}
   204  	d.SetMysqlTime(t)
   205  	return
   206  }
   207  
   208  func (ts *testDatumSuite) TestToJSON(c *C) {
   209  	ft := NewFieldType(mysql.TypeJSON)
   210  	sc := new(stmtctx.StatementContext)
   211  	tests := []struct {
   212  		datum    Datum
   213  		expected string
   214  		success  bool
   215  	}{
   216  		{NewIntDatum(1), `1.0`, true},
   217  		{NewFloat64Datum(2), `2`, true},
   218  		{NewStringDatum("\"hello, 世界\""), `"hello, 世界"`, true},
   219  		{NewStringDatum("[1, 2, 3]"), `[1, 2, 3]`, true},
   220  		{NewStringDatum("{}"), `{}`, true},
   221  		{mustParseTimeIntoDatum("2011-11-10 11:11:11.111111", mysql.TypeTimestamp, 6), `"2011-11-10 11:11:11.111111"`, true},
   222  
   223  		// can not parse JSON from this string, so error occurs.
   224  		{NewStringDatum("hello, 世界"), "", false},
   225  	}
   226  	for _, tt := range tests {
   227  		obtain, err := tt.datum.ConvertTo(sc, ft)
   228  		if tt.success {
   229  			c.Assert(err, IsNil)
   230  
   231  			sd := NewStringDatum(tt.expected)
   232  			var expected Datum
   233  			expected, err = sd.ConvertTo(sc, ft)
   234  			c.Assert(err, IsNil)
   235  
   236  			var cmp int
   237  			cmp, err = obtain.CompareDatum(sc, &expected)
   238  			c.Assert(err, IsNil)
   239  			c.Assert(cmp, Equals, 0)
   240  		} else {
   241  			c.Assert(err, NotNil)
   242  		}
   243  	}
   244  }
   245  
   246  func (ts *testDatumSuite) TestIsNull(c *C) {
   247  	tests := []struct {
   248  		data   interface{}
   249  		isnull bool
   250  	}{
   251  		{nil, true},
   252  		{0, false},
   253  		{1, false},
   254  		{1.1, false},
   255  		{"string", false},
   256  		{"", false},
   257  	}
   258  	for _, tt := range tests {
   259  		testIsNull(c, tt.data, tt.isnull)
   260  	}
   261  }
   262  
   263  func testIsNull(c *C, data interface{}, isnull bool) {
   264  	d := NewDatum(data)
   265  	c.Assert(d.IsNull(), Equals, isnull, Commentf("data: %v, isnull: %v", data, isnull))
   266  }
   267  
   268  func (ts *testDatumSuite) TestToBytes(c *C) {
   269  	tests := []struct {
   270  		a   Datum
   271  		out []byte
   272  	}{
   273  		{NewIntDatum(1), []byte("1")},
   274  		{NewDecimalDatum(NewDecFromInt(1)), []byte("1")},
   275  		{NewFloat64Datum(1.23), []byte("1.23")},
   276  		{NewStringDatum("abc"), []byte("abc")},
   277  	}
   278  	sc := new(stmtctx.StatementContext)
   279  	sc.IgnoreTruncate = true
   280  	for _, tt := range tests {
   281  		bin, err := tt.a.ToBytes()
   282  		c.Assert(err, IsNil)
   283  		c.Assert(bin, BytesEquals, tt.out)
   284  	}
   285  }
   286  
   287  func mustParseDurationDatum(str string, fsp int) Datum {
   288  	dur, err := ParseDuration(nil, str, fsp)
   289  	if err != nil {
   290  		panic(err)
   291  	}
   292  	return NewDurationDatum(dur)
   293  }
   294  
   295  func (ts *testDatumSuite) TestComputePlusAndMinus(c *C) {
   296  	sc := &stmtctx.StatementContext{TimeZone: time.UTC}
   297  	tests := []struct {
   298  		a      Datum
   299  		b      Datum
   300  		plus   Datum
   301  		minus  Datum
   302  		hasErr bool
   303  	}{
   304  		{NewIntDatum(72), NewIntDatum(28), NewIntDatum(100), NewIntDatum(44), false},
   305  		{NewIntDatum(72), NewUintDatum(28), NewIntDatum(100), NewIntDatum(44), false},
   306  		{NewUintDatum(72), NewUintDatum(28), NewUintDatum(100), NewUintDatum(44), false},
   307  		{NewUintDatum(72), NewIntDatum(28), NewUintDatum(100), NewUintDatum(44), false},
   308  		{NewFloat64Datum(72.0), NewFloat64Datum(28.0), NewFloat64Datum(100.0), NewFloat64Datum(44.0), false},
   309  		{NewDecimalDatum(NewDecFromStringForTest("72.5")), NewDecimalDatum(NewDecFromInt(3)), NewDecimalDatum(NewDecFromStringForTest("75.5")), NewDecimalDatum(NewDecFromStringForTest("69.5")), false},
   310  		{NewIntDatum(72), NewFloat64Datum(42), Datum{}, Datum{}, true},
   311  		{NewStringDatum("abcd"), NewIntDatum(42), Datum{}, Datum{}, true},
   312  	}
   313  
   314  	for ith, tt := range tests {
   315  		got, err := ComputePlus(tt.a, tt.b)
   316  		c.Assert(err != nil, Equals, tt.hasErr)
   317  		v, err := got.CompareDatum(sc, &tt.plus)
   318  		c.Assert(err, IsNil)
   319  		c.Assert(v, Equals, 0, Commentf("%dth got:%#v, expect:%#v", ith, got, tt.plus))
   320  	}
   321  }
   322  
   323  func (ts *testDatumSuite) TestCopyDatum(c *C) {
   324  	var raw Datum
   325  	raw.b = []byte("raw")
   326  	raw.k = KindRaw
   327  	tests := []Datum{
   328  		NewIntDatum(72),
   329  		NewUintDatum(72),
   330  		NewStringDatum("abcd"),
   331  		NewBytesDatum([]byte("abcd")),
   332  		raw,
   333  	}
   334  
   335  	sc := new(stmtctx.StatementContext)
   336  	sc.IgnoreTruncate = true
   337  	for _, tt := range tests {
   338  		tt1 := CopyDatum(tt)
   339  		res, err := tt.CompareDatum(sc, &tt1)
   340  		c.Assert(err, IsNil)
   341  		c.Assert(res, Equals, 0)
   342  		if tt.b != nil {
   343  			c.Assert(&tt.b[0], Not(Equals), &tt1.b[0])
   344  		}
   345  	}
   346  }
   347  
   348  func prepareCompareDatums() ([]Datum, []Datum) {
   349  	vals := make([]Datum, 0, 5)
   350  	vals = append(vals, NewIntDatum(1))
   351  	vals = append(vals, NewFloat64Datum(1.23))
   352  	vals = append(vals, NewStringDatum("abcde"))
   353  	vals = append(vals, NewDecimalDatum(NewDecFromStringForTest("1.2345")))
   354  	vals = append(vals, NewTimeDatum(Time{Time: FromGoTime(time.Date(2018, 3, 8, 16, 1, 0, 315313000, time.UTC)), Fsp: 6, Type: mysql.TypeTimestamp}))
   355  
   356  	vals1 := make([]Datum, 0, 5)
   357  	vals1 = append(vals1, NewIntDatum(1))
   358  	vals1 = append(vals1, NewFloat64Datum(1.23))
   359  	vals1 = append(vals1, NewStringDatum("abcde"))
   360  	vals1 = append(vals1, NewDecimalDatum(NewDecFromStringForTest("1.2345")))
   361  	vals1 = append(vals1, NewTimeDatum(Time{Time: FromGoTime(time.Date(2018, 3, 8, 16, 1, 0, 315313000, time.UTC)), Fsp: 6, Type: mysql.TypeTimestamp}))
   362  	return vals, vals1
   363  }
   364  
   365  func BenchmarkCompareDatum(b *testing.B) {
   366  	vals, vals1 := prepareCompareDatums()
   367  	sc := new(stmtctx.StatementContext)
   368  	b.ResetTimer()
   369  	for i := 0; i < b.N; i++ {
   370  		for j, v := range vals {
   371  			v.CompareDatum(sc, &vals1[j])
   372  		}
   373  	}
   374  }
   375  
   376  func BenchmarkCompareDatumByReflect(b *testing.B) {
   377  	vals, vals1 := prepareCompareDatums()
   378  	b.ResetTimer()
   379  	for i := 0; i < b.N; i++ {
   380  		reflect.DeepEqual(vals, vals1)
   381  	}
   382  }