Skip to content

Commit afde866

Browse files
committed
[CALCITE-6300] Function MAP_VALUES/MAP_KEYS gives exception when mapVauleType and mapKeyType not equals map Biggest mapKeytype or mapValueType
1 parent ea7fb17 commit afde866

2 files changed

Lines changed: 61 additions & 1 deletion

File tree

core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
import org.apache.calcite.sql.SqlOperator;
3232
import org.apache.calcite.sql.SqlOperatorBinding;
3333
import org.apache.calcite.sql.SqlUtil;
34+
import org.apache.calcite.sql.fun.SqlStdOperatorTable;
35+
import org.apache.calcite.sql.parser.SqlParserPos;
3436
import org.apache.calcite.sql.util.SqlBasicVisitor;
3537
import org.apache.calcite.sql.validate.SqlLambdaScope;
3638
import org.apache.calcite.sql.validate.SqlValidator;
@@ -1555,9 +1557,37 @@ private static class MapFunctionOperandTypeChecker
15551557
}
15561558
return false;
15571559
}
1560+
// Insert implicit casts for operands whose SqlTypeName differs
1561+
// from the inferred key/value type.
1562+
coerceOperands(callBinding, argTypes,
1563+
componentType.left, componentType.right);
15581564
return true;
15591565
}
15601566

1567+
/** Casts operands whose {@code SqlTypeName} differs from the
1568+
* target key or value type. Operands at even positions are keys,
1569+
* odd positions are values. */
1570+
private static void coerceOperands(SqlCallBinding callBinding,
1571+
List<RelDataType> operandTypes,
1572+
RelDataType keyType, RelDataType valueType) {
1573+
final SqlValidator validator = callBinding.getValidator();
1574+
final SqlCall call = callBinding.getCall();
1575+
final List<SqlNode> operands = call.getOperandList();
1576+
for (int i = 0; i < operands.size(); i++) {
1577+
final RelDataType targetType = i % 2 == 0 ? keyType : valueType;
1578+
if (operandTypes.get(i).getSqlTypeName()
1579+
!= targetType.getSqlTypeName()) {
1580+
final SqlNode castNode =
1581+
SqlStdOperatorTable.CAST.createCall(SqlParserPos.ZERO,
1582+
operands.get(i),
1583+
SqlTypeUtil.convertTypeToSpec(targetType)
1584+
.withNullable(targetType.isNullable()));
1585+
call.setOperand(i, castNode);
1586+
validator.setValidatedNodeType(castNode, targetType);
1587+
}
1588+
}
1589+
}
1590+
15611591
/**
15621592
* Extract the key type and value type of arg types.
15631593
*/

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

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9355,6 +9355,20 @@ void checkArrayReverseFunc(SqlOperatorFixture f0, SqlFunction function,
93559355
f1.checkScalar("map_keys(map('foo', 1, 'bar', 2))", "[foo, bar]",
93569356
"CHAR(3) NOT NULL ARRAY NOT NULL");
93579357

9358+
// [CALCITE-6300] MAP function with mixed key/value types
9359+
f1.checkScalar("map_keys(map(cast(1 as tinyint), 1, 2, 2))", "[1, 2]",
9360+
"INTEGER NOT NULL ARRAY NOT NULL");
9361+
f1.checkScalar("map_keys(map(cast(1 as tinyint), 1, cast(2 as double), 2))",
9362+
"[1.0, 2.0]",
9363+
"DOUBLE NOT NULL ARRAY NOT NULL");
9364+
f1.checkScalar("map_keys(map(cast(1 as tinyint), 1, cast(2 as float), 2))",
9365+
"[1.0, 2.0]",
9366+
"FLOAT NOT NULL ARRAY NOT NULL");
9367+
f1.checkFails("map_keys(map(cast(1 as tinyint), 1, cast(null as float), 2))",
9368+
"Illegal arguments for MAP_KEYS function: "
9369+
+ "using a map with a null key is not allowed",
9370+
true);
9371+
93589372
f.checkFails("map_keys(map['foo', 1, null, 2])",
93599373
"Illegal arguments for MAP_KEYS function: using a map with a null key is not allowed",
93609374
true);
@@ -9394,6 +9408,21 @@ void checkArrayReverseFunc(SqlOperatorFixture f0, SqlFunction function,
93949408
f1.checkScalar("map_values(map('foo', 1, 'bar', cast(null as integer)))", "[1, null]",
93959409
"INTEGER ARRAY NOT NULL");
93969410

9411+
// [CALCITE-6300] MAP function with mixed key/value types
9412+
f1.checkScalar("map_values(map('foo', null))", "[null]",
9413+
"NULL ARRAY NOT NULL");
9414+
f1.checkScalar("map_values(map('foo', 1, 'bar', cast(1 as tinyint)))", "[1, 1]",
9415+
"INTEGER NOT NULL ARRAY NOT NULL");
9416+
f1.checkScalar("map_values(map('foo', 1, 'bar', cast(1 as double)))",
9417+
"[1.0, 1.0]",
9418+
"DOUBLE NOT NULL ARRAY NOT NULL");
9419+
f1.checkScalar("map_values(map('foo', 1, 'bar', cast(1 as float)))",
9420+
"[1.0, 1.0]",
9421+
"FLOAT NOT NULL ARRAY NOT NULL");
9422+
f1.checkScalar("map_values(map('foo', 1, 'bar', cast(null as float)))",
9423+
"[1.0, null]",
9424+
"FLOAT ARRAY NOT NULL");
9425+
93979426
f.checkFails("map_values(map['foo', 1, null, 2])",
93989427
"Illegal arguments for MAP_VALUES function: using a map with a null key is not allowed",
93999428
true);
@@ -13879,8 +13908,9 @@ private static void checkArrayConcatAggFuncFails(SqlOperatorFixture t) {
1387913908
f1.checkScalar("map('washington', 1, 'obama', 44)",
1388013909
"{washington=1, obama=44}",
1388113910
"(CHAR(10) NOT NULL, INTEGER NOT NULL) MAP NOT NULL");
13911+
// [CALCITE-6300] values are coerced to DECIMAL(11, 1)
1388213912
f1.checkScalar("map('k1', 1, 'k2', 2.0)",
13883-
"{k1=1, k2=2.0}",
13913+
"{k1=1.0, k2=2.0}",
1388413914
"(CHAR(2) NOT NULL, DECIMAL(11, 1) NOT NULL) MAP NOT NULL");
1388513915
}
1388613916

0 commit comments

Comments
 (0)