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