github.com/dolthub/go-mysql-server@v0.18.0/sql/expression/function/json/json_value.go (about) 1 // Copyright 2020-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 "encoding/json" 19 "fmt" 20 "strings" 21 22 "github.com/dolthub/jsonpath" 23 "github.com/dolthub/vitess/go/sqltypes" 24 25 "github.com/dolthub/go-mysql-server/sql" 26 "github.com/dolthub/go-mysql-server/sql/expression" 27 "github.com/dolthub/go-mysql-server/sql/types" 28 ) 29 30 // JsonValue selects data from a json document using a json path and 31 // optional type coercion. 32 // https://dev.mysql.com/doc/refman/8.0/en/json-search-functions.html#function_json-value 33 // usage: JSON_VALUE(json_doc, path, [returning type]) 34 // TODO: [RETURNING TYPE] should be appended to path option in parser 35 // TODO: missing [on empty] and [on error] support 36 type JsonValue struct { 37 JSON sql.Expression 38 Path sql.Expression 39 Typ sql.Type 40 } 41 42 var _ sql.FunctionExpression = (*JsonValue)(nil) 43 var _ sql.CollationCoercible = (*JsonValue)(nil) 44 45 var jsonValueDefaultType = types.MustCreateString(sqltypes.VarChar, 512, sql.Collation_Default) 46 47 // NewJsonValue creates a new JsonValue UDF. 48 func NewJsonValue(args ...sql.Expression) (sql.Expression, error) { 49 if len(args) < 1 || len(args) > 3 { 50 return nil, sql.ErrInvalidArgumentNumber.New("JSON_VALUE", 2, len(args)) 51 } else if len(args) == 1 { 52 return &JsonValue{JSON: args[0], Path: expression.NewLiteral("$", types.Text), Typ: jsonValueDefaultType}, nil 53 } else if len(args) == 2 { 54 return &JsonValue{JSON: args[0], Path: args[1], Typ: jsonValueDefaultType}, nil 55 } else { 56 // third argument is literal zero of the coercion type 57 return &JsonValue{JSON: args[0], Path: args[1], Typ: args[2].Type()}, nil 58 } 59 } 60 61 // FunctionName implements sql.FunctionExpression 62 func (j *JsonValue) FunctionName() string { 63 return "json_value" 64 } 65 66 // Description implements sql.FunctionExpression 67 func (j *JsonValue) Description() string { 68 return "returns value from JSON document" 69 } 70 71 // Resolved implements the sql.Expression interface. 72 func (j *JsonValue) Resolved() bool { 73 return j.JSON.Resolved() && j.Path.Resolved() 74 } 75 76 // Type implements the sql.Expression interface. 77 func (j *JsonValue) Type() sql.Type { return j.Typ } 78 79 // CollationCoercibility implements the interface sql.CollationCoercible. 80 func (*JsonValue) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) { 81 return ctx.GetCharacterSet().BinaryCollation(), 2 82 } 83 84 // Eval implements the sql.Expression interface. 85 func (j *JsonValue) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { 86 span, ctx := ctx.Span("function.JsonValue") 87 defer span.End() 88 89 js, err := j.JSON.Eval(ctx, row) 90 if err != nil { 91 return nil, err 92 } 93 // sql NULLs, should result in sql NULLs. 94 if js == nil { 95 return nil, err 96 } 97 98 strData, _, err := types.LongBlob.Convert(js) 99 if err != nil { 100 return nil, fmt.Errorf("invalid data type for JSON data in argument 1 to function json_value; a JSON string or JSON type is required") 101 } 102 if strData == nil { 103 return nil, nil 104 } 105 106 var jsonData interface{} 107 if err = json.Unmarshal(strData.([]byte), &jsonData); err != nil { 108 return nil, err 109 } 110 111 path, err := j.Path.Eval(ctx, row) 112 if err != nil { 113 return nil, err 114 } 115 116 res, err := jsonpath.JsonPathLookup(jsonData, path.(string)) 117 if err != nil { 118 return nil, err 119 } 120 121 switch r := res.(type) { 122 case nil: 123 return nil, nil 124 case []interface{}: 125 if len(r) == 0 { 126 return nil, nil 127 } 128 res = types.JSONDocument{Val: res} 129 case map[string]interface{}: 130 res = types.JSONDocument{Val: res} 131 } 132 133 if j.Typ != nil { 134 res, _, err = j.Typ.Convert(res) 135 if err != nil { 136 return nil, err 137 } 138 } 139 140 return res, nil 141 } 142 143 // IsNullable implements the sql.Expression interface. 144 func (j *JsonValue) IsNullable() bool { 145 return j.JSON.IsNullable() || j.Path.IsNullable() 146 } 147 148 // Children implements the sql.Expression interface. 149 func (j *JsonValue) Children() []sql.Expression { 150 return []sql.Expression{j.JSON, j.Path} 151 } 152 153 // WithChildren implements the Expression interface. 154 func (j *JsonValue) WithChildren(children ...sql.Expression) (sql.Expression, error) { 155 if len(children) != 2 { 156 return nil, sql.ErrInvalidChildrenNumber.New(j, len(children), 2) 157 } 158 ret := *j 159 ret.JSON = children[0] 160 ret.Path = children[1] 161 return &ret, nil 162 } 163 164 func (j *JsonValue) String() string { 165 children := j.Children() 166 var parts = make([]string, len(children)) 167 for i, c := range children { 168 parts[i] = c.String() 169 } 170 return fmt.Sprintf("json_value(%s)", strings.Join(parts, ", ")) 171 }