Search in sources :

Example 1 with Expressions

use of org.apache.druid.sql.calcite.expression.Expressions in project druid by druid-io.

the class DruidRexExecutor method reduce.

@Override
public void reduce(final RexBuilder rexBuilder, final List<RexNode> constExps, final List<RexNode> reducedValues) {
    for (RexNode constExp : constExps) {
        final DruidExpression druidExpression = Expressions.toDruidExpression(plannerContext, EMPTY_ROW_SIGNATURE, constExp);
        if (druidExpression == null) {
            reducedValues.add(constExp);
        } else {
            final SqlTypeName sqlTypeName = constExp.getType().getSqlTypeName();
            final Expr expr = Parser.parse(druidExpression.getExpression(), plannerContext.getExprMacroTable());
            final ExprEval exprResult = expr.eval(InputBindings.forFunction(name -> {
                // Sanity check. Bindings should not be used for a constant expression.
                throw new UnsupportedOperationException();
            }));
            final RexNode literal;
            if (sqlTypeName == SqlTypeName.BOOLEAN) {
                literal = rexBuilder.makeLiteral(exprResult.asBoolean(), constExp.getType(), true);
            } else if (sqlTypeName == SqlTypeName.DATE) {
                // ExprEval.isNumericNull checks whether the parsed primitive value is null or not.
                if (!constExp.getType().isNullable() && exprResult.isNumericNull()) {
                    throw new UnsupportedSQLQueryException("Illegal DATE constant: %s", constExp);
                }
                literal = rexBuilder.makeDateLiteral(Calcites.jodaToCalciteDateString(DateTimes.utc(exprResult.asLong()), plannerContext.getTimeZone()));
            } else if (sqlTypeName == SqlTypeName.TIMESTAMP) {
                // ExprEval.isNumericNull checks whether the parsed primitive value is null or not.
                if (!constExp.getType().isNullable() && exprResult.isNumericNull()) {
                    throw new UnsupportedSQLQueryException("Illegal TIMESTAMP constant: %s", constExp);
                }
                literal = rexBuilder.makeTimestampLiteral(Calcites.jodaToCalciteTimestampString(DateTimes.utc(exprResult.asLong()), plannerContext.getTimeZone()), RelDataType.PRECISION_NOT_SPECIFIED);
            } else if (SqlTypeName.NUMERIC_TYPES.contains(sqlTypeName)) {
                final BigDecimal bigDecimal;
                if (exprResult.isNumericNull()) {
                    literal = rexBuilder.makeNullLiteral(constExp.getType());
                } else {
                    if (exprResult.type().is(ExprType.LONG)) {
                        bigDecimal = BigDecimal.valueOf(exprResult.asLong());
                    } else {
                        // if exprResult evaluates to Nan or infinity, this will throw a NumberFormatException.
                        // If you find yourself in such a position, consider casting the literal to a BIGINT so that
                        // the query can execute.
                        double exprResultDouble = exprResult.asDouble();
                        if (Double.isNaN(exprResultDouble) || Double.isInfinite(exprResultDouble)) {
                            String expression = druidExpression.getExpression();
                            throw new UnsupportedSQLQueryException("'%s' evaluates to '%s' that is not supported in SQL. You can either cast the expression as bigint ('cast(%s as bigint)') or char ('cast(%s as char)') or change the expression itself", expression, Double.toString(exprResultDouble), expression, expression);
                        }
                        bigDecimal = BigDecimal.valueOf(exprResult.asDouble());
                    }
                    literal = rexBuilder.makeLiteral(bigDecimal, constExp.getType(), true);
                }
            } else if (sqlTypeName == SqlTypeName.ARRAY) {
                assert exprResult.isArray();
                if (SqlTypeName.NUMERIC_TYPES.contains(constExp.getType().getComponentType().getSqlTypeName())) {
                    if (exprResult.type().getElementType().is(ExprType.LONG)) {
                        List<BigDecimal> resultAsBigDecimalList = Arrays.stream(exprResult.asLongArray()).map(BigDecimal::valueOf).collect(Collectors.toList());
                        literal = rexBuilder.makeLiteral(resultAsBigDecimalList, constExp.getType(), true);
                    } else {
                        List<BigDecimal> resultAsBigDecimalList = Arrays.stream(exprResult.asDoubleArray()).map(doubleVal -> {
                            if (Double.isNaN(doubleVal) || Double.isInfinite(doubleVal)) {
                                String expression = druidExpression.getExpression();
                                throw new UnsupportedSQLQueryException("'%s' contains an element that evaluates to '%s' which is not supported in SQL. You can either cast the element in the array to bigint or char or change the expression itself", expression, Double.toString(doubleVal));
                            }
                            return BigDecimal.valueOf(doubleVal);
                        }).collect(Collectors.toList());
                        literal = rexBuilder.makeLiteral(resultAsBigDecimalList, constExp.getType(), true);
                    }
                } else {
                    literal = rexBuilder.makeLiteral(Arrays.asList(exprResult.asArray()), constExp.getType(), true);
                }
            } else if (sqlTypeName == SqlTypeName.OTHER && constExp.getType() instanceof RowSignatures.ComplexSqlType) {
                // complex constant is not reducible, so just leave it as an expression
                literal = constExp;
            } else {
                if (exprResult.isArray()) {
                    // just leave array expressions on multi-value strings alone, we're going to push them down into a virtual
                    // column selector anyway
                    literal = constExp;
                } else {
                    literal = rexBuilder.makeLiteral(exprResult.value(), constExp.getType(), true);
                }
            }
            reducedValues.add(literal);
        }
    }
}
Also used : RelDataType(org.apache.calcite.rel.type.RelDataType) DateTimes(org.apache.druid.java.util.common.DateTimes) InputBindings(org.apache.druid.math.expr.InputBindings) Arrays(java.util.Arrays) SqlTypeName(org.apache.calcite.sql.type.SqlTypeName) RexBuilder(org.apache.calcite.rex.RexBuilder) Parser(org.apache.druid.math.expr.Parser) ExprType(org.apache.druid.math.expr.ExprType) RowSignatures(org.apache.druid.sql.calcite.table.RowSignatures) Collectors(java.util.stream.Collectors) ExprEval(org.apache.druid.math.expr.ExprEval) DruidExpression(org.apache.druid.sql.calcite.expression.DruidExpression) BigDecimal(java.math.BigDecimal) List(java.util.List) RexNode(org.apache.calcite.rex.RexNode) RowSignature(org.apache.druid.segment.column.RowSignature) Expr(org.apache.druid.math.expr.Expr) RexExecutor(org.apache.calcite.rex.RexExecutor) Expressions(org.apache.druid.sql.calcite.expression.Expressions) ExprEval(org.apache.druid.math.expr.ExprEval) DruidExpression(org.apache.druid.sql.calcite.expression.DruidExpression) SqlTypeName(org.apache.calcite.sql.type.SqlTypeName) Expr(org.apache.druid.math.expr.Expr) BigDecimal(java.math.BigDecimal) RexNode(org.apache.calcite.rex.RexNode)

Aggregations

BigDecimal (java.math.BigDecimal)1 Arrays (java.util.Arrays)1 List (java.util.List)1 Collectors (java.util.stream.Collectors)1 RelDataType (org.apache.calcite.rel.type.RelDataType)1 RexBuilder (org.apache.calcite.rex.RexBuilder)1 RexExecutor (org.apache.calcite.rex.RexExecutor)1 RexNode (org.apache.calcite.rex.RexNode)1 SqlTypeName (org.apache.calcite.sql.type.SqlTypeName)1 DateTimes (org.apache.druid.java.util.common.DateTimes)1 Expr (org.apache.druid.math.expr.Expr)1 ExprEval (org.apache.druid.math.expr.ExprEval)1 ExprType (org.apache.druid.math.expr.ExprType)1 InputBindings (org.apache.druid.math.expr.InputBindings)1 Parser (org.apache.druid.math.expr.Parser)1 RowSignature (org.apache.druid.segment.column.RowSignature)1 DruidExpression (org.apache.druid.sql.calcite.expression.DruidExpression)1 Expressions (org.apache.druid.sql.calcite.expression.Expressions)1 RowSignatures (org.apache.druid.sql.calcite.table.RowSignatures)1