This is an automated email from the ASF dual-hosted git repository. XiaoHongbo-Hope pushed a commit to branch release-2.1 in repository https://gitbox.apache.org/repos/asf/paimon.git
commit b09fafcd1febd93cb9050275cd20f5f3e42c8432 Author: YeJunHao <[email protected]> AuthorDate: Thu Sep 17 18:24:50 2026 +0800 [core] Preserve query authorization when wrapping data evolution index splits (#9925) --- .../paimon/globalindex/DataEvolutionBatchScan.java | 23 +++++--- .../globalindex/DataEvolutionBatchScanTest.java | 68 ++++++++++++++++++++++ 2 files changed, 83 insertions(+), 8 deletions(-) diff --git a/paimon-core/src/main/java/org/apache/paimon/globalindex/DataEvolutionBatchScan.java b/paimon-core/src/main/java/org/apache/paimon/globalindex/DataEvolutionBatchScan.java index e412d8123f..618a0ee765 100644 --- a/paimon-core/src/main/java/org/apache/paimon/globalindex/DataEvolutionBatchScan.java +++ b/paimon-core/src/main/java/org/apache/paimon/globalindex/DataEvolutionBatchScan.java @@ -37,6 +37,7 @@ import org.apache.paimon.table.source.AppendBatchTableScan; import org.apache.paimon.table.source.DataSplit; import org.apache.paimon.table.source.DataTableScan; import org.apache.paimon.table.source.InnerTableScan; +import org.apache.paimon.table.source.QueryAuthSplit; import org.apache.paimon.table.source.Split; import org.apache.paimon.types.RowType; import org.apache.paimon.utils.Filter; @@ -455,18 +456,24 @@ public class DataEvolutionBatchScan implements DataTableScan { public static Plan wrapToIndexSplits( List<Split> splits, RowRangeIndex rowRangeIndex, ScoreGetter scoreGetter) { List<Split> indexedSplits = new ArrayList<>(); - Function<Split, List<IndexedSplit>> process = - split -> - Collections.singletonList( - split instanceof IndexedSplit - ? (IndexedSplit) split - : wrap((DataSplit) split, rowRangeIndex, scoreGetter)); + Function<Split, List<Split>> process = + split -> Collections.singletonList(wrap(split, rowRangeIndex, scoreGetter)); randomlyExecuteSequentialReturn(process, splits, null).forEachRemaining(indexedSplits::add); return () -> indexedSplits; } - private static IndexedSplit wrap( - DataSplit dataSplit, final RowRangeIndex rowRangeIndex, ScoreGetter scoreGetter) { + private static Split wrap( + Split split, final RowRangeIndex rowRangeIndex, ScoreGetter scoreGetter) { + if (split instanceof QueryAuthSplit) { + QueryAuthSplit authSplit = (QueryAuthSplit) split; + return new QueryAuthSplit( + wrap(authSplit.split(), rowRangeIndex, scoreGetter), authSplit.authResult()); + } + if (split instanceof IndexedSplit) { + return split; + } + + DataSplit dataSplit = (DataSplit) split; List<DataFileMeta> files = dataSplit.dataFiles(); List<Range> expected = new ArrayList<>(); diff --git a/paimon-core/src/test/java/org/apache/paimon/globalindex/DataEvolutionBatchScanTest.java b/paimon-core/src/test/java/org/apache/paimon/globalindex/DataEvolutionBatchScanTest.java index 78771719d0..8ec43fe004 100644 --- a/paimon-core/src/test/java/org/apache/paimon/globalindex/DataEvolutionBatchScanTest.java +++ b/paimon-core/src/test/java/org/apache/paimon/globalindex/DataEvolutionBatchScanTest.java @@ -18,6 +18,7 @@ package org.apache.paimon.globalindex; +import org.apache.paimon.catalog.TableQueryAuthResult; import org.apache.paimon.data.BinaryRow; import org.apache.paimon.io.DataFileMeta; import org.apache.paimon.predicate.Predicate; @@ -25,6 +26,7 @@ import org.apache.paimon.predicate.PredicateBuilder; import org.apache.paimon.table.source.AppendBatchTableScan; import org.apache.paimon.table.source.DataSplit; import org.apache.paimon.table.source.DataTableScan; +import org.apache.paimon.table.source.QueryAuthSplit; import org.apache.paimon.table.source.Split; import org.apache.paimon.table.source.snapshot.SnapshotReader; import org.apache.paimon.types.DataField; @@ -34,6 +36,8 @@ import org.apache.paimon.utils.Range; import org.apache.paimon.utils.RowRangeIndex; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.mockito.ArgumentCaptor; import java.util.ArrayList; @@ -210,6 +214,70 @@ public class DataEvolutionBatchScanTest { .containsExactly(new Range(4200, 4450), new Range(4650, 4700)); } + @ParameterizedTest + @ValueSource(booleans = {false, true}) + public void testWrapToIndexSplitsWithQueryAuth(boolean hasAuthResult) { + DataSplit split = + DataSplit.builder() + .withSnapshot(1L) + .withPartition(BinaryRow.EMPTY_ROW) + .withBucket(0) + .withBucketPath("bucket-0") + .withDataFiles(Collections.singletonList(newAppendFile(0L, 10L, "file-0"))) + .build(); + TableQueryAuthResult authResult = + hasAuthResult + ? new TableQueryAuthResult( + Collections.singletonList("filter-json"), + Collections.singletonMap("f0", "mask-json")) + : null; + QueryAuthSplit authSplit = new QueryAuthSplit(split, authResult); + + List<Split> indexedSplits = + DataEvolutionBatchScan.wrapToIndexSplits( + Collections.singletonList(authSplit), + RowRangeIndex.create( + Arrays.asList(new Range(1, 2), new Range(7, 12))), + rowId -> rowId + 0.5f) + .splits(); + + assertThat(indexedSplits).hasSize(1); + assertThat(indexedSplits.get(0)).isInstanceOf(QueryAuthSplit.class); + QueryAuthSplit indexedAuthSplit = (QueryAuthSplit) indexedSplits.get(0); + assertThat(indexedAuthSplit.authResult()).isSameAs(authResult); + assertThat(indexedAuthSplit.split()).isInstanceOf(IndexedSplit.class); + IndexedSplit indexedSplit = (IndexedSplit) indexedAuthSplit.split(); + assertThat(indexedSplit.dataSplit()).isSameAs(split); + assertThat(indexedSplit.rowRanges()).containsExactly(new Range(1, 2), new Range(7, 9)); + assertThat(indexedSplit.scores()).containsExactly(1.5f, 2.5f, 7.5f, 8.5f, 9.5f); + assertThat(authSplit.split()).isSameAs(split); + } + + @Test + public void testWrapToIndexSplitsKeepsExistingIndexedSplits() { + IndexedSplit indexedSplit = + new IndexedSplit( + mock(DataSplit.class), + Collections.singletonList(new Range(1, 2)), + new float[] {1.5f, 2.5f}); + TableQueryAuthResult authResult = mock(TableQueryAuthResult.class); + + List<Split> indexedSplits = + DataEvolutionBatchScan.wrapToIndexSplits( + Arrays.asList( + indexedSplit, new QueryAuthSplit(indexedSplit, authResult)), + null, + null) + .splits(); + + assertThat(indexedSplits).hasSize(2); + assertThat(indexedSplits.get(0)).isSameAs(indexedSplit); + assertThat(indexedSplits.get(1)).isInstanceOf(QueryAuthSplit.class); + QueryAuthSplit indexedAuthSplit = (QueryAuthSplit) indexedSplits.get(1); + assertThat(indexedAuthSplit.authResult()).isSameAs(authResult); + assertThat(indexedAuthSplit.split()).isSameAs(indexedSplit); + } + private static RowType rowTypeWithRowId() { return RowType.of( new DataField(0, "f0", DataTypes.INT()),
