Skip to content

Commit f70e6fe

Browse files
committed
[CALCITE-6522] MAP_KEYS and MAP_VALUES function should throw if a key value is null
1 parent 91fe118 commit f70e6fe

File tree

4 files changed

+58
-19
lines changed

4 files changed

+58
-19
lines changed

core/src/main/java/org/apache/calcite/runtime/CalciteResource.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -933,6 +933,15 @@ ExInst<CalciteException> illegalArgumentForTableFunctionCall(String a0,
933933
@BaseMessage("Illegal arguments for 'FORMAT_NUMBER' function: negative decimal value not allowed")
934934
ExInst<CalciteException> illegalNegativeDecimalValue();
935935

936+
@BaseMessage("Illegal arguments for 'MAP_ENTRIES' function: using a map with a null key is not allowed")
937+
ExInst<CalciteException> illegalMapEntriesWithNullKey();
938+
939+
@BaseMessage("Illegal arguments for 'MAP_KEYS' function: using a map with a null key is not allowed")
940+
ExInst<CalciteException> illegalMapKeysWithNullKey();
941+
942+
@BaseMessage("Illegal arguments for 'MAP_VALUES' function: using a map with a null key is not allowed")
943+
ExInst<CalciteException> illegalMapValuesWithNullKey();
944+
936945
@BaseMessage("Illegal arguments: The length of the keys array {0,number,#} is not equal to the length of the values array {1,number,#} in MAP_FROM_ARRAYS function")
937946
ExInst<CalciteException> illegalArgumentsInMapFromArraysFunc(int arg0, int arg1);
938947

core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5907,7 +5907,7 @@ public static List mapEntries(Map<Object, Object> map) {
59075907
final List result = new ArrayList(map.size());
59085908
for (Map.Entry<Object, Object> entry : map.entrySet()) {
59095909
if (entry.getKey() == null) {
5910-
throw new IllegalArgumentException("Cannot use null as map key");
5910+
throw RESOURCE.illegalMapEntriesWithNullKey().ex();
59115911
}
59125912
result.add(Arrays.asList(entry.getKey(), entry.getValue()));
59135913
}
@@ -5916,11 +5916,18 @@ public static List mapEntries(Map<Object, Object> map) {
59165916

59175917
/** Support the MAP_KEYS function. */
59185918
public static List mapKeys(Map map) {
5919-
return new ArrayList<>(map.keySet());
5919+
try {
5920+
return ImmutableList.copyOf(map.keySet());
5921+
} catch (NullPointerException e) {
5922+
throw RESOURCE.illegalMapKeysWithNullKey().ex();
5923+
}
59205924
}
59215925

59225926
/** Support the MAP_VALUES function. */
59235927
public static List mapValues(Map map) {
5928+
if (map.containsKey(null)) {
5929+
throw RESOURCE.illegalMapValuesWithNullKey().ex();
5930+
}
59245931
return new ArrayList<>(map.values());
59255932
}
59265933

core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,8 +302,11 @@ FunctionNotFound=Function ''{0}'' not found
302302
DialectDoesNotSupportFeature=Dialect does not support feature: ''{0}''
303303
IllegalNegativePadLength=Second argument for LPAD/RPAD must not be negative
304304
IllegalEmptyPadPattern=Third argument (pad pattern) for LPAD/RPAD must not be empty
305-
IllegalNegativeSubstringLength=Substring error: negative substring length not allowed
306305
IllegalNegativeDecimalValue=Illegal arguments for 'FORMAT_NUMBER' function: negative decimal value not allowed
306+
IllegalNegativeSubstringLength=Substring error: negative substring length not allowed
307+
IllegalMapEntriesWithNullKey=Illegal arguments for 'MAP_ENTRIES' function: using a map with a null key is not allowed
308+
IllegalMapKeysWithNullKey=Illegal arguments for 'MAP_KEYS' function: using a map with a null key is not allowed
309+
IllegalMapValuesWithNullKey=Illegal arguments for 'MAP_VALUES' function: using a map with a null key is not allowed
307310
IllegalArgumentsInMapFromArraysFunc=Illegal arguments: The length of the keys array {0,number,#} is not equal to the length of the values array {1,number,#} in MAP_FROM_ARRAYS function
308311
TrimError=Trim error: trim character must be exactly 1 character
309312
InvalidTypesForArithmetic=Invalid types for arithmetic: {0} {1} {2}

testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8513,13 +8513,17 @@ void checkArrayReverseFunc(SqlOperatorFixture f0, SqlFunction function,
85138513

85148514
// 3. check key is not allowed to be null
85158515
f.checkFails("map_entries(map[cast(1 as decimal), 1, null, 2])",
8516-
"Cannot use null as map key", true);
8516+
"Illegal arguments for MAP_ENTRIES function: using a map with a null key is not allowed",
8517+
true);
85178518
f.checkFails("map_entries(map[1, cast(1 as bigint), null, 2])",
8518-
"Cannot use null as map key", true);
8519+
"Illegal arguments for MAP_ENTRIES function: using a map with a null key is not allowed",
8520+
true);
85198521
f.checkFails("map_entries(map[1, cast(1 as decimal), null, 2])",
8520-
"Cannot use null as map key", true);
8522+
"Illegal arguments for MAP_ENTRIES function: using a map with a null key is not allowed",
8523+
true);
85218524
f.checkFails("map_entries(map['foo', 1, null, 2])",
8522-
"Cannot use null as map key", true);
8525+
"Illegal arguments for MAP_ENTRIES function: using a map with a null key is not allowed",
8526+
true);
85238527
}
85248528

85258529
/** Tests {@code MAP_KEYS} function from Spark. */
@@ -8533,23 +8537,15 @@ void checkArrayReverseFunc(SqlOperatorFixture f0, SqlFunction function,
85338537
final SqlOperatorFixture f = f0.withLibrary(SqlLibrary.SPARK);
85348538
f.checkScalar("map_keys(map['foo', 1, 'bar', 2])", "[foo, bar]",
85358539
"CHAR(3) NOT NULL ARRAY NOT NULL");
8536-
f.checkScalar("map_keys(map['foo', 1, null, 2])", "[foo, null]",
8537-
"CHAR(3) ARRAY NOT NULL");
8540+
85388541
// elements cast
85398542
// key cast
85408543
f.checkScalar("map_keys(map[cast(1 as tinyint), 1, 2, 2])", "[1, 2]",
85418544
"INTEGER NOT NULL ARRAY NOT NULL");
8542-
f.checkScalar("map_keys(map[cast(1 as bigint), 1, null, 2])", "[1, null]",
8543-
"BIGINT ARRAY NOT NULL");
8544-
f.checkScalar("map_keys(map[cast(1 as decimal), 1, null, 2])", "[1, null]",
8545-
"DECIMAL(19, 0) ARRAY NOT NULL");
8545+
85468546
// value cast
85478547
f.checkScalar("map_keys(map[1, cast(1 as tinyint), 2, 2])", "[1, 2]",
85488548
"INTEGER NOT NULL ARRAY NOT NULL");
8549-
f.checkScalar("map_keys(map[1, cast(1 as bigint), null, 2])", "[1, null]",
8550-
"INTEGER ARRAY NOT NULL");
8551-
f.checkScalar("map_keys(map[1, cast(1 as decimal), null, 2])", "[1, null]",
8552-
"INTEGER ARRAY NOT NULL");
85538549

85548550
// 2. check with map function, map(k, v ...)
85558551
final SqlOperatorFixture f1 = fixture()
@@ -8559,8 +8555,19 @@ void checkArrayReverseFunc(SqlOperatorFixture f0, SqlFunction function,
85598555
"UNKNOWN NOT NULL ARRAY NOT NULL");
85608556
f1.checkScalar("map_keys(map('foo', 1, 'bar', 2))", "[foo, bar]",
85618557
"CHAR(3) NOT NULL ARRAY NOT NULL");
8562-
f1.checkScalar("map_keys(map('foo', 1, null, 2))", "[foo, null]",
8563-
"CHAR(3) ARRAY NOT NULL");
8558+
8559+
f.checkFails("map_keys(map['foo', 1, null, 2])",
8560+
"Illegal arguments for MAP_KEYS function: using a map with a null key is not allowed",
8561+
true);
8562+
f.checkFails("map_keys(map[1, cast(1 as decimal), null, 2])",
8563+
"Illegal arguments for MAP_KEYS function: using a map with a null key is not allowed",
8564+
true);
8565+
f.checkFails("map_keys(map[1, cast(1 as bigint), null, 2])",
8566+
"Illegal arguments for MAP_KEYS function: using a map with a null key is not allowed",
8567+
true);
8568+
f.checkFails("map_keys(map[cast(1 as decimal), 1, null, 2])",
8569+
"Illegal arguments for MAP_KEYS function: using a map with a null key is not allowed",
8570+
true);
85648571
}
85658572

85668573
/** Tests {@code MAP_VALUES} function from Spark. */
@@ -8587,6 +8594,19 @@ void checkArrayReverseFunc(SqlOperatorFixture f0, SqlFunction function,
85878594
"INTEGER NOT NULL ARRAY NOT NULL");
85888595
f1.checkScalar("map_values(map('foo', 1, 'bar', cast(null as integer)))", "[1, null]",
85898596
"INTEGER ARRAY NOT NULL");
8597+
8598+
f.checkFails("map_values(map['foo', 1, null, 2])",
8599+
"Illegal arguments for MAP_VALUES function: using a map with a null key is not allowed",
8600+
true);
8601+
f.checkFails("map_values(map[1, cast(1 as decimal), null, 2])",
8602+
"Illegal arguments for MAP_VALUES function: using a map with a null key is not allowed",
8603+
true);
8604+
f.checkFails("map_values(map[1, cast(1 as bigint), null, 2])",
8605+
"Illegal arguments for MAP_VALUES function: using a map with a null key is not allowed",
8606+
true);
8607+
f.checkFails("map_values(map[cast(1 as decimal), 1, null, 2])",
8608+
"Illegal arguments for MAP_VALUES function: using a map with a null key is not allowed",
8609+
true);
85908610
}
85918611

85928612
/** Test case for

0 commit comments

Comments
 (0)