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 b02d8f0707 [spark] Reject null map keys before conversion (#10587)
b02d8f0707 is described below

commit b02d8f0707461452120de61f60f7b13fc986aaf7
Author: LsomeYeah <[email protected]>
AuthorDate: Fri Oct 16 08:50:07 2026 +0800

    [spark] Reject null map keys before conversion (#10587)
---
 .../org/apache/paimon/spark/DataConverter.java     | 18 ++++----
 .../apache/paimon/spark/SparkInternalRowTest.java  | 18 ++++++--
 .../org/apache/paimon/spark/SparkReadITCase.java   | 49 ++++++++++++++++++++++
 3 files changed, 73 insertions(+), 12 deletions(-)

diff --git 
a/paimon-spark/paimon-spark-common/src/main/java/org/apache/paimon/spark/DataConverter.java
 
b/paimon-spark/paimon-spark-common/src/main/java/org/apache/paimon/spark/DataConverter.java
index d34a9c7da7..b7ae9849ea 100644
--- 
a/paimon-spark/paimon-spark-common/src/main/java/org/apache/paimon/spark/DataConverter.java
+++ 
b/paimon-spark/paimon-spark-common/src/main/java/org/apache/paimon/spark/DataConverter.java
@@ -37,6 +37,7 @@ import org.apache.paimon.types.MapType;
 import org.apache.paimon.types.MultisetType;
 import org.apache.paimon.types.RowType;
 import org.apache.paimon.types.VectorType;
+import org.apache.paimon.utils.Preconditions;
 
 import org.apache.spark.sql.catalyst.util.ArrayBasedMapData;
 import org.apache.spark.sql.catalyst.util.ArrayData;
@@ -154,18 +155,17 @@ public class DataConverter {
             throw new UnsupportedOperationException("Unsupported type: " + 
mapType);
         }
 
-        if (valueType.getTypeRoot() == 
org.apache.paimon.types.DataTypeRoot.BLOB) {
-            InternalArray keys = map.keyArray();
-            for (int i = 0; i < keys.size(); i++) {
-                if (keys.isNullAt(i)) {
-                    throw new IllegalArgumentException(
-                            "Spark MAP<X, BLOB> does not support null keys.");
-                }
-            }
+        InternalArray keys = map.keyArray();
+        String nullKeyErrorMessage =
+                valueType.getTypeRoot() == 
org.apache.paimon.types.DataTypeRoot.BLOB
+                        ? "Spark MAP<X, BLOB> does not support null keys."
+                        : "Spark map data does not support null keys.";
+        for (int i = 0; i < keys.size(); i++) {
+            Preconditions.checkArgument(!keys.isNullAt(i), 
nullKeyErrorMessage);
         }
 
         return new ArrayBasedMapData(
-                fromPaimonArrayElementType(map.keyArray(), keyType),
+                fromPaimonArrayElementType(keys, keyType),
                 fromPaimonArrayElementType(map.valueArray(), valueType, 
blobAsDescriptor));
     }
 }
diff --git 
a/paimon-spark/paimon-spark-ut/src/test/java/org/apache/paimon/spark/SparkInternalRowTest.java
 
b/paimon-spark/paimon-spark-ut/src/test/java/org/apache/paimon/spark/SparkInternalRowTest.java
index bde723cdd0..74225ffe0b 100644
--- 
a/paimon-spark/paimon-spark-ut/src/test/java/org/apache/paimon/spark/SparkInternalRowTest.java
+++ 
b/paimon-spark/paimon-spark-ut/src/test/java/org/apache/paimon/spark/SparkInternalRowTest.java
@@ -246,17 +246,29 @@ public class SparkInternalRowTest {
 
     @Test
     public void testMapBlobRejectsNullKey() {
-        Map<Object, Object> map = new LinkedHashMap<>();
-        map.put(null, Blob.fromData(new byte[] {1}));
+        Map<Object, Object> blobMap = new LinkedHashMap<>();
+        blobMap.put(null, Blob.fromData(new byte[] {1}));
 
         assertThatThrownBy(
                         () ->
                                 DataConverter.fromPaimon(
-                                        new GenericMap(map),
+                                        new GenericMap(blobMap),
                                         DataTypes.MAP(DataTypes.STRING(), 
DataTypes.BLOB()),
                                         false))
                 .isInstanceOf(IllegalArgumentException.class)
                 .hasMessage("Spark MAP<X, BLOB> does not support null keys.");
+
+        Map<Object, Object> intMap = new LinkedHashMap<>();
+        intMap.put(null, 1);
+
+        assertThatThrownBy(
+                        () ->
+                                DataConverter.fromPaimon(
+                                        new GenericMap(intMap),
+                                        DataTypes.MAP(DataTypes.INT(), 
DataTypes.INT()),
+                                        false))
+                .isInstanceOf(IllegalArgumentException.class)
+                .hasMessage("Spark map data does not support null keys.");
     }
 
     @Test
diff --git 
a/paimon-spark/paimon-spark-ut/src/test/java/org/apache/paimon/spark/SparkReadITCase.java
 
b/paimon-spark/paimon-spark-ut/src/test/java/org/apache/paimon/spark/SparkReadITCase.java
index fbc2f660d4..a1c01a4be5 100644
--- 
a/paimon-spark/paimon-spark-ut/src/test/java/org/apache/paimon/spark/SparkReadITCase.java
+++ 
b/paimon-spark/paimon-spark-ut/src/test/java/org/apache/paimon/spark/SparkReadITCase.java
@@ -18,14 +18,23 @@
 
 package org.apache.paimon.spark;
 
+import org.apache.paimon.catalog.Catalog;
+import org.apache.paimon.catalog.CatalogContext;
+import org.apache.paimon.catalog.CatalogFactory;
+import org.apache.paimon.catalog.Identifier;
+import org.apache.paimon.data.GenericMap;
+import org.apache.paimon.data.GenericRow;
 import org.apache.paimon.fs.Path;
 import org.apache.paimon.fs.local.LocalFileIO;
+import org.apache.paimon.options.Options;
+import org.apache.paimon.schema.Schema;
 import org.apache.paimon.schema.TableSchema;
 import org.apache.paimon.table.FileStoreTableFactory;
 import org.apache.paimon.types.ArrayType;
 import org.apache.paimon.types.BigIntType;
 import org.apache.paimon.types.DataField;
 import org.apache.paimon.types.DataType;
+import org.apache.paimon.types.DataTypes;
 import org.apache.paimon.types.DoubleType;
 import org.apache.paimon.types.VarCharType;
 
@@ -40,11 +49,13 @@ import org.junit.jupiter.api.Test;
 import java.io.File;
 import java.util.Arrays;
 import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
 import java.util.stream.IntStream;
 
+import static org.apache.paimon.options.CatalogOptions.WAREHOUSE;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
@@ -61,6 +72,44 @@ public class SparkReadITCase extends SparkReadTestBase {
         innerTestNestedType(spark.table("t2"));
     }
 
+    @Test
+    public void testReadOrcMapWithNullKey() throws Exception {
+        Schema schema =
+                Schema.newBuilder()
+                        .column("id", DataTypes.INT())
+                        .column("m", DataTypes.MAP(DataTypes.INT(), 
DataTypes.INT()))
+                        .option("file.format", "orc")
+                        .build();
+
+        Options options = new Options();
+        options.set(WAREHOUSE, warehousePath.toString());
+        try (Catalog catalog = 
CatalogFactory.createCatalog(CatalogContext.create(options))) {
+            catalog.createTable(Identifier.create("default", "invalid_map"), 
schema, false);
+            catalog.createTable(Identifier.create("default", "valid_map"), 
schema, false);
+        }
+
+        Map<Integer, Integer> invalidMap = new LinkedHashMap<>();
+        invalidMap.put(null, 17);
+        invalidMap.put(2, 20);
+        writeTable("invalid_map", GenericRow.of(1, new 
GenericMap(invalidMap)));
+
+        Map<Integer, Integer> validMap = new LinkedHashMap<>();
+        validMap.put(1, 17);
+        validMap.put(2, null);
+        writeTable("valid_map", GenericRow.of(2, new GenericMap(validMap)));
+
+        assertThat(spark.sql("SELECT id FROM 
invalid_map").collectAsList().toString())
+                .isEqualTo("[[1]]");
+
+        Row validRow = spark.sql("SELECT id, m FROM 
valid_map").collectAsList().get(0);
+        assertThat(validRow.getInt(0)).isEqualTo(2);
+        assertThat(validRow.<Integer, Integer>getJavaMap(1))
+                .containsExactlyInAnyOrderEntriesOf(validMap);
+
+        assertThatThrownBy(() -> spark.sql("SELECT id, m FROM 
invalid_map").collectAsList())
+                .hasStackTraceContaining("Spark map data does not support null 
keys.");
+    }
+
     @Test
     public void testFilterPushDown() {
         innerTestSimpleTypeFilterPushDown(spark.table("t1"));

Reply via email to