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 }