storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/pkg/s3select/csv/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 csv 18 19 import ( 20 "encoding/json" 21 "errors" 22 "fmt" 23 "io" 24 25 "github.com/bcicen/jstream" 26 27 csv "storj.io/minio/pkg/csvparser" 28 "storj.io/minio/pkg/s3select/sql" 29 ) 30 31 // Record - is a CSV record. 32 type Record struct { 33 columnNames []string 34 csvRecord []string 35 nameIndexMap map[string]int64 36 } 37 38 // Get - gets the value for a column name. CSV fields do not have any 39 // defined type (other than the default string). So this function 40 // always returns fields using sql.FromBytes so that the type 41 // specified/implied by the query can be used, or can be automatically 42 // converted based on the query. 43 func (r *Record) Get(name string) (*sql.Value, error) { 44 index, found := r.nameIndexMap[name] 45 if !found { 46 return nil, fmt.Errorf("column %v not found", name) 47 } 48 49 if index >= int64(len(r.csvRecord)) { 50 // No value found for column 'name', hence return null 51 // value 52 return sql.FromNull(), nil 53 } 54 55 return sql.FromBytes([]byte(r.csvRecord[index])), nil 56 } 57 58 // Set - sets the value for a column name. 59 func (r *Record) Set(name string, value *sql.Value) (sql.Record, error) { 60 r.columnNames = append(r.columnNames, name) 61 r.csvRecord = append(r.csvRecord, value.CSVString()) 62 return r, nil 63 } 64 65 // Reset data in record. 66 func (r *Record) Reset() { 67 if len(r.columnNames) > 0 { 68 r.columnNames = r.columnNames[:0] 69 } 70 if len(r.csvRecord) > 0 { 71 r.csvRecord = r.csvRecord[:0] 72 } 73 for k := range r.nameIndexMap { 74 delete(r.nameIndexMap, k) 75 } 76 } 77 78 // Clone the record. 79 func (r *Record) Clone(dst sql.Record) sql.Record { 80 other, ok := dst.(*Record) 81 if !ok { 82 other = &Record{} 83 } 84 if len(other.columnNames) > 0 { 85 other.columnNames = other.columnNames[:0] 86 } 87 if len(other.csvRecord) > 0 { 88 other.csvRecord = other.csvRecord[:0] 89 } 90 other.columnNames = append(other.columnNames, r.columnNames...) 91 other.csvRecord = append(other.csvRecord, r.csvRecord...) 92 return other 93 } 94 95 // WriteCSV - encodes to CSV data. 96 func (r *Record) WriteCSV(writer io.Writer, opts sql.WriteCSVOpts) error { 97 w := csv.NewWriter(writer) 98 w.Comma = opts.FieldDelimiter 99 w.AlwaysQuote = opts.AlwaysQuote 100 w.Quote = opts.Quote 101 w.QuoteEscape = opts.QuoteEscape 102 if err := w.Write(r.csvRecord); err != nil { 103 return err 104 } 105 w.Flush() 106 if err := w.Error(); err != nil { 107 return err 108 } 109 110 return nil 111 } 112 113 // WriteJSON - encodes to JSON data. 114 func (r *Record) WriteJSON(writer io.Writer) error { 115 var kvs jstream.KVS = make([]jstream.KV, len(r.columnNames)) 116 for i := 0; i < len(r.columnNames); i++ { 117 kvs[i] = jstream.KV{Key: r.columnNames[i], Value: r.csvRecord[i]} 118 } 119 return json.NewEncoder(writer).Encode(kvs) 120 } 121 122 // Raw - returns the underlying data with format info. 123 func (r *Record) Raw() (sql.SelectObjectFormat, interface{}) { 124 return sql.SelectFmtCSV, r 125 } 126 127 // Replace - is not supported for CSV 128 func (r *Record) Replace(_ interface{}) error { 129 return errors.New("Replace is not supported for CSV") 130 } 131 132 // NewRecord - creates new CSV record. 133 func NewRecord() *Record { 134 return &Record{} 135 }