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