Expression translateCast()

in modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/RexToLixTranslator.java [286:687]


    Expression translateCast(
            RelDataType sourceType,
            RelDataType targetType,
            Expression operand) {
        Expression convert = null;
        switch (targetType.getSqlTypeName()) {
            case ANY:
                var toCustomType = CustomTypesConversion.INSTANCE.tryConvert(operand, targetType);
                if (toCustomType != null) {
                    convert = toCustomType;
                } else {
                    convert = operand;
                }
                break;
            case GEOMETRY:
                switch (sourceType.getSqlTypeName()) {
                    case CHAR:
                    case VARCHAR:
                        convert = Expressions.call(BuiltInMethod.ST_GEOM_FROM_EWKT.method, operand);
                        break;
                    default:
                        break;
                }
                break;
            case DATE:
                convert = translateCastToDate(sourceType, operand);
                break;
            case TIME:
                convert = translateCastToTime(sourceType, operand);
                break;
            case TIME_WITH_LOCAL_TIME_ZONE:
                switch (sourceType.getSqlTypeName()) {
                    case CHAR:
                    case VARCHAR:
                        convert =
                                Expressions.call(BuiltInMethod.STRING_TO_TIME_WITH_LOCAL_TIME_ZONE.method,
                                        operand);
                        break;
                    case TIME:
                        convert =
                                Expressions.call(
                                        BuiltInMethod.TIME_STRING_TO_TIME_WITH_LOCAL_TIME_ZONE.method,
                                        RexImpTable.optimize2(operand,
                                                Expressions.call(BuiltInMethod.UNIX_TIME_TO_STRING.method,
                                                        operand)),
                                        Expressions.call(BuiltInMethod.TIME_ZONE.method, root));
                        break;
                    case TIMESTAMP:
                        convert =
                                Expressions.call(
                                        BuiltInMethod.TIMESTAMP_STRING_TO_TIMESTAMP_WITH_LOCAL_TIME_ZONE.method,
                                        RexImpTable.optimize2(operand,
                                                Expressions.call(
                                                        BuiltInMethod.UNIX_TIMESTAMP_TO_STRING.method,
                                                        operand)),
                                        Expressions.call(BuiltInMethod.TIME_ZONE.method, root));
                        break;
                    case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
                        convert =
                                RexImpTable.optimize2(operand,
                                        Expressions.call(
                                                BuiltInMethod
                                                        .TIMESTAMP_WITH_LOCAL_TIME_ZONE_TO_TIME_WITH_LOCAL_TIME_ZONE
                                                        .method,
                                                operand));
                        break;
                    default:
                        break;
                }
                break;
            case TIMESTAMP:
                switch (sourceType.getSqlTypeName()) {
                    case CHAR:
                    case VARCHAR:
                        convert =
                                Expressions.call(IgniteMethod.STRING_TO_TIMESTAMP.method(), operand);
                        break;
                    case DATE:
                        convert =
                                Expressions.multiply(Expressions.convert_(operand, long.class),
                                        Expressions.constant(DateTimeUtils.MILLIS_PER_DAY));
                        break;
                    case TIME:
                        convert =
                                Expressions.add(
                                        Expressions.multiply(
                                                Expressions.convert_(
                                                        Expressions.call(BuiltInMethod.CURRENT_DATE.method, root),
                                                        long.class),
                                                Expressions.constant(DateTimeUtils.MILLIS_PER_DAY)),
                                        Expressions.convert_(operand, long.class));
                        break;
                    case TIME_WITH_LOCAL_TIME_ZONE:
                        convert =
                                RexImpTable.optimize2(operand,
                                        Expressions.call(
                                                BuiltInMethod.TIME_WITH_LOCAL_TIME_ZONE_TO_TIMESTAMP.method,
                                                Expressions.call(BuiltInMethod.UNIX_DATE_TO_STRING.method,
                                                        Expressions.call(BuiltInMethod.CURRENT_DATE.method, root)),
                                                operand,
                                                Expressions.call(BuiltInMethod.TIME_ZONE.method, root)));
                        break;
                    case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
                        convert =
                                RexImpTable.optimize2(operand,
                                        Expressions.call(
                                                BuiltInMethod.TIMESTAMP_WITH_LOCAL_TIME_ZONE_TO_TIMESTAMP.method,
                                                operand,
                                                Expressions.call(BuiltInMethod.TIME_ZONE.method, root)));
                        break;
                    default:
                        break;
                }
                break;
            case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
                switch (sourceType.getSqlTypeName()) {
                    case CHAR:
                    case VARCHAR:
                        convert =
                                Expressions.call(
                                        BuiltInMethod.STRING_TO_TIMESTAMP_WITH_LOCAL_TIME_ZONE.method,
                                        operand);
                        break;
                    case DATE:
                        convert =
                                Expressions.call(
                                        BuiltInMethod.TIMESTAMP_STRING_TO_TIMESTAMP_WITH_LOCAL_TIME_ZONE.method,
                                        RexImpTable.optimize2(operand,
                                                Expressions.call(
                                                        BuiltInMethod.UNIX_TIMESTAMP_TO_STRING.method,
                                                        Expressions.multiply(
                                                                Expressions.convert_(operand, long.class),
                                                                Expressions.constant(DateTimeUtils.MILLIS_PER_DAY)))),
                                        Expressions.call(BuiltInMethod.TIME_ZONE.method, root));
                        break;
                    case TIME:
                        convert =
                                Expressions.call(
                                        BuiltInMethod.TIMESTAMP_STRING_TO_TIMESTAMP_WITH_LOCAL_TIME_ZONE.method,
                                        RexImpTable.optimize2(operand,
                                                Expressions.call(
                                                        BuiltInMethod.UNIX_TIMESTAMP_TO_STRING.method,
                                                        Expressions.add(
                                                                Expressions.multiply(
                                                                        Expressions.convert_(
                                                                                Expressions.call(BuiltInMethod.CURRENT_DATE.method, root),
                                                                                long.class),
                                                                        Expressions.constant(DateTimeUtils.MILLIS_PER_DAY)),
                                                                Expressions.convert_(operand, long.class)))),
                                        Expressions.call(BuiltInMethod.TIME_ZONE.method, root));
                        break;
                    case TIME_WITH_LOCAL_TIME_ZONE:
                        convert =
                                RexImpTable.optimize2(operand,
                                        Expressions.call(
                                                BuiltInMethod
                                                        .TIME_WITH_LOCAL_TIME_ZONE_TO_TIMESTAMP_WITH_LOCAL_TIME_ZONE
                                                        .method,
                                                Expressions.call(BuiltInMethod.UNIX_DATE_TO_STRING.method,
                                                        Expressions.call(BuiltInMethod.CURRENT_DATE.method, root)),
                                                operand));
                        break;
                    case TIMESTAMP:
                        convert =
                                Expressions.call(
                                        BuiltInMethod.TIMESTAMP_STRING_TO_TIMESTAMP_WITH_LOCAL_TIME_ZONE.method,
                                        RexImpTable.optimize2(operand,
                                                Expressions.call(
                                                        BuiltInMethod.UNIX_TIMESTAMP_TO_STRING.method,
                                                        operand)),
                                        Expressions.call(BuiltInMethod.TIME_ZONE.method, root));
                        break;
                    default:
                        break;
                }
                break;
            case BOOLEAN:
                switch (sourceType.getSqlTypeName()) {
                    case CHAR:
                    case VARCHAR:
                        convert =
                                Expressions.call(BuiltInMethod.STRING_TO_BOOLEAN.method, operand);
                        break;
                    default:
                        break;
                }
                break;
            case CHAR:
            case VARCHAR:
                final SqlIntervalQualifier interval =
                        sourceType.getIntervalQualifier();
                switch (sourceType.getSqlTypeName()) {
                    case DATE:
                        convert =
                                RexImpTable.optimize2(operand,
                                        Expressions.call(BuiltInMethod.UNIX_DATE_TO_STRING.method,
                                                operand));
                        break;
                    case TIME:
                        convert =
                                RexImpTable.optimize2(operand,
                                        Expressions.call(BuiltInMethod.UNIX_TIME_TO_STRING.method,
                                                operand));
                        break;
                    case TIME_WITH_LOCAL_TIME_ZONE:
                        convert =
                                RexImpTable.optimize2(operand,
                                        Expressions.call(
                                                BuiltInMethod.TIME_WITH_LOCAL_TIME_ZONE_TO_STRING.method,
                                                operand,
                                                Expressions.call(BuiltInMethod.TIME_ZONE.method, root)));
                        break;
                    case TIMESTAMP:
                        convert =
                                RexImpTable.optimize2(operand,
                                        Expressions.call(BuiltInMethod.UNIX_TIMESTAMP_TO_STRING.method,
                                                operand));
                        break;
                    case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
                        convert =
                                RexImpTable.optimize2(operand,
                                        Expressions.call(
                                                BuiltInMethod.TIMESTAMP_WITH_LOCAL_TIME_ZONE_TO_STRING.method,
                                                operand,
                                                Expressions.call(BuiltInMethod.TIME_ZONE.method, root)));
                        break;
                    case INTERVAL_YEAR:
                    case INTERVAL_YEAR_MONTH:
                    case INTERVAL_MONTH:
                        convert =
                                RexImpTable.optimize2(operand,
                                        Expressions.call(
                                                BuiltInMethod.INTERVAL_YEAR_MONTH_TO_STRING.method,
                                                operand,
                                                Expressions.constant(
                                                        requireNonNull(interval, "interval").timeUnitRange)));
                        break;
                    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:
                        convert =
                                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()))));
                        break;
                    case BOOLEAN:
                        convert =
                                RexImpTable.optimize2(operand,
                                        Expressions.call(BuiltInMethod.BOOLEAN_TO_STRING.method,
                                                operand));
                        break;
                    case BINARY:
                    case VARBINARY:
                        convert = RexImpTable.optimize2(
                                operand,
                                Expressions.call(IgniteMethod.BYTESTRING_TO_STRING.method(), operand));
                        break;
                    default:
                        break;
                }
                break;
            case BINARY:
            case VARBINARY:
                switch (sourceType.getSqlTypeName().getFamily()) {
                    case CHARACTER:
                        convert = Expressions.call(IgniteMethod.STRING_TO_BYTESTRING.method(), operand);
                        break;
                    default:
                        // No-Op.
                }
                break;
            default:
                break;
        }

        if (targetType.getSqlTypeName() == SqlTypeName.DECIMAL) {
            convert = ConverterUtils.convertToDecimal(operand, targetType);
        }

        if (convert == null) {
            convert = ConverterUtils.convert(operand, typeFactory.getJavaClass(targetType));
        }
        // Going from anything to CHAR(n) or VARCHAR(n), make sure value is no
        // longer than n.
        boolean pad = false;
        boolean truncate = true;
        switch (targetType.getSqlTypeName()) {
            case CHAR:
            case BINARY:
                pad = true;
                // fall through
            case VARCHAR:
            case VARBINARY:
                final int targetPrecision = targetType.getPrecision();
                if (targetPrecision >= 0) {
                    switch (sourceType.getSqlTypeName()) {
                        case CHAR:
                        case VARCHAR:
                        case BINARY:
                        case VARBINARY:
                            // If this is a widening cast, no need to truncate.
                            final int sourcePrecision = sourceType.getPrecision();
                            if (SqlTypeUtil.comparePrecision(sourcePrecision, targetPrecision)
                                    <= 0) {
                                truncate = false;
                            }
                            // If this is a widening cast, no need to pad.
                            if (SqlTypeUtil.comparePrecision(sourcePrecision, targetPrecision)
                                    >= 0) {
                                pad = false;
                            }
                            // fall through
                        default:
                            if (truncate || pad) {
                                convert =
                                        Expressions.call(
                                                pad
                                                        ? BuiltInMethod.TRUNCATE_OR_PAD.method
                                                        : BuiltInMethod.TRUNCATE.method,
                                                convert,
                                                Expressions.constant(targetPrecision));
                            }
                    }
                }
                break;
            case TIMESTAMP:
                int targetScale = targetType.getScale();
                if (targetScale == RelDataType.SCALE_NOT_SPECIFIED) {
                    targetScale = 0;
                }
                if (targetScale < sourceType.getScale()) {
                    convert =
                            Expressions.call(
                                    BuiltInMethod.ROUND_LONG.method,
                                    convert,
                                    Expressions.constant(
                                            (long) Math.pow(10, 3 - targetScale)));
                }
                break;
            case INTERVAL_YEAR:
            case INTERVAL_YEAR_MONTH:
            case INTERVAL_MONTH:
            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:
                switch (requireNonNull(sourceType.getSqlTypeName().getFamily(),
                        () -> "null SqlTypeFamily for " + sourceType + ", SqlTypeName "
                                + sourceType.getSqlTypeName())) {
                    case NUMERIC:
                        final BigDecimal multiplier =
                                targetType.getSqlTypeName().getEndUnit().multiplier;
                        final BigDecimal divider = BigDecimal.ONE;
                        convert = RexImpTable.multiplyDivide(convert, multiplier, divider);
                        break;
                    case CHARACTER:
                        SqlIntervalQualifier intervalQualifier = targetType.getIntervalQualifier();

                        Method method = intervalQualifier.isYearMonth()
                                ? IgniteMethod.PARSE_INTERVAL_YEAR_MONTH.method()
                                : IgniteMethod.PARSE_INTERVAL_DAY_TIME.method();

                        convert = Expressions.call(
                                method,
                                operand,
                                Expressions.new_(SqlIntervalQualifier.class,
                                        Expressions.constant(intervalQualifier.getStartUnit()),
                                        Expressions.constant(intervalQualifier.getStartPrecisionPreservingDefault()),
                                        Expressions.constant(intervalQualifier.getEndUnit()),
                                        Expressions.constant(intervalQualifier.getFractionalSecondPrecisionPreservingDefault()),
                                        Expressions.field(null, SqlParserPos.class, "ZERO")
                                )
                        );
                        break;
                    default:
                        break;
                }
                break;
            default:
                break;
        }
        return scaleIntervalToNumber(sourceType, targetType, convert);
    }