github.com/dolthub/go-mysql-server@v0.18.0/sql/expression/function/json/json_insert.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 21 "github.com/dolthub/go-mysql-server/sql" 22 "github.com/dolthub/go-mysql-server/sql/types" 23 ) 24 25 // JSON_INSERT(json_doc, path, val[, path, val] ...) 26 // 27 // JSONInsert Inserts data into a JSON document and returns the result. Returns NULL if any argument is NULL. An error 28 // occurs if the json_doc argument is not a valid JSON document or any path argument is not a valid path expression or 29 // contains a * or ** wildcard. The path-value pairs are evaluated left to right. The document produced by evaluating 30 // one pair becomes the new value against which the next pair is evaluated. A path-value pair for an existing path in 31 // the document is ignored and does not overwrite the existing document value. A path-value pair for a nonexisting path 32 // in the document adds the value to the document if the path identifies one of these types of values: 33 // - A member not present in an existing object. The member is added to the object and associated with the new value. 34 // - A position past the end of an existing array. The array is extended with the new value. If the existing value is 35 // not an array, it is autowrapped as an array, then extended with the new value. 36 // 37 // Otherwise, a path-value pair for a nonexisting path in the document is ignored and has no effect. 38 // 39 // https://dev.mysql.com/doc/refman/8.0/en/json-modification-functions.html#function_json-insert 40 type JSONInsert struct { 41 doc sql.Expression 42 pathVals []sql.Expression 43 } 44 45 var _ sql.FunctionExpression = JSONInsert{} 46 47 func (j JSONInsert) Resolved() bool { 48 for _, child := range j.Children() { 49 if child != nil && !child.Resolved() { 50 return false 51 } 52 } 53 return true 54 } 55 56 func (j JSONInsert) String() string { 57 children := j.Children() 58 var parts = make([]string, len(children)) 59 60 for i, c := range children { 61 parts[i] = c.String() 62 } 63 64 return fmt.Sprintf("%s(%s)", j.FunctionName(), strings.Join(parts, ",")) 65 } 66 67 func (j JSONInsert) Type() sql.Type { 68 return types.JSON 69 } 70 71 func (j JSONInsert) IsNullable() bool { 72 for _, arg := range j.pathVals { 73 if arg.IsNullable() { 74 return true 75 } 76 } 77 return j.doc.IsNullable() 78 } 79 80 func (j JSONInsert) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { 81 doc, err := getMutableJSONVal(ctx, row, j.doc) 82 if err != nil || doc == nil { 83 return nil, err 84 } 85 86 pairs := make([]pathValPair, 0, len(j.pathVals)/2) 87 for i := 0; i < len(j.pathVals); i += 2 { 88 argPair, err := buildPathValue(ctx, j.pathVals[i], j.pathVals[i+1], row) 89 if argPair == nil || err != nil { 90 return nil, err 91 } 92 pairs = append(pairs, *argPair) 93 } 94 95 // Apply the path-value pairs to the document. 96 for _, pair := range pairs { 97 doc, _, err = doc.Insert(pair.path, pair.val) 98 if err != nil { 99 return nil, err 100 } 101 } 102 103 return doc, nil 104 } 105 106 func (j JSONInsert) Children() []sql.Expression { 107 return append([]sql.Expression{j.doc}, j.pathVals...) 108 } 109 110 func (j JSONInsert) WithChildren(children ...sql.Expression) (sql.Expression, error) { 111 if len(j.Children()) != len(children) { 112 return nil, fmt.Errorf("json_replace did not receive the correct amount of args") 113 } 114 return NewJSONInsert(children...) 115 } 116 117 // NewJSONInsert creates a new JSONInsert function. 118 func NewJSONInsert(args ...sql.Expression) (sql.Expression, error) { 119 if len(args) <= 1 { 120 return nil, sql.ErrInvalidArgumentNumber.New("JSON_INSERT", "more than 1", len(args)) 121 } else if (len(args)-1)%2 == 1 { 122 return nil, sql.ErrInvalidArgumentNumber.New("JSON_INSERT", "even number of path/val", len(args)-1) 123 } 124 125 return JSONInsert{args[0], args[1:]}, nil 126 } 127 128 // FunctionName implements sql.FunctionExpression 129 func (j JSONInsert) FunctionName() string { 130 return "json_insert" 131 } 132 133 // Description implements sql.FunctionExpression 134 func (j JSONInsert) Description() string { 135 return "inserts data into JSON document" 136 } 137 138 // IsUnsupported implements sql.UnsupportedFunctionStub 139 func (j JSONInsert) IsUnsupported() bool { 140 return false 141 }