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

loserwang1024 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/flink-cdc.git


The following commit(s) were added to refs/heads/master by this push:
     new 7ecab6a8f [FLINK-40690][cdc-connect][fluss] Preserve nested row field 
positions (#4549)
7ecab6a8f is described below

commit 7ecab6a8f0b8887bb18ac77988a5c9c1f4c01513
Author: Xiaobing Fang <[email protected]>
AuthorDate: Mon Sep 21 10:41:52 2026 +0800

    [FLINK-40690][cdc-connect][fluss] Preserve nested row field positions 
(#4549)
---
 .../connectors/fluss/sink/row/CdcAsFlussRow.java   |   3 +-
 .../fluss/sink/FlussSinkPipelineITCase.java        |  45 ++++++++
 .../fluss/sink/row/CdcAsFlussRowTest.java          | 117 +++++++++++++++++++++
 3 files changed, 163 insertions(+), 2 deletions(-)

diff --git 
a/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-fluss/src/main/java/org/apache/flink/cdc/connectors/fluss/sink/row/CdcAsFlussRow.java
 
b/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-fluss/src/main/java/org/apache/flink/cdc/connectors/fluss/sink/row/CdcAsFlussRow.java
index 4a97ebc22..b107e897b 100644
--- 
a/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-fluss/src/main/java/org/apache/flink/cdc/connectors/fluss/sink/row/CdcAsFlussRow.java
+++ 
b/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-fluss/src/main/java/org/apache/flink/cdc/connectors/fluss/sink/row/CdcAsFlussRow.java
@@ -181,8 +181,7 @@ public class CdcAsFlussRow implements InternalRow {
 
     @Override
     public InternalRow getRow(int i, int numFields) {
-        return new CdcAsFlussRow(
-                cdcRecord.getRow(indexMapping.get(i), numFields), numFields, 
indexMapping);
+        return CdcAsFlussRow.replace(cdcRecord.getRow(indexMapping.get(i), 
numFields));
     }
 
     @VisibleForTesting
diff --git 
a/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-fluss/src/test/java/org/apache/flink/cdc/connectors/fluss/sink/FlussSinkPipelineITCase.java
 
b/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-fluss/src/test/java/org/apache/flink/cdc/connectors/fluss/sink/FlussSinkPipelineITCase.java
index f371cdd0e..455f1e8cf 100644
--- 
a/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-fluss/src/test/java/org/apache/flink/cdc/connectors/fluss/sink/FlussSinkPipelineITCase.java
+++ 
b/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-fluss/src/test/java/org/apache/flink/cdc/connectors/fluss/sink/FlussSinkPipelineITCase.java
@@ -18,6 +18,7 @@
 package org.apache.flink.cdc.connectors.fluss.sink;
 
 import org.apache.flink.cdc.common.configuration.Configuration;
+import org.apache.flink.cdc.common.data.GenericRecordData;
 import org.apache.flink.cdc.common.data.binary.BinaryStringData;
 import org.apache.flink.cdc.common.event.AddColumnEvent;
 import org.apache.flink.cdc.common.event.CreateTableEvent;
@@ -75,6 +76,7 @@ import static 
org.apache.flink.configuration.CoreOptions.ALWAYS_PARENT_FIRST_LOA
 import static org.apache.fluss.config.ConfigOptions.BOOTSTRAP_SERVERS;
 import static 
org.apache.fluss.flink.source.testutils.FlinkRowAssertionsUtils.assertResultsIgnoreOrder;
 import static 
org.apache.fluss.server.testutils.FlussClusterExtension.BUILTIN_DATABASE;
+import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
 /** ITCase for Fluss Pipeline. */
@@ -508,6 +510,49 @@ public class FlussSinkPipelineITCase {
         checkResult(TABLE_2, Arrays.asList("+I[1, 1]", "+I[2, 2]", "+I[3, 
3]"));
     }
 
+    @Test
+    void testNestedRowWithReorderedColumns() throws Exception {
+        tBatchEnv
+                .executeSql(
+                        String.format(
+                                "CREATE TABLE %s.%s (id INT, nested ROW<a INT, 
b INT>,"
+                                        + " PRIMARY KEY (id) NOT ENFORCED)",
+                                TABLE_1.getSchemaName(), 
TABLE_1.getTableName()))
+                .await();
+
+        RowType nestedType =
+                DataTypes.ROW(
+                        DataTypes.FIELD("a", DataTypes.INT()),
+                        DataTypes.FIELD("b", DataTypes.INT()));
+        Schema schema =
+                Schema.newBuilder()
+                        .physicalColumn("nested", nestedType)
+                        .physicalColumn("id", DataTypes.INT().notNull())
+                        .primaryKey("id")
+                        .build();
+        BinaryRecordDataGenerator generator =
+                new BinaryRecordDataGenerator(RowType.of(nestedType, 
DataTypes.INT().notNull()));
+        List<Event> events =
+                Arrays.asList(
+                        new CreateTableEvent(TABLE_1, schema),
+                        DataChangeEvent.insertEvent(
+                                TABLE_1,
+                                generator.generate(new Object[] 
{GenericRecordData.of(7, 8), 42})));
+
+        composeAndExecuteInEvolveMode(Collections.singletonList(events));
+        List<Row> results = new ArrayList<>();
+        try (CloseableIterator<Row> rows =
+                tBatchEnv
+                        .executeSql(
+                                String.format(
+                                        "SELECT id, nested FROM %s.%s",
+                                        TABLE_1.getSchemaName(), 
TABLE_1.getTableName()))
+                        .collect()) {
+            rows.forEachRemaining(results::add);
+        }
+        assertThat(results).containsExactly(Row.of(42, Row.of(7, 8)));
+    }
+
     @Test
     void testInsertExistTableWithMoreColumns() throws Exception {
         // create a fluss table with 2 columns but then cdc read a source 
table with 3 columns
diff --git 
a/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-fluss/src/test/java/org/apache/flink/cdc/connectors/fluss/sink/row/CdcAsFlussRowTest.java
 
b/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-fluss/src/test/java/org/apache/flink/cdc/connectors/fluss/sink/row/CdcAsFlussRowTest.java
new file mode 100644
index 000000000..fcd44278f
--- /dev/null
+++ 
b/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-fluss/src/test/java/org/apache/flink/cdc/connectors/fluss/sink/row/CdcAsFlussRowTest.java
@@ -0,0 +1,117 @@
+/*
+ * 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.flink.cdc.connectors.fluss.sink.row;
+
+import org.apache.flink.cdc.common.data.GenericRecordData;
+import org.apache.flink.cdc.common.data.binary.BinaryRecordData;
+import org.apache.flink.cdc.common.types.DataTypes;
+import org.apache.flink.cdc.runtime.typeutils.BinaryRecordDataGenerator;
+
+import org.apache.fluss.row.InternalRow;
+import org.junit.jupiter.api.Test;
+
+import java.util.Map;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/** Test for {@link CdcAsFlussRow}. */
+class CdcAsFlussRowTest {
+
+    @Test
+    void testNestedRowWithIndexMapping() {
+        GenericRecordData input = GenericRecordData.of(GenericRecordData.of(7, 
8), 42);
+        CdcAsFlussRow row = CdcAsFlussRow.replace(input, 2, Map.of(0, 1, 1, 
0));
+
+        assertThat(row.getInt(0)).isEqualTo(42);
+
+        InternalRow nested = row.getRow(1, 2);
+        assertThat(nested.getInt(0)).isEqualTo(7);
+        assertThat(nested.getInt(1)).isEqualTo(8);
+    }
+
+    @Test
+    void testNestedRowWithMoreChildrenThanOuterRow() {
+        GenericRecordData input = GenericRecordData.of(GenericRecordData.of(7, 
8, 9), 42);
+        CdcAsFlussRow row = CdcAsFlussRow.replace(input);
+
+        InternalRow nested = row.getRow(0, 3);
+        assertThat(nested.isNullAt(2)).isFalse();
+        assertThat(nested.getInt(0)).isEqualTo(7);
+        assertThat(nested.getInt(1)).isEqualTo(8);
+        assertThat(nested.getInt(2)).isEqualTo(9);
+    }
+
+    @Test
+    void testSparseOuterMappingKeepsNestedRowIntact() {
+        GenericRecordData input = GenericRecordData.of(GenericRecordData.of(7, 
8), 42);
+        CdcAsFlussRow row = CdcAsFlussRow.replace(input, 3, Map.of(0, 0, 2, 
1));
+
+        assertThat(row.isNullAt(1)).isTrue();
+
+        InternalRow nested = row.getRow(0, 2);
+        assertThat(nested.isNullAt(1)).isFalse();
+        assertThat(nested.getInt(0)).isEqualTo(7);
+        assertThat(nested.getInt(1)).isEqualTo(8);
+    }
+
+    @Test
+    void testNestedRowWithNullChild() {
+        GenericRecordData input = GenericRecordData.of(GenericRecordData.of(7, 
null, 8), 42);
+        CdcAsFlussRow row = CdcAsFlussRow.replace(input, 2, Map.of(0, 1, 1, 
0));
+
+        InternalRow nested = row.getRow(1, 3);
+        assertThat(nested.isNullAt(0)).isFalse();
+        assertThat(nested.getInt(0)).isEqualTo(7);
+        assertThat(nested.isNullAt(1)).isTrue();
+        assertThat(nested.isNullAt(2)).isFalse();
+        assertThat(nested.getInt(2)).isEqualTo(8);
+    }
+
+    @Test
+    void testNestedRowsWithDifferentArities() {
+        GenericRecordData innerA = GenericRecordData.of(7, 8, 9);
+        GenericRecordData innerB = GenericRecordData.of(10, 11);
+        GenericRecordData input = 
GenericRecordData.of(GenericRecordData.of(innerA, innerB), 42);
+        CdcAsFlussRow row = CdcAsFlussRow.replace(input, 2, Map.of(0, 1, 1, 
0));
+
+        InternalRow middle = row.getRow(1, 2);
+        InternalRow first = middle.getRow(0, 3);
+        InternalRow second = middle.getRow(1, 2);
+        assertThat(first.getInt(0)).isEqualTo(7);
+        assertThat(first.getInt(1)).isEqualTo(8);
+        assertThat(first.getInt(2)).isEqualTo(9);
+        assertThat(second.getInt(0)).isEqualTo(10);
+        assertThat(second.getInt(1)).isEqualTo(11);
+    }
+
+    @Test
+    void testBinaryRecordDataNestedRow() {
+        BinaryRecordDataGenerator generator =
+                new BinaryRecordDataGenerator(
+                        DataTypes.ROW(
+                                DataTypes.ROW(DataTypes.INT(), 
DataTypes.INT()), DataTypes.INT()));
+        BinaryRecordData input = generator.generate(new Object[] 
{GenericRecordData.of(7, 8), 42});
+        CdcAsFlussRow row = CdcAsFlussRow.replace(input, 2, Map.of(0, 1, 1, 
0));
+
+        assertThat(row.getInt(0)).isEqualTo(42);
+
+        InternalRow nested = row.getRow(1, 2);
+        assertThat(nested.getInt(0)).isEqualTo(7);
+        assertThat(nested.getInt(1)).isEqualTo(8);
+    }
+}

Reply via email to