in flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/codegen/calls/ScalarOperators.scala [608:816]
def generateCast(
nullCheck: Boolean,
operand: GeneratedExpression,
targetType: TypeInformation[_])
: GeneratedExpression = (operand.resultType, targetType) match {
// special case: cast from TimeIndicatorTypeInfo to SqlTimeTypeInfo
case (ti: TimeIndicatorTypeInfo, SqlTimeTypeInfo.TIMESTAMP) =>
operand.copy(resultType = SqlTimeTypeInfo.TIMESTAMP) // just replace the TypeInformation
// identity casting
case (fromTp, toTp) if fromTp == toTp =>
operand
// array identity casting
// (e.g. for Integer[] that can be ObjectArrayTypeInfo or BasicArrayTypeInfo)
case (fromTp, toTp) if isArray(fromTp) && fromTp.getTypeClass == toTp.getTypeClass =>
operand
// Date -> String
case (SqlTimeTypeInfo.DATE, STRING_TYPE_INFO) =>
val method = qualifyMethod(BuiltInMethod.UNIX_DATE_TO_STRING.method)
generateUnaryOperatorIfNotNull(nullCheck, targetType, operand) {
(operandTerm) => s"$method($operandTerm)"
}
// Time -> String
case (SqlTimeTypeInfo.TIME, STRING_TYPE_INFO) =>
generateUnaryOperatorIfNotNull(nullCheck, targetType, operand) {
(operandTerm) => s"${internalToTimePointCode(operand.resultType, operandTerm)}.toString()"
}
// Timestamp -> String
case (SqlTimeTypeInfo.TIMESTAMP, STRING_TYPE_INFO) =>
val method = qualifyMethod(BuiltInMethod.UNIX_TIMESTAMP_TO_STRING.method)
generateUnaryOperatorIfNotNull(nullCheck, targetType, operand) {
(operandTerm) => s"$method($operandTerm, 3)"
}
// Interval Months -> String
case (TimeIntervalTypeInfo.INTERVAL_MONTHS, STRING_TYPE_INFO) =>
val method = qualifyMethod(BuiltInMethod.INTERVAL_YEAR_MONTH_TO_STRING.method)
val timeUnitRange = qualifyEnum(TimeUnitRange.YEAR_TO_MONTH)
generateUnaryOperatorIfNotNull(nullCheck, targetType, operand) {
(operandTerm) => s"$method($operandTerm, $timeUnitRange)"
}
// Interval Millis -> String
case (TimeIntervalTypeInfo.INTERVAL_MILLIS, STRING_TYPE_INFO) =>
val method = qualifyMethod(BuiltInMethod.INTERVAL_DAY_TIME_TO_STRING.method)
val timeUnitRange = qualifyEnum(TimeUnitRange.DAY_TO_SECOND)
generateUnaryOperatorIfNotNull(nullCheck, targetType, operand) {
(operandTerm) => s"$method($operandTerm, $timeUnitRange, 3)" // milli second precision
}
// Object array -> String
case (_: ObjectArrayTypeInfo[_, _] | _: BasicArrayTypeInfo[_, _], STRING_TYPE_INFO) =>
generateUnaryOperatorIfNotNull(nullCheck, targetType, operand) {
(operandTerm) => s"java.util.Arrays.deepToString($operandTerm)"
}
// Primitive array -> String
case (_: PrimitiveArrayTypeInfo[_], STRING_TYPE_INFO) =>
generateUnaryOperatorIfNotNull(nullCheck, targetType, operand) {
(operandTerm) => s"java.util.Arrays.toString($operandTerm)"
}
// * (not Date/Time/Timestamp) -> String
case (_, STRING_TYPE_INFO) =>
generateUnaryOperatorIfNotNull(nullCheck, targetType, operand) {
(operandTerm) => s""" "" + $operandTerm"""
}
// * -> Character
case (_, CHAR_TYPE_INFO) =>
throw new CodeGenException("Character type not supported.")
// String -> NUMERIC TYPE (not Character), Boolean
case (STRING_TYPE_INFO, _: NumericTypeInfo[_])
| (STRING_TYPE_INFO, BOOLEAN_TYPE_INFO) =>
val wrapperClass = targetType.getTypeClass.getCanonicalName
generateUnaryOperatorIfNotNull(nullCheck, targetType, operand) {
(operandTerm) => s"$wrapperClass.valueOf($operandTerm)"
}
// String -> BigDecimal
case (STRING_TYPE_INFO, BIG_DEC_TYPE_INFO) =>
val wrapperClass = targetType.getTypeClass.getCanonicalName
generateUnaryOperatorIfNotNull(nullCheck, targetType, operand) {
(operandTerm) => s"new $wrapperClass($operandTerm)"
}
// String -> Date
case (STRING_TYPE_INFO, SqlTimeTypeInfo.DATE) =>
generateUnaryOperatorIfNotNull(nullCheck, targetType, operand) {
(operandTerm) => s"${qualifyMethod(BuiltInMethod.STRING_TO_DATE.method)}($operandTerm)"
}
// String -> Time
case (STRING_TYPE_INFO, SqlTimeTypeInfo.TIME) =>
generateUnaryOperatorIfNotNull(nullCheck, targetType, operand) {
(operandTerm) => s"${qualifyMethod(BuiltInMethod.STRING_TO_TIME.method)}($operandTerm)"
}
// String -> Timestamp
case (STRING_TYPE_INFO, SqlTimeTypeInfo.TIMESTAMP) =>
generateUnaryOperatorIfNotNull(nullCheck, targetType, operand) {
(operandTerm) => s"${qualifyMethod(BuiltInMethod.STRING_TO_TIMESTAMP.method)}" +
s"($operandTerm)"
}
// Boolean -> NUMERIC TYPE
case (BOOLEAN_TYPE_INFO, nti: NumericTypeInfo[_]) =>
val targetTypeTerm = primitiveTypeTermForTypeInfo(nti)
generateUnaryOperatorIfNotNull(nullCheck, targetType, operand) {
(operandTerm) => s"($targetTypeTerm) ($operandTerm ? 1 : 0)"
}
// Boolean -> BigDecimal
case (BOOLEAN_TYPE_INFO, BIG_DEC_TYPE_INFO) =>
generateUnaryOperatorIfNotNull(nullCheck, targetType, operand) {
(operandTerm) => s"$operandTerm ? java.math.BigDecimal.ONE : java.math.BigDecimal.ZERO"
}
// NUMERIC TYPE -> Boolean
case (_: NumericTypeInfo[_], BOOLEAN_TYPE_INFO) =>
generateUnaryOperatorIfNotNull(nullCheck, targetType, operand) {
(operandTerm) => s"$operandTerm != 0"
}
// BigDecimal -> Boolean
case (BIG_DEC_TYPE_INFO, BOOLEAN_TYPE_INFO) =>
generateUnaryOperatorIfNotNull(nullCheck, targetType, operand) {
(operandTerm) => s"$operandTerm.compareTo(java.math.BigDecimal.ZERO) != 0"
}
// NUMERIC TYPE, BigDecimal -> NUMERIC TYPE, BigDecimal
case (_: NumericTypeInfo[_], _: NumericTypeInfo[_])
| (BIG_DEC_TYPE_INFO, _: NumericTypeInfo[_])
| (_: NumericTypeInfo[_], BIG_DEC_TYPE_INFO) =>
val operandCasting = numericCasting(operand.resultType, targetType)
generateUnaryOperatorIfNotNull(nullCheck, targetType, operand) {
(operandTerm) => s"${operandCasting(operandTerm)}"
}
// Date -> Timestamp
case (SqlTimeTypeInfo.DATE, SqlTimeTypeInfo.TIMESTAMP) =>
generateUnaryOperatorIfNotNull(nullCheck, targetType, operand) {
(operandTerm) =>
s"$operandTerm * ${classOf[DateTimeUtils].getCanonicalName}.MILLIS_PER_DAY"
}
// Timestamp -> Date
case (SqlTimeTypeInfo.TIMESTAMP, SqlTimeTypeInfo.DATE) =>
val targetTypeTerm = primitiveTypeTermForTypeInfo(targetType)
generateUnaryOperatorIfNotNull(nullCheck, targetType, operand) {
(operandTerm) =>
s"($targetTypeTerm) ($operandTerm / " +
s"${classOf[DateTimeUtils].getCanonicalName}.MILLIS_PER_DAY)"
}
// Time -> Timestamp
case (SqlTimeTypeInfo.TIME, SqlTimeTypeInfo.TIMESTAMP) =>
generateUnaryOperatorIfNotNull(nullCheck, targetType, operand) {
(operandTerm) => s"$operandTerm"
}
// Timestamp -> Time
case (SqlTimeTypeInfo.TIMESTAMP, SqlTimeTypeInfo.TIME) =>
val targetTypeTerm = primitiveTypeTermForTypeInfo(targetType)
generateUnaryOperatorIfNotNull(nullCheck, targetType, operand) {
(operandTerm) =>
s"($targetTypeTerm) ($operandTerm % " +
s"${classOf[DateTimeUtils].getCanonicalName}.MILLIS_PER_DAY)"
}
// internal temporal casting
// Date -> Integer
// Time -> Integer
// Timestamp -> Long
// Integer -> Date
// Integer -> Time
// Long -> Timestamp
// Integer -> Interval Months
// Long -> Interval Millis
// Interval Months -> Integer
// Interval Millis -> Long
case (SqlTimeTypeInfo.DATE, INT_TYPE_INFO) |
(SqlTimeTypeInfo.TIME, INT_TYPE_INFO) |
(SqlTimeTypeInfo.TIMESTAMP, LONG_TYPE_INFO) |
(INT_TYPE_INFO, SqlTimeTypeInfo.DATE) |
(INT_TYPE_INFO, SqlTimeTypeInfo.TIME) |
(LONG_TYPE_INFO, SqlTimeTypeInfo.TIMESTAMP) |
(INT_TYPE_INFO, TimeIntervalTypeInfo.INTERVAL_MONTHS) |
(LONG_TYPE_INFO, TimeIntervalTypeInfo.INTERVAL_MILLIS) |
(TimeIntervalTypeInfo.INTERVAL_MONTHS, INT_TYPE_INFO) |
(TimeIntervalTypeInfo.INTERVAL_MILLIS, LONG_TYPE_INFO) =>
internalExprCasting(operand, targetType)
// internal reinterpretation of temporal types
// Date, Time, Interval Months -> Long
case (SqlTimeTypeInfo.DATE, LONG_TYPE_INFO)
| (SqlTimeTypeInfo.TIME, LONG_TYPE_INFO)
| (TimeIntervalTypeInfo.INTERVAL_MONTHS, LONG_TYPE_INFO) =>
internalExprCasting(operand, targetType)
case (from, to) =>
throw new CodeGenException(s"Unsupported cast from '$from' to '$to'.")
}