github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/internal/s3select/sql/jsonpath.go (about) 1 // Copyright (c) 2015-2021 MinIO, Inc. 2 // 3 // This file is part of MinIO Object Storage stack 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package sql 19 20 import ( 21 "errors" 22 23 "github.com/bcicen/jstream" 24 "github.com/minio/simdjson-go" 25 ) 26 27 var ( 28 errKeyLookup = errors.New("Cannot look up key in non-object value") 29 errIndexLookup = errors.New("Cannot look up array index in non-array value") 30 errWildcardObjectLookup = errors.New("Object wildcard used on non-object value") 31 errWildcardArrayLookup = errors.New("Array wildcard used on non-array value") 32 errWildcardObjectUsageInvalid = errors.New("Invalid usage of object wildcard") 33 ) 34 35 // jsonpathEval evaluates a JSON path and returns the value at the path. 36 // If the value should be considered flat (from wildcards) any array returned should be considered individual values. 37 func jsonpathEval(p []*JSONPathElement, v interface{}) (r interface{}, flat bool, err error) { 38 // fmt.Printf("JPATHexpr: %v jsonobj: %v\n\n", p, v) 39 if len(p) == 0 || v == nil { 40 return v, false, nil 41 } 42 43 switch { 44 case p[0].Key != nil: 45 key := p[0].Key.keyString() 46 47 switch kvs := v.(type) { 48 case jstream.KVS: 49 for _, kv := range kvs { 50 if kv.Key == key { 51 return jsonpathEval(p[1:], kv.Value) 52 } 53 } 54 // Key not found - return nil result 55 return Missing{}, false, nil 56 case simdjson.Object: 57 elem := kvs.FindKey(key, nil) 58 if elem == nil { 59 // Key not found - return nil result 60 return Missing{}, false, nil 61 } 62 val, err := IterToValue(elem.Iter) 63 if err != nil { 64 return nil, false, err 65 } 66 return jsonpathEval(p[1:], val) 67 default: 68 return nil, false, errKeyLookup 69 } 70 71 case p[0].Index != nil: 72 idx := *p[0].Index 73 74 arr, ok := v.([]interface{}) 75 if !ok { 76 return nil, false, errIndexLookup 77 } 78 79 if idx >= len(arr) { 80 return nil, false, nil 81 } 82 return jsonpathEval(p[1:], arr[idx]) 83 84 case p[0].ObjectWildcard: 85 switch kvs := v.(type) { 86 case jstream.KVS: 87 if len(p[1:]) > 0 { 88 return nil, false, errWildcardObjectUsageInvalid 89 } 90 91 return kvs, false, nil 92 case simdjson.Object: 93 if len(p[1:]) > 0 { 94 return nil, false, errWildcardObjectUsageInvalid 95 } 96 97 return kvs, false, nil 98 default: 99 return nil, false, errWildcardObjectLookup 100 } 101 102 case p[0].ArrayWildcard: 103 arr, ok := v.([]interface{}) 104 if !ok { 105 return nil, false, errWildcardArrayLookup 106 } 107 108 // Lookup remainder of path in each array element and 109 // make result array. 110 var result []interface{} 111 for _, a := range arr { 112 rval, flatten, err := jsonpathEval(p[1:], a) 113 if err != nil { 114 return nil, false, err 115 } 116 117 if flatten { 118 // Flatten if array. 119 if arr, ok := rval.([]interface{}); ok { 120 result = append(result, arr...) 121 continue 122 } 123 } 124 result = append(result, rval) 125 } 126 return result, true, nil 127 } 128 panic("cannot reach here") 129 }