Search in sources :

Example 36 with Expr

use of org.apache.druid.math.expr.Expr 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)

Example 37 with Expr

use of org.apache.druid.math.expr.Expr in project druid by druid-io.

the class TruncateOperatorConversion method toDruidExpression.

@Override
public DruidExpression toDruidExpression(final PlannerContext plannerContext, final RowSignature rowSignature, final RexNode rexNode) {
    return OperatorConversions.convertCallBuilder(plannerContext, rowSignature, rexNode, inputExpressions -> {
        final DruidExpression arg = inputExpressions.get(0);
        final Expr digitsExpr = inputExpressions.size() > 1 ? inputExpressions.get(1).parse(plannerContext.getExprMacroTable()) : null;
        final String factorString;
        if (digitsExpr == null) {
            factorString = "1";
        } else if (digitsExpr.isLiteral()) {
            final int digits = ((Number) digitsExpr.getLiteralValue()).intValue();
            final double factor = Math.pow(10, digits);
            factorString = DruidExpression.numberLiteral(factor);
        } else {
            factorString = StringUtils.format("pow(10,%s)", inputExpressions.get(1));
        }
        return StringUtils.format("(cast(cast(%s * %s,'long'),'double') / %s)", arg.getExpression(), factorString, factorString);
    });
}
Also used : DruidExpression(org.apache.druid.sql.calcite.expression.DruidExpression) Expr(org.apache.druid.math.expr.Expr)

Example 38 with Expr

use of org.apache.druid.math.expr.Expr in project druid by druid-io.

the class ArrayContainsOperatorConversion method toDruidFilter.

@Nullable
@Override
public DimFilter toDruidFilter(final PlannerContext plannerContext, RowSignature rowSignature, @Nullable VirtualColumnRegistry virtualColumnRegistry, final RexNode rexNode) {
    final List<RexNode> operands = ((RexCall) rexNode).getOperands();
    final List<DruidExpression> druidExpressions = Expressions.toDruidExpressions(plannerContext, rowSignature, operands);
    if (druidExpressions == null) {
        return null;
    }
    // Converts array_contains() function into an AND of Selector filters if possible.
    final DruidExpression leftExpr = druidExpressions.get(0);
    final DruidExpression rightExpr = druidExpressions.get(1);
    if (leftExpr.isSimpleExtraction()) {
        Expr expr = Parser.parse(rightExpr.getExpression(), plannerContext.getExprMacroTable());
        // different package.
        if (expr.isLiteral()) {
            // Evaluate the expression to get out the array elements.
            // We can safely pass a noop ObjectBinding if the expression is literal.
            ExprEval<?> exprEval = expr.eval(InputBindings.nilBindings());
            String[] arrayElements = exprEval.asStringArray();
            if (arrayElements == null || arrayElements.length == 0) {
                // to create an empty array with no argument, we just return null.
                return null;
            } else if (arrayElements.length == 1) {
                return newSelectorDimFilter(leftExpr.getSimpleExtraction(), arrayElements[0]);
            } else {
                final List<DimFilter> selectFilters = Arrays.stream(arrayElements).map(val -> newSelectorDimFilter(leftExpr.getSimpleExtraction(), val)).collect(Collectors.toList());
                return new AndDimFilter(selectFilters);
            }
        }
    }
    return toExpressionFilter(plannerContext, getDruidFunctionName(), druidExpressions);
}
Also used : RexCall(org.apache.calcite.rex.RexCall) DruidExpression(org.apache.druid.sql.calcite.expression.DruidExpression) Expr(org.apache.druid.math.expr.Expr) AndDimFilter(org.apache.druid.query.filter.AndDimFilter) List(java.util.List) RexNode(org.apache.calcite.rex.RexNode) Nullable(javax.annotation.Nullable)

Example 39 with Expr

use of org.apache.druid.math.expr.Expr in project druid by druid-io.

the class ArrayOverlapOperatorConversion method toDruidFilter.

@Nullable
@Override
public DimFilter toDruidFilter(final PlannerContext plannerContext, RowSignature rowSignature, @Nullable VirtualColumnRegistry virtualColumnRegistry, final RexNode rexNode) {
    final List<RexNode> operands = ((RexCall) rexNode).getOperands();
    final List<DruidExpression> druidExpressions = Expressions.toDruidExpressions(plannerContext, rowSignature, operands);
    if (druidExpressions == null) {
        return null;
    }
    // Converts array_overlaps() function into an OR of Selector filters if possible.
    final boolean leftSimpleExtractionExpr = druidExpressions.get(0).isSimpleExtraction();
    final boolean rightSimpleExtractionExpr = druidExpressions.get(1).isSimpleExtraction();
    final DruidExpression simpleExtractionExpr;
    final DruidExpression complexExpr;
    if (leftSimpleExtractionExpr ^ rightSimpleExtractionExpr) {
        if (leftSimpleExtractionExpr) {
            simpleExtractionExpr = druidExpressions.get(0);
            complexExpr = druidExpressions.get(1);
        } else {
            simpleExtractionExpr = druidExpressions.get(1);
            complexExpr = druidExpressions.get(0);
        }
    } else {
        return toExpressionFilter(plannerContext, getDruidFunctionName(), druidExpressions);
    }
    Expr expr = Parser.parse(complexExpr.getExpression(), plannerContext.getExprMacroTable());
    if (expr.isLiteral()) {
        // Evaluate the expression to take out the array elements.
        // We can safely pass null if the expression is literal.
        ExprEval<?> exprEval = expr.eval(InputBindings.nilBindings());
        String[] arrayElements = exprEval.asStringArray();
        if (arrayElements == null || arrayElements.length == 0) {
            // to create an empty array with no argument, we just return null.
            return null;
        } else if (arrayElements.length == 1) {
            return newSelectorDimFilter(simpleExtractionExpr.getSimpleExtraction(), arrayElements[0]);
        } else {
            return new InDimFilter(simpleExtractionExpr.getSimpleExtraction().getColumn(), Sets.newHashSet(arrayElements), simpleExtractionExpr.getSimpleExtraction().getExtractionFn(), null);
        }
    }
    return toExpressionFilter(plannerContext, getDruidFunctionName(), druidExpressions);
}
Also used : RexCall(org.apache.calcite.rex.RexCall) DruidExpression(org.apache.druid.sql.calcite.expression.DruidExpression) Expr(org.apache.druid.math.expr.Expr) InDimFilter(org.apache.druid.query.filter.InDimFilter) RexNode(org.apache.calcite.rex.RexNode) Nullable(javax.annotation.Nullable)

Example 40 with Expr

use of org.apache.druid.math.expr.Expr in project druid by druid-io.

the class RegexpExtractOperatorConversion method toDruidExpression.

@Override
public DruidExpression toDruidExpression(final PlannerContext plannerContext, final RowSignature rowSignature, final RexNode rexNode) {
    return OperatorConversions.convertDirectCallWithExtraction(plannerContext, rowSignature, rexNode, StringUtils.toLowerCase(calciteOperator().getName()), inputExpressions -> {
        final DruidExpression arg = inputExpressions.get(0);
        final Expr patternExpr = inputExpressions.get(1).parse(plannerContext.getExprMacroTable());
        final Expr indexExpr = inputExpressions.size() > 2 ? inputExpressions.get(2).parse(plannerContext.getExprMacroTable()) : null;
        if (arg.isSimpleExtraction() && patternExpr.isLiteral() && (indexExpr == null || indexExpr.isLiteral())) {
            final String pattern = (String) patternExpr.getLiteralValue();
            return arg.getSimpleExtraction().cascade(new RegexDimExtractionFn(// non-SQL-compliant null handling mode).
            StringUtils.nullToEmptyNonDruidDataString(pattern), indexExpr == null ? DEFAULT_INDEX : ((Number) indexExpr.getLiteralValue()).intValue(), true, null));
        } else {
            return null;
        }
    });
}
Also used : DruidExpression(org.apache.druid.sql.calcite.expression.DruidExpression) Expr(org.apache.druid.math.expr.Expr) RegexDimExtractionFn(org.apache.druid.query.extraction.RegexDimExtractionFn)

Aggregations

Expr (org.apache.druid.math.expr.Expr)104 Test (org.junit.Test)58 ExprEval (org.apache.druid.math.expr.ExprEval)18 InitializedNullHandlingTest (org.apache.druid.testing.InitializedNullHandlingTest)17 IAE (org.apache.druid.java.util.common.IAE)14 ExpressionType (org.apache.druid.math.expr.ExpressionType)8 DruidExpression (org.apache.druid.sql.calcite.expression.DruidExpression)7 ArrayList (java.util.ArrayList)6 Nullable (javax.annotation.Nullable)6 HashSet (java.util.HashSet)5 List (java.util.List)4 HyperLogLogCollector (org.apache.druid.hll.HyperLogLogCollector)4 BloomKFilter (org.apache.druid.query.filter.BloomKFilter)4 InDimFilter (org.apache.druid.query.filter.InDimFilter)4 RexNode (org.apache.calcite.rex.RexNode)3 Filter (org.apache.druid.query.filter.Filter)3 VirtualColumn (org.apache.druid.segment.VirtualColumn)3 FalseFilter (org.apache.druid.segment.filter.FalseFilter)3 OrFilter (org.apache.druid.segment.filter.OrFilter)3 SelectorFilter (org.apache.druid.segment.filter.SelectorFilter)3