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 0dbb30fb21 [GLUTEN-12459][FLINK] feat: supprt RowKind via $row_kind 
TINYINT column between Flink and Velox (#12510)
0dbb30fb21 is described below

commit 0dbb30fb2178273bff3c0b739246de574c8a0fef
Author: GGboom <[email protected]>
AuthorDate: Tue Sep 1 16:14:27 2026 +0800

    [GLUTEN-12459][FLINK] feat: supprt RowKind via $row_kind TINYINT column 
between Flink and Velox (#12510)
    
    * Carry RowKind via $row_kind TINYINT column between Flink and Velox
---
 .github/workflows/flink.yml                        |   5 +-
 gluten-flink/docs/Flink.md                         |   4 +-
 .../gluten/vectorized/ArrowVectorAccessor.java     |  13 ++
 .../gluten/vectorized/ArrowVectorWriter.java       |  26 ++++
 .../vectorized/FlinkRowToVLVectorConvertor.java    |  47 +++++-
 .../FlinkRowToVLVectorConvertorTest.java           | 173 +++++++++++++++++++++
 6 files changed, 261 insertions(+), 7 deletions(-)

diff --git a/.github/workflows/flink.yml b/.github/workflows/flink.yml
index ef8c36c311..37a6acbf88 100644
--- a/.github/workflows/flink.yml
+++ b/.github/workflows/flink.yml
@@ -87,8 +87,9 @@ jobs:
           export VELOX_DEPENDENCY_SOURCE=BUNDLED
           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 
42a77de002707b6811791ba2d728f169aeb5d7bb
+          git clone -b gluten-20260829 https://github.com/bigo-sg/velox4j.git
+          cd velox4j
+          git reset --hard 26c7715278e6f6795084f6334998cb2ce382f7aa
           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 3a4fcf9fdb..b5586eea22 100644
--- a/gluten-flink/docs/Flink.md
+++ b/gluten-flink/docs/Flink.md
@@ -46,9 +46,9 @@ As some features have not been committed to upstream, you 
have to use the follow
 
 ```bash
 ## fetch velox4j code
-git clone -b gluten-0530 https://github.com/bigo-sg/velox4j.git
+git clone -b gluten-20260829 https://github.com/bigo-sg/velox4j.git
 cd velox4j
-git reset --hard 42a77de002707b6811791ba2d728f169aeb5d7bb
+git reset --hard 26c7715278e6f6795084f6334998cb2ce382f7aa
 mvn clean install -DskipTests -Dgpg.skip -Dspotless.skip=true
 ```
 **Get gluten**
diff --git 
a/gluten-flink/runtime/src/main/java/org/apache/gluten/vectorized/ArrowVectorAccessor.java
 
b/gluten-flink/runtime/src/main/java/org/apache/gluten/vectorized/ArrowVectorAccessor.java
index c397e6fada..41ec500f38 100644
--- 
a/gluten-flink/runtime/src/main/java/org/apache/gluten/vectorized/ArrowVectorAccessor.java
+++ 
b/gluten-flink/runtime/src/main/java/org/apache/gluten/vectorized/ArrowVectorAccessor.java
@@ -31,6 +31,7 @@ import org.apache.arrow.vector.FieldVector;
 import org.apache.arrow.vector.Float8Vector;
 import org.apache.arrow.vector.IntVector;
 import org.apache.arrow.vector.TimeStampMicroVector;
+import org.apache.arrow.vector.TinyIntVector;
 import org.apache.arrow.vector.VarCharVector;
 import org.apache.arrow.vector.complex.ListVector;
 import org.apache.arrow.vector.complex.MapVector;
@@ -56,6 +57,7 @@ public abstract class ArrowVectorAccessor {
       Map.ofEntries(
           Map.entry(BitVector.class, vector -> new 
BooleanVectorAccessor(vector)),
           Map.entry(IntVector.class, vector -> new IntVectorAccessor(vector)),
+          Map.entry(TinyIntVector.class, vector -> new 
TinyIntVectorAccessor(vector)),
           Map.entry(BigIntVector.class, vector -> new 
BigIntVectorAccessor(vector)),
           Map.entry(Float8Vector.class, vector -> new 
DoubleVectorAccessor(vector)),
           Map.entry(DecimalVector.class, vector -> new 
DecimalVectorAccessor(vector)),
@@ -128,6 +130,17 @@ class IntVectorAccessor extends 
BaseArrowVectorAccessor<IntVector> {
   }
 }
 
+class TinyIntVectorAccessor extends BaseArrowVectorAccessor<TinyIntVector> {
+  public TinyIntVectorAccessor(FieldVector vector) {
+    super(vector);
+  }
+
+  @Override
+  protected Object getImpl(int rowIndex) {
+    return typedVector.get(rowIndex);
+  }
+}
+
 class BigIntVectorAccessor extends BaseArrowVectorAccessor<BigIntVector> {
 
   public BigIntVectorAccessor(FieldVector vector) {
diff --git 
a/gluten-flink/runtime/src/main/java/org/apache/gluten/vectorized/ArrowVectorWriter.java
 
b/gluten-flink/runtime/src/main/java/org/apache/gluten/vectorized/ArrowVectorWriter.java
index e6a511a492..d30280077f 100644
--- 
a/gluten-flink/runtime/src/main/java/org/apache/gluten/vectorized/ArrowVectorWriter.java
+++ 
b/gluten-flink/runtime/src/main/java/org/apache/gluten/vectorized/ArrowVectorWriter.java
@@ -50,6 +50,10 @@ public abstract class ArrowVectorWriter {
           Map.entry(
               IntegerType.class,
               (fieldType, allocator, vector) -> new IntVectorWriter(fieldType, 
allocator, vector)),
+          Map.entry(
+              TinyIntType.class,
+              (fieldType, allocator, vector) ->
+                  new TinyIntVectorWriter(fieldType, allocator, vector)),
           Map.entry(
               BooleanType.class,
               (fieldType, allocator, vector) ->
@@ -154,6 +158,7 @@ class FieldVectorCreator {
       Map.ofEntries(
           Map.entry(BooleanType.class, (dataType, timeZoneId) -> 
ArrowType.Bool.INSTANCE),
           Map.entry(IntegerType.class, (dataType, timeZoneId) -> new 
ArrowType.Int(8 * 4, true)),
+          Map.entry(TinyIntType.class, (dataType, timeZoneId) -> new 
ArrowType.Int(8 * 1, true)),
           Map.entry(BigIntType.class, (dataType, timeZoneId) -> new 
ArrowType.Int(8 * 8, true)),
           Map.entry(
               DoubleType.class,
@@ -284,6 +289,27 @@ class IntVectorWriter extends BaseVectorWriter<IntVector, 
Integer> {
   }
 }
 
+class TinyIntVectorWriter extends BaseVectorWriter<TinyIntVector, Byte> {
+  public TinyIntVectorWriter(Type fieldType, BufferAllocator allocator, 
FieldVector vector) {
+    super(vector);
+  }
+
+  @Override
+  protected Byte getValue(RowData rowData, int fieldIndex) {
+    return rowData.getByte(fieldIndex);
+  }
+
+  @Override
+  protected Byte getValue(ArrayData arrayData, int index) {
+    return arrayData.getByte(index);
+  }
+
+  @Override
+  protected void setValue(int index, Byte value) {
+    this.typedVector.setSafe(index, value);
+  }
+}
+
 class BooleanVectorWriter extends BaseVectorWriter<BitVector, Boolean> {
   public BooleanVectorWriter(Type fieldType, BufferAllocator allocator, 
FieldVector vector) {
     super(vector);
diff --git 
a/gluten-flink/runtime/src/main/java/org/apache/gluten/vectorized/FlinkRowToVLVectorConvertor.java
 
b/gluten-flink/runtime/src/main/java/org/apache/gluten/vectorized/FlinkRowToVLVectorConvertor.java
index 3abb06c511..04f6dc5769 100644
--- 
a/gluten-flink/runtime/src/main/java/org/apache/gluten/vectorized/FlinkRowToVLVectorConvertor.java
+++ 
b/gluten-flink/runtime/src/main/java/org/apache/gluten/vectorized/FlinkRowToVLVectorConvertor.java
@@ -25,9 +25,11 @@ import io.github.zhztheplayer.velox4j.type.Type;
 
 import org.apache.flink.table.data.GenericRowData;
 import org.apache.flink.table.data.RowData;
+import org.apache.flink.types.RowKind;
 
 import org.apache.arrow.memory.BufferAllocator;
 import org.apache.arrow.vector.FieldVector;
+import org.apache.arrow.vector.TinyIntVector;
 import org.apache.arrow.vector.table.Table;
 
 import java.util.ArrayList;
@@ -35,11 +37,19 @@ import java.util.List;
 
 /** Converter between velox RowVector and Flink RowData. */
 public class FlinkRowToVLVectorConvertor {
+
+  // Matches the C++ kRowKindColumnName in 
velox/experimental/stateful/RowKind.h.
+  // The merged RowVector carries per-row RowKind bytes as a trailing TINYINT 
column
+  // with this name. Java appends it in fromRowData; C++ strips it via
+  // StreamRecord::create and re-appends it on output via
+  // StreamRecord::toMergedRowVector (only when the stream is not appendOnly).
+  public static final String ROW_KIND_COLUMN_NAME = "$row_kind";
+
   public static RowVector fromRowData(
       RowData row, BufferAllocator allocator, Session session, RowType 
rowType) {
-    List<FieldVector> arrowVectors = new ArrayList<>(rowType.size());
     List<Type> fieldTypes = rowType.getChildren();
     List<String> fieldNames = rowType.getNames();
+    List<FieldVector> arrowVectors = new ArrayList<>(rowType.size() + 1);
     for (int i = 0; i < rowType.size(); i++) {
       ArrowVectorWriter writer =
           ArrowVectorWriter.create(fieldNames.get(i), fieldTypes.get(i), 
allocator);
@@ -47,6 +57,11 @@ public class FlinkRowToVLVectorConvertor {
       writer.finish();
       arrowVectors.add(i, writer.getVector());
     }
+    TinyIntVector rowKindVector = new TinyIntVector(ROW_KIND_COLUMN_NAME, 
allocator);
+    rowKindVector.allocateNew(1);
+    rowKindVector.setSafe(0, row.getRowKind().toByteValue());
+    rowKindVector.setValueCount(1);
+    arrowVectors.add(rowKindVector);
 
     return session.arrowOps().fromArrowTable(allocator, new 
Table(arrowVectors));
   }
@@ -62,14 +77,20 @@ public class FlinkRowToVLVectorConvertor {
       // The result is StructVector
       structVector = Arrow.toArrowVector(allocator, loadedVector);
       final List<FieldVector> fieldVectors = 
structVector.getChildrenFromFields();
-      List<ArrowVectorAccessor> accessors = 
buildArrowVectorAccessors(fieldVectors);
+      List<ArrowVectorAccessor> accessors =
+          buildArrowVectorAccessors(fieldVectors.subList(0, rowType.size()));
+      byte[] rowKinds = extractRowKindsIfPresent(fieldVectors, rowType.size(), 
rowVector.getSize());
       List<RowData> rowDatas = new ArrayList<>(rowVector.getSize());
       for (int j = 0; j < rowVector.getSize(); j++) {
         Object[] fieldValues = new Object[rowType.size()];
         for (int i = 0; i < rowType.size(); i++) {
           fieldValues[i] = accessors.get(i).get(j);
         }
-        rowDatas.add(GenericRowData.of(fieldValues));
+        if (rowKinds != null) {
+          
rowDatas.add(GenericRowData.ofKind(RowKind.fromByteValue(rowKinds[j]), 
fieldValues));
+        } else {
+          rowDatas.add(GenericRowData.of(fieldValues));
+        }
       }
       return rowDatas;
     } finally {
@@ -85,6 +106,26 @@ public class FlinkRowToVLVectorConvertor {
     }
   }
 
+  private static byte[] extractRowKindsIfPresent(
+      List<FieldVector> fieldVectors, int schemaSize, int rowCount) {
+    if (fieldVectors.size() != schemaSize + 1) {
+      return null;
+    }
+    FieldVector lastVector = fieldVectors.get(schemaSize);
+    if (!ROW_KIND_COLUMN_NAME.equals(lastVector.getField().getName())) {
+      return null;
+    }
+    if (!(lastVector instanceof TinyIntVector)) {
+      return null;
+    }
+    TinyIntVector tinyInt = (TinyIntVector) lastVector;
+    byte[] rowKinds = new byte[rowCount];
+    for (int i = 0; i < rowCount; i++) {
+      rowKinds[i] = tinyInt.get(i);
+    }
+    return rowKinds;
+  }
+
   private static List<ArrowVectorAccessor> 
buildArrowVectorAccessors(List<FieldVector> vectors) {
     List<ArrowVectorAccessor> accessors = new ArrayList<>(vectors.size());
     for (int i = 0; i < vectors.size(); ++i) {
diff --git 
a/gluten-flink/ut/src/test/java/org/apache/gluten/vectorized/FlinkRowToVLVectorConvertorTest.java
 
b/gluten-flink/ut/src/test/java/org/apache/gluten/vectorized/FlinkRowToVLVectorConvertorTest.java
new file mode 100644
index 0000000000..36558d94e5
--- /dev/null
+++ 
b/gluten-flink/ut/src/test/java/org/apache/gluten/vectorized/FlinkRowToVLVectorConvertorTest.java
@@ -0,0 +1,173 @@
+/*
+ * 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.vectorized;
+
+import org.apache.gluten.table.runtime.stream.common.Velox4jEnvironment;
+
+import io.github.zhztheplayer.velox4j.Velox4j;
+import io.github.zhztheplayer.velox4j.data.RowVector;
+import io.github.zhztheplayer.velox4j.memory.AllocationListener;
+import io.github.zhztheplayer.velox4j.memory.MemoryManager;
+import io.github.zhztheplayer.velox4j.session.Session;
+
+import org.apache.flink.table.data.GenericRowData;
+import org.apache.flink.table.data.RowData;
+import org.apache.flink.table.data.StringData;
+import org.apache.flink.table.types.logical.IntType;
+import org.apache.flink.table.types.logical.LogicalType;
+import org.apache.flink.table.types.logical.RowType;
+import org.apache.flink.table.types.logical.VarCharType;
+import org.apache.flink.types.RowKind;
+
+import org.apache.arrow.memory.BufferAllocator;
+import org.apache.arrow.memory.RootAllocator;
+import org.apache.arrow.vector.FieldVector;
+import org.apache.arrow.vector.IntVector;
+import org.apache.arrow.vector.TinyIntVector;
+import org.apache.arrow.vector.VarCharVector;
+import org.apache.arrow.vector.table.Table;
+import org.apache.arrow.vector.types.pojo.ArrowType;
+import org.apache.arrow.vector.types.pojo.FieldType;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class FlinkRowToVLVectorConvertorTest {
+  private static MemoryManager memoryManager;
+  private static Session session;
+  private static BufferAllocator allocator;
+  private static RowType flinkRowType;
+  private static io.github.zhztheplayer.velox4j.type.RowType veloxRowType;
+
+  @BeforeAll
+  public static void initializeVelox() {
+    Velox4jEnvironment.initializeOnce();
+    memoryManager = MemoryManager.create(AllocationListener.NOOP);
+    session = Velox4j.newSession(memoryManager);
+    allocator = new RootAllocator(Long.MAX_VALUE);
+    flinkRowType =
+        RowType.of(
+            new LogicalType[] {new IntType(), new 
VarCharType(VarCharType.MAX_LENGTH)},
+            new String[] {"id", "name"});
+    veloxRowType =
+        (io.github.zhztheplayer.velox4j.type.RowType)
+            org.apache.gluten.util.LogicalTypeConverter.toVLType(flinkRowType);
+  }
+
+  @AfterAll
+  public static void tearDownVelox() {
+    if (allocator != null) {
+      allocator.close();
+      allocator = null;
+    }
+    if (session != null) {
+      session.close();
+      session = null;
+    }
+    if (memoryManager != null) {
+      memoryManager.close();
+      memoryManager = null;
+    }
+  }
+
+  @Test
+  public void testToRowDataReadsRowKindFromMergedRowVector() {
+    RowKind[] kinds = {
+      RowKind.INSERT, RowKind.UPDATE_BEFORE, RowKind.UPDATE_AFTER, 
RowKind.DELETE,
+    };
+    List<FieldVector> arrowVectors = new ArrayList<>(3);
+    IntVector idVec = new IntVector("id", allocator);
+    idVec.allocateNew(4);
+    VarCharVector nameVec = new VarCharVector("name", allocator);
+    nameVec.allocateNew(4);
+    TinyIntVector rowKindVec =
+        new TinyIntVector(
+            FlinkRowToVLVectorConvertor.ROW_KIND_COLUMN_NAME,
+            new FieldType(false, new ArrowType.Int(8, true), null),
+            allocator);
+    rowKindVec.allocateNew(4);
+    for (int i = 0; i < kinds.length; i++) {
+      idVec.setSafe(i, 100 + i);
+      nameVec.setSafe(i, ("u" + 
i).getBytes(java.nio.charset.StandardCharsets.UTF_8));
+      rowKindVec.setSafe(i, kinds[i].toByteValue());
+    }
+    idVec.setValueCount(4);
+    nameVec.setValueCount(4);
+    rowKindVec.setValueCount(4);
+    arrowVectors.add(idVec);
+    arrowVectors.add(nameVec);
+    arrowVectors.add(rowKindVec);
+    RowVector rv = session.arrowOps().fromArrowTable(allocator, new 
Table(arrowVectors));
+    try {
+      List<RowData> rows = FlinkRowToVLVectorConvertor.toRowData(rv, 
allocator, veloxRowType);
+      assertThat(rows).hasSize(kinds.length);
+      for (int i = 0; i < kinds.length; i++) {
+        assertThat(rows.get(i).getRowKind()).isEqualTo(kinds[i]);
+        assertThat(rows.get(i).getInt(0)).isEqualTo(100 + i);
+        
assertThat(rows.get(i).getString(1)).isEqualTo(StringData.fromString("u" + i));
+      }
+    } finally {
+      rv.close();
+      idVec.close();
+      nameVec.close();
+      rowKindVec.close();
+    }
+  }
+
+  @Test
+  public void testToRowDataFallsBackToInsertWhenNoRowKindColumn() {
+    RowData input = GenericRowData.of(42, StringData.fromString("alice"));
+    RowVector rv = FlinkRowToVLVectorConvertor.fromRowData(input, allocator, 
session, veloxRowType);
+    try {
+      List<RowData> rows = FlinkRowToVLVectorConvertor.toRowData(rv, 
allocator, veloxRowType);
+      assertThat(rows).hasSize(1);
+      assertThat(rows.get(0).getRowKind()).isEqualTo(RowKind.INSERT);
+      assertThat(rows.get(0).getInt(0)).isEqualTo(42);
+      
assertThat(rows.get(0).getString(1)).isEqualTo(StringData.fromString("alice"));
+    } finally {
+      rv.close();
+    }
+  }
+
+  @Test
+  public void testFromRowDataAppendsRowKindColumnForAllKinds() {
+    RowKind[] kinds = {
+      RowKind.INSERT, RowKind.UPDATE_BEFORE, RowKind.UPDATE_AFTER, 
RowKind.DELETE,
+    };
+    for (RowKind kind : kinds) {
+      RowData input =
+          GenericRowData.ofKind(kind, 7, StringData.fromString("row-" + 
kind.shortString()));
+      RowVector rv =
+          FlinkRowToVLVectorConvertor.fromRowData(input, allocator, session, 
veloxRowType);
+      try {
+        List<RowData> rows = FlinkRowToVLVectorConvertor.toRowData(rv, 
allocator, veloxRowType);
+        assertThat(rows).hasSize(1);
+        assertThat(rows.get(0).getRowKind()).isEqualTo(kind);
+        assertThat(rows.get(0).getInt(0)).isEqualTo(7);
+        assertThat(rows.get(0).getString(1))
+            .isEqualTo(StringData.fromString("row-" + kind.shortString()));
+      } finally {
+        rv.close();
+      }
+    }
+  }
+}


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

Reply via email to