github.com/dolthub/go-mysql-server@v0.18.0/sql/expression/function/json/json_keys.go (about) 1 // Copyright 2024 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 "sort" 20 21 "github.com/dolthub/jsonpath" 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/expression" 26 "github.com/dolthub/go-mysql-server/sql/types" 27 ) 28 29 // JSONKeys (json_doc[, path]) 30 // 31 // JSONKeys Returns the keys from the top-level value of a JSON object as a JSON array, or, if a path argument is given, 32 // the top-level keys from the selected path. Returns NULL if any argument is NULL, the json_doc argument is not an 33 // object, or path, if given, does not locate an object. An error occurs if the json_doc argument is not a valid JSON 34 // document or the path argument is not a valid path expression or contains a * or ** wildcard. The result array is 35 // empty if the selected object is empty. If the top-level value has nested subobjects, the return value does not 36 // include keys from those subobjects. 37 // 38 // https://dev.mysql.com/doc/refman/8.0/en/json-search-functions.html#function_json-keys 39 type JSONKeys struct { 40 JSON sql.Expression 41 Path sql.Expression 42 } 43 44 var _ sql.FunctionExpression = &JSONKeys{} 45 46 // NewJSONKeys creates a new JSONKeys function. 47 func NewJSONKeys(args ...sql.Expression) (sql.Expression, error) { 48 if len(args) == 1 { 49 return &JSONKeys{args[0], expression.NewLiteral("$", types.Text)}, nil 50 } 51 if len(args) == 2 { 52 return &JSONKeys{args[0], args[1]}, nil 53 } 54 return nil, sql.ErrInvalidArgumentNumber.New("JSON_KEYS", "1 or 2", len(args)) 55 } 56 57 // FunctionName implements sql.FunctionExpression 58 func (j *JSONKeys) FunctionName() string { 59 return "json_keys" 60 } 61 62 // Description implements sql.FunctionExpression 63 func (j *JSONKeys) Description() string { 64 return "returns the keys from the top-level value of a JSON object as a JSON array." 65 } 66 67 // Resolved implements the sql.Expression interface. 68 func (j *JSONKeys) Resolved() bool { 69 return j.JSON.Resolved() && j.Path.Resolved() 70 } 71 72 // String implements the sql.Expression interface. 73 func (j *JSONKeys) String() string { 74 return fmt.Sprintf("%s(%s, %s)", j.FunctionName(), j.JSON.String(), j.Path.String()) 75 } 76 77 // Type implements the sql.Expression interface. 78 func (j *JSONKeys) Type() sql.Type { 79 return types.JSON 80 } 81 82 // IsNullable implements the sql.Expression interface. 83 func (j *JSONKeys) IsNullable() bool { 84 return j.JSON.IsNullable() 85 } 86 87 // Eval implements the sql.Expression interface. 88 func (j *JSONKeys) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { 89 span, ctx := ctx.Span(fmt.Sprintf("function.%s", j.FunctionName())) 90 defer span.End() 91 92 doc, err := getJSONDocumentFromRow(ctx, row, j.JSON) 93 if err != nil { 94 return nil, err 95 } 96 if doc == nil { 97 return nil, nil 98 } 99 100 path, err := buildPath(ctx, j.Path, row) 101 if err != nil { 102 return nil, err 103 } 104 if path == nil { 105 return nil, nil 106 } 107 108 js, err := jsonpath.JsonPathLookup(doc.Val, path.(string)) 109 if err != nil { 110 if errors.Is(err, jsonpath.ErrKeyError) { 111 return nil, nil 112 } 113 return nil, err 114 } 115 116 switch v := js.(type) { 117 case map[string]any: 118 res := make([]string, 0) 119 for k := range v { 120 res = append(res, k) 121 } 122 sort.Slice(res, func(i, j int) bool { 123 if len(res[i]) != len(res[j]) { 124 return len(res[i]) < len(res[j]) 125 } 126 return res[i] < res[j] 127 }) 128 result, _, err := types.JSON.Convert(res) 129 if err != nil { 130 return nil, err 131 } 132 return result, nil 133 default: 134 return nil, nil 135 } 136 } 137 138 // Children implements the Expression interface. 139 func (j *JSONKeys) Children() []sql.Expression { 140 return []sql.Expression{j.JSON, j.Path} 141 } 142 143 // WithChildren implements the Expression interface. 144 func (j *JSONKeys) WithChildren(children ...sql.Expression) (sql.Expression, error) { 145 return NewJSONKeys(children...) 146 }