2024-01-07 19:38:49 +08:00
|
|
|
// Licensed to the LF AI & Data foundation under one
|
|
|
|
// or more contributor license agreements. See the NOTICE file
|
|
|
|
// distributed with this work for additional information
|
|
|
|
// regarding copyright ownership. The ASF licenses this file
|
|
|
|
// to you under the Apache License, Version 2.0 (the
|
|
|
|
// "License"); you may not use this file except in compliance
|
|
|
|
// with the License. You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
package parquet
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"github.com/apache/arrow/go/v12/arrow"
|
|
|
|
"github.com/apache/arrow/go/v12/arrow/array"
|
|
|
|
"github.com/apache/arrow/go/v12/parquet/pqarrow"
|
|
|
|
"github.com/samber/lo"
|
|
|
|
"golang.org/x/exp/constraints"
|
|
|
|
|
|
|
|
"github.com/milvus-io/milvus-proto/go-api/v2/schemapb"
|
2024-05-07 18:43:30 +08:00
|
|
|
"github.com/milvus-io/milvus/internal/storage"
|
2024-08-20 11:42:55 +08:00
|
|
|
"github.com/milvus-io/milvus/internal/util/importutilv2/common"
|
2024-01-07 19:38:49 +08:00
|
|
|
"github.com/milvus-io/milvus/pkg/util/merr"
|
2024-08-20 11:42:55 +08:00
|
|
|
"github.com/milvus-io/milvus/pkg/util/parameterutil"
|
2024-01-07 19:38:49 +08:00
|
|
|
"github.com/milvus-io/milvus/pkg/util/typeutil"
|
|
|
|
)
|
|
|
|
|
|
|
|
type FieldReader struct {
|
|
|
|
columnIndex int
|
|
|
|
columnReader *pqarrow.ColumnReader
|
|
|
|
|
|
|
|
dim int
|
|
|
|
field *schemapb.FieldSchema
|
|
|
|
}
|
|
|
|
|
2024-01-31 20:45:04 +08:00
|
|
|
func NewFieldReader(ctx context.Context, reader *pqarrow.FileReader, columnIndex int, field *schemapb.FieldSchema) (*FieldReader, error) {
|
|
|
|
columnReader, err := reader.GetColumn(ctx, columnIndex)
|
2024-01-07 19:38:49 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var dim int64 = 1
|
2024-05-07 18:43:30 +08:00
|
|
|
if typeutil.IsVectorType(field.GetDataType()) && !typeutil.IsSparseFloatVectorType(field.GetDataType()) {
|
2024-01-07 19:38:49 +08:00
|
|
|
dim, err = typeutil.GetDim(field)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cr := &FieldReader{
|
|
|
|
columnIndex: columnIndex,
|
|
|
|
columnReader: columnReader,
|
|
|
|
dim: int(dim),
|
|
|
|
field: field,
|
|
|
|
}
|
|
|
|
return cr, nil
|
|
|
|
}
|
|
|
|
|
2024-08-20 16:50:55 +08:00
|
|
|
func (c *FieldReader) Next(count int64) (any, any, error) {
|
2024-01-07 19:38:49 +08:00
|
|
|
switch c.field.GetDataType() {
|
|
|
|
case schemapb.DataType_Bool:
|
2024-08-20 16:50:55 +08:00
|
|
|
if c.field.GetNullable() {
|
|
|
|
return ReadNullableBoolData(c, count)
|
|
|
|
}
|
|
|
|
data, err := ReadBoolData(c, count)
|
|
|
|
return data, nil, err
|
2024-01-07 19:38:49 +08:00
|
|
|
case schemapb.DataType_Int8:
|
2024-08-20 16:50:55 +08:00
|
|
|
if c.field.GetNullable() {
|
|
|
|
return ReadNullableIntegerOrFloatData[int8](c, count)
|
|
|
|
}
|
|
|
|
data, err := ReadIntegerOrFloatData[int8](c, count)
|
|
|
|
return data, nil, err
|
2024-01-07 19:38:49 +08:00
|
|
|
case schemapb.DataType_Int16:
|
2024-08-20 16:50:55 +08:00
|
|
|
if c.field.GetNullable() {
|
|
|
|
return ReadNullableIntegerOrFloatData[int16](c, count)
|
|
|
|
}
|
|
|
|
data, err := ReadIntegerOrFloatData[int16](c, count)
|
|
|
|
return data, nil, err
|
2024-01-07 19:38:49 +08:00
|
|
|
case schemapb.DataType_Int32:
|
2024-08-20 16:50:55 +08:00
|
|
|
if c.field.GetNullable() {
|
|
|
|
return ReadNullableIntegerOrFloatData[int32](c, count)
|
|
|
|
}
|
|
|
|
data, err := ReadIntegerOrFloatData[int32](c, count)
|
|
|
|
return data, nil, err
|
2024-01-07 19:38:49 +08:00
|
|
|
case schemapb.DataType_Int64:
|
2024-08-20 16:50:55 +08:00
|
|
|
if c.field.GetNullable() {
|
|
|
|
return ReadNullableIntegerOrFloatData[int64](c, count)
|
|
|
|
}
|
|
|
|
data, err := ReadIntegerOrFloatData[int64](c, count)
|
|
|
|
return data, nil, err
|
2024-01-07 19:38:49 +08:00
|
|
|
case schemapb.DataType_Float:
|
2024-08-20 16:50:55 +08:00
|
|
|
if c.field.GetNullable() {
|
|
|
|
data, validData, err := ReadNullableIntegerOrFloatData[float32](c, count)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
if data == nil {
|
|
|
|
return nil, nil, nil
|
|
|
|
}
|
|
|
|
return data, validData, typeutil.VerifyFloats32(data.([]float32))
|
|
|
|
}
|
2024-01-07 19:38:49 +08:00
|
|
|
data, err := ReadIntegerOrFloatData[float32](c, count)
|
|
|
|
if err != nil {
|
2024-08-20 16:50:55 +08:00
|
|
|
return nil, nil, err
|
2024-01-07 19:38:49 +08:00
|
|
|
}
|
|
|
|
if data == nil {
|
2024-08-20 16:50:55 +08:00
|
|
|
return nil, nil, nil
|
2024-01-07 19:38:49 +08:00
|
|
|
}
|
2024-08-20 16:50:55 +08:00
|
|
|
return data, nil, typeutil.VerifyFloats32(data.([]float32))
|
2024-01-07 19:38:49 +08:00
|
|
|
case schemapb.DataType_Double:
|
2024-08-20 16:50:55 +08:00
|
|
|
if c.field.GetNullable() {
|
|
|
|
data, validData, err := ReadNullableIntegerOrFloatData[float64](c, count)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
if data == nil {
|
|
|
|
return nil, nil, nil
|
|
|
|
}
|
|
|
|
return data, validData, typeutil.VerifyFloats64(data.([]float64))
|
|
|
|
}
|
2024-01-07 19:38:49 +08:00
|
|
|
data, err := ReadIntegerOrFloatData[float64](c, count)
|
|
|
|
if err != nil {
|
2024-08-20 16:50:55 +08:00
|
|
|
return nil, nil, err
|
2024-01-07 19:38:49 +08:00
|
|
|
}
|
|
|
|
if data == nil {
|
2024-08-20 16:50:55 +08:00
|
|
|
return nil, nil, nil
|
2024-01-07 19:38:49 +08:00
|
|
|
}
|
2024-08-20 16:50:55 +08:00
|
|
|
return data, nil, typeutil.VerifyFloats64(data.([]float64))
|
2024-01-07 19:38:49 +08:00
|
|
|
case schemapb.DataType_VarChar, schemapb.DataType_String:
|
2024-08-20 16:50:55 +08:00
|
|
|
if c.field.GetNullable() {
|
|
|
|
return ReadNullableVarcharData(c, count)
|
|
|
|
}
|
|
|
|
data, err := ReadVarcharData(c, count)
|
|
|
|
return data, nil, err
|
2024-01-07 19:38:49 +08:00
|
|
|
case schemapb.DataType_JSON:
|
2024-08-20 16:50:55 +08:00
|
|
|
if c.field.GetNullable() {
|
|
|
|
return ReadNullableJSONData(c, count)
|
|
|
|
}
|
|
|
|
data, err := ReadJSONData(c, count)
|
|
|
|
return data, nil, err
|
2024-05-07 18:43:30 +08:00
|
|
|
case schemapb.DataType_BinaryVector, schemapb.DataType_Float16Vector, schemapb.DataType_BFloat16Vector:
|
2024-08-20 16:50:55 +08:00
|
|
|
if c.field.GetNullable() {
|
|
|
|
return nil, nil, merr.WrapErrParameterInvalidMsg("not support nullable in vector")
|
|
|
|
}
|
|
|
|
data, err := ReadBinaryData(c, count)
|
|
|
|
return data, nil, err
|
2024-01-07 19:38:49 +08:00
|
|
|
case schemapb.DataType_FloatVector:
|
2024-08-20 16:50:55 +08:00
|
|
|
if c.field.GetNullable() {
|
|
|
|
return nil, nil, merr.WrapErrParameterInvalidMsg("not support nullable in vector")
|
|
|
|
}
|
2024-01-07 19:38:49 +08:00
|
|
|
arrayData, err := ReadIntegerOrFloatArrayData[float32](c, count)
|
|
|
|
if err != nil {
|
2024-08-20 16:50:55 +08:00
|
|
|
return nil, nil, err
|
2024-01-07 19:38:49 +08:00
|
|
|
}
|
|
|
|
if arrayData == nil {
|
2024-08-20 16:50:55 +08:00
|
|
|
return nil, nil, nil
|
2024-01-07 19:38:49 +08:00
|
|
|
}
|
|
|
|
vectors := lo.Flatten(arrayData.([][]float32))
|
2024-08-20 16:50:55 +08:00
|
|
|
return vectors, nil, nil
|
2024-05-07 18:43:30 +08:00
|
|
|
case schemapb.DataType_SparseFloatVector:
|
2024-08-20 16:50:55 +08:00
|
|
|
if c.field.GetNullable() {
|
|
|
|
return nil, nil, merr.WrapErrParameterInvalidMsg("not support nullable in vector")
|
|
|
|
}
|
|
|
|
data, err := ReadSparseFloatVectorData(c, count)
|
|
|
|
return data, nil, err
|
2024-01-07 19:38:49 +08:00
|
|
|
case schemapb.DataType_Array:
|
2024-08-20 16:50:55 +08:00
|
|
|
if c.field.GetNullable() {
|
|
|
|
return ReadNullableArrayData(c, count)
|
|
|
|
}
|
|
|
|
data, err := ReadArrayData(c, count)
|
|
|
|
return data, nil, err
|
2024-01-07 19:38:49 +08:00
|
|
|
default:
|
2024-08-20 16:50:55 +08:00
|
|
|
return nil, nil, merr.WrapErrImportFailed(fmt.Sprintf("unsupported data type '%s' for field '%s'",
|
2024-01-07 19:38:49 +08:00
|
|
|
c.field.GetDataType().String(), c.field.GetName()))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *FieldReader) Close() {}
|
|
|
|
|
|
|
|
func ReadBoolData(pcr *FieldReader, count int64) (any, error) {
|
|
|
|
chunked, err := pcr.columnReader.NextBatch(count)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
data := make([]bool, 0, count)
|
|
|
|
for _, chunk := range chunked.Chunks() {
|
|
|
|
dataNums := chunk.Data().Len()
|
|
|
|
boolReader, ok := chunk.(*array.Boolean)
|
2024-08-20 16:50:55 +08:00
|
|
|
if boolReader.NullN() > 0 {
|
|
|
|
return nil, merr.WrapErrParameterInvalidMsg("not nullable, but has null value")
|
|
|
|
}
|
2024-01-07 19:38:49 +08:00
|
|
|
if !ok {
|
|
|
|
return nil, WrapTypeErr("bool", chunk.DataType().Name(), pcr.field)
|
|
|
|
}
|
|
|
|
for i := 0; i < dataNums; i++ {
|
2024-05-20 22:25:38 +08:00
|
|
|
data = append(data, boolReader.Value(i))
|
2024-01-07 19:38:49 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(data) == 0 {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
return data, nil
|
|
|
|
}
|
|
|
|
|
2024-08-20 16:50:55 +08:00
|
|
|
func ReadNullableBoolData(pcr *FieldReader, count int64) (any, []bool, error) {
|
|
|
|
chunked, err := pcr.columnReader.NextBatch(count)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
data := make([]bool, 0, count)
|
|
|
|
validData := make([]bool, 0, count)
|
|
|
|
for _, chunk := range chunked.Chunks() {
|
|
|
|
dataNums := chunk.Data().Len()
|
|
|
|
boolReader, ok := chunk.(*array.Boolean)
|
|
|
|
if !ok {
|
2024-09-19 18:43:10 +08:00
|
|
|
// the chunk type may be *array.Null if the data in chunk is all null
|
|
|
|
_, ok := chunk.(*array.Null)
|
|
|
|
if !ok {
|
|
|
|
return nil, nil, WrapTypeErr("bool|null", chunk.DataType().Name(), pcr.field)
|
|
|
|
}
|
|
|
|
validData = append(validData, make([]bool, dataNums)...)
|
|
|
|
data = append(data, make([]bool, dataNums)...)
|
|
|
|
} else {
|
|
|
|
validData = append(validData, bytesToBoolArray(dataNums, boolReader.NullBitmapBytes())...)
|
|
|
|
for i := 0; i < dataNums; i++ {
|
|
|
|
data = append(data, boolReader.Value(i))
|
|
|
|
}
|
2024-08-20 16:50:55 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(data) == 0 {
|
|
|
|
return nil, nil, nil
|
|
|
|
}
|
|
|
|
if len(data) != len(validData) {
|
|
|
|
return nil, nil, merr.WrapErrParameterInvalid(len(data), len(validData), "length of data is not equal to length of valid_data")
|
|
|
|
}
|
|
|
|
return data, validData, nil
|
|
|
|
}
|
|
|
|
|
2024-01-07 19:38:49 +08:00
|
|
|
func ReadIntegerOrFloatData[T constraints.Integer | constraints.Float](pcr *FieldReader, count int64) (any, error) {
|
|
|
|
chunked, err := pcr.columnReader.NextBatch(count)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
data := make([]T, 0, count)
|
|
|
|
for _, chunk := range chunked.Chunks() {
|
|
|
|
dataNums := chunk.Data().Len()
|
|
|
|
switch chunk.DataType().ID() {
|
|
|
|
case arrow.INT8:
|
|
|
|
int8Reader := chunk.(*array.Int8)
|
2024-08-20 16:50:55 +08:00
|
|
|
if int8Reader.NullN() > 0 {
|
|
|
|
return nil, merr.WrapErrParameterInvalidMsg("not nullable, but has null value")
|
|
|
|
}
|
2024-01-07 19:38:49 +08:00
|
|
|
for i := 0; i < dataNums; i++ {
|
2024-05-20 22:25:38 +08:00
|
|
|
data = append(data, T(int8Reader.Value(i)))
|
2024-01-07 19:38:49 +08:00
|
|
|
}
|
|
|
|
case arrow.INT16:
|
|
|
|
int16Reader := chunk.(*array.Int16)
|
2024-08-20 16:50:55 +08:00
|
|
|
if int16Reader.NullN() > 0 {
|
|
|
|
return nil, merr.WrapErrParameterInvalidMsg("not nullable, but has null value")
|
|
|
|
}
|
2024-01-07 19:38:49 +08:00
|
|
|
for i := 0; i < dataNums; i++ {
|
2024-05-20 22:25:38 +08:00
|
|
|
data = append(data, T(int16Reader.Value(i)))
|
2024-01-07 19:38:49 +08:00
|
|
|
}
|
|
|
|
case arrow.INT32:
|
|
|
|
int32Reader := chunk.(*array.Int32)
|
2024-08-20 16:50:55 +08:00
|
|
|
if int32Reader.NullN() > 0 {
|
|
|
|
return nil, merr.WrapErrParameterInvalidMsg("not nullable, but has null value")
|
|
|
|
}
|
2024-01-07 19:38:49 +08:00
|
|
|
for i := 0; i < dataNums; i++ {
|
2024-05-20 22:25:38 +08:00
|
|
|
data = append(data, T(int32Reader.Value(i)))
|
2024-01-07 19:38:49 +08:00
|
|
|
}
|
|
|
|
case arrow.INT64:
|
|
|
|
int64Reader := chunk.(*array.Int64)
|
2024-08-20 16:50:55 +08:00
|
|
|
if int64Reader.NullN() > 0 {
|
|
|
|
return nil, merr.WrapErrParameterInvalidMsg("not nullable, but has null value")
|
|
|
|
}
|
2024-01-07 19:38:49 +08:00
|
|
|
for i := 0; i < dataNums; i++ {
|
2024-05-20 22:25:38 +08:00
|
|
|
data = append(data, T(int64Reader.Value(i)))
|
2024-01-07 19:38:49 +08:00
|
|
|
}
|
|
|
|
case arrow.FLOAT32:
|
|
|
|
float32Reader := chunk.(*array.Float32)
|
2024-08-20 16:50:55 +08:00
|
|
|
if float32Reader.NullN() > 0 {
|
|
|
|
return nil, merr.WrapErrParameterInvalidMsg("not nullable, but has null value")
|
|
|
|
}
|
2024-01-07 19:38:49 +08:00
|
|
|
for i := 0; i < dataNums; i++ {
|
2024-05-20 22:25:38 +08:00
|
|
|
data = append(data, T(float32Reader.Value(i)))
|
2024-01-07 19:38:49 +08:00
|
|
|
}
|
|
|
|
case arrow.FLOAT64:
|
|
|
|
float64Reader := chunk.(*array.Float64)
|
2024-08-20 16:50:55 +08:00
|
|
|
if float64Reader.NullN() > 0 {
|
|
|
|
return nil, merr.WrapErrParameterInvalidMsg("not nullable, but has null value")
|
|
|
|
}
|
2024-01-07 19:38:49 +08:00
|
|
|
for i := 0; i < dataNums; i++ {
|
2024-05-20 22:25:38 +08:00
|
|
|
data = append(data, T(float64Reader.Value(i)))
|
2024-01-07 19:38:49 +08:00
|
|
|
}
|
|
|
|
default:
|
|
|
|
return nil, WrapTypeErr("integer|float", chunk.DataType().Name(), pcr.field)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(data) == 0 {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
return data, nil
|
|
|
|
}
|
|
|
|
|
2024-08-20 16:50:55 +08:00
|
|
|
func ReadNullableIntegerOrFloatData[T constraints.Integer | constraints.Float](pcr *FieldReader, count int64) (any, []bool, error) {
|
|
|
|
chunked, err := pcr.columnReader.NextBatch(count)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
data := make([]T, 0, count)
|
|
|
|
validData := make([]bool, 0, count)
|
|
|
|
for _, chunk := range chunked.Chunks() {
|
|
|
|
dataNums := chunk.Data().Len()
|
|
|
|
switch chunk.DataType().ID() {
|
|
|
|
case arrow.INT8:
|
|
|
|
int8Reader := chunk.(*array.Int8)
|
|
|
|
validData = append(validData, bytesToBoolArray(dataNums, int8Reader.NullBitmapBytes())...)
|
|
|
|
for i := 0; i < dataNums; i++ {
|
|
|
|
data = append(data, T(int8Reader.Value(i)))
|
|
|
|
}
|
|
|
|
case arrow.INT16:
|
|
|
|
int16Reader := chunk.(*array.Int16)
|
|
|
|
validData = append(validData, bytesToBoolArray(dataNums, int16Reader.NullBitmapBytes())...)
|
|
|
|
for i := 0; i < dataNums; i++ {
|
|
|
|
data = append(data, T(int16Reader.Value(i)))
|
|
|
|
}
|
|
|
|
case arrow.INT32:
|
|
|
|
int32Reader := chunk.(*array.Int32)
|
|
|
|
validData = append(validData, bytesToBoolArray(dataNums, int32Reader.NullBitmapBytes())...)
|
|
|
|
for i := 0; i < dataNums; i++ {
|
|
|
|
data = append(data, T(int32Reader.Value(i)))
|
|
|
|
}
|
|
|
|
case arrow.INT64:
|
|
|
|
int64Reader := chunk.(*array.Int64)
|
|
|
|
validData = append(validData, bytesToBoolArray(dataNums, int64Reader.NullBitmapBytes())...)
|
|
|
|
for i := 0; i < dataNums; i++ {
|
|
|
|
data = append(data, T(int64Reader.Value(i)))
|
|
|
|
}
|
|
|
|
case arrow.FLOAT32:
|
|
|
|
float32Reader := chunk.(*array.Float32)
|
|
|
|
validData = append(validData, bytesToBoolArray(dataNums, float32Reader.NullBitmapBytes())...)
|
|
|
|
for i := 0; i < dataNums; i++ {
|
|
|
|
data = append(data, T(float32Reader.Value(i)))
|
|
|
|
}
|
|
|
|
case arrow.FLOAT64:
|
|
|
|
float64Reader := chunk.(*array.Float64)
|
|
|
|
validData = append(validData, bytesToBoolArray(dataNums, float64Reader.NullBitmapBytes())...)
|
|
|
|
for i := 0; i < dataNums; i++ {
|
|
|
|
data = append(data, T(float64Reader.Value(i)))
|
|
|
|
}
|
2024-09-19 18:43:10 +08:00
|
|
|
case arrow.NULL:
|
|
|
|
// the chunk type may be *array.Null if the data in chunk is all null
|
|
|
|
validData = append(validData, make([]bool, dataNums)...)
|
|
|
|
data = append(data, make([]T, dataNums)...)
|
2024-08-20 16:50:55 +08:00
|
|
|
default:
|
2024-09-19 18:43:10 +08:00
|
|
|
return nil, nil, WrapTypeErr("integer|float|null", chunk.DataType().Name(), pcr.field)
|
2024-08-20 16:50:55 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(data) == 0 {
|
|
|
|
return nil, nil, nil
|
|
|
|
}
|
|
|
|
if len(data) != len(validData) {
|
|
|
|
return nil, nil, merr.WrapErrParameterInvalid(len(data), len(validData), "length of data is not equal to length of valid_data")
|
|
|
|
}
|
|
|
|
return data, validData, nil
|
|
|
|
}
|
|
|
|
|
2024-01-07 19:38:49 +08:00
|
|
|
func ReadStringData(pcr *FieldReader, count int64) (any, error) {
|
|
|
|
chunked, err := pcr.columnReader.NextBatch(count)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
data := make([]string, 0, count)
|
|
|
|
for _, chunk := range chunked.Chunks() {
|
|
|
|
dataNums := chunk.Data().Len()
|
|
|
|
stringReader, ok := chunk.(*array.String)
|
2024-08-20 16:50:55 +08:00
|
|
|
if stringReader.NullN() > 0 {
|
|
|
|
return nil, merr.WrapErrParameterInvalidMsg("not nullable, but has null value")
|
|
|
|
}
|
2024-01-07 19:38:49 +08:00
|
|
|
if !ok {
|
|
|
|
return nil, WrapTypeErr("string", chunk.DataType().Name(), pcr.field)
|
|
|
|
}
|
|
|
|
for i := 0; i < dataNums; i++ {
|
2024-05-20 22:25:38 +08:00
|
|
|
data = append(data, stringReader.Value(i))
|
2024-01-07 19:38:49 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(data) == 0 {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
return data, nil
|
|
|
|
}
|
|
|
|
|
2024-08-20 16:50:55 +08:00
|
|
|
func ReadNullableStringData(pcr *FieldReader, count int64) (any, []bool, error) {
|
|
|
|
chunked, err := pcr.columnReader.NextBatch(count)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
data := make([]string, 0, count)
|
|
|
|
validData := make([]bool, 0, count)
|
|
|
|
for _, chunk := range chunked.Chunks() {
|
|
|
|
dataNums := chunk.Data().Len()
|
|
|
|
stringReader, ok := chunk.(*array.String)
|
|
|
|
if !ok {
|
2024-09-19 18:43:10 +08:00
|
|
|
// the chunk type may be *array.Null if the data in chunk is all null
|
|
|
|
_, ok := chunk.(*array.Null)
|
|
|
|
if !ok {
|
|
|
|
return nil, nil, WrapTypeErr("string|null", chunk.DataType().Name(), pcr.field)
|
|
|
|
}
|
|
|
|
validData = append(validData, make([]bool, dataNums)...)
|
|
|
|
data = append(data, make([]string, dataNums)...)
|
|
|
|
} else {
|
|
|
|
validData = append(validData, bytesToBoolArray(dataNums, stringReader.NullBitmapBytes())...)
|
|
|
|
for i := 0; i < dataNums; i++ {
|
|
|
|
if stringReader.IsNull(i) {
|
|
|
|
data = append(data, "")
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
data = append(data, stringReader.ValueStr(i))
|
2024-08-20 16:50:55 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(data) == 0 {
|
|
|
|
return nil, nil, nil
|
|
|
|
}
|
|
|
|
if len(data) != len(validData) {
|
|
|
|
return nil, nil, merr.WrapErrParameterInvalid(len(data), len(validData), "length of data is not equal to length of valid_data")
|
|
|
|
}
|
|
|
|
return data, validData, nil
|
|
|
|
}
|
|
|
|
|
2024-08-20 11:42:55 +08:00
|
|
|
func ReadVarcharData(pcr *FieldReader, count int64) (any, error) {
|
|
|
|
chunked, err := pcr.columnReader.NextBatch(count)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
data := make([]string, 0, count)
|
|
|
|
maxLength, err := parameterutil.GetMaxLength(pcr.field)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
for _, chunk := range chunked.Chunks() {
|
|
|
|
dataNums := chunk.Data().Len()
|
|
|
|
stringReader, ok := chunk.(*array.String)
|
2024-08-20 16:50:55 +08:00
|
|
|
if stringReader.NullN() > 0 {
|
|
|
|
return nil, merr.WrapErrParameterInvalidMsg("not nullable, but has null value")
|
|
|
|
}
|
2024-08-20 11:42:55 +08:00
|
|
|
if !ok {
|
|
|
|
return nil, WrapTypeErr("string", chunk.DataType().Name(), pcr.field)
|
|
|
|
}
|
|
|
|
for i := 0; i < dataNums; i++ {
|
|
|
|
if err = common.CheckVarcharLength(stringReader.Value(i), maxLength); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
data = append(data, stringReader.Value(i))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(data) == 0 {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
return data, nil
|
|
|
|
}
|
|
|
|
|
2024-08-20 16:50:55 +08:00
|
|
|
func ReadNullableVarcharData(pcr *FieldReader, count int64) (any, []bool, error) {
|
|
|
|
chunked, err := pcr.columnReader.NextBatch(count)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
data := make([]string, 0, count)
|
|
|
|
maxLength, err := parameterutil.GetMaxLength(pcr.field)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
validData := make([]bool, 0, count)
|
|
|
|
for _, chunk := range chunked.Chunks() {
|
|
|
|
dataNums := chunk.Data().Len()
|
|
|
|
stringReader, ok := chunk.(*array.String)
|
|
|
|
if !ok {
|
2024-09-19 18:43:10 +08:00
|
|
|
// the chunk type may be *array.Null if the data in chunk is all null
|
|
|
|
_, ok := chunk.(*array.Null)
|
|
|
|
if !ok {
|
|
|
|
return nil, nil, WrapTypeErr("string|null", chunk.DataType().Name(), pcr.field)
|
2024-08-20 16:50:55 +08:00
|
|
|
}
|
2024-09-19 18:43:10 +08:00
|
|
|
validData = append(validData, make([]bool, dataNums)...)
|
|
|
|
data = append(data, make([]string, dataNums)...)
|
|
|
|
} else {
|
|
|
|
validData = append(validData, bytesToBoolArray(dataNums, stringReader.NullBitmapBytes())...)
|
|
|
|
for i := 0; i < dataNums; i++ {
|
|
|
|
if stringReader.IsNull(i) {
|
|
|
|
data = append(data, "")
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if err = common.CheckVarcharLength(stringReader.Value(i), maxLength); err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
data = append(data, stringReader.ValueStr(i))
|
2024-08-20 16:50:55 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(data) == 0 {
|
|
|
|
return nil, nil, nil
|
|
|
|
}
|
|
|
|
if len(data) != len(validData) {
|
|
|
|
return nil, nil, merr.WrapErrParameterInvalid(len(data), len(validData), "length of data is not equal to length of valid_data")
|
|
|
|
}
|
|
|
|
return data, validData, nil
|
|
|
|
}
|
|
|
|
|
2024-05-17 15:01:37 +08:00
|
|
|
func ReadJSONData(pcr *FieldReader, count int64) (any, error) {
|
|
|
|
// JSON field read data from string array Parquet
|
|
|
|
data, err := ReadStringData(pcr, count)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if data == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
byteArr := make([][]byte, 0)
|
|
|
|
for _, str := range data.([]string) {
|
|
|
|
var dummy interface{}
|
|
|
|
err = json.Unmarshal([]byte(str), &dummy)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if pcr.field.GetIsDynamic() {
|
|
|
|
var dummy2 map[string]interface{}
|
|
|
|
err = json.Unmarshal([]byte(str), &dummy2)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
byteArr = append(byteArr, []byte(str))
|
|
|
|
}
|
|
|
|
return byteArr, nil
|
|
|
|
}
|
|
|
|
|
2024-08-20 16:50:55 +08:00
|
|
|
func ReadNullableJSONData(pcr *FieldReader, count int64) (any, []bool, error) {
|
|
|
|
// JSON field read data from string array Parquet
|
|
|
|
data, validData, err := ReadNullableStringData(pcr, count)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
if data == nil {
|
|
|
|
return nil, nil, nil
|
|
|
|
}
|
|
|
|
byteArr := make([][]byte, 0)
|
|
|
|
for i, str := range data.([]string) {
|
|
|
|
if !validData[i] {
|
|
|
|
byteArr = append(byteArr, []byte(nil))
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
var dummy interface{}
|
|
|
|
err = json.Unmarshal([]byte(str), &dummy)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
if pcr.field.GetIsDynamic() {
|
|
|
|
var dummy2 map[string]interface{}
|
|
|
|
err = json.Unmarshal([]byte(str), &dummy2)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
byteArr = append(byteArr, []byte(str))
|
|
|
|
}
|
|
|
|
return byteArr, validData, nil
|
|
|
|
}
|
|
|
|
|
2024-05-07 18:43:30 +08:00
|
|
|
func ReadBinaryData(pcr *FieldReader, count int64) (any, error) {
|
|
|
|
dataType := pcr.field.GetDataType()
|
2024-01-07 19:38:49 +08:00
|
|
|
chunked, err := pcr.columnReader.NextBatch(count)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
data := make([]byte, 0, count)
|
|
|
|
for _, chunk := range chunked.Chunks() {
|
2024-05-07 18:43:30 +08:00
|
|
|
rows := chunk.Data().Len()
|
2024-01-07 19:38:49 +08:00
|
|
|
switch chunk.DataType().ID() {
|
|
|
|
case arrow.BINARY:
|
|
|
|
binaryReader := chunk.(*array.Binary)
|
2024-05-07 18:43:30 +08:00
|
|
|
for i := 0; i < rows; i++ {
|
2024-01-07 19:38:49 +08:00
|
|
|
data = append(data, binaryReader.Value(i)...)
|
|
|
|
}
|
|
|
|
case arrow.LIST:
|
|
|
|
listReader := chunk.(*array.List)
|
2024-08-05 15:30:16 +08:00
|
|
|
if err = checkVectorAligned(listReader.Offsets(), pcr.dim, dataType); err != nil {
|
|
|
|
return nil, merr.WrapErrImportFailed(fmt.Sprintf("length of vector is not aligned: %s, data type: %s", err.Error(), dataType.String()))
|
2024-01-07 19:38:49 +08:00
|
|
|
}
|
|
|
|
uint8Reader, ok := listReader.ListValues().(*array.Uint8)
|
|
|
|
if !ok {
|
|
|
|
return nil, WrapTypeErr("binary", listReader.ListValues().DataType().Name(), pcr.field)
|
|
|
|
}
|
2024-05-07 18:43:30 +08:00
|
|
|
data = append(data, uint8Reader.Uint8Values()...)
|
2024-01-07 19:38:49 +08:00
|
|
|
default:
|
|
|
|
return nil, WrapTypeErr("binary", chunk.DataType().Name(), pcr.field)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(data) == 0 {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
return data, nil
|
|
|
|
}
|
|
|
|
|
2024-05-17 15:01:37 +08:00
|
|
|
func ReadSparseFloatVectorData(pcr *FieldReader, count int64) (any, error) {
|
|
|
|
data, err := ReadStringData(pcr, count)
|
2024-05-07 18:43:30 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2024-05-17 15:01:37 +08:00
|
|
|
if data == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
byteArr := make([][]byte, 0, count)
|
2024-05-07 18:43:30 +08:00
|
|
|
maxDim := uint32(0)
|
2024-05-17 15:01:37 +08:00
|
|
|
for _, str := range data.([]string) {
|
|
|
|
rowVec, err := typeutil.CreateSparseFloatRowFromJSON([]byte(str))
|
|
|
|
if err != nil {
|
2024-05-21 15:09:39 +08:00
|
|
|
return nil, merr.WrapErrImportFailed(fmt.Sprintf("Invalid JSON string for SparseFloatVector: '%s', err = %v", str, err))
|
2024-05-07 18:43:30 +08:00
|
|
|
}
|
2024-05-17 15:01:37 +08:00
|
|
|
byteArr = append(byteArr, rowVec)
|
|
|
|
elemCount := len(rowVec) / 8
|
|
|
|
maxIdx := typeutil.SparseFloatRowIndexAt(rowVec, elemCount-1)
|
|
|
|
if maxIdx+1 > maxDim {
|
|
|
|
maxDim = maxIdx + 1
|
2024-05-07 18:43:30 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return &storage.SparseFloatVectorFieldData{
|
|
|
|
SparseFloatArray: schemapb.SparseFloatArray{
|
|
|
|
Dim: int64(maxDim),
|
2024-05-17 15:01:37 +08:00
|
|
|
Contents: byteArr,
|
2024-05-07 18:43:30 +08:00
|
|
|
},
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2024-08-05 15:30:16 +08:00
|
|
|
func checkVectorAlignWithDim(offsets []int32, dim int32) error {
|
2024-05-07 18:43:30 +08:00
|
|
|
for i := 1; i < len(offsets); i++ {
|
|
|
|
if offsets[i]-offsets[i-1] != dim {
|
2024-08-05 15:30:16 +08:00
|
|
|
return fmt.Errorf("expected %d but got %d", dim, offsets[i]-offsets[i-1])
|
2024-05-07 18:43:30 +08:00
|
|
|
}
|
|
|
|
}
|
2024-08-05 15:30:16 +08:00
|
|
|
return nil
|
2024-05-07 18:43:30 +08:00
|
|
|
}
|
|
|
|
|
2024-08-05 15:30:16 +08:00
|
|
|
func checkVectorAligned(offsets []int32, dim int, dataType schemapb.DataType) error {
|
2024-01-07 19:38:49 +08:00
|
|
|
if len(offsets) < 1 {
|
2024-08-05 15:30:16 +08:00
|
|
|
return fmt.Errorf("empty offsets")
|
2024-01-07 19:38:49 +08:00
|
|
|
}
|
2024-04-22 10:05:22 +08:00
|
|
|
switch dataType {
|
|
|
|
case schemapb.DataType_BinaryVector:
|
2024-05-07 18:43:30 +08:00
|
|
|
return checkVectorAlignWithDim(offsets, int32(dim/8))
|
2024-04-22 10:05:22 +08:00
|
|
|
case schemapb.DataType_FloatVector:
|
2024-05-07 18:43:30 +08:00
|
|
|
return checkVectorAlignWithDim(offsets, int32(dim))
|
2024-04-22 10:05:22 +08:00
|
|
|
case schemapb.DataType_Float16Vector, schemapb.DataType_BFloat16Vector:
|
2024-05-07 18:43:30 +08:00
|
|
|
return checkVectorAlignWithDim(offsets, int32(dim*2))
|
|
|
|
case schemapb.DataType_SparseFloatVector:
|
2024-05-17 15:01:37 +08:00
|
|
|
// JSON format, skip alignment check
|
2024-08-05 15:30:16 +08:00
|
|
|
return nil
|
2024-05-07 18:43:30 +08:00
|
|
|
default:
|
2024-08-05 15:30:16 +08:00
|
|
|
return fmt.Errorf("unexpected vector data type %s", dataType.String())
|
2024-01-07 19:38:49 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func ReadBoolArrayData(pcr *FieldReader, count int64) (any, error) {
|
|
|
|
chunked, err := pcr.columnReader.NextBatch(count)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
data := make([][]bool, 0, count)
|
|
|
|
for _, chunk := range chunked.Chunks() {
|
|
|
|
listReader, ok := chunk.(*array.List)
|
|
|
|
if !ok {
|
|
|
|
return nil, WrapTypeErr("list", chunk.DataType().Name(), pcr.field)
|
|
|
|
}
|
|
|
|
boolReader, ok := listReader.ListValues().(*array.Boolean)
|
|
|
|
if !ok {
|
|
|
|
return nil, WrapTypeErr("boolArray", chunk.DataType().Name(), pcr.field)
|
|
|
|
}
|
|
|
|
offsets := listReader.Offsets()
|
|
|
|
for i := 1; i < len(offsets); i++ {
|
|
|
|
start, end := offsets[i-1], offsets[i]
|
|
|
|
elementData := make([]bool, 0, end-start)
|
|
|
|
for j := start; j < end; j++ {
|
|
|
|
elementData = append(elementData, boolReader.Value(int(j)))
|
|
|
|
}
|
|
|
|
data = append(data, elementData)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(data) == 0 {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
return data, nil
|
|
|
|
}
|
|
|
|
|
2024-08-20 16:50:55 +08:00
|
|
|
func ReadNullableBoolArrayData(pcr *FieldReader, count int64) (any, []bool, error) {
|
|
|
|
chunked, err := pcr.columnReader.NextBatch(count)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
data := make([][]bool, 0, count)
|
|
|
|
validData := make([]bool, 0, count)
|
|
|
|
for _, chunk := range chunked.Chunks() {
|
|
|
|
listReader, ok := chunk.(*array.List)
|
|
|
|
if !ok {
|
2024-09-19 18:43:10 +08:00
|
|
|
// the chunk type may be *array.Null if the data in chunk is all null
|
|
|
|
_, ok := chunk.(*array.Null)
|
|
|
|
if !ok {
|
|
|
|
return nil, nil, WrapTypeErr("list|null", chunk.DataType().Name(), pcr.field)
|
2024-08-20 16:50:55 +08:00
|
|
|
}
|
2024-09-19 18:43:10 +08:00
|
|
|
dataNums := chunk.Data().Len()
|
|
|
|
validData = append(validData, make([]bool, dataNums)...)
|
|
|
|
data = append(data, make([][]bool, dataNums)...)
|
|
|
|
} else {
|
|
|
|
boolReader, ok := listReader.ListValues().(*array.Boolean)
|
|
|
|
if !ok {
|
|
|
|
return nil, nil, WrapTypeErr("boolArray", chunk.DataType().Name(), pcr.field)
|
|
|
|
}
|
|
|
|
offsets := listReader.Offsets()
|
|
|
|
for i := 1; i < len(offsets); i++ {
|
|
|
|
start, end := offsets[i-1], offsets[i]
|
|
|
|
elementData := make([]bool, 0, end-start)
|
|
|
|
for j := start; j < end; j++ {
|
|
|
|
elementData = append(elementData, boolReader.Value(int(j)))
|
|
|
|
}
|
|
|
|
data = append(data, elementData)
|
|
|
|
elementDataValid := true
|
|
|
|
if start == end {
|
|
|
|
elementDataValid = false
|
|
|
|
}
|
|
|
|
validData = append(validData, elementDataValid)
|
2024-08-20 16:50:55 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(data) == 0 {
|
|
|
|
return nil, nil, nil
|
|
|
|
}
|
|
|
|
if len(data) != len(validData) {
|
|
|
|
return nil, nil, merr.WrapErrParameterInvalid(len(data), len(validData), "length of data is not equal to length of valid_data")
|
|
|
|
}
|
|
|
|
return data, validData, nil
|
|
|
|
}
|
|
|
|
|
2024-01-07 19:38:49 +08:00
|
|
|
func ReadIntegerOrFloatArrayData[T constraints.Integer | constraints.Float](pcr *FieldReader, count int64) (any, error) {
|
|
|
|
chunked, err := pcr.columnReader.NextBatch(count)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
data := make([][]T, 0, count)
|
|
|
|
|
|
|
|
getDataFunc := func(offsets []int32, getValue func(int) T) {
|
|
|
|
for i := 1; i < len(offsets); i++ {
|
|
|
|
start, end := offsets[i-1], offsets[i]
|
|
|
|
elementData := make([]T, 0, end-start)
|
|
|
|
for j := start; j < end; j++ {
|
|
|
|
elementData = append(elementData, getValue(int(j)))
|
|
|
|
}
|
|
|
|
data = append(data, elementData)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for _, chunk := range chunked.Chunks() {
|
|
|
|
listReader, ok := chunk.(*array.List)
|
|
|
|
if !ok {
|
|
|
|
return nil, WrapTypeErr("list", chunk.DataType().Name(), pcr.field)
|
|
|
|
}
|
|
|
|
offsets := listReader.Offsets()
|
2024-04-22 10:05:22 +08:00
|
|
|
dataType := pcr.field.GetDataType()
|
2024-08-05 15:30:16 +08:00
|
|
|
if typeutil.IsVectorType(dataType) {
|
|
|
|
if err = checkVectorAligned(offsets, pcr.dim, dataType); err != nil {
|
|
|
|
return nil, merr.WrapErrImportFailed(fmt.Sprintf("length of vector is not aligned: %s, data type: %s", err.Error(), dataType.String()))
|
|
|
|
}
|
2024-01-07 19:38:49 +08:00
|
|
|
}
|
|
|
|
valueReader := listReader.ListValues()
|
|
|
|
switch valueReader.DataType().ID() {
|
|
|
|
case arrow.INT8:
|
|
|
|
int8Reader := valueReader.(*array.Int8)
|
|
|
|
getDataFunc(offsets, func(i int) T {
|
|
|
|
return T(int8Reader.Value(i))
|
|
|
|
})
|
|
|
|
case arrow.INT16:
|
|
|
|
int16Reader := valueReader.(*array.Int16)
|
|
|
|
getDataFunc(offsets, func(i int) T {
|
|
|
|
return T(int16Reader.Value(i))
|
|
|
|
})
|
|
|
|
case arrow.INT32:
|
|
|
|
int32Reader := valueReader.(*array.Int32)
|
|
|
|
getDataFunc(offsets, func(i int) T {
|
|
|
|
return T(int32Reader.Value(i))
|
|
|
|
})
|
|
|
|
case arrow.INT64:
|
|
|
|
int64Reader := valueReader.(*array.Int64)
|
|
|
|
getDataFunc(offsets, func(i int) T {
|
|
|
|
return T(int64Reader.Value(i))
|
|
|
|
})
|
|
|
|
case arrow.FLOAT32:
|
|
|
|
float32Reader := valueReader.(*array.Float32)
|
|
|
|
getDataFunc(offsets, func(i int) T {
|
|
|
|
return T(float32Reader.Value(i))
|
|
|
|
})
|
|
|
|
case arrow.FLOAT64:
|
|
|
|
float64Reader := valueReader.(*array.Float64)
|
|
|
|
getDataFunc(offsets, func(i int) T {
|
|
|
|
return T(float64Reader.Value(i))
|
|
|
|
})
|
|
|
|
default:
|
|
|
|
return nil, WrapTypeErr("integerArray|floatArray", chunk.DataType().Name(), pcr.field)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(data) == 0 {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
return data, nil
|
|
|
|
}
|
|
|
|
|
2024-08-20 16:50:55 +08:00
|
|
|
func ReadNullableIntegerOrFloatArrayData[T constraints.Integer | constraints.Float](pcr *FieldReader, count int64) (any, []bool, error) {
|
|
|
|
chunked, err := pcr.columnReader.NextBatch(count)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
data := make([][]T, 0, count)
|
|
|
|
validData := make([]bool, 0, count)
|
|
|
|
|
|
|
|
getDataFunc := func(offsets []int32, getValue func(int) T) {
|
|
|
|
for i := 1; i < len(offsets); i++ {
|
|
|
|
start, end := offsets[i-1], offsets[i]
|
|
|
|
elementData := make([]T, 0, end-start)
|
|
|
|
for j := start; j < end; j++ {
|
|
|
|
elementData = append(elementData, getValue(int(j)))
|
|
|
|
}
|
|
|
|
data = append(data, elementData)
|
|
|
|
elementDataValid := true
|
|
|
|
if start == end {
|
|
|
|
elementDataValid = false
|
|
|
|
}
|
|
|
|
validData = append(validData, elementDataValid)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for _, chunk := range chunked.Chunks() {
|
|
|
|
listReader, ok := chunk.(*array.List)
|
|
|
|
if !ok {
|
2024-09-19 18:43:10 +08:00
|
|
|
// the chunk type may be *array.Null if the data in chunk is all null
|
|
|
|
_, ok := chunk.(*array.Null)
|
|
|
|
if !ok {
|
|
|
|
return nil, nil, WrapTypeErr("list|null", chunk.DataType().Name(), pcr.field)
|
|
|
|
}
|
|
|
|
dataNums := chunk.Data().Len()
|
|
|
|
validData = append(validData, make([]bool, dataNums)...)
|
|
|
|
data = append(data, make([][]T, dataNums)...)
|
|
|
|
} else {
|
|
|
|
offsets := listReader.Offsets()
|
|
|
|
dataType := pcr.field.GetDataType()
|
|
|
|
if typeutil.IsVectorType(dataType) {
|
|
|
|
if err = checkVectorAligned(offsets, pcr.dim, dataType); err != nil {
|
|
|
|
return nil, nil, merr.WrapErrImportFailed(fmt.Sprintf("length of vector is not aligned: %s, data type: %s", err.Error(), dataType.String()))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
valueReader := listReader.ListValues()
|
|
|
|
switch valueReader.DataType().ID() {
|
|
|
|
case arrow.INT8:
|
|
|
|
int8Reader := valueReader.(*array.Int8)
|
|
|
|
getDataFunc(offsets, func(i int) T {
|
|
|
|
return T(int8Reader.Value(i))
|
|
|
|
})
|
|
|
|
case arrow.INT16:
|
|
|
|
int16Reader := valueReader.(*array.Int16)
|
|
|
|
getDataFunc(offsets, func(i int) T {
|
|
|
|
return T(int16Reader.Value(i))
|
|
|
|
})
|
|
|
|
case arrow.INT32:
|
|
|
|
int32Reader := valueReader.(*array.Int32)
|
|
|
|
getDataFunc(offsets, func(i int) T {
|
|
|
|
return T(int32Reader.Value(i))
|
|
|
|
})
|
|
|
|
case arrow.INT64:
|
|
|
|
int64Reader := valueReader.(*array.Int64)
|
|
|
|
getDataFunc(offsets, func(i int) T {
|
|
|
|
return T(int64Reader.Value(i))
|
|
|
|
})
|
|
|
|
case arrow.FLOAT32:
|
|
|
|
float32Reader := valueReader.(*array.Float32)
|
|
|
|
getDataFunc(offsets, func(i int) T {
|
|
|
|
return T(float32Reader.Value(i))
|
|
|
|
})
|
|
|
|
case arrow.FLOAT64:
|
|
|
|
float64Reader := valueReader.(*array.Float64)
|
|
|
|
getDataFunc(offsets, func(i int) T {
|
|
|
|
return T(float64Reader.Value(i))
|
|
|
|
})
|
|
|
|
default:
|
|
|
|
return nil, nil, WrapTypeErr("integerArray|floatArray", chunk.DataType().Name(), pcr.field)
|
2024-08-20 16:50:55 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(data) == 0 {
|
|
|
|
return nil, nil, nil
|
|
|
|
}
|
|
|
|
if len(data) != len(validData) {
|
|
|
|
return nil, nil, merr.WrapErrParameterInvalid(len(data), len(validData), "length of data is not equal to length of valid_data")
|
|
|
|
}
|
|
|
|
return data, validData, nil
|
|
|
|
}
|
|
|
|
|
2024-01-07 19:38:49 +08:00
|
|
|
func ReadStringArrayData(pcr *FieldReader, count int64) (any, error) {
|
|
|
|
chunked, err := pcr.columnReader.NextBatch(count)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
data := make([][]string, 0, count)
|
|
|
|
for _, chunk := range chunked.Chunks() {
|
|
|
|
listReader, ok := chunk.(*array.List)
|
|
|
|
if !ok {
|
|
|
|
return nil, WrapTypeErr("list", chunk.DataType().Name(), pcr.field)
|
|
|
|
}
|
|
|
|
stringReader, ok := listReader.ListValues().(*array.String)
|
|
|
|
if !ok {
|
|
|
|
return nil, WrapTypeErr("stringArray", chunk.DataType().Name(), pcr.field)
|
|
|
|
}
|
|
|
|
offsets := listReader.Offsets()
|
|
|
|
for i := 1; i < len(offsets); i++ {
|
|
|
|
start, end := offsets[i-1], offsets[i]
|
|
|
|
elementData := make([]string, 0, end-start)
|
|
|
|
for j := start; j < end; j++ {
|
|
|
|
elementData = append(elementData, stringReader.Value(int(j)))
|
|
|
|
}
|
|
|
|
data = append(data, elementData)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(data) == 0 {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
return data, nil
|
|
|
|
}
|
2024-05-17 15:01:37 +08:00
|
|
|
|
2024-08-20 16:50:55 +08:00
|
|
|
func ReadNullableStringArrayData(pcr *FieldReader, count int64) (any, []bool, error) {
|
|
|
|
chunked, err := pcr.columnReader.NextBatch(count)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
data := make([][]string, 0, count)
|
|
|
|
validData := make([]bool, 0, count)
|
|
|
|
for _, chunk := range chunked.Chunks() {
|
|
|
|
listReader, ok := chunk.(*array.List)
|
|
|
|
if !ok {
|
2024-09-19 18:43:10 +08:00
|
|
|
// the chunk type may be *array.Null if the data in chunk is all null
|
|
|
|
_, ok := chunk.(*array.Null)
|
|
|
|
if !ok {
|
|
|
|
return nil, nil, WrapTypeErr("list|null", chunk.DataType().Name(), pcr.field)
|
2024-08-20 16:50:55 +08:00
|
|
|
}
|
2024-09-19 18:43:10 +08:00
|
|
|
dataNums := chunk.Data().Len()
|
|
|
|
validData = append(validData, make([]bool, dataNums)...)
|
|
|
|
data = append(data, make([][]string, dataNums)...)
|
|
|
|
} else {
|
|
|
|
stringReader, ok := listReader.ListValues().(*array.String)
|
|
|
|
if !ok {
|
|
|
|
return nil, nil, WrapTypeErr("stringArray", chunk.DataType().Name(), pcr.field)
|
|
|
|
}
|
|
|
|
offsets := listReader.Offsets()
|
|
|
|
for i := 1; i < len(offsets); i++ {
|
|
|
|
start, end := offsets[i-1], offsets[i]
|
|
|
|
elementData := make([]string, 0, end-start)
|
|
|
|
for j := start; j < end; j++ {
|
|
|
|
elementData = append(elementData, stringReader.Value(int(j)))
|
|
|
|
}
|
|
|
|
data = append(data, elementData)
|
|
|
|
elementDataValid := true
|
|
|
|
if start == end {
|
|
|
|
elementDataValid = false
|
|
|
|
}
|
|
|
|
validData = append(validData, elementDataValid)
|
2024-08-20 16:50:55 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(data) == 0 {
|
|
|
|
return nil, nil, nil
|
|
|
|
}
|
|
|
|
if len(data) != len(validData) {
|
|
|
|
return nil, nil, merr.WrapErrParameterInvalid(len(data), len(validData), "length of data is not equal to length of valid_data")
|
|
|
|
}
|
|
|
|
return data, validData, nil
|
|
|
|
}
|
|
|
|
|
2024-05-17 15:01:37 +08:00
|
|
|
func ReadArrayData(pcr *FieldReader, count int64) (any, error) {
|
|
|
|
data := make([]*schemapb.ScalarField, 0, count)
|
2024-08-20 11:42:55 +08:00
|
|
|
maxCapacity, err := parameterutil.GetMaxCapacity(pcr.field)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2024-05-17 15:01:37 +08:00
|
|
|
elementType := pcr.field.GetElementType()
|
|
|
|
switch elementType {
|
|
|
|
case schemapb.DataType_Bool:
|
|
|
|
boolArray, err := ReadBoolArrayData(pcr, count)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if boolArray == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
for _, elementArray := range boolArray.([][]bool) {
|
2024-08-20 11:42:55 +08:00
|
|
|
if err = common.CheckArrayCapacity(len(elementArray), maxCapacity); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2024-05-17 15:01:37 +08:00
|
|
|
data = append(data, &schemapb.ScalarField{
|
|
|
|
Data: &schemapb.ScalarField_BoolData{
|
|
|
|
BoolData: &schemapb.BoolArray{
|
|
|
|
Data: elementArray,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
case schemapb.DataType_Int8:
|
|
|
|
int8Array, err := ReadIntegerOrFloatArrayData[int32](pcr, count)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if int8Array == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
for _, elementArray := range int8Array.([][]int32) {
|
2024-08-20 11:42:55 +08:00
|
|
|
if err = common.CheckArrayCapacity(len(elementArray), maxCapacity); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2024-05-17 15:01:37 +08:00
|
|
|
data = append(data, &schemapb.ScalarField{
|
|
|
|
Data: &schemapb.ScalarField_IntData{
|
|
|
|
IntData: &schemapb.IntArray{
|
|
|
|
Data: elementArray,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
case schemapb.DataType_Int16:
|
|
|
|
int16Array, err := ReadIntegerOrFloatArrayData[int32](pcr, count)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if int16Array == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
for _, elementArray := range int16Array.([][]int32) {
|
2024-08-20 11:42:55 +08:00
|
|
|
if err = common.CheckArrayCapacity(len(elementArray), maxCapacity); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2024-05-17 15:01:37 +08:00
|
|
|
data = append(data, &schemapb.ScalarField{
|
|
|
|
Data: &schemapb.ScalarField_IntData{
|
|
|
|
IntData: &schemapb.IntArray{
|
|
|
|
Data: elementArray,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
case schemapb.DataType_Int32:
|
|
|
|
int32Array, err := ReadIntegerOrFloatArrayData[int32](pcr, count)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if int32Array == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
for _, elementArray := range int32Array.([][]int32) {
|
2024-08-20 11:42:55 +08:00
|
|
|
if err = common.CheckArrayCapacity(len(elementArray), maxCapacity); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2024-05-17 15:01:37 +08:00
|
|
|
data = append(data, &schemapb.ScalarField{
|
|
|
|
Data: &schemapb.ScalarField_IntData{
|
|
|
|
IntData: &schemapb.IntArray{
|
|
|
|
Data: elementArray,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
case schemapb.DataType_Int64:
|
|
|
|
int64Array, err := ReadIntegerOrFloatArrayData[int64](pcr, count)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if int64Array == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
for _, elementArray := range int64Array.([][]int64) {
|
2024-08-20 11:42:55 +08:00
|
|
|
if err = common.CheckArrayCapacity(len(elementArray), maxCapacity); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2024-05-17 15:01:37 +08:00
|
|
|
data = append(data, &schemapb.ScalarField{
|
|
|
|
Data: &schemapb.ScalarField_LongData{
|
|
|
|
LongData: &schemapb.LongArray{
|
|
|
|
Data: elementArray,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
case schemapb.DataType_Float:
|
|
|
|
float32Array, err := ReadIntegerOrFloatArrayData[float32](pcr, count)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if float32Array == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
for _, elementArray := range float32Array.([][]float32) {
|
2024-08-20 11:42:55 +08:00
|
|
|
if err = common.CheckArrayCapacity(len(elementArray), maxCapacity); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2024-05-17 15:01:37 +08:00
|
|
|
data = append(data, &schemapb.ScalarField{
|
|
|
|
Data: &schemapb.ScalarField_FloatData{
|
|
|
|
FloatData: &schemapb.FloatArray{
|
|
|
|
Data: elementArray,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
case schemapb.DataType_Double:
|
|
|
|
float64Array, err := ReadIntegerOrFloatArrayData[float64](pcr, count)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if float64Array == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
for _, elementArray := range float64Array.([][]float64) {
|
2024-08-20 11:42:55 +08:00
|
|
|
if err = common.CheckArrayCapacity(len(elementArray), maxCapacity); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2024-05-17 15:01:37 +08:00
|
|
|
data = append(data, &schemapb.ScalarField{
|
|
|
|
Data: &schemapb.ScalarField_DoubleData{
|
|
|
|
DoubleData: &schemapb.DoubleArray{
|
|
|
|
Data: elementArray,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
case schemapb.DataType_VarChar, schemapb.DataType_String:
|
|
|
|
stringArray, err := ReadStringArrayData(pcr, count)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if stringArray == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
for _, elementArray := range stringArray.([][]string) {
|
2024-08-20 11:42:55 +08:00
|
|
|
if err = common.CheckArrayCapacity(len(elementArray), maxCapacity); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2024-05-17 15:01:37 +08:00
|
|
|
data = append(data, &schemapb.ScalarField{
|
|
|
|
Data: &schemapb.ScalarField_StringData{
|
|
|
|
StringData: &schemapb.StringArray{
|
|
|
|
Data: elementArray,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return nil, merr.WrapErrImportFailed(fmt.Sprintf("unsupported data type '%s' for array field '%s'",
|
|
|
|
elementType.String(), pcr.field.GetName()))
|
|
|
|
}
|
|
|
|
return data, nil
|
|
|
|
}
|
2024-08-20 16:50:55 +08:00
|
|
|
|
|
|
|
func ReadNullableArrayData(pcr *FieldReader, count int64) (any, []bool, error) {
|
|
|
|
data := make([]*schemapb.ScalarField, 0, count)
|
|
|
|
maxCapacity, err := parameterutil.GetMaxCapacity(pcr.field)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
elementType := pcr.field.GetElementType()
|
|
|
|
switch elementType {
|
|
|
|
case schemapb.DataType_Bool:
|
|
|
|
boolArray, validData, err := ReadNullableBoolArrayData(pcr, count)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
if boolArray == nil {
|
|
|
|
return nil, nil, nil
|
|
|
|
}
|
|
|
|
for _, elementArray := range boolArray.([][]bool) {
|
|
|
|
if err = common.CheckArrayCapacity(len(elementArray), maxCapacity); err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
data = append(data, &schemapb.ScalarField{
|
|
|
|
Data: &schemapb.ScalarField_BoolData{
|
|
|
|
BoolData: &schemapb.BoolArray{
|
|
|
|
Data: elementArray,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
return data, validData, nil
|
|
|
|
case schemapb.DataType_Int8:
|
|
|
|
int8Array, validData, err := ReadNullableIntegerOrFloatArrayData[int32](pcr, count)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
if int8Array == nil {
|
|
|
|
return nil, nil, nil
|
|
|
|
}
|
|
|
|
for _, elementArray := range int8Array.([][]int32) {
|
|
|
|
if err = common.CheckArrayCapacity(len(elementArray), maxCapacity); err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
data = append(data, &schemapb.ScalarField{
|
|
|
|
Data: &schemapb.ScalarField_IntData{
|
|
|
|
IntData: &schemapb.IntArray{
|
|
|
|
Data: elementArray,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
return data, validData, nil
|
|
|
|
case schemapb.DataType_Int16:
|
|
|
|
int16Array, validData, err := ReadNullableIntegerOrFloatArrayData[int32](pcr, count)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
if int16Array == nil {
|
|
|
|
return nil, nil, nil
|
|
|
|
}
|
|
|
|
for _, elementArray := range int16Array.([][]int32) {
|
|
|
|
if err = common.CheckArrayCapacity(len(elementArray), maxCapacity); err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
data = append(data, &schemapb.ScalarField{
|
|
|
|
Data: &schemapb.ScalarField_IntData{
|
|
|
|
IntData: &schemapb.IntArray{
|
|
|
|
Data: elementArray,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
return data, validData, nil
|
|
|
|
case schemapb.DataType_Int32:
|
|
|
|
int32Array, validData, err := ReadNullableIntegerOrFloatArrayData[int32](pcr, count)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
if int32Array == nil {
|
|
|
|
return nil, nil, nil
|
|
|
|
}
|
|
|
|
for _, elementArray := range int32Array.([][]int32) {
|
|
|
|
if err = common.CheckArrayCapacity(len(elementArray), maxCapacity); err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
data = append(data, &schemapb.ScalarField{
|
|
|
|
Data: &schemapb.ScalarField_IntData{
|
|
|
|
IntData: &schemapb.IntArray{
|
|
|
|
Data: elementArray,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
return data, validData, nil
|
|
|
|
case schemapb.DataType_Int64:
|
|
|
|
int64Array, validData, err := ReadNullableIntegerOrFloatArrayData[int64](pcr, count)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
if int64Array == nil {
|
|
|
|
return nil, nil, nil
|
|
|
|
}
|
|
|
|
for _, elementArray := range int64Array.([][]int64) {
|
|
|
|
if err = common.CheckArrayCapacity(len(elementArray), maxCapacity); err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
data = append(data, &schemapb.ScalarField{
|
|
|
|
Data: &schemapb.ScalarField_LongData{
|
|
|
|
LongData: &schemapb.LongArray{
|
|
|
|
Data: elementArray,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
return data, validData, nil
|
|
|
|
case schemapb.DataType_Float:
|
|
|
|
float32Array, validData, err := ReadNullableIntegerOrFloatArrayData[float32](pcr, count)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
if float32Array == nil {
|
|
|
|
return nil, nil, nil
|
|
|
|
}
|
|
|
|
for _, elementArray := range float32Array.([][]float32) {
|
|
|
|
if err = common.CheckArrayCapacity(len(elementArray), maxCapacity); err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
data = append(data, &schemapb.ScalarField{
|
|
|
|
Data: &schemapb.ScalarField_FloatData{
|
|
|
|
FloatData: &schemapb.FloatArray{
|
|
|
|
Data: elementArray,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
return data, validData, nil
|
|
|
|
case schemapb.DataType_Double:
|
|
|
|
float64Array, validData, err := ReadNullableIntegerOrFloatArrayData[float64](pcr, count)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
if float64Array == nil {
|
|
|
|
return nil, nil, nil
|
|
|
|
}
|
|
|
|
for _, elementArray := range float64Array.([][]float64) {
|
|
|
|
if err = common.CheckArrayCapacity(len(elementArray), maxCapacity); err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
data = append(data, &schemapb.ScalarField{
|
|
|
|
Data: &schemapb.ScalarField_DoubleData{
|
|
|
|
DoubleData: &schemapb.DoubleArray{
|
|
|
|
Data: elementArray,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
return data, validData, nil
|
|
|
|
case schemapb.DataType_VarChar, schemapb.DataType_String:
|
|
|
|
stringArray, validData, err := ReadNullableStringArrayData(pcr, count)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
if stringArray == nil {
|
|
|
|
return nil, nil, nil
|
|
|
|
}
|
|
|
|
for _, elementArray := range stringArray.([][]string) {
|
|
|
|
if err = common.CheckArrayCapacity(len(elementArray), maxCapacity); err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
data = append(data, &schemapb.ScalarField{
|
|
|
|
Data: &schemapb.ScalarField_StringData{
|
|
|
|
StringData: &schemapb.StringArray{
|
|
|
|
Data: elementArray,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
return data, validData, nil
|
|
|
|
default:
|
|
|
|
return nil, nil, merr.WrapErrImportFailed(fmt.Sprintf("unsupported data type '%s' for array field '%s'",
|
|
|
|
elementType.String(), pcr.field.GetName()))
|
|
|
|
}
|
|
|
|
}
|