All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hazelcast.sql.impl.calcite.opt.physical.visitor.RexToExpression Maven / Gradle / Ivy

There is a newer version: 5.4.0
Show newest version
/*
 * Copyright (c) 2008-2020, Hazelcast, Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in com.hazelcast.com.liance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.com.hazelcast.org.licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.hazelcast.com.hazelcast.sql.impl.calcite.opt.physical.visitor;

import com.hazelcast.com.hazelcast.sql.impl.QueryException;
import com.hazelcast.com.hazelcast.sql.impl.calcite.SqlToQueryType;
import com.hazelcast.com.hazelcast.sql.impl.calcite.validate.HazelcastSqlOperatorTable;
import com.hazelcast.com.hazelcast.sql.impl.expression.CastExpression;
import com.hazelcast.com.hazelcast.sql.impl.expression.ConstantExpression;
import com.hazelcast.com.hazelcast.sql.impl.expression.Expression;
import com.hazelcast.com.hazelcast.sql.impl.expression.SymbolExpression;
import com.hazelcast.com.hazelcast.sql.impl.expression.math.AbsFunction;
import com.hazelcast.com.hazelcast.sql.impl.expression.math.DivideFunction;
import com.hazelcast.com.hazelcast.sql.impl.expression.math.DoubleFunction;
import com.hazelcast.com.hazelcast.sql.impl.expression.math.FloorCeilFunction;
import com.hazelcast.com.hazelcast.sql.impl.expression.math.MinusFunction;
import com.hazelcast.com.hazelcast.sql.impl.expression.math.MultiplyFunction;
import com.hazelcast.com.hazelcast.sql.impl.expression.math.PlusFunction;
import com.hazelcast.com.hazelcast.sql.impl.expression.math.RandFunction;
import com.hazelcast.com.hazelcast.sql.impl.expression.math.RoundTruncateFunction;
import com.hazelcast.com.hazelcast.sql.impl.expression.math.SignFunction;
import com.hazelcast.com.hazelcast.sql.impl.expression.math.UnaryMinusFunction;
import com.hazelcast.com.hazelcast.sql.impl.expression.predicate.AndPredicate;
import com.hazelcast.com.hazelcast.sql.impl.expression.predicate.ComparisonMode;
import com.hazelcast.com.hazelcast.sql.impl.expression.predicate.ComparisonPredicate;
import com.hazelcast.com.hazelcast.sql.impl.expression.predicate.IsFalsePredicate;
import com.hazelcast.com.hazelcast.sql.impl.expression.predicate.IsNotFalsePredicate;
import com.hazelcast.com.hazelcast.sql.impl.expression.predicate.IsNotNullPredicate;
import com.hazelcast.com.hazelcast.sql.impl.expression.predicate.IsNotTruePredicate;
import com.hazelcast.com.hazelcast.sql.impl.expression.predicate.IsNullPredicate;
import com.hazelcast.com.hazelcast.sql.impl.expression.predicate.IsTruePredicate;
import com.hazelcast.com.hazelcast.sql.impl.expression.predicate.NotPredicate;
import com.hazelcast.com.hazelcast.sql.impl.expression.predicate.OrPredicate;
import com.hazelcast.com.hazelcast.sql.impl.expression.string.AsciiFunction;
import com.hazelcast.com.hazelcast.sql.impl.expression.string.CharLengthFunction;
import com.hazelcast.com.hazelcast.sql.impl.expression.string.ConcatFunction;
import com.hazelcast.com.hazelcast.sql.impl.expression.string.InitcapFunction;
import com.hazelcast.com.hazelcast.sql.impl.expression.string.LikeFunction;
import com.hazelcast.com.hazelcast.sql.impl.expression.string.LowerFunction;
import com.hazelcast.com.hazelcast.sql.impl.expression.string.SubstringFunction;
import com.hazelcast.com.hazelcast.sql.impl.expression.string.TrimFunction;
import com.hazelcast.com.hazelcast.sql.impl.expression.string.UpperFunction;
import com.hazelcast.com.hazelcast.sql.impl.type.QueryDataType;
import com.hazelcast.org.apache.calcite.rex.RexCall;
import com.hazelcast.org.apache.calcite.rex.RexLiteral;
import com.hazelcast.org.apache.calcite.sql.SqlFunction;
import com.hazelcast.org.apache.calcite.sql.SqlOperator;
import com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable;
import com.hazelcast.org.apache.calcite.sql.fun.SqlTrimFunction;
import com.hazelcast.org.apache.calcite.sql.type.SqlTypeName;

import java.math.BigDecimal;

import static com.hazelcast.com.hazelcast.sql.impl.calcite.validate.HazelcastSqlOperatorTable.CHARACTER_LENGTH;
import static com.hazelcast.com.hazelcast.sql.impl.calcite.validate.HazelcastSqlOperatorTable.CHAR_LENGTH;
import static com.hazelcast.com.hazelcast.sql.impl.calcite.validate.HazelcastSqlOperatorTable.LENGTH;

/**
 * Utility methods for REX to Hazelcast expression conversion.
 */
public final class RexToExpression {

    private RexToExpression() {
        // No-op.
    }

    /**
     * Converts the given REX literal to runtime {@link ConstantExpression
     * constant expression}.
     *
     * @param literal the literal to convert.
     * @return the resulting constant expression.
     */
    @SuppressWarnings("checkstyle:CyclomaticComplexity")
    public static Expression convertLiteral(RexLiteral literal) {
        SqlTypeName type = literal.getType().getSqlTypeName();

        switch (type) {
            case BOOLEAN:
                return convertBooleanLiteral(literal, type);

            case TINYINT:
            case SMALLINT:
            case INTEGER:
            case BIGINT:
            case DECIMAL:
            case REAL:
            case FLOAT:
            case DOUBLE:
                return convertNumericLiteral(literal, type);

            case CHAR:
            case VARCHAR:
                return convertStringLiteral(literal, type);

            case NULL:
                return ConstantExpression.create(null, QueryDataType.NULL);

            case ANY:
                // currently, the only possible literal of ANY type is NULL
                assert literal.getValueAs(Object.class) == null;
                return ConstantExpression.create(null, QueryDataType.OBJECT);

            case SYMBOL:
                return SymbolExpression.create(literal.getValue());

            default:
                throw QueryException.error("Unsupported literal: " + literal);
        }
    }

    /**
     * Converts a {@link RexCall} to {@link Expression}.
     *
     * @param call the call to convert.
     * @return the resulting expression.
     * @throws QueryException if the given {@link RexCall} can't be
     *                        converted.
     */
    @SuppressWarnings({"checkstyle:CyclomaticComplexity", "checkstyle:MethodLength", "checkstyle:ReturnCount",
        "checkstyle:NPathComplexity"})
    public static Expression convertCall(RexCall call, Expression[] operands) {
        SqlOperator operator = call.getOperator();
        QueryDataType resultType = SqlToQueryType.map(call.getType().getSqlTypeName());

        switch (operator.getKind()) {
            case DEFAULT:
                return ConstantExpression.create(null, resultType);

            case CAST:
                if (operands[0].getType().equals(resultType)) {
                    // It might happen that two types Calcite considers different
                    // are mapped to the same Hazelcast type. For instance, to
                    // preserve the row signature, Calcite may insert synthetic
                    // casts from a non-nullable type to the same nullable type.
                    // Technically, such casts are not 100% valid SQL construct
                    // since casts can't change the nullability of the operand
                    // being casted, but they are valid on the RexNode level.
                    //
                    // Consider nullableBooleanColumn OR TRUE, the type of this
                    // expression is nullable BOOLEAN. When Calcite simplifies it
                    // to TRUE, the type changes to non-nullable BOOLEAN since
                    // literals are non-nullable (except NULL literal itself).
                    // That changes the row signature, to preserve it Calcite
                    // synthetically casts TRUE to a nullable BOOLEAN.
                    //
                    // Currently, all Hazelcast types are nullable, therefore
                    // there is no distinction between non-nullable types and
                    // nullable ones after the conversion.
                    return operands[0];
                }
                return CastExpression.create(operands[0], resultType);

            case AND:
                return AndPredicate.create(operands);

            case OR:
                return OrPredicate.create(operands);

            case NOT:
                return NotPredicate.create(operands[0]);

            case PLUS:
                return PlusFunction.create(operands[0], operands[1], resultType);

            case MINUS:
                return MinusFunction.create(operands[0], operands[1], resultType);

            case TIMES:
                return MultiplyFunction.create(operands[0], operands[1], resultType);

            case DIVIDE:
                return DivideFunction.create(operands[0], operands[1], resultType);

            case MINUS_PREFIX:
                return UnaryMinusFunction.create(operands[0], resultType);

            case PLUS_PREFIX:
                return operands[0];

            case FLOOR:
                return FloorCeilFunction.create(operands[0], resultType, false);

            case CEIL:
                return FloorCeilFunction.create(operands[0], resultType, true);

            case EQUALS:
                return ComparisonPredicate.create(operands[0], operands[1], ComparisonMode.EQUALS);

            case NOT_EQUALS:
                return ComparisonPredicate.create(operands[0], operands[1], ComparisonMode.NOT_EQUALS);

            case GREATER_THAN:
                return ComparisonPredicate.create(operands[0], operands[1], ComparisonMode.GREATER_THAN);

            case GREATER_THAN_OR_EQUAL:
                return ComparisonPredicate.create(operands[0], operands[1], ComparisonMode.GREATER_THAN_OR_EQUAL);

            case LESS_THAN:
                return ComparisonPredicate.create(operands[0], operands[1], ComparisonMode.LESS_THAN);

            case LESS_THAN_OR_EQUAL:
                return ComparisonPredicate.create(operands[0], operands[1], ComparisonMode.LESS_THAN_OR_EQUAL);

            case IS_TRUE:
                return IsTruePredicate.create(operands[0]);

            case IS_NOT_TRUE:
                return IsNotTruePredicate.create(operands[0]);

            case IS_FALSE:
                return IsFalsePredicate.create(operands[0]);

            case IS_NOT_FALSE:
                return IsNotFalsePredicate.create(operands[0]);

            case IS_NULL:
                return IsNullPredicate.create(operands[0]);

            case IS_NOT_NULL:
                return IsNotNullPredicate.create(operands[0]);

            case LIKE:
                Expression escape = operands.length == 2 ? null : operands[2];

                return LikeFunction.create(operands[0], operands[1], escape);

            case TRIM:
                assert operands.length == 3;
                assert operands[0] instanceof SymbolExpression;

                SqlTrimFunction.Flag trimFlag = ((SymbolExpression) operands[0]).getSymbol();

                return TrimFunction.create(
                    operands[2],
                    operands[1],
                    trimFlag.getLeft() == 1,
                    trimFlag.getRight() == 1
                );

            case OTHER:
                if (operator == HazelcastSqlOperatorTable.CONCAT) {
                    assert operands.length == 2;

                    return ConcatFunction.create(operands[0], operands[1]);
                }

                break;

            case OTHER_FUNCTION:
                SqlFunction function = (SqlFunction) operator;

                // Math.

                if (function == HazelcastSqlOperatorTable.COS) {
                    return DoubleFunction.create(operands[0], DoubleFunction.COS);
                } else if (function == HazelcastSqlOperatorTable.SIN) {
                    return DoubleFunction.create(operands[0], DoubleFunction.SIN);
                } else if (function == HazelcastSqlOperatorTable.TAN) {
                    return DoubleFunction.create(operands[0], DoubleFunction.TAN);
                } else if (function == HazelcastSqlOperatorTable.COT) {
                    return DoubleFunction.create(operands[0], DoubleFunction.COT);
                } else if (function == HazelcastSqlOperatorTable.ACOS) {
                    return DoubleFunction.create(operands[0], DoubleFunction.ACOS);
                } else if (function == HazelcastSqlOperatorTable.ASIN) {
                    return DoubleFunction.create(operands[0], DoubleFunction.ASIN);
                } else if (function == HazelcastSqlOperatorTable.ATAN) {
                    return DoubleFunction.create(operands[0], DoubleFunction.ATAN);
                } else if (function == HazelcastSqlOperatorTable.EXP) {
                    return DoubleFunction.create(operands[0], DoubleFunction.EXP);
                } else if (function == HazelcastSqlOperatorTable.LN) {
                    return DoubleFunction.create(operands[0], DoubleFunction.LN);
                } else if (function == HazelcastSqlOperatorTable.LOG10) {
                    return DoubleFunction.create(operands[0], DoubleFunction.LOG10);
                } else if (function == HazelcastSqlOperatorTable.RAND) {
                    return RandFunction.create(operands.length == 0 ? null : operands[0]);
                } else if (function == HazelcastSqlOperatorTable.ABS) {
                    return AbsFunction.create(operands[0], resultType);
                } else if (function == SqlStdOperatorTable.PI) {
                    return ConstantExpression.create(Math.PI, resultType);
                } else if (function == HazelcastSqlOperatorTable.SIGN) {
                    return SignFunction.create(operands[0], resultType);
                } else if (function == HazelcastSqlOperatorTable.DEGREES) {
                    return DoubleFunction.create(operands[0], DoubleFunction.DEGREES);
                } else if (function == HazelcastSqlOperatorTable.RADIANS) {
                    return DoubleFunction.create(operands[0], DoubleFunction.RADIANS);
                } else if (function == HazelcastSqlOperatorTable.ROUND) {
                    return RoundTruncateFunction.create(
                        operands[0],
                        operands.length == 1 ? null : operands[1],
                        resultType,
                        false
                    );
                } else if (function == HazelcastSqlOperatorTable.TRUNCATE) {
                    return RoundTruncateFunction.create(
                        operands[0],
                        operands.length == 1 ? null : operands[1],
                        resultType,
                        true
                    );
                }

                // Strings.

                if (function == CHAR_LENGTH || function == CHARACTER_LENGTH || function == LENGTH) {
                    return CharLengthFunction.create(operands[0]);
                } else if (function == HazelcastSqlOperatorTable.UPPER) {
                    return UpperFunction.create(operands[0]);
                } else if (function == HazelcastSqlOperatorTable.LOWER) {
                    return LowerFunction.create(operands[0]);
                } else if (function == HazelcastSqlOperatorTable.INITCAP) {
                    return InitcapFunction.create(operands[0]);
                } else if (function == HazelcastSqlOperatorTable.ASCII) {
                    return AsciiFunction.create(operands[0]);
                } else if (function == HazelcastSqlOperatorTable.SUBSTRING) {
                    Expression input = operands[0];
                    Expression start = operands[1];
                    Expression length = operands.length > 2 ? operands[2] : null;

                    return SubstringFunction.create(input, start, length);
                } else if (function == HazelcastSqlOperatorTable.LTRIM) {
                   return TrimFunction.create(operands[0], null, true, false);
                } else if (function == HazelcastSqlOperatorTable.RTRIM) {
                    return TrimFunction.create(operands[0], null, false, true);
                } else if (function == HazelcastSqlOperatorTable.BTRIM) {
                    return TrimFunction.create(operands[0], null, true, true);
                }

                break;

            default:
                break;
        }

        throw QueryException.error("Unsupported operator: " + operator);
    }

    private static Expression convertBooleanLiteral(RexLiteral literal, SqlTypeName type) {
        assert type == SqlTypeName.BOOLEAN;
        Boolean value = literal.getValueAs(Boolean.class);
        return ConstantExpression.create(value, SqlToQueryType.map(type));
    }

    private static Expression convertNumericLiteral(RexLiteral literal, SqlTypeName type) {
        Object value;
        switch (type) {
            case TINYINT:
                value = literal.getValueAs(Byte.class);
                break;

            case SMALLINT:
                value = literal.getValueAs(Short.class);
                break;

            case INTEGER:
                value = literal.getValueAs(Integer.class);
                break;

            case BIGINT:
                // XXX: Calcite returns unscaled value of the internally stored
                // BigDecimal if a long value is requested on the literal.
                BigDecimal decimalValue = literal.getValueAs(BigDecimal.class);
                value = decimalValue == null ? null : decimalValue.longValue();
                break;

            case DECIMAL:
                value = literal.getValueAs(BigDecimal.class);
                break;

            case REAL:
                value = literal.getValueAs(Float.class);
                break;

            case DOUBLE:
                value = literal.getValueAs(Double.class);
                break;

            default:
                throw new IllegalArgumentException("Unsupported literal type: " + type);
        }

        return ConstantExpression.create(value, SqlToQueryType.map(type));
    }

    private static Expression convertStringLiteral(RexLiteral literal, SqlTypeName type) {
        Object value;
        switch (type) {
            case CHAR:
            case VARCHAR:
                value = literal.getValueAs(String.class);
                break;

            default:
                throw new IllegalArgumentException("Unsupported literal type: " + type);
        }

        return ConstantExpression.create(value, SqlToQueryType.map(type));
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy