github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/col/colserde/arrowbatchconverter.go (about)

     1  // Copyright 2019 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  package colserde
    12  
    13  import (
    14  	"encoding/binary"
    15  	"fmt"
    16  	"reflect"
    17  	"unsafe"
    18  
    19  	"github.com/apache/arrow/go/arrow"
    20  	"github.com/apache/arrow/go/arrow/array"
    21  	"github.com/apache/arrow/go/arrow/memory"
    22  	"github.com/cockroachdb/cockroach/pkg/col/coldata"
    23  	"github.com/cockroachdb/cockroach/pkg/col/typeconv"
    24  	"github.com/cockroachdb/cockroach/pkg/sql/types"
    25  	"github.com/cockroachdb/cockroach/pkg/util/duration"
    26  	"github.com/cockroachdb/errors"
    27  )
    28  
    29  // ArrowBatchConverter converts batches to arrow column data
    30  // ([]*array.Data) and back again.
    31  type ArrowBatchConverter struct {
    32  	typs []*types.T
    33  
    34  	// builders are the set of builders that need to be kept around in order to
    35  	// construct arrow representations of certain types when they cannot be simply
    36  	// cast from our current data representation.
    37  	builders struct {
    38  		// boolBuilder builds arrow bool columns as a bitmap from a bool slice.
    39  		boolBuilder *array.BooleanBuilder
    40  		// binaryBuilder builds arrow []byte columns as one []byte slice with
    41  		// accompanying offsets from a [][]byte slice.
    42  		binaryBuilder *array.BinaryBuilder
    43  	}
    44  
    45  	scratch struct {
    46  		// arrowData is used as scratch space returned as the corresponding
    47  		// conversion result.
    48  		arrowData []*array.Data
    49  		// buffers is scratch space for exactly two buffers per element in
    50  		// arrowData.
    51  		buffers [][]*memory.Buffer
    52  	}
    53  }
    54  
    55  // NewArrowBatchConverter converts coldata.Batches to []*array.Data and back
    56  // again according to the schema specified by typs. Converting data that does
    57  // not conform to typs results in undefined behavior.
    58  func NewArrowBatchConverter(typs []*types.T) (*ArrowBatchConverter, error) {
    59  	c := &ArrowBatchConverter{typs: typs}
    60  	c.builders.boolBuilder = array.NewBooleanBuilder(memory.DefaultAllocator)
    61  	c.builders.binaryBuilder = array.NewBinaryBuilder(memory.DefaultAllocator, arrow.BinaryTypes.Binary)
    62  	c.scratch.arrowData = make([]*array.Data, len(typs))
    63  	c.scratch.buffers = make([][]*memory.Buffer, len(typs))
    64  	for i := range c.scratch.buffers {
    65  		// Some types need only two buffers: one for the nulls, and one for the
    66  		// values, but others (i.e. Bytes) need an extra buffer for the
    67  		// offsets.
    68  		c.scratch.buffers[i] = make([]*memory.Buffer, 0, 3)
    69  	}
    70  	return c, nil
    71  }
    72  
    73  const (
    74  	sizeOfInt16   = int(unsafe.Sizeof(int16(0)))
    75  	sizeOfInt32   = int(unsafe.Sizeof(int32(0)))
    76  	sizeOfInt64   = int(unsafe.Sizeof(int64(0)))
    77  	sizeOfFloat64 = int(unsafe.Sizeof(float64(0)))
    78  )
    79  
    80  // BatchToArrow converts the first batch.Length elements of the batch into an
    81  // arrow []*array.Data. It is assumed that the batch is not larger than
    82  // coldata.BatchSize(). The returned []*array.Data may only be used until the
    83  // next call to BatchToArrow.
    84  func (c *ArrowBatchConverter) BatchToArrow(batch coldata.Batch) ([]*array.Data, error) {
    85  	if batch.Width() != len(c.typs) {
    86  		return nil, errors.AssertionFailedf("mismatched batch width and schema length: %d != %d", batch.Width(), len(c.typs))
    87  	}
    88  	n := batch.Length()
    89  	for i, typ := range c.typs {
    90  		vec := batch.ColVec(i)
    91  		canonicalTypeFamily := typeconv.TypeFamilyToCanonicalTypeFamily(typ.Family())
    92  
    93  		var arrowBitmap []byte
    94  		if vec.MaybeHasNulls() {
    95  			n := vec.Nulls()
    96  			// To conform to the Arrow spec, zero out all trailing null values.
    97  			n.Truncate(batch.Length())
    98  			arrowBitmap = n.NullBitmap()
    99  		}
   100  
   101  		var data *array.Data
   102  		switch canonicalTypeFamily {
   103  		case types.BoolFamily:
   104  			c.builders.boolBuilder.AppendValues(vec.Bool()[:n], nil /* valid */)
   105  			data = c.builders.boolBuilder.NewBooleanArray().Data()
   106  		case types.DecimalFamily:
   107  			decimals := vec.Decimal()[:n]
   108  			for _, d := range decimals {
   109  				marshaled, err := d.MarshalText()
   110  				if err != nil {
   111  					return nil, err
   112  				}
   113  				c.builders.binaryBuilder.Append(marshaled)
   114  			}
   115  			data = c.builders.binaryBuilder.NewBinaryArray().Data()
   116  		case types.TimestampTZFamily:
   117  			timestamps := vec.Timestamp()[:n]
   118  			for _, ts := range timestamps {
   119  				marshaled, err := ts.MarshalBinary()
   120  				if err != nil {
   121  					return nil, err
   122  				}
   123  				c.builders.binaryBuilder.Append(marshaled)
   124  			}
   125  			data = c.builders.binaryBuilder.NewBinaryArray().Data()
   126  		case types.IntervalFamily:
   127  			intervals := vec.Interval()[:n]
   128  			// Appending to the binary builder will copy the bytes, so it's safe to
   129  			// reuse a scratch bytes to encode the interval into.
   130  			scratchIntervalBytes := make([]byte, sizeOfInt64*3)
   131  			for _, interval := range intervals {
   132  				nanos, months, days, err := interval.Encode()
   133  				if err != nil {
   134  					return nil, err
   135  				}
   136  				binary.LittleEndian.PutUint64(scratchIntervalBytes[0:sizeOfInt64], uint64(nanos))
   137  				binary.LittleEndian.PutUint64(scratchIntervalBytes[sizeOfInt64:sizeOfInt64*2], uint64(months))
   138  				binary.LittleEndian.PutUint64(scratchIntervalBytes[sizeOfInt64*2:sizeOfInt64*3], uint64(days))
   139  				c.builders.binaryBuilder.Append(scratchIntervalBytes)
   140  			}
   141  			data = c.builders.binaryBuilder.NewBinaryArray().Data()
   142  		case typeconv.DatumVecCanonicalTypeFamily:
   143  			datums := vec.Datum().Slice(0 /* start */, n)
   144  			for idx := 0; idx < n; idx++ {
   145  				b, err := datums.MarshalAt(idx)
   146  				if err != nil {
   147  					return nil, err
   148  				}
   149  				c.builders.binaryBuilder.Append(b)
   150  			}
   151  			data = c.builders.binaryBuilder.NewBinaryArray().Data()
   152  		}
   153  		if data != nil {
   154  			if arrowBitmap != nil {
   155  				// Overwrite empty null bitmap with the true bitmap.
   156  				data.Buffers()[0] = memory.NewBufferBytes(arrowBitmap)
   157  			}
   158  			c.scratch.arrowData[i] = data
   159  			continue
   160  		}
   161  
   162  		var (
   163  			values  []byte
   164  			offsets []byte
   165  
   166  			// dataHeader is the reflect.SliceHeader of the coldata.Vec's underlying
   167  			// data slice that we are casting to bytes.
   168  			dataHeader *reflect.SliceHeader
   169  			// datumSize is the size of one datum that we are casting to a byte slice.
   170  			datumSize int
   171  		)
   172  
   173  		switch canonicalTypeFamily {
   174  		case types.BytesFamily:
   175  			var int32Offsets []int32
   176  			values, int32Offsets = vec.Bytes().ToArrowSerializationFormat(n)
   177  			// Cast int32Offsets to []byte.
   178  			int32Header := (*reflect.SliceHeader)(unsafe.Pointer(&int32Offsets))
   179  			offsetsHeader := (*reflect.SliceHeader)(unsafe.Pointer(&offsets))
   180  			offsetsHeader.Data = int32Header.Data
   181  			offsetsHeader.Len = int32Header.Len * sizeOfInt32
   182  			offsetsHeader.Cap = int32Header.Cap * sizeOfInt32
   183  		case types.IntFamily:
   184  			switch typ.Width() {
   185  			case 16:
   186  				ints := vec.Int16()[:n]
   187  				dataHeader = (*reflect.SliceHeader)(unsafe.Pointer(&ints))
   188  				datumSize = sizeOfInt16
   189  			case 32:
   190  				ints := vec.Int32()[:n]
   191  				dataHeader = (*reflect.SliceHeader)(unsafe.Pointer(&ints))
   192  				datumSize = sizeOfInt32
   193  			case 0, 64:
   194  				ints := vec.Int64()[:n]
   195  				dataHeader = (*reflect.SliceHeader)(unsafe.Pointer(&ints))
   196  				datumSize = sizeOfInt64
   197  			default:
   198  				panic(fmt.Sprintf("unexpected int width: %d", typ.Width()))
   199  			}
   200  		case types.FloatFamily:
   201  			floats := vec.Float64()[:n]
   202  			dataHeader = (*reflect.SliceHeader)(unsafe.Pointer(&floats))
   203  			datumSize = sizeOfFloat64
   204  		default:
   205  			panic(fmt.Sprintf("unsupported type for conversion to arrow data %s", typ))
   206  		}
   207  
   208  		// Cast values if not set (mostly for non-byte types).
   209  		if values == nil {
   210  			valuesHeader := (*reflect.SliceHeader)(unsafe.Pointer(&values))
   211  			valuesHeader.Data = dataHeader.Data
   212  			valuesHeader.Len = dataHeader.Len * datumSize
   213  			valuesHeader.Cap = dataHeader.Cap * datumSize
   214  		}
   215  
   216  		// Construct the underlying arrow buffers.
   217  		// WARNING: The ordering of construction is critical.
   218  		c.scratch.buffers[i] = c.scratch.buffers[i][:0]
   219  		c.scratch.buffers[i] = append(c.scratch.buffers[i], memory.NewBufferBytes(arrowBitmap))
   220  		if offsets != nil {
   221  			c.scratch.buffers[i] = append(c.scratch.buffers[i], memory.NewBufferBytes(offsets))
   222  		}
   223  		c.scratch.buffers[i] = append(c.scratch.buffers[i], memory.NewBufferBytes(values))
   224  
   225  		// Create the data from the buffers. It might be surprising that we don't
   226  		// set a type or a null count, but these fields are not used in the way that
   227  		// we're using array.Data. The part we care about are the buffers. Type
   228  		// information is inferred from the ArrowBatchConverter schema, null count
   229  		// is an optimization we can use when working with nulls, and childData is
   230  		// only used for nested types like Lists, Structs, or Unions.
   231  		c.scratch.arrowData[i] = array.NewData(
   232  			nil /* dtype */, n, c.scratch.buffers[i], nil /* childData */, 0 /* nulls */, 0, /* offset */
   233  		)
   234  	}
   235  	return c.scratch.arrowData, nil
   236  }
   237  
   238  // ArrowToBatch converts []*array.Data to a coldata.Batch. There must not be
   239  // more than coldata.BatchSize() elements in data. It's safe to call ArrowToBatch
   240  // concurrently.
   241  //
   242  // The passed in batch is overwritten, but after this method returns it stays
   243  // valid as long as `data` stays valid. Callers can use this to control the
   244  // lifetimes of the batches, saving allocations when they can be reused (i.e.
   245  // reused by passing them back into this function).
   246  //
   247  // The passed in data is also mutated (we store nulls differently than arrow and
   248  // the adjustment is done in place).
   249  func (c *ArrowBatchConverter) ArrowToBatch(data []*array.Data, b coldata.Batch) error {
   250  	if len(data) != len(c.typs) {
   251  		return errors.Errorf("mismatched data and schema length: %d != %d", len(data), len(c.typs))
   252  	}
   253  	// Assume > 0 length data.
   254  	n := data[0].Len()
   255  
   256  	for i, typ := range c.typs {
   257  		vec := b.ColVec(i)
   258  		d := data[i]
   259  
   260  		var arr array.Interface
   261  		switch typeconv.TypeFamilyToCanonicalTypeFamily(typ.Family()) {
   262  		case types.BoolFamily:
   263  			boolArr := array.NewBooleanData(d)
   264  			vecArr := vec.Bool()
   265  			for i := 0; i < boolArr.Len(); i++ {
   266  				vecArr[i] = boolArr.Value(i)
   267  			}
   268  			arr = boolArr
   269  		case types.BytesFamily:
   270  			bytesArr := array.NewBinaryData(d)
   271  			bytes := bytesArr.ValueBytes()
   272  			if bytes == nil {
   273  				// All bytes values are empty, so the representation is solely with the
   274  				// offsets slice, so create an empty slice so that the conversion
   275  				// corresponds.
   276  				bytes = make([]byte, 0)
   277  			}
   278  			coldata.BytesFromArrowSerializationFormat(vec.Bytes(), bytes, bytesArr.ValueOffsets())
   279  			arr = bytesArr
   280  		case types.DecimalFamily:
   281  			// TODO(yuzefovich): this serialization is quite inefficient - improve
   282  			// it.
   283  			bytesArr := array.NewBinaryData(d)
   284  			bytes := bytesArr.ValueBytes()
   285  			if bytes == nil {
   286  				// All bytes values are empty, so the representation is solely with the
   287  				// offsets slice, so create an empty slice so that the conversion
   288  				// corresponds.
   289  				bytes = make([]byte, 0)
   290  			}
   291  			offsets := bytesArr.ValueOffsets()
   292  			vecArr := vec.Decimal()
   293  			for i := 0; i < len(offsets)-1; i++ {
   294  				if err := vecArr[i].UnmarshalText(bytes[offsets[i]:offsets[i+1]]); err != nil {
   295  					return err
   296  				}
   297  			}
   298  			arr = bytesArr
   299  		case types.TimestampTZFamily:
   300  			// TODO(yuzefovich): this serialization is quite inefficient - improve
   301  			// it.
   302  			bytesArr := array.NewBinaryData(d)
   303  			bytes := bytesArr.ValueBytes()
   304  			if bytes == nil {
   305  				// All bytes values are empty, so the representation is solely with the
   306  				// offsets slice, so create an empty slice so that the conversion
   307  				// corresponds.
   308  				bytes = make([]byte, 0)
   309  			}
   310  			offsets := bytesArr.ValueOffsets()
   311  			vecArr := vec.Timestamp()
   312  			for i := 0; i < len(offsets)-1; i++ {
   313  				if err := vecArr[i].UnmarshalBinary(bytes[offsets[i]:offsets[i+1]]); err != nil {
   314  					return err
   315  				}
   316  			}
   317  			arr = bytesArr
   318  		case types.IntervalFamily:
   319  			// TODO(asubiotto): this serialization is quite inefficient compared to
   320  			//  the direct casts below. Improve it.
   321  			bytesArr := array.NewBinaryData(d)
   322  			bytes := bytesArr.ValueBytes()
   323  			if bytes == nil {
   324  				// All bytes values are empty, so the representation is solely with the
   325  				// offsets slice, so create an empty slice so that the conversion
   326  				// corresponds.
   327  				bytes = make([]byte, 0)
   328  			}
   329  			offsets := bytesArr.ValueOffsets()
   330  			vecArr := vec.Interval()
   331  			for i := 0; i < len(offsets)-1; i++ {
   332  				intervalBytes := bytes[offsets[i]:offsets[i+1]]
   333  				var err error
   334  				vecArr[i], err = duration.Decode(
   335  					int64(binary.LittleEndian.Uint64(intervalBytes[0:sizeOfInt64])),
   336  					int64(binary.LittleEndian.Uint64(intervalBytes[sizeOfInt64:sizeOfInt64*2])),
   337  					int64(binary.LittleEndian.Uint64(intervalBytes[sizeOfInt64*2:sizeOfInt64*3])),
   338  				)
   339  				if err != nil {
   340  					return err
   341  				}
   342  			}
   343  			arr = bytesArr
   344  		case typeconv.DatumVecCanonicalTypeFamily:
   345  			bytesArr := array.NewBinaryData(d)
   346  			bytes := bytesArr.ValueBytes()
   347  			if bytes == nil {
   348  				// All bytes values are empty, so the representation is solely with the
   349  				// offsets slice, so create an empty slice so that the conversion
   350  				// corresponds.
   351  				bytes = make([]byte, 0)
   352  			}
   353  			offsets := bytesArr.ValueOffsets()
   354  			vecArr := vec.Datum()
   355  			for i := 0; i < len(offsets)-1; i++ {
   356  				err := vecArr.UnmarshalTo(i, bytes[offsets[i]:offsets[i+1]])
   357  				if err != nil {
   358  					return err
   359  				}
   360  			}
   361  			arr = bytesArr
   362  		default:
   363  			var col interface{}
   364  			switch typeconv.TypeFamilyToCanonicalTypeFamily(typ.Family()) {
   365  			case types.IntFamily:
   366  				switch typ.Width() {
   367  				case 16:
   368  					intArr := array.NewInt16Data(d)
   369  					col = intArr.Int16Values()
   370  					arr = intArr
   371  				case 32:
   372  					intArr := array.NewInt32Data(d)
   373  					col = intArr.Int32Values()
   374  					arr = intArr
   375  				case 0, 64:
   376  					intArr := array.NewInt64Data(d)
   377  					col = intArr.Int64Values()
   378  					arr = intArr
   379  				default:
   380  					panic(fmt.Sprintf("unexpected int width: %d", typ.Width()))
   381  				}
   382  			case types.FloatFamily:
   383  				floatArr := array.NewFloat64Data(d)
   384  				col = floatArr.Float64Values()
   385  				arr = floatArr
   386  			default:
   387  				panic(
   388  					fmt.Sprintf("unsupported type for conversion to column batch %s", d.DataType().Name()),
   389  				)
   390  			}
   391  			vec.SetCol(col)
   392  		}
   393  		arrowBitmap := arr.NullBitmapBytes()
   394  		if len(arrowBitmap) != 0 {
   395  			vec.Nulls().SetNullBitmap(arrowBitmap, n)
   396  		} else {
   397  			vec.Nulls().UnsetNulls()
   398  		}
   399  		b.SetSelection(false)
   400  	}
   401  	b.SetLength(n)
   402  	return nil
   403  }