ArrowBlockBuilder.java

/*
 * Licensed 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 com.facebook.plugin.arrow;

import com.facebook.presto.common.block.Block;
import com.facebook.presto.common.block.BlockBuilder;
import com.facebook.presto.common.block.DictionaryBlock;
import com.facebook.presto.common.type.ArrayType;
import com.facebook.presto.common.type.BigintType;
import com.facebook.presto.common.type.BooleanType;
import com.facebook.presto.common.type.CharType;
import com.facebook.presto.common.type.DateType;
import com.facebook.presto.common.type.DecimalType;
import com.facebook.presto.common.type.Decimals;
import com.facebook.presto.common.type.DoubleType;
import com.facebook.presto.common.type.IntegerType;
import com.facebook.presto.common.type.MapType;
import com.facebook.presto.common.type.RealType;
import com.facebook.presto.common.type.RowType;
import com.facebook.presto.common.type.SmallintType;
import com.facebook.presto.common.type.TimeType;
import com.facebook.presto.common.type.TimestampType;
import com.facebook.presto.common.type.TinyintType;
import com.facebook.presto.common.type.Type;
import com.facebook.presto.common.type.TypeManager;
import com.facebook.presto.common.type.VarbinaryType;
import com.facebook.presto.common.type.VarcharType;
import com.google.common.base.CharMatcher;
import io.airlift.slice.Slice;
import io.airlift.slice.Slices;
import org.apache.arrow.vector.BigIntVector;
import org.apache.arrow.vector.BitVector;
import org.apache.arrow.vector.DateDayVector;
import org.apache.arrow.vector.DateMilliVector;
import org.apache.arrow.vector.DecimalVector;
import org.apache.arrow.vector.FieldVector;
import org.apache.arrow.vector.Float4Vector;
import org.apache.arrow.vector.Float8Vector;
import org.apache.arrow.vector.IntVector;
import org.apache.arrow.vector.NullVector;
import org.apache.arrow.vector.SmallIntVector;
import org.apache.arrow.vector.TimeMicroVector;
import org.apache.arrow.vector.TimeMilliVector;
import org.apache.arrow.vector.TimeSecVector;
import org.apache.arrow.vector.TimeStampMicroVector;
import org.apache.arrow.vector.TimeStampMilliTZVector;
import org.apache.arrow.vector.TimeStampMilliVector;
import org.apache.arrow.vector.TimeStampSecVector;
import org.apache.arrow.vector.TinyIntVector;
import org.apache.arrow.vector.ValueVector;
import org.apache.arrow.vector.VarBinaryVector;
import org.apache.arrow.vector.VarCharVector;
import org.apache.arrow.vector.complex.ListVector;
import org.apache.arrow.vector.complex.MapVector;
import org.apache.arrow.vector.complex.StructVector;
import org.apache.arrow.vector.dictionary.Dictionary;
import org.apache.arrow.vector.dictionary.DictionaryProvider;
import org.apache.arrow.vector.types.pojo.ArrowType;
import org.apache.arrow.vector.types.pojo.Field;

import javax.inject.Inject;

import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.LocalTime;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

import static com.facebook.plugin.arrow.ArrowErrorCode.ARROW_FLIGHT_TYPE_ERROR;
import static com.facebook.presto.common.Utils.checkArgument;
import static com.facebook.presto.common.type.TypeSignature.parseTypeSignature;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static java.lang.String.format;
import static java.util.Objects.requireNonNull;

public class ArrowBlockBuilder
{
    private final TypeManager typeManager;

    @Inject
    public ArrowBlockBuilder(TypeManager typeManager)
    {
        this.typeManager = requireNonNull(typeManager, "typeManager is null");
    }
    public Block buildBlockFromFieldVector(FieldVector vector, Type type, DictionaryProvider dictionaryProvider)
    {
        // Use Arrow dictionary to create a DictionaryBlock
        if (dictionaryProvider != null && vector.getField().getDictionary() != null) {
            Dictionary dictionary = dictionaryProvider.lookup(vector.getField().getDictionary().getId());
            if (dictionary != null) {
                Type prestoType = getPrestoTypeFromArrowField(dictionary.getVector().getField());
                BlockBuilder dictionaryBuilder = prestoType.createBlockBuilder(null, vector.getValueCount());
                assignBlockFromValueVector(dictionary.getVector(), prestoType, dictionaryBuilder, 0, dictionary.getVector().getValueCount());
                return buildDictionaryBlock(vector, dictionaryBuilder.build());
            }
        }

        BlockBuilder builder = type.createBlockBuilder(null, vector.getValueCount());
        assignBlockFromValueVector(vector, type, builder, 0, vector.getValueCount());
        return builder.build();
    }

    protected Type getPrestoTypeFromArrowField(Field field)
    {
        switch (field.getType().getTypeID()) {
            case Int:
                ArrowType.Int intType = (ArrowType.Int) field.getType();
                return getPrestoTypeForArrowIntType(intType);
            case Binary:
            case LargeBinary:
            case FixedSizeBinary:
                return VarbinaryType.VARBINARY;
            case Date:
                return DateType.DATE;
            case Timestamp:
                return TimestampType.TIMESTAMP;
            case Utf8:
            case LargeUtf8:
                return VarcharType.VARCHAR;
            case FloatingPoint:
                ArrowType.FloatingPoint floatingPoint = (ArrowType.FloatingPoint) field.getType();
                return getPrestoTypeForArrowFloatingPointType(floatingPoint);
            case Decimal:
                ArrowType.Decimal decimalType = (ArrowType.Decimal) field.getType();
                return DecimalType.createDecimalType(decimalType.getPrecision(), decimalType.getScale());
            case Bool:
                return BooleanType.BOOLEAN;
            case Time:
                return TimeType.TIME;
            case List: {
                List<Field> children = field.getChildren();
                checkArgument(children.size() == 1, "Arrow List expected to have 1 child Field, got: " + children.size());
                return new ArrayType(getPrestoTypeFromArrowField(field.getChildren().get(0)));
            }
            case Map: {
                List<Field> children = field.getChildren();
                checkArgument(children.size() == 1, "Arrow Map expected to have 1 child Field for entries, got: " + children.size());
                Field entryField = children.get(0);
                checkArgument(entryField.getChildren().size() == 2, "Arrow Map entries expected to have 2 child Fields, got: " + children.size());
                Type keyType = getPrestoTypeFromArrowField(entryField.getChildren().get(0));
                Type valueType = getPrestoTypeFromArrowField(entryField.getChildren().get(1));
                return typeManager.getType(parseTypeSignature(format("map(%s,%s)", keyType.getTypeSignature(), valueType.getTypeSignature())));
            }
            case Struct: {
                List<RowType.Field> children = field.getChildren().stream().map(child -> new RowType.Field(Optional.of(child.getName()), getPrestoTypeFromArrowField(child))).collect(toImmutableList());
                return RowType.from(children);
            }
            default:
                throw new UnsupportedOperationException("The data type " + field.getType().getTypeID() + " is not supported.");
        }
    }

    private Type getPrestoTypeForArrowFloatingPointType(ArrowType.FloatingPoint floatingPoint)
    {
        switch (floatingPoint.getPrecision()) {
            case SINGLE:
                return RealType.REAL;
            case DOUBLE:
                return DoubleType.DOUBLE;
            default:
                throw new ArrowException(ARROW_FLIGHT_TYPE_ERROR, "Unexpected floating point precision: " + floatingPoint.getPrecision());
        }
    }

    private Type getPrestoTypeForArrowIntType(ArrowType.Int intType)
    {
        switch (intType.getBitWidth()) {
            case 64:
                return BigintType.BIGINT;
            case 32:
                return IntegerType.INTEGER;
            case 16:
                return SmallintType.SMALLINT;
            case 8:
                return TinyintType.TINYINT;
            default:
                throw new ArrowException(ARROW_FLIGHT_TYPE_ERROR, "Unexpected bit width: " + intType.getBitWidth());
        }
    }

    private DictionaryBlock buildDictionaryBlock(FieldVector fieldVector, Block dictionaryblock)
    {
        if (fieldVector instanceof IntVector) {
            // Get the Arrow indices vector
            IntVector indicesVector = (IntVector) fieldVector;
            int[] ids = new int[indicesVector.getValueCount()];
            for (int i = 0; i < indicesVector.getValueCount(); i++) {
                ids[i] = indicesVector.get(i);
            }
            return new DictionaryBlock(ids.length, dictionaryblock, ids);
        }
        else if (fieldVector instanceof SmallIntVector) {
            // Get the SmallInt indices vector
            SmallIntVector smallIntIndicesVector = (SmallIntVector) fieldVector;
            int[] ids = new int[smallIntIndicesVector.getValueCount()];
            for (int i = 0; i < smallIntIndicesVector.getValueCount(); i++) {
                ids[i] = smallIntIndicesVector.get(i);
            }
            return new DictionaryBlock(ids.length, dictionaryblock, ids);
        }
        else if (fieldVector instanceof TinyIntVector) {
            // Get the TinyInt indices vector
            TinyIntVector tinyIntIndicesVector = (TinyIntVector) fieldVector;
            int[] ids = new int[tinyIntIndicesVector.getValueCount()];
            for (int i = 0; i < tinyIntIndicesVector.getValueCount(); i++) {
                ids[i] = tinyIntIndicesVector.get(i);
            }
            return new DictionaryBlock(ids.length, dictionaryblock, ids);
        }
        else {
            // Handle the case where the FieldVector is of an unsupported type
            throw new IllegalArgumentException("Unsupported FieldVector type: " + fieldVector.getClass());
        }
    }

    private void assignBlockFromValueVector(ValueVector vector, Type type, BlockBuilder builder, int startIndex, int endIndex)
    {
        if (vector instanceof BitVector) {
            assignBlockFromBitVector((BitVector) vector, type, builder, startIndex, endIndex);
        }
        else if (vector instanceof TinyIntVector) {
            assignBlockFromTinyIntVector((TinyIntVector) vector, type, builder, startIndex, endIndex);
        }
        else if (vector instanceof IntVector) {
            assignBlockFromIntVector((IntVector) vector, type, builder, startIndex, endIndex);
        }
        else if (vector instanceof SmallIntVector) {
            assignBlockFromSmallIntVector((SmallIntVector) vector, type, builder, startIndex, endIndex);
        }
        else if (vector instanceof BigIntVector) {
            assignBlockFromBigIntVector((BigIntVector) vector, type, builder, startIndex, endIndex);
        }
        else if (vector instanceof DecimalVector) {
            assignBlockFromDecimalVector((DecimalVector) vector, type, builder, startIndex, endIndex);
        }
        else if (vector instanceof NullVector) {
            assignBlockFromNullVector((NullVector) vector, type, builder, startIndex, endIndex);
        }
        else if (vector instanceof TimeStampMicroVector) {
            assignBlockFromTimeStampMicroVector((TimeStampMicroVector) vector, type, builder, startIndex, endIndex);
        }
        else if (vector instanceof TimeStampMilliVector) {
            assignBlockFromTimeStampMilliVector((TimeStampMilliVector) vector, type, builder, startIndex, endIndex);
        }
        else if (vector instanceof Float4Vector) {
            assignBlockFromFloat4Vector((Float4Vector) vector, type, builder, startIndex, endIndex);
        }
        else if (vector instanceof Float8Vector) {
            assignBlockFromFloat8Vector((Float8Vector) vector, type, builder, startIndex, endIndex);
        }
        else if (vector instanceof VarCharVector) {
            if (type instanceof CharType) {
                assignCharTypeBlockFromVarcharVector((VarCharVector) vector, type, builder, startIndex, endIndex);
            }
            else if (type instanceof TimeType) {
                assignTimeTypeBlockFromVarcharVector((VarCharVector) vector, type, builder, startIndex, endIndex);
            }
            else {
                assignBlockFromVarCharVector((VarCharVector) vector, type, builder, startIndex, endIndex);
            }
        }
        else if (vector instanceof VarBinaryVector) {
            assignBlockFromVarBinaryVector((VarBinaryVector) vector, type, builder, startIndex, endIndex);
        }
        else if (vector instanceof DateDayVector) {
            assignBlockFromDateDayVector((DateDayVector) vector, type, builder, startIndex, endIndex);
        }
        else if (vector instanceof DateMilliVector) {
            assignBlockFromDateMilliVector((DateMilliVector) vector, type, builder, startIndex, endIndex);
        }
        else if (vector instanceof TimeMilliVector) {
            assignBlockFromTimeMilliVector((TimeMilliVector) vector, type, builder, startIndex, endIndex);
        }
        else if (vector instanceof TimeSecVector) {
            assignBlockFromTimeSecVector((TimeSecVector) vector, type, builder, startIndex, endIndex);
        }
        else if (vector instanceof TimeStampSecVector) {
            assignBlockFromTimeStampSecVector((TimeStampSecVector) vector, type, builder, startIndex, endIndex);
        }
        else if (vector instanceof TimeMicroVector) {
            assignBlockFromTimeMicroVector((TimeMicroVector) vector, type, builder, startIndex, endIndex);
        }
        else if (vector instanceof TimeStampMilliTZVector) {
            assignBlockFromTimeMilliTZVector((TimeStampMilliTZVector) vector, type, builder, startIndex, endIndex);
        }
        else if (vector instanceof MapVector) {
            // NOTE: MapVector is also instanceof ListVector, so check for Map first
            assignBlockFromMapVector((MapVector) vector, type, builder, startIndex, endIndex);
        }
        else if (vector instanceof ListVector) {
            assignBlockFromListVector((ListVector) vector, type, builder, startIndex, endIndex);
        }
        else if (vector instanceof StructVector) {
            assignBlockFromStructVector((StructVector) vector, type, builder, startIndex, endIndex);
        }
        else {
            throw new UnsupportedOperationException("Unsupported vector type: " + vector.getClass());
        }
    }

    public void assignBlockFromBitVector(BitVector vector, Type type, BlockBuilder builder, int startIndex, int endIndex)
    {
        for (int i = startIndex; i < endIndex; i++) {
            if (vector.isNull(i)) {
                builder.appendNull();
            }
            else {
                type.writeBoolean(builder, vector.get(i) == 1);
            }
        }
    }

    public void assignBlockFromIntVector(IntVector vector, Type type, BlockBuilder builder, int startIndex, int endIndex)
    {
        for (int i = startIndex; i < endIndex; i++) {
            if (vector.isNull(i)) {
                builder.appendNull();
            }
            else {
                type.writeLong(builder, vector.get(i));
            }
        }
    }

    public void assignBlockFromSmallIntVector(SmallIntVector vector, Type type, BlockBuilder builder, int startIndex, int endIndex)
    {
        for (int i = startIndex; i < endIndex; i++) {
            if (vector.isNull(i)) {
                builder.appendNull();
            }
            else {
                type.writeLong(builder, vector.get(i));
            }
        }
    }

    public void assignBlockFromTinyIntVector(TinyIntVector vector, Type type, BlockBuilder builder, int startIndex, int endIndex)
    {
        for (int i = startIndex; i < endIndex; i++) {
            if (vector.isNull(i)) {
                builder.appendNull();
            }
            else {
                type.writeLong(builder, vector.get(i));
            }
        }
    }

    public void assignBlockFromBigIntVector(BigIntVector vector, Type type, BlockBuilder builder, int startIndex, int endIndex)
    {
        for (int i = startIndex; i < endIndex; i++) {
            if (vector.isNull(i)) {
                builder.appendNull();
            }
            else {
                type.writeLong(builder, vector.get(i));
            }
        }
    }

    public void assignBlockFromDecimalVector(DecimalVector vector, Type type, BlockBuilder builder, int startIndex, int endIndex)
    {
        if (!(type instanceof DecimalType)) {
            throw new IllegalArgumentException("Type must be a DecimalType for DecimalVector");
        }

        DecimalType decimalType = (DecimalType) type;

        for (int i = startIndex; i < endIndex; i++) {
            if (vector.isNull(i)) {
                builder.appendNull();
            }
            else {
                BigDecimal decimal = vector.getObject(i); // Get the BigDecimal value
                if (decimalType.isShort()) {
                    builder.writeLong(decimal.unscaledValue().longValue());
                }
                else {
                    Slice slice = Decimals.encodeScaledValue(decimal);
                    decimalType.writeSlice(builder, slice, 0, slice.length());
                }
            }
        }
    }

    public void assignBlockFromNullVector(NullVector vector, Type type, BlockBuilder builder, int startIndex, int endIndex)
    {
        for (int i = startIndex; i < endIndex; i++) {
            builder.appendNull();
        }
    }

    public void assignBlockFromTimeStampMicroVector(TimeStampMicroVector vector, Type type, BlockBuilder builder, int startIndex, int endIndex)
    {
        if (!(type instanceof TimestampType)) {
            throw new IllegalArgumentException("Expected TimestampType but got " + type.getClass().getName());
        }

        for (int i = startIndex; i < endIndex; i++) {
            if (vector.isNull(i)) {
                builder.appendNull();
            }
            else {
                long micros = vector.get(i);
                long millis = TimeUnit.MICROSECONDS.toMillis(micros);
                type.writeLong(builder, millis);
            }
        }
    }

    public void assignBlockFromTimeStampMilliVector(TimeStampMilliVector vector, Type type, BlockBuilder builder, int startIndex, int endIndex)
    {
        if (!(type instanceof TimestampType)) {
            throw new IllegalArgumentException("Expected TimestampType but got " + type.getClass().getName());
        }

        for (int i = startIndex; i < endIndex; i++) {
            if (vector.isNull(i)) {
                builder.appendNull();
            }
            else {
                long millis = vector.get(i);
                type.writeLong(builder, millis);
            }
        }
    }

    public void assignBlockFromFloat8Vector(Float8Vector vector, Type type, BlockBuilder builder, int startIndex, int endIndex)
    {
        for (int i = startIndex; i < endIndex; i++) {
            if (vector.isNull(i)) {
                builder.appendNull();
            }
            else {
                type.writeDouble(builder, vector.get(i));
            }
        }
    }

    public void assignBlockFromFloat4Vector(Float4Vector vector, Type type, BlockBuilder builder, int startIndex, int endIndex)
    {
        for (int i = startIndex; i < endIndex; i++) {
            if (vector.isNull(i)) {
                builder.appendNull();
            }
            else {
                int intBits = Float.floatToIntBits(vector.get(i));
                type.writeLong(builder, intBits);
            }
        }
    }

    public void assignBlockFromVarBinaryVector(VarBinaryVector vector, Type type, BlockBuilder builder, int startIndex, int endIndex)
    {
        for (int i = startIndex; i < endIndex; i++) {
            if (vector.isNull(i)) {
                builder.appendNull();
            }
            else {
                byte[] value = vector.get(i);
                type.writeSlice(builder, Slices.wrappedBuffer(value));
            }
        }
    }

    public void assignBlockFromVarCharVector(VarCharVector vector, Type type, BlockBuilder builder, int startIndex, int endIndex)
    {
        if (!(type instanceof VarcharType)) {
            throw new IllegalArgumentException("Expected VarcharType but got " + type.getClass().getName());
        }

        for (int i = startIndex; i < endIndex; i++) {
            if (vector.isNull(i)) {
                builder.appendNull();
            }
            else {
                // Directly create a Slice from the raw byte array
                byte[] rawBytes = vector.get(i);
                Slice slice = Slices.wrappedBuffer(rawBytes);
                // Write the Slice directly to the builder
                type.writeSlice(builder, slice);
            }
        }
    }

    public void assignBlockFromDateDayVector(DateDayVector vector, Type type, BlockBuilder builder, int startIndex, int endIndex)
    {
        if (!(type instanceof DateType)) {
            throw new IllegalArgumentException("Expected DateType but got " + type.getClass().getName());
        }

        for (int i = startIndex; i < endIndex; i++) {
            if (vector.isNull(i)) {
                builder.appendNull();
            }
            else {
                type.writeLong(builder, vector.get(i));
            }
        }
    }

    public void assignBlockFromDateMilliVector(DateMilliVector vector, Type type, BlockBuilder builder, int startIndex, int endIndex)
    {
        if (!(type instanceof DateType)) {
            throw new IllegalArgumentException("Expected DateType but got " + type.getClass().getName());
        }

        for (int i = startIndex; i < endIndex; i++) {
            if (vector.isNull(i)) {
                builder.appendNull();
            }
            else {
                DateType dateType = (DateType) type;
                long days = TimeUnit.MILLISECONDS.toDays(vector.get(i));
                dateType.writeLong(builder, days);
            }
        }
    }

    public void assignBlockFromTimeSecVector(TimeSecVector vector, Type type, BlockBuilder builder, int startIndex, int endIndex)
    {
        if (!(type instanceof TimeType)) {
            throw new IllegalArgumentException("Type must be a TimeType for TimeSecVector");
        }

        for (int i = startIndex; i < endIndex; i++) {
            if (vector.isNull(i)) {
                builder.appendNull();
            }
            else {
                int value = vector.get(i);
                long millis = TimeUnit.SECONDS.toMillis(value);
                type.writeLong(builder, millis);
            }
        }
    }

    public void assignBlockFromTimeMilliVector(TimeMilliVector vector, Type type, BlockBuilder builder, int startIndex, int endIndex)
    {
        if (!(type instanceof TimeType)) {
            throw new IllegalArgumentException("Type must be a TimeType for TimeSecVector");
        }

        for (int i = startIndex; i < endIndex; i++) {
            if (vector.isNull(i)) {
                builder.appendNull();
            }
            else {
                long millis = vector.get(i);
                type.writeLong(builder, millis);
            }
        }
    }

    public void assignBlockFromTimeMicroVector(TimeMicroVector vector, Type type, BlockBuilder builder, int startIndex, int endIndex)
    {
        if (!(type instanceof TimeType)) {
            throw new IllegalArgumentException("Type must be a TimeType for TimemicroVector");
        }
        for (int i = startIndex; i < endIndex; i++) {
            if (vector.isNull(i)) {
                builder.appendNull();
            }
            else {
                long value = vector.get(i);
                long micro = TimeUnit.MICROSECONDS.toMillis(value);
                type.writeLong(builder, micro);
            }
        }
    }

    public void assignBlockFromTimeStampSecVector(TimeStampSecVector vector, Type type, BlockBuilder builder, int startIndex, int endIndex)
    {
        if (!(type instanceof TimestampType)) {
            throw new IllegalArgumentException("Type must be a TimestampType for TimeStampSecVector");
        }

        for (int i = startIndex; i < endIndex; i++) {
            if (vector.isNull(i)) {
                builder.appendNull();
            }
            else {
                long value = vector.get(i);
                long millis = TimeUnit.SECONDS.toMillis(value);
                type.writeLong(builder, millis);
            }
        }
    }

    public void assignCharTypeBlockFromVarcharVector(VarCharVector vector, Type type, BlockBuilder builder, int startIndex, int endIndex)
    {
        for (int i = startIndex; i < endIndex; i++) {
            if (vector.isNull(i)) {
                builder.appendNull();
            }
            else {
                String value = new String(vector.get(i), StandardCharsets.UTF_8);
                type.writeSlice(builder, Slices.utf8Slice(CharMatcher.is(' ').trimTrailingFrom(value)));
            }
        }
    }

    public void assignTimeTypeBlockFromVarcharVector(VarCharVector vector, Type type, BlockBuilder builder, int startIndex, int endIndex)
    {
        for (int i = startIndex; i < endIndex; i++) {
            if (vector.isNull(i)) {
                builder.appendNull();
            }
            else {
                String timeString = new String(vector.get(i), StandardCharsets.UTF_8);
                LocalTime time = LocalTime.parse(timeString);
                long millis = Duration.between(LocalTime.MIN, time).toMillis();
                type.writeLong(builder, millis);
            }
        }
    }

    public void assignBlockFromTimeMilliTZVector(TimeStampMilliTZVector vector, Type type, BlockBuilder builder, int startIndex, int endIndex)
    {
        if (!(type instanceof TimestampType)) {
            throw new IllegalArgumentException("Type must be a TimestampType for TimeStampMilliTZVector");
        }

        for (int i = startIndex; i < endIndex; i++) {
            if (vector.isNull(i)) {
                builder.appendNull();
            }
            else {
                long millis = vector.get(i);
                type.writeLong(builder, millis);
            }
        }
    }

    public void assignBlockFromListVector(ListVector vector, Type type, BlockBuilder builder, int startIndex, int endIndex)
    {
        if (!(type instanceof ArrayType)) {
            throw new IllegalArgumentException("Type must be an ArrayType for ListVector");
        }

        ArrayType arrayType = (ArrayType) type;
        Type elementType = arrayType.getElementType();

        for (int i = startIndex; i < endIndex; i++) {
            if (vector.isNull(i)) {
                builder.appendNull();
            }
            else {
                BlockBuilder elementBuilder = builder.beginBlockEntry();
                assignBlockFromValueVector(
                        vector.getDataVector(), elementType, elementBuilder, vector.getElementStartIndex(i), vector.getElementEndIndex(i));
                builder.closeEntry();
            }
        }
    }

    public void assignBlockFromMapVector(MapVector vector, Type type, BlockBuilder builder, int startIndex, int endIndex)
    {
        if (!(type instanceof MapType)) {
            throw new IllegalArgumentException("Type must be a MapType for MapVector");
        }

        MapType mapType = (MapType) type;
        StructVector entryVector = (StructVector) vector.getDataVector();
        ValueVector keyVector = entryVector.getChildByOrdinal(0);
        ValueVector valueVector = entryVector.getChildByOrdinal(1);

        for (int i = startIndex; i < endIndex; i++) {
            if (vector.isNull(i)) {
                builder.appendNull();
            }
            else {
                BlockBuilder entryBuilder = builder.beginBlockEntry();
                int entryStart = vector.getElementStartIndex(i);
                int entryEnd = vector.getElementEndIndex(i);
                for (int entryIndex = entryStart; entryIndex < entryEnd; entryIndex++) {
                    assignBlockFromValueVector(keyVector, mapType.getKeyType(), entryBuilder, entryIndex, entryIndex + 1);
                    assignBlockFromValueVector(valueVector, mapType.getValueType(), entryBuilder, entryIndex, entryIndex + 1);
                }
                builder.closeEntry();
            }
        }
    }

    public void assignBlockFromStructVector(StructVector vector, Type type, BlockBuilder builder, int startIndex, int endIndex)
    {
        if (!(type instanceof RowType)) {
            throw new IllegalArgumentException("Type must be a RowType for StructVector");
        }

        RowType rowType = (RowType) type;

        for (int i = startIndex; i < endIndex; i++) {
            if (vector.isNull(i)) {
                builder.appendNull();
            }
            else {
                BlockBuilder childBuilder = builder.beginBlockEntry();
                List<Type> childTypes = rowType.getTypeParameters();
                for (int childIndex = 0; childIndex < childTypes.size(); childIndex++) {
                    Type childType = childTypes.get(childIndex);
                    ValueVector childVector = vector.getChildByOrdinal(childIndex);
                    assignBlockFromValueVector(childVector, childType, childBuilder, i, i + 1);
                }
                builder.closeEntry();
            }
        }
    }
}