This is an automated email from the ASF dual-hosted git repository.
mbudiu pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/calcite.git
The following commit(s) were added to refs/heads/main by this push:
new 3cb9fc612a [CALCITE-6522] MAP_KEYS and MAP_VALUES function should
throw if a key value is null
3cb9fc612a is described below
commit 3cb9fc612a9d18db162357824fd351a2f8801a31
Author: cancaicai <[email protected]>
AuthorDate: Fri Aug 9 00:12:37 2024 +0800
[CALCITE-6522] MAP_KEYS and MAP_VALUES function should throw if a key
value is null
---
.../apache/calcite/runtime/CalciteResource.java | 9 ++++
.../org/apache/calcite/runtime/SqlFunctions.java | 11 ++++-
.../calcite/runtime/CalciteResource.properties | 5 ++-
.../org/apache/calcite/test/SqlOperatorTest.java | 52 +++++++++++++++-------
4 files changed, 58 insertions(+), 19 deletions(-)
diff --git a/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java
b/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java
index 67236d9c74..85a995a945 100644
--- a/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java
+++ b/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java
@@ -933,6 +933,15 @@ public interface CalciteResource {
@BaseMessage("Illegal arguments for 'FORMAT_NUMBER' function: negative
decimal value not allowed")
ExInst<CalciteException> illegalNegativeDecimalValue();
+ @BaseMessage("Illegal arguments for 'MAP_ENTRIES' function: using a map with
a null key is not allowed")
+ ExInst<CalciteException> illegalMapEntriesWithNullKey();
+
+ @BaseMessage("Illegal arguments for 'MAP_KEYS' function: using a map with a
null key is not allowed")
+ ExInst<CalciteException> illegalMapKeysWithNullKey();
+
+ @BaseMessage("Illegal arguments for 'MAP_VALUES' function: using a map with
a null key is not allowed")
+ ExInst<CalciteException> illegalMapValuesWithNullKey();
+
@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")
ExInst<CalciteException> illegalArgumentsInMapFromArraysFunc(int arg0, int
arg1);
diff --git a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
index f7b208c0a8..9e4250e44b 100644
--- a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
+++ b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
@@ -5907,7 +5907,7 @@ public class SqlFunctions {
final List result = new ArrayList(map.size());
for (Map.Entry<Object, Object> entry : map.entrySet()) {
if (entry.getKey() == null) {
- throw new IllegalArgumentException("Cannot use null as map key");
+ throw RESOURCE.illegalMapEntriesWithNullKey().ex();
}
result.add(Arrays.asList(entry.getKey(), entry.getValue()));
}
@@ -5916,11 +5916,18 @@ public class SqlFunctions {
/** Support the MAP_KEYS function. */
public static List mapKeys(Map map) {
- return new ArrayList<>(map.keySet());
+ try {
+ return ImmutableList.copyOf(map.keySet());
+ } catch (NullPointerException e) {
+ throw RESOURCE.illegalMapKeysWithNullKey().ex();
+ }
}
/** Support the MAP_VALUES function. */
public static List mapValues(Map map) {
+ if (map.containsKey(null)) {
+ throw RESOURCE.illegalMapValuesWithNullKey().ex();
+ }
return new ArrayList<>(map.values());
}
diff --git
a/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties
b/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties
index 55bf0ba73c..a16d5469c1 100644
---
a/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties
+++
b/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties
@@ -302,8 +302,11 @@ FunctionNotFound=Function ''{0}'' not found
DialectDoesNotSupportFeature=Dialect does not support feature: ''{0}''
IllegalNegativePadLength=Second argument for LPAD/RPAD must not be negative
IllegalEmptyPadPattern=Third argument (pad pattern) for LPAD/RPAD must not be
empty
-IllegalNegativeSubstringLength=Substring error: negative substring length not
allowed
IllegalNegativeDecimalValue=Illegal arguments for 'FORMAT_NUMBER' function:
negative decimal value not allowed
+IllegalNegativeSubstringLength=Substring error: negative substring length not
allowed
+IllegalMapEntriesWithNullKey=Illegal arguments for 'MAP_ENTRIES' function:
using a map with a null key is not allowed
+IllegalMapKeysWithNullKey=Illegal arguments for 'MAP_KEYS' function: using a
map with a null key is not allowed
+IllegalMapValuesWithNullKey=Illegal arguments for 'MAP_VALUES' function: using
a map with a null key is not allowed
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
TrimError=Trim error: trim character must be exactly 1 character
InvalidTypesForArithmetic=Invalid types for arithmetic: {0} {1} {2}
diff --git a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
index 4b7c5972c9..c5ad9a4b64 100644
--- a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
+++ b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
@@ -8517,13 +8517,17 @@ public class SqlOperatorTest {
// 3. check key is not allowed to be null
f.checkFails("map_entries(map[cast(1 as decimal), 1, null, 2])",
- "Cannot use null as map key", true);
+ "Illegal arguments for MAP_ENTRIES function: using a map with a null
key is not allowed",
+ true);
f.checkFails("map_entries(map[1, cast(1 as bigint), null, 2])",
- "Cannot use null as map key", true);
+ "Illegal arguments for MAP_ENTRIES function: using a map with a null
key is not allowed",
+ true);
f.checkFails("map_entries(map[1, cast(1 as decimal), null, 2])",
- "Cannot use null as map key", true);
+ "Illegal arguments for MAP_ENTRIES function: using a map with a null
key is not allowed",
+ true);
f.checkFails("map_entries(map['foo', 1, null, 2])",
- "Cannot use null as map key", true);
+ "Illegal arguments for MAP_ENTRIES function: using a map with a null
key is not allowed",
+ true);
}
/** Tests {@code MAP_KEYS} function from Spark. */
@@ -8537,23 +8541,15 @@ public class SqlOperatorTest {
final SqlOperatorFixture f = f0.withLibrary(SqlLibrary.SPARK);
f.checkScalar("map_keys(map['foo', 1, 'bar', 2])", "[foo, bar]",
"CHAR(3) NOT NULL ARRAY NOT NULL");
- f.checkScalar("map_keys(map['foo', 1, null, 2])", "[foo, null]",
- "CHAR(3) ARRAY NOT NULL");
+
// elements cast
// key cast
f.checkScalar("map_keys(map[cast(1 as tinyint), 1, 2, 2])", "[1, 2]",
"INTEGER NOT NULL ARRAY NOT NULL");
- f.checkScalar("map_keys(map[cast(1 as bigint), 1, null, 2])", "[1, null]",
- "BIGINT ARRAY NOT NULL");
- f.checkScalar("map_keys(map[cast(1 as decimal), 1, null, 2])", "[1, null]",
- "DECIMAL(19, 0) ARRAY NOT NULL");
+
// value cast
f.checkScalar("map_keys(map[1, cast(1 as tinyint), 2, 2])", "[1, 2]",
"INTEGER NOT NULL ARRAY NOT NULL");
- f.checkScalar("map_keys(map[1, cast(1 as bigint), null, 2])", "[1, null]",
- "INTEGER ARRAY NOT NULL");
- f.checkScalar("map_keys(map[1, cast(1 as decimal), null, 2])", "[1, null]",
- "INTEGER ARRAY NOT NULL");
// 2. check with map function, map(k, v ...)
final SqlOperatorFixture f1 = fixture()
@@ -8563,8 +8559,19 @@ public class SqlOperatorTest {
"UNKNOWN NOT NULL ARRAY NOT NULL");
f1.checkScalar("map_keys(map('foo', 1, 'bar', 2))", "[foo, bar]",
"CHAR(3) NOT NULL ARRAY NOT NULL");
- f1.checkScalar("map_keys(map('foo', 1, null, 2))", "[foo, null]",
- "CHAR(3) ARRAY NOT NULL");
+
+ f.checkFails("map_keys(map['foo', 1, null, 2])",
+ "Illegal arguments for MAP_KEYS function: using a map with a null key
is not allowed",
+ true);
+ f.checkFails("map_keys(map[1, cast(1 as decimal), null, 2])",
+ "Illegal arguments for MAP_KEYS function: using a map with a null key
is not allowed",
+ true);
+ f.checkFails("map_keys(map[1, cast(1 as bigint), null, 2])",
+ "Illegal arguments for MAP_KEYS function: using a map with a null key
is not allowed",
+ true);
+ f.checkFails("map_keys(map[cast(1 as decimal), 1, null, 2])",
+ "Illegal arguments for MAP_KEYS function: using a map with a null key
is not allowed",
+ true);
}
/** Tests {@code MAP_VALUES} function from Spark. */
@@ -8591,6 +8598,19 @@ public class SqlOperatorTest {
"INTEGER NOT NULL ARRAY NOT NULL");
f1.checkScalar("map_values(map('foo', 1, 'bar', cast(null as integer)))",
"[1, null]",
"INTEGER ARRAY NOT NULL");
+
+ f.checkFails("map_values(map['foo', 1, null, 2])",
+ "Illegal arguments for MAP_VALUES function: using a map with a null
key is not allowed",
+ true);
+ f.checkFails("map_values(map[1, cast(1 as decimal), null, 2])",
+ "Illegal arguments for MAP_VALUES function: using a map with a null
key is not allowed",
+ true);
+ f.checkFails("map_values(map[1, cast(1 as bigint), null, 2])",
+ "Illegal arguments for MAP_VALUES function: using a map with a null
key is not allowed",
+ true);
+ f.checkFails("map_values(map[cast(1 as decimal), 1, null, 2])",
+ "Illegal arguments for MAP_VALUES function: using a map with a null
key is not allowed",
+ true);
}
/** Test case for