storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/pkg/s3select/simdj/record.go (about) 1 /* 2 * MinIO Cloud Storage, (C) 2019 MinIO, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package simdj 18 19 import ( 20 "fmt" 21 "io" 22 23 csv "storj.io/minio/pkg/csvparser" 24 25 "github.com/bcicen/jstream" 26 "github.com/minio/simdjson-go" 27 28 "storj.io/minio/pkg/s3select/json" 29 "storj.io/minio/pkg/s3select/sql" 30 ) 31 32 // Record - is JSON record. 33 type Record struct { 34 // object 35 object simdjson.Object 36 } 37 38 // Get - gets the value for a column name. 39 func (r *Record) Get(name string) (*sql.Value, error) { 40 elem := r.object.FindKey(name, nil) 41 if elem == nil { 42 return nil, nil 43 } 44 return iterToValue(elem.Iter) 45 } 46 47 func iterToValue(iter simdjson.Iter) (*sql.Value, error) { 48 switch iter.Type() { 49 case simdjson.TypeString: 50 v, err := iter.String() 51 if err != nil { 52 return nil, err 53 } 54 return sql.FromString(v), nil 55 case simdjson.TypeFloat: 56 v, err := iter.Float() 57 if err != nil { 58 return nil, err 59 } 60 return sql.FromFloat(v), nil 61 case simdjson.TypeInt: 62 v, err := iter.Int() 63 if err != nil { 64 return nil, err 65 } 66 return sql.FromInt(v), nil 67 case simdjson.TypeUint: 68 v, err := iter.Int() 69 if err != nil { 70 // Can't fit into int, convert to float. 71 v, err := iter.Float() 72 return sql.FromFloat(v), err 73 } 74 return sql.FromInt(v), nil 75 case simdjson.TypeBool: 76 v, err := iter.Bool() 77 if err != nil { 78 return nil, err 79 } 80 return sql.FromBool(v), nil 81 case simdjson.TypeNull: 82 return sql.FromNull(), nil 83 case simdjson.TypeObject, simdjson.TypeArray: 84 b, err := iter.MarshalJSON() 85 return sql.FromBytes(b), err 86 } 87 return nil, fmt.Errorf("iterToValue: unknown JSON type: %s", iter.Type().String()) 88 } 89 90 // Reset the record. 91 func (r *Record) Reset() { 92 r.object = simdjson.Object{} 93 } 94 95 // Clone the record and if possible use the destination provided. 96 func (r *Record) Clone(dst sql.Record) sql.Record { 97 other, ok := dst.(*Record) 98 if !ok { 99 other = &Record{} 100 } 101 other.object = r.object 102 return other 103 } 104 105 // CloneTo clones the record to a json Record. 106 // Values are only unmashaled on object level. 107 func (r *Record) CloneTo(dst *json.Record) (sql.Record, error) { 108 if dst == nil { 109 dst = &json.Record{SelectFormat: sql.SelectFmtJSON} 110 } 111 dst.Reset() 112 elems, err := r.object.Parse(nil) 113 if err != nil { 114 return nil, err 115 } 116 if cap(dst.KVS) < len(elems.Elements) { 117 dst.KVS = make(jstream.KVS, 0, len(elems.Elements)) 118 } 119 for _, elem := range elems.Elements { 120 v, err := sql.IterToValue(elem.Iter) 121 if err != nil { 122 v, err = elem.Iter.Interface() 123 if err != nil { 124 panic(err) 125 } 126 } 127 dst.KVS = append(dst.KVS, jstream.KV{ 128 Key: elem.Name, 129 Value: v, 130 }) 131 } 132 return dst, nil 133 } 134 135 // Set - sets the value for a column name. 136 func (r *Record) Set(name string, value *sql.Value) (sql.Record, error) { 137 dst, err := r.CloneTo(nil) 138 if err != nil { 139 return nil, err 140 } 141 return dst.Set(name, value) 142 } 143 144 // WriteCSV - encodes to CSV data. 145 func (r *Record) WriteCSV(writer io.Writer, opts sql.WriteCSVOpts) error { 146 csvRecord := make([]string, 0, 10) 147 var tmp simdjson.Iter 148 obj := r.object 149 allElems: 150 for { 151 _, typ, err := obj.NextElement(&tmp) 152 if err != nil { 153 return err 154 } 155 var columnValue string 156 switch typ { 157 case simdjson.TypeNull, simdjson.TypeFloat, simdjson.TypeUint, simdjson.TypeInt, simdjson.TypeBool, simdjson.TypeString: 158 val, err := tmp.StringCvt() 159 if err != nil { 160 return err 161 } 162 columnValue = val 163 case simdjson.TypeObject, simdjson.TypeArray: 164 b, err := tmp.MarshalJSON() 165 if err != nil { 166 return err 167 } 168 columnValue = string(b) 169 case simdjson.TypeNone: 170 break allElems 171 default: 172 return fmt.Errorf("cannot marshal unhandled type: %s", typ.String()) 173 } 174 csvRecord = append(csvRecord, columnValue) 175 } 176 w := csv.NewWriter(writer) 177 w.Comma = opts.FieldDelimiter 178 w.Quote = opts.Quote 179 w.QuoteEscape = opts.QuoteEscape 180 w.AlwaysQuote = opts.AlwaysQuote 181 if err := w.Write(csvRecord); err != nil { 182 return err 183 } 184 w.Flush() 185 if err := w.Error(); err != nil { 186 return err 187 } 188 189 return nil 190 } 191 192 // Raw - returns the underlying representation. 193 func (r *Record) Raw() (sql.SelectObjectFormat, interface{}) { 194 return sql.SelectFmtSIMDJSON, r.object 195 } 196 197 // WriteJSON - encodes to JSON data. 198 func (r *Record) WriteJSON(writer io.Writer) error { 199 o := r.object 200 elems, err := o.Parse(nil) 201 if err != nil { 202 return err 203 } 204 b, err := elems.MarshalJSON() 205 if err != nil { 206 return err 207 } 208 n, err := writer.Write(b) 209 if err != nil { 210 return err 211 } 212 if n != len(b) { 213 return io.ErrShortWrite 214 } 215 return nil 216 } 217 218 // Replace the underlying buffer of json data. 219 func (r *Record) Replace(k interface{}) error { 220 v, ok := k.(simdjson.Object) 221 if !ok { 222 return fmt.Errorf("cannot replace internal data in simd json record with type %T", k) 223 } 224 r.object = v 225 return nil 226 } 227 228 // NewRecord - creates new empty JSON record. 229 func NewRecord(f sql.SelectObjectFormat, obj simdjson.Object) *Record { 230 return &Record{ 231 object: obj, 232 } 233 }