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);
}
}
}
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);
});
}
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);
}
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);
}
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;
}
});
}
Aggregations