github.com/dolthub/go-mysql-server@v0.18.0/sql/expression/function/json/json_contains_test.go (about) 1 // Copyright 2021 Dolthub, 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 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package json 16 17 import ( 18 "testing" 19 20 "github.com/pkg/errors" 21 "github.com/stretchr/testify/require" 22 23 "github.com/dolthub/go-mysql-server/sql" 24 "github.com/dolthub/go-mysql-server/sql/expression" 25 "github.com/dolthub/go-mysql-server/sql/types" 26 ) 27 28 func TestJSONContains(t *testing.T) { 29 // Quickly assert that an error is thrown with < 2 and > 3 arguments 30 _, err := NewJSONContains( 31 expression.NewGetField(0, types.JSON, "arg1", false), 32 ) 33 require.Error(t, err) 34 35 _, err = NewJSONContains( 36 expression.NewGetField(0, types.JSON, "arg1", false), 37 expression.NewGetField(1, types.JSON, "arg2", false), 38 expression.NewGetField(2, types.LongText, "arg3", false), 39 expression.NewGetField(3, types.LongText, "arg4", false), 40 ) 41 require.Error(t, err) 42 43 f, err := NewJSONContains( 44 expression.NewGetField(0, types.JSON, "arg1", false), 45 expression.NewGetField(1, types.JSON, "arg2", false), 46 expression.NewGetField(2, types.LongText, "arg3", false), 47 ) 48 require.NoError(t, err) 49 50 f2, err := NewJSONContains( 51 expression.NewGetField(0, types.JSON, "arg1", false), 52 expression.NewGetField(1, types.JSON, "arg2", false), 53 ) 54 require.NoError(t, err) 55 56 json, _, err := types.JSON.Convert(`{` + 57 `"a": [1, 2, 3, 4], ` + 58 `"b": {"c": "foo", "d": true}, ` + 59 `"e": [[1, 2], [3, 4]] ` + 60 `}`) 61 require.NoError(t, err) 62 63 badMap, _, err := types.JSON.Convert(`{"x": [[1, 2], [3, 4]]}`) 64 require.NoError(t, err) 65 66 goodMap, _, err := types.JSON.Convert(`{"e": [[1, 2], [3, 4]]}`) 67 require.NoError(t, err) 68 69 testCases := []struct { 70 f sql.Expression 71 row sql.Row 72 expected interface{} 73 err error 74 }{ 75 // JSON Array Tests 76 {f2, sql.Row{`[1, [1, 2, 3], 10]`, `[1, 10]`}, true, nil}, 77 {f2, sql.Row{`[1, [1, 2, 3, 10]]`, `[1, 10]`}, true, nil}, 78 {f2, sql.Row{`[1, [1, 2, 3], [10]]`, `[1, [10]]`}, true, nil}, 79 {f2, sql.Row{`[1, [1, 2, 3], [10]]`, `1`}, true, nil}, 80 {f2, sql.Row{`[1, [1, 2, 3], [10], {"e": 1, "f": 2}]`, `{"e": 1}`}, true, nil}, 81 {f2, sql.Row{`[1, [1, 2, 3], [10], {"e": [6, 7], "f": 2}]`, `[6, 7]`}, false, nil}, 82 83 // JSON Object Tests 84 {f2, sql.Row{`{"b": {"a": [1, 2, 3]}}`, `{"a": [1]}`}, false, nil}, 85 {f2, sql.Row{`{"a": [1, 2, 3, 4], "b": {"c": "foo", "d": true}}`, `{"a": [1]}`}, true, nil}, 86 {f2, sql.Row{`{"a": [1, 2, 3, 4], "b": {"c": "foo", "d": true}}`, `{"a": []}`}, true, nil}, 87 {f2, sql.Row{`{"a": [1, 2, 3, 4], "b": {"c": "foo", "d": true}}`, `{"a": {}}`}, false, nil}, 88 {f2, sql.Row{`{"a": [1, [2, 3], 4], "b": {"c": "foo", "d": true}}`, `{"a": [2, 4]}`}, true, nil}, 89 {f2, sql.Row{`{"a": [1, [2, 3], 4], "b": {"c": "foo", "d": true}}`, `[2]`}, false, nil}, 90 {f2, sql.Row{`{"a": [1, [2, 3], 4], "b": {"c": "foo", "d": true}}`, `2`}, false, nil}, 91 {f2, sql.Row{`{"a": [1, [2, 3], 4], "b": {"c": "foo", "d": true}}`, `"foo"`}, false, nil}, 92 {f2, sql.Row{"{\"a\": {\"foo\": [1, 2, 3]}}", "{\"a\": {\"foo\": [1]}}"}, true, nil}, 93 {f2, sql.Row{"{\"a\": {\"foo\": [1, 2, 3]}}", "{\"foo\": [1]}"}, false, nil}, 94 95 // Path Tests 96 {f, sql.Row{json, json, "FOO"}, nil, errors.New("Invalid JSON path expression. Path must start with '$', but received: 'FOO'")}, 97 {f, sql.Row{1, nil, "$.a"}, nil, errors.New("Invalid argument to 1")}, 98 {f, sql.Row{json, 2, "$.e[0][*]"}, nil, errors.New("Invalid argument to 2")}, 99 {f, sql.Row{nil, json, "$.b.c"}, nil, nil}, 100 {f, sql.Row{json, nil, "$.b.c"}, nil, nil}, 101 {f, sql.Row{json, json, "$.foo"}, nil, nil}, 102 {f, sql.Row{json, `"foo"`, "$.b.c"}, true, nil}, 103 {f, sql.Row{json, `1`, "$.e[0][0]"}, true, nil}, 104 {f, sql.Row{json, `1`, "$.e[0][*]"}, true, nil}, 105 {f, sql.Row{json, `1`, "$.e[0][0]"}, true, nil}, 106 {f, sql.Row{json, `[1, 2]`, "$.e[0][*]"}, true, nil}, 107 {f, sql.Row{json, `[1, 2]`, "$.e[0]"}, true, nil}, 108 {f, sql.Row{json, json, "$"}, true, nil}, // reflexivity 109 {f, sql.Row{json, goodMap, "$.e"}, false, nil}, // The path statement selects an array, which does not contain goodMap 110 {f, sql.Row{json, badMap, "$"}, false, nil}, // false due to key name difference 111 {f, sql.Row{json, goodMap, "$"}, true, nil}, 112 113 // Miscellaneous Tests 114 {f2, sql.Row{json, `[1, 2]`}, false, nil}, // When testing containment against a map, scalars and arrays always return false 115 {f2, sql.Row{"[1,2,3,4]", `[1, 2]`}, true, nil}, 116 {f2, sql.Row{"[1,2,3,4]", `1`}, true, nil}, 117 {f2, sql.Row{`["apple", "orange", "banana"]`, `"orange"`}, true, nil}, 118 {f2, sql.Row{`"hello"`, `"hello"`}, true, nil}, 119 {f2, sql.Row{"{}", "{}"}, true, nil}, 120 {f2, sql.Row{"hello", "hello"}, nil, sql.ErrInvalidJSONText.New("hello")}, 121 {f2, sql.Row{"[1,2", "[1]"}, nil, sql.ErrInvalidJSONText.New("[1,2")}, 122 {f2, sql.Row{"[1,2]", "[1"}, nil, sql.ErrInvalidJSONText.New("[1")}, 123 } 124 125 for _, tt := range testCases { 126 t.Run(tt.f.String(), func(t *testing.T) { 127 require := require.New(t) 128 result, err := tt.f.Eval(sql.NewEmptyContext(), tt.row) 129 if tt.err == nil { 130 require.NoError(err) 131 } else { 132 require.Equal(err.Error(), tt.err.Error()) 133 } 134 135 require.Equal(tt.expected, result) 136 }) 137 } 138 }