This is an automated email from the ASF dual-hosted git repository.

voonhous pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hudi.git


The following commit(s) were added to refs/heads/master by this push:
     new 66b9c6eeb60f fix(trino): report real block size and slice splits 
solely by target_split_size (#19478)
66b9c6eeb60f is described below

commit 66b9c6eeb60f377b86ebaaa7a0e96d37f007593b
Author: voonhous <[email protected]>
AuthorDate: Tue Aug 4 19:08:57 2026 +0800

    fix(trino): report real block size and slice splits solely by 
target_split_size (#19478)
    
    * fix(trino): report real block size in HudiTrinoStorage and slice splits 
by target_split_size
    
    HudiTrinoStorage hardcoded blockSize=0 in convertToPathInfo and getPathInfo,
    the hudi-trino counterpart of trinodb/trino#29842. Report the file length as
    the block size instead, matching the upstream fix at the storage layer.
    
    HudiSplitFactory used max(target_split_size, blockSize) to size base file
    splits. With the storage layer now reporting length as block size, that 
max()
    would silently disable the target_split_size knob, so make the policy
    explicit: slicing is governed solely by target_split_size. Also fail fast on
    a non-positive target, which previously looped forever.
    
    Covers apache/hudi#19231.
    
    * fix(trino): validate target_split_size at config time and pin the split 
sizing tests
    
    Addresses review feedback on #19478:
    
    - Move the non-positive target split size guard from the middle of
      createSplitsForBaseFile into the HudiSplitFactory constructor, so it 
covers
      every split path instead of only base file slicing past the fileSize == 0
      early return. createHudiSplits becomes private, leaving the constructor as
      the single entry point that can be handed a bad target.
    - Reject a zero target at config time as well: @MinDataSize("1B") on
      HudiConfig.getTargetSplitSize and validateMinDataSize on the
      target_split_size session property, matching the shape already used by
      parquet_small_file_threshold. The constructor check stays as a backstop.
    - Set the TestHudiSplitFactory fixture block size to the base file length,
      which is what HudiTrinoStorage now reports. The old fixed 8MB fixture was
      below the 128MB target, so the default-target tests passed on master
      unchanged and did not pin the max() removal. With the block size tracking
      the file length, restoring max(target, blockSize) fails 5 tests, including
      the 500MB default-target case.
---
 .../main/java/io/trino/plugin/hudi/HudiConfig.java |   2 +
 .../trino/plugin/hudi/HudiSessionProperties.java   |   2 +
 .../trino/plugin/hudi/split/HudiSplitFactory.java  |   8 +-
 .../plugin/hudi/storage/HudiTrinoStorage.java      |   5 +-
 .../java/io/trino/plugin/hudi/TestHudiConfig.java  |  13 ++
 .../plugin/hudi/TestHudiSessionProperties.java     |  18 +++
 .../plugin/hudi/split/TestHudiSplitFactory.java    |  78 ++++++++---
 .../plugin/hudi/storage/TestHudiTrinoStorage.java  | 150 +++++++++++++++++++++
 8 files changed, 257 insertions(+), 19 deletions(-)

diff --git a/hudi-trino/src/main/java/io/trino/plugin/hudi/HudiConfig.java 
b/hudi-trino/src/main/java/io/trino/plugin/hudi/HudiConfig.java
index 8ecb76eeda9d..c5ce5ce0e23d 100644
--- a/hudi-trino/src/main/java/io/trino/plugin/hudi/HudiConfig.java
+++ b/hudi-trino/src/main/java/io/trino/plugin/hudi/HudiConfig.java
@@ -19,6 +19,7 @@ import io.airlift.configuration.ConfigDescription;
 import io.airlift.configuration.DefunctConfig;
 import io.airlift.units.DataSize;
 import io.airlift.units.Duration;
+import io.airlift.units.MinDataSize;
 import jakarta.validation.constraints.DecimalMax;
 import jakarta.validation.constraints.DecimalMin;
 import jakarta.validation.constraints.Min;
@@ -220,6 +221,7 @@ public class HudiConfig
     }
 
     @NotNull
+    @MinDataSize("1B")
     public DataSize getTargetSplitSize()
     {
         return targetSplitSize;
diff --git 
a/hudi-trino/src/main/java/io/trino/plugin/hudi/HudiSessionProperties.java 
b/hudi-trino/src/main/java/io/trino/plugin/hudi/HudiSessionProperties.java
index 32ac46a95977..8a47e3a8b03d 100644
--- a/hudi-trino/src/main/java/io/trino/plugin/hudi/HudiSessionProperties.java
+++ b/hudi-trino/src/main/java/io/trino/plugin/hudi/HudiSessionProperties.java
@@ -31,6 +31,7 @@ import static 
com.google.common.collect.ImmutableList.toImmutableList;
 import static 
io.trino.plugin.base.session.PropertyMetadataUtil.dataSizeProperty;
 import static 
io.trino.plugin.base.session.PropertyMetadataUtil.durationProperty;
 import static 
io.trino.plugin.base.session.PropertyMetadataUtil.validateMaxDataSize;
+import static 
io.trino.plugin.base.session.PropertyMetadataUtil.validateMinDataSize;
 import static 
io.trino.plugin.hive.parquet.ParquetReaderConfig.PARQUET_READER_MAX_SMALL_FILE_THRESHOLD;
 import static io.trino.spi.StandardErrorCode.INVALID_SESSION_PROPERTY;
 import static io.trino.spi.session.PropertyMetadata.booleanProperty;
@@ -188,6 +189,7 @@ public class HudiSessionProperties
                         TARGET_SPLIT_SIZE,
                         "The target split size",
                         hudiConfig.getTargetSplitSize(),
+                        value -> validateMinDataSize(TARGET_SPLIT_SIZE, value, 
DataSize.ofBytes(1)),
                         false),
                 integerProperty(
                         MAX_SPLITS_PER_SECOND,
diff --git 
a/hudi-trino/src/main/java/io/trino/plugin/hudi/split/HudiSplitFactory.java 
b/hudi-trino/src/main/java/io/trino/plugin/hudi/split/HudiSplitFactory.java
index 6bab996e199b..ece6472473c0 100644
--- a/hudi-trino/src/main/java/io/trino/plugin/hudi/split/HudiSplitFactory.java
+++ b/hudi-trino/src/main/java/io/trino/plugin/hudi/split/HudiSplitFactory.java
@@ -51,6 +51,8 @@ public class HudiSplitFactory
         this.hudiTableHandle = requireNonNull(hudiTableHandle, 
"hudiTableHandle is null");
         this.hudiSplitWeightProvider = requireNonNull(hudiSplitWeightProvider, 
"hudiSplitWeightProvider is null");
         this.targetSplitSize = requireNonNull(targetSplitSize, 
"targetSplitSize is null");
+        // A non-positive target would make split generation loop forever, so 
reject it here rather than mid-scan
+        checkArgument(targetSplitSize.toBytes() > 0, "targetSplitSize must be 
positive: %s", targetSplitSize);
     }
 
     public List<HudiSplit> createSplits(List<HivePartitionKey> partitionKeys, 
FileSlice fileSlice, String commitTime)
@@ -65,7 +67,7 @@ public class HudiSplitFactory
      * <p>
      * For regular MOR tables, a single split is created for the combination 
of the base file and its log files.
      */
-    public static List<HudiSplit> createHudiSplits(
+    private static List<HudiSplit> createHudiSplits(
             HudiTableHandle hudiTableHandle,
             List<HivePartitionKey> partitionKeys,
             FileSlice fileSlice,
@@ -127,7 +129,9 @@ public class HudiSplitFactory
         }
 
         ImmutableList.Builder<HudiSplit> splits = ImmutableList.builder();
-        long targetSplitSizeInBytes = Math.max(targetSplitSize.toBytes(), 
baseFile.getPathInfo().getBlockSize());
+        // Slicing is governed solely by the target split size; the block size 
reported by
+        // storage is not meaningful on object stores and must not influence 
split sizing.
+        long targetSplitSizeInBytes = targetSplitSize.toBytes();
 
         long bytesRemaining = fileSize;
         while (((double) bytesRemaining) / targetSplitSizeInBytes > 
SPLIT_SLOP) {
diff --git 
a/hudi-trino/src/main/java/io/trino/plugin/hudi/storage/HudiTrinoStorage.java 
b/hudi-trino/src/main/java/io/trino/plugin/hudi/storage/HudiTrinoStorage.java
index 48c5409c10d8..1edff63e8a7b 100644
--- 
a/hudi-trino/src/main/java/io/trino/plugin/hudi/storage/HudiTrinoStorage.java
+++ 
b/hudi-trino/src/main/java/io/trino/plugin/hudi/storage/HudiTrinoStorage.java
@@ -71,7 +71,7 @@ public class HudiTrinoStorage
                 fileEntry.length(),
                 false,
                 (short) 0,
-                0,
+                fileEntry.length(),
                 fileEntry.lastModified().toEpochMilli());
     }
 
@@ -170,7 +170,8 @@ public class HudiTrinoStorage
         if (!inputFile.exists()) {
             throw new FileNotFoundException("Path " + path + " does not 
exist");
         }
-        return new StoragePathInfo(path, inputFile.length(), false, (short) 0, 
0, inputFile.lastModified().toEpochMilli());
+        long length = inputFile.length();
+        return new StoragePathInfo(path, length, false, (short) 0, length, 
inputFile.lastModified().toEpochMilli());
     }
 
     @Override
diff --git a/hudi-trino/src/test/java/io/trino/plugin/hudi/TestHudiConfig.java 
b/hudi-trino/src/test/java/io/trino/plugin/hudi/TestHudiConfig.java
index d5353b896a1a..29aeded55ffd 100644
--- a/hudi-trino/src/test/java/io/trino/plugin/hudi/TestHudiConfig.java
+++ b/hudi-trino/src/test/java/io/trino/plugin/hudi/TestHudiConfig.java
@@ -17,6 +17,7 @@ import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import io.airlift.units.DataSize;
 import io.airlift.units.Duration;
+import io.airlift.units.MinDataSize;
 import org.junit.jupiter.api.Test;
 
 import java.util.Map;
@@ -24,6 +25,7 @@ import java.util.Map;
 import static 
io.airlift.configuration.testing.ConfigAssertions.assertFullMapping;
 import static 
io.airlift.configuration.testing.ConfigAssertions.assertRecordedDefaults;
 import static io.airlift.configuration.testing.ConfigAssertions.recordDefaults;
+import static io.airlift.testing.ValidationAssertions.assertFailsValidation;
 import static io.airlift.units.DataSize.Unit.MEGABYTE;
 
 public class TestHudiConfig
@@ -131,4 +133,15 @@ public class TestHudiConfig
 
         assertFullMapping(properties, expected);
     }
+
+    @Test
+    public void testTargetSplitSizeValidation()
+    {
+        // A zero target split size would make split generation loop forever, 
so reject it at config time
+        assertFailsValidation(
+                new HudiConfig().setTargetSplitSize(DataSize.ofBytes(0)),
+                "targetSplitSize",
+                "must be greater than or equal to 1B",
+                MinDataSize.class);
+    }
 }
diff --git 
a/hudi-trino/src/test/java/io/trino/plugin/hudi/TestHudiSessionProperties.java 
b/hudi-trino/src/test/java/io/trino/plugin/hudi/TestHudiSessionProperties.java
index ed17a203089e..b97bdc47750e 100644
--- 
a/hudi-trino/src/test/java/io/trino/plugin/hudi/TestHudiSessionProperties.java
+++ 
b/hudi-trino/src/test/java/io/trino/plugin/hudi/TestHudiSessionProperties.java
@@ -14,14 +14,18 @@
 package io.trino.plugin.hudi;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import io.trino.plugin.hive.parquet.ParquetReaderConfig;
+import io.trino.spi.TrinoException;
 import io.trino.spi.connector.ConnectorSession;
 import io.trino.testing.TestingConnectorSession;
 import org.junit.jupiter.api.Test;
 
 import static io.trino.plugin.hudi.HudiSessionProperties.getColumnsToHide;
 import static io.trino.plugin.hudi.HudiSessionProperties.getRecordMergerImpls;
+import static io.trino.plugin.hudi.HudiSessionProperties.getTargetSplitSize;
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
 public class TestHudiSessionProperties
 {
@@ -50,4 +54,18 @@ public class TestHudiSessionProperties
         assertThat(getRecordMergerImpls(session))
                 .containsExactly("com.example.MergerOne", 
"com.example.MergerTwo");
     }
+
+    @Test
+    public void testSessionPropertyTargetSplitSizeRejectsZero()
+    {
+        // A zero target split size would make split generation loop forever, 
so reject it when the property is read
+        HudiSessionProperties sessionProperties = new 
HudiSessionProperties(new HudiConfig(), new ParquetReaderConfig());
+        ConnectorSession session = TestingConnectorSession.builder()
+                .setPropertyMetadata(sessionProperties.getSessionProperties())
+                .setPropertyValues(ImmutableMap.of("target_split_size", "0B"))
+                .build();
+        assertThatThrownBy(() -> getTargetSplitSize(session))
+                .isInstanceOf(TrinoException.class)
+                .hasMessageContaining("target_split_size must be at least 1B: 
0B");
+    }
 }
diff --git 
a/hudi-trino/src/test/java/io/trino/plugin/hudi/split/TestHudiSplitFactory.java 
b/hudi-trino/src/test/java/io/trino/plugin/hudi/split/TestHudiSplitFactory.java
index d659044d2456..067c0f7b3dbd 100644
--- 
a/hudi-trino/src/test/java/io/trino/plugin/hudi/split/TestHudiSplitFactory.java
+++ 
b/hudi-trino/src/test/java/io/trino/plugin/hudi/split/TestHudiSplitFactory.java
@@ -35,6 +35,7 @@ import java.util.OptionalLong;
 
 import static io.airlift.units.DataSize.Unit.MEGABYTE;
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
 public class TestHudiSplitFactory
 {
@@ -101,19 +102,63 @@ public class TestHudiSplitFactory
     }
 
     @Test
-    public void testCreateHudiSplitsWithLargerBlockSize()
+    public void testCreateHudiSplitsIgnoresBlockSize()
     {
-        // Test with 1MB target split size and 32MB base file
-        // - should create 4 splits because the block size of 8MB is larger 
than the target split size
+        // Test with 2MB target and 8MB base file whose reported block size is 
8MB
+        // - the block size must be ignored, so 4 splits of the 2MB target 
size are expected
+        //   (previously the 8MB block size beat the target and produced 1 
split of 8MB)
         testSplitCreation(
-                DataSize.of(1, MEGABYTE),
-                DataSize.of(32, MEGABYTE),
+                DataSize.of(2, MEGABYTE),
+                DataSize.of(8, MEGABYTE),
                 Option.empty(),
                 ImmutableList.of(
-                        Pair.of(0L, DataSize.of(8, MEGABYTE)),
-                        Pair.of(DataSize.of(8, MEGABYTE).toBytes(), 
DataSize.of(8, MEGABYTE)),
-                        Pair.of(DataSize.of(16, MEGABYTE).toBytes(), 
DataSize.of(8, MEGABYTE)),
-                        Pair.of(DataSize.of(24, MEGABYTE).toBytes(), 
DataSize.of(8, MEGABYTE))));
+                        Pair.of(0L, DataSize.of(2, MEGABYTE)),
+                        Pair.of(DataSize.of(2, MEGABYTE).toBytes(), 
DataSize.of(2, MEGABYTE)),
+                        Pair.of(DataSize.of(4, MEGABYTE).toBytes(), 
DataSize.of(2, MEGABYTE)),
+                        Pair.of(DataSize.of(6, MEGABYTE).toBytes(), 
DataSize.of(2, MEGABYTE))));
+    }
+
+    @Test
+    public void testCreateHudiSplitsWithFileSmallerThanDefaultTarget()
+    {
+        // Regression test for the split inflation reported in 
trinodb/trino#29842 (hudi#19231):
+        // a ~120MB file with the default 128MB target must produce exactly 1 
split
+        testSplitCreation(
+                DataSize.of(128, MEGABYTE),
+                DataSize.of(120, MEGABYTE),
+                Option.empty(),
+                ImmutableList.of(
+                        Pair.of(0L, DataSize.of(120, MEGABYTE))));
+    }
+
+    @Test
+    public void testCreateHudiSplitsWithFileLargerThanDefaultTarget()
+    {
+        // Test with 128MB target and 500MB base file
+        // - should be sliced at target boundaries into 3 x 128MB + 116MB 
remainder, even though the
+        //   reported block size (500MB, the file length) would otherwise 
force a single split
+        testSplitCreation(
+                DataSize.of(128, MEGABYTE),
+                DataSize.of(500, MEGABYTE),
+                Option.empty(),
+                ImmutableList.of(
+                        Pair.of(0L, DataSize.of(128, MEGABYTE)),
+                        Pair.of(DataSize.of(128, MEGABYTE).toBytes(), 
DataSize.of(128, MEGABYTE)),
+                        Pair.of(DataSize.of(256, MEGABYTE).toBytes(), 
DataSize.of(128, MEGABYTE)),
+                        Pair.of(DataSize.of(384, MEGABYTE).toBytes(), 
DataSize.of(116, MEGABYTE))));
+    }
+
+    @Test
+    public void testCreateHudiSplitsWithZeroTargetSplitSize()
+    {
+        // A zero target split size must be rejected on construction, before 
any file slice is seen,
+        // instead of looping forever once split generation reaches a 
non-empty base file
+        assertThatThrownBy(() -> new HudiSplitFactory(
+                createTableHandle(),
+                new SizeBasedSplitWeightProvider(0.05, DataSize.of(128, 
MEGABYTE)),
+                DataSize.ofBytes(0)))
+                .isInstanceOf(IllegalArgumentException.class)
+                .hasMessageContaining("targetSplitSize");
     }
 
     @Test
@@ -151,8 +196,8 @@ public class TestHudiSplitFactory
 
         FileSlice fileSlice = createFileSlice(baseFileSize, logFileSize);
 
-        List<HudiSplit> splits = HudiSplitFactory.createHudiSplits(
-                tableHandle, PARTITION_KEYS, fileSlice, COMMIT_TIME, 
weightProvider, targetSplitSize);
+        List<HudiSplit> splits = new HudiSplitFactory(tableHandle, 
weightProvider, targetSplitSize)
+                .createSplits(PARTITION_KEYS, fileSlice, COMMIT_TIME);
 
         assertThat(splits).hasSize(expectedSplitInfo.size());
 
@@ -192,14 +237,17 @@ public class TestHudiSplitFactory
     {
         String fileId = "5a4f6a70-0306-40a8-952b-045b0d8ff0d4-0";
         HoodieFileGroupId fileGroupId = new HoodieFileGroupId("partition", 
fileId);
-        long blockSize = 8L * 1024 * 1024;
+        // Block size mirrors the file length, which is what HudiTrinoStorage 
now reports. Split
+        // generation must ignore it, so every multi-split expectation below 
would collapse to a
+        // single whole-file split if the block size were allowed back into 
the sizing decision.
         String baseFilePath = "/test/path/" + fileGroupId + "_4-19-0_" + 
COMMIT_TIME + ".parquet";
         String logFilePath = "/test/path/." + fileId + 
"_2025062515374131546.log.1_0-53-80";
+        long logFileSizeInBytes = logFileSize.isPresent() ? 
logFileSize.get().toBytes() : 0L;
         StoragePathInfo baseFileInfo = new StoragePathInfo(
-                new StoragePath(baseFilePath), baseFileSize.toBytes(), false, 
(short) 0, blockSize, System.currentTimeMillis());
+                new StoragePath(baseFilePath), baseFileSize.toBytes(), false, 
(short) 0, baseFileSize.toBytes(), System.currentTimeMillis());
         StoragePathInfo logFileInfo = new StoragePathInfo(
-                new StoragePath(logFilePath), logFileSize.isPresent() ? 
logFileSize.get().toBytes() : 0L,
-                false, (short) 0, blockSize, System.currentTimeMillis());
+                new StoragePath(logFilePath), logFileSizeInBytes,
+                false, (short) 0, logFileSizeInBytes, 
System.currentTimeMillis());
         HoodieBaseFile baseFile = new HoodieBaseFile(baseFileInfo);
         return new FileSlice(fileGroupId, COMMIT_TIME, baseFile,
                 logFileSize.isPresent() ? ImmutableList.of(new 
HoodieLogFile(logFileInfo)) : ImmutableList.of());
diff --git 
a/hudi-trino/src/test/java/io/trino/plugin/hudi/storage/TestHudiTrinoStorage.java
 
b/hudi-trino/src/test/java/io/trino/plugin/hudi/storage/TestHudiTrinoStorage.java
new file mode 100644
index 000000000000..a085a73af551
--- /dev/null
+++ 
b/hudi-trino/src/test/java/io/trino/plugin/hudi/storage/TestHudiTrinoStorage.java
@@ -0,0 +1,150 @@
+/*
+ * 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.plugin.hudi.storage;
+
+import io.trino.filesystem.FileEntry;
+import io.trino.filesystem.Location;
+import io.trino.filesystem.TrinoFileSystem;
+import io.trino.filesystem.memory.MemoryFileSystem;
+import org.apache.hudi.storage.StoragePath;
+import org.apache.hudi.storage.StoragePathInfo;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.time.Instant;
+import java.util.List;
+import java.util.Optional;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class TestHudiTrinoStorage
+{
+    @Test
+    void testConvertToPathInfo()
+    {
+        FileEntry fileEntry = new FileEntry(
+                Location.of("memory:///table/data.parquet"),
+                42,
+                Instant.ofEpochMilli(1234567890123L),
+                Optional.empty());
+
+        StoragePathInfo pathInfo = 
HudiTrinoStorage.convertToPathInfo(fileEntry);
+
+        assertThat(pathInfo.getPath()).isEqualTo(new 
StoragePath("memory:///table/data.parquet"));
+        assertThat(pathInfo.getLength()).isEqualTo(42);
+        assertThat(pathInfo.isFile()).isTrue();
+        assertThat(pathInfo.getBlockReplication()).isEqualTo((short) 0);
+        assertThat(pathInfo.getBlockSize()).isEqualTo(42);
+        assertThat(pathInfo.getModificationTime()).isEqualTo(1234567890123L);
+    }
+
+    @Test
+    void testGetPathInfoForFile()
+            throws IOException
+    {
+        TrinoFileSystem fileSystem = new MemoryFileSystem();
+        writeFile(fileSystem, "memory:///table/data.parquet", 42);
+        HudiTrinoStorage storage = new HudiTrinoStorage(fileSystem, new 
TrinoStorageConfiguration());
+
+        StoragePathInfo pathInfo = storage.getPathInfo(new 
StoragePath("memory:///table/data.parquet"));
+
+        assertThat(pathInfo.getLength()).isEqualTo(42);
+        assertThat(pathInfo.isFile()).isTrue();
+        assertThat(pathInfo.getBlockSize()).isEqualTo(42);
+        assertThat(pathInfo.getModificationTime()).isGreaterThan(0);
+    }
+
+    @Test
+    void testGetPathInfoForDirectory()
+            throws IOException
+    {
+        TrinoFileSystem fileSystem = new MemoryFileSystem();
+        writeFile(fileSystem, "memory:///table/data.parquet", 42);
+        HudiTrinoStorage storage = new HudiTrinoStorage(fileSystem, new 
TrinoStorageConfiguration());
+
+        StoragePathInfo pathInfo = storage.getPathInfo(new 
StoragePath("memory:///table"));
+
+        assertThat(pathInfo.isDirectory()).isTrue();
+        assertThat(pathInfo.getLength()).isEqualTo(0);
+        assertThat(pathInfo.getBlockSize()).isEqualTo(0);
+    }
+
+    @Test
+    void testListFiles()
+            throws IOException
+    {
+        HudiTrinoStorage storage = createStorageWithFiles();
+
+        List<StoragePathInfo> entries = storage.listFiles(new 
StoragePath("memory:///table"));
+
+        assertThat(entries).hasSize(3);
+        assertThat(entries.get(0).getPath()).isEqualTo(new 
StoragePath("memory:///table/a.parquet"));
+        assertThat(entries.get(1).getPath()).isEqualTo(new 
StoragePath("memory:///table/b.parquet"));
+        assertThat(entries.get(2).getPath()).isEqualTo(new 
StoragePath("memory:///table/nested/c.parquet"));
+        assertThat(entries.get(0).getLength()).isEqualTo(10);
+        assertThat(entries.get(1).getLength()).isEqualTo(20);
+        assertThat(entries.get(2).getLength()).isEqualTo(30);
+        for (StoragePathInfo entry : entries) {
+            assertThat(entry.getBlockSize()).isEqualTo(entry.getLength());
+        }
+    }
+
+    @Test
+    void testListDirectEntries()
+            throws IOException
+    {
+        HudiTrinoStorage storage = createStorageWithFiles();
+
+        List<StoragePathInfo> entries = storage.listDirectEntries(new 
StoragePath("memory:///table"));
+
+        assertThat(entries).hasSize(2);
+        assertThat(entries.get(0).getPath()).isEqualTo(new 
StoragePath("memory:///table/a.parquet"));
+        assertThat(entries.get(1).getPath()).isEqualTo(new 
StoragePath("memory:///table/b.parquet"));
+        for (StoragePathInfo entry : entries) {
+            assertThat(entry.getBlockSize()).isEqualTo(entry.getLength());
+        }
+    }
+
+    @Test
+    void testListDirectEntriesWithFilter()
+            throws IOException
+    {
+        HudiTrinoStorage storage = createStorageWithFiles();
+
+        List<StoragePathInfo> entries = storage.listDirectEntries(
+                new StoragePath("memory:///table"),
+                path -> path.getName().equals("b.parquet"));
+
+        assertThat(entries).hasSize(1);
+        assertThat(entries.get(0).getPath()).isEqualTo(new 
StoragePath("memory:///table/b.parquet"));
+        assertThat(entries.get(0).getLength()).isEqualTo(20);
+        assertThat(entries.get(0).getBlockSize()).isEqualTo(20);
+    }
+
+    private static HudiTrinoStorage createStorageWithFiles()
+            throws IOException
+    {
+        TrinoFileSystem fileSystem = new MemoryFileSystem();
+        writeFile(fileSystem, "memory:///table/a.parquet", 10);
+        writeFile(fileSystem, "memory:///table/b.parquet", 20);
+        writeFile(fileSystem, "memory:///table/nested/c.parquet", 30);
+        return new HudiTrinoStorage(fileSystem, new 
TrinoStorageConfiguration());
+    }
+
+    private static void writeFile(TrinoFileSystem fileSystem, String location, 
int length)
+            throws IOException
+    {
+        fileSystem.newOutputFile(Location.of(location)).createOrOverwrite(new 
byte[length]);
+    }
+}

Reply via email to