use of org.apache.druid.math.expr.ExprEval in project druid by druid-io.
the class IPv4AddressParseExprMacroTest method eval.
private Object eval(Expr arg) {
Expr expr = apply(Collections.singletonList(arg));
ExprEval eval = expr.eval(InputBindings.nilBindings());
return eval.value();
}
use of org.apache.druid.math.expr.ExprEval 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.ExprEval in project druid by druid-io.
the class IPv4AddressMatchExprMacro method apply.
@Override
public Expr apply(final List<Expr> args) {
if (args.size() != 2) {
throw new IAE(ExprUtils.createErrMsg(name(), "must have 2 arguments"));
}
SubnetUtils.SubnetInfo subnetInfo = getSubnetInfo(args);
Expr arg = args.get(0);
class IPv4AddressMatchExpr extends ExprMacroTable.BaseScalarUnivariateMacroFunctionExpr {
private final SubnetUtils.SubnetInfo subnetInfo;
private IPv4AddressMatchExpr(Expr arg, SubnetUtils.SubnetInfo subnetInfo) {
super(FN_NAME, arg);
this.subnetInfo = subnetInfo;
}
@Nonnull
@Override
public ExprEval eval(final ObjectBinding bindings) {
ExprEval eval = arg.eval(bindings);
boolean match;
switch(eval.type().getType()) {
case STRING:
match = isStringMatch(eval.asString());
break;
case LONG:
match = !eval.isNumericNull() && isLongMatch(eval.asLong());
break;
default:
match = false;
}
return ExprEval.ofLongBoolean(match);
}
private boolean isStringMatch(String stringValue) {
return IPv4AddressExprUtils.isValidAddress(stringValue) && subnetInfo.isInRange(stringValue);
}
private boolean isLongMatch(long longValue) {
return !IPv4AddressExprUtils.overflowsUnsignedInt(longValue) && subnetInfo.isInRange((int) longValue);
}
@Override
public Expr visit(Shuttle shuttle) {
return shuttle.visit(apply(shuttle.visitAll(args)));
}
@Nullable
@Override
public ExpressionType getOutputType(InputBindingInspector inspector) {
return ExpressionType.LONG;
}
@Override
public String stringify() {
return StringUtils.format("%s(%s, %s)", FN_NAME, arg.stringify(), args.get(ARG_SUBNET).stringify());
}
}
return new IPv4AddressMatchExpr(arg, subnetInfo);
}
use of org.apache.druid.math.expr.ExprEval in project druid by druid-io.
the class BloomFilterExpressionsTest method testAddLong.
@Test
public void testAddLong() {
Expr expr = Parser.parse("bloom_filter_add(1234, bloomy)", macroTable);
ExprEval eval = expr.eval(inputBindings);
Assert.assertEquals(BloomFilterExpressions.BLOOM_FILTER_TYPE, eval.type());
Assert.assertTrue(eval.value() instanceof BloomKFilter);
Assert.assertTrue(((BloomKFilter) eval.value()).testLong(SOME_LONG));
expr = Parser.parse("bloom_filter_add(long, bloomy)", macroTable);
eval = expr.eval(inputBindings);
Assert.assertEquals(BloomFilterExpressions.BLOOM_FILTER_TYPE, eval.type());
Assert.assertTrue(eval.value() instanceof BloomKFilter);
Assert.assertTrue(((BloomKFilter) eval.value()).testLong(SOME_LONG));
}
use of org.apache.druid.math.expr.ExprEval in project druid by druid-io.
the class BloomFilterExpressionsTest method testCreate.
@Test
public void testCreate() {
Expr expr = Parser.parse("bloom_filter(100)", macroTable);
ExprEval eval = expr.eval(inputBindings);
Assert.assertEquals(BloomFilterExpressions.BLOOM_FILTER_TYPE, eval.type());
Assert.assertTrue(eval.value() instanceof BloomKFilter);
Assert.assertEquals(1024, ((BloomKFilter) eval.value()).getBitSize());
}
Aggregations