From 105d4caaf6b2371d344fd73906796d53c11ee1bc Mon Sep 17 00:00:00 2001 From: Mihai Budiu Date: Thu, 11 Jun 2026 21:14:49 -0700 Subject: [PATCH] [CALCITE-7556] NameMultimap.remove(key, value) leaves an empty key bucket behind Signed-off-by: Mihai Budiu --- .../java/org/apache/calcite/util/NameMultimap.java | 9 +++++---- .../test/java/org/apache/calcite/util/UtilTest.java | 13 +++++++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/org/apache/calcite/util/NameMultimap.java b/core/src/main/java/org/apache/calcite/util/NameMultimap.java index 8d1505746eb6..d37f5366c98e 100644 --- a/core/src/main/java/org/apache/calcite/util/NameMultimap.java +++ b/core/src/main/java/org/apache/calcite/util/NameMultimap.java @@ -16,8 +16,6 @@ */ package org.apache.calcite.util; -import org.apache.calcite.linq4j.function.Experimental; - import org.checkerframework.checker.nullness.qual.Nullable; import java.util.ArrayList; @@ -71,13 +69,16 @@ public void put(String name, V v) { /** Removes all entries that have the given case-sensitive key. * * @return Whether a value was removed */ - @Experimental public boolean remove(String key, V value) { final List list = map().get(key); if (list == null) { return false; } - return list.remove(value); + boolean result = list.remove(value); + if (list.isEmpty()) { + map().remove(key); + } + return result; } /** Returns a map containing all the entries in this multimap that match the diff --git a/core/src/test/java/org/apache/calcite/util/UtilTest.java b/core/src/test/java/org/apache/calcite/util/UtilTest.java index 2d7ab0f606b5..7a6df9a5a07f 100644 --- a/core/src/test/java/org/apache/calcite/util/UtilTest.java +++ b/core/src/test/java/org/apache/calcite/util/UtilTest.java @@ -3292,4 +3292,17 @@ static String describe(Matcher m) { m.describeTo(d); return d.toString(); } + + /** Test case for [CALCITE-7556] + * NameMultimap.remove(key, value) leaves an empty key bucket behind. */ + @Test void testNameMultimapRemoveLastValueRemovesKey() { + final NameMultimap map = new NameMultimap<>(); + map.put("baz", 1); + + assertTrue(map.remove("baz", 1)); + assertThat(map.range("baz", true), hasSize(0)); + assertThat(map.containsKey("baz", true), is(false)); + assertThat(map.containsKey("BAZ", false), is(false)); + assertThat(map.map(), aMapWithSize(0)); + } }