This is an automated email from the ASF dual-hosted git repository.
cancai 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 ac81f758da [CALCITE-6300] Function MAP_VALUES/MAP_KEYS gives exception
when mapVauleType and mapKeyType not equals map Biggest mapKeytype or
mapValueType
ac81f758da is described below
commit ac81f758da2de2023713bbae594b1deea83a9e1d
Author: Cancai Cai <[email protected]>
AuthorDate: Fri Mar 27 21:31:24 2026 +0800
[CALCITE-6300] Function MAP_VALUES/MAP_KEYS gives exception when
mapVauleType and mapKeyType not equals map Biggest mapKeytype or mapValueType
---
.../org/apache/calcite/sql/type/OperandTypes.java | 30 ++++++++++++++++++++
.../org/apache/calcite/test/SqlOperatorTest.java | 32 +++++++++++++++++++++-
2 files changed, 61 insertions(+), 1 deletion(-)
diff --git a/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java
b/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java
index b67899d761..66b4aab9b6 100644
--- a/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java
+++ b/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java
@@ -31,6 +31,8 @@
import org.apache.calcite.sql.SqlOperator;
import org.apache.calcite.sql.SqlOperatorBinding;
import org.apache.calcite.sql.SqlUtil;
+import org.apache.calcite.sql.fun.SqlStdOperatorTable;
+import org.apache.calcite.sql.parser.SqlParserPos;
import org.apache.calcite.sql.util.SqlBasicVisitor;
import org.apache.calcite.sql.validate.SqlLambdaScope;
import org.apache.calcite.sql.validate.SqlValidator;
@@ -1555,9 +1557,37 @@ private static class MapFunctionOperandTypeChecker
}
return false;
}
+ // Insert implicit casts for operands whose SqlTypeName differs
+ // from the inferred key/value type.
+ coerceOperands(callBinding, argTypes,
+ componentType.left, componentType.right);
return true;
}
+ /** Casts operands whose {@code SqlTypeName} differs from the
+ * target key or value type. Operands at even positions are keys,
+ * odd positions are values. */
+ private static void coerceOperands(SqlCallBinding callBinding,
+ List<RelDataType> operandTypes,
+ RelDataType keyType, RelDataType valueType) {
+ final SqlValidator validator = callBinding.getValidator();
+ final SqlCall call = callBinding.getCall();
+ final List<SqlNode> operands = call.getOperandList();
+ for (int i = 0; i < operands.size(); i++) {
+ final RelDataType targetType = i % 2 == 0 ? keyType : valueType;
+ if (operandTypes.get(i).getSqlTypeName()
+ != targetType.getSqlTypeName()) {
+ final SqlNode castNode =
+ SqlStdOperatorTable.CAST.createCall(SqlParserPos.ZERO,
+ operands.get(i),
+ SqlTypeUtil.convertTypeToSpec(targetType)
+ .withNullable(targetType.isNullable()));
+ call.setOperand(i, castNode);
+ validator.setValidatedNodeType(castNode, targetType);
+ }
+ }
+ }
+
/**
* Extract the key type and value type of arg types.
*/
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 b6731f9e63..e79bbe197b 100644
--- a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
+++ b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
@@ -9356,6 +9356,20 @@ void checkArrayReverseFunc(SqlOperatorFixture f0,
SqlFunction function,
f1.checkScalar("map_keys(map('foo', 1, 'bar', 2))", "[foo, bar]",
"CHAR(3) NOT NULL ARRAY NOT NULL");
+ // [CALCITE-6300] MAP function with mixed key/value types
+ f1.checkScalar("map_keys(map(cast(1 as tinyint), 1, 2, 2))", "[1, 2]",
+ "INTEGER NOT NULL ARRAY NOT NULL");
+ f1.checkScalar("map_keys(map(cast(1 as tinyint), 1, cast(2 as double),
2))",
+ "[1.0, 2.0]",
+ "DOUBLE NOT NULL ARRAY NOT NULL");
+ f1.checkScalar("map_keys(map(cast(1 as tinyint), 1, cast(2 as float), 2))",
+ "[1.0, 2.0]",
+ "FLOAT NOT NULL ARRAY NOT NULL");
+ f1.checkFails("map_keys(map(cast(1 as tinyint), 1, cast(null as float),
2))",
+ "Illegal arguments for MAP_KEYS function: "
+ + "using a map with a null key is not allowed",
+ true);
+
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);
@@ -9395,6 +9409,21 @@ void checkArrayReverseFunc(SqlOperatorFixture f0,
SqlFunction function,
f1.checkScalar("map_values(map('foo', 1, 'bar', cast(null as integer)))",
"[1, null]",
"INTEGER ARRAY NOT NULL");
+ // [CALCITE-6300] MAP function with mixed key/value types
+ f1.checkScalar("map_values(map('foo', null))", "[null]",
+ "NULL ARRAY NOT NULL");
+ f1.checkScalar("map_values(map('foo', 1, 'bar', cast(1 as tinyint)))",
"[1, 1]",
+ "INTEGER NOT NULL ARRAY NOT NULL");
+ f1.checkScalar("map_values(map('foo', 1, 'bar', cast(1 as double)))",
+ "[1.0, 1.0]",
+ "DOUBLE NOT NULL ARRAY NOT NULL");
+ f1.checkScalar("map_values(map('foo', 1, 'bar', cast(1 as float)))",
+ "[1.0, 1.0]",
+ "FLOAT NOT NULL ARRAY NOT NULL");
+ f1.checkScalar("map_values(map('foo', 1, 'bar', cast(null as float)))",
+ "[1.0, null]",
+ "FLOAT 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);
@@ -13880,8 +13909,9 @@ private static void
checkArrayConcatAggFuncFails(SqlOperatorFixture t) {
f1.checkScalar("map('washington', 1, 'obama', 44)",
"{washington=1, obama=44}",
"(CHAR(10) NOT NULL, INTEGER NOT NULL) MAP NOT NULL");
+ // [CALCITE-6300] values are coerced to DECIMAL(11, 1)
f1.checkScalar("map('k1', 1, 'k2', 2.0)",
- "{k1=1, k2=2.0}",
+ "{k1=1.0, k2=2.0}",
"(CHAR(2) NOT NULL, DECIMAL(11, 1) NOT NULL) MAP NOT NULL");
}