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 80d136bfe3 [core] Validate the timestamp field type for 
merge_map_with_keytime (#10367)
80d136bfe3 is described below

commit 80d136bfe38d0ffbe19114a08c6046d47b47dc5c
Author: jackylee <[email protected]>
AuthorDate: Mon Oct 5 20:46:17 2026 +0800

    [core] Validate the timestamp field type for merge_map_with_keytime (#10367)
---
 .../FieldMergeMapWithKeyTimeAggFactory.java        |  9 ++++++
 .../compact/aggregate/FieldAggregatorTest.java     | 35 ++++++++++++++++++++++
 2 files changed, 44 insertions(+)

diff --git 
a/paimon-core/src/main/java/org/apache/paimon/mergetree/compact/aggregate/factory/FieldMergeMapWithKeyTimeAggFactory.java
 
b/paimon-core/src/main/java/org/apache/paimon/mergetree/compact/aggregate/factory/FieldMergeMapWithKeyTimeAggFactory.java
index 452b3e473f..7b24890924 100644
--- 
a/paimon-core/src/main/java/org/apache/paimon/mergetree/compact/aggregate/factory/FieldMergeMapWithKeyTimeAggFactory.java
+++ 
b/paimon-core/src/main/java/org/apache/paimon/mergetree/compact/aggregate/factory/FieldMergeMapWithKeyTimeAggFactory.java
@@ -21,6 +21,7 @@ package org.apache.paimon.mergetree.compact.aggregate.factory;
 import org.apache.paimon.CoreOptions;
 import 
org.apache.paimon.mergetree.compact.aggregate.FieldMergeMapWithKeyTimeAgg;
 import org.apache.paimon.types.DataType;
+import org.apache.paimon.types.DataTypeFamily;
 import org.apache.paimon.types.MapType;
 import org.apache.paimon.types.RowType;
 
@@ -77,6 +78,14 @@ public class FieldMergeMapWithKeyTimeAggFactory implements 
FieldAggregatorFactor
                     field,
                     rowType.getFieldNames());
         }
+
+        DataType tsFieldType = rowType.getTypeAt(tsFieldIndex);
+        checkArgument(
+                
tsFieldType.getTypeRoot().getFamilies().contains(DataTypeFamily.CHARACTER_STRING),
+                "Timestamp field '%s' for field '%s' must be a string type 
(CHAR/VARCHAR) but was '%s'.",
+                rowType.getFieldNames().get(tsFieldIndex),
+                field,
+                tsFieldType);
         return tsFieldIndex;
     }
 
diff --git 
a/paimon-core/src/test/java/org/apache/paimon/mergetree/compact/aggregate/FieldAggregatorTest.java
 
b/paimon-core/src/test/java/org/apache/paimon/mergetree/compact/aggregate/FieldAggregatorTest.java
index 820888d7a1..b915866e7f 100644
--- 
a/paimon-core/src/test/java/org/apache/paimon/mergetree/compact/aggregate/FieldAggregatorTest.java
+++ 
b/paimon-core/src/test/java/org/apache/paimon/mergetree/compact/aggregate/FieldAggregatorTest.java
@@ -40,6 +40,7 @@ import 
org.apache.paimon.mergetree.compact.aggregate.factory.FieldLastValueAggFa
 import 
org.apache.paimon.mergetree.compact.aggregate.factory.FieldListaggAggFactory;
 import 
org.apache.paimon.mergetree.compact.aggregate.factory.FieldMaxAggFactory;
 import 
org.apache.paimon.mergetree.compact.aggregate.factory.FieldMergeMapAggFactory;
+import 
org.apache.paimon.mergetree.compact.aggregate.factory.FieldMergeMapWithKeyTimeAggFactory;
 import 
org.apache.paimon.mergetree.compact.aggregate.factory.FieldMinAggFactory;
 import 
org.apache.paimon.mergetree.compact.aggregate.factory.FieldNestedPartialUpdateAggFactory;
 import 
org.apache.paimon.mergetree.compact.aggregate.factory.FieldNestedUpdateAggFactory;
@@ -3071,6 +3072,40 @@ public class FieldAggregatorTest {
                 createExpectedEntry("key3", "C"));
     }
 
+    @Test
+    public void 
testFieldMergeMapWithKeyTimeAggFactoryRejectsNonStringTsField() {
+        // The merge path reads the ts field via InternalRow#getString, so a 
non-string ts field
+        // (declared BIGINT/TIMESTAMP/INT, which is natural for a "ts" field) 
is accepted at DDL
+        // today and only throws ClassCastException at the first 
merge/compaction, naming neither
+        // the field nor the function. Validate it is a string type at factory 
creation.
+        MapType mapType =
+                DataTypes.MAP(
+                        DataTypes.STRING(),
+                        DataTypes.ROW(
+                                DataTypes.FIELD(0, "actual_value", 
DataTypes.STRING()),
+                                DataTypes.FIELD(1, "dbsync_ts", 
DataTypes.BIGINT())));
+        assertThatThrownBy(
+                        () ->
+                                new FieldMergeMapWithKeyTimeAggFactory()
+                                        .create(mapType, new CoreOptions(new 
HashMap<>()), "f"))
+                .isInstanceOf(IllegalArgumentException.class)
+                .hasMessageContaining("must be a string type");
+    }
+
+    @Test
+    public void testFieldMergeMapWithKeyTimeAggFactoryAcceptsStringTsField() {
+        MapType mapType =
+                DataTypes.MAP(
+                        DataTypes.STRING(),
+                        DataTypes.ROW(
+                                DataTypes.FIELD(0, "actual_value", 
DataTypes.STRING()),
+                                DataTypes.FIELD(1, "dbsync_ts", 
DataTypes.STRING())));
+        assertThat(
+                        new FieldMergeMapWithKeyTimeAggFactory()
+                                .create(mapType, new CoreOptions(new 
HashMap<>()), "f"))
+                .isNotNull();
+    }
+
     /**
      * With a binary key the timestamp comparison never runs, because the 
lookup of the existing
      * entry misses: the newer row is appended as a second entry under the 
same logical key, and a

Reply via email to