Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The newly created cast node will not have a type in the typemap. You have to insert it there as well.

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.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9355,6 +9355,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);
Expand Down Expand Up @@ -9394,6 +9408,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);
Expand Down Expand Up @@ -13879,8 +13908,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");
}

Expand Down
Loading