in core/src/main/java/org/apache/calcite/adapter/enumerable/RexToLixTranslator.java [311:609]
private Expression getConvertExpression(
RelDataType sourceType,
RelDataType targetType,
Expression operand,
ConstantExpression format) {
final Supplier<Expression> defaultExpression = () ->
EnumUtils.convert(operand, typeFactory.getJavaClass(targetType));
if (sourceType.getSqlTypeName() == SqlTypeName.VARIANT) {
// Converting VARIANT to VARIANT uses the default conversion
if (targetType.getSqlTypeName() == SqlTypeName.VARIANT) {
return defaultExpression.get();
}
// Converting a VARIANT to any other type calls the Variant.cast method
// First cast operand to a VariantValue (it may be an Object)
Expression operandCast = Expressions.convert_(operand, VariantValue.class);
Expression cast =
Expressions.call(operandCast, BuiltInMethod.VARIANT_CAST.method,
RuntimeTypeInformation.createExpression(targetType));
// The cast returns an Object, so we need a convert to the expected Java type
RelDataType nullableTarget = typeFactory.createTypeWithNullability(targetType, true);
return Expressions.convert_(cast, typeFactory.getJavaClass(nullableTarget));
}
if (targetType.getSqlTypeName() == SqlTypeName.ROW) {
assert sourceType.getSqlTypeName() == SqlTypeName.ROW;
List<RelDataTypeField> targetTypes = targetType.getFieldList();
List<RelDataTypeField> sourceTypes = sourceType.getFieldList();
assert targetTypes.size() == sourceTypes.size();
List<Expression> fields = new ArrayList<>();
for (int i = 0; i < targetTypes.size(); i++) {
RelDataTypeField targetField = targetTypes.get(i);
RelDataTypeField sourceField = sourceTypes.get(i);
Expression field = Expressions.arrayIndex(operand, Expressions.constant(i));
// In the generated Java code 'field' is an Object,
// we need to also cast it to the correct type to enable correct method dispatch in Java.
// We force the type to be nullable; this way, instead of (int) we get (Integer).
// Casting an object ot an int is not legal.
RelDataType nullableSourceFieldType =
typeFactory.createTypeWithNullability(sourceField.getType(), true);
Type javaType = typeFactory.getJavaClass(nullableSourceFieldType);
if (!javaType.getTypeName().equals("java.lang.Void")
&& !nullableSourceFieldType.isStruct()) {
// Cannot cast to Void - this is the type of NULL literals.
field = Expressions.convert_(field, javaType);
}
Expression convert =
getConvertExpression(sourceField.getType(), targetField.getType(), field, format);
fields.add(convert);
}
return Expressions.call(BuiltInMethod.ARRAY.method, fields);
}
switch (targetType.getSqlTypeName()) {
case ARRAY:
final RelDataType sourceDataType = sourceType.getComponentType();
final RelDataType targetDataType = targetType.getComponentType();
assert sourceDataType != null;
assert targetDataType != null;
final ParameterExpression parameter =
Expressions.parameter(typeFactory.getJavaClass(sourceDataType), "root");
Expression convert =
getConvertExpression(sourceDataType, targetDataType, parameter, format);
return Expressions.call(BuiltInMethod.LIST_TRANSFORM.method, operand,
Expressions.lambda(Function1.class, convert, parameter));
case VARIANT:
// Converting any type to a VARIANT invokes the Variant constructor
Expression rtti = RuntimeTypeInformation.createExpression(sourceType);
Expression roundingMode = Expressions.constant(typeFactory.getTypeSystem().roundingMode());
return Expressions.call(BuiltInMethod.VARIANT_CREATE.method, roundingMode, operand, rtti);
case ANY:
return operand;
case VARBINARY:
case BINARY:
switch (sourceType.getSqlTypeName()) {
case CHAR:
case VARCHAR:
return Expressions.call(BuiltInMethod.STRING_TO_BINARY.method, operand,
new ConstantExpression(Charset.class, sourceType.getCharset()));
case UUID:
return Expressions.call(BuiltInMethod.UUID_TO_BINARY.method, operand);
default:
return defaultExpression.get();
}
case GEOMETRY:
switch (sourceType.getSqlTypeName()) {
case CHAR:
case VARCHAR:
return Expressions.call(BuiltInMethod.ST_GEOM_FROM_EWKT.method, operand);
default:
return defaultExpression.get();
}
case DATE:
return translateCastToDate(sourceType, operand, format, defaultExpression);
case TIME:
return translateCastToTime(sourceType, operand, format, defaultExpression);
case TIME_WITH_LOCAL_TIME_ZONE:
return translateCastToTimeWithLocalTimeZone(sourceType, operand, defaultExpression);
case TIMESTAMP:
return translateCastToTimestamp(sourceType, operand, format, defaultExpression);
case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
return translateCastToTimestampWithLocalTimeZone(sourceType, operand, defaultExpression);
case BOOLEAN:
switch (sourceType.getSqlTypeName()) {
case CHAR:
case VARCHAR:
return Expressions.call(BuiltInMethod.STRING_TO_BOOLEAN.method, operand);
default:
return defaultExpression.get();
}
case UUID:
switch (sourceType.getSqlTypeName()) {
case UUID:
return operand;
case CHAR:
case VARCHAR:
return Expressions.call(BuiltInMethod.UUID_FROM_STRING.method, operand);
case BINARY:
case VARBINARY:
return Expressions.call(BuiltInMethod.BINARY_TO_UUID.method, operand);
default:
return defaultExpression.get();
}
case CHAR:
case VARCHAR:
final SqlIntervalQualifier interval =
sourceType.getIntervalQualifier();
switch (sourceType.getSqlTypeName()) {
case UUID:
return Expressions.call(BuiltInMethod.UUID_TO_STRING.method, operand);
// If format string is supplied, return formatted date/time/timestamp
case DATE:
return RexImpTable.optimize2(operand, Expressions.isConstantNull(format)
? Expressions.call(BuiltInMethod.UNIX_DATE_TO_STRING.method, operand)
: Expressions.call(
Expressions.new_(
BuiltInMethod.FORMAT_DATE.method.getDeclaringClass()),
BuiltInMethod.FORMAT_DATE.method, format, operand));
case TIME:
return RexImpTable.optimize2(operand, Expressions.isConstantNull(format)
? Expressions.call(BuiltInMethod.UNIX_TIME_TO_STRING.method, operand)
: Expressions.call(
Expressions.new_(
BuiltInMethod.FORMAT_TIME.method.getDeclaringClass()),
BuiltInMethod.FORMAT_TIME.method, format, operand));
case TIME_WITH_LOCAL_TIME_ZONE:
return RexImpTable.optimize2(operand, Expressions.isConstantNull(format)
? Expressions.call(BuiltInMethod.TIME_WITH_LOCAL_TIME_ZONE_TO_STRING.method, operand,
Expressions.call(BuiltInMethod.TIME_ZONE.method, root))
: Expressions.call(
Expressions.new_(
BuiltInMethod.FORMAT_TIME.method.getDeclaringClass()),
BuiltInMethod.FORMAT_TIME.method, format, operand));
case TIMESTAMP:
return RexImpTable.optimize2(operand, Expressions.isConstantNull(format)
? Expressions.call(BuiltInMethod.UNIX_TIMESTAMP_TO_STRING.method, operand)
: Expressions.call(
Expressions.new_(
BuiltInMethod.FORMAT_TIMESTAMP.method.getDeclaringClass()),
BuiltInMethod.FORMAT_TIMESTAMP.method, format, operand));
case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
return RexImpTable.optimize2(operand, Expressions.isConstantNull(format)
? Expressions.call(BuiltInMethod.TIMESTAMP_WITH_LOCAL_TIME_ZONE_TO_STRING.method,
operand, Expressions.call(BuiltInMethod.TIME_ZONE.method, root))
: Expressions.call(
Expressions.new_(
BuiltInMethod.FORMAT_TIMESTAMP.method.getDeclaringClass()),
BuiltInMethod.FORMAT_TIMESTAMP.method, format, operand));
case INTERVAL_YEAR:
case INTERVAL_YEAR_MONTH:
case INTERVAL_MONTH:
return RexImpTable.optimize2(operand,
Expressions.call(BuiltInMethod.INTERVAL_YEAR_MONTH_TO_STRING.method,
operand,
Expressions.constant(
requireNonNull(interval, "interval").timeUnitRange)));
case INTERVAL_DAY:
case INTERVAL_DAY_HOUR:
case INTERVAL_DAY_MINUTE:
case INTERVAL_DAY_SECOND:
case INTERVAL_HOUR:
case INTERVAL_HOUR_MINUTE:
case INTERVAL_HOUR_SECOND:
case INTERVAL_MINUTE:
case INTERVAL_MINUTE_SECOND:
case INTERVAL_SECOND:
return RexImpTable.optimize2(operand,
Expressions.call(BuiltInMethod.INTERVAL_DAY_TIME_TO_STRING.method,
operand,
Expressions.constant(
requireNonNull(interval, "interval").timeUnitRange),
Expressions.constant(
interval.getFractionalSecondPrecision(
typeFactory.getTypeSystem()))));
case BOOLEAN:
return RexImpTable.optimize2(operand,
Expressions.call(BuiltInMethod.BOOLEAN_TO_STRING.method,
operand));
default:
return defaultExpression.get();
}
case DECIMAL: {
int precision = targetType.getPrecision();
int scale = targetType.getScale();
if (precision != RelDataType.PRECISION_NOT_SPECIFIED
&& scale != RelDataType.SCALE_NOT_SPECIFIED) {
if (sourceType.getFamily() == SqlTypeFamily.CHARACTER) {
return Expressions.call(
BuiltInMethod.CHAR_DECIMAL_CAST_ROUNDING_MODE.method,
operand,
Expressions.constant(precision),
Expressions.constant(scale),
Expressions.constant(typeFactory.getTypeSystem().roundingMode()));
} else if (sourceType.getFamily() == SqlTypeFamily.INTERVAL_DAY_TIME) {
return Expressions.call(
BuiltInMethod.SHORT_INTERVAL_DECIMAL_CAST_ROUNDING_MODE.method,
operand,
Expressions.constant(precision),
Expressions.constant(scale),
Expressions.constant(sourceType.getSqlTypeName().getEndUnit().multiplier),
Expressions.constant(typeFactory.getTypeSystem().roundingMode()));
} else if (sourceType.getFamily() == SqlTypeFamily.INTERVAL_YEAR_MONTH) {
return Expressions.call(
BuiltInMethod.LONG_INTERVAL_DECIMAL_CAST_ROUNDING_MODE.method,
operand,
Expressions.constant(precision),
Expressions.constant(scale),
Expressions.constant(sourceType.getSqlTypeName().getEndUnit().multiplier),
Expressions.constant(typeFactory.getTypeSystem().roundingMode()));
} else if (sourceType.getSqlTypeName() == SqlTypeName.DECIMAL) {
// Cast from DECIMAL to DECIMAL, may adjust scale and precision.
return Expressions.call(
BuiltInMethod.DECIMAL_DECIMAL_CAST_ROUNDING_MODE.method,
operand,
Expressions.constant(precision),
Expressions.constant(scale),
Expressions.constant(typeFactory.getTypeSystem().roundingMode()));
} else if (SqlTypeName.INT_TYPES.contains(sourceType.getSqlTypeName())) {
// Cast from INTEGER to DECIMAL, check for overflow
return Expressions.call(
BuiltInMethod.INTEGER_DECIMAL_CAST_ROUNDING_MODE.method,
operand,
Expressions.constant(precision),
Expressions.constant(scale),
Expressions.constant(typeFactory.getTypeSystem().roundingMode()));
} else if (SqlTypeName.APPROX_TYPES.contains(sourceType.getSqlTypeName())) {
// Cast from FLOAT/DOUBLE to DECIMAL
return Expressions.call(
BuiltInMethod.FP_DECIMAL_CAST_ROUNDING_MODE.method,
operand,
Expressions.constant(precision),
Expressions.constant(scale),
Expressions.constant(typeFactory.getTypeSystem().roundingMode()));
}
}
return defaultExpression.get();
}
case BIGINT:
case INTEGER:
case TINYINT:
case SMALLINT: {
if (SqlTypeName.NUMERIC_TYPES.contains(sourceType.getSqlTypeName())) {
Type javaClass = typeFactory.getJavaClass(targetType);
Primitive primitive = Primitive.of(javaClass);
if (primitive == null) {
primitive = Primitive.ofBox(javaClass);
}
return Expressions.call(
BuiltInMethod.INTEGER_CAST_ROUNDING_MODE.method,
Expressions.constant(primitive),
operand, Expressions.constant(typeFactory.getTypeSystem().roundingMode()));
}
return defaultExpression.get();
}
default:
return defaultExpression.get();
}
}