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  }