github.com/dolthub/go-mysql-server@v0.18.0/sql/expression/function/json/json_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  	"strings"
    19  	"testing"
    20  
    21  	"github.com/stretchr/testify/require"
    22  	"gopkg.in/src-d/go-errors.v1"
    23  
    24  	"github.com/dolthub/go-mysql-server/sql"
    25  	"github.com/dolthub/go-mysql-server/sql/types"
    26  )
    27  
    28  func TestInsert(t *testing.T) {
    29  	_, err := NewJSONInsert()
    30  	require.True(t, errors.Is(err, sql.ErrInvalidArgumentNumber))
    31  
    32  	f1 := buildGetFieldExpressions(t, NewJSONInsert, 3)
    33  	f2 := buildGetFieldExpressions(t, NewJSONInsert, 5)
    34  
    35  	json := `{"a": 1, "b": [2, 3], "c": {"d": "foo"}}`
    36  
    37  	testCases := []struct {
    38  		f        sql.Expression
    39  		row      sql.Row
    40  		expected interface{}
    41  		err      error
    42  	}{
    43  		{f1, sql.Row{json, "$.a", 10.1}, json, nil},                                                               // insert existing does nothing
    44  		{f1, sql.Row{json, "$.e", "new"}, `{"a": 1, "b": [2, 3], "c": {"d": "foo"},"e":"new"}`, nil},              // insert new
    45  		{f1, sql.Row{json, "$.c.d", "test"}, json, nil},                                                           // insert existing nested does nothing
    46  		{f2, sql.Row{json, "$.a", 10.1, "$.e", "new"}, `{"a": 1, "b": [2, 3], "c": {"d": "foo"},"e":"new"}`, nil}, // insert multiple, one change.
    47  		{f1, sql.Row{json, "$.a.e", "test"}, json, nil},                                                           // insert nested does nothing
    48  		{f1, sql.Row{json, "$.c.e", "test"}, `{"a": 1, "b": [2, 3], "c": {"d": "foo","e":"test"}}`, nil},          // insert nested in existing struct
    49  		{f1, sql.Row{json, "$.c[5]", 4.1}, `{"a": 1, "b": [2, 3], "c": [{"d": "foo"}, 4.1]}`, nil},                // insert struct with indexing out of range
    50  		{f1, sql.Row{json, "$.b[0]", 4.1}, json, nil},                                                             // insert element in array does nothing
    51  		{f1, sql.Row{json, "$.b[5]", 4.1}, `{"a": 1, "b": [2, 3, 4.1], "c": {"d": "foo"}}`, nil},                  // insert element in array out of range
    52  		{f1, sql.Row{json, "$.b.c", 4}, json, nil},                                                                // insert nested in array does nothing
    53  		{f1, sql.Row{json, "$.a[0]", 4.1}, json, nil},                                                             // struct as array does nothing
    54  		{f1, sql.Row{json, "$[0]", 4.1}, json, nil},                                                               // struct does nothing.
    55  		{f1, sql.Row{json, "$.[0]", 4.1}, nil, ErrInvalidPath},                                                    // improper struct indexing
    56  		{f1, sql.Row{json, "foo", "test"}, nil, ErrInvalidPath},                                                   // invalid path
    57  		{f1, sql.Row{json, "$.c.*", "test"}, nil, ErrPathWildcard},                                                // path contains * wildcard
    58  		{f1, sql.Row{json, "$.c.**", "test"}, nil, ErrPathWildcard},                                               // path contains ** wildcard
    59  		{f1, sql.Row{json, "$", 10.1}, json, nil},                                                                 // whole document no opt
    60  		{f1, sql.Row{nil, "$", 42.7}, nil, nil},                                                                   // null document returns null
    61  		{f1, sql.Row{json, nil, 10}, nil, nil},                                                                    // if any path is null, return null
    62  
    63  		// mysql> select JSON_INSERT(JSON_ARRAY(), "$[2]", 1 , "$[2]", 2 ,"$[2]", 3 ,"$[2]", 4);
    64  		// +------------------------------------------------------------------------+
    65  		// | JSON_INSERT(JSON_ARRAY(), "$[2]", 1 , "$[2]", 2 ,"$[2]", 3 ,"$[2]", 4) |
    66  		// +------------------------------------------------------------------------+
    67  		// | [1, 2, 3]                                                              |
    68  		// +------------------------------------------------------------------------+
    69  		{buildGetFieldExpressions(t, NewJSONInsert, 9),
    70  			sql.Row{`[]`,
    71  				"$[2]", 1.1, // [] -> [1.1]
    72  				"$[2]", 2.2, // [1.1] -> [1.1,2.2]
    73  				"$[2]", 3.3, // [1.1, 2.2] -> [1.1, 2.2, 3.3]
    74  				"$[2]", 4.4}, // [1.1, 2.2, 3.3] -> [1.1, 2.2, 3.3]
    75  			`[1.1, 2.2, 3.3]`, nil},
    76  	}
    77  
    78  	for _, tstC := range testCases {
    79  		var paths []string
    80  		for _, path := range tstC.row[1:] {
    81  			if _, ok := path.(string); ok {
    82  				paths = append(paths, path.(string))
    83  			}
    84  		}
    85  
    86  		t.Run(tstC.f.String()+"."+strings.Join(paths, ","), func(t *testing.T) {
    87  			req := require.New(t)
    88  			result, err := tstC.f.Eval(sql.NewEmptyContext(), tstC.row)
    89  			if tstC.err == nil {
    90  				req.NoError(err)
    91  
    92  				var expect interface{}
    93  				if tstC.expected != nil {
    94  					expect, _, err = types.JSON.Convert(tstC.expected)
    95  					if err != nil {
    96  						panic("Bad test string. Can't convert string to JSONDocument: " + tstC.expected.(string))
    97  					}
    98  				}
    99  
   100  				req.Equal(expect, result)
   101  			} else {
   102  				req.Error(tstC.err, err)
   103  			}
   104  		})
   105  	}
   106  
   107  }