Expression translateCast()

in modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/RexToLixTranslator.java [221:637]


    Expression translateCast(
        RelDataType sourceType,
        RelDataType targetType,
        Expression operand) {
        Expression convert = null;
        switch (targetType.getSqlTypeName()) {
            case ANY:
                convert = operand;
                break;
            case DATE:
                switch (sourceType.getSqlTypeName()) {
                    case CHAR:
                    case VARCHAR:
                        convert =
                            Expressions.call(BuiltInMethod.STRING_TO_DATE.method, operand);
                        break;
                    case TIMESTAMP:
                        convert = Expressions.convert_(
                            Expressions.call(BuiltInMethod.FLOOR_DIV.method,
                                operand, Expressions.constant(DateTimeUtils.MILLIS_PER_DAY)),
                            int.class);
                        break;
                    case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
                        convert = RexImpTable.optimize2(
                            operand,
                            Expressions.call(
                                BuiltInMethod.TIMESTAMP_WITH_LOCAL_TIME_ZONE_TO_DATE.method,
                                operand,
                                Expressions.call(BuiltInMethod.TIME_ZONE.method, root)));
                }
                break;
            case TIME:
                switch (sourceType.getSqlTypeName()) {
                    case CHAR:
                    case VARCHAR:
                        convert =
                            Expressions.call(BuiltInMethod.STRING_TO_TIME.method, operand);
                        break;
                    case TIME_WITH_LOCAL_TIME_ZONE:
                        convert = RexImpTable.optimize2(
                            operand,
                            Expressions.call(
                                BuiltInMethod.TIME_WITH_LOCAL_TIME_ZONE_TO_TIME.method,
                                operand,
                                Expressions.call(BuiltInMethod.TIME_ZONE.method, root)));
                        break;
                    case TIMESTAMP:
                        convert = Expressions.convert_(
                            Expressions.call(
                                BuiltInMethod.FLOOR_MOD.method,
                                operand,
                                Expressions.constant(DateTimeUtils.MILLIS_PER_DAY)),
                            int.class);
                        break;
                    case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
                        convert = RexImpTable.optimize2(
                            operand,
                            Expressions.call(
                                BuiltInMethod.TIMESTAMP_WITH_LOCAL_TIME_ZONE_TO_TIME.method,
                                operand,
                                Expressions.call(BuiltInMethod.TIME_ZONE.method, root)));
                }
                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;
            case TIMESTAMP:
                switch (sourceType.getSqlTypeName()) {
                    case CHAR:
                    case VARCHAR:
                        convert =
                            Expressions.call(BuiltInMethod.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;
            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;
            case BOOLEAN:
                switch (sourceType.getSqlTypeName()) {
                    case CHAR:
                    case VARCHAR:
                        convert = Expressions.call(
                            BuiltInMethod.STRING_TO_BOOLEAN.method,
                            operand);
                }
                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(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(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;
                }
                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 (sourceType.getSqlTypeName().getFamily()) {
                    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;
                    case NUMERIC:
                        BigDecimal multiplier = targetType.getSqlTypeName().getEndUnit().multiplier;

                        if (SqlTypeName.FRACTIONAL_TYPES.contains(sourceType.getSqlTypeName())) {
                            convert = sourceType.getSqlTypeName() == SqlTypeName.DECIMAL
                                ? operand
                                : ConverterUtils.convertToDecimal(operand, typeFactory.createSqlType(SqlTypeName.DECIMAL));

                            convert = Expressions.call(
                                convert,
                                IgniteMethod.BIG_DECIMAL_MULTIPLY.method(),
                                Expressions.constant(multiplier));
                        }
                        else
                            convert = IgniteExpressions.multiplyExact(operand, Expressions.constant(multiplier.longValue()));

                        convert = ConverterUtils.convert(convert, targetType);
                        break;
                }
                break;
            case BINARY:
            case VARBINARY:
                switch (sourceType.getSqlTypeName().getFamily()) {
                    case CHARACTER:
                        convert = Expressions.call(IgniteMethod.STRING_TO_BYTESTRING.method(), operand);
                }
                break;
        }

        if (convert == null)
            convert = ConverterUtils.convert(operand, 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 srcPrecision = sourceType.getPrecision();
                            if (SqlTypeUtil.comparePrecision(srcPrecision, targetPrecision) <= 0)
                                truncate = false;

                            // If this is a widening cast, no need to pad.
                            if (SqlTypeUtil.comparePrecision(srcPrecision, 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;
        }
        return scaleIntervalToNumber(sourceType, targetType, convert);
    }