diff --git a/core/trino-main/src/main/java/io/trino/operator/output/TypedPositionsAppender.java b/core/trino-main/src/main/java/io/trino/operator/output/TypedPositionsAppender.java
index 2fcc0fda3b52..eb26ff3241b5 100644
--- a/core/trino-main/src/main/java/io/trino/operator/output/TypedPositionsAppender.java
+++ b/core/trino-main/src/main/java/io/trino/operator/output/TypedPositionsAppender.java
@@ -20,43 +20,35 @@
import it.unimi.dsi.fastutil.ints.IntArrayList;
import static io.airlift.slice.SizeOf.instanceSize;
-import static java.util.Objects.requireNonNull;
class TypedPositionsAppender
implements PositionsAppender
{
private static final int INSTANCE_SIZE = instanceSize(TypedPositionsAppender.class);
- private final Type type;
private BlockBuilder blockBuilder;
TypedPositionsAppender(Type type, int expectedPositions)
{
- this.type = requireNonNull(type, "type is null");
this.blockBuilder = type.createBlockBuilder(null, expectedPositions);
}
@Override
- public void append(IntArrayList positions, ValueBlock source)
+ public void append(IntArrayList positions, ValueBlock block)
{
- int[] positionArray = positions.elements();
- for (int i = 0; i < positions.size(); i++) {
- type.appendTo(source, positionArray[i], blockBuilder);
- }
+ blockBuilder.appendPositions(block, positions.elements(), 0, positions.size());
}
@Override
- public void appendRle(ValueBlock block, int rlePositionCount)
+ public void appendRle(ValueBlock block, int count)
{
- for (int i = 0; i < rlePositionCount; i++) {
- type.appendTo(block, 0, blockBuilder);
- }
+ blockBuilder.appendRepeated(block, 0, count);
}
@Override
- public void append(int position, ValueBlock source)
+ public void append(int position, ValueBlock block)
{
- type.appendTo(source, position, blockBuilder);
+ blockBuilder.append(block, position);
}
@Override
diff --git a/core/trino-spi/pom.xml b/core/trino-spi/pom.xml
index 36a598fa6590..eb497d8c8ffc 100644
--- a/core/trino-spi/pom.xml
+++ b/core/trino-spi/pom.xml
@@ -220,6 +220,33 @@
java.method.removed
method <E extends java.lang.Throwable> void io.trino.spi.block.VariableWidthBlockBuilder::buildEntry(io.trino.spi.block.VariableWidthBlockBuilder.VariableWidthEntryBuilder<E>) throws E
+ -
+ true
+
java.method.addedToInterface
+ method void io.trino.spi.block.BlockBuilder::append(io.trino.spi.block.ValueBlock, int)
+
+ -
+
java.method.addedToInterface
+ method void io.trino.spi.block.BlockBuilder::appendPositions(io.trino.spi.block.ValueBlock, int[], int, int)
+
+ -
+
java.method.addedToInterface
+ method void io.trino.spi.block.BlockBuilder::appendRange(io.trino.spi.block.ValueBlock, int, int)
+
+ -
+
java.method.addedToInterface
+ method void io.trino.spi.block.BlockBuilder::appendRepeated(io.trino.spi.block.ValueBlock, int, int)
+
+ -
+
java.method.returnTypeChangedCovariantly
+ method io.trino.spi.block.BlockBuilder io.trino.spi.block.ShortArrayBlockBuilder::appendNull()
+ method io.trino.spi.block.ShortArrayBlockBuilder io.trino.spi.block.ShortArrayBlockBuilder::appendNull()
+
+ -
+
java.method.returnTypeChangedCovariantly
+ method io.trino.spi.block.BlockBuilder io.trino.spi.block.ShortArrayBlockBuilder::writeShort(short)
+ method io.trino.spi.block.ShortArrayBlockBuilder io.trino.spi.block.ShortArrayBlockBuilder::writeShort(short)
+
diff --git a/core/trino-spi/src/main/java/io/trino/spi/block/ArrayBlock.java b/core/trino-spi/src/main/java/io/trino/spi/block/ArrayBlock.java
index 9aad1d9da67e..ee7ab4f5bc85 100644
--- a/core/trino-spi/src/main/java/io/trino/spi/block/ArrayBlock.java
+++ b/core/trino-spi/src/main/java/io/trino/spi/block/ArrayBlock.java
@@ -177,6 +177,11 @@ int[] getOffsets()
return offsets;
}
+ boolean[] getRawValueIsNull()
+ {
+ return valueIsNull;
+ }
+
int getOffsetBase()
{
return arrayOffset;
diff --git a/core/trino-spi/src/main/java/io/trino/spi/block/ArrayBlockBuilder.java b/core/trino-spi/src/main/java/io/trino/spi/block/ArrayBlockBuilder.java
index df28648003e7..4e5baa0c5bbd 100644
--- a/core/trino-spi/src/main/java/io/trino/spi/block/ArrayBlockBuilder.java
+++ b/core/trino-spi/src/main/java/io/trino/spi/block/ArrayBlockBuilder.java
@@ -21,6 +21,8 @@
import static io.airlift.slice.SizeOf.instanceSize;
import static io.airlift.slice.SizeOf.sizeOf;
import static io.trino.spi.block.ArrayBlock.createArrayBlockInternal;
+import static io.trino.spi.block.BlockUtil.appendRawBlockRange;
+import static io.trino.spi.block.BlockUtil.calculateNewArraySize;
import static java.lang.Math.max;
import static java.util.Objects.requireNonNull;
@@ -28,6 +30,7 @@ public class ArrayBlockBuilder
implements BlockBuilder
{
private static final int INSTANCE_SIZE = instanceSize(ArrayBlockBuilder.class);
+ private static final int SIZE_IN_BYTES_PER_POSITION = Integer.BYTES + Byte.BYTES;
private int positionCount;
@@ -39,7 +42,7 @@ public class ArrayBlockBuilder
private int[] offsets = new int[1];
private boolean[] valueIsNull = new boolean[0];
private boolean hasNullValue;
- private boolean hasNonNullRow;
+ private boolean hasNonNullValue;
private final BlockBuilder values;
private boolean currentEntryOpened;
@@ -82,7 +85,7 @@ private ArrayBlockBuilder(@Nullable BlockBuilderStatus blockBuilderStatus, Block
this.values = requireNonNull(values, "values is null");
this.initialEntryCount = max(expectedEntries, 1);
- updateDataSize();
+ updateRetainedSize();
}
@Override
@@ -94,7 +97,7 @@ public int getPositionCount()
@Override
public long getSizeInBytes()
{
- return values.getSizeInBytes() + ((Integer.BYTES + Byte.BYTES) * (long) positionCount);
+ return values.getSizeInBytes() + (SIZE_IN_BYTES_PER_POSITION * (long) positionCount);
}
@Override
@@ -116,6 +119,97 @@ public void buildEntry(ArrayValueBuilder builder)
currentEntryOpened = false;
}
+ @Override
+ public void append(ValueBlock block, int position)
+ {
+ if (currentEntryOpened) {
+ throw new IllegalStateException("Current entry must be closed before a null can be written");
+ }
+
+ ArrayBlock arrayBlock = (ArrayBlock) block;
+ if (block.isNull(position)) {
+ entryAdded(true);
+ return;
+ }
+
+ int offsetBase = arrayBlock.getOffsetBase();
+ int[] offsets = arrayBlock.getOffsets();
+ int startOffset = offsets[offsetBase + position];
+ int length = offsets[offsetBase + position + 1] - startOffset;
+
+ appendRawBlockRange(arrayBlock.getRawElementBlock(), startOffset, length, values);
+ entryAdded(false);
+ }
+
+ @Override
+ public void appendRepeated(ValueBlock block, int position, int count)
+ {
+ if (currentEntryOpened) {
+ throw new IllegalStateException("Current entry must be closed before a null can be written");
+ }
+ for (int i = 0; i < count; i++) {
+ append(block, position);
+ }
+ }
+
+ @Override
+ public void appendRange(ValueBlock block, int offset, int length)
+ {
+ if (currentEntryOpened) {
+ throw new IllegalStateException("Current entry must be closed before a null can be written");
+ }
+
+ ensureCapacity(positionCount + length);
+
+ ArrayBlock arrayBlock = (ArrayBlock) block;
+
+ int rawOffsetBase = arrayBlock.getOffsetBase();
+ int[] rawOffsets = arrayBlock.getOffsets();
+ int startOffset = rawOffsets[rawOffsetBase + offset];
+ int endOffset = rawOffsets[rawOffsetBase + offset + length];
+
+ appendRawBlockRange(arrayBlock.getRawElementBlock(), startOffset, endOffset - startOffset, values);
+
+ // update offsets for copied data
+ for (int i = 0; i < length; i++) {
+ int entrySize = rawOffsets[rawOffsetBase + offset + i + 1] - rawOffsets[rawOffsetBase + offset + i];
+ offsets[positionCount + i + 1] = offsets[positionCount + i] + entrySize;
+ }
+
+ boolean[] rawValueIsNull = arrayBlock.getRawValueIsNull();
+ if (rawValueIsNull != null) {
+ for (int i = 0; i < length; i++) {
+ if (rawValueIsNull[rawOffsetBase + offset + i]) {
+ valueIsNull[positionCount + i] = true;
+ hasNullValue = true;
+ }
+ else {
+ hasNonNullValue = true;
+ }
+ }
+ }
+ else {
+ hasNonNullValue = true;
+ }
+
+ positionCount += length;
+
+ if (blockBuilderStatus != null) {
+ blockBuilderStatus.addBytes(length * SIZE_IN_BYTES_PER_POSITION);
+ }
+ }
+
+ @Override
+ public void appendPositions(ValueBlock block, int[] positions, int offset, int length)
+ {
+ if (currentEntryOpened) {
+ throw new IllegalStateException("Current entry must be closed before a null can be written");
+ }
+ for (int i = 0; i < length; i++) {
+ append(block, positions[offset + i]);
+ }
+ }
+
@Override
public BlockBuilder appendNull()
{
@@ -129,37 +223,41 @@ public BlockBuilder appendNull()
private void entryAdded(boolean isNull)
{
- if (valueIsNull.length <= positionCount) {
- growCapacity();
- }
+ ensureCapacity(positionCount + 1);
+
offsets[positionCount + 1] = values.getPositionCount();
valueIsNull[positionCount] = isNull;
hasNullValue |= isNull;
- hasNonNullRow |= !isNull;
+ hasNonNullValue |= !isNull;
positionCount++;
if (blockBuilderStatus != null) {
- blockBuilderStatus.addBytes(Integer.BYTES + Byte.BYTES);
+ blockBuilderStatus.addBytes(SIZE_IN_BYTES_PER_POSITION);
}
}
- private void growCapacity()
+ private void ensureCapacity(int capacity)
{
+ if (valueIsNull.length >= capacity) {
+ return;
+ }
+
int newSize;
if (initialized) {
- newSize = BlockUtil.calculateNewArraySize(valueIsNull.length);
+ newSize = calculateNewArraySize(capacity);
}
else {
newSize = initialEntryCount;
initialized = true;
}
+ newSize = max(newSize, capacity);
valueIsNull = Arrays.copyOf(valueIsNull, newSize);
offsets = Arrays.copyOf(offsets, newSize + 1);
- updateDataSize();
+ updateRetainedSize();
}
- private void updateDataSize()
+ private void updateRetainedSize()
{
retainedSizeInBytes = INSTANCE_SIZE + sizeOf(valueIsNull) + sizeOf(offsets);
if (blockBuilderStatus != null) {
@@ -173,7 +271,7 @@ public Block build()
if (currentEntryOpened) {
throw new IllegalStateException("Current entry must be closed before the block can be built");
}
- if (!hasNonNullRow) {
+ if (!hasNonNullValue) {
return nullRle(positionCount);
}
return buildValueBlock();
@@ -197,10 +295,9 @@ public BlockBuilder newBlockBuilderLike(int expectedEntries, BlockBuilderStatus
@Override
public String toString()
{
- StringBuilder sb = new StringBuilder("ArrayBlockBuilder{");
- sb.append("positionCount=").append(getPositionCount());
- sb.append('}');
- return sb.toString();
+ return "ArrayBlockBuilder{" +
+ "positionCount=" + getPositionCount() +
+ '}';
}
private Block nullRle(int positionCount)
diff --git a/core/trino-spi/src/main/java/io/trino/spi/block/BlockBuilder.java b/core/trino-spi/src/main/java/io/trino/spi/block/BlockBuilder.java
index 7d458991497e..97df9b75b235 100644
--- a/core/trino-spi/src/main/java/io/trino/spi/block/BlockBuilder.java
+++ b/core/trino-spi/src/main/java/io/trino/spi/block/BlockBuilder.java
@@ -36,6 +36,26 @@ public interface BlockBuilder
*/
long getRetainedSizeInBytes();
+ /**
+ * Append the specified value.
+ */
+ void append(ValueBlock block, int position);
+
+ /**
+ * Append the specified value multiple times.
+ */
+ void appendRepeated(ValueBlock block, int position, int count);
+
+ /**
+ * Append the values in the specified range.
+ */
+ void appendRange(ValueBlock block, int offset, int length);
+
+ /**
+ * Append the values at the specified positions.
+ */
+ void appendPositions(ValueBlock block, int[] positions, int offset, int length);
+
/**
* Appends a null value to the block.
*/
diff --git a/core/trino-spi/src/main/java/io/trino/spi/block/BlockUtil.java b/core/trino-spi/src/main/java/io/trino/spi/block/BlockUtil.java
index 0f074c5f4310..06af44be190e 100644
--- a/core/trino-spi/src/main/java/io/trino/spi/block/BlockUtil.java
+++ b/core/trino-spi/src/main/java/io/trino/spi/block/BlockUtil.java
@@ -357,4 +357,21 @@ else if (buffer.length < capacity) {
return buffer;
}
+
+ static void appendRawBlockRange(Block rawBlock, int offset, int length, BlockBuilder blockBuilder)
+ {
+ rawBlock = rawBlock.getLoadedBlock();
+ if (rawBlock instanceof RunLengthEncodedBlock rleBlock) {
+ blockBuilder.appendRepeated(rleBlock.getValue(), 0, length);
+ }
+ else if (rawBlock instanceof DictionaryBlock dictionaryBlock) {
+ blockBuilder.appendPositions(dictionaryBlock.getDictionary(), dictionaryBlock.getRawIds(), offset, length);
+ }
+ else if (rawBlock instanceof ValueBlock valueBlock) {
+ blockBuilder.appendRange(valueBlock, offset, length);
+ }
+ else {
+ throw new IllegalArgumentException("Unsupported block type " + rawBlock.getClass().getSimpleName());
+ }
+ }
}
diff --git a/core/trino-spi/src/main/java/io/trino/spi/block/ByteArrayBlock.java b/core/trino-spi/src/main/java/io/trino/spi/block/ByteArrayBlock.java
index 744c6753445c..e5fa09d77611 100644
--- a/core/trino-spi/src/main/java/io/trino/spi/block/ByteArrayBlock.java
+++ b/core/trino-spi/src/main/java/io/trino/spi/block/ByteArrayBlock.java
@@ -242,4 +242,19 @@ Slice getValuesSlice()
{
return Slices.wrappedBuffer(values, arrayOffset, positionCount);
}
+
+ int getRawValuesOffset()
+ {
+ return arrayOffset;
+ }
+
+ boolean[] getRawValueIsNull()
+ {
+ return valueIsNull;
+ }
+
+ byte[] getRawValues()
+ {
+ return values;
+ }
}
diff --git a/core/trino-spi/src/main/java/io/trino/spi/block/ByteArrayBlockBuilder.java b/core/trino-spi/src/main/java/io/trino/spi/block/ByteArrayBlockBuilder.java
index 559ead304ead..ef9971ca5253 100644
--- a/core/trino-spi/src/main/java/io/trino/spi/block/ByteArrayBlockBuilder.java
+++ b/core/trino-spi/src/main/java/io/trino/spi/block/ByteArrayBlockBuilder.java
@@ -19,6 +19,7 @@
import static io.airlift.slice.SizeOf.instanceSize;
import static io.airlift.slice.SizeOf.sizeOf;
+import static io.trino.spi.block.BlockUtil.calculateNewArraySize;
import static java.lang.Math.max;
public class ByteArrayBlockBuilder
@@ -47,14 +48,12 @@ public ByteArrayBlockBuilder(@Nullable BlockBuilderStatus blockBuilderStatus, in
this.blockBuilderStatus = blockBuilderStatus;
this.initialEntryCount = max(expectedEntries, 1);
- updateDataSize();
+ updateRetainedSize();
}
public BlockBuilder writeByte(byte value)
{
- if (values.length <= positionCount) {
- growCapacity();
- }
+ ensureCapacity(positionCount + 1);
values[positionCount] = value;
@@ -67,12 +66,147 @@ public BlockBuilder writeByte(byte value)
}
@Override
- public BlockBuilder appendNull()
+ public void append(ValueBlock block, int position)
+ {
+ ensureCapacity(positionCount + 1);
+
+ ByteArrayBlock byteArrayBlock = (ByteArrayBlock) block;
+ if (byteArrayBlock.isNull(position)) {
+ valueIsNull[positionCount] = true;
+ hasNullValue = true;
+ }
+ else {
+ values[positionCount] = byteArrayBlock.getByte(position);
+ hasNonNullValue = true;
+ }
+ positionCount++;
+
+ if (blockBuilderStatus != null) {
+ blockBuilderStatus.addBytes(ByteArrayBlock.SIZE_IN_BYTES_PER_POSITION);
+ }
+ }
+
+ @Override
+ public void appendRepeated(ValueBlock block, int position, int count)
+ {
+ if (count == 0) {
+ return;
+ }
+ if (count == 1) {
+ append(block, position);
+ return;
+ }
+
+ ensureCapacity(positionCount + count);
+
+ ByteArrayBlock byteArrayBlock = (ByteArrayBlock) block;
+
+ if (byteArrayBlock.isNull(position)) {
+ Arrays.fill(valueIsNull, positionCount, positionCount + count, true);
+ hasNullValue = true;
+ }
+ else {
+ byte value = byteArrayBlock.getByte(position);
+ Arrays.fill(values, positionCount, positionCount + count, value);
+ hasNonNullValue = true;
+ }
+ positionCount += count;
+
+ if (blockBuilderStatus != null) {
+ blockBuilderStatus.addBytes(count * ByteArrayBlock.SIZE_IN_BYTES_PER_POSITION);
+ }
+ }
+
+ @Override
+ public void appendRange(ValueBlock block, int offset, int length)
+ {
+ if (length == 0) {
+ return;
+ }
+ if (length == 1) {
+ append(block, offset);
+ return;
+ }
+
+ ensureCapacity(positionCount + length);
+
+ ByteArrayBlock byteArrayBlock = (ByteArrayBlock) block;
+ int rawOffset = byteArrayBlock.getRawValuesOffset();
+
+ byte[] rawValues = byteArrayBlock.getRawValues();
+ System.arraycopy(rawValues, rawOffset + offset, values, positionCount, length);
+
+ boolean[] rawValueIsNull = byteArrayBlock.getRawValueIsNull();
+ if (rawValueIsNull != null) {
+ for (int i = 0; i < length; i++) {
+ if (rawValueIsNull[rawOffset + offset + i]) {
+ valueIsNull[positionCount + i] = true;
+ hasNullValue = true;
+ }
+ else {
+ hasNonNullValue = true;
+ }
+ }
+ }
+ else {
+ hasNonNullValue = true;
+ }
+ positionCount += length;
+
+ if (blockBuilderStatus != null) {
+ blockBuilderStatus.addBytes(length * ByteArrayBlock.SIZE_IN_BYTES_PER_POSITION);
+ }
+ }
+
+ @Override
+ public void appendPositions(ValueBlock block, int[] positions, int offset, int length)
{
- if (values.length <= positionCount) {
- growCapacity();
+ if (length == 0) {
+ return;
+ }
+ if (length == 1) {
+ append(block, positions[offset]);
+ return;
}
+ ensureCapacity(positionCount + length);
+
+ ByteArrayBlock byteArrayBlock = (ByteArrayBlock) block;
+ int rawOffset = byteArrayBlock.getRawValuesOffset();
+ byte[] rawValues = byteArrayBlock.getRawValues();
+ boolean[] rawValueIsNull = byteArrayBlock.getRawValueIsNull();
+ if (rawValueIsNull != null) {
+ for (int i = 0; i < length; i++) {
+ int rawPosition = positions[offset + i] + rawOffset;
+ if (rawValueIsNull[rawPosition]) {
+ valueIsNull[positionCount + i] = true;
+ hasNullValue = true;
+ }
+ else {
+ values[positionCount + i] = rawValues[rawPosition];
+ hasNonNullValue = true;
+ }
+ }
+ }
+ else {
+ for (int i = 0; i < length; i++) {
+ int rawPosition = positions[offset + i] + rawOffset;
+ values[positionCount + i] = rawValues[rawPosition];
+ }
+ hasNonNullValue = true;
+ }
+ positionCount += length;
+
+ if (blockBuilderStatus != null) {
+ blockBuilderStatus.addBytes(length * ByteArrayBlock.SIZE_IN_BYTES_PER_POSITION);
+ }
+ }
+
+ @Override
+ public BlockBuilder appendNull()
+ {
+ ensureCapacity(positionCount + 1);
+
valueIsNull[positionCount] = true;
hasNullValue = true;
@@ -104,23 +238,28 @@ public BlockBuilder newBlockBuilderLike(int expectedEntries, BlockBuilderStatus
return new ByteArrayBlockBuilder(blockBuilderStatus, expectedEntries);
}
- private void growCapacity()
+ private void ensureCapacity(int capacity)
{
+ if (values.length >= capacity) {
+ return;
+ }
+
int newSize;
if (initialized) {
- newSize = BlockUtil.calculateNewArraySize(values.length);
+ newSize = calculateNewArraySize(capacity);
}
else {
newSize = initialEntryCount;
initialized = true;
}
+ newSize = max(newSize, capacity);
valueIsNull = Arrays.copyOf(valueIsNull, newSize);
values = Arrays.copyOf(values, newSize);
- updateDataSize();
+ updateRetainedSize();
}
- private void updateDataSize()
+ private void updateRetainedSize()
{
retainedSizeInBytes = INSTANCE_SIZE + sizeOf(valueIsNull) + sizeOf(values);
if (blockBuilderStatus != null) {
diff --git a/core/trino-spi/src/main/java/io/trino/spi/block/Fixed12Block.java b/core/trino-spi/src/main/java/io/trino/spi/block/Fixed12Block.java
index f38e059ad2f6..a8694f321806 100644
--- a/core/trino-spi/src/main/java/io/trino/spi/block/Fixed12Block.java
+++ b/core/trino-spi/src/main/java/io/trino/spi/block/Fixed12Block.java
@@ -294,11 +294,17 @@ public static int decodeFixed12Second(int[] values, int position)
return values[offset + 2];
}
- int getPositionOffset()
+ int getRawOffset()
{
return positionOffset;
}
+ @Nullable
+ boolean[] getRawValueIsNull()
+ {
+ return valueIsNull;
+ }
+
int[] getRawValues()
{
return values;
diff --git a/core/trino-spi/src/main/java/io/trino/spi/block/Fixed12BlockBuilder.java b/core/trino-spi/src/main/java/io/trino/spi/block/Fixed12BlockBuilder.java
index f0d9e278510f..3a30f6fca05c 100644
--- a/core/trino-spi/src/main/java/io/trino/spi/block/Fixed12BlockBuilder.java
+++ b/core/trino-spi/src/main/java/io/trino/spi/block/Fixed12BlockBuilder.java
@@ -19,6 +19,7 @@
import static io.airlift.slice.SizeOf.instanceSize;
import static io.airlift.slice.SizeOf.sizeOf;
+import static io.trino.spi.block.BlockUtil.calculateNewArraySize;
import static io.trino.spi.block.Fixed12Block.FIXED12_BYTES;
import static io.trino.spi.block.Fixed12Block.encodeFixed12;
import static java.lang.Math.max;
@@ -49,14 +50,12 @@ public Fixed12BlockBuilder(@Nullable BlockBuilderStatus blockBuilderStatus, int
this.blockBuilderStatus = blockBuilderStatus;
this.initialEntryCount = max(expectedEntries, 1);
- updateDataSize();
+ updateRetainedSize();
}
public void writeFixed12(long first, int second)
{
- if (valueIsNull.length <= positionCount) {
- growCapacity();
- }
+ ensureCapacity(positionCount + 1);
encodeFixed12(first, second, values, positionCount);
@@ -68,12 +67,174 @@ public void writeFixed12(long first, int second)
}
@Override
- public BlockBuilder appendNull()
+ public void append(ValueBlock block, int position)
+ {
+ ensureCapacity(positionCount + 1);
+
+ Fixed12Block fixed12Block = (Fixed12Block) block;
+ if (fixed12Block.isNull(position)) {
+ valueIsNull[positionCount] = true;
+ hasNullValue = true;
+ }
+ else {
+ int[] rawValues = fixed12Block.getRawValues();
+ int rawValuePosition = (fixed12Block.getRawOffset() + position) * 3;
+
+ int positionIndex = positionCount * 3;
+ values[positionIndex] = rawValues[rawValuePosition];
+ values[positionIndex + 1] = rawValues[rawValuePosition + 1];
+ values[positionIndex + 2] = rawValues[rawValuePosition + 2];
+ hasNonNullValue = true;
+ }
+ positionCount++;
+
+ if (blockBuilderStatus != null) {
+ blockBuilderStatus.addBytes(Fixed12Block.SIZE_IN_BYTES_PER_POSITION);
+ }
+ }
+
+ @Override
+ public void appendRepeated(ValueBlock block, int position, int count)
+ {
+ if (count == 0) {
+ return;
+ }
+ if (count == 1) {
+ append(block, position);
+ return;
+ }
+
+ ensureCapacity(positionCount + count);
+
+ Fixed12Block fixed12Block = (Fixed12Block) block;
+ if (fixed12Block.isNull(position)) {
+ Arrays.fill(valueIsNull, positionCount, positionCount + count, true);
+ hasNullValue = true;
+ }
+ else {
+ int[] rawValues = fixed12Block.getRawValues();
+ int rawValuePosition = (fixed12Block.getRawOffset() + position) * 3;
+ int valueFirst = rawValues[rawValuePosition];
+ int valueSecond = rawValues[rawValuePosition + 1];
+ int valueThird = rawValues[rawValuePosition + 2];
+
+ int positionIndex = positionCount * 3;
+ for (int i = 0; i < count; i++) {
+ values[positionIndex] = valueFirst;
+ values[positionIndex + 1] = valueSecond;
+ values[positionIndex + 2] = valueThird;
+ positionIndex += 3;
+ }
+
+ hasNonNullValue = true;
+ }
+ positionCount += count;
+
+ if (blockBuilderStatus != null) {
+ blockBuilderStatus.addBytes(count * Fixed12Block.SIZE_IN_BYTES_PER_POSITION);
+ }
+ }
+
+ @Override
+ public void appendRange(ValueBlock block, int offset, int length)
+ {
+ if (length == 0) {
+ return;
+ }
+ if (length == 1) {
+ append(block, offset);
+ return;
+ }
+
+ ensureCapacity(positionCount + length);
+
+ Fixed12Block fixed12Block = (Fixed12Block) block;
+ int rawOffset = fixed12Block.getRawOffset();
+
+ int[] rawValues = fixed12Block.getRawValues();
+ System.arraycopy(rawValues, (rawOffset + offset) * 3, values, positionCount * 3, length * 3);
+
+ boolean[] rawValueIsNull = fixed12Block.getRawValueIsNull();
+ if (rawValueIsNull != null) {
+ for (int i = 0; i < length; i++) {
+ if (rawValueIsNull[rawOffset + offset + i]) {
+ valueIsNull[positionCount + i] = true;
+ hasNullValue = true;
+ }
+ else {
+ hasNonNullValue = true;
+ }
+ }
+ }
+ else {
+ hasNonNullValue = true;
+ }
+ positionCount += length;
+
+ if (blockBuilderStatus != null) {
+ blockBuilderStatus.addBytes(length * LongArrayBlock.SIZE_IN_BYTES_PER_POSITION);
+ }
+ }
+
+ @Override
+ public void appendPositions(ValueBlock block, int[] positions, int offset, int length)
{
- if (valueIsNull.length <= positionCount) {
- growCapacity();
+ if (length == 0) {
+ return;
+ }
+ if (length == 1) {
+ append(block, positions[offset]);
+ return;
}
+ ensureCapacity(positionCount + length);
+
+ Fixed12Block fixed12Block = (Fixed12Block) block;
+ int rawOffset = fixed12Block.getRawOffset();
+ int[] rawValues = fixed12Block.getRawValues();
+ boolean[] rawValueIsNull = fixed12Block.getRawValueIsNull();
+
+ int positionIndex = positionCount * 3;
+ if (rawValueIsNull != null) {
+ for (int i = 0; i < length; i++) {
+ int rawPosition = positions[offset + i] + rawOffset;
+ if (rawValueIsNull[rawPosition]) {
+ valueIsNull[positionCount + i] = true;
+ hasNullValue = true;
+ }
+ else {
+ int rawValuePosition = rawPosition * 3;
+ values[positionIndex] = rawValues[rawValuePosition];
+ values[positionIndex + 1] = rawValues[rawValuePosition + 1];
+ values[positionIndex + 2] = rawValues[rawValuePosition + 2];
+ hasNonNullValue = true;
+ }
+ positionIndex += 3;
+ }
+ }
+ else {
+ for (int i = 0; i < length; i++) {
+ int rawPosition = positions[offset + i] + rawOffset;
+ int rawValuePosition = rawPosition * 3;
+ values[positionIndex] = rawValues[rawValuePosition];
+ values[positionIndex + 1] = rawValues[rawValuePosition + 1];
+ values[positionIndex + 2] = rawValues[rawValuePosition + 2];
+ positionIndex += 3;
+ }
+ hasNonNullValue = true;
+ }
+ positionCount += length;
+
+ if (blockBuilderStatus != null) {
+ blockBuilderStatus.addBytes(length * Fixed12Block.SIZE_IN_BYTES_PER_POSITION);
+ }
+ }
+
+ @Override
+ public BlockBuilder appendNull()
+ {
+ ensureCapacity(positionCount + 1);
+
valueIsNull[positionCount] = true;
hasNullValue = true;
@@ -105,23 +266,28 @@ public BlockBuilder newBlockBuilderLike(int expectedEntries, BlockBuilderStatus
return new Fixed12BlockBuilder(blockBuilderStatus, expectedEntries);
}
- private void growCapacity()
+ private void ensureCapacity(int capacity)
{
+ if (valueIsNull.length >= capacity) {
+ return;
+ }
+
int newSize;
if (initialized) {
- newSize = BlockUtil.calculateNewArraySize(valueIsNull.length);
+ newSize = calculateNewArraySize(capacity);
}
else {
newSize = initialEntryCount;
initialized = true;
}
+ newSize = max(newSize, capacity);
valueIsNull = Arrays.copyOf(valueIsNull, newSize);
values = Arrays.copyOf(values, newSize * 3);
- updateDataSize();
+ updateRetainedSize();
}
- private void updateDataSize()
+ private void updateRetainedSize()
{
retainedSizeInBytes = INSTANCE_SIZE + sizeOf(valueIsNull) + sizeOf(values);
if (blockBuilderStatus != null) {
diff --git a/core/trino-spi/src/main/java/io/trino/spi/block/Fixed12BlockEncoding.java b/core/trino-spi/src/main/java/io/trino/spi/block/Fixed12BlockEncoding.java
index 131837f74c86..5d1799f83c4a 100644
--- a/core/trino-spi/src/main/java/io/trino/spi/block/Fixed12BlockEncoding.java
+++ b/core/trino-spi/src/main/java/io/trino/spi/block/Fixed12BlockEncoding.java
@@ -40,7 +40,7 @@ public void writeBlock(BlockEncodingSerde blockEncodingSerde, SliceOutput sliceO
encodeNullsAsBits(sliceOutput, fixed12Block);
if (!fixed12Block.mayHaveNull()) {
- sliceOutput.writeInts(fixed12Block.getRawValues(), fixed12Block.getPositionOffset() * 3, fixed12Block.getPositionCount() * 3);
+ sliceOutput.writeInts(fixed12Block.getRawValues(), fixed12Block.getRawOffset() * 3, fixed12Block.getPositionCount() * 3);
}
else {
int[] valuesWithoutNull = new int[positionCount * 3];
diff --git a/core/trino-spi/src/main/java/io/trino/spi/block/Int128ArrayBlock.java b/core/trino-spi/src/main/java/io/trino/spi/block/Int128ArrayBlock.java
index 57b8e4ac9bd4..0cdf686a3d21 100644
--- a/core/trino-spi/src/main/java/io/trino/spi/block/Int128ArrayBlock.java
+++ b/core/trino-spi/src/main/java/io/trino/spi/block/Int128ArrayBlock.java
@@ -256,13 +256,19 @@ public String toString()
return sb.toString();
}
- long[] getRawValues()
+ int getRawOffset()
{
- return values;
+ return positionOffset;
}
- int getPositionOffset()
+ @Nullable
+ boolean[] getRawValueIsNull()
{
- return positionOffset;
+ return valueIsNull;
+ }
+
+ long[] getRawValues()
+ {
+ return values;
}
}
diff --git a/core/trino-spi/src/main/java/io/trino/spi/block/Int128ArrayBlockBuilder.java b/core/trino-spi/src/main/java/io/trino/spi/block/Int128ArrayBlockBuilder.java
index f22ae8951fea..5d512b718453 100644
--- a/core/trino-spi/src/main/java/io/trino/spi/block/Int128ArrayBlockBuilder.java
+++ b/core/trino-spi/src/main/java/io/trino/spi/block/Int128ArrayBlockBuilder.java
@@ -19,6 +19,7 @@
import static io.airlift.slice.SizeOf.instanceSize;
import static io.airlift.slice.SizeOf.sizeOf;
+import static io.trino.spi.block.BlockUtil.calculateNewArraySize;
import static java.lang.Math.max;
public class Int128ArrayBlockBuilder
@@ -47,14 +48,12 @@ public Int128ArrayBlockBuilder(@Nullable BlockBuilderStatus blockBuilderStatus,
this.blockBuilderStatus = blockBuilderStatus;
this.initialEntryCount = max(expectedEntries, 1);
- updateDataSize();
+ updateRetainedSize();
}
public void writeInt128(long high, long low)
{
- if (valueIsNull.length <= positionCount) {
- growCapacity();
- }
+ ensureCapacity(positionCount + 1);
int valueIndex = positionCount * 2;
values[valueIndex] = high;
@@ -68,12 +67,168 @@ public void writeInt128(long high, long low)
}
@Override
- public BlockBuilder appendNull()
+ public void append(ValueBlock block, int position)
+ {
+ ensureCapacity(positionCount + 1);
+
+ Int128ArrayBlock int128ArrayBlock = (Int128ArrayBlock) block;
+ if (int128ArrayBlock.isNull(position)) {
+ valueIsNull[positionCount] = true;
+ hasNullValue = true;
+ }
+ else {
+ long[] rawValues = int128ArrayBlock.getRawValues();
+ int rawValuePosition = (int128ArrayBlock.getRawOffset() + position) * 2;
+
+ int positionIndex = positionCount * 2;
+ values[positionIndex] = rawValues[rawValuePosition];
+ values[positionIndex + 1] = rawValues[rawValuePosition + 1];
+ hasNonNullValue = true;
+ }
+ positionCount++;
+
+ if (blockBuilderStatus != null) {
+ blockBuilderStatus.addBytes(Int128ArrayBlock.SIZE_IN_BYTES_PER_POSITION);
+ }
+ }
+
+ @Override
+ public void appendRepeated(ValueBlock block, int position, int count)
+ {
+ if (count == 0) {
+ return;
+ }
+ if (count == 1) {
+ append(block, position);
+ return;
+ }
+
+ ensureCapacity(positionCount + count);
+
+ Int128ArrayBlock int128ArrayBlock = (Int128ArrayBlock) block;
+ if (int128ArrayBlock.isNull(position)) {
+ Arrays.fill(valueIsNull, positionCount, positionCount + count, true);
+ hasNullValue = true;
+ }
+ else {
+ long[] rawValues = int128ArrayBlock.getRawValues();
+ int rawValuePosition = (int128ArrayBlock.getRawOffset() + position) * 2;
+ long valueHigh = rawValues[rawValuePosition];
+ long valueLow = rawValues[rawValuePosition + 1];
+
+ int positionIndex = positionCount * 2;
+ for (int i = 0; i < count; i++) {
+ values[positionIndex] = valueHigh;
+ values[positionIndex + 1] = valueLow;
+ positionIndex += 2;
+ }
+ hasNonNullValue = true;
+ }
+ positionCount += count;
+
+ if (blockBuilderStatus != null) {
+ blockBuilderStatus.addBytes(count * Int128ArrayBlock.SIZE_IN_BYTES_PER_POSITION);
+ }
+ }
+
+ @Override
+ public void appendRange(ValueBlock block, int offset, int length)
+ {
+ if (length == 0) {
+ return;
+ }
+ if (length == 1) {
+ append(block, offset);
+ return;
+ }
+
+ ensureCapacity(positionCount + length);
+
+ Int128ArrayBlock int128ArrayBlock = (Int128ArrayBlock) block;
+ int rawOffset = int128ArrayBlock.getRawOffset();
+
+ long[] rawValues = int128ArrayBlock.getRawValues();
+ System.arraycopy(rawValues, (rawOffset + offset) * 2, values, positionCount * 2, length * 2);
+
+ boolean[] rawValueIsNull = int128ArrayBlock.getRawValueIsNull();
+ if (rawValueIsNull != null) {
+ for (int i = 0; i < length; i++) {
+ if (rawValueIsNull[rawOffset + offset + i]) {
+ valueIsNull[positionCount + i] = true;
+ hasNullValue = true;
+ }
+ else {
+ hasNonNullValue = true;
+ }
+ }
+ }
+ else {
+ hasNonNullValue = true;
+ }
+ positionCount += length;
+
+ if (blockBuilderStatus != null) {
+ blockBuilderStatus.addBytes(length * LongArrayBlock.SIZE_IN_BYTES_PER_POSITION);
+ }
+ }
+
+ @Override
+ public void appendPositions(ValueBlock block, int[] positions, int offset, int length)
{
- if (valueIsNull.length <= positionCount) {
- growCapacity();
+ if (length == 0) {
+ return;
+ }
+ if (length == 1) {
+ append(block, positions[offset]);
+ return;
}
+ ensureCapacity(positionCount + length);
+
+ Int128ArrayBlock int128ArrayBlock = (Int128ArrayBlock) block;
+ int rawOffset = int128ArrayBlock.getRawOffset();
+ long[] rawValues = int128ArrayBlock.getRawValues();
+ boolean[] rawValueIsNull = int128ArrayBlock.getRawValueIsNull();
+
+ int positionIndex = positionCount * 2;
+ if (rawValueIsNull != null) {
+ for (int i = 0; i < length; i++) {
+ int rawPosition = positions[offset + i] + rawOffset;
+ if (rawValueIsNull[rawPosition]) {
+ valueIsNull[positionCount + i] = true;
+ hasNullValue = true;
+ }
+ else {
+ int rawValuePosition = rawPosition * 2;
+ values[positionIndex] = rawValues[rawValuePosition];
+ values[positionIndex + 1] = rawValues[rawValuePosition + 1];
+ hasNonNullValue = true;
+ }
+ positionIndex += 2;
+ }
+ }
+ else {
+ for (int i = 0; i < length; i++) {
+ int rawPosition = positions[offset + i] + rawOffset;
+ int rawValuePosition = rawPosition * 2;
+ values[positionIndex] = rawValues[rawValuePosition];
+ values[positionIndex + 1] = rawValues[rawValuePosition + 1];
+ positionIndex += 2;
+ }
+ hasNonNullValue = true;
+ }
+ positionCount += length;
+
+ if (blockBuilderStatus != null) {
+ blockBuilderStatus.addBytes(length * Int128ArrayBlock.SIZE_IN_BYTES_PER_POSITION);
+ }
+ }
+
+ @Override
+ public BlockBuilder appendNull()
+ {
+ ensureCapacity(positionCount + 1);
+
valueIsNull[positionCount] = true;
hasNullValue = true;
@@ -105,23 +260,28 @@ public BlockBuilder newBlockBuilderLike(int expectedEntries, BlockBuilderStatus
return new Int128ArrayBlockBuilder(blockBuilderStatus, expectedEntries);
}
- private void growCapacity()
+ private void ensureCapacity(int capacity)
{
+ if (valueIsNull.length >= capacity) {
+ return;
+ }
+
int newSize;
if (initialized) {
- newSize = BlockUtil.calculateNewArraySize(valueIsNull.length);
+ newSize = calculateNewArraySize(capacity);
}
else {
newSize = initialEntryCount;
initialized = true;
}
+ newSize = max(newSize, capacity);
valueIsNull = Arrays.copyOf(valueIsNull, newSize);
values = Arrays.copyOf(values, newSize * 2);
- updateDataSize();
+ updateRetainedSize();
}
- private void updateDataSize()
+ private void updateRetainedSize()
{
retainedSizeInBytes = INSTANCE_SIZE + sizeOf(valueIsNull) + sizeOf(values);
if (blockBuilderStatus != null) {
diff --git a/core/trino-spi/src/main/java/io/trino/spi/block/Int128ArrayBlockEncoding.java b/core/trino-spi/src/main/java/io/trino/spi/block/Int128ArrayBlockEncoding.java
index 78e8191202e5..55539440d8cd 100644
--- a/core/trino-spi/src/main/java/io/trino/spi/block/Int128ArrayBlockEncoding.java
+++ b/core/trino-spi/src/main/java/io/trino/spi/block/Int128ArrayBlockEncoding.java
@@ -40,7 +40,7 @@ public void writeBlock(BlockEncodingSerde blockEncodingSerde, SliceOutput sliceO
encodeNullsAsBits(sliceOutput, int128ArrayBlock);
if (!int128ArrayBlock.mayHaveNull()) {
- sliceOutput.writeLongs(int128ArrayBlock.getRawValues(), int128ArrayBlock.getPositionOffset() * 2, int128ArrayBlock.getPositionCount() * 2);
+ sliceOutput.writeLongs(int128ArrayBlock.getRawValues(), int128ArrayBlock.getRawOffset() * 2, int128ArrayBlock.getPositionCount() * 2);
}
else {
long[] valuesWithoutNull = new long[positionCount * 2];
diff --git a/core/trino-spi/src/main/java/io/trino/spi/block/IntArrayBlock.java b/core/trino-spi/src/main/java/io/trino/spi/block/IntArrayBlock.java
index 93fa86da8456..11d426889fb2 100644
--- a/core/trino-spi/src/main/java/io/trino/spi/block/IntArrayBlock.java
+++ b/core/trino-spi/src/main/java/io/trino/spi/block/IntArrayBlock.java
@@ -237,6 +237,11 @@ public String toString()
return sb.toString();
}
+ boolean[] getRawValueIsNull()
+ {
+ return valueIsNull;
+ }
+
@Experimental(eta = "2023-12-31")
public int[] getRawValues()
{
diff --git a/core/trino-spi/src/main/java/io/trino/spi/block/IntArrayBlockBuilder.java b/core/trino-spi/src/main/java/io/trino/spi/block/IntArrayBlockBuilder.java
index bf124103418b..50ebd1029e23 100644
--- a/core/trino-spi/src/main/java/io/trino/spi/block/IntArrayBlockBuilder.java
+++ b/core/trino-spi/src/main/java/io/trino/spi/block/IntArrayBlockBuilder.java
@@ -19,6 +19,7 @@
import static io.airlift.slice.SizeOf.instanceSize;
import static io.airlift.slice.SizeOf.sizeOf;
+import static io.trino.spi.block.BlockUtil.calculateNewArraySize;
import static java.lang.Math.max;
public class IntArrayBlockBuilder
@@ -47,14 +48,12 @@ public IntArrayBlockBuilder(@Nullable BlockBuilderStatus blockBuilderStatus, int
this.blockBuilderStatus = blockBuilderStatus;
this.initialEntryCount = max(expectedEntries, 1);
- updateDataSize();
+ updateRetainedSize();
}
public BlockBuilder writeInt(int value)
{
- if (values.length <= positionCount) {
- growCapacity();
- }
+ ensureCapacity(positionCount + 1);
values[positionCount] = value;
@@ -67,12 +66,146 @@ public BlockBuilder writeInt(int value)
}
@Override
- public BlockBuilder appendNull()
+ public void append(ValueBlock block, int position)
+ {
+ ensureCapacity(positionCount + 1);
+
+ IntArrayBlock intArrayBlock = (IntArrayBlock) block;
+ if (intArrayBlock.isNull(position)) {
+ valueIsNull[positionCount] = true;
+ hasNullValue = true;
+ }
+ else {
+ values[positionCount] = intArrayBlock.getInt(position);
+ hasNonNullValue = true;
+ }
+ positionCount++;
+
+ if (blockBuilderStatus != null) {
+ blockBuilderStatus.addBytes(IntArrayBlock.SIZE_IN_BYTES_PER_POSITION);
+ }
+ }
+
+ @Override
+ public void appendRepeated(ValueBlock block, int position, int count)
{
- if (values.length <= positionCount) {
- growCapacity();
+ if (count == 0) {
+ return;
+ }
+ if (count == 1) {
+ append(block, position);
+ return;
}
+ ensureCapacity(positionCount + count);
+
+ IntArrayBlock intArrayBlock = (IntArrayBlock) block;
+ if (intArrayBlock.isNull(position)) {
+ Arrays.fill(valueIsNull, positionCount, positionCount + count, true);
+ hasNullValue = true;
+ }
+ else {
+ int value = intArrayBlock.getInt(position);
+ Arrays.fill(values, positionCount, positionCount + count, value);
+ hasNonNullValue = true;
+ }
+ positionCount += count;
+
+ if (blockBuilderStatus != null) {
+ blockBuilderStatus.addBytes(count * IntArrayBlock.SIZE_IN_BYTES_PER_POSITION);
+ }
+ }
+
+ @Override
+ public void appendRange(ValueBlock block, int offset, int length)
+ {
+ if (length == 0) {
+ return;
+ }
+ if (length == 1) {
+ append(block, offset);
+ return;
+ }
+
+ ensureCapacity(positionCount + length);
+
+ IntArrayBlock intArrayBlock = (IntArrayBlock) block;
+ int rawOffset = intArrayBlock.getRawValuesOffset();
+
+ int[] rawValues = intArrayBlock.getRawValues();
+ System.arraycopy(rawValues, rawOffset + offset, values, positionCount, length);
+
+ boolean[] rawValueIsNull = intArrayBlock.getRawValueIsNull();
+ if (rawValueIsNull != null) {
+ for (int i = 0; i < length; i++) {
+ if (rawValueIsNull[rawOffset + offset + i]) {
+ valueIsNull[positionCount + i] = true;
+ hasNullValue = true;
+ }
+ else {
+ hasNonNullValue = true;
+ }
+ }
+ }
+ else {
+ hasNonNullValue = true;
+ }
+ positionCount += length;
+
+ if (blockBuilderStatus != null) {
+ blockBuilderStatus.addBytes(length * IntArrayBlock.SIZE_IN_BYTES_PER_POSITION);
+ }
+ }
+
+ @Override
+ public void appendPositions(ValueBlock block, int[] positions, int offset, int length)
+ {
+ if (length == 0) {
+ return;
+ }
+ if (length == 1) {
+ append(block, positions[offset]);
+ return;
+ }
+
+ ensureCapacity(positionCount + length);
+
+ IntArrayBlock intArrayBlock = (IntArrayBlock) block;
+ int rawOffset = intArrayBlock.getRawValuesOffset();
+ int[] rawValues = intArrayBlock.getRawValues();
+ boolean[] rawValueIsNull = intArrayBlock.getRawValueIsNull();
+ if (rawValueIsNull != null) {
+ for (int i = 0; i < length; i++) {
+ int rawPosition = positions[offset + i] + rawOffset;
+ if (rawValueIsNull[rawPosition]) {
+ valueIsNull[positionCount + i] = true;
+ hasNullValue = true;
+ }
+ else {
+ values[positionCount + i] = rawValues[rawPosition];
+ hasNonNullValue = true;
+ }
+ }
+ }
+ else {
+ for (int i = 0; i < length; i++) {
+ int rawPosition = positions[offset + i] + rawOffset;
+ values[positionCount + i] = rawValues[rawPosition];
+ }
+ hasNonNullValue = true;
+ }
+ positionCount += length;
+
+ if (blockBuilderStatus != null) {
+ blockBuilderStatus.addBytes(length * IntArrayBlock.SIZE_IN_BYTES_PER_POSITION);
+ }
+ }
+
+ @Override
+ public BlockBuilder appendNull()
+ {
+ ensureCapacity(positionCount + 1);
+
valueIsNull[positionCount] = true;
hasNullValue = true;
@@ -104,23 +237,28 @@ public BlockBuilder newBlockBuilderLike(int expectedEntries, BlockBuilderStatus
return new IntArrayBlockBuilder(blockBuilderStatus, expectedEntries);
}
- private void growCapacity()
+ private void ensureCapacity(int capacity)
{
+ if (values.length >= capacity) {
+ return;
+ }
+
int newSize;
if (initialized) {
- newSize = BlockUtil.calculateNewArraySize(values.length);
+ newSize = calculateNewArraySize(capacity);
}
else {
newSize = initialEntryCount;
initialized = true;
}
+ newSize = max(newSize, capacity);
valueIsNull = Arrays.copyOf(valueIsNull, newSize);
values = Arrays.copyOf(values, newSize);
- updateDataSize();
+ updateRetainedSize();
}
- private void updateDataSize()
+ private void updateRetainedSize()
{
retainedSizeInBytes = INSTANCE_SIZE + sizeOf(valueIsNull) + sizeOf(values);
if (blockBuilderStatus != null) {
diff --git a/core/trino-spi/src/main/java/io/trino/spi/block/LongArrayBlock.java b/core/trino-spi/src/main/java/io/trino/spi/block/LongArrayBlock.java
index 2b9aec633844..b79e5939ba79 100644
--- a/core/trino-spi/src/main/java/io/trino/spi/block/LongArrayBlock.java
+++ b/core/trino-spi/src/main/java/io/trino/spi/block/LongArrayBlock.java
@@ -283,13 +283,18 @@ public String toString()
return sb.toString();
}
+ int getRawValuesOffset()
+ {
+ return arrayOffset;
+ }
+
long[] getRawValues()
{
return values;
}
- int getRawValuesOffset()
+ boolean[] getRawValueIsNull()
{
- return arrayOffset;
+ return valueIsNull;
}
}
diff --git a/core/trino-spi/src/main/java/io/trino/spi/block/LongArrayBlockBuilder.java b/core/trino-spi/src/main/java/io/trino/spi/block/LongArrayBlockBuilder.java
index 09a530971ac1..059709edbb1e 100644
--- a/core/trino-spi/src/main/java/io/trino/spi/block/LongArrayBlockBuilder.java
+++ b/core/trino-spi/src/main/java/io/trino/spi/block/LongArrayBlockBuilder.java
@@ -19,6 +19,7 @@
import static io.airlift.slice.SizeOf.instanceSize;
import static io.airlift.slice.SizeOf.sizeOf;
+import static io.trino.spi.block.BlockUtil.calculateNewArraySize;
import static java.lang.Math.max;
public class LongArrayBlockBuilder
@@ -47,14 +48,12 @@ public LongArrayBlockBuilder(@Nullable BlockBuilderStatus blockBuilderStatus, in
this.blockBuilderStatus = blockBuilderStatus;
this.initialEntryCount = max(expectedEntries, 1);
- updateDataSize();
+ updateRetainedSize();
}
public BlockBuilder writeLong(long value)
{
- if (values.length <= positionCount) {
- growCapacity();
- }
+ ensureCapacity(positionCount + 1);
values[positionCount] = value;
@@ -67,12 +66,146 @@ public BlockBuilder writeLong(long value)
}
@Override
- public BlockBuilder appendNull()
+ public void append(ValueBlock block, int position)
+ {
+ ensureCapacity(positionCount + 1);
+
+ LongArrayBlock longArrayBlock = (LongArrayBlock) block;
+ if (longArrayBlock.isNull(position)) {
+ valueIsNull[positionCount] = true;
+ hasNullValue = true;
+ }
+ else {
+ values[positionCount] = longArrayBlock.getLong(position);
+ hasNonNullValue = true;
+ }
+ positionCount++;
+
+ if (blockBuilderStatus != null) {
+ blockBuilderStatus.addBytes(LongArrayBlock.SIZE_IN_BYTES_PER_POSITION);
+ }
+ }
+
+ @Override
+ public void appendRepeated(ValueBlock block, int position, int count)
{
- if (values.length <= positionCount) {
- growCapacity();
+ if (count == 0) {
+ return;
+ }
+ if (count == 1) {
+ append(block, position);
+ return;
}
+ ensureCapacity(positionCount + count);
+
+ LongArrayBlock longArrayBlock = (LongArrayBlock) block;
+ if (longArrayBlock.isNull(position)) {
+ Arrays.fill(valueIsNull, positionCount, positionCount + count, true);
+ hasNullValue = true;
+ }
+ else {
+ long value = longArrayBlock.getLong(position);
+ Arrays.fill(values, positionCount, positionCount + count, value);
+ hasNonNullValue = true;
+ }
+ positionCount += count;
+
+ if (blockBuilderStatus != null) {
+ blockBuilderStatus.addBytes(count * LongArrayBlock.SIZE_IN_BYTES_PER_POSITION);
+ }
+ }
+
+ @Override
+ public void appendRange(ValueBlock block, int offset, int length)
+ {
+ if (length == 0) {
+ return;
+ }
+ if (length == 1) {
+ append(block, offset);
+ return;
+ }
+
+ ensureCapacity(positionCount + length);
+
+ LongArrayBlock longArrayBlock = (LongArrayBlock) block;
+ int rawOffset = longArrayBlock.getRawValuesOffset();
+
+ long[] rawValues = longArrayBlock.getRawValues();
+ System.arraycopy(rawValues, rawOffset + offset, values, positionCount, length);
+
+ boolean[] rawValueIsNull = longArrayBlock.getRawValueIsNull();
+ if (rawValueIsNull != null) {
+ for (int i = 0; i < length; i++) {
+ if (rawValueIsNull[rawOffset + offset + i]) {
+ valueIsNull[positionCount + i] = true;
+ hasNullValue = true;
+ }
+ else {
+ hasNonNullValue = true;
+ }
+ }
+ }
+ else {
+ hasNonNullValue = true;
+ }
+ positionCount += length;
+
+ if (blockBuilderStatus != null) {
+ blockBuilderStatus.addBytes(length * LongArrayBlock.SIZE_IN_BYTES_PER_POSITION);
+ }
+ }
+
+ @Override
+ public void appendPositions(ValueBlock block, int[] positions, int offset, int length)
+ {
+ if (length == 0) {
+ return;
+ }
+ if (length == 1) {
+ append(block, positions[offset]);
+ return;
+ }
+
+ ensureCapacity(positionCount + length);
+
+ LongArrayBlock longArrayBlock = (LongArrayBlock) block;
+ int rawOffset = longArrayBlock.getRawValuesOffset();
+ long[] rawValues = longArrayBlock.getRawValues();
+ boolean[] rawValueIsNull = longArrayBlock.getRawValueIsNull();
+ if (rawValueIsNull != null) {
+ for (int i = 0; i < length; i++) {
+ int rawPosition = positions[offset + i] + rawOffset;
+ if (rawValueIsNull[rawPosition]) {
+ valueIsNull[positionCount + i] = true;
+ hasNullValue = true;
+ }
+ else {
+ values[positionCount + i] = rawValues[rawPosition];
+ hasNonNullValue = true;
+ }
+ }
+ }
+ else {
+ for (int i = 0; i < length; i++) {
+ int rawPosition = positions[offset + i] + rawOffset;
+ values[positionCount + i] = rawValues[rawPosition];
+ }
+ hasNonNullValue = true;
+ }
+ positionCount += length;
+
+ if (blockBuilderStatus != null) {
+ blockBuilderStatus.addBytes(length * LongArrayBlock.SIZE_IN_BYTES_PER_POSITION);
+ }
+ }
+
+ @Override
+ public BlockBuilder appendNull()
+ {
+ ensureCapacity(positionCount + 1);
+
valueIsNull[positionCount] = true;
hasNullValue = true;
@@ -104,23 +237,28 @@ public BlockBuilder newBlockBuilderLike(int expectedEntries, BlockBuilderStatus
return new LongArrayBlockBuilder(blockBuilderStatus, expectedEntries);
}
- private void growCapacity()
+ private void ensureCapacity(int capacity)
{
+ if (values.length >= capacity) {
+ return;
+ }
+
int newSize;
if (initialized) {
- newSize = BlockUtil.calculateNewArraySize(values.length);
+ newSize = calculateNewArraySize(capacity);
}
else {
newSize = initialEntryCount;
initialized = true;
}
+ newSize = max(newSize, capacity);
valueIsNull = Arrays.copyOf(valueIsNull, newSize);
values = Arrays.copyOf(values, newSize);
- updateDataSize();
+ updateRetainedSize();
}
- private void updateDataSize()
+ private void updateRetainedSize()
{
retainedSizeInBytes = INSTANCE_SIZE + sizeOf(valueIsNull) + sizeOf(values);
if (blockBuilderStatus != null) {
diff --git a/core/trino-spi/src/main/java/io/trino/spi/block/MapBlockBuilder.java b/core/trino-spi/src/main/java/io/trino/spi/block/MapBlockBuilder.java
index 477ae48f4ce1..c1e4af377f2c 100644
--- a/core/trino-spi/src/main/java/io/trino/spi/block/MapBlockBuilder.java
+++ b/core/trino-spi/src/main/java/io/trino/spi/block/MapBlockBuilder.java
@@ -23,6 +23,7 @@
import static io.airlift.slice.SizeOf.instanceSize;
import static io.airlift.slice.SizeOf.sizeOf;
+import static io.trino.spi.block.BlockUtil.appendRawBlockRange;
import static io.trino.spi.block.BlockUtil.calculateNewArraySize;
import static io.trino.spi.block.MapBlock.createMapBlockInternal;
import static io.trino.spi.block.MapHashTables.HASH_MULTIPLIER;
@@ -132,6 +133,64 @@ public void buildEntry(MapValueBuilder builder)
currentEntryOpened = false;
}
+ @Override
+ public void append(ValueBlock block, int position)
+ {
+ if (currentEntryOpened) {
+ throw new IllegalStateException("Current entry must be closed before a null can be written");
+ }
+
+ MapBlock mapBlock = (MapBlock) block;
+ if (block.isNull(position)) {
+ entryAdded(true);
+ return;
+ }
+
+ int offsetBase = mapBlock.getOffsetBase();
+ int[] offsets = mapBlock.getOffsets();
+ int startOffset = offsets[offsetBase + position];
+ int length = offsets[offsetBase + position + 1] - startOffset;
+
+ appendRawBlockRange(mapBlock.getRawKeyBlock(), startOffset, length, keyBlockBuilder);
+ appendRawBlockRange(mapBlock.getRawValueBlock(), startOffset, length, valueBlockBuilder);
+ entryAdded(false);
+ }
+
+ @Override
+ public void appendRange(ValueBlock block, int offset, int length)
+ {
+ if (currentEntryOpened) {
+ throw new IllegalStateException("Current entry must be closed before a null can be written");
+ }
+
+ // this could be optimized to append all array elements using a single append range call
+ for (int i = 0; i < length; i++) {
+ append(block, offset + i);
+ }
+ }
+
+ @Override
+ public void appendRepeated(ValueBlock block, int position, int count)
+ {
+ if (currentEntryOpened) {
+ throw new IllegalStateException("Current entry must be closed before a null can be written");
+ }
+ for (int i = 0; i < count; i++) {
+ append(block, position);
+ }
+ }
+
+ @Override
+ public void appendPositions(ValueBlock block, int[] positions, int offset, int length)
+ {
+ if (currentEntryOpened) {
+ throw new IllegalStateException("Current entry must be closed before a null can be written");
+ }
+ for (int i = 0; i < length; i++) {
+ append(block, positions[offset + i]);
+ }
+ }
+
@Override
public BlockBuilder appendNull()
{
@@ -167,6 +226,29 @@ private void entryAdded(boolean isNull)
@Override
public Block build()
{
+ if (positionCount > 1 && hasNullValue) {
+ boolean hasNonNull = false;
+ for (int i = 0; i < positionCount; i++) {
+ hasNonNull |= !mapIsNull[i];
+ }
+ if (!hasNonNull) {
+ Block emptyKeyBlock = mapType.getKeyType().createBlockBuilder(null, 0).build();
+ Block emptyValueBlock = mapType.getValueType().createBlockBuilder(null, 0).build();
+ int[] emptyOffsets = {0, 0};
+ boolean[] nulls = {true};
+ return RunLengthEncodedBlock.create(
+ createMapBlockInternal(
+ mapType,
+ 0,
+ 1,
+ Optional.of(nulls),
+ emptyOffsets,
+ emptyKeyBlock,
+ emptyValueBlock,
+ MapHashTables.create(hashBuildMode, mapType, 0, emptyKeyBlock, emptyOffsets, nulls)),
+ positionCount);
+ }
+ }
return buildValueBlock();
}
diff --git a/core/trino-spi/src/main/java/io/trino/spi/block/RowBlock.java b/core/trino-spi/src/main/java/io/trino/spi/block/RowBlock.java
index 930f82bbe50a..2e4d98ee2769 100644
--- a/core/trino-spi/src/main/java/io/trino/spi/block/RowBlock.java
+++ b/core/trino-spi/src/main/java/io/trino/spi/block/RowBlock.java
@@ -152,6 +152,11 @@ public boolean mayHaveNull()
return rowIsNull != null;
}
+ boolean[] getRawRowIsNull()
+ {
+ return rowIsNull;
+ }
+
@Override
public int getPositionCount()
{
diff --git a/core/trino-spi/src/main/java/io/trino/spi/block/RowBlockBuilder.java b/core/trino-spi/src/main/java/io/trino/spi/block/RowBlockBuilder.java
index 291c97c1b9ae..ea1a16f4bdf9 100644
--- a/core/trino-spi/src/main/java/io/trino/spi/block/RowBlockBuilder.java
+++ b/core/trino-spi/src/main/java/io/trino/spi/block/RowBlockBuilder.java
@@ -112,6 +112,205 @@ public void buildEntry(RowValueBuilder builder)
currentEntryOpened = false;
}
+ @Override
+ public void append(ValueBlock block, int position)
+ {
+ if (currentEntryOpened) {
+ throw new IllegalStateException("Current entry must be closed before a null can be written");
+ }
+
+ RowBlock rowBlock = (RowBlock) block;
+ if (block.isNull(position)) {
+ appendNull();
+ return;
+ }
+
+ List fieldBlocks = rowBlock.getFieldBlocks();
+ for (int fieldId = 0; fieldId < fieldBlockBuilders.length; fieldId++) {
+ appendToField(fieldBlocks.get(fieldId), position, fieldBlockBuilders[fieldId]);
+ }
+ entryAdded(false);
+ }
+
+ private static void appendToField(Block fieldBlock, int position, BlockBuilder fieldBlockBuilder)
+ {
+ fieldBlock = fieldBlock.getLoadedBlock();
+ if (fieldBlock instanceof RunLengthEncodedBlock rleBlock) {
+ fieldBlockBuilder.append(rleBlock.getValue(), 0);
+ }
+ else if (fieldBlock instanceof DictionaryBlock dictionaryBlock) {
+ fieldBlockBuilder.append(dictionaryBlock.getDictionary(), dictionaryBlock.getId(position));
+ }
+ else if (fieldBlock instanceof ValueBlock valueBlock) {
+ fieldBlockBuilder.append(valueBlock, position);
+ }
+ else {
+ throw new IllegalArgumentException("Unsupported block type " + fieldBlock.getClass().getSimpleName());
+ }
+ }
+
+ @Override
+ public void appendRange(ValueBlock block, int offset, int length)
+ {
+ if (currentEntryOpened) {
+ throw new IllegalStateException("Current entry must be closed before a null can be written");
+ }
+ if (length == 0) {
+ return;
+ }
+
+ RowBlock rowBlock = (RowBlock) block;
+ ensureCapacity(positionCount + length);
+
+ List fieldBlocks = rowBlock.getFieldBlocks();
+ for (int fieldId = 0; fieldId < fieldBlockBuilders.length; fieldId++) {
+ appendRangeToField(fieldBlocks.get(fieldId), offset, length, fieldBlockBuilders[fieldId]);
+ }
+
+ boolean[] rawRowIsNull = rowBlock.getRawRowIsNull();
+ if (rawRowIsNull != null) {
+ for (int i = 0; i < length; i++) {
+ if (rawRowIsNull[offset + i]) {
+ rowIsNull[positionCount + i] = true;
+ hasNullRow = true;
+ }
+ else {
+ hasNonNullRow = true;
+ }
+ }
+ }
+ else {
+ hasNonNullRow = true;
+ }
+ positionCount += length;
+ }
+
+ private static void appendRangeToField(Block fieldBlock, int offset, int length, BlockBuilder fieldBlockBuilder)
+ {
+ fieldBlock = fieldBlock.getLoadedBlock();
+ if (fieldBlock instanceof RunLengthEncodedBlock rleBlock) {
+ fieldBlockBuilder.appendRepeated(rleBlock.getValue(), 0, length);
+ }
+ else if (fieldBlock instanceof DictionaryBlock dictionaryBlock) {
+ int[] rawIds = dictionaryBlock.getRawIds();
+ int rawIdsOffset = dictionaryBlock.getRawIdsOffset();
+ fieldBlockBuilder.appendPositions(dictionaryBlock.getDictionary(), rawIds, rawIdsOffset + offset, length);
+ }
+ else if (fieldBlock instanceof ValueBlock valueBlock) {
+ fieldBlockBuilder.appendRange(valueBlock, offset, length);
+ }
+ else {
+ throw new IllegalArgumentException("Unsupported block type " + fieldBlock.getClass().getSimpleName());
+ }
+ }
+
+ @Override
+ public void appendRepeated(ValueBlock block, int position, int count)
+ {
+ if (currentEntryOpened) {
+ throw new IllegalStateException("Current entry must be closed before a null can be written");
+ }
+ if (count == 0) {
+ return;
+ }
+
+ RowBlock rowBlock = (RowBlock) block;
+ ensureCapacity(positionCount + count);
+
+ List fieldBlocks = rowBlock.getFieldBlocks();
+ for (int fieldId = 0; fieldId < fieldBlockBuilders.length; fieldId++) {
+ appendRepeatedToField(fieldBlocks.get(fieldId), position, count, fieldBlockBuilders[fieldId]);
+ }
+
+ if (rowBlock.isNull(position)) {
+ Arrays.fill(rowIsNull, positionCount, positionCount + count, true);
+ hasNullRow = true;
+ }
+ else {
+ hasNonNullRow = true;
+ }
+
+ positionCount += count;
+
+ if (blockBuilderStatus != null) {
+ blockBuilderStatus.addBytes(Integer.BYTES + Byte.BYTES);
+ }
+ }
+
+ private static void appendRepeatedToField(Block fieldBlock, int position, int count, BlockBuilder fieldBlockBuilder)
+ {
+ fieldBlock = fieldBlock.getLoadedBlock();
+ if (fieldBlock instanceof RunLengthEncodedBlock rleBlock) {
+ fieldBlockBuilder.appendRepeated(rleBlock.getValue(), 0, count);
+ }
+ else if (fieldBlock instanceof DictionaryBlock dictionaryBlock) {
+ fieldBlockBuilder.appendRepeated(dictionaryBlock.getDictionary(), dictionaryBlock.getId(position), count);
+ }
+ else if (fieldBlock instanceof ValueBlock valueBlock) {
+ fieldBlockBuilder.appendRepeated(valueBlock, position, count);
+ }
+ else {
+ throw new IllegalArgumentException("Unsupported block type " + fieldBlock.getClass().getSimpleName());
+ }
+ }
+
+ @Override
+ public void appendPositions(ValueBlock block, int[] positions, int offset, int length)
+ {
+ if (currentEntryOpened) {
+ throw new IllegalStateException("Current entry must be closed before a null can be written");
+ }
+ if (length == 0) {
+ return;
+ }
+
+ RowBlock rowBlock = (RowBlock) block;
+ ensureCapacity(positionCount + length);
+
+ List fieldBlocks = rowBlock.getFieldBlocks();
+ for (int fieldId = 0; fieldId < fieldBlockBuilders.length; fieldId++) {
+ appendPositionsToField(fieldBlocks.get(fieldId), positions, offset, length, fieldBlockBuilders[fieldId]);
+ }
+
+ boolean[] rawRowIsNull = rowBlock.getRawRowIsNull();
+ if (rawRowIsNull != null) {
+ for (int i = 0; i < length; i++) {
+ if (rawRowIsNull[positions[offset + i]]) {
+ rowIsNull[positionCount + i] = true;
+ hasNullRow = true;
+ }
+ else {
+ hasNonNullRow = true;
+ }
+ }
+ }
+ else {
+ hasNonNullRow = true;
+ }
+ positionCount += length;
+ }
+
+ private static void appendPositionsToField(Block fieldBlock, int[] positions, int offset, int length, BlockBuilder fieldBlockBuilder)
+ {
+ fieldBlock = fieldBlock.getLoadedBlock();
+ if (fieldBlock instanceof RunLengthEncodedBlock rleBlock) {
+ fieldBlockBuilder.appendRepeated(rleBlock.getValue(), 0, length);
+ }
+ else if (fieldBlock instanceof DictionaryBlock dictionaryBlock) {
+ int[] newPositions = new int[length];
+ for (int i = 0; i < newPositions.length; i++) {
+ newPositions[i] = dictionaryBlock.getId(positions[offset + i]);
+ }
+ fieldBlockBuilder.appendPositions(dictionaryBlock.getDictionary(), newPositions, 0, length);
+ }
+ else if (fieldBlock instanceof ValueBlock valueBlock) {
+ fieldBlockBuilder.appendPositions(valueBlock, positions, offset, length);
+ }
+ else {
+ throw new IllegalArgumentException("Unsupported block type " + fieldBlock.getClass().getSimpleName());
+ }
+ }
+
@Override
public BlockBuilder appendNull()
{
@@ -129,10 +328,7 @@ public BlockBuilder appendNull()
private void entryAdded(boolean isNull)
{
- if (rowIsNull.length <= positionCount) {
- int newSize = BlockUtil.calculateNewArraySize(rowIsNull.length);
- rowIsNull = Arrays.copyOf(rowIsNull, newSize);
- }
+ ensureCapacity(positionCount + 1);
rowIsNull[positionCount] = isNull;
hasNullRow |= isNull;
@@ -176,6 +372,17 @@ public RowBlock buildValueBlock()
return createRowBlockInternal(positionCount, hasNullRow ? rowIsNull : null, fieldBlocks);
}
+ private void ensureCapacity(int capacity)
+ {
+ if (rowIsNull.length >= capacity) {
+ return;
+ }
+
+ // todo add lazy initialize
+ int newSize = BlockUtil.calculateNewArraySize(rowIsNull.length);
+ rowIsNull = Arrays.copyOf(rowIsNull, newSize);
+ }
+
@Override
public String toString()
{
diff --git a/core/trino-spi/src/main/java/io/trino/spi/block/ShortArrayBlock.java b/core/trino-spi/src/main/java/io/trino/spi/block/ShortArrayBlock.java
index 336a5b845539..88ed0b470405 100644
--- a/core/trino-spi/src/main/java/io/trino/spi/block/ShortArrayBlock.java
+++ b/core/trino-spi/src/main/java/io/trino/spi/block/ShortArrayBlock.java
@@ -244,4 +244,9 @@ short[] getRawValues()
{
return values;
}
+
+ boolean[] getRawValueIsNull()
+ {
+ return valueIsNull;
+ }
}
diff --git a/core/trino-spi/src/main/java/io/trino/spi/block/ShortArrayBlockBuilder.java b/core/trino-spi/src/main/java/io/trino/spi/block/ShortArrayBlockBuilder.java
index ee44b44b6dc2..a18d02142b1a 100644
--- a/core/trino-spi/src/main/java/io/trino/spi/block/ShortArrayBlockBuilder.java
+++ b/core/trino-spi/src/main/java/io/trino/spi/block/ShortArrayBlockBuilder.java
@@ -19,6 +19,7 @@
import static io.airlift.slice.SizeOf.instanceSize;
import static io.airlift.slice.SizeOf.sizeOf;
+import static io.trino.spi.block.BlockUtil.calculateNewArraySize;
import static java.lang.Math.max;
public class ShortArrayBlockBuilder
@@ -47,14 +48,12 @@ public ShortArrayBlockBuilder(@Nullable BlockBuilderStatus blockBuilderStatus, i
this.blockBuilderStatus = blockBuilderStatus;
this.initialEntryCount = max(expectedEntries, 1);
- updateDataSize();
+ updateRetainedSize();
}
- public BlockBuilder writeShort(short value)
+ public ShortArrayBlockBuilder writeShort(short value)
{
- if (values.length <= positionCount) {
- growCapacity();
- }
+ ensureCapacity(positionCount + 1);
values[positionCount] = value;
@@ -67,12 +66,146 @@ public BlockBuilder writeShort(short value)
}
@Override
- public BlockBuilder appendNull()
+ public void append(ValueBlock block, int position)
+ {
+ ensureCapacity(positionCount + 1);
+
+ ShortArrayBlock shortArrayBlock = (ShortArrayBlock) block;
+ if (shortArrayBlock.isNull(position)) {
+ valueIsNull[positionCount] = true;
+ hasNullValue = true;
+ }
+ else {
+ values[positionCount] = shortArrayBlock.getShort(position);
+ hasNonNullValue = true;
+ }
+ positionCount++;
+
+ if (blockBuilderStatus != null) {
+ blockBuilderStatus.addBytes(ShortArrayBlock.SIZE_IN_BYTES_PER_POSITION);
+ }
+ }
+
+ @Override
+ public void appendRepeated(ValueBlock block, int position, int count)
+ {
+ if (count == 0) {
+ return;
+ }
+ if (count == 1) {
+ append(block, position);
+ return;
+ }
+
+ ensureCapacity(positionCount + count);
+
+ ShortArrayBlock shortArrayBlock = (ShortArrayBlock) block;
+ if (shortArrayBlock.isNull(position)) {
+ Arrays.fill(valueIsNull, positionCount, positionCount + count, true);
+ hasNullValue = true;
+ }
+ else {
+ short value = shortArrayBlock.getShort(position);
+ Arrays.fill(values, positionCount, positionCount + count, value);
+ hasNonNullValue = true;
+ }
+ positionCount += count;
+
+ if (blockBuilderStatus != null) {
+ blockBuilderStatus.addBytes(count * ShortArrayBlock.SIZE_IN_BYTES_PER_POSITION);
+ }
+ }
+
+ @Override
+ public void appendRange(ValueBlock block, int offset, int length)
{
- if (values.length <= positionCount) {
- growCapacity();
+ if (length == 0) {
+ return;
+ }
+ if (length == 1) {
+ append(block, offset);
+ return;
}
+ ensureCapacity(positionCount + length);
+
+ ShortArrayBlock shortArrayBlock = (ShortArrayBlock) block;
+ int rawOffset = shortArrayBlock.getRawValuesOffset();
+
+ short[] rawValues = shortArrayBlock.getRawValues();
+ System.arraycopy(rawValues, rawOffset + offset, values, positionCount, length);
+
+ boolean[] rawValueIsNull = shortArrayBlock.getRawValueIsNull();
+ if (rawValueIsNull != null) {
+ for (int i = 0; i < length; i++) {
+ if (rawValueIsNull[rawOffset + offset + i]) {
+ valueIsNull[positionCount + i] = true;
+ hasNullValue = true;
+ }
+ else {
+ hasNonNullValue = true;
+ }
+ }
+ }
+ else {
+ hasNonNullValue = true;
+ }
+ positionCount += length;
+
+ if (blockBuilderStatus != null) {
+ blockBuilderStatus.addBytes(length * ShortArrayBlock.SIZE_IN_BYTES_PER_POSITION);
+ }
+ }
+
+ @Override
+ public void appendPositions(ValueBlock block, int[] positions, int offset, int length)
+ {
+ if (length == 0) {
+ return;
+ }
+ if (length == 1) {
+ append(block, positions[offset]);
+ return;
+ }
+
+ ensureCapacity(positionCount + length);
+
+ ShortArrayBlock shortArrayBlock = (ShortArrayBlock) block;
+ int rawOffset = shortArrayBlock.getRawValuesOffset();
+ short[] rawValues = shortArrayBlock.getRawValues();
+ boolean[] rawValueIsNull = shortArrayBlock.getRawValueIsNull();
+ if (rawValueIsNull != null) {
+ for (int i = 0; i < length; i++) {
+ int rawPosition = positions[offset + i] + rawOffset;
+ if (rawValueIsNull[rawPosition]) {
+ valueIsNull[positionCount + i] = true;
+ hasNullValue = true;
+ }
+ else {
+ values[positionCount + i] = rawValues[rawPosition];
+ hasNonNullValue = true;
+ }
+ }
+ }
+ else {
+ for (int i = 0; i < length; i++) {
+ int rawPosition = positions[offset + i] + rawOffset;
+ values[positionCount + i] = rawValues[rawPosition];
+ }
+ hasNonNullValue = true;
+ }
+ positionCount += length;
+
+ if (blockBuilderStatus != null) {
+ blockBuilderStatus.addBytes(length * ShortArrayBlock.SIZE_IN_BYTES_PER_POSITION);
+ }
+ }
+
+ @Override
+ public ShortArrayBlockBuilder appendNull()
+ {
+ ensureCapacity(positionCount + 1);
+
valueIsNull[positionCount] = true;
hasNullValue = true;
@@ -104,23 +237,28 @@ public BlockBuilder newBlockBuilderLike(int expectedEntries, BlockBuilderStatus
return new ShortArrayBlockBuilder(blockBuilderStatus, expectedEntries);
}
- private void growCapacity()
+ private void ensureCapacity(int capacity)
{
+ if (values.length >= capacity) {
+ return;
+ }
+
int newSize;
if (initialized) {
- newSize = BlockUtil.calculateNewArraySize(values.length);
+ newSize = calculateNewArraySize(capacity);
}
else {
newSize = initialEntryCount;
initialized = true;
}
+ newSize = max(newSize, capacity);
valueIsNull = Arrays.copyOf(valueIsNull, newSize);
values = Arrays.copyOf(values, newSize);
- updateDataSize();
+ updateRetainedSize();
}
- private void updateDataSize()
+ private void updateRetainedSize()
{
retainedSizeInBytes = INSTANCE_SIZE + sizeOf(valueIsNull) + sizeOf(values);
if (blockBuilderStatus != null) {
diff --git a/core/trino-spi/src/main/java/io/trino/spi/block/VariableWidthBlock.java b/core/trino-spi/src/main/java/io/trino/spi/block/VariableWidthBlock.java
index 931d4cae70eb..58bc8535bebb 100644
--- a/core/trino-spi/src/main/java/io/trino/spi/block/VariableWidthBlock.java
+++ b/core/trino-spi/src/main/java/io/trino/spi/block/VariableWidthBlock.java
@@ -332,6 +332,21 @@ public VariableWidthBlock copyWithAppendedNull()
return new VariableWidthBlock(arrayOffset, positionCount + 1, slice, newOffsets, newValueIsNull);
}
+ int getRawArrayBase()
+ {
+ return arrayOffset;
+ }
+
+ int[] getRawOffsets()
+ {
+ return offsets;
+ }
+
+ boolean[] getRawValueIsNull()
+ {
+ return valueIsNull;
+ }
+
@Override
public VariableWidthBlock getUnderlyingValueBlock()
{
diff --git a/core/trino-spi/src/main/java/io/trino/spi/block/VariableWidthBlockBuilder.java b/core/trino-spi/src/main/java/io/trino/spi/block/VariableWidthBlockBuilder.java
index ba53ef75c57c..59aca4f3b550 100644
--- a/core/trino-spi/src/main/java/io/trino/spi/block/VariableWidthBlockBuilder.java
+++ b/core/trino-spi/src/main/java/io/trino/spi/block/VariableWidthBlockBuilder.java
@@ -19,16 +19,14 @@
import java.util.Arrays;
-import static io.airlift.slice.SizeOf.SIZE_OF_BYTE;
-import static io.airlift.slice.SizeOf.SIZE_OF_INT;
import static io.airlift.slice.SizeOf.instanceSize;
import static io.airlift.slice.SizeOf.sizeOf;
import static io.airlift.slice.Slices.EMPTY_SLICE;
import static io.trino.spi.block.BlockUtil.MAX_ARRAY_SIZE;
import static io.trino.spi.block.BlockUtil.calculateBlockResetBytes;
import static io.trino.spi.block.BlockUtil.calculateNewArraySize;
-import static java.lang.Math.max;
import static java.lang.Math.min;
+import static java.lang.Math.toIntExact;
public class VariableWidthBlockBuilder
implements BlockBuilder
@@ -79,7 +77,7 @@ public long getSizeInBytes()
@Override
public long getRetainedSizeInBytes()
{
- long size = INSTANCE_SIZE + sizeOf(bytes) + arraysRetainedSizeInBytes;
+ long size = INSTANCE_SIZE + arraysRetainedSizeInBytes;
if (blockBuilderStatus != null) {
size += BlockBuilderStatus.INSTANCE_SIZE;
}
@@ -108,53 +106,247 @@ public VariableWidthBlockBuilder writeEntry(byte[] source, int sourceIndex, int
}
@Override
- public BlockBuilder appendNull()
+ public void append(ValueBlock block, int position)
{
- hasNullValue = true;
- entryAdded(0, true);
- return this;
+ ensureCapacity(positionCount + 1);
+
+ VariableWidthBlock variableWidthBlock = (VariableWidthBlock) block;
+ int bytesWritten = 0;
+ if (variableWidthBlock.isNull(position)) {
+ valueIsNull[positionCount] = true;
+ hasNullValue = true;
+ }
+ else {
+ int rawArrayBase = variableWidthBlock.getRawArrayBase();
+ int[] rawOffsets = variableWidthBlock.getRawOffsets();
+ int startValueOffset = rawOffsets[rawArrayBase + position];
+ int endValueOffset = rawOffsets[rawArrayBase + position + 1];
+ int length = endValueOffset - startValueOffset;
+ ensureFreeSpace(length);
+
+ Slice rawSlice = variableWidthBlock.getRawSlice();
+ byte[] rawByteArray = rawSlice.byteArray();
+ int byteArrayOffset = rawSlice.byteArrayOffset();
+
+ System.arraycopy(rawByteArray, byteArrayOffset + startValueOffset, bytes, offsets[positionCount], length);
+ bytesWritten = length;
+ hasNonNullValue = true;
+ }
+ offsets[positionCount + 1] = offsets[positionCount] + bytesWritten;
+ positionCount++;
+
+ if (blockBuilderStatus != null) {
+ blockBuilderStatus.addBytes(SIZE_IN_BYTES_PER_POSITION + bytesWritten);
+ }
}
- private void entryAdded(int bytesWritten, boolean isNull)
+ @Override
+ public void appendRepeated(ValueBlock block, int position, int count)
{
- if (valueIsNull.length <= positionCount) {
- growCapacity();
+ if (count == 0) {
+ return;
+ }
+ if (count == 1) {
+ append(block, position);
+ return;
}
- valueIsNull[positionCount] = isNull;
- offsets[positionCount + 1] = offsets[positionCount] + bytesWritten;
+ ensureCapacity(positionCount + count);
+
+ VariableWidthBlock variableWidthBlock = (VariableWidthBlock) block;
+ int bytesWritten = 0;
+ if (variableWidthBlock.isNull(position)) {
+ Arrays.fill(valueIsNull, positionCount, positionCount + count, true);
+ Arrays.fill(offsets, positionCount + 1, positionCount + count + 1, offsets[positionCount]);
+ hasNullValue = true;
+ }
+ else {
+ int rawArrayBase = variableWidthBlock.getRawArrayBase();
+ int[] rawOffsets = variableWidthBlock.getRawOffsets();
+ int startValueOffset = rawOffsets[rawArrayBase + position];
+ int endValueOffset = rawOffsets[rawArrayBase + position + 1];
+ int length = endValueOffset - startValueOffset;
+
+ if (length > 0) {
+ bytesWritten = toIntExact((long) length * count);
+ ensureFreeSpace(bytesWritten);
+
+ // copy in the value
+ Slice rawSlice = variableWidthBlock.getRawSlice();
+ byte[] rawByteArray = rawSlice.byteArray();
+ int byteArrayOffset = rawSlice.byteArrayOffset();
+
+ int currentOffset = offsets[positionCount];
+ System.arraycopy(rawByteArray, byteArrayOffset + startValueOffset, bytes, currentOffset, length);
+
+ // repeatedly duplicate the written vales, doubling the number of values copied each time
+ int duplicatedBytes = length;
+ while (duplicatedBytes * 2 <= bytesWritten) {
+ System.arraycopy(bytes, currentOffset, bytes, currentOffset + duplicatedBytes, duplicatedBytes);
+ duplicatedBytes = duplicatedBytes * 2;
+ }
+ // copy the remaining values
+ System.arraycopy(bytes, currentOffset, bytes, currentOffset + duplicatedBytes, bytesWritten - duplicatedBytes);
+
+ // set the offsets
+ int previousOffset = currentOffset;
+ for (int i = 0; i < count; i++) {
+ previousOffset += length;
+ offsets[positionCount + i + 1] = previousOffset;
+ }
+ }
+ else {
+ // zero length array
+ Arrays.fill(offsets, positionCount + 1, positionCount + count + 1, offsets[positionCount]);
+ }
+
+ hasNonNullValue = true;
+ }
+ positionCount += count;
- positionCount++;
- hasNonNullValue |= !isNull;
if (blockBuilderStatus != null) {
- blockBuilderStatus.addBytes(SIZE_OF_BYTE + SIZE_OF_INT + bytesWritten);
+ blockBuilderStatus.addBytes(count * SIZE_IN_BYTES_PER_POSITION + bytesWritten);
}
}
- private void growCapacity()
+ @Override
+ public void appendRange(ValueBlock block, int offset, int length)
{
- int newSize = calculateNewArraySize(valueIsNull.length, initialEntryCount);
- valueIsNull = Arrays.copyOf(valueIsNull, newSize);
- offsets = Arrays.copyOf(offsets, newSize + 1);
- updateRetainedSize();
+ if (length == 0) {
+ return;
+ }
+ if (length == 1) {
+ append(block, offset);
+ return;
+ }
+
+ ensureCapacity(positionCount + length);
+
+ VariableWidthBlock variableWidthBlock = (VariableWidthBlock) block;
+ int rawArrayBase = variableWidthBlock.getRawArrayBase();
+ int[] rawOffsets = variableWidthBlock.getRawOffsets();
+ int startValueOffset = rawOffsets[rawArrayBase + offset];
+ int totalSize = rawOffsets[rawArrayBase + offset + length] - startValueOffset;
+ // grow the buffer for the new data
+ ensureFreeSpace(totalSize);
+
+ Slice sourceSlice = variableWidthBlock.getRawSlice();
+ System.arraycopy(sourceSlice.byteArray(), sourceSlice.byteArrayOffset() + startValueOffset, bytes, offsets[positionCount], totalSize);
+
+ // update offsets for copied data
+ int offsetDelta = offsets[positionCount] - rawOffsets[rawArrayBase + offset];
+ for (int i = 0; i < length; i++) {
+ offsets[positionCount + i + 1] = rawOffsets[rawArrayBase + offset + i + 1] + offsetDelta;
+ }
+
+ // update nulls
+ boolean[] rawValueIsNull = variableWidthBlock.getRawValueIsNull();
+ if (rawValueIsNull != null) {
+ for (int i = 0; i < length; i++) {
+ if (rawValueIsNull[rawArrayBase + offset + i]) {
+ valueIsNull[positionCount + i] = true;
+ hasNullValue = true;
+ }
+ else {
+ hasNonNullValue = true;
+ }
+ }
+ }
+ else {
+ hasNonNullValue = true;
+ }
+ positionCount += length;
+
+ if (blockBuilderStatus != null) {
+ blockBuilderStatus.addBytes(length * SIZE_IN_BYTES_PER_POSITION);
+ }
}
- private void ensureFreeSpace(int extraBytesCapacity)
+ @Override
+ public void appendPositions(ValueBlock block, int[] positions, int offset, int length)
{
- int requiredSize = offsets[positionCount] + extraBytesCapacity;
- if (bytes.length < requiredSize) {
- int newBytesLength = max(bytes.length, initialSliceOutputSize);
- if (requiredSize > newBytesLength) {
- newBytesLength = max(requiredSize, calculateNewArraySize(newBytesLength));
+ if (length == 0) {
+ return;
+ }
+ if (length == 1) {
+ append(block, positions[offset]);
+ return;
+ }
+
+ ensureCapacity(positionCount + length);
+
+ VariableWidthBlock variableWidthBlock = (VariableWidthBlock) block;
+ int rawArrayBase = variableWidthBlock.getRawArrayBase();
+ int[] rawOffsets = variableWidthBlock.getRawOffsets();
+
+ // update the offsets and compute the total size
+ int initialOffset = offsets[positionCount];
+ int totalSize = 0;
+ for (int i = 0; i < length; i++) {
+ int position = positions[offset + i];
+ totalSize += rawOffsets[rawArrayBase + position + 1] - rawOffsets[rawArrayBase + position];
+ offsets[positionCount + i + 1] = initialOffset + totalSize;
+ }
+ // grow the buffer for the new data
+ ensureFreeSpace(totalSize);
+
+ // copy values to buffer
+ Slice rawSlice = variableWidthBlock.getRawSlice();
+ byte[] sourceBytes = rawSlice.byteArray();
+ int sourceBytesOffset = rawSlice.byteArrayOffset();
+ for (int i = 0; i < length; i++) {
+ int position = positions[offset + i];
+ int sourceStart = rawOffsets[rawArrayBase + position];
+ int sourceLength = rawOffsets[rawArrayBase + position + 1] - sourceStart;
+ System.arraycopy(sourceBytes, sourceBytesOffset + sourceStart, bytes, offsets[positionCount + i], sourceLength);
+ totalSize += sourceLength;
+ }
+
+ // update nulls
+ boolean[] rawValueIsNull = variableWidthBlock.getRawValueIsNull();
+ if (rawValueIsNull != null) {
+ for (int i = 0; i < length; i++) {
+ int rawPosition = positions[offset + i] + rawArrayBase;
+ if (rawValueIsNull[rawPosition]) {
+ valueIsNull[positionCount + i] = true;
+ hasNullValue = true;
+ }
+ else {
+ hasNonNullValue = true;
+ }
}
- bytes = Arrays.copyOf(bytes, newBytesLength);
- updateRetainedSize();
+ }
+ else {
+ hasNonNullValue = true;
+ }
+ positionCount += length;
+
+ if (blockBuilderStatus != null) {
+ blockBuilderStatus.addBytes(length * SIZE_IN_BYTES_PER_POSITION + totalSize);
}
}
- private void updateRetainedSize()
+ @Override
+ public BlockBuilder appendNull()
{
- arraysRetainedSizeInBytes = sizeOf(valueIsNull) + sizeOf(offsets);
+ hasNullValue = true;
+ entryAdded(0, true);
+ return this;
+ }
+
+ private void entryAdded(int bytesWritten, boolean isNull)
+ {
+ ensureCapacity(positionCount + 1);
+
+ valueIsNull[positionCount] = isNull;
+ offsets[positionCount + 1] = offsets[positionCount] + bytesWritten;
+
+ positionCount++;
+ hasNonNullValue |= !isNull;
+
+ if (blockBuilderStatus != null) {
+ blockBuilderStatus.addBytes(SIZE_IN_BYTES_PER_POSITION + bytesWritten);
+ }
}
@Override
@@ -184,6 +376,35 @@ private int getOffset(int position)
return offsets[position];
}
+ private void ensureCapacity(int capacity)
+ {
+ if (valueIsNull.length >= capacity) {
+ return;
+ }
+
+ int newSize = calculateNewArraySize(capacity, initialEntryCount);
+ valueIsNull = Arrays.copyOf(valueIsNull, newSize);
+ offsets = Arrays.copyOf(offsets, newSize + 1);
+ updateRetainedSize();
+ }
+
+ private void ensureFreeSpace(int extraBytesCapacity)
+ {
+ int requiredSize = offsets[positionCount] + extraBytesCapacity;
+ if (bytes.length >= requiredSize) {
+ return;
+ }
+
+ int newSize = calculateNewArraySize(requiredSize, initialSliceOutputSize);
+ bytes = Arrays.copyOf(bytes, newSize);
+ updateRetainedSize();
+ }
+
+ private void updateRetainedSize()
+ {
+ arraysRetainedSizeInBytes = sizeOf(valueIsNull) + sizeOf(offsets) + sizeOf(bytes);
+ }
+
@Override
public String toString()
{
diff --git a/core/trino-spi/src/test/java/io/trino/spi/block/AbstractTestBlockBuilder.java b/core/trino-spi/src/test/java/io/trino/spi/block/AbstractTestBlockBuilder.java
new file mode 100644
index 000000000000..bcc0057aded7
--- /dev/null
+++ b/core/trino-spi/src/test/java/io/trino/spi/block/AbstractTestBlockBuilder.java
@@ -0,0 +1,236 @@
+/*
+ * 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 io.trino.spi.block;
+
+import com.google.common.collect.Iterables;
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static java.util.Collections.nCopies;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public abstract class AbstractTestBlockBuilder
+{
+ protected abstract BlockBuilder createBlockBuilder();
+
+ protected abstract List getTestValues();
+
+ protected abstract T getUnusedTestValue();
+
+ protected abstract ValueBlock blockFromValues(Iterable values);
+
+ protected abstract List blockToValues(ValueBlock valueBlock);
+
+ @Test
+ public void verifyTestData()
+ {
+ List values = getTestValues();
+ assertThat(values)
+ .hasSize(5)
+ .doesNotHaveDuplicates()
+ .doesNotContainNull()
+ .doesNotContain(getUnusedTestValue());
+
+ ValueBlock valueBlock = blockFromValues(values);
+ assertThat(blockToValues(valueBlock)).isEqualTo(values);
+ }
+
+ @Test
+ public void testAppend()
+ {
+ List values = getTestValues();
+ ValueBlock inputValues = createOffsetBlock(values);
+
+ BlockBuilder blockBuilder = createBlockBuilder();
+ blockBuilder.append(inputValues, 1);
+ blockBuilder.append(inputValues, 3);
+ blockBuilder.append(inputValues, 1);
+ blockBuilder.append(inputValues, 3);
+ ValueBlock valueBlock = blockBuilder.buildValueBlock();
+
+ assertThat(valueBlock.mayHaveNull()).isFalse();
+
+ List actualValues = blockToValues(valueBlock);
+ assertThat(actualValues).containsExactly(values.get(1), values.get(3), values.get(1), values.get(3));
+ }
+
+ @Test
+ public void testAppendWithNulls()
+ {
+ List values = getTestValues();
+ ValueBlock inputValues = createOffsetBlockWithOddPositionsNull(values);
+
+ BlockBuilder blockBuilder = createBlockBuilder();
+ blockBuilder.append(inputValues, 1);
+ blockBuilder.append(inputValues, 3);
+ ValueBlock valueBlock = blockBuilder.buildValueBlock();
+
+ assertThat(valueBlock.mayHaveNull()).isTrue();
+
+ List actualValues = blockToValues(valueBlock);
+ assertThat(actualValues).hasSize(2).containsOnlyNulls();
+
+ // add a non-null value
+ blockBuilder.append(inputValues, 2);
+ valueBlock = blockBuilder.buildValueBlock();
+
+ actualValues = blockToValues(valueBlock);
+ assertThat(actualValues).containsExactly(null, null, values.get(2));
+ }
+
+ @Test
+ public void testAppendRepeated()
+ {
+ List values = getTestValues();
+ ValueBlock inputValues = createOffsetBlock(values);
+
+ BlockBuilder blockBuilder = createBlockBuilder();
+ blockBuilder.appendRepeated(inputValues, 1, 10);
+ blockBuilder.appendRepeated(inputValues, 3, 10);
+ ValueBlock valueBlock = blockBuilder.buildValueBlock();
+
+ assertThat(valueBlock.mayHaveNull()).isFalse();
+
+ List actualValues = blockToValues(valueBlock);
+ assertThat(actualValues).containsExactlyElementsOf(Iterables.concat(nCopies(10, values.get(1)), nCopies(10, values.get(3))));
+ }
+
+ @Test
+ public void testAppendRepeatedWithNulls()
+ {
+ List values = getTestValues();
+ ValueBlock inputValues = createOffsetBlockWithOddPositionsNull(values);
+
+ BlockBuilder blockBuilder = createBlockBuilder();
+ blockBuilder.appendRepeated(inputValues, 1, 10);
+ blockBuilder.appendRepeated(inputValues, 3, 10);
+ ValueBlock valueBlock = blockBuilder.buildValueBlock();
+
+ assertThat(valueBlock.mayHaveNull()).isTrue();
+
+ List actualValues = blockToValues(valueBlock);
+ assertThat(actualValues).hasSize(20).containsOnlyNulls();
+
+ // an all-null block should be converted to a RunLengthEncodedBlock
+ assertThat(blockBuilder.build()).isInstanceOf(RunLengthEncodedBlock.class);
+
+ // add some non-null values
+ blockBuilder.appendRepeated(inputValues, 2, 10);
+ valueBlock = blockBuilder.buildValueBlock();
+
+ actualValues = blockToValues(valueBlock);
+ assertThat(actualValues).containsExactlyElementsOf(Iterables.concat(nCopies(20, null), nCopies(10, values.get(2))));
+ }
+
+ @Test
+ public void testAppendRange()
+ {
+ List values = getTestValues();
+ ValueBlock inputValues = createOffsetBlock(values);
+
+ BlockBuilder blockBuilder = createBlockBuilder();
+ blockBuilder.appendRange(inputValues, 1, 3);
+ blockBuilder.appendRange(inputValues, 2, 3);
+ ValueBlock valueBlock = blockBuilder.buildValueBlock();
+
+ assertThat(valueBlock.mayHaveNull()).isFalse();
+
+ List actualValues = blockToValues(valueBlock);
+ assertThat(actualValues).containsExactlyElementsOf(Iterables.concat(values.subList(1, 4), values.subList(2, 5)));
+ }
+
+ @Test
+ public void testAppendRangeWithNulls()
+ {
+ List values = getTestValues();
+ ValueBlock inputValues = createOffsetBlockWithOddPositionsNull(values);
+
+ BlockBuilder blockBuilder = createBlockBuilder();
+ blockBuilder.appendRange(inputValues, 1, 3);
+ blockBuilder.appendRange(inputValues, 2, 3);
+ ValueBlock valueBlock = blockBuilder.buildValueBlock();
+
+ assertThat(valueBlock.mayHaveNull()).isTrue();
+
+ List actualValues = blockToValues(valueBlock);
+ assertThat(actualValues).containsExactly(null, values.get(2), null, values.get(2), null, values.get(4));
+ }
+
+ @Test
+ public void testAppendPositions()
+ {
+ List values = getTestValues();
+ ValueBlock inputValues = createOffsetBlock(values);
+
+ BlockBuilder blockBuilder = createBlockBuilder();
+ blockBuilder.appendPositions(inputValues, new int[] {-100, 1, 3, 2, -100}, 1, 3);
+ blockBuilder.appendPositions(inputValues, new int[] {-100, 4, 0, -100}, 1, 2);
+ ValueBlock valueBlock = blockBuilder.buildValueBlock();
+
+ assertThat(valueBlock.mayHaveNull()).isFalse();
+
+ List actualValues = blockToValues(valueBlock);
+ assertThat(actualValues).containsExactly(values.get(1), values.get(3), values.get(2), values.get(4), values.get(0));
+ }
+
+ @Test
+ public void testAppendPositionsWithNull()
+ {
+ List values = getTestValues();
+ ValueBlock inputValues = createOffsetBlockWithOddPositionsNull(values);
+
+ BlockBuilder blockBuilder = createBlockBuilder();
+ blockBuilder.appendPositions(inputValues, new int[] {-100, 1, 3, 2, -100}, 1, 3);
+ blockBuilder.appendPositions(inputValues, new int[] {-100, 4, 0, -100}, 1, 2);
+ ValueBlock valueBlock = blockBuilder.buildValueBlock();
+
+ assertThat(valueBlock.mayHaveNull()).isTrue();
+
+ List actualValues = blockToValues(valueBlock);
+ assertThat(actualValues).containsExactly(null, null, values.get(2), values.get(4), values.get(0));
+ }
+
+ /**
+ * Create a block that is offset from the start of the underlying array
+ */
+ private ValueBlock createOffsetBlock(List values)
+ {
+ return blockFromValues(Iterables.concat(nCopies(2, getUnusedTestValue()), values, nCopies(2, getUnusedTestValue())))
+ .getRegion(2, values.size());
+ }
+
+ /**
+ * Create a block that is offset from the start of the underlying array
+ */
+ private ValueBlock createOffsetBlockWithOddPositionsNull(List values)
+ {
+ ArrayList blockValues = new ArrayList<>();
+ blockValues.add(getUnusedTestValue());
+ blockValues.add(getUnusedTestValue());
+ for (int i = 0; i < values.size(); i++) {
+ T value = values.get(i);
+ if (i % 2 == 0) {
+ blockValues.add(value);
+ }
+ else {
+ blockValues.add(null);
+ }
+ }
+ blockValues.add(getUnusedTestValue());
+ blockValues.add(getUnusedTestValue());
+ return blockFromValues(blockValues).getRegion(2, values.size());
+ }
+}
diff --git a/core/trino-spi/src/test/java/io/trino/spi/block/TestArrayBlockBuilder.java b/core/trino-spi/src/test/java/io/trino/spi/block/TestArrayBlockBuilder.java
index 511961cb8260..689504f8f927 100644
--- a/core/trino-spi/src/test/java/io/trino/spi/block/TestArrayBlockBuilder.java
+++ b/core/trino-spi/src/test/java/io/trino/spi/block/TestArrayBlockBuilder.java
@@ -15,13 +15,19 @@
import org.junit.jupiter.api.Test;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
import static io.airlift.slice.SizeOf.instanceSize;
import static io.trino.spi.type.BigintType.BIGINT;
+import static io.trino.spi.type.VarcharType.VARCHAR;
import static java.lang.Long.BYTES;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
public class TestArrayBlockBuilder
+ extends AbstractTestBlockBuilder>
{
// ArrayBlockBuilder: isNull, offset, 3 * value (FixedWidthBlockBuilder: isNull, value)
private static final int THREE_INTS_ENTRY_SIZE = Byte.BYTES + Integer.BYTES + 3 * (Byte.BYTES + Long.BYTES);
@@ -107,4 +113,77 @@ private static void assertIsAllNulls(Block block, int expectedPositionCount)
assertThat(block.isNull(0)).isTrue();
}
}
+
+ @Override
+ protected BlockBuilder createBlockBuilder()
+ {
+ return new ArrayBlockBuilder(new VariableWidthBlockBuilder(null, 1, 100), null, 1);
+ }
+
+ @Override
+ protected List> getTestValues()
+ {
+ return List.of(
+ List.of("a", "apple", "ape"),
+ Arrays.asList("b", null, "bear", "break"),
+ List.of("c", "cherry"),
+ Arrays.asList("d", "date", "dinosaur", null, "dirt"),
+ List.of("e", "eggplant", "empty", ""));
+ }
+
+ @Override
+ protected List getUnusedTestValue()
+ {
+ return List.of("unused", "ignore me");
+ }
+
+ @Override
+ protected ValueBlock blockFromValues(Iterable> values)
+ {
+ ArrayBlockBuilder blockBuilder = new ArrayBlockBuilder(new VariableWidthBlockBuilder(null, 1, 100), null, 1);
+ for (List array : values) {
+ if (array == null) {
+ blockBuilder.appendNull();
+ }
+ else {
+ blockBuilder.buildEntry(elementBuilder -> {
+ for (String entry : array) {
+ if (entry == null) {
+ elementBuilder.appendNull();
+ }
+ else {
+ VARCHAR.writeString(elementBuilder, entry);
+ }
+ }
+ });
+ }
+ }
+ return blockBuilder.buildValueBlock();
+ }
+
+ @Override
+ protected List> blockToValues(ValueBlock valueBlock)
+ {
+ ArrayBlock block = (ArrayBlock) valueBlock;
+ List> actualValues = new ArrayList<>(block.getPositionCount());
+ for (int i = 0; i < block.getPositionCount(); i++) {
+ if (block.isNull(i)) {
+ actualValues.add(null);
+ }
+ else {
+ Block array = block.getArray(i);
+ ArrayList arrayBuilder = new ArrayList<>();
+ for (int j = 0; j < array.getPositionCount(); j++) {
+ if (array.isNull(j)) {
+ arrayBuilder.add(null);
+ }
+ else {
+ arrayBuilder.add(VARCHAR.getSlice(array, j).toStringUtf8());
+ }
+ }
+ actualValues.add(arrayBuilder);
+ }
+ }
+ return actualValues;
+ }
}
diff --git a/core/trino-spi/src/test/java/io/trino/spi/block/TestByteArrayBlockBuilder.java b/core/trino-spi/src/test/java/io/trino/spi/block/TestByteArrayBlockBuilder.java
new file mode 100644
index 000000000000..91463d2a473f
--- /dev/null
+++ b/core/trino-spi/src/test/java/io/trino/spi/block/TestByteArrayBlockBuilder.java
@@ -0,0 +1,70 @@
+/*
+ * 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 io.trino.spi.block;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class TestByteArrayBlockBuilder
+ extends AbstractTestBlockBuilder
+{
+ @Override
+ protected BlockBuilder createBlockBuilder()
+ {
+ return new ByteArrayBlockBuilder(null, 1);
+ }
+
+ @Override
+ protected List getTestValues()
+ {
+ return List.of((byte) 10, (byte) 11, (byte) 12, (byte) 13, (byte) 14);
+ }
+
+ @Override
+ protected Byte getUnusedTestValue()
+ {
+ return (byte) -1;
+ }
+
+ @Override
+ protected ValueBlock blockFromValues(Iterable values)
+ {
+ ByteArrayBlockBuilder blockBuilder = new ByteArrayBlockBuilder(null, 1);
+ for (Byte value : values) {
+ if (value == null) {
+ blockBuilder.appendNull();
+ }
+ else {
+ blockBuilder.writeByte(value);
+ }
+ }
+ return blockBuilder.buildValueBlock();
+ }
+
+ @Override
+ protected List blockToValues(ValueBlock valueBlock)
+ {
+ ByteArrayBlock byteArrayBlock = (ByteArrayBlock) valueBlock;
+ List actualValues = new ArrayList<>(byteArrayBlock.getPositionCount());
+ for (int i = 0; i < byteArrayBlock.getPositionCount(); i++) {
+ if (byteArrayBlock.isNull(i)) {
+ actualValues.add(null);
+ }
+ else {
+ actualValues.add(byteArrayBlock.getByte(i));
+ }
+ }
+ return actualValues;
+ }
+}
diff --git a/core/trino-spi/src/test/java/io/trino/spi/block/TestFixed12BlockBuilder.java b/core/trino-spi/src/test/java/io/trino/spi/block/TestFixed12BlockBuilder.java
new file mode 100644
index 000000000000..392125972bfa
--- /dev/null
+++ b/core/trino-spi/src/test/java/io/trino/spi/block/TestFixed12BlockBuilder.java
@@ -0,0 +1,72 @@
+/*
+ * 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 io.trino.spi.block;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class TestFixed12BlockBuilder
+ extends AbstractTestBlockBuilder
+{
+ @Override
+ protected BlockBuilder createBlockBuilder()
+ {
+ return new Fixed12BlockBuilder(null, 1);
+ }
+
+ @Override
+ protected List getTestValues()
+ {
+ return List.of(new Fixed12(90, 10), new Fixed12(91, 11), new Fixed12(92, 12), new Fixed12(93, 13), new Fixed12(94, 14));
+ }
+
+ @Override
+ protected Fixed12 getUnusedTestValue()
+ {
+ return new Fixed12(-1, -2);
+ }
+
+ @Override
+ protected ValueBlock blockFromValues(Iterable values)
+ {
+ Fixed12BlockBuilder blockBuilder = new Fixed12BlockBuilder(null, 1);
+ for (Fixed12 value : values) {
+ if (value == null) {
+ blockBuilder.appendNull();
+ }
+ else {
+ blockBuilder.writeFixed12(value.first(), value.second());
+ }
+ }
+ return blockBuilder.buildValueBlock();
+ }
+
+ @Override
+ protected List blockToValues(ValueBlock valueBlock)
+ {
+ Fixed12Block fixed12ArrayBlock = (Fixed12Block) valueBlock;
+ List actualValues = new ArrayList<>(fixed12ArrayBlock.getPositionCount());
+ for (int i = 0; i < fixed12ArrayBlock.getPositionCount(); i++) {
+ if (fixed12ArrayBlock.isNull(i)) {
+ actualValues.add(null);
+ }
+ else {
+ actualValues.add(new Fixed12(fixed12ArrayBlock.getFixed12First(i), fixed12ArrayBlock.getFixed12Second(i)));
+ }
+ }
+ return actualValues;
+ }
+
+ public record Fixed12(long first, int second) {}
+}
diff --git a/core/trino-spi/src/test/java/io/trino/spi/block/TestInt128ArrayBlockBuilder.java b/core/trino-spi/src/test/java/io/trino/spi/block/TestInt128ArrayBlockBuilder.java
new file mode 100644
index 000000000000..e25fe5af37f8
--- /dev/null
+++ b/core/trino-spi/src/test/java/io/trino/spi/block/TestInt128ArrayBlockBuilder.java
@@ -0,0 +1,72 @@
+/*
+ * 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 io.trino.spi.block;
+
+import io.trino.spi.type.Int128;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class TestInt128ArrayBlockBuilder
+ extends AbstractTestBlockBuilder
+{
+ @Override
+ protected BlockBuilder createBlockBuilder()
+ {
+ return new Int128ArrayBlockBuilder(null, 1);
+ }
+
+ @Override
+ protected List getTestValues()
+ {
+ return List.of(Int128.valueOf(90, 10), Int128.valueOf(91, 11), Int128.valueOf(92, 12), Int128.valueOf(93, 13), Int128.valueOf(94, 14));
+ }
+
+ @Override
+ protected Int128 getUnusedTestValue()
+ {
+ return Int128.valueOf(-1, -2);
+ }
+
+ @Override
+ protected ValueBlock blockFromValues(Iterable values)
+ {
+ Int128ArrayBlockBuilder blockBuilder = new Int128ArrayBlockBuilder(null, 1);
+ for (Int128 value : values) {
+ if (value == null) {
+ blockBuilder.appendNull();
+ }
+ else {
+ blockBuilder.writeInt128(value.getHigh(), value.getLow());
+ }
+ }
+ return blockBuilder.buildValueBlock();
+ }
+
+ @Override
+ protected List blockToValues(ValueBlock valueBlock)
+ {
+ Int128ArrayBlock int128ArrayBlock = (Int128ArrayBlock) valueBlock;
+ List actualValues = new ArrayList<>(int128ArrayBlock.getPositionCount());
+ for (int i = 0; i < int128ArrayBlock.getPositionCount(); i++) {
+ if (int128ArrayBlock.isNull(i)) {
+ actualValues.add(null);
+ }
+ else {
+ actualValues.add(int128ArrayBlock.getInt128(i));
+ }
+ }
+ return actualValues;
+ }
+}
diff --git a/core/trino-spi/src/test/java/io/trino/spi/block/TestIntArrayBlockBuilder.java b/core/trino-spi/src/test/java/io/trino/spi/block/TestIntArrayBlockBuilder.java
new file mode 100644
index 000000000000..9996772166fd
--- /dev/null
+++ b/core/trino-spi/src/test/java/io/trino/spi/block/TestIntArrayBlockBuilder.java
@@ -0,0 +1,70 @@
+/*
+ * 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 io.trino.spi.block;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class TestIntArrayBlockBuilder
+ extends AbstractTestBlockBuilder
+{
+ @Override
+ protected BlockBuilder createBlockBuilder()
+ {
+ return new IntArrayBlockBuilder(null, 1);
+ }
+
+ @Override
+ protected List getTestValues()
+ {
+ return List.of(10, 11, 12, 13, 14);
+ }
+
+ @Override
+ protected Integer getUnusedTestValue()
+ {
+ return -1;
+ }
+
+ @Override
+ protected ValueBlock blockFromValues(Iterable values)
+ {
+ IntArrayBlockBuilder blockBuilder = new IntArrayBlockBuilder(null, 1);
+ for (Integer value : values) {
+ if (value == null) {
+ blockBuilder.appendNull();
+ }
+ else {
+ blockBuilder.writeInt(value);
+ }
+ }
+ return blockBuilder.buildValueBlock();
+ }
+
+ @Override
+ protected List blockToValues(ValueBlock valueBlock)
+ {
+ IntArrayBlock intArrayBlock = (IntArrayBlock) valueBlock;
+ List actualValues = new ArrayList<>(intArrayBlock.getPositionCount());
+ for (int i = 0; i < intArrayBlock.getPositionCount(); i++) {
+ if (intArrayBlock.isNull(i)) {
+ actualValues.add(null);
+ }
+ else {
+ actualValues.add(intArrayBlock.getInt(i));
+ }
+ }
+ return actualValues;
+ }
+}
diff --git a/core/trino-spi/src/test/java/io/trino/spi/block/TestLongArrayBlockBuilder.java b/core/trino-spi/src/test/java/io/trino/spi/block/TestLongArrayBlockBuilder.java
new file mode 100644
index 000000000000..88766a0f3e67
--- /dev/null
+++ b/core/trino-spi/src/test/java/io/trino/spi/block/TestLongArrayBlockBuilder.java
@@ -0,0 +1,70 @@
+/*
+ * 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 io.trino.spi.block;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class TestLongArrayBlockBuilder
+ extends AbstractTestBlockBuilder
+{
+ @Override
+ protected BlockBuilder createBlockBuilder()
+ {
+ return new LongArrayBlockBuilder(null, 1);
+ }
+
+ @Override
+ protected List getTestValues()
+ {
+ return List.of(10L, 11L, 12L, 13L, 14L);
+ }
+
+ @Override
+ protected Long getUnusedTestValue()
+ {
+ return -1L;
+ }
+
+ @Override
+ protected ValueBlock blockFromValues(Iterable values)
+ {
+ LongArrayBlockBuilder blockBuilder = new LongArrayBlockBuilder(null, 1);
+ for (Long value : values) {
+ if (value == null) {
+ blockBuilder.appendNull();
+ }
+ else {
+ blockBuilder.writeLong(value);
+ }
+ }
+ return blockBuilder.buildValueBlock();
+ }
+
+ @Override
+ protected List blockToValues(ValueBlock valueBlock)
+ {
+ LongArrayBlock longArrayBlock = (LongArrayBlock) valueBlock;
+ List actualValues = new ArrayList<>(longArrayBlock.getPositionCount());
+ for (int i = 0; i < longArrayBlock.getPositionCount(); i++) {
+ if (longArrayBlock.isNull(i)) {
+ actualValues.add(null);
+ }
+ else {
+ actualValues.add(longArrayBlock.getLong(i));
+ }
+ }
+ return actualValues;
+ }
+}
diff --git a/core/trino-spi/src/test/java/io/trino/spi/block/TestMapBlockBuilder.java b/core/trino-spi/src/test/java/io/trino/spi/block/TestMapBlockBuilder.java
new file mode 100644
index 000000000000..137d0cc6c2cb
--- /dev/null
+++ b/core/trino-spi/src/test/java/io/trino/spi/block/TestMapBlockBuilder.java
@@ -0,0 +1,101 @@
+/*
+ * 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 io.trino.spi.block;
+
+import io.trino.spi.type.MapType;
+import io.trino.spi.type.TypeOperators;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static io.trino.spi.type.IntegerType.INTEGER;
+import static io.trino.spi.type.VarcharType.VARCHAR;
+
+public class TestMapBlockBuilder
+ extends AbstractTestBlockBuilder