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

     1  // Copyright 2017 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 json
    15  
    16  import (
    17  	"testing"
    18  
    19  	. "github.com/pingcap/check"
    20  )
    21  
    22  var _ = Suite(&testJSONSuite{})
    23  
    24  type testJSONSuite struct{}
    25  
    26  func TestT(t *testing.T) {
    27  	TestingT(t)
    28  }
    29  
    30  func (s *testJSONSuite) TestBinaryJSONMarshalUnmarshal(c *C) {
    31  	strs := []string{
    32  		`{"a": [1, "2", {"aa": "bb"}, 4, null], "b": true, "c": null}`,
    33  		`{"aaaaaaaaaaa": [1, "2", {"aa": "bb"}, 4.1], "bbbbbbbbbb": true, "ccccccccc": "d"}`,
    34  		`[{"a": 1, "b": true}, 3, 3.5, "hello, world", null, true]`,
    35  	}
    36  	for _, str := range strs {
    37  		parsedBJ := mustParseBinaryFromString(c, str)
    38  		c.Assert(parsedBJ.String(), Equals, str)
    39  	}
    40  }
    41  
    42  func (s *testJSONSuite) TestBinaryJSONExtract(c *C) {
    43  	bj1 := mustParseBinaryFromString(c, `{"\"hello\"": "world", "a": [1, "2", {"aa": "bb"}, 4.0, {"aa": "cc"}], "b": true, "c": ["d"]}`)
    44  	bj2 := mustParseBinaryFromString(c, `[{"a": 1, "b": true}, 3, 3.5, "hello, world", null, true]`)
    45  
    46  	var tests = []struct {
    47  		bj              BinaryJSON
    48  		pathExprStrings []string
    49  		expected        BinaryJSON
    50  		found           bool
    51  		err             error
    52  	}{
    53  		// test extract with only one path expression.
    54  		{bj1, []string{"$.a"}, mustParseBinaryFromString(c, `[1, "2", {"aa": "bb"}, 4.0, {"aa": "cc"}]`), true, nil},
    55  		{bj2, []string{"$.a"}, mustParseBinaryFromString(c, "null"), false, nil},
    56  		{bj1, []string{"$[0]"}, bj1, true, nil}, // in Extract, autowraped bj1 as an array.
    57  		{bj2, []string{"$[0]"}, mustParseBinaryFromString(c, `{"a": 1, "b": true}`), true, nil},
    58  		{bj1, []string{"$.a[2].aa"}, mustParseBinaryFromString(c, `"bb"`), true, nil},
    59  		{bj1, []string{"$.a[*].aa"}, mustParseBinaryFromString(c, `["bb", "cc"]`), true, nil},
    60  		{bj1, []string{"$.*[0]"}, mustParseBinaryFromString(c, `["world", 1, true, "d"]`), true, nil},
    61  		{bj1, []string{`$.a[*]."aa"`}, mustParseBinaryFromString(c, `["bb", "cc"]`), true, nil},
    62  		{bj1, []string{`$."\"hello\""`}, mustParseBinaryFromString(c, `"world"`), true, nil},
    63  		{bj1, []string{`$**[1]`}, mustParseBinaryFromString(c, `"2"`), true, nil},
    64  
    65  		// test extract with multi path expressions.
    66  		{bj1, []string{"$.a", "$[5]"}, mustParseBinaryFromString(c, `[[1, "2", {"aa": "bb"}, 4.0, {"aa": "cc"}]]`), true, nil},
    67  		{bj2, []string{"$.a", "$[0]"}, mustParseBinaryFromString(c, `[{"a": 1, "b": true}]`), true, nil},
    68  	}
    69  
    70  	for _, tt := range tests {
    71  		var pathExprList = make([]PathExpression, 0)
    72  		for _, peStr := range tt.pathExprStrings {
    73  			pe, err := ParseJSONPathExpr(peStr)
    74  			c.Assert(err, IsNil)
    75  			pathExprList = append(pathExprList, pe)
    76  		}
    77  
    78  		result, found := tt.bj.Extract(pathExprList)
    79  		c.Assert(found, Equals, tt.found)
    80  		if found {
    81  			c.Assert(result.String(), Equals, tt.expected.String())
    82  		}
    83  	}
    84  }
    85  
    86  func (s *testJSONSuite) TestBinaryJSONType(c *C) {
    87  	var tests = []struct {
    88  		In  string
    89  		Out string
    90  	}{
    91  		{`{"a": "b"}`, "OBJECT"},
    92  		{`["a", "b"]`, "ARRAY"},
    93  		{`3`, "INTEGER"},
    94  		{`3.0`, "DOUBLE"},
    95  		{`null`, "NULL"},
    96  		{`true`, "BOOLEAN"},
    97  	}
    98  	for _, tt := range tests {
    99  		bj := mustParseBinaryFromString(c, tt.In)
   100  		c.Assert(bj.Type(), Equals, tt.Out)
   101  	}
   102  	// we can't parse '9223372036854775808' to JSON::Uint64 now,
   103  	// because go builtin JSON parser treats that as DOUBLE.
   104  	c.Assert(CreateBinary(uint64(1<<63)).Type(), Equals, "UNSIGNED INTEGER")
   105  }
   106  
   107  func (s *testJSONSuite) TestBinaryJSONUnquote(c *C) {
   108  	var tests = []struct {
   109  		j        string
   110  		unquoted string
   111  	}{
   112  		{j: `3`, unquoted: "3"},
   113  		{j: `"3"`, unquoted: "3"},
   114  		{j: `"hello, \"escaped quotes\" world"`, unquoted: "hello, \"escaped quotes\" world"},
   115  		{j: "\"\\u4f60\"", unquoted: "你"},
   116  		{j: `true`, unquoted: "true"},
   117  		{j: `null`, unquoted: "null"},
   118  		{j: `{"a": [1, 2]}`, unquoted: `{"a": [1, 2]}`},
   119  		{j: `"\""`, unquoted: `"`},
   120  		{j: `"'"`, unquoted: `'`},
   121  		{j: `"''"`, unquoted: `''`},
   122  		{j: `""`, unquoted: ``},
   123  	}
   124  	for _, tt := range tests {
   125  		bj := mustParseBinaryFromString(c, tt.j)
   126  		unquoted, err := bj.Unquote()
   127  		c.Assert(err, IsNil)
   128  		c.Assert(unquoted, Equals, tt.unquoted)
   129  	}
   130  }
   131  
   132  func (s *testJSONSuite) TestBinaryJSONModify(c *C) {
   133  	var tests = []struct {
   134  		base     string
   135  		setField string
   136  		setValue string
   137  		mt       ModifyType
   138  		expected string
   139  		success  bool
   140  	}{
   141  		{`null`, "$", `{}`, ModifySet, `{}`, true},
   142  		{`{}`, "$.a", `3`, ModifySet, `{"a": 3}`, true},
   143  		{`{"a": 3}`, "$.a", `[]`, ModifyReplace, `{"a": []}`, true},
   144  		{`{"a": 3}`, "$.b", `"3"`, ModifySet, `{"a": 3, "b": "3"}`, true},
   145  		{`{"a": []}`, "$.a[0]", `3`, ModifySet, `{"a": [3]}`, true},
   146  		{`{"a": [3]}`, "$.a[1]", `4`, ModifyInsert, `{"a": [3, 4]}`, true},
   147  		{`{"a": [3]}`, "$[0]", `4`, ModifySet, `4`, true},
   148  		{`{"a": [3]}`, "$[1]", `4`, ModifySet, `[{"a": [3]}, 4]`, true},
   149  		{`{"b": true}`, "$.b", `false`, ModifySet, `{"b": false}`, true},
   150  
   151  		// nothing changed because the path is empty and we want to insert.
   152  		{`{}`, "$", `1`, ModifyInsert, `{}`, true},
   153  		// nothing changed because the path without last leg doesn't exist.
   154  		{`{"a": [3, 4]}`, "$.b[1]", `3`, ModifySet, `{"a": [3, 4]}`, true},
   155  		// nothing changed because the path without last leg doesn't exist.
   156  		{`{"a": [3, 4]}`, "$.a[2].b", `3`, ModifySet, `{"a": [3, 4]}`, true},
   157  		// nothing changed because we want to insert but the full path exists.
   158  		{`{"a": [3, 4]}`, "$.a[0]", `30`, ModifyInsert, `{"a": [3, 4]}`, true},
   159  		// nothing changed because we want to replace but the full path doesn't exist.
   160  		{`{"a": [3, 4]}`, "$.a[2]", `30`, ModifyReplace, `{"a": [3, 4]}`, true},
   161  
   162  		// bad path expression.
   163  		{"null", "$.*", "{}", ModifySet, "null", false},
   164  		{"null", "$[*]", "{}", ModifySet, "null", false},
   165  		{"null", "$**.a", "{}", ModifySet, "null", false},
   166  		{"null", "$**[3]", "{}", ModifySet, "null", false},
   167  	}
   168  	for _, tt := range tests {
   169  		pathExpr, err := ParseJSONPathExpr(tt.setField)
   170  		c.Assert(err, IsNil)
   171  
   172  		base := mustParseBinaryFromString(c, tt.base)
   173  		value := mustParseBinaryFromString(c, tt.setValue)
   174  		expected := mustParseBinaryFromString(c, tt.expected)
   175  		obtain, err := base.Modify([]PathExpression{pathExpr}, []BinaryJSON{value}, tt.mt)
   176  		if tt.success {
   177  			c.Assert(err, IsNil)
   178  			c.Assert(obtain.String(), Equals, expected.String())
   179  		} else {
   180  			c.Assert(err, NotNil)
   181  		}
   182  	}
   183  }
   184  
   185  func (s *testJSONSuite) TestBinaryJSONRemove(c *C) {
   186  	var tests = []struct {
   187  		base     string
   188  		path     string
   189  		expected string
   190  		success  bool
   191  	}{
   192  		{`null`, "$", `{}`, false},
   193  		{`{"a":[3]}`, "$.a[*]", `{"a":[3]}`, false},
   194  		{`{}`, "$.a", `{}`, true},
   195  		{`{"a":3}`, "$.a", `{}`, true},
   196  		{`{"a":1,"b":2,"c":3}`, "$.b", `{"a":1,"c":3}`, true},
   197  		{`{"a":1,"b":2,"c":3}`, "$.d", `{"a":1,"b":2,"c":3}`, true},
   198  		{`{"a":3}`, "$[0]", `{"a":3}`, true},
   199  		{`{"a":[3,4,5]}`, "$.a[0]", `{"a":[4,5]}`, true},
   200  		{`{"a":[3,4,5]}`, "$.a[1]", `{"a":[3,5]}`, true},
   201  		{`{"a":[3,4,5]}`, "$.a[4]", `{"a":[3,4,5]}`, true},
   202  		{`{"a": [1, 2, {"aa": "xx"}]}`, "$.a[2].aa", `{"a": [1, 2, {}]}`, true},
   203  	}
   204  	for _, tt := range tests {
   205  		pathExpr, err := ParseJSONPathExpr(tt.path)
   206  		c.Assert(err, IsNil)
   207  
   208  		base := mustParseBinaryFromString(c, tt.base)
   209  		expected := mustParseBinaryFromString(c, tt.expected)
   210  		obtain, err := base.Remove([]PathExpression{pathExpr})
   211  		if tt.success {
   212  			c.Assert(err, IsNil)
   213  			c.Assert(obtain.String(), Equals, expected.String())
   214  		} else {
   215  			c.Assert(err, NotNil)
   216  		}
   217  	}
   218  }
   219  
   220  func (s *testJSONSuite) TestCompareBinary(c *C) {
   221  	jNull := mustParseBinaryFromString(c, `null`)
   222  	jBoolTrue := mustParseBinaryFromString(c, `true`)
   223  	jBoolFalse := mustParseBinaryFromString(c, `false`)
   224  	jIntegerLarge := CreateBinary(uint64(1 << 63))
   225  	jIntegerSmall := mustParseBinaryFromString(c, `3`)
   226  	jStringLarge := mustParseBinaryFromString(c, `"hello, world"`)
   227  	jStringSmall := mustParseBinaryFromString(c, `"hello"`)
   228  	jArrayLarge := mustParseBinaryFromString(c, `["a", "c"]`)
   229  	jArraySmall := mustParseBinaryFromString(c, `["a", "b"]`)
   230  	jObject := mustParseBinaryFromString(c, `{"a": "b"}`)
   231  
   232  	var tests = []struct {
   233  		left  BinaryJSON
   234  		right BinaryJSON
   235  	}{
   236  		{jNull, jIntegerSmall},
   237  		{jIntegerSmall, jIntegerLarge},
   238  		{jIntegerLarge, jStringSmall},
   239  		{jStringSmall, jStringLarge},
   240  		{jStringLarge, jObject},
   241  		{jObject, jArraySmall},
   242  		{jArraySmall, jArrayLarge},
   243  		{jArrayLarge, jBoolFalse},
   244  		{jBoolFalse, jBoolTrue},
   245  	}
   246  	for _, tt := range tests {
   247  		cmp := CompareBinary(tt.left, tt.right)
   248  		c.Assert(cmp < 0, IsTrue)
   249  	}
   250  }
   251  
   252  func (s *testJSONSuite) TestBinaryJSONMerge(c *C) {
   253  	var tests = []struct {
   254  		suffixes []string
   255  		expected string
   256  	}{
   257  		{[]string{`{"a": 1}`, `{"b": 2}`}, `{"a": 1, "b": 2}`},
   258  		{[]string{`{"a": 1}`, `{"a": 2}`}, `{"a": [1, 2]}`},
   259  		{[]string{`[1]`, `[2]`}, `[1, 2]`},
   260  		{[]string{`{"a": 1}`, `[1]`}, `[{"a": 1}, 1]`},
   261  		{[]string{`[1]`, `{"a": 1}`}, `[1, {"a": 1}]`},
   262  		{[]string{`{"a": 1}`, `4`}, `[{"a": 1}, 4]`},
   263  		{[]string{`[1]`, `4`}, `[1, 4]`},
   264  		{[]string{`4`, `{"a": 1}`}, `[4, {"a": 1}]`},
   265  		{[]string{`4`, `1`}, `[4, 1]`},
   266  		{[]string{`{}`, `[]`}, `[{}]`},
   267  	}
   268  
   269  	for _, tt := range tests {
   270  		suffixes := make([]BinaryJSON, 0, len(tt.suffixes)+1)
   271  		for _, s := range tt.suffixes {
   272  			suffixes = append(suffixes, mustParseBinaryFromString(c, s))
   273  		}
   274  		result := MergeBinary(suffixes)
   275  		cmp := CompareBinary(result, mustParseBinaryFromString(c, tt.expected))
   276  		c.Assert(cmp, Equals, 0)
   277  	}
   278  }
   279  
   280  func mustParseBinaryFromString(c *C, s string) BinaryJSON {
   281  	bj, err := ParseBinaryFromString(s)
   282  	c.Assert(err, IsNil)
   283  	return bj
   284  }
   285  
   286  const benchStr = `{"a":[1,"2",{"aa":"bb"},4,null],"b":true,"c":null}`
   287  
   288  func BenchmarkBinaryMarshal(b *testing.B) {
   289  	b.ReportAllocs()
   290  	b.SetBytes(int64(len(benchStr)))
   291  	bj, _ := ParseBinaryFromString(benchStr)
   292  	for i := 0; i < b.N; i++ {
   293  		bj.MarshalJSON()
   294  	}
   295  }
   296  
   297  func (s *testJSONSuite) TestBinaryJSONContains(c *C) {
   298  	var tests = []struct {
   299  		input    string
   300  		target   string
   301  		expected bool
   302  	}{
   303  		{`{}`, `{}`, true},
   304  		{`{"a":1}`, `{}`, true},
   305  		{`{"a":1}`, `1`, false},
   306  		{`{"a":[1]}`, `[1]`, false},
   307  		{`{"b":2, "c":3}`, `{"c":3}`, true},
   308  		{`1`, `1`, true},
   309  		{`[1]`, `1`, true},
   310  		{`[1,2]`, `[1]`, true},
   311  		{`[1,2]`, `[1,3]`, false},
   312  		{`[1,2]`, `["1"]`, false},
   313  		{`[1,2,[1,3]]`, `[1,3]`, true},
   314  		{`[1,2,[1,[5,[3]]]]`, `[1,3]`, true},
   315  		{`[1,2,[1,[5,{"a":[2,3]}]]]`, `[1,{"a":[3]}]`, true},
   316  		{`[{"a":1}]`, `{"a":1}`, true},
   317  		{`[{"a":1,"b":2}]`, `{"a":1}`, true},
   318  		{`[{"a":{"a":1},"b":2}]`, `{"a":1}`, false},
   319  	}
   320  
   321  	for _, tt := range tests {
   322  		obj := mustParseBinaryFromString(c, tt.input)
   323  		target := mustParseBinaryFromString(c, tt.target)
   324  		c.Assert(ContainsBinary(obj, target), Equals, tt.expected)
   325  	}
   326  }
   327  
   328  func (s *testJSONSuite) TestBinaryJSONDepth(c *C) {
   329  	var tests = []struct {
   330  		input    string
   331  		expected int
   332  	}{
   333  		{`{}`, 1},
   334  		{`[]`, 1},
   335  		{`true`, 1},
   336  		{`[10, 20]`, 2},
   337  		{`[[], {}]`, 2},
   338  		{`[10, {"a": 20}]`, 3},
   339  		{`{"Person": {"Name": "Homer", "Age": 39, "Hobbies": ["Eating", "Sleeping"]} }`, 4},
   340  	}
   341  
   342  	for _, tt := range tests {
   343  		obj := mustParseBinaryFromString(c, tt.input)
   344  		c.Assert(obj.GetElemDepth(), Equals, tt.expected)
   345  	}
   346  }