github.com/vc42/parquet-go@v0.0.0-20240320194221-1a9adb5f23f5/row.go (about)

     1  package parquet
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"github.com/vc42/parquet-go/utils"
     8  	"io"
     9  	"reflect"
    10  )
    11  
    12  const (
    13  	defaultRowBufferSize = 20
    14  )
    15  
    16  // Row represents a parquet row as a slice of values.
    17  //
    18  // Each value should embed a column index, repetition level, and definition
    19  // level allowing the program to determine how to reconstruct the original
    20  // object from the row. Repeated values share the same column index, their
    21  // relative position of repeated values is represented by their relative
    22  // position in the row.
    23  type Row []Value
    24  
    25  // Clone creates a copy of the row which shares no pointers.
    26  //
    27  // This method is useful to capture rows after a call to RowReader.ReadRows when
    28  // values need to be retained before the next call to ReadRows or after the lifespan
    29  // of the reader.
    30  func (row Row) Clone() Row {
    31  	clone := make(Row, len(row))
    32  	for i := range row {
    33  		clone[i] = row[i].Clone()
    34  	}
    35  	return clone
    36  }
    37  
    38  // Equal returns true if row and other contain the same sequence of values.
    39  func (row Row) Equal(other Row) bool {
    40  	if len(row) != len(other) {
    41  		return false
    42  	}
    43  	for i := range row {
    44  		if !Equal(row[i], other[i]) {
    45  			return false
    46  		}
    47  		if row[i].repetitionLevel != other[i].repetitionLevel {
    48  			return false
    49  		}
    50  		if row[i].definitionLevel != other[i].definitionLevel {
    51  			return false
    52  		}
    53  		if row[i].columnIndex != other[i].columnIndex {
    54  			return false
    55  		}
    56  	}
    57  	return true
    58  }
    59  
    60  func (row Row) startsWith(columnIndex int16) bool {
    61  	return len(row) > 0 && row[0].Column() == int(columnIndex)
    62  }
    63  
    64  // RowSeeker is an interface implemented by readers of parquet rows which can be
    65  // positioned at a specific row index.
    66  type RowSeeker interface {
    67  	// Positions the stream on the given row index.
    68  	//
    69  	// Some implementations of the interface may only allow seeking forward.
    70  	//
    71  	// The method returns io.ErrClosedPipe if the stream had already been closed.
    72  	SeekToRow(int64) error
    73  }
    74  
    75  // RowReader reads a sequence of parquet rows.
    76  type RowReader interface {
    77  	// Read rows from the reader, returning the number of rows read into the
    78  	// buffer, and any error that occurred.
    79  	//
    80  	// When all rows have been read, the reader returns io.EOF to indicate the
    81  	// end of the sequence. It is valid for the reader to return both a non-zero
    82  	// number of rows and a non-nil error (including io.EOF).
    83  	//
    84  	// The buffer of rows passed as argument will be used to store values of
    85  	// each row read from the reader. If the rows are not nil, the backing array
    86  	// of the slices will be used as an optimization to avoid re-allocating new
    87  	// arrays.
    88  	ReadRows([]Row) (int, error)
    89  }
    90  
    91  // RowReaderFrom reads parquet rows from reader.
    92  type RowReaderFrom interface {
    93  	ReadRowsFrom(RowReader) (int64, error)
    94  }
    95  
    96  // RowReaderWithSchema is an extension of the RowReader interface which
    97  // advertises the schema of rows returned by ReadRow calls.
    98  type RowReaderWithSchema interface {
    99  	RowReader
   100  	Schema() *Schema
   101  }
   102  
   103  // RowReadSeeker is an interface implemented by row readers which support
   104  // seeking to arbitrary row positions.
   105  type RowReadSeeker interface {
   106  	RowReader
   107  	RowSeeker
   108  }
   109  
   110  // RowWriter writes parquet rows to an underlying medium.
   111  type RowWriter interface {
   112  	// Writes rows to the writer, returning the number of rows written and any
   113  	// error that occurred.
   114  	//
   115  	// Because columnar operations operate on independent columns of values,
   116  	// writes of rows may not be atomic operations, and could result in some
   117  	// rows being partially written. The method returns the number of rows that
   118  	// were successfully written, but if an error occurs, values of the row(s)
   119  	// that failed to be written may have been partially committed to their
   120  	// columns. For that reason, applications should consider a write error as
   121  	// fatal and assume that they need to discard the state, they cannot retry
   122  	// the write nor recover the underlying file.
   123  	WriteRows([]Row) (int, error)
   124  }
   125  
   126  // RowWriterTo writes parquet rows to a writer.
   127  type RowWriterTo interface {
   128  	WriteRowsTo(RowWriter) (int64, error)
   129  }
   130  
   131  // RowWriterWithSchema is an extension of the RowWriter interface which
   132  // advertises the schema of rows expected to be passed to WriteRow calls.
   133  type RowWriterWithSchema interface {
   134  	RowWriter
   135  	Schema() *Schema
   136  }
   137  
   138  type forwardRowSeeker struct {
   139  	rows  RowReader
   140  	seek  int64
   141  	index int64
   142  }
   143  
   144  func (r *forwardRowSeeker) ReadRows(rows []Row) (int, error) {
   145  	for {
   146  		n, err := r.rows.ReadRows(rows)
   147  
   148  		if n > 0 && r.index < r.seek {
   149  			skip := r.seek - r.index
   150  			r.index += int64(n)
   151  			if skip >= int64(n) {
   152  				continue
   153  			}
   154  
   155  			for i, j := 0, int(skip); j < n; i++ {
   156  				rows[i] = append(rows[i][:0], rows[j]...)
   157  			}
   158  
   159  			n -= int(skip)
   160  		}
   161  
   162  		return n, err
   163  	}
   164  }
   165  
   166  func (r *forwardRowSeeker) SeekToRow(rowIndex int64) error {
   167  	if rowIndex >= r.index {
   168  		r.seek = rowIndex
   169  		return nil
   170  	}
   171  	return fmt.Errorf("SeekToRow: %T does not implement parquet.RowSeeker: cannot seek backward from row %d to %d", r.rows, r.index, rowIndex)
   172  }
   173  
   174  // CopyRows copies rows from src to dst.
   175  //
   176  // The underlying types of src and dst are tested to determine if they expose
   177  // information about the schema of rows that are read and expected to be
   178  // written. If the schema information are available but do not match, the
   179  // function will attempt to automatically convert the rows from the source
   180  // schema to the destination.
   181  //
   182  // As an optimization, the src argument may implement RowWriterTo to bypass
   183  // the default row copy logic and provide its own. The dst argument may also
   184  // implement RowReaderFrom for the same purpose.
   185  //
   186  // The function returns the number of rows written, or any error encountered
   187  // other than io.EOF.
   188  func CopyRows(dst RowWriter, src RowReader) (int64, error) {
   189  	return copyRows(dst, src, nil)
   190  }
   191  
   192  func copyRows(dst RowWriter, src RowReader, buf []Row) (written int64, err error) {
   193  	targetSchema := targetSchemaOf(dst)
   194  	sourceSchema := sourceSchemaOf(src)
   195  
   196  	if targetSchema != nil && sourceSchema != nil {
   197  		if !nodesAreEqual(targetSchema, sourceSchema) {
   198  			conv, err := Convert(targetSchema, sourceSchema)
   199  			if err != nil {
   200  				return 0, err
   201  			}
   202  			// The conversion effectively disables a potential optimization
   203  			// if the source reader implemented RowWriterTo. It is a trade off
   204  			// we are making to optimize for safety rather than performance.
   205  			//
   206  			// Entering this code path should not be the common case tho, it is
   207  			// most often used when parquet schemas are evolving, but we expect
   208  			// that the majority of files of an application to be sharing a
   209  			// common schema.
   210  			src = ConvertRowReader(src, conv)
   211  		}
   212  	}
   213  
   214  	if wt, ok := src.(RowWriterTo); ok {
   215  		return wt.WriteRowsTo(dst)
   216  	}
   217  
   218  	if rf, ok := dst.(RowReaderFrom); ok {
   219  		return rf.ReadRowsFrom(src)
   220  	}
   221  
   222  	if len(buf) == 0 {
   223  		buf = make([]Row, defaultRowBufferSize)
   224  	}
   225  
   226  	defer clearRows(buf)
   227  
   228  	for {
   229  		rn, err := src.ReadRows(buf)
   230  
   231  		if rn > 0 {
   232  			wn, err := dst.WriteRows(buf[:rn])
   233  			if err != nil {
   234  				return written, err
   235  			}
   236  
   237  			written += int64(wn)
   238  		}
   239  
   240  		if err != nil {
   241  			if errors.Is(err, io.EOF) {
   242  				err = nil
   243  			}
   244  			return written, err
   245  		}
   246  
   247  		if rn == 0 {
   248  			return written, io.ErrNoProgress
   249  		}
   250  	}
   251  }
   252  
   253  func clearRows(rows []Row) {
   254  	for i, values := range rows {
   255  		clearValues(values)
   256  		rows[i] = values[:0]
   257  	}
   258  }
   259  
   260  func sourceSchemaOf(r RowReader) *Schema {
   261  	if rrs, ok := r.(RowReaderWithSchema); ok {
   262  		return rrs.Schema()
   263  	}
   264  	return nil
   265  }
   266  
   267  func targetSchemaOf(w RowWriter) *Schema {
   268  	if rws, ok := w.(RowWriterWithSchema); ok {
   269  		return rws.Schema()
   270  	}
   271  	return nil
   272  }
   273  
   274  func errRowIndexOutOfBounds(rowIndex, rowCount int64) error {
   275  	return fmt.Errorf("row index out of bounds: %d/%d", rowIndex, rowCount)
   276  }
   277  
   278  func hasRepeatedRowValues(values []Value) bool {
   279  	for _, v := range values {
   280  		if v.repetitionLevel != 0 {
   281  			return true
   282  		}
   283  	}
   284  	return false
   285  }
   286  
   287  // repeatedRowLength gives the length of the repeated row starting at the
   288  // beginning of the repetitionLevels slice.
   289  func repeatedRowLength(repetitionLevels []byte) int {
   290  	// If a repetition level exists, at least one value is required to represent
   291  	// the column.
   292  	if len(repetitionLevels) > 0 {
   293  		// The subsequent levels will represent the start of a new record when
   294  		// they go back to zero.
   295  		if i := bytes.IndexByte(repetitionLevels[1:], 0); i >= 0 {
   296  			return i + 1
   297  		}
   298  	}
   299  	return len(repetitionLevels)
   300  }
   301  
   302  func countRowsOf(values []Value) (numRows int) {
   303  	if !hasRepeatedRowValues(values) {
   304  		return len(values) // Faster path when there are no repeated values.
   305  	}
   306  	if len(values) > 0 {
   307  		// The values may have not been at the start of a repeated row,
   308  		// it could be the continuation of a repeated row. Skip until we
   309  		// find the beginning of a row before starting to count how many
   310  		// rows there are.
   311  		if values[0].repetitionLevel != 0 {
   312  			_, values = splitRowValues(values)
   313  		}
   314  		for len(values) > 0 {
   315  			numRows++
   316  			_, values = splitRowValues(values)
   317  		}
   318  	}
   319  	return numRows
   320  }
   321  
   322  func limitRowValues(values []Value, rowCount int) []Value {
   323  	if !hasRepeatedRowValues(values) {
   324  		if len(values) > rowCount {
   325  			values = values[:rowCount]
   326  		}
   327  	} else {
   328  		var row Row
   329  		var limit int
   330  		for len(values) > 0 {
   331  			row, values = splitRowValues(values)
   332  			limit += len(row)
   333  		}
   334  		values = values[:limit]
   335  	}
   336  	return values
   337  }
   338  
   339  func splitRowValues(values []Value) (head, tail []Value) {
   340  	for i, v := range values {
   341  		if v.repetitionLevel == 0 {
   342  			return values[:i+1], values[i+1:]
   343  		}
   344  	}
   345  	return values, nil
   346  }
   347  
   348  // =============================================================================
   349  // Functions returning closures are marked with "go:noinline" below to prevent
   350  // losing naming information of the closure in stack traces.
   351  //
   352  // Because some of the functions are very short (simply return a closure), the
   353  // compiler inlines when at their call site, which result in the closure being
   354  // named something like parquet.deconstructFuncOf.func2 instead of the original
   355  // parquet.deconstructFuncOfLeaf.func1; the latter being much more meaningful
   356  // when reading CPU or memory profiles.
   357  // =============================================================================
   358  
   359  type levels struct {
   360  	repetitionDepth byte
   361  	repetitionLevel byte
   362  	definitionLevel byte
   363  }
   364  
   365  type deconstructFunc func(Row, levels, reflect.Value) Row
   366  
   367  func deconstructFuncOf(columnIndex int16, node Node) (int16, deconstructFunc) {
   368  	switch {
   369  	case node.Optional():
   370  		return deconstructFuncOfOptional(columnIndex, node)
   371  	case node.Repeated():
   372  		return deconstructFuncOfRepeated(columnIndex, node)
   373  	case isList(node):
   374  		return deconstructFuncOfList(columnIndex, node)
   375  	case isMap(node):
   376  		return deconstructFuncOfMap(columnIndex, node)
   377  	default:
   378  		return deconstructFuncOfRequired(columnIndex, node)
   379  	}
   380  }
   381  
   382  //go:noinline
   383  func deconstructFuncOfOptional(columnIndex int16, node Node) (int16, deconstructFunc) {
   384  	columnIndex, deconstruct := deconstructFuncOf(columnIndex, Required(node))
   385  	return columnIndex, func(row Row, levels levels, value reflect.Value) Row {
   386  		if value.IsValid() {
   387  			if value.IsZero() {
   388  				value = reflect.Value{}
   389  			} else {
   390  				if value.Kind() == reflect.Ptr {
   391  					value = value.Elem()
   392  				}
   393  				levels.definitionLevel++
   394  			}
   395  		}
   396  		return deconstruct(row, levels, value)
   397  	}
   398  }
   399  
   400  //go:noinline
   401  func deconstructFuncOfRepeated(columnIndex int16, node Node) (int16, deconstructFunc) {
   402  	columnIndex, deconstruct := deconstructFuncOf(columnIndex, Required(node))
   403  	return columnIndex, func(row Row, levels levels, value reflect.Value) Row {
   404  		if !value.IsValid() || value.Len() == 0 {
   405  			return deconstruct(row, levels, reflect.Value{})
   406  		}
   407  
   408  		levels.repetitionDepth++
   409  		levels.definitionLevel++
   410  
   411  		for i, n := 0, value.Len(); i < n; i++ {
   412  			row = deconstruct(row, levels, value.Index(i))
   413  			levels.repetitionLevel = levels.repetitionDepth
   414  		}
   415  
   416  		return row
   417  	}
   418  }
   419  
   420  func deconstructFuncOfRequired(columnIndex int16, node Node) (int16, deconstructFunc) {
   421  	switch {
   422  	case node.Leaf():
   423  		return deconstructFuncOfLeaf(columnIndex, node)
   424  	default:
   425  		return deconstructFuncOfGroup(columnIndex, node)
   426  	}
   427  }
   428  
   429  func deconstructFuncOfList(columnIndex int16, node Node) (int16, deconstructFunc) {
   430  	return deconstructFuncOf(columnIndex, Repeated(listElementOf(node)))
   431  }
   432  
   433  //go:noinline
   434  func deconstructFuncOfMap(columnIndex int16, node Node) (int16, deconstructFunc) {
   435  	keyValue := mapKeyValueOf(node)
   436  	keyValueType := keyValue.GoType()
   437  	keyValueElem := keyValueType.Elem()
   438  	keyType := keyValueElem.Field(0).Type
   439  	valueType := keyValueElem.Field(1).Type
   440  	columnIndex, deconstruct := deconstructFuncOf(columnIndex, schemaOf(keyValueElem))
   441  	return columnIndex, func(row Row, levels levels, mapValue reflect.Value) Row {
   442  		if !mapValue.IsValid() || mapValue.Len() == 0 {
   443  			return deconstruct(row, levels, reflect.Value{})
   444  		}
   445  
   446  		levels.repetitionDepth++
   447  		levels.definitionLevel++
   448  
   449  		elem := reflect.New(keyValueElem).Elem()
   450  		k := elem.Field(0)
   451  		v := elem.Field(1)
   452  
   453  		for _, key := range mapValue.MapKeys() {
   454  			k.Set(key.Convert(keyType))
   455  			v.Set(mapValue.MapIndex(key).Convert(valueType))
   456  			row = deconstruct(row, levels, elem)
   457  			levels.repetitionLevel = levels.repetitionDepth
   458  		}
   459  
   460  		return row
   461  	}
   462  }
   463  
   464  //go:noinline
   465  func deconstructFuncOfGroup(columnIndex int16, node Node) (int16, deconstructFunc) {
   466  	fields := node.Fields()
   467  	funcs := make([]deconstructFunc, len(fields))
   468  	for i, field := range fields {
   469  		columnIndex, funcs[i] = deconstructFuncOf(columnIndex, field)
   470  	}
   471  	return columnIndex, func(row Row, levels levels, value reflect.Value) Row {
   472  		if value.IsValid() {
   473  			for i, f := range funcs {
   474  				row = f(row, levels, fields[i].Value(value))
   475  			}
   476  		} else {
   477  			for _, f := range funcs {
   478  				row = f(row, levels, value)
   479  			}
   480  		}
   481  		return row
   482  	}
   483  }
   484  
   485  //go:noinline
   486  func deconstructFuncOfLeaf(columnIndex int16, node Node) (int16, deconstructFunc) {
   487  	if columnIndex > MaxColumnIndex {
   488  		panic("row cannot be deconstructed because it has more than 127 columns")
   489  	}
   490  	kind := node.Type().Kind()
   491  	logicalType := node.Type().LogicalType()
   492  	valueColumnIndex := ^columnIndex
   493  	return columnIndex + 1, func(row Row, levels levels, value reflect.Value) Row {
   494  		v := Value{}
   495  
   496  		if value.IsValid() {
   497  			switch {
   498  			case value.Kind() == reflect.String && logicalType.Timestamp != nil:
   499  				value = reflect.ValueOf(utils.StringToTimeMs(value.String()))
   500  			case value.Kind() == reflect.String && logicalType.Date != nil:
   501  				value = reflect.ValueOf(utils.StringToDate(value.String()))
   502  			}
   503  			v = makeValue(kind, value)
   504  		}
   505  
   506  		v.repetitionLevel = levels.repetitionLevel
   507  		v.definitionLevel = levels.definitionLevel
   508  		v.columnIndex = valueColumnIndex
   509  		return append(row, v)
   510  	}
   511  }
   512  
   513  type reconstructFunc func(reflect.Value, levels, Row) (Row, error)
   514  
   515  func reconstructFuncOf(columnIndex int16, node Node) (int16, reconstructFunc) {
   516  	switch {
   517  	case node.Optional():
   518  		return reconstructFuncOfOptional(columnIndex, node)
   519  	case node.Repeated():
   520  		return reconstructFuncOfRepeated(columnIndex, node)
   521  	case isList(node):
   522  		return reconstructFuncOfList(columnIndex, node)
   523  	case isMap(node):
   524  		return reconstructFuncOfMap(columnIndex, node)
   525  	default:
   526  		return reconstructFuncOfRequired(columnIndex, node)
   527  	}
   528  }
   529  
   530  //go:noinline
   531  func reconstructFuncOfOptional(columnIndex int16, node Node) (int16, reconstructFunc) {
   532  	nextColumnIndex, reconstruct := reconstructFuncOf(columnIndex, Required(node))
   533  	rowLength := nextColumnIndex - columnIndex
   534  	return nextColumnIndex, func(value reflect.Value, levels levels, row Row) (Row, error) {
   535  		if !row.startsWith(columnIndex) {
   536  			return row, fmt.Errorf("row is missing optional column %d", columnIndex)
   537  		}
   538  		if len(row) < int(rowLength) {
   539  			return row, fmt.Errorf("expected optional column %d to have at least %d values but got %d", columnIndex, rowLength, len(row))
   540  		}
   541  
   542  		levels.definitionLevel++
   543  
   544  		if row[0].definitionLevel < levels.definitionLevel {
   545  			value.Set(reflect.Zero(value.Type()))
   546  			return row[rowLength:], nil
   547  		}
   548  
   549  		if value.Kind() == reflect.Ptr {
   550  			if value.IsNil() {
   551  				value.Set(reflect.New(value.Type().Elem()))
   552  			}
   553  			value = value.Elem()
   554  		}
   555  
   556  		return reconstruct(value, levels, row)
   557  	}
   558  }
   559  
   560  //go:noinline
   561  func reconstructFuncOfRepeated(columnIndex int16, node Node) (int16, reconstructFunc) {
   562  	nextColumnIndex, reconstruct := reconstructFuncOf(columnIndex, Required(node))
   563  	rowLength := nextColumnIndex - columnIndex
   564  	return nextColumnIndex, func(value reflect.Value, lvls levels, row Row) (Row, error) {
   565  		t := value.Type()
   566  		c := value.Cap()
   567  		n := 0
   568  		if c > 0 {
   569  			value.Set(value.Slice(0, c))
   570  		} else {
   571  			c = 10
   572  			value.Set(reflect.MakeSlice(t, c, c))
   573  		}
   574  		if t.Elem().Kind() == reflect.Ptr {
   575  			for i := 0; i < c; i++ {
   576  				value.Index(i).Set(reflect.New(t.Elem().Elem()))
   577  			}
   578  		}
   579  
   580  		defer func() {
   581  			value.Set(value.Slice(0, n))
   582  		}()
   583  
   584  		return reconstructRepeated(columnIndex, rowLength, lvls, row, func(levels levels, row Row) (Row, error) {
   585  			if n == c {
   586  				c *= 2
   587  				newValue := reflect.MakeSlice(t, c, c)
   588  				reflect.Copy(newValue, value)
   589  				value.Set(newValue)
   590  			}
   591  			row, err := reconstruct(value.Index(n), levels, row)
   592  			n++
   593  			return row, err
   594  		})
   595  	}
   596  }
   597  
   598  func reconstructRepeated(columnIndex, rowLength int16, levels levels, row Row, do func(levels, Row) (Row, error)) (Row, error) {
   599  	if !row.startsWith(columnIndex) {
   600  		return row, fmt.Errorf("row is missing repeated column %d: %+v", columnIndex, row)
   601  	}
   602  	if len(row) < int(rowLength) {
   603  		return row, fmt.Errorf("expected repeated column %d to have at least %d values but got %d", columnIndex, rowLength, len(row))
   604  	}
   605  
   606  	levels.repetitionDepth++
   607  	levels.definitionLevel++
   608  
   609  	if row[0].definitionLevel < levels.definitionLevel {
   610  		return row[rowLength:], nil
   611  	}
   612  
   613  	var err error
   614  	for row.startsWith(columnIndex) && row[0].repetitionLevel == levels.repetitionLevel {
   615  		if row, err = do(levels, row); err != nil {
   616  			break
   617  		}
   618  		levels.repetitionLevel = levels.repetitionDepth
   619  	}
   620  	return row, err
   621  }
   622  
   623  func reconstructFuncOfRequired(columnIndex int16, node Node) (int16, reconstructFunc) {
   624  	switch {
   625  	case node.Leaf():
   626  		return reconstructFuncOfLeaf(columnIndex, node)
   627  	default:
   628  		return reconstructFuncOfGroup(columnIndex, node)
   629  	}
   630  }
   631  
   632  func reconstructFuncOfList(columnIndex int16, node Node) (int16, reconstructFunc) {
   633  	return reconstructFuncOf(columnIndex, Repeated(listElementOf(node)))
   634  }
   635  
   636  //go:noinline
   637  func reconstructFuncOfMap(columnIndex int16, node Node) (int16, reconstructFunc) {
   638  	keyValue := mapKeyValueOf(node)
   639  	keyValueType := keyValue.GoType()
   640  	keyValueElem := keyValueType.Elem()
   641  	keyValueZero := reflect.Zero(keyValueElem)
   642  	nextColumnIndex, reconstruct := reconstructFuncOf(columnIndex, schemaOf(keyValueElem))
   643  	rowLength := nextColumnIndex - columnIndex
   644  	return nextColumnIndex, func(mapValue reflect.Value, lvls levels, row Row) (Row, error) {
   645  		t := mapValue.Type()
   646  		k := t.Key()
   647  		v := t.Elem()
   648  
   649  		if mapValue.IsNil() {
   650  			mapValue.Set(reflect.MakeMap(t))
   651  		}
   652  
   653  		elem := reflect.New(keyValueElem).Elem()
   654  		return reconstructRepeated(columnIndex, rowLength, lvls, row, func(levels levels, row Row) (Row, error) {
   655  			row, err := reconstruct(elem, levels, row)
   656  			if err == nil {
   657  				mapValue.SetMapIndex(elem.Field(0).Convert(k), elem.Field(1).Convert(v))
   658  				elem.Set(keyValueZero)
   659  			}
   660  			return row, err
   661  		})
   662  	}
   663  }
   664  
   665  //go:noinline
   666  func reconstructFuncOfGroup(columnIndex int16, node Node) (int16, reconstructFunc) {
   667  	fields := node.Fields()
   668  	funcs := make([]reconstructFunc, len(fields))
   669  	columnIndexes := make([]int16, len(fields))
   670  
   671  	for i, field := range fields {
   672  		columnIndex, funcs[i] = reconstructFuncOf(columnIndex, field)
   673  		columnIndexes[i] = columnIndex
   674  	}
   675  
   676  	return columnIndex, func(value reflect.Value, levels levels, row Row) (Row, error) {
   677  		var err error
   678  
   679  		for i, f := range funcs {
   680  			if row, err = f(fields[i].Value(value), levels, row); err != nil {
   681  				err = fmt.Errorf("%s → %w", fields[i].Name(), err)
   682  				break
   683  			}
   684  		}
   685  
   686  		return row, err
   687  	}
   688  }
   689  
   690  //go:noinline
   691  func reconstructFuncOfLeaf(columnIndex int16, node Node) (int16, reconstructFunc) {
   692  	logicalType := node.Type().LogicalType()
   693  	return columnIndex + 1, func(value reflect.Value, _ levels, row Row) (Row, error) {
   694  		if !row.startsWith(columnIndex) {
   695  			return row, fmt.Errorf("no values found in parquet row for column %d", columnIndex)
   696  		}
   697  		switch {
   698  		case value.Kind() == reflect.String && logicalType.Timestamp != nil:
   699  			{
   700  				value.SetString(utils.TimeMsToString(row[0].Int64()))
   701  				return row[1:], nil
   702  			}
   703  		case value.Kind() == reflect.String && logicalType.Date != nil:
   704  			{
   705  				value.SetString(utils.DateToString(row[0].Int32()))
   706  				return row[1:], nil
   707  			}
   708  		}
   709  		return row[1:], assignValue(value, row[0])
   710  	}
   711  }