def generateCast()

in flink-table/flink-table-planner-blink/src/main/scala/org/apache/flink/table/planner/codegen/calls/ScalarOperatorGens.scala [860:1295]


  def generateCast(
      ctx: CodeGeneratorContext,
      operand: GeneratedExpression,
      targetType: LogicalType)
    : GeneratedExpression = (operand.resultType.getTypeRoot, targetType.getTypeRoot) match {

    // special case: cast from TimeIndicatorTypeInfo to SqlTimeTypeInfo
    case (TIMESTAMP_WITHOUT_TIME_ZONE, TIMESTAMP_WITHOUT_TIME_ZONE)
      if operand.resultType.asInstanceOf[TimestampType].getKind == TimestampKind.PROCTIME ||
          operand.resultType.asInstanceOf[TimestampType].getKind == TimestampKind.ROWTIME ||
          targetType.asInstanceOf[TimestampType].getKind == TimestampKind.PROCTIME ||
          targetType.asInstanceOf[TimestampType].getKind == TimestampKind.ROWTIME =>
        operand.copy(resultType = new TimestampType(3)) // just replace the DataType

    case (TIMESTAMP_WITHOUT_TIME_ZONE, TIMESTAMP_WITHOUT_TIME_ZONE) =>
      val fromType = operand.resultType.asInstanceOf[TimestampType]
      val toType = targetType.asInstanceOf[TimestampType]
      if (fromType.getPrecision <= toType.getPrecision) {
        operand.copy(resultType = targetType)
      } else {
        val method = qualifyMethod(BuiltInMethods.TRUNCATE_SQL_TIMESTAMP)
        generateUnaryOperatorIfNotNull(ctx, targetType, operand) {
          operandTerm =>
            s"$method($operandTerm, ${toType.getPrecision})"
        }
      }

    case (TIMESTAMP_WITHOUT_TIME_ZONE, TIMESTAMP_WITH_LOCAL_TIME_ZONE) =>
      val fromType = operand.resultType.asInstanceOf[TimestampType]
      val toType = targetType.asInstanceOf[LocalZonedTimestampType]
      val method = qualifyMethod(BuiltInMethods.TIMESTAMP_TO_TIMESTAMP_WITH_LOCAL_ZONE)
      if (fromType.getPrecision < toType.getPrecision) {
        generateUnaryOperatorIfNotNull(ctx, targetType, operand) {
          operandTerm =>
            val timeZone = ctx.addReusableSessionTimeZone()
            s"$method($operandTerm, $timeZone)"
        }
      } else {
        val truncate_method = qualifyMethod(BuiltInMethods.TRUNCATE_SQL_TIMESTAMP)
        generateUnaryOperatorIfNotNull(ctx, targetType, operand) {
          operandTerm =>
            val timeZone = ctx.addReusableSessionTimeZone()
            s"$truncate_method($method($operandTerm, $timeZone), ${toType.getPrecision})"
        }
      }

    case (TIMESTAMP_WITH_LOCAL_TIME_ZONE, TIMESTAMP_WITHOUT_TIME_ZONE) =>
      val fromType = operand.resultType.asInstanceOf[LocalZonedTimestampType]
      val toType = targetType.asInstanceOf[TimestampType]
      val method = qualifyMethod(BuiltInMethods.TIMESTAMP_WITH_LOCAL_ZONE_TO_TIMESTAMP)
      if (fromType.getPrecision < toType.getPrecision) {
        generateUnaryOperatorIfNotNull(ctx, targetType, operand) {
          operandTerm =>
            val zone = ctx.addReusableSessionTimeZone()
            s"$method($operandTerm, $zone)"
        }
      } else {
        val truncate_method = qualifyMethod(BuiltInMethods.TRUNCATE_SQL_TIMESTAMP)
        generateUnaryOperatorIfNotNull(ctx, targetType, operand) {
          operandTerm =>
            val zone = ctx.addReusableSessionTimeZone()
            s"$truncate_method($method($operandTerm, $zone), ${toType.getPrecision})"
        }
      }

    case (TIMESTAMP_WITH_LOCAL_TIME_ZONE, TIMESTAMP_WITH_LOCAL_TIME_ZONE) =>
      val fromType = operand.resultType.asInstanceOf[LocalZonedTimestampType]
      val toType = targetType.asInstanceOf[LocalZonedTimestampType]
      if (fromType.getPrecision <= toType.getPrecision) {
        operand.copy(resultType = targetType)
      } else {
        val method = qualifyMethod(BuiltInMethods.TRUNCATE_SQL_TIMESTAMP)
        generateUnaryOperatorIfNotNull(ctx, targetType, operand) {
          operandTerm =>
            s"$method($operandTerm, ${toType.getPrecision})"
        }
      }

    // identity casting
    case (_, _) if isInteroperable(operand.resultType, targetType) =>
      operand.copy(resultType = targetType)

    // Date/Time/Timestamp -> String
    case (_, VARCHAR | CHAR) if TypeCheckUtils.isTimePoint(operand.resultType) =>
      generateStringResultCallIfArgsNotNull(ctx, Seq(operand)) {
        operandTerm =>
          s"${localTimeToStringCode(ctx, operand.resultType, operandTerm.head)}"
      }

    // Interval Months -> String
    case (INTERVAL_YEAR_MONTH, VARCHAR | CHAR) =>
      val method = qualifyMethod(BuiltInMethod.INTERVAL_YEAR_MONTH_TO_STRING.method)
      val timeUnitRange = qualifyEnum(TimeUnitRange.YEAR_TO_MONTH)
      generateStringResultCallIfArgsNotNull(ctx, Seq(operand)) {
        terms => s"$method(${terms.head}, $timeUnitRange)"
      }

    // Interval Millis -> String
    case (INTERVAL_DAY_TIME, VARCHAR | CHAR) =>
      val method = qualifyMethod(BuiltInMethod.INTERVAL_DAY_TIME_TO_STRING.method)
      val timeUnitRange = qualifyEnum(TimeUnitRange.DAY_TO_SECOND)
      generateStringResultCallIfArgsNotNull(ctx, Seq(operand)) {
        terms => s"$method(${terms.head}, $timeUnitRange, 3)" // milli second precision
      }

    // Array -> String
    case (ARRAY, VARCHAR | CHAR) =>
      generateCastArrayToString(ctx, operand, operand.resultType.asInstanceOf[ArrayType])

    // Byte array -> String UTF-8
    case (VARBINARY, VARCHAR | CHAR) =>
      val charset = classOf[StandardCharsets].getCanonicalName
      generateStringResultCallIfArgsNotNull(ctx, Seq(operand)) {
        terms => s"(new String(${terms.head}, $charset.UTF_8))"
      }


    // Map -> String
    case (MAP, VARCHAR | CHAR) =>
      generateCastMapToString(ctx, operand, operand.resultType.asInstanceOf[MapType])

    // composite type -> String
    case (ROW, VARCHAR | CHAR) =>
      generateCastRowDataToString(ctx, operand, operand.resultType.asInstanceOf[RowType])

    case (RAW, VARCHAR | CHAR) =>
      generateStringResultCallIfArgsNotNull(ctx, Seq(operand)) {
        terms =>
          val converter = DataFormatConverters.getConverterForDataType(
            fromLogicalTypeToDataType(operand.resultType))
          val converterTerm = ctx.addReusableObject(converter, "converter")
          s""" "" + $converterTerm.toExternal(${terms.head})"""
      }

    // * (not Date/Time/Timestamp) -> String
    // TODO: GenericType with Date/Time/Timestamp -> String would call toString implicitly
    case (_, VARCHAR | CHAR) =>
      generateStringResultCallIfArgsNotNull(ctx, Seq(operand)) {
        terms => s""" "" + ${terms.head}"""
      }

    // String -> Boolean
    case (VARCHAR | CHAR, BOOLEAN) =>
      generateUnaryOperatorIfNotNull(
        ctx,
        targetType,
        operand,
        resultNullable = true) {
        operandTerm => s"$BINARY_STRING_UTIL.toBooleanSQL($operandTerm)"
      }

    // String -> NUMERIC TYPE (not Character)
    case (VARCHAR | CHAR, _)
      if TypeCheckUtils.isNumeric(targetType) =>
      targetType match {
        case dt: DecimalType =>
          generateUnaryOperatorIfNotNull(ctx, targetType, operand) { operandTerm =>
            s"$BINARY_STRING_UTIL.toDecimal($operandTerm, ${dt.getPrecision}, ${dt.getScale})"
          }
        case _ =>
          val methodName = targetType.getTypeRoot match {
            case TINYINT => "toByte"
            case SMALLINT => "toShort"
            case INTEGER => "toInt"
            case BIGINT => "toLong"
            case DOUBLE => "toDouble"
            case FLOAT => "toFloat"
            case _ => null
          }
          assert(methodName != null, "Unexpected data type.")
          generateUnaryOperatorIfNotNull(
            ctx,
            targetType,
            operand,
            resultNullable = true) {
            operandTerm => s"($BINARY_STRING_UTIL.$methodName($operandTerm.trim()))"
          }
      }

    // String -> Date
    case (VARCHAR | CHAR, DATE) =>
      generateUnaryOperatorIfNotNull(
        ctx,
        targetType,
        operand,
        resultNullable = true) {
        operandTerm =>
          s"${qualifyMethod(BuiltInMethods.STRING_TO_DATE)}($operandTerm.toString())"
      }

    // String -> Time
    case (VARCHAR | CHAR, TIME_WITHOUT_TIME_ZONE) =>
      generateUnaryOperatorIfNotNull(
        ctx, 
        targetType,
        operand, 
        resultNullable = true) {
        operandTerm =>
          s"${qualifyMethod(BuiltInMethods.STRING_TO_TIME)}($operandTerm.toString())"
      }

    // String -> Timestamp
    case (VARCHAR | CHAR, TIMESTAMP_WITHOUT_TIME_ZONE) =>
      generateUnaryOperatorIfNotNull(
        ctx, 
        targetType,
        operand, 
        resultNullable = true) {
        operandTerm =>
          s"""
             |${qualifyMethod(BuiltInMethods.STRING_TO_TIMESTAMP)}($operandTerm.toString())
           """.stripMargin
      }

    case (VARCHAR | CHAR, TIMESTAMP_WITH_LOCAL_TIME_ZONE) =>
      generateUnaryOperatorIfNotNull(
        ctx, targetType, operand, resultNullable = true) { operandTerm =>
        val zone = ctx.addReusableSessionTimeZone()
        val method = qualifyMethod(BuiltInMethods.STRING_TO_TIMESTAMP_TIME_ZONE)
        s"$TIMESTAMP_DATA.fromEpochMillis($method($operandTerm.toString(), $zone))"
      }

    // String -> binary
    case (VARCHAR | CHAR, VARBINARY | BINARY) =>
      generateUnaryOperatorIfNotNull(ctx, targetType, operand) {
        operandTerm => s"$operandTerm.toBytes()"
      }

    // Note: SQL2003 $6.12 - casting is not allowed between boolean and numeric types.
    //       Calcite does not allow it either.

    // Boolean -> DECIMAL
    case (BOOLEAN, DECIMAL) =>
      val dt = targetType.asInstanceOf[DecimalType]
      generateUnaryOperatorIfNotNull(ctx, targetType, operand) {
        operandTerm => s"$DECIMAL_UTIL.castFrom($operandTerm, ${dt.getPrecision}, ${dt.getScale})"
      }

    // Boolean -> NUMERIC TYPE
    case (BOOLEAN, _) if TypeCheckUtils.isNumeric(targetType) =>
      val targetTypeTerm = primitiveTypeTermForType(targetType)
      generateUnaryOperatorIfNotNull(ctx, targetType, operand) {
        operandTerm => s"($targetTypeTerm) ($operandTerm ? 1 : 0)"
      }

    // DECIMAL -> Boolean
    case (DECIMAL, BOOLEAN) =>
      generateUnaryOperatorIfNotNull(ctx, targetType, operand) {
        operandTerm => s"$DECIMAL_UTIL.castToBoolean($operandTerm)"
      }

    // DECIMAL -> Timestamp
    case (DECIMAL, TIMESTAMP_WITHOUT_TIME_ZONE) =>
      generateUnaryOperatorIfNotNull(ctx, targetType, operand) {
        operandTerm =>
          s"$TIMESTAMP_DATA.fromEpochMillis($DECIMAL_UTIL.castToTimestamp($operandTerm))"
      }

    // NUMERIC TYPE -> Boolean
    case (_, BOOLEAN) if isNumeric(operand.resultType) =>
      generateUnaryOperatorIfNotNull(ctx, targetType, operand) {
        operandTerm => s"$operandTerm != 0"
      }

    // between NUMERIC TYPE | Decimal
    case  (_, _) if isNumeric(operand.resultType) && isNumeric(targetType) =>
      val operandCasting = numericCasting(operand.resultType, targetType)
      generateUnaryOperatorIfNotNull(ctx, targetType, operand) {
        operandTerm => s"${operandCasting(operandTerm)}"
      }

    // Date -> Timestamp
    case (DATE, TIMESTAMP_WITHOUT_TIME_ZONE) =>
      generateUnaryOperatorIfNotNull(ctx, targetType, operand) {
        operandTerm =>
          s"""
             |$TIMESTAMP_DATA.fromEpochMillis(
             |  $operandTerm * ${classOf[DateTimeUtils].getCanonicalName}.MILLIS_PER_DAY)
           """.stripMargin
      }

    // Timestamp -> Date
    case (TIMESTAMP_WITHOUT_TIME_ZONE, DATE) =>
      val targetTypeTerm = primitiveTypeTermForType(targetType)
      generateUnaryOperatorIfNotNull(ctx, targetType, operand) {
        operandTerm =>
          s"""
             |($targetTypeTerm) ($operandTerm.getMillisecond() /
             |  ${classOf[DateTimeUtils].getCanonicalName}.MILLIS_PER_DAY)
           """.stripMargin
      }

    // Time -> Timestamp
    case (TIME_WITHOUT_TIME_ZONE, TIMESTAMP_WITHOUT_TIME_ZONE) =>
      generateUnaryOperatorIfNotNull(ctx, targetType, operand) {
        operandTerm => s"$TIMESTAMP_DATA.fromEpochMillis($operandTerm)"
      }

    // Timestamp -> Time
    case (TIMESTAMP_WITHOUT_TIME_ZONE, TIME_WITHOUT_TIME_ZONE) =>
      val targetTypeTerm = primitiveTypeTermForType(targetType)
      generateUnaryOperatorIfNotNull(ctx, targetType, operand) {
        operandTerm =>
          s"($targetTypeTerm) ($operandTerm.getMillisecond() % " +
            s"${classOf[DateTimeUtils].getCanonicalName}.MILLIS_PER_DAY)"
      }

    // Date -> Timestamp with local time zone
    case (DATE, TIMESTAMP_WITH_LOCAL_TIME_ZONE) =>
      generateUnaryOperatorIfNotNull(ctx, targetType, operand) { operandTerm =>
        val zone = ctx.addReusableSessionTimeZone()
        val method = qualifyMethod(BuiltInMethods.DATE_TO_TIMESTAMP_WITH_LOCAL_TIME_ZONE)
        s"$TIMESTAMP_DATA.fromEpochMillis($method($operandTerm, $zone))"
      }

    // Timestamp with local time zone -> Date
    case (TIMESTAMP_WITH_LOCAL_TIME_ZONE, DATE) =>
      generateUnaryOperatorIfNotNull(ctx, targetType, operand) { operandTerm =>
        val zone = ctx.addReusableSessionTimeZone()
        val method = qualifyMethod(BuiltInMethods.TIMESTAMP_WITH_LOCAL_TIME_ZONE_TO_DATE)
        s"$method($operandTerm.getMillisecond(), $zone)"
      }

    // Time -> Timestamp with local time zone
    case (TIME_WITHOUT_TIME_ZONE, TIMESTAMP_WITH_LOCAL_TIME_ZONE) =>
      generateUnaryOperatorIfNotNull(ctx, targetType, operand) { operandTerm =>
        val zone = ctx.addReusableSessionTimeZone()
        val method = qualifyMethod(BuiltInMethods.TIME_TO_TIMESTAMP_WITH_LOCAL_TIME_ZONE)
        s"$TIMESTAMP_DATA.fromEpochMillis($method($operandTerm, $zone))"
      }

    // Timestamp with local time zone -> Time
    case (TIMESTAMP_WITH_LOCAL_TIME_ZONE, TIME_WITHOUT_TIME_ZONE) =>
      generateUnaryOperatorIfNotNull(ctx, targetType, operand) { operandTerm =>
        val zone = ctx.addReusableSessionTimeZone()
        val method = qualifyMethod(BuiltInMethods.TIMESTAMP_WITH_LOCAL_TIME_ZONE_TO_TIME)
        s"$method($operandTerm.getMillisecond(), $zone)"
      }

    // Timestamp -> Decimal
    case  (TIMESTAMP_WITHOUT_TIME_ZONE, DECIMAL) =>
      val dt = targetType.asInstanceOf[DecimalType]
      generateUnaryOperatorIfNotNull(ctx, targetType, operand) {
        operandTerm =>
          s"""
             |$DECIMAL_UTIL.castFrom(
             |  ((double) ($operandTerm.getMillisecond() / 1000.0)),
             |  ${dt.getPrecision}, ${dt.getScale})
           """.stripMargin
      }

    // Tinyint -> Timestamp
    // Smallint -> Timestamp
    // Int -> Timestamp
    // Bigint -> Timestamp
    case (TINYINT, TIMESTAMP_WITHOUT_TIME_ZONE) |
         (SMALLINT, TIMESTAMP_WITHOUT_TIME_ZONE) |
         (INTEGER, TIMESTAMP_WITHOUT_TIME_ZONE) |
         (BIGINT, TIMESTAMP_WITHOUT_TIME_ZONE) =>
      generateUnaryOperatorIfNotNull(ctx, targetType, operand) {
        operandTerm => s"$TIMESTAMP_DATA.fromEpochMillis(((long) $operandTerm) * 1000)"
      }

    // Float -> Timestamp
    // Double -> Timestamp
    case (FLOAT, TIMESTAMP_WITHOUT_TIME_ZONE) |
         (DOUBLE, TIMESTAMP_WITHOUT_TIME_ZONE) =>
      generateUnaryOperatorIfNotNull(ctx, targetType, operand) {
        operandTerm => s"$TIMESTAMP_DATA.fromEpochMillis((long) ($operandTerm * 1000))"
      }

    // Timestamp -> Tinyint
    case (TIMESTAMP_WITHOUT_TIME_ZONE, TINYINT) =>
      generateUnaryOperatorIfNotNull(ctx, targetType, operand) {
        operandTerm => s"((byte) ($operandTerm.getMillisecond() / 1000))"
      }

    // Timestamp -> Smallint
    case (TIMESTAMP_WITHOUT_TIME_ZONE, SMALLINT) =>
      generateUnaryOperatorIfNotNull(ctx, targetType, operand) {
        operandTerm => s"((short) ($operandTerm.getMillisecond() / 1000))"
      }

    // Timestamp -> Int
    case (TIMESTAMP_WITHOUT_TIME_ZONE, INTEGER) =>
      generateUnaryOperatorIfNotNull(ctx, targetType, operand) {
        operandTerm => s"((int) ($operandTerm.getMillisecond() / 1000))"
      }

    // Timestamp -> BigInt
    case (TIMESTAMP_WITHOUT_TIME_ZONE, BIGINT) =>
      generateUnaryOperatorIfNotNull(ctx, targetType, operand) {
        operandTerm => s"((long) ($operandTerm.getMillisecond() / 1000))"
      }

    // Timestamp -> Float
    case (TIMESTAMP_WITHOUT_TIME_ZONE, FLOAT) =>
      generateUnaryOperatorIfNotNull(ctx, targetType, operand) {
        operandTerm => s"((float) ($operandTerm.getMillisecond() / 1000.0))"
      }

    // Timestamp -> Double
    case (TIMESTAMP_WITHOUT_TIME_ZONE, DOUBLE) =>
      generateUnaryOperatorIfNotNull(ctx, targetType, operand) {
        operandTerm => s"((double) ($operandTerm.getMillisecond() / 1000.0))"
      }

    // internal temporal casting
    // Date -> Integer
    // Time -> Integer
    // Integer -> Date
    // Integer -> Time
    // Integer -> Interval Months
    // Long -> Interval Millis
    // Interval Months -> Integer
    // Interval Millis -> Long
    case (DATE, INTEGER) |
         (TIME_WITHOUT_TIME_ZONE, INTEGER) |
         (INTEGER, DATE) |
         (INTEGER, TIME_WITHOUT_TIME_ZONE) |
         (INTEGER, INTERVAL_YEAR_MONTH) |
         (BIGINT, INTERVAL_DAY_TIME) |
         (INTERVAL_YEAR_MONTH, INTEGER) |
         (INTERVAL_DAY_TIME, BIGINT) =>
      internalExprCasting(operand, targetType)

    // internal reinterpretation of temporal types
    // Date, Time, Interval Months -> Long
    case  (DATE, BIGINT)
          | (TIME_WITHOUT_TIME_ZONE, BIGINT)
          | (INTERVAL_YEAR_MONTH, BIGINT) =>
      internalExprCasting(operand, targetType)

    case (_, _) =>
      throw new CodeGenException(s"Unsupported cast from '${operand.resultType}' to '$targetType'.")
  }