github.com/dolthub/go-mysql-server@v0.18.0/sql/expression/function/json/json_array_insert_test.go (about) 1 // Copyright 2023 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 "fmt" 19 "strings" 20 "testing" 21 22 "github.com/stretchr/testify/require" 23 "gopkg.in/src-d/go-errors.v1" 24 25 "github.com/dolthub/go-mysql-server/sql" 26 "github.com/dolthub/go-mysql-server/sql/types" 27 ) 28 29 func TestArrayInsert(t *testing.T) { 30 _, err := NewJSONArrayInsert() 31 require.True(t, errors.Is(err, sql.ErrInvalidArgumentNumber)) 32 33 f1 := buildGetFieldExpressions(t, NewJSONArrayInsert, 3) 34 f2 := buildGetFieldExpressions(t, NewJSONArrayInsert, 5) 35 36 json := `{"a": 1, "b": [2, 3], "c": {"d": "foo"}}` 37 38 testCases := []struct { 39 f sql.Expression 40 row sql.Row 41 expected interface{} 42 err error 43 }{ 44 // Manual testing on MySQL verifies these behaviors are consistent. It seems a little chaotic, but json_array_insert 45 // logic is more consistent than other JSON functions. It requires a path ending with an index, and if it doesn't 46 // find one, it reports an error about the path expression. If the object lookup does find an array, then it 47 // inserts the value at the index. If the index is out of range, it inserts at the appropriate end of the array 48 // (similar to other json mutating functions). Finally, if the object lookup finds a non-array, it's a no-op. 49 {f1, sql.Row{json, "$.b[0]", 4.1}, `{"a": 1, "b": [4.1, 2, 3], "c": {"d": "foo"}}`, nil}, 50 {f1, sql.Row{json, "$.a", 2}, nil, fmt.Errorf("A path expression is not a path to a cell in an array at character 3 of $.a")}, 51 {f1, sql.Row{json, "$.e", "new"}, nil, fmt.Errorf("A path expression is not a path to a cell in an array at character 3 of $.e")}, 52 {f1, sql.Row{json, "$.c.d", "test"}, nil, fmt.Errorf("A path expression is not a path to a cell in an array at character 5 of $.c.d")}, 53 {f2, sql.Row{json, "$.b[0]", 4.1, "$.c.d", "test"}, nil, fmt.Errorf("A path expression is not a path to a cell in an array at character 5 of $.c.d")}, 54 {f1, sql.Row{json, "$.b[5]", 4.1}, `{"a": 1, "b": [2, 3, 4.1], "c": {"d": "foo"}}`, nil}, 55 {f1, sql.Row{json, "$.b.c", 4}, nil, fmt.Errorf("A path expression is not a path to a cell in an array at character 5 of $.b.c")}, 56 {f1, sql.Row{json, "$.a[0]", 4.1}, json, nil}, 57 {f1, sql.Row{json, "$[0]", 4.1}, json, nil}, 58 {f1, sql.Row{json, "$.[0]", 4.1}, nil, ErrInvalidPath}, 59 {f1, sql.Row{json, "foo", "test"}, nil, ErrInvalidPath}, 60 {f1, sql.Row{json, "$.c.*", "test"}, nil, ErrPathWildcard}, 61 {f1, sql.Row{json, "$.c.**", "test"}, nil, ErrPathWildcard}, 62 {f1, sql.Row{json, "$", 10.1}, nil, fmt.Errorf("Path expression is not a path to a cell in an array: $")}, 63 {f1, sql.Row{nil, "$", 42.7}, nil, nil}, 64 {f1, sql.Row{json, nil, 10}, nil, nil}, 65 66 // mysql> select JSON_ARRAY_INSERT(JSON_ARRAY(1,2,3), "$[1]", 51, "$[1]", 52, "$[1]", 53); 67 //+--------------------------------------------------------------------------+ 68 //| JSON_ARRAY_INSERT(JSON_ARRAY(1,2,3), "$[1]", 51, "$[1]", 52, "$[1]", 53) | 69 //+--------------------------------------------------------------------------+ 70 //| [1, 53, 52, 51, 2, 3] | 71 //+--------------------------------------------------------------------------+ 72 {buildGetFieldExpressions(t, NewJSONArrayInsert, 7), 73 sql.Row{`[1.0,2.0,3.0]`, 74 "$[1]", 51.0, // [1, 2, 3] -> [1, 51, 2, 3] 75 "$[1]", 52.0, // [1, 51, 2, 3] -> [1, 52, 51, 2, 3] 76 "$[1]", 53.0, // [1, 52, 51, 2, 3] -> [1, 53, 52, 51, 2, 3] 77 }, 78 `[1,53,52,51,2,3]`, nil}, 79 } 80 81 for _, tstC := range testCases { 82 var paths []string 83 for _, path := range tstC.row[1:] { 84 if _, ok := path.(string); ok { 85 paths = append(paths, path.(string)) 86 } 87 } 88 89 t.Run(tstC.f.String()+"."+strings.Join(paths, ","), func(t *testing.T) { 90 req := require.New(t) 91 result, err := tstC.f.Eval(sql.NewEmptyContext(), tstC.row) 92 if tstC.err == nil { 93 req.NoError(err) 94 95 var expect interface{} 96 if tstC.expected != nil { 97 expect, _, err = types.JSON.Convert(tstC.expected) 98 if err != nil { 99 panic("Bad test string. Can't convert string to JSONDocument: " + tstC.expected.(string)) 100 } 101 } 102 103 req.Equal(expect, result) 104 } else { 105 req.Nil(result) 106 req.Error(tstC.err, err) 107 } 108 }) 109 } 110 111 }