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 8f46a6285f [common] Compare maps of different representations without
throwing (#9211)
8f46a6285f is described below
commit 8f46a6285ff8a8550aec8ef2fc552c1c350d25f9
Author: ZIHAN DAI <[email protected]>
AuthorDate: Sat Aug 15 16:34:33 2026 +1000
[common] Compare maps of different representations without throwing (#9211)
---
.../org/apache/paimon/utils/InternalRowUtils.java | 29 ++++++++----------
.../apache/paimon/utils/InternalRowUtilsTest.java | 34 ++++++++++++++++++++++
2 files changed, 46 insertions(+), 17 deletions(-)
diff --git
a/paimon-common/src/main/java/org/apache/paimon/utils/InternalRowUtils.java
b/paimon-common/src/main/java/org/apache/paimon/utils/InternalRowUtils.java
index 4c329797f3..8fb0b8f805 100644
--- a/paimon-common/src/main/java/org/apache/paimon/utils/InternalRowUtils.java
+++ b/paimon-common/src/main/java/org/apache/paimon/utils/InternalRowUtils.java
@@ -91,23 +91,12 @@ public class InternalRowUtils {
return false;
}
MapType mapType = (MapType) dataType;
- GenericMap map1;
- GenericMap map2;
- if (data1 instanceof GenericMap) {
- map1 = (GenericMap) data1;
- map2 = (GenericMap) data2;
- } else {
- map1 =
- copyToGenericMap(
- (InternalMap) data1,
- mapType.getKeyType(),
- mapType.getValueType());
- map2 =
- copyToGenericMap(
- (InternalMap) data2,
- mapType.getKeyType(),
- mapType.getValueType());
- }
+ // Decide per operand. One MapType is represented by
GenericMap, BinaryMap or
+ // ColumnarMap interchangeably -- which is why the conversion
below exists at all --
+ // so gating data2's cast on data1's concrete class threw
ClassCastException
+ // whenever the two sides happened to use different
representations.
+ GenericMap map1 = toGenericMap((InternalMap) data1, mapType);
+ GenericMap map2 = toGenericMap((InternalMap) data2, mapType);
InternalArray keyArray1 = map1.keyArray();
InternalArray keyArray2 = map2.keyArray();
InternalArray valueArray1 = map1.valueArray();
@@ -284,6 +273,12 @@ public class InternalRowUtils {
return copyToGenericMap(map, keyType, valueType);
}
+ private static GenericMap toGenericMap(InternalMap map, MapType mapType) {
+ return map instanceof GenericMap
+ ? (GenericMap) map
+ : copyToGenericMap(map, mapType.getKeyType(),
mapType.getValueType());
+ }
+
private static GenericMap copyToGenericMap(
InternalMap map, DataType keyType, DataType valueType) {
Map<Object, Object> javaMap = new HashMap<>();
diff --git
a/paimon-common/src/test/java/org/apache/paimon/utils/InternalRowUtilsTest.java
b/paimon-common/src/test/java/org/apache/paimon/utils/InternalRowUtilsTest.java
index dec1ab4ad6..fb14c024ac 100644
---
a/paimon-common/src/test/java/org/apache/paimon/utils/InternalRowUtilsTest.java
+++
b/paimon-common/src/test/java/org/apache/paimon/utils/InternalRowUtilsTest.java
@@ -18,6 +18,9 @@
package org.apache.paimon.utils;
+import org.apache.paimon.data.BinaryArray;
+import org.apache.paimon.data.BinaryArrayWriter;
+import org.apache.paimon.data.BinaryMap;
import org.apache.paimon.data.BinaryRow;
import org.apache.paimon.data.BinaryString;
import org.apache.paimon.data.BinaryVector;
@@ -33,6 +36,7 @@ import org.apache.paimon.datagen.DataGenerator;
import org.apache.paimon.datagen.RandomGeneratorVisitor;
import org.apache.paimon.datagen.RowDataGenerator;
import org.apache.paimon.options.Options;
+import org.apache.paimon.types.DataType;
import org.apache.paimon.types.DataTypeRoot;
import org.apache.paimon.types.DataTypes;
import org.apache.paimon.types.RowType;
@@ -299,4 +303,34 @@ public class InternalRowUtilsTest {
rowWithMap2.setField(0, new GenericMap(map2));
assertThat(InternalRowUtils.equals(rowWithMap1, rowWithMap2,
rowType2)).isFalse();
}
+
+ @Test
+ public void testEqualsAcrossMapImplementations() {
+ DataType mapType = DataTypes.MAP(DataTypes.STRING(), DataTypes.INT());
+
+ Map<Object, Object> entries = new HashMap<>();
+ entries.put(BinaryString.fromString("a"), 1);
+ GenericMap generic = new GenericMap(entries);
+
+ BinaryArray keys = new BinaryArray();
+ BinaryArrayWriter keyWriter = new BinaryArrayWriter(keys, 1, 8);
+ keyWriter.writeString(0, BinaryString.fromString("a"));
+ keyWriter.complete();
+ BinaryArray values = new BinaryArray();
+ BinaryArrayWriter valueWriter = new BinaryArrayWriter(values, 1, 4);
+ valueWriter.writeInt(0, 1);
+ valueWriter.complete();
+ BinaryMap binary = BinaryMap.valueOf(keys, values);
+
+ // hash() already treats the two representations as interchangeable,
so equals() throwing
+ // for one ordering is the inconsistency being fixed here.
+ assertThat(InternalRowUtils.hash(generic, mapType))
+ .isEqualTo(InternalRowUtils.hash(binary, mapType));
+
+ // Both orderings must agree. Only the generic-first one changes: it
used to pick the
+ // GenericMap fast path off data1 and then cast data2 to GenericMap
unconditionally,
+ // throwing ClassCastException for a BinaryMap.
+ assertThat(InternalRowUtils.equals(generic, binary, mapType)).isTrue();
+ assertThat(InternalRowUtils.equals(binary, generic, mapType)).isTrue();
+ }
}