This is an automated email from the ASF dual-hosted git repository.
JingsongLi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/paimon.git
The following commit(s) were added to refs/heads/master by this push:
new cf381b9aeb [flink] Support grouped sorted index definitions in
topology builders (#10469)
cf381b9aeb is described below
commit cf381b9aeb3a6450ccc88b7bd590125955b68087
Author: YeJunHao <[email protected]>
AuthorDate: Fri Oct 9 17:45:49 2026 +0800
[flink] Support grouped sorted index definitions in topology builders
(#10469)
---
.../flink/globalindex/SortedIndexTopoBuilder.java | 52 +++++----------
.../procedure/CreateGlobalIndexProcedure.java | 3 +-
.../paimon/flink/SortedGlobalIndexITCase.java | 75 ++++++++++++++++++++++
.../globalindex/SortedIndexTopoBuilderTest.java | 4 +-
4 files changed, 94 insertions(+), 40 deletions(-)
diff --git
a/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/globalindex/SortedIndexTopoBuilder.java
b/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/globalindex/SortedIndexTopoBuilder.java
index f40a224265..11900b0fad 100644
---
a/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/globalindex/SortedIndexTopoBuilder.java
+++
b/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/globalindex/SortedIndexTopoBuilder.java
@@ -110,11 +110,12 @@ public class SortedIndexTopoBuilder {
return SUPPORTED_INDEX_TYPES.contains(indexType);
}
+ /** Builds independent indexes, each defined by its ordered columns. */
public static boolean buildIndex(
StreamExecutionEnvironment env,
Supplier<SortedGlobalIndexScanner> indexScannerSupplier,
FileStoreTable table,
- List<String> indexColumns,
+ List<List<String>> definitions,
String indexType,
PartitionPredicate partitionPredicate,
Options userOptions)
@@ -124,7 +125,7 @@ public class SortedIndexTopoBuilder {
env,
indexScannerSupplier,
table,
- indexColumns,
+ definitions,
indexType,
partitionPredicate,
userOptions);
@@ -143,29 +144,6 @@ public class SortedIndexTopoBuilder {
* to one shared committer.
*/
public static Optional<DataStream<Committable>> buildIndexStream(
- StreamExecutionEnvironment env,
- Supplier<SortedGlobalIndexScanner> indexScannerSupplier,
- FileStoreTable table,
- List<String> indexColumns,
- String indexType,
- PartitionPredicate partitionPredicate,
- Options userOptions)
- throws Exception {
- List<List<String>> definitions = new ArrayList<>();
- for (String name : indexColumns) {
- definitions.add(Collections.singletonList(name));
- }
- return buildIndexDefinitions(
- env,
- indexScannerSupplier,
- table,
- definitions,
- indexType,
- partitionPredicate,
- userOptions);
- }
-
- private static Optional<DataStream<Committable>> buildIndexDefinitions(
StreamExecutionEnvironment env,
Supplier<SortedGlobalIndexScanner> indexScannerSupplier,
FileStoreTable table,
@@ -176,6 +154,9 @@ public class SortedIndexTopoBuilder {
throws Exception {
List<DataStream<Committable>> allStreams = new ArrayList<>();
for (List<String> indexColumns : definitions) {
+ if (indexColumns.isEmpty()) {
+ throw new IllegalArgumentException("At least one index column
is required.");
+ }
String indexColumn = indexColumns.get(0);
SortedGlobalIndexScanner indexScanner =
indexScannerSupplier.get().withIndexFields(indexColumns);
@@ -322,22 +303,19 @@ public class SortedIndexTopoBuilder {
public static void buildIndexAndExecute(
StreamExecutionEnvironment env,
FileStoreTable table,
- List<String> indexColumns,
+ List<List<String>> definitions,
String indexType,
PartitionPredicate partitionPredicate,
Options userOptions)
throws Exception {
- Optional<DataStream<Committable>> written =
- buildIndexDefinitions(
- env,
- () -> new SortedGlobalIndexScanner(table, indexType,
userOptions),
- table,
- Collections.singletonList(indexColumns),
- indexType,
- partitionPredicate,
- userOptions);
- if (written.isPresent()) {
- commit(table, written.get(),
CoreOptions.createCommitUser(userOptions));
+ if (buildIndex(
+ env,
+ () -> new SortedGlobalIndexScanner(table, indexType,
userOptions),
+ table,
+ definitions,
+ indexType,
+ partitionPredicate,
+ userOptions)) {
env.execute("Create " + indexType + " global index for table: " +
table.name());
}
}
diff --git
a/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/procedure/CreateGlobalIndexProcedure.java
b/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/procedure/CreateGlobalIndexProcedure.java
index bb38de474f..6c40f09371 100644
---
a/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/procedure/CreateGlobalIndexProcedure.java
+++
b/paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/procedure/CreateGlobalIndexProcedure.java
@@ -35,6 +35,7 @@ import org.apache.flink.table.annotation.ProcedureHint;
import org.apache.flink.table.procedure.ProcedureContext;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
@@ -135,7 +136,7 @@ public class CreateGlobalIndexProcedure extends
ProcedureBase {
SortedIndexTopoBuilder.buildIndexAndExecute(
procedureContext.getExecutionEnvironment(),
table,
- indexColumns,
+ Collections.singletonList(indexColumns),
indexType,
partitionPredicate,
userOptions);
diff --git
a/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/SortedGlobalIndexITCase.java
b/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/SortedGlobalIndexITCase.java
index a73f9b8dbe..05053eccb1 100644
---
a/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/SortedGlobalIndexITCase.java
+++
b/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/SortedGlobalIndexITCase.java
@@ -22,12 +22,14 @@ import org.apache.paimon.catalog.Catalog;
import org.apache.paimon.data.BinaryString;
import org.apache.paimon.data.GenericRow;
import org.apache.paimon.data.InternalRow;
+import org.apache.paimon.flink.globalindex.SortedIndexTopoBuilder;
import org.apache.paimon.globalindex.IndexedSplit;
import org.apache.paimon.globalindex.sorted.SortedGlobalIndexTestUtils;
import org.apache.paimon.index.DataEvolutionIndexSourceMeta;
import org.apache.paimon.index.IndexFileMeta;
import org.apache.paimon.io.DataFileMeta;
import org.apache.paimon.manifest.IndexManifestEntry;
+import org.apache.paimon.options.Options;
import org.apache.paimon.predicate.Predicate;
import org.apache.paimon.predicate.PredicateBuilder;
import org.apache.paimon.reader.RecordReader;
@@ -66,6 +68,7 @@ import java.util.stream.IntStream;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.assertj.core.api.Assertions.entry;
/** Test case for sorted global indexes. */
public class SortedGlobalIndexITCase extends CatalogITCaseBase {
@@ -102,6 +105,78 @@ public class SortedGlobalIndexITCase extends
CatalogITCaseBase {
assertThat(sql("SELECT * FROM T WHERE id =
100")).containsOnly(Row.of(100, "name_100"));
}
+ @Test
+ public void testGroupedBTreeIndexes() throws Exception {
+ tEnv.getConfig().set(TableConfigOptions.TABLE_DML_SYNC, true);
+ sql(
+ "CREATE TABLE T_GROUPED (a INT, b INT, c INT, d INT, e INT)
WITH ("
+ + "'global-index.enabled' = 'true', "
+ + "'row-tracking.enabled' = 'true',
'data-evolution.enabled' = 'true', "
+ + "'sorted-index.records-per-file' = '3',
'sorted-index.build.max-parallelism' = '2')");
+ sql(
+ "INSERT INTO T_GROUPED VALUES "
+ + IntStream.range(0, 12)
+ .mapToObj(
+ i ->
+ String.format(
+ "(%d, %d, %d, %d, %s)",
+ i,
+ i % 4,
+ i % 3,
+ i % 5,
+ i == 7 ? "CAST(NULL AS
INT)" : i))
+ .collect(Collectors.joining(",")));
+ // Preserve composite key order and distinguish definitions sharing
their first column.
+ List<List<String>> definitions =
+ Arrays.asList(
+ Collections.singletonList("a"),
+ Arrays.asList("d", "b", "c"),
+ Collections.singletonList("e"),
+ Collections.singletonList("d"));
+ FileStoreTable table = paimonTable("T_GROUPED");
+ Options userOptions = new Options(table.options());
+ long snapshot = table.snapshotManager().latestSnapshot().id();
+ SortedIndexTopoBuilder.buildIndexAndExecute(
+
streamExecutionEnvironmentBuilder().batchMode().parallelism(2).build(),
+ table,
+ definitions,
+ "btree",
+ null,
+ userOptions);
+
assertThat(table.snapshotManager().latestSnapshot().id()).isEqualTo(snapshot +
1);
+ Map<List<Integer>, Long> rowCountsByFields =
+ indexEntries(table, "btree").stream()
+ .collect(
+ Collectors.groupingBy(
+ index ->
+ index.indexFile()
+ .globalIndexMeta()
+ .getIndexedFieldIds(),
+ Collectors.summingLong(
+ index ->
index.indexFile().rowCount())));
+ assertThat(rowCountsByFields)
+ .containsOnly(
+ entry(Collections.singletonList(0), 12L),
+ entry(Arrays.asList(3, 1, 2), 12L),
+ entry(Collections.singletonList(4), 12L),
+ entry(Collections.singletonList(3), 12L));
+ assertThat(sql("SELECT a FROM T_GROUPED WHERE a =
7")).containsExactly(Row.of(7));
+ assertThat(sql("SELECT a FROM T_GROUPED WHERE d = 2 AND b = 3 AND c =
1"))
+ .containsExactly(Row.of(7));
+ assertThat(sql("SELECT a FROM T_GROUPED WHERE e IS
NULL")).containsExactly(Row.of(7));
+
+ Set<String> indexedFiles = fileNames(indexEntries(table, "btree"));
+ SortedIndexTopoBuilder.buildIndexAndExecute(
+
streamExecutionEnvironmentBuilder().batchMode().parallelism(2).build(),
+ table,
+ definitions,
+ "btree",
+ null,
+ userOptions);
+
assertThat(table.snapshotManager().latestSnapshot().id()).isEqualTo(snapshot +
1);
+ assertThat(fileNames(indexEntries(table,
"btree"))).isEqualTo(indexedFiles);
+ }
+
@Test
public void testCompositeBTreeIndex() throws Exception {
tEnv.getConfig().set(TableConfigOptions.TABLE_DML_SYNC, true);
diff --git
a/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/globalindex/SortedIndexTopoBuilderTest.java
b/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/globalindex/SortedIndexTopoBuilderTest.java
index f5abf37b70..afcc6ec829 100644
---
a/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/globalindex/SortedIndexTopoBuilderTest.java
+++
b/paimon-flink/paimon-flink-common/src/test/java/org/apache/paimon/flink/globalindex/SortedIndexTopoBuilderTest.java
@@ -127,7 +127,7 @@ public class SortedIndexTopoBuilderTest {
env,
() -> indexScanner,
mock(FileStoreTable.class),
- Collections.singletonList("id"),
+
Collections.singletonList(Collections.singletonList("id")),
"btree",
null,
new Options()))
@@ -149,7 +149,7 @@ public class SortedIndexTopoBuilderTest {
env,
() -> indexScanner,
mock(FileStoreTable.class),
- Collections.singletonList("id"),
+
Collections.singletonList(Collections.singletonList("id")),
"btree",
null,
new Options()))