diff --git a/CHANGELOG.md b/CHANGELOG.md index 6fb1c3e79..31ffbb5d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -117,6 +117,7 @@ All notable changes to this project will be documented in this file. - nifi: Bump Hazelcast to `5.7.0` (partial backport of [NIFI-15942](https://issues.apache.org/jira/browse/NIFI-15942)) to fix CVE-2026-107726, for `2.6.0` and `2.9.0` ([#1737]). - superset: Bump brotli to `1.2.1` to fix CVE-2026-111497 ([#1737]). - hbase: Add `hadoop-azure` to the HBase `lib` directory again, so that `abfs://` works. This regressed in 26.7.0 with [#1511] ([#1731]). +- trino: Backport [trinodb/trino#26806](https://github.com/trinodb/trino/pull/26806) to `477` to fix `appendPositions` on `RowBlockBuilder` ignoring the offset ([#1738]). ### Removed @@ -203,6 +204,7 @@ All notable changes to this project will be documented in this file. [#1732]: https://github.com/stackabletech/docker-images/pull/1732 [#1735]: https://github.com/stackabletech/docker-images/pull/1735 [#1737]: https://github.com/stackabletech/docker-images/pull/1737 +[#1738]: https://github.com/stackabletech/docker-images/pull/1738 ## [26.7.0] - 2026-07-21 diff --git a/trino/trino/stackable/patches/477/0005-Fix-an-issue-on-appendPositions-on-RowBlockBuilder.patch b/trino/trino/stackable/patches/477/0005-Fix-an-issue-on-appendPositions-on-RowBlockBuilder.patch new file mode 100644 index 000000000..ac02fa591 --- /dev/null +++ b/trino/trino/stackable/patches/477/0005-Fix-an-issue-on-appendPositions-on-RowBlockBuilder.patch @@ -0,0 +1,363 @@ +From 9d95866bf8e9e05416ee878923308f143dbd128e Mon Sep 17 00:00:00 2001 +From: Razvan-Daniel Mihai <84674+razvan@users.noreply.github.com> +Date: Mon, 9 Nov 2026 10:19:25 +0100 +Subject: Fix an issue on appendPositions on RowBlockBuilder + +This is a backport of https://github.com/trinodb/trino/pull/26806 +--- + .../io/trino/spi/block/RowBlockBuilder.java | 2 +- + .../spi/block/AbstractTestBlockBuilder.java | 220 ++++++++++-------- + 2 files changed, 122 insertions(+), 100 deletions(-) + +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 b7c4f88068..acdb2d4b06 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 +@@ -276,7 +276,7 @@ public class RowBlockBuilder + + if (startOffset == 0) { + for (int fieldId = 0; fieldId < fieldBlockBuilders.length; fieldId++) { +- appendPositionsToField(rawFieldBlocks[fieldId], positions, 0, length, fieldBlockBuilders[fieldId]); ++ appendPositionsToField(rawFieldBlocks[fieldId], positions, offset, length, fieldBlockBuilders[fieldId]); + } + } + else { +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 +index ceca56bae0..28ec61b07c 100644 +--- 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 +@@ -24,6 +24,8 @@ import static org.assertj.core.api.Assertions.assertThat; + + public abstract class AbstractTestBlockBuilder + { ++ private static final int[] OFFSETS = new int[] {0, 2}; ++ + protected abstract BlockBuilder createBlockBuilder(); + + protected abstract List getTestValues(); +@@ -52,197 +54,216 @@ public abstract class AbstractTestBlockBuilder + public void testAppend() + { + List values = getTestValues(); +- ValueBlock inputValues = createOffsetBlock(values); ++ for (int offset : OFFSETS) { ++ ValueBlock inputValues = createOffsetBlock(values, offset); + +- BlockBuilder blockBuilder = createBlockBuilder(); +- blockBuilder.append(inputValues, 1); +- blockBuilder.append(inputValues, 3); +- blockBuilder.append(inputValues, 1); +- blockBuilder.append(inputValues, 3); +- ValueBlock valueBlock = blockBuilder.buildValueBlock(); ++ 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(); ++ assertThat(valueBlock.mayHaveNull()).isFalse(); + +- List actualValues = blockToValues(valueBlock); +- assertThat(actualValues).containsExactly(values.get(1), values.get(3), values.get(1), values.get(3)); ++ 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); ++ for (int offset : OFFSETS) { ++ ValueBlock inputValues = createOffsetBlockWithOddPositionsNull(values, offset); + +- BlockBuilder blockBuilder = createBlockBuilder(); +- blockBuilder.append(inputValues, 1); +- blockBuilder.append(inputValues, 3); +- ValueBlock valueBlock = blockBuilder.buildValueBlock(); ++ BlockBuilder blockBuilder = createBlockBuilder(); ++ blockBuilder.append(inputValues, 1); ++ blockBuilder.append(inputValues, 3); ++ ValueBlock valueBlock = blockBuilder.buildValueBlock(); + +- assertThat(valueBlock.mayHaveNull()).isTrue(); ++ assertThat(valueBlock.mayHaveNull()).isTrue(); + +- List actualValues = blockToValues(valueBlock); +- assertThat(actualValues).hasSize(2).containsOnlyNulls(); ++ List actualValues = blockToValues(valueBlock); ++ assertThat(actualValues).hasSize(2).containsOnlyNulls(); + +- // add a non-null value +- blockBuilder.append(inputValues, 2); +- valueBlock = blockBuilder.buildValueBlock(); ++ // add a non-null value ++ blockBuilder.append(inputValues, 2); ++ valueBlock = blockBuilder.buildValueBlock(); + +- actualValues = blockToValues(valueBlock); +- assertThat(actualValues).containsExactly(null, null, values.get(2)); ++ actualValues = blockToValues(valueBlock); ++ assertThat(actualValues).containsExactly(null, null, values.get(2)); ++ } + } + + @Test + public void testAppendRepeated() + { + List values = getTestValues(); +- ValueBlock inputValues = createOffsetBlock(values); ++ for (int offset : OFFSETS) { ++ ValueBlock inputValues = createOffsetBlock(values, offset); + +- BlockBuilder blockBuilder = createBlockBuilder(); +- blockBuilder.appendRepeated(inputValues, 1, 10); +- blockBuilder.appendRepeated(inputValues, 3, 10); +- ValueBlock valueBlock = blockBuilder.buildValueBlock(); ++ BlockBuilder blockBuilder = createBlockBuilder(); ++ blockBuilder.appendRepeated(inputValues, 1, 10); ++ blockBuilder.appendRepeated(inputValues, 3, 10); ++ ValueBlock valueBlock = blockBuilder.buildValueBlock(); + +- assertThat(valueBlock.mayHaveNull()).isFalse(); ++ assertThat(valueBlock.mayHaveNull()).isFalse(); + +- List actualValues = blockToValues(valueBlock); +- assertThat(actualValues).containsExactlyElementsOf(Iterables.concat(nCopies(10, values.get(1)), nCopies(10, values.get(3)))); ++ 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); ++ for (int offset : OFFSETS) { ++ ValueBlock inputValues = createOffsetBlockWithOddPositionsNull(values, offset); + +- BlockBuilder blockBuilder = createBlockBuilder(); +- blockBuilder.appendRepeated(inputValues, 1, 10); +- blockBuilder.appendRepeated(inputValues, 3, 10); +- ValueBlock valueBlock = blockBuilder.buildValueBlock(); ++ BlockBuilder blockBuilder = createBlockBuilder(); ++ blockBuilder.appendRepeated(inputValues, 1, 10); ++ blockBuilder.appendRepeated(inputValues, 3, 10); ++ ValueBlock valueBlock = blockBuilder.buildValueBlock(); + +- assertThat(valueBlock.mayHaveNull()).isTrue(); ++ assertThat(valueBlock.mayHaveNull()).isTrue(); + +- List actualValues = blockToValues(valueBlock); +- assertThat(actualValues).hasSize(20).containsOnlyNulls(); ++ 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); ++ // 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(); ++ // 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)))); ++ 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); ++ for (int offset : OFFSETS) { ++ ValueBlock inputValues = createOffsetBlock(values, offset); + +- BlockBuilder blockBuilder = createBlockBuilder(); +- blockBuilder.appendRange(inputValues, 1, 3); +- blockBuilder.appendRange(inputValues, 2, 3); +- ValueBlock valueBlock = blockBuilder.buildValueBlock(); ++ BlockBuilder blockBuilder = createBlockBuilder(); ++ blockBuilder.appendRange(inputValues, 1, 3); ++ blockBuilder.appendRange(inputValues, 2, 3); ++ ValueBlock valueBlock = blockBuilder.buildValueBlock(); + +- assertThat(valueBlock.mayHaveNull()).isFalse(); ++ assertThat(valueBlock.mayHaveNull()).isFalse(); + +- List actualValues = blockToValues(valueBlock); +- assertThat(actualValues).containsExactlyElementsOf(Iterables.concat(values.subList(1, 4), values.subList(2, 5))); ++ 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); ++ for (int offset : OFFSETS) { ++ ValueBlock inputValues = createOffsetBlockWithOddPositionsNull(values, offset); + +- BlockBuilder blockBuilder = createBlockBuilder(); +- blockBuilder.appendRange(inputValues, 1, 3); +- blockBuilder.appendRange(inputValues, 2, 3); +- ValueBlock valueBlock = blockBuilder.buildValueBlock(); ++ BlockBuilder blockBuilder = createBlockBuilder(); ++ blockBuilder.appendRange(inputValues, 1, 3); ++ blockBuilder.appendRange(inputValues, 2, 3); ++ ValueBlock valueBlock = blockBuilder.buildValueBlock(); + +- assertThat(valueBlock.mayHaveNull()).isTrue(); ++ assertThat(valueBlock.mayHaveNull()).isTrue(); + +- List actualValues = blockToValues(valueBlock); +- assertThat(actualValues).containsExactly(null, values.get(2), null, values.get(2), null, values.get(4)); ++ 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); ++ for (int offset : OFFSETS) { ++ ValueBlock inputValues = createOffsetBlock(values, offset); + +- 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(); ++ 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(); ++ 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)); ++ 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); ++ for (int offset : OFFSETS) { ++ ValueBlock inputValues = createOffsetBlockWithOddPositionsNull(values, offset); + +- 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(); ++ 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(); ++ assertThat(valueBlock.mayHaveNull()).isTrue(); + +- List actualValues = blockToValues(valueBlock); +- assertThat(actualValues).containsExactly(null, null, values.get(2), values.get(4), values.get(0)); ++ List actualValues = blockToValues(valueBlock); ++ assertThat(actualValues).containsExactly(null, null, values.get(2), values.get(4), values.get(0)); ++ } + } + + @Test + public void testResetTo() + { + List values = getTestValues(); +- ValueBlock inputValues = createOffsetBlock(values); ++ for (int offset : OFFSETS) { ++ ValueBlock inputValues = createOffsetBlock(values, offset); + +- BlockBuilder blockBuilder = createBlockBuilder(); +- blockBuilder.appendRange(inputValues, 0, inputValues.getPositionCount()); +- assertThat(blockToValues(blockBuilder.buildValueBlock())).containsExactlyElementsOf(values); ++ BlockBuilder blockBuilder = createBlockBuilder(); ++ blockBuilder.appendRange(inputValues, 0, inputValues.getPositionCount()); ++ assertThat(blockToValues(blockBuilder.buildValueBlock())).containsExactlyElementsOf(values); + +- blockBuilder.resetTo(4); +- assertThat(blockToValues(blockBuilder.buildValueBlock())).containsExactlyElementsOf(values.subList(0, 4)); ++ blockBuilder.resetTo(4); ++ assertThat(blockToValues(blockBuilder.buildValueBlock())).containsExactlyElementsOf(values.subList(0, 4)); + +- blockBuilder.appendRange(inputValues, 0, inputValues.getPositionCount()); +- assertThat(blockToValues(blockBuilder.buildValueBlock())).containsExactlyElementsOf(Iterables.concat(values.subList(0, 4), values)); ++ blockBuilder.appendRange(inputValues, 0, inputValues.getPositionCount()); ++ assertThat(blockToValues(blockBuilder.buildValueBlock())).containsExactlyElementsOf(Iterables.concat(values.subList(0, 4), values)); + +- blockBuilder.resetTo(0); +- assertThat(blockToValues(blockBuilder.buildValueBlock())).isEmpty(); ++ blockBuilder.resetTo(0); ++ assertThat(blockToValues(blockBuilder.buildValueBlock())).isEmpty(); + +- blockBuilder.appendRange(inputValues, 0, inputValues.getPositionCount()); +- assertThat(blockToValues(blockBuilder.buildValueBlock())).containsExactlyElementsOf(values); ++ blockBuilder.appendRange(inputValues, 0, inputValues.getPositionCount()); ++ assertThat(blockToValues(blockBuilder.buildValueBlock())).containsExactlyElementsOf(values); ++ } + } + + /** + * Create a block that is offset from the start of the underlying array + */ +- private ValueBlock createOffsetBlock(List values) ++ private ValueBlock createOffsetBlock(List values, int offset) + { +- return blockFromValues(Iterables.concat(nCopies(2, getUnusedTestValue()), values, nCopies(2, getUnusedTestValue()))) +- .getRegion(2, values.size()); ++ return blockFromValues(Iterables.concat(nCopies(offset, getUnusedTestValue()), values, nCopies(offset, getUnusedTestValue()))) ++ .getRegion(offset, values.size()); + } + + /** + * Create a block that is offset from the start of the underlying array + */ +- private ValueBlock createOffsetBlockWithOddPositionsNull(List values) ++ private ValueBlock createOffsetBlockWithOddPositionsNull(List values, int offset) + { + ArrayList blockValues = new ArrayList<>(); +- blockValues.add(getUnusedTestValue()); +- blockValues.add(getUnusedTestValue()); ++ for (int i = 0; i < offset; i++) { ++ blockValues.add(getUnusedTestValue()); ++ } + for (int i = 0; i < values.size(); i++) { + T value = values.get(i); + if (i % 2 == 0) { +@@ -252,8 +273,9 @@ public abstract class AbstractTestBlockBuilder + blockValues.add(null); + } + } +- blockValues.add(getUnusedTestValue()); +- blockValues.add(getUnusedTestValue()); +- return blockFromValues(blockValues).getRegion(2, values.size()); ++ for (int i = 0; i < offset; i++) { ++ blockValues.add(getUnusedTestValue()); ++ } ++ return blockFromValues(blockValues).getRegion(offset, values.size()); + } + }