From 9aecd5d078fd086f8965dd2d597aed19ce721455 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dapeng=20Sun=28=E5=AD=99=E5=A4=A7=E9=B9=8F=29?= Date: Wed, 16 Sep 2026 17:06:40 +0800 Subject: [PATCH] [api] Do not ask an immutable partitionOptions list whether it contains null CreatePartitionsRequest validates partitionOptions with contains(null), and List.of answers that with a NullPointerException rather than false. A caller handing over a valid immutable list therefore gets an NPE from the guard whose job is to give it a clear message, with a stack that points at the collection instead of at the argument. Ask with a null-safe stream instead. --- .../requests/CreatePartitionsRequest.java | 3 ++- .../apache/paimon/rest/RESTApiJsonTest.java | 24 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/paimon-api/src/main/java/org/apache/paimon/rest/requests/CreatePartitionsRequest.java b/paimon-api/src/main/java/org/apache/paimon/rest/requests/CreatePartitionsRequest.java index e5260a8dcb5b..d989fb64d7d1 100644 --- a/paimon-api/src/main/java/org/apache/paimon/rest/requests/CreatePartitionsRequest.java +++ b/paimon-api/src/main/java/org/apache/paimon/rest/requests/CreatePartitionsRequest.java @@ -32,6 +32,7 @@ import java.util.List; import java.util.Map; +import java.util.Objects; import static org.apache.paimon.utils.Preconditions.checkArgument; @@ -106,7 +107,7 @@ public CreatePartitionsRequest( && partitionOptions.size() == partitionSpecs.size()), "partitionOptions must be null or have the same size as partitionSpecs."); checkArgument( - partitionOptions == null || !partitionOptions.contains(null), + partitionOptions == null || partitionOptions.stream().noneMatch(Objects::isNull), "partitionOptions must not contain null maps."); checkArgument( partitionOptions == null diff --git a/paimon-core/src/test/java/org/apache/paimon/rest/RESTApiJsonTest.java b/paimon-core/src/test/java/org/apache/paimon/rest/RESTApiJsonTest.java index 6ed35b84d497..06cd8c6a912f 100644 --- a/paimon-core/src/test/java/org/apache/paimon/rest/RESTApiJsonTest.java +++ b/paimon-core/src/test/java/org/apache/paimon/rest/RESTApiJsonTest.java @@ -65,6 +65,7 @@ import org.junit.Test; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; @@ -530,6 +531,29 @@ public void createPartitionsRequestCarriesStatisticsTest() throws Exception { assertFalse(PartitionStatistics.isKnown(parsedUnknown.fileCount())); } + @Test + public void createPartitionsRequestAcceptsImmutablePartitionOptionsTest() { + Map spec = Collections.singletonMap("dt", "20260916"); + Map options = + Collections.singletonMap("path", "oss://bucket/archive/dt=20260916"); + + // List.of answers contains(null) with a NullPointerException rather than false, so a + // caller handing over a valid immutable list must not be asked that question. + CreatePartitionsRequest request = + new CreatePartitionsRequest(List.of(spec), true, null, null, List.of(options)); + assertEquals(List.of(options), request.getPartitionOptions()); + + List> withNullEntry = new ArrayList<>(); + withNullEntry.add(null); + IllegalArgumentException failure = + assertThrows( + IllegalArgumentException.class, + () -> + new CreatePartitionsRequest( + List.of(spec), true, null, null, withNullEntry)); + assertTrue(failure.getMessage().contains("partitionOptions must not contain null maps")); + } + @Test public void dropPartitionsResponseParseTest() throws Exception { Map dropped = new HashMap<>();