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

KevinyhZou pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gluten.git


The following commit(s) were added to refs/heads/main by this push:
     new 90e9825a89 [GLUTEN-12572][FLINK]Add support for compression in hive 
table sink (#12571)
90e9825a89 is described below

commit 90e9825a895cc926d246724cb47140f3f326fbdc
Author: kevinyhzou <[email protected]>
AuthorDate: Tue Jul 28 17:21:55 2026 +0800

    [GLUTEN-12572][FLINK]Add support for compression in hive table sink (#12571)
    
    * [GLUTEN-12206][FLINK] Support filesystem writer bucket state snapshot and 
restore
    
    * Support compresion type of hive table
---
 .github/workflows/flink.yml                        |   2 +-
 gluten-flink/docs/Flink.md                         |   2 +-
 .../apache/gluten/velox/HiveSourceSinkFactory.java |  98 +++++++++++++++--
 .../GlutenStreamingFileWriterOperator.java         |  14 ++-
 .../gluten/velox/HiveSourceSinkFactoryTest.java    | 118 +++++++++++++++++++++
 5 files changed, 220 insertions(+), 14 deletions(-)

diff --git a/.github/workflows/flink.yml b/.github/workflows/flink.yml
index 878b47145d..9d7906a1e9 100644
--- a/.github/workflows/flink.yml
+++ b/.github/workflows/flink.yml
@@ -88,7 +88,7 @@ jobs:
           export fmt_SOURCE=BUNDLED
           export folly_SOURCE=BUNDLED
           git clone -b gluten-0530 https://github.com/bigo-sg/velox4j.git
-          cd velox4j && git reset --hard 
95e9afe64c38d2e5ba962eee7949ef629fe50197
+          cd velox4j && git reset --hard 
feb921eabe637bff88eda0fc753d36bbe422fe87
           git apply $GITHUB_WORKSPACE/gluten-flink/patches/fix-velox4j.patch
           $GITHUB_WORKSPACE/build/mvn clean install -DskipTests -Dgpg.skip 
-Dspotless.skip=true
           cd ..
diff --git a/gluten-flink/docs/Flink.md b/gluten-flink/docs/Flink.md
index 1da1209f20..3fc46f25d1 100644
--- a/gluten-flink/docs/Flink.md
+++ b/gluten-flink/docs/Flink.md
@@ -48,7 +48,7 @@ As some features have not been committed to upstream, you 
have to use the follow
 ## fetch velox4j code
 git clone -b gluten-0530 https://github.com/bigo-sg/velox4j.git
 cd velox4j
-git reset --hard 95e9afe64c38d2e5ba962eee7949ef629fe50197
+git reset --hard feb921eabe637bff88eda0fc753d36bbe422fe87
 mvn clean install -DskipTests -Dgpg.skip -Dspotless.skip=true
 ```
 **Get gluten**
diff --git 
a/gluten-flink/planner/src/main/java/org/apache/gluten/velox/HiveSourceSinkFactory.java
 
b/gluten-flink/planner/src/main/java/org/apache/gluten/velox/HiveSourceSinkFactory.java
index 56d356f237..b0e5248b6a 100644
--- 
a/gluten-flink/planner/src/main/java/org/apache/gluten/velox/HiveSourceSinkFactory.java
+++ 
b/gluten-flink/planner/src/main/java/org/apache/gluten/velox/HiveSourceSinkFactory.java
@@ -25,8 +25,13 @@ import org.apache.flink.table.data.RowData;
 
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Properties;
 
 public class HiveSourceSinkFactory extends FileSystemSinkFactory {
+  private static final String COMPRESSION_KIND = "sink.file.compression";
+  private static final String[] SUPPORTED_COMPRESSION_TABLE_KEYS = {
+    "orc.compress", "parquet.compression", "parquet.compression.codec", 
"parquet.compression-codec"
+  };
 
   @Override
   public boolean match(Transformation<RowData> transformation) {
@@ -42,8 +47,10 @@ public class HiveSourceSinkFactory extends 
FileSystemSinkFactory {
     Configuration tableOptions = getTableOptions(partitionCommitter, 
fileWriterOperator);
     Map<String, String> tableParams = new HashMap<>(tableOptions.toMap());
     tableParams.put("path", getLocationPath(partitionCommitter, 
fileWriterOperator));
-    tableParams.putIfAbsent("format", resolveWriteFormat(fileWriterOperator));
+    Object bucketsBuilder = getBucketsBuilder(fileWriterOperator);
+    tableParams.putIfAbsent("format", resolveWriteFormat(bucketsBuilder));
     tableParams.put("connector", "hive");
+    addHiveCompressionParams(bucketsBuilder, tableParams);
     return tableParams;
   }
 
@@ -57,15 +64,21 @@ public class HiveSourceSinkFactory extends 
FileSystemSinkFactory {
     return "hive";
   }
 
+  private String resolveWriteFormat(Object bucketsBuilder) {
+    String format = resolveFormatFromBucketsBuilder(bucketsBuilder);
+    if (format != null) {
+      return format;
+    }
+    return getDefaultFormat();
+  }
+
   @Override
-  protected String resolveFormatFromHadoopBulkWriterFactory(Object 
writerFactory) {
-    Class<?> factoryClass = writerFactory.getClass();
-    if (factoryClass.getName().contains("HiveBulkWriterFactory")) {
-      Object hiveWriterFactory =
-          ReflectUtils.getObjectField(factoryClass, writerFactory, "factory");
+  protected String resolveFormatFromBucketsBuilder(Object bucketsBuilder) {
+    Object hiveWriterFactory = 
getHiveWriterFactoryFromBucketsBuilder(bucketsBuilder);
+    if (hiveWriterFactory != null) {
       return resolveFormatFromHiveWriterFactory(hiveWriterFactory);
     }
-    return super.resolveFormatFromHadoopBulkWriterFactory(writerFactory);
+    return super.resolveFormatFromBucketsBuilder(bucketsBuilder);
   }
 
   private String resolveFormatFromHiveWriterFactory(Object hiveWriterFactory) {
@@ -96,4 +109,75 @@ public class HiveSourceSinkFactory extends 
FileSystemSinkFactory {
             ReflectUtils.getObjectField(factoryClass, hiveWriterFactory, 
"hiveOutputFormatClz");
     return inferFormatFromClassName(outputFormatClz.getName());
   }
+
+  private void addHiveCompressionParams(Object bucketsBuilder, Map<String, 
String> tableParams) {
+    Object hiveWriterFactory = 
getHiveWriterFactoryFromBucketsBuilder(bucketsBuilder);
+    if (hiveWriterFactory == null) {
+      return;
+    }
+    Properties tableProperties =
+        (Properties) ReflectUtils.tryGetObjectField(hiveWriterFactory, 
"tableProperties");
+    addNativeCompressionParamFromTableProperties(tableProperties, tableParams);
+  }
+
+  private Object getBucketsBuilder(OneInputStreamOperator<?, ?> 
fileWriterOperator) {
+    return ReflectUtils.getObjectField(
+        ABSTRACT_STREAMING_WRITER_CLASS, fileWriterOperator, "bucketsBuilder");
+  }
+
+  private Object getHiveWriterFactoryFromBucketsBuilder(Object bucketsBuilder) 
{
+    Object writerFactory = ReflectUtils.tryGetObjectField(bucketsBuilder, 
"writerFactory");
+    if (writerFactory == null
+        || 
!writerFactory.getClass().getName().contains("HiveBulkWriterFactory")) {
+      return null;
+    }
+    return ReflectUtils.getObjectField(writerFactory.getClass(), 
writerFactory, "factory");
+  }
+
+  static void addNativeCompressionParamFromTableProperties(
+      Properties tableProperties, Map<String, String> tableParams) {
+    if (!isParquetFormat(tableParams.get("format"))) {
+      tableParams.remove(COMPRESSION_KIND);
+      return;
+    }
+    if (tableProperties == null) {
+      return;
+    }
+
+    String compressionKind = resolveCompressionKind(tableProperties);
+    if (compressionKind != null) {
+      tableParams.put(COMPRESSION_KIND, compressionKind);
+    }
+  }
+
+  private static boolean isParquetFormat(String format) {
+    return format != null && "parquet".equalsIgnoreCase(format.trim());
+  }
+
+  private static String resolveCompressionKind(Properties tableProperties) {
+    for (String key : SUPPORTED_COMPRESSION_TABLE_KEYS) {
+      String compressionKind = 
normalizeCompressionKind(tableProperties.getProperty(key));
+      if (compressionKind != null) {
+        return compressionKind;
+      }
+    }
+    return null;
+  }
+
+  static String normalizeCompressionKind(String compression) {
+    if (compression == null) {
+      return null;
+    }
+    final Map<String, String> supportedCompressionKinds =
+        Map.ofEntries(
+            Map.entry("snappy", "snappy"),
+            Map.entry("gzip", "gzip"),
+            Map.entry("zstd", "zstd"),
+            Map.entry("zstandard", "zstd"),
+            Map.entry("lz4", "lz4"),
+            Map.entry("lzo", "lzo"),
+            Map.entry("zlib", "zlib"),
+            Map.entry("deflate", "zlib"));
+    return 
supportedCompressionKinds.getOrDefault(compression.trim().toLowerCase(), null);
+  }
 }
diff --git 
a/gluten-flink/runtime/src/main/java/org/apache/gluten/table/runtime/operators/GlutenStreamingFileWriterOperator.java
 
b/gluten-flink/runtime/src/main/java/org/apache/gluten/table/runtime/operators/GlutenStreamingFileWriterOperator.java
index 833b2c452a..0f4eee8411 100644
--- 
a/gluten-flink/runtime/src/main/java/org/apache/gluten/table/runtime/operators/GlutenStreamingFileWriterOperator.java
+++ 
b/gluten-flink/runtime/src/main/java/org/apache/gluten/table/runtime/operators/GlutenStreamingFileWriterOperator.java
@@ -95,11 +95,15 @@ public class GlutenStreamingFileWriterOperator<IN>
       }
       restoredCheckpointRecords = records.toArray(new String[0]);
     }
-    LOG.info(
-        "Restore native file writer state for operator {}, restored {}, 
records {}",
-        getDescription(),
-        context.isRestored(),
-        Arrays.toString(restoredCheckpointRecords));
+    if (restoredCheckpointRecords != null) {
+      LOG.info(
+          "Restore native file writer state for operator {}, restored {}, 
records {}, contents {}",
+          getDescription(),
+          context.isRestored(),
+          restoredCheckpointRecords.length,
+          Arrays.toString(
+              restoredCheckpointRecords != null ? restoredCheckpointRecords : 
new String[0]));
+    }
     if (task == null) {
       initSession();
     }
diff --git 
a/gluten-flink/ut/src/test/java/org/apache/gluten/velox/HiveSourceSinkFactoryTest.java
 
b/gluten-flink/ut/src/test/java/org/apache/gluten/velox/HiveSourceSinkFactoryTest.java
new file mode 100644
index 0000000000..1b1dabfbe7
--- /dev/null
+++ 
b/gluten-flink/ut/src/test/java/org/apache/gluten/velox/HiveSourceSinkFactoryTest.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.gluten.velox;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class HiveSourceSinkFactoryTest {
+
+  @Test
+  void addNativeCompressionParamMapsSupportedParquetCodecs() {
+    String[][] compressionCodecs = {
+      {"SNAPPY", "snappy"},
+      {"GZIP", "gzip"},
+      {"zstandard", "zstd"},
+      {"LZ4", "lz4"},
+      {"LZO", "lzo"},
+      {"deflate", "zlib"}
+    };
+
+    for (String[] compressionCodec : compressionCodecs) {
+      Properties tableProperties = new Properties();
+      tableProperties.setProperty("parquet.compression", compressionCodec[0]);
+
+      Map<String, String> tableParams = new HashMap<>();
+      tableParams.put("format", "parquet");
+      HiveSourceSinkFactory.addNativeCompressionParamFromTableProperties(
+          tableProperties, tableParams);
+
+      assertThat(tableParams)
+          .containsEntry("sink.file.compression", compressionCodec[1])
+          .doesNotContainKey("parquet.compression");
+    }
+  }
+
+  @Test
+  void addNativeCompressionParamReadsSupportedParquetCompressionKeys() {
+    Properties tableProperties = new Properties();
+    tableProperties.setProperty("parquet.compression.codec", "SNAPPY");
+
+    Map<String, String> tableParams = new HashMap<>();
+    tableParams.put("format", "parquet");
+    HiveSourceSinkFactory.addNativeCompressionParamFromTableProperties(
+        tableProperties, tableParams);
+
+    assertThat(tableParams).containsEntry("sink.file.compression", "snappy");
+  }
+
+  @Test
+  void addNativeCompressionParamDoesNotProduceConfigForUnsupportedFormats() {
+    for (String format : new String[] {"orc", "json", "csv", "hive"}) {
+      Properties tableProperties = new Properties();
+      tableProperties.setProperty("parquet.compression", "SNAPPY");
+
+      Map<String, String> tableParams = new HashMap<>();
+      tableParams.put("format", format);
+      tableParams.put("sink.file.compression", "snappy");
+      HiveSourceSinkFactory.addNativeCompressionParamFromTableProperties(
+          tableProperties, tableParams);
+
+      assertThat(tableParams)
+          .containsEntry("format", format)
+          .doesNotContainKey("sink.file.compression");
+    }
+  }
+
+  @Test
+  void 
addNativeCompressionParamDoesNotProduceConfigForUnsupportedParquetCodecs() {
+    for (String compressionCodec : new String[] {"brotli", 
"org.example.SnappyCodec"}) {
+      Properties tableProperties = new Properties();
+      tableProperties.setProperty("parquet.compression", compressionCodec);
+
+      Map<String, String> tableParams = new HashMap<>();
+      tableParams.put("format", "parquet");
+      HiveSourceSinkFactory.addNativeCompressionParamFromTableProperties(
+          tableProperties, tableParams);
+
+      assertThat(tableParams)
+          .containsEntry("format", "parquet")
+          .doesNotContainKey("sink.file.compression");
+    }
+  }
+
+  @Test
+  void 
addNativeCompressionParamDoesNotProduceConfigForUnsupportedCompressionKeys() {
+    Properties tableProperties = new Properties();
+    tableProperties.setProperty("custom.compress", "SNAPPY");
+    tableProperties.setProperty("custom.codec", "GZIP");
+
+    Map<String, String> tableParams = new HashMap<>();
+    tableParams.put("format", "parquet");
+    HiveSourceSinkFactory.addNativeCompressionParamFromTableProperties(
+        tableProperties, tableParams);
+
+    assertThat(tableParams)
+        .containsEntry("format", "parquet")
+        .doesNotContainKey("sink.file.compression");
+  }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to