private Expression getConvertExpression()

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();
    }
  }