mirror of https://github.com/apache/druid.git
split up Expr.java (#10333)
This commit is contained in:
parent
8ab1979304
commit
475d86a4f7
|
@ -0,0 +1,266 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.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 org.apache.druid.math.expr;
|
||||||
|
|
||||||
|
import org.apache.druid.java.util.common.guava.Comparators;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
// logical operators live here
|
||||||
|
|
||||||
|
class BinLtExpr extends BinaryEvalOpExprBase
|
||||||
|
{
|
||||||
|
BinLtExpr(String op, Expr left, Expr right)
|
||||||
|
{
|
||||||
|
super(op, left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected BinaryOpExprBase copy(Expr left, Expr right)
|
||||||
|
{
|
||||||
|
return new BinLtExpr(op, left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ExprEval evalString(@Nullable String left, @Nullable String right)
|
||||||
|
{
|
||||||
|
return ExprEval.of(Comparators.<String>naturalNullsFirst().compare(left, right) < 0, ExprType.LONG);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final long evalLong(long left, long right)
|
||||||
|
{
|
||||||
|
return Evals.asLong(left < right);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final double evalDouble(double left, double right)
|
||||||
|
{
|
||||||
|
// Use Double.compare for more consistent NaN handling.
|
||||||
|
return Evals.asDouble(Double.compare(left, right) < 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BinLeqExpr extends BinaryEvalOpExprBase
|
||||||
|
{
|
||||||
|
BinLeqExpr(String op, Expr left, Expr right)
|
||||||
|
{
|
||||||
|
super(op, left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected BinaryOpExprBase copy(Expr left, Expr right)
|
||||||
|
{
|
||||||
|
return new BinLeqExpr(op, left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ExprEval evalString(@Nullable String left, @Nullable String right)
|
||||||
|
{
|
||||||
|
return ExprEval.of(Comparators.<String>naturalNullsFirst().compare(left, right) <= 0, ExprType.LONG);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final long evalLong(long left, long right)
|
||||||
|
{
|
||||||
|
return Evals.asLong(left <= right);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final double evalDouble(double left, double right)
|
||||||
|
{
|
||||||
|
// Use Double.compare for more consistent NaN handling.
|
||||||
|
return Evals.asDouble(Double.compare(left, right) <= 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BinGtExpr extends BinaryEvalOpExprBase
|
||||||
|
{
|
||||||
|
BinGtExpr(String op, Expr left, Expr right)
|
||||||
|
{
|
||||||
|
super(op, left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected BinaryOpExprBase copy(Expr left, Expr right)
|
||||||
|
{
|
||||||
|
return new BinGtExpr(op, left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ExprEval evalString(@Nullable String left, @Nullable String right)
|
||||||
|
{
|
||||||
|
return ExprEval.of(Comparators.<String>naturalNullsFirst().compare(left, right) > 0, ExprType.LONG);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final long evalLong(long left, long right)
|
||||||
|
{
|
||||||
|
return Evals.asLong(left > right);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final double evalDouble(double left, double right)
|
||||||
|
{
|
||||||
|
// Use Double.compare for more consistent NaN handling.
|
||||||
|
return Evals.asDouble(Double.compare(left, right) > 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BinGeqExpr extends BinaryEvalOpExprBase
|
||||||
|
{
|
||||||
|
BinGeqExpr(String op, Expr left, Expr right)
|
||||||
|
{
|
||||||
|
super(op, left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected BinaryOpExprBase copy(Expr left, Expr right)
|
||||||
|
{
|
||||||
|
return new BinGeqExpr(op, left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ExprEval evalString(@Nullable String left, @Nullable String right)
|
||||||
|
{
|
||||||
|
return ExprEval.of(Comparators.<String>naturalNullsFirst().compare(left, right) >= 0, ExprType.LONG);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final long evalLong(long left, long right)
|
||||||
|
{
|
||||||
|
return Evals.asLong(left >= right);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final double evalDouble(double left, double right)
|
||||||
|
{
|
||||||
|
// Use Double.compare for more consistent NaN handling.
|
||||||
|
return Evals.asDouble(Double.compare(left, right) >= 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BinEqExpr extends BinaryEvalOpExprBase
|
||||||
|
{
|
||||||
|
BinEqExpr(String op, Expr left, Expr right)
|
||||||
|
{
|
||||||
|
super(op, left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected BinaryOpExprBase copy(Expr left, Expr right)
|
||||||
|
{
|
||||||
|
return new BinEqExpr(op, left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ExprEval evalString(@Nullable String left, @Nullable String right)
|
||||||
|
{
|
||||||
|
return ExprEval.of(Objects.equals(left, right), ExprType.LONG);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final long evalLong(long left, long right)
|
||||||
|
{
|
||||||
|
return Evals.asLong(left == right);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final double evalDouble(double left, double right)
|
||||||
|
{
|
||||||
|
return Evals.asDouble(left == right);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BinNeqExpr extends BinaryEvalOpExprBase
|
||||||
|
{
|
||||||
|
BinNeqExpr(String op, Expr left, Expr right)
|
||||||
|
{
|
||||||
|
super(op, left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected BinaryOpExprBase copy(Expr left, Expr right)
|
||||||
|
{
|
||||||
|
return new BinNeqExpr(op, left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ExprEval evalString(@Nullable String left, @Nullable String right)
|
||||||
|
{
|
||||||
|
return ExprEval.of(!Objects.equals(left, right), ExprType.LONG);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final long evalLong(long left, long right)
|
||||||
|
{
|
||||||
|
return Evals.asLong(left != right);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final double evalDouble(double left, double right)
|
||||||
|
{
|
||||||
|
return Evals.asDouble(left != right);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BinAndExpr extends BinaryOpExprBase
|
||||||
|
{
|
||||||
|
BinAndExpr(String op, Expr left, Expr right)
|
||||||
|
{
|
||||||
|
super(op, left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected BinaryOpExprBase copy(Expr left, Expr right)
|
||||||
|
{
|
||||||
|
return new BinAndExpr(op, left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExprEval eval(ObjectBinding bindings)
|
||||||
|
{
|
||||||
|
ExprEval leftVal = left.eval(bindings);
|
||||||
|
return leftVal.asBoolean() ? right.eval(bindings) : leftVal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BinOrExpr extends BinaryOpExprBase
|
||||||
|
{
|
||||||
|
BinOrExpr(String op, Expr left, Expr right)
|
||||||
|
{
|
||||||
|
super(op, left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected BinaryOpExprBase copy(Expr left, Expr right)
|
||||||
|
{
|
||||||
|
return new BinOrExpr(op, left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExprEval eval(ObjectBinding bindings)
|
||||||
|
{
|
||||||
|
ExprEval leftVal = left.eval(bindings);
|
||||||
|
return leftVal.asBoolean() ? leftVal : right.eval(bindings);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,191 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.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 org.apache.druid.math.expr;
|
||||||
|
|
||||||
|
import com.google.common.math.LongMath;
|
||||||
|
import com.google.common.primitives.Ints;
|
||||||
|
import org.apache.druid.common.config.NullHandling;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
// math operators live here
|
||||||
|
|
||||||
|
class BinPlusExpr extends BinaryEvalOpExprBase
|
||||||
|
{
|
||||||
|
BinPlusExpr(String op, Expr left, Expr right)
|
||||||
|
{
|
||||||
|
super(op, left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected BinaryOpExprBase copy(Expr left, Expr right)
|
||||||
|
{
|
||||||
|
return new BinPlusExpr(op, left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ExprEval evalString(@Nullable String left, @Nullable String right)
|
||||||
|
{
|
||||||
|
return ExprEval.of(NullHandling.nullToEmptyIfNeeded(left)
|
||||||
|
+ NullHandling.nullToEmptyIfNeeded(right));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final long evalLong(long left, long right)
|
||||||
|
{
|
||||||
|
return left + right;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final double evalDouble(double left, double right)
|
||||||
|
{
|
||||||
|
return left + right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BinMinusExpr extends BinaryEvalOpExprBase
|
||||||
|
{
|
||||||
|
BinMinusExpr(String op, Expr left, Expr right)
|
||||||
|
{
|
||||||
|
super(op, left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected BinaryOpExprBase copy(Expr left, Expr right)
|
||||||
|
{
|
||||||
|
return new BinMinusExpr(op, left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final long evalLong(long left, long right)
|
||||||
|
{
|
||||||
|
return left - right;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final double evalDouble(double left, double right)
|
||||||
|
{
|
||||||
|
return left - right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BinMulExpr extends BinaryEvalOpExprBase
|
||||||
|
{
|
||||||
|
BinMulExpr(String op, Expr left, Expr right)
|
||||||
|
{
|
||||||
|
super(op, left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected BinaryOpExprBase copy(Expr left, Expr right)
|
||||||
|
{
|
||||||
|
return new BinMulExpr(op, left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final long evalLong(long left, long right)
|
||||||
|
{
|
||||||
|
return left * right;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final double evalDouble(double left, double right)
|
||||||
|
{
|
||||||
|
return left * right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BinDivExpr extends BinaryEvalOpExprBase
|
||||||
|
{
|
||||||
|
BinDivExpr(String op, Expr left, Expr right)
|
||||||
|
{
|
||||||
|
super(op, left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected BinaryOpExprBase copy(Expr left, Expr right)
|
||||||
|
{
|
||||||
|
return new BinDivExpr(op, left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final long evalLong(long left, long right)
|
||||||
|
{
|
||||||
|
return left / right;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final double evalDouble(double left, double right)
|
||||||
|
{
|
||||||
|
return left / right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BinPowExpr extends BinaryEvalOpExprBase
|
||||||
|
{
|
||||||
|
BinPowExpr(String op, Expr left, Expr right)
|
||||||
|
{
|
||||||
|
super(op, left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected BinaryOpExprBase copy(Expr left, Expr right)
|
||||||
|
{
|
||||||
|
return new BinPowExpr(op, left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final long evalLong(long left, long right)
|
||||||
|
{
|
||||||
|
return LongMath.pow(left, Ints.checkedCast(right));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final double evalDouble(double left, double right)
|
||||||
|
{
|
||||||
|
return Math.pow(left, right);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BinModuloExpr extends BinaryEvalOpExprBase
|
||||||
|
{
|
||||||
|
BinModuloExpr(String op, Expr left, Expr right)
|
||||||
|
{
|
||||||
|
super(op, left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected BinaryOpExprBase copy(Expr left, Expr right)
|
||||||
|
{
|
||||||
|
return new BinModuloExpr(op, left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final long evalLong(long left, long right)
|
||||||
|
{
|
||||||
|
return left % right;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected final double evalDouble(double left, double right)
|
||||||
|
{
|
||||||
|
return left % right;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,158 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.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 org.apache.druid.math.expr;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import org.apache.druid.common.config.NullHandling;
|
||||||
|
import org.apache.druid.java.util.common.StringUtils;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base type for all binary operators, this {@link Expr} has two children {@link Expr} for the left and right side
|
||||||
|
* operands.
|
||||||
|
*
|
||||||
|
* Note: all concrete subclass of this should have constructor with the form of <init>(String, Expr, Expr)
|
||||||
|
* if it's not possible, just be sure Evals.binaryOp() can handle that
|
||||||
|
*/
|
||||||
|
abstract class BinaryOpExprBase implements Expr
|
||||||
|
{
|
||||||
|
protected final String op;
|
||||||
|
protected final Expr left;
|
||||||
|
protected final Expr right;
|
||||||
|
|
||||||
|
BinaryOpExprBase(String op, Expr left, Expr right)
|
||||||
|
{
|
||||||
|
this.op = op;
|
||||||
|
this.left = left;
|
||||||
|
this.right = right;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(Visitor visitor)
|
||||||
|
{
|
||||||
|
left.visit(visitor);
|
||||||
|
right.visit(visitor);
|
||||||
|
visitor.visit(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Expr visit(Shuttle shuttle)
|
||||||
|
{
|
||||||
|
Expr newLeft = left.visit(shuttle);
|
||||||
|
Expr newRight = right.visit(shuttle);
|
||||||
|
//noinspection ObjectEquality (checking for object equality here is intentional)
|
||||||
|
if (left != newLeft || right != newRight) {
|
||||||
|
return shuttle.visit(copy(newLeft, newRight));
|
||||||
|
}
|
||||||
|
return shuttle.visit(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return StringUtils.format("(%s %s %s)", op, left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String stringify()
|
||||||
|
{
|
||||||
|
return StringUtils.format("(%s %s %s)", left.stringify(), op, right.stringify());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract BinaryOpExprBase copy(Expr left, Expr right);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BindingDetails analyzeInputs()
|
||||||
|
{
|
||||||
|
// currently all binary operators operate on scalar inputs
|
||||||
|
return left.analyzeInputs().with(right).withScalarArguments(ImmutableSet.of(left, right));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o)
|
||||||
|
{
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
BinaryOpExprBase that = (BinaryOpExprBase) o;
|
||||||
|
return Objects.equals(op, that.op) &&
|
||||||
|
Objects.equals(left, that.left) &&
|
||||||
|
Objects.equals(right, that.right);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode()
|
||||||
|
{
|
||||||
|
return Objects.hash(op, left, right);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for numerical binary operators, with additional methods defined to evaluate primitive values directly
|
||||||
|
* instead of wrapped with {@link ExprEval}
|
||||||
|
*/
|
||||||
|
abstract class BinaryEvalOpExprBase extends BinaryOpExprBase
|
||||||
|
{
|
||||||
|
BinaryEvalOpExprBase(String op, Expr left, Expr right)
|
||||||
|
{
|
||||||
|
super(op, left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExprEval eval(ObjectBinding bindings)
|
||||||
|
{
|
||||||
|
ExprEval leftVal = left.eval(bindings);
|
||||||
|
ExprEval rightVal = right.eval(bindings);
|
||||||
|
|
||||||
|
// Result of any Binary expressions is null if any of the argument is null.
|
||||||
|
// e.g "select null * 2 as c;" or "select null + 1 as c;" will return null as per Standard SQL spec.
|
||||||
|
if (NullHandling.sqlCompatible() && (leftVal.value() == null || rightVal.value() == null)) {
|
||||||
|
return ExprEval.of(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (leftVal.type() == ExprType.STRING && rightVal.type() == ExprType.STRING) {
|
||||||
|
return evalString(leftVal.asString(), rightVal.asString());
|
||||||
|
} else if (leftVal.type() == ExprType.LONG && rightVal.type() == ExprType.LONG) {
|
||||||
|
if (NullHandling.sqlCompatible() && (leftVal.isNumericNull() || rightVal.isNumericNull())) {
|
||||||
|
return ExprEval.of(null);
|
||||||
|
}
|
||||||
|
return ExprEval.of(evalLong(leftVal.asLong(), rightVal.asLong()));
|
||||||
|
} else {
|
||||||
|
if (NullHandling.sqlCompatible() && (leftVal.isNumericNull() || rightVal.isNumericNull())) {
|
||||||
|
return ExprEval.of(null);
|
||||||
|
}
|
||||||
|
return ExprEval.of(evalDouble(leftVal.asDouble(), rightVal.asDouble()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ExprEval evalString(@Nullable String left, @Nullable String right)
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException("unsupported type " + ExprType.STRING);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract long evalLong(long left, long right);
|
||||||
|
|
||||||
|
protected abstract double evalDouble(double left, double right);
|
||||||
|
}
|
|
@ -0,0 +1,457 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.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 org.apache.druid.math.expr;
|
||||||
|
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
import org.apache.commons.lang.StringEscapeUtils;
|
||||||
|
import org.apache.druid.common.config.NullHandling;
|
||||||
|
import org.apache.druid.java.util.common.StringUtils;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base type for all constant expressions. {@link ConstantExpr} allow for direct value extraction without evaluating
|
||||||
|
* {@link Expr.ObjectBinding}. {@link ConstantExpr} are terminal nodes of an expression tree, and have no children
|
||||||
|
* {@link Expr}.
|
||||||
|
*/
|
||||||
|
abstract class ConstantExpr implements Expr
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public boolean isLiteral()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(Visitor visitor)
|
||||||
|
{
|
||||||
|
visitor.visit(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Expr visit(Shuttle shuttle)
|
||||||
|
{
|
||||||
|
return shuttle.visit(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BindingDetails analyzeInputs()
|
||||||
|
{
|
||||||
|
return new BindingDetails();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String stringify()
|
||||||
|
{
|
||||||
|
return toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for typed 'null' value constants (or default value, depending on {@link NullHandling#sqlCompatible})
|
||||||
|
*/
|
||||||
|
abstract class NullNumericConstantExpr extends ConstantExpr
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public Object getLiteralValue()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return NULL_LITERAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LongExpr extends ConstantExpr
|
||||||
|
{
|
||||||
|
private final Long value;
|
||||||
|
|
||||||
|
LongExpr(Long value)
|
||||||
|
{
|
||||||
|
this.value = Preconditions.checkNotNull(value, "value");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getLiteralValue()
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return String.valueOf(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExprEval eval(ObjectBinding bindings)
|
||||||
|
{
|
||||||
|
return ExprEval.ofLong(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o)
|
||||||
|
{
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
LongExpr longExpr = (LongExpr) o;
|
||||||
|
return Objects.equals(value, longExpr.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode()
|
||||||
|
{
|
||||||
|
return Objects.hash(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class NullLongExpr extends NullNumericConstantExpr
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public ExprEval eval(ObjectBinding bindings)
|
||||||
|
{
|
||||||
|
return ExprEval.ofLong(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final int hashCode()
|
||||||
|
{
|
||||||
|
return NullLongExpr.class.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean equals(Object obj)
|
||||||
|
{
|
||||||
|
return obj instanceof NullLongExpr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LongArrayExpr extends ConstantExpr
|
||||||
|
{
|
||||||
|
private final Long[] value;
|
||||||
|
|
||||||
|
LongArrayExpr(Long[] value)
|
||||||
|
{
|
||||||
|
this.value = Preconditions.checkNotNull(value, "value");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getLiteralValue()
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return Arrays.toString(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExprEval eval(ObjectBinding bindings)
|
||||||
|
{
|
||||||
|
return ExprEval.ofLongArray(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String stringify()
|
||||||
|
{
|
||||||
|
if (value.length == 0) {
|
||||||
|
return "<LONG>[]";
|
||||||
|
}
|
||||||
|
return StringUtils.format("<LONG>%s", toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o)
|
||||||
|
{
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
LongArrayExpr that = (LongArrayExpr) o;
|
||||||
|
return Arrays.equals(value, that.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode()
|
||||||
|
{
|
||||||
|
return Arrays.hashCode(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class StringExpr extends ConstantExpr
|
||||||
|
{
|
||||||
|
@Nullable
|
||||||
|
private final String value;
|
||||||
|
|
||||||
|
StringExpr(@Nullable String value)
|
||||||
|
{
|
||||||
|
this.value = NullHandling.emptyToNullIfNeeded(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Object getLiteralValue()
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExprEval eval(ObjectBinding bindings)
|
||||||
|
{
|
||||||
|
return ExprEval.of(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String stringify()
|
||||||
|
{
|
||||||
|
// escape as javascript string since string literals are wrapped in single quotes
|
||||||
|
return value == null ? NULL_LITERAL : StringUtils.format("'%s'", StringEscapeUtils.escapeJavaScript(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o)
|
||||||
|
{
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
StringExpr that = (StringExpr) o;
|
||||||
|
return Objects.equals(value, that.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode()
|
||||||
|
{
|
||||||
|
return Objects.hash(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class StringArrayExpr extends ConstantExpr
|
||||||
|
{
|
||||||
|
private final String[] value;
|
||||||
|
|
||||||
|
StringArrayExpr(String[] value)
|
||||||
|
{
|
||||||
|
this.value = Preconditions.checkNotNull(value, "value");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getLiteralValue()
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return Arrays.toString(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExprEval eval(ObjectBinding bindings)
|
||||||
|
{
|
||||||
|
return ExprEval.ofStringArray(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String stringify()
|
||||||
|
{
|
||||||
|
if (value.length == 0) {
|
||||||
|
return "<STRING>[]";
|
||||||
|
}
|
||||||
|
|
||||||
|
return StringUtils.format(
|
||||||
|
"<STRING>[%s]",
|
||||||
|
ARG_JOINER.join(
|
||||||
|
Arrays.stream(value)
|
||||||
|
.map(s -> s == null
|
||||||
|
? NULL_LITERAL
|
||||||
|
// escape as javascript string since string literals are wrapped in single quotes
|
||||||
|
: StringUtils.format("'%s'", StringEscapeUtils.escapeJavaScript(s))
|
||||||
|
)
|
||||||
|
.iterator()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o)
|
||||||
|
{
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
StringArrayExpr that = (StringArrayExpr) o;
|
||||||
|
return Arrays.equals(value, that.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode()
|
||||||
|
{
|
||||||
|
return Arrays.hashCode(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DoubleExpr extends ConstantExpr
|
||||||
|
{
|
||||||
|
private final Double value;
|
||||||
|
|
||||||
|
DoubleExpr(Double value)
|
||||||
|
{
|
||||||
|
this.value = Preconditions.checkNotNull(value, "value");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getLiteralValue()
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return String.valueOf(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExprEval eval(ObjectBinding bindings)
|
||||||
|
{
|
||||||
|
return ExprEval.ofDouble(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o)
|
||||||
|
{
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
DoubleExpr that = (DoubleExpr) o;
|
||||||
|
return Objects.equals(value, that.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode()
|
||||||
|
{
|
||||||
|
return Objects.hash(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class NullDoubleExpr extends NullNumericConstantExpr
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public ExprEval eval(ObjectBinding bindings)
|
||||||
|
{
|
||||||
|
return ExprEval.ofDouble(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final int hashCode()
|
||||||
|
{
|
||||||
|
return NullDoubleExpr.class.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean equals(Object obj)
|
||||||
|
{
|
||||||
|
return obj instanceof NullDoubleExpr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DoubleArrayExpr extends ConstantExpr
|
||||||
|
{
|
||||||
|
private final Double[] value;
|
||||||
|
|
||||||
|
DoubleArrayExpr(Double[] value)
|
||||||
|
{
|
||||||
|
this.value = Preconditions.checkNotNull(value, "value");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getLiteralValue()
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return Arrays.toString(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExprEval eval(ObjectBinding bindings)
|
||||||
|
{
|
||||||
|
return ExprEval.ofDoubleArray(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String stringify()
|
||||||
|
{
|
||||||
|
if (value.length == 0) {
|
||||||
|
return "<DOUBLE>[]";
|
||||||
|
}
|
||||||
|
return StringUtils.format("<DOUBLE>%s", toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o)
|
||||||
|
{
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
DoubleArrayExpr that = (DoubleArrayExpr) o;
|
||||||
|
return Arrays.equals(value, that.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode()
|
||||||
|
{
|
||||||
|
return Arrays.hashCode(value);
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,334 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.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 org.apache.druid.math.expr;
|
||||||
|
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import org.apache.druid.java.util.common.StringUtils;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
class LambdaExpr implements Expr
|
||||||
|
{
|
||||||
|
private final ImmutableList<IdentifierExpr> args;
|
||||||
|
private final Expr expr;
|
||||||
|
|
||||||
|
LambdaExpr(List<IdentifierExpr> args, Expr expr)
|
||||||
|
{
|
||||||
|
this.args = ImmutableList.copyOf(args);
|
||||||
|
this.expr = expr;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return StringUtils.format("(%s -> %s)", args, expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
int identifierCount()
|
||||||
|
{
|
||||||
|
return args.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public String getIdentifier()
|
||||||
|
{
|
||||||
|
Preconditions.checkState(args.size() < 2, "LambdaExpr has multiple arguments");
|
||||||
|
if (args.size() == 1) {
|
||||||
|
return args.get(0).toString();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getIdentifiers()
|
||||||
|
{
|
||||||
|
return args.stream().map(IdentifierExpr::toString).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
ImmutableList<IdentifierExpr> getIdentifierExprs()
|
||||||
|
{
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Expr getExpr()
|
||||||
|
{
|
||||||
|
return expr;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExprEval eval(ObjectBinding bindings)
|
||||||
|
{
|
||||||
|
return expr.eval(bindings);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String stringify()
|
||||||
|
{
|
||||||
|
return StringUtils.format("(%s) -> %s", ARG_JOINER.join(getIdentifiers()), expr.stringify());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(Visitor visitor)
|
||||||
|
{
|
||||||
|
expr.visit(visitor);
|
||||||
|
visitor.visit(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Expr visit(Shuttle shuttle)
|
||||||
|
{
|
||||||
|
List<IdentifierExpr> newArgs =
|
||||||
|
args.stream().map(arg -> (IdentifierExpr) shuttle.visit(arg)).collect(Collectors.toList());
|
||||||
|
Expr newBody = expr.visit(shuttle);
|
||||||
|
return shuttle.visit(new LambdaExpr(newArgs, newBody));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BindingDetails analyzeInputs()
|
||||||
|
{
|
||||||
|
final Set<String> lambdaArgs = args.stream().map(IdentifierExpr::toString).collect(Collectors.toSet());
|
||||||
|
BindingDetails bodyDetails = expr.analyzeInputs();
|
||||||
|
return bodyDetails.removeLambdaArguments(lambdaArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o)
|
||||||
|
{
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
LambdaExpr that = (LambdaExpr) o;
|
||||||
|
return Objects.equals(args, that.args) &&
|
||||||
|
Objects.equals(expr, that.expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode()
|
||||||
|
{
|
||||||
|
return Objects.hash(args, expr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link Expr} node for a {@link Function} call. {@link FunctionExpr} has children {@link Expr} in the form of the
|
||||||
|
* list of arguments that are passed to the {@link Function} along with the {@link Expr.ObjectBinding} when it is
|
||||||
|
* evaluated.
|
||||||
|
*/
|
||||||
|
class FunctionExpr implements Expr
|
||||||
|
{
|
||||||
|
final Function function;
|
||||||
|
final ImmutableList<Expr> args;
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
FunctionExpr(Function function, String name, List<Expr> args)
|
||||||
|
{
|
||||||
|
this.function = function;
|
||||||
|
this.name = name;
|
||||||
|
this.args = ImmutableList.copyOf(args);
|
||||||
|
function.validateArguments(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return StringUtils.format("(%s %s)", name, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExprEval eval(ObjectBinding bindings)
|
||||||
|
{
|
||||||
|
return function.apply(args, bindings);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String stringify()
|
||||||
|
{
|
||||||
|
return StringUtils.format("%s(%s)", name, ARG_JOINER.join(args.stream().map(Expr::stringify).iterator()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(Visitor visitor)
|
||||||
|
{
|
||||||
|
for (Expr child : args) {
|
||||||
|
child.visit(visitor);
|
||||||
|
}
|
||||||
|
visitor.visit(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Expr visit(Shuttle shuttle)
|
||||||
|
{
|
||||||
|
List<Expr> newArgs = args.stream().map(shuttle::visit).collect(Collectors.toList());
|
||||||
|
return shuttle.visit(new FunctionExpr(function, name, newArgs));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BindingDetails analyzeInputs()
|
||||||
|
{
|
||||||
|
BindingDetails accumulator = new BindingDetails();
|
||||||
|
|
||||||
|
for (Expr arg : args) {
|
||||||
|
accumulator = accumulator.with(arg);
|
||||||
|
}
|
||||||
|
return accumulator.withScalarArguments(function.getScalarInputs(args))
|
||||||
|
.withArrayArguments(function.getArrayInputs(args))
|
||||||
|
.withArrayInputs(function.hasArrayInputs())
|
||||||
|
.withArrayOutput(function.hasArrayOutput());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o)
|
||||||
|
{
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
FunctionExpr that = (FunctionExpr) o;
|
||||||
|
return args.equals(that.args) &&
|
||||||
|
name.equals(that.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode()
|
||||||
|
{
|
||||||
|
return Objects.hash(args, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This {@link Expr} node is representative of an {@link ApplyFunction}, and has children in the form of a
|
||||||
|
* {@link LambdaExpr} and the list of {@link Expr} arguments that are combined with {@link Expr.ObjectBinding} to
|
||||||
|
* evaluate the {@link LambdaExpr}.
|
||||||
|
*/
|
||||||
|
class ApplyFunctionExpr implements Expr
|
||||||
|
{
|
||||||
|
final ApplyFunction function;
|
||||||
|
final String name;
|
||||||
|
final LambdaExpr lambdaExpr;
|
||||||
|
final ImmutableList<Expr> argsExpr;
|
||||||
|
final BindingDetails bindingDetails;
|
||||||
|
final BindingDetails lambdaBindingDetails;
|
||||||
|
final ImmutableList<BindingDetails> argsBindingDetails;
|
||||||
|
|
||||||
|
ApplyFunctionExpr(ApplyFunction function, String name, LambdaExpr expr, List<Expr> args)
|
||||||
|
{
|
||||||
|
this.function = function;
|
||||||
|
this.name = name;
|
||||||
|
this.argsExpr = ImmutableList.copyOf(args);
|
||||||
|
this.lambdaExpr = expr;
|
||||||
|
|
||||||
|
function.validateArguments(expr, args);
|
||||||
|
|
||||||
|
// apply function expressions are examined during expression selector creation, so precompute and cache the
|
||||||
|
// binding details of children
|
||||||
|
ImmutableList.Builder<BindingDetails> argBindingDetailsBuilder = ImmutableList.builder();
|
||||||
|
BindingDetails accumulator = new BindingDetails();
|
||||||
|
for (Expr arg : argsExpr) {
|
||||||
|
BindingDetails argDetails = arg.analyzeInputs();
|
||||||
|
argBindingDetailsBuilder.add(argDetails);
|
||||||
|
accumulator = accumulator.with(argDetails);
|
||||||
|
}
|
||||||
|
|
||||||
|
lambdaBindingDetails = lambdaExpr.analyzeInputs();
|
||||||
|
|
||||||
|
bindingDetails = accumulator.with(lambdaBindingDetails)
|
||||||
|
.withArrayArguments(function.getArrayInputs(argsExpr))
|
||||||
|
.withArrayInputs(true)
|
||||||
|
.withArrayOutput(function.hasArrayOutput(lambdaExpr));
|
||||||
|
argsBindingDetails = argBindingDetailsBuilder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return StringUtils.format("(%s %s, %s)", name, lambdaExpr, argsExpr);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExprEval eval(ObjectBinding bindings)
|
||||||
|
{
|
||||||
|
return function.apply(lambdaExpr, argsExpr, bindings);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String stringify()
|
||||||
|
{
|
||||||
|
return StringUtils.format(
|
||||||
|
"%s(%s, %s)",
|
||||||
|
name,
|
||||||
|
lambdaExpr.stringify(),
|
||||||
|
ARG_JOINER.join(argsExpr.stream().map(Expr::stringify).iterator())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(Visitor visitor)
|
||||||
|
{
|
||||||
|
lambdaExpr.visit(visitor);
|
||||||
|
for (Expr arg : argsExpr) {
|
||||||
|
arg.visit(visitor);
|
||||||
|
}
|
||||||
|
visitor.visit(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Expr visit(Shuttle shuttle)
|
||||||
|
{
|
||||||
|
LambdaExpr newLambda = (LambdaExpr) lambdaExpr.visit(shuttle);
|
||||||
|
List<Expr> newArgs = argsExpr.stream().map(shuttle::visit).collect(Collectors.toList());
|
||||||
|
return shuttle.visit(new ApplyFunctionExpr(function, name, newLambda, newArgs));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BindingDetails analyzeInputs()
|
||||||
|
{
|
||||||
|
return bindingDetails;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o)
|
||||||
|
{
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ApplyFunctionExpr that = (ApplyFunctionExpr) o;
|
||||||
|
return name.equals(that.name) &&
|
||||||
|
lambdaExpr.equals(that.lambdaExpr) &&
|
||||||
|
argsExpr.equals(that.argsExpr);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode()
|
||||||
|
{
|
||||||
|
return Objects.hash(name, lambdaExpr, argsExpr);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,153 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.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 org.apache.druid.math.expr;
|
||||||
|
|
||||||
|
import org.apache.commons.lang.StringEscapeUtils;
|
||||||
|
import org.apache.druid.java.util.common.StringUtils;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This {@link Expr} node is used to represent a variable in the expression language. At evaluation time, the string
|
||||||
|
* identifier will be used to retrieve the runtime value for the variable from {@link Expr.ObjectBinding}.
|
||||||
|
* {@link IdentifierExpr} are terminal nodes of an expression tree, and have no children {@link Expr}.
|
||||||
|
*/
|
||||||
|
class IdentifierExpr implements Expr
|
||||||
|
{
|
||||||
|
private final String identifier;
|
||||||
|
private final String binding;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a identifier expression for a {@link LambdaExpr}, where the {@link #identifier} is equal to
|
||||||
|
* {@link #binding}
|
||||||
|
*/
|
||||||
|
IdentifierExpr(String value)
|
||||||
|
{
|
||||||
|
this.identifier = value;
|
||||||
|
this.binding = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a normal identifier expression, where {@link #binding} is the key to fetch the backing value from
|
||||||
|
* {@link Expr.ObjectBinding} and the {@link #identifier} is a unique string that identifies this usage of the
|
||||||
|
* binding.
|
||||||
|
*/
|
||||||
|
IdentifierExpr(String identifier, String binding)
|
||||||
|
{
|
||||||
|
this.identifier = identifier;
|
||||||
|
this.binding = binding;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return binding;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unique identifier for the binding
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public String getIdentifier()
|
||||||
|
{
|
||||||
|
return identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value binding, key to retrieve value from {@link Expr.ObjectBinding#get(String)}
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public String getBinding()
|
||||||
|
{
|
||||||
|
return binding;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public String getIdentifierIfIdentifier()
|
||||||
|
{
|
||||||
|
return identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public String getBindingIfIdentifier()
|
||||||
|
{
|
||||||
|
return binding;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public IdentifierExpr getIdentifierExprIfIdentifierExpr()
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BindingDetails analyzeInputs()
|
||||||
|
{
|
||||||
|
return new BindingDetails(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExprEval eval(ObjectBinding bindings)
|
||||||
|
{
|
||||||
|
return ExprEval.bestEffortOf(bindings.get(binding));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String stringify()
|
||||||
|
{
|
||||||
|
// escape as java strings since identifiers are wrapped in double quotes
|
||||||
|
return StringUtils.format("\"%s\"", StringEscapeUtils.escapeJava(binding));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(Visitor visitor)
|
||||||
|
{
|
||||||
|
visitor.visit(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Expr visit(Shuttle shuttle)
|
||||||
|
{
|
||||||
|
return shuttle.visit(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o)
|
||||||
|
{
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
IdentifierExpr that = (IdentifierExpr) o;
|
||||||
|
return Objects.equals(identifier, that.identifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode()
|
||||||
|
{
|
||||||
|
return Objects.hash(identifier);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,166 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.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 org.apache.druid.math.expr;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import org.apache.druid.common.config.NullHandling;
|
||||||
|
import org.apache.druid.java.util.common.IAE;
|
||||||
|
import org.apache.druid.java.util.common.StringUtils;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base type for all single argument operators, with a single {@link Expr} child for the operand.
|
||||||
|
*/
|
||||||
|
abstract class UnaryExpr implements Expr
|
||||||
|
{
|
||||||
|
final Expr expr;
|
||||||
|
|
||||||
|
UnaryExpr(Expr expr)
|
||||||
|
{
|
||||||
|
this.expr = expr;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract UnaryExpr copy(Expr expr);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(Visitor visitor)
|
||||||
|
{
|
||||||
|
expr.visit(visitor);
|
||||||
|
visitor.visit(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Expr visit(Shuttle shuttle)
|
||||||
|
{
|
||||||
|
Expr newExpr = expr.visit(shuttle);
|
||||||
|
//noinspection ObjectEquality (checking for object equality here is intentional)
|
||||||
|
if (newExpr != expr) {
|
||||||
|
return shuttle.visit(copy(newExpr));
|
||||||
|
}
|
||||||
|
return shuttle.visit(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BindingDetails analyzeInputs()
|
||||||
|
{
|
||||||
|
// currently all unary operators only operate on scalar inputs
|
||||||
|
return expr.analyzeInputs().withScalarArguments(ImmutableSet.of(expr));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o)
|
||||||
|
{
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
UnaryExpr unaryExpr = (UnaryExpr) o;
|
||||||
|
return Objects.equals(expr, unaryExpr.expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode()
|
||||||
|
{
|
||||||
|
return Objects.hash(expr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class UnaryMinusExpr extends UnaryExpr
|
||||||
|
{
|
||||||
|
UnaryMinusExpr(Expr expr)
|
||||||
|
{
|
||||||
|
super(expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
UnaryExpr copy(Expr expr)
|
||||||
|
{
|
||||||
|
return new UnaryMinusExpr(expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExprEval eval(ObjectBinding bindings)
|
||||||
|
{
|
||||||
|
ExprEval ret = expr.eval(bindings);
|
||||||
|
if (NullHandling.sqlCompatible() && (ret.value() == null)) {
|
||||||
|
return ExprEval.of(null);
|
||||||
|
}
|
||||||
|
if (ret.type() == ExprType.LONG) {
|
||||||
|
return ExprEval.of(-ret.asLong());
|
||||||
|
}
|
||||||
|
if (ret.type() == ExprType.DOUBLE) {
|
||||||
|
return ExprEval.of(-ret.asDouble());
|
||||||
|
}
|
||||||
|
throw new IAE("unsupported type " + ret.type());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String stringify()
|
||||||
|
{
|
||||||
|
return StringUtils.format("-%s", expr.stringify());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return StringUtils.format("-%s", expr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class UnaryNotExpr extends UnaryExpr
|
||||||
|
{
|
||||||
|
UnaryNotExpr(Expr expr)
|
||||||
|
{
|
||||||
|
super(expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
UnaryExpr copy(Expr expr)
|
||||||
|
{
|
||||||
|
return new UnaryNotExpr(expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExprEval eval(ObjectBinding bindings)
|
||||||
|
{
|
||||||
|
ExprEval ret = expr.eval(bindings);
|
||||||
|
if (NullHandling.sqlCompatible() && (ret.value() == null)) {
|
||||||
|
return ExprEval.of(null);
|
||||||
|
}
|
||||||
|
// conforming to other boolean-returning binary operators
|
||||||
|
ExprType retType = ret.type() == ExprType.DOUBLE ? ExprType.DOUBLE : ExprType.LONG;
|
||||||
|
return ExprEval.of(!ret.asBoolean(), retType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String stringify()
|
||||||
|
{
|
||||||
|
return StringUtils.format("!%s", expr.stringify());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return StringUtils.format("!%s", expr);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue