SQL: Add Atan2 and Power functions (elastic/x-pack-elasticsearch#4412)
Add missing Atan2 & Power(and introduce BinaryMath operations), similar to MathOperation. Also align arithmetic package with binary math for code reuse. Original commit: elastic/x-pack-elasticsearch@311961815e
This commit is contained in:
parent
e6f69ee269
commit
bc2ef139a8
|
@ -20,6 +20,7 @@ import org.elasticsearch.xpack.sql.expression.function.aggregate.StddevPop;
|
|||
import org.elasticsearch.xpack.sql.expression.function.aggregate.Sum;
|
||||
import org.elasticsearch.xpack.sql.expression.function.aggregate.SumOfSquares;
|
||||
import org.elasticsearch.xpack.sql.expression.function.aggregate.VarPop;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.arithmetic.Mod;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DayOfMonth;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DayOfWeek;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DayOfYear;
|
||||
|
@ -33,6 +34,7 @@ import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.Year;
|
|||
import org.elasticsearch.xpack.sql.expression.function.scalar.math.ACos;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.math.ASin;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.math.ATan;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.math.ATan2;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.math.Abs;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.math.Cbrt;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.math.Ceil;
|
||||
|
@ -46,6 +48,7 @@ import org.elasticsearch.xpack.sql.expression.function.scalar.math.Floor;
|
|||
import org.elasticsearch.xpack.sql.expression.function.scalar.math.Log;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.math.Log10;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.math.Pi;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.math.Power;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.math.Radians;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.math.Round;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.math.Sin;
|
||||
|
@ -104,6 +107,7 @@ public class FunctionRegistry {
|
|||
def(ACos.class, ACos::new),
|
||||
def(ASin.class, ASin::new),
|
||||
def(ATan.class, ATan::new),
|
||||
def(ATan2.class, ATan2::new),
|
||||
def(Cbrt.class, Cbrt::new),
|
||||
def(Ceil.class, Ceil::new),
|
||||
def(Cos.class, Cos::new),
|
||||
|
@ -115,7 +119,10 @@ public class FunctionRegistry {
|
|||
def(Floor.class, Floor::new),
|
||||
def(Log.class, Log::new),
|
||||
def(Log10.class, Log10::new),
|
||||
// SQL and ODBC require MOD as a _function_
|
||||
def(Mod.class, Mod::new),
|
||||
def(Pi.class, Pi::new),
|
||||
def(Power.class, Power::new),
|
||||
def(Radians.class, Radians::new),
|
||||
def(Round.class, Round::new),
|
||||
def(Sin.class, Sin::new),
|
||||
|
|
|
@ -7,8 +7,9 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.arithmetic;
|
|||
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.Literal;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.BinaryScalarFunction;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.arithmetic.BinaryArithmeticProcessor.BinaryArithmeticOperation;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.math.BinaryNumericFunction;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.processor.definition.ProcessorDefinition;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.processor.definition.ProcessorDefinitions;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.script.ScriptTemplate;
|
||||
import org.elasticsearch.xpack.sql.tree.Location;
|
||||
|
@ -16,20 +17,20 @@ import org.elasticsearch.xpack.sql.type.DataType;
|
|||
import org.elasticsearch.xpack.sql.type.DataTypeConversion;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static org.elasticsearch.xpack.sql.expression.function.scalar.script.ParamsBuilder.paramsBuilder;
|
||||
|
||||
public abstract class ArithmeticFunction extends BinaryScalarFunction {
|
||||
public abstract class ArithmeticFunction extends BinaryNumericFunction {
|
||||
|
||||
private BinaryArithmeticOperation operation;
|
||||
private final BinaryArithmeticOperation operation;
|
||||
|
||||
ArithmeticFunction(Location location, Expression left, Expression right, BinaryArithmeticOperation operation) {
|
||||
super(location, left, right);
|
||||
this.operation = operation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BinaryArithmeticOperation operation() {
|
||||
return operation;
|
||||
}
|
||||
|
@ -39,32 +40,6 @@ public abstract class ArithmeticFunction extends BinaryScalarFunction {
|
|||
return DataTypeConversion.commonType(left().dataType(), right().dataType());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TypeResolution resolveType() {
|
||||
if (!childrenResolved()) {
|
||||
return new TypeResolution("Unresolved children");
|
||||
}
|
||||
DataType l = left().dataType();
|
||||
DataType r = right().dataType();
|
||||
|
||||
TypeResolution resolution = resolveInputType(l);
|
||||
|
||||
if (resolution == TypeResolution.TYPE_RESOLVED) {
|
||||
return resolveInputType(r);
|
||||
}
|
||||
return resolution;
|
||||
}
|
||||
|
||||
protected TypeResolution resolveInputType(DataType inputType) {
|
||||
return inputType.isNumeric() ? TypeResolution.TYPE_RESOLVED
|
||||
: new TypeResolution("'%s' requires a numeric type, not %s", operation, inputType.sqlName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object fold() {
|
||||
return operation.apply((Number) left().fold(), (Number) right().fold());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ScriptTemplate asScriptFrom(ScriptTemplate leftScript, ScriptTemplate rightScript) {
|
||||
String op = operation.symbol();
|
||||
|
@ -79,10 +54,11 @@ public abstract class ArithmeticFunction extends BinaryScalarFunction {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected final BinaryArithmeticProcessorDefinition makeProcessorDefinition() {
|
||||
protected ProcessorDefinition makeProcessorDefinition() {
|
||||
return new BinaryArithmeticProcessorDefinition(location(), this,
|
||||
ProcessorDefinitions.toProcessorDefinition(left()),
|
||||
ProcessorDefinitions.toProcessorDefinition(right()), operation);
|
||||
ProcessorDefinitions.toProcessorDefinition(right()),
|
||||
operation);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -115,20 +91,4 @@ public abstract class ArithmeticFunction extends BinaryScalarFunction {
|
|||
protected boolean useParanthesis() {
|
||||
return !(left() instanceof Literal) || !(right() instanceof Literal);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null || obj.getClass() != getClass()) {
|
||||
return false;
|
||||
}
|
||||
ArithmeticFunction other = (ArithmeticFunction) obj;
|
||||
return Objects.equals(other.left(), left())
|
||||
&& Objects.equals(other.right(), right())
|
||||
&& Objects.equals(other.operation, operation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(left(), right(), operation);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,18 +7,16 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.arithmetic;
|
|||
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.xpack.sql.SqlIllegalArgumentException;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.processor.runtime.BinaryProcessor;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.arithmetic.BinaryArithmeticProcessor.BinaryArithmeticOperation;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.math.BinaryNumericProcessor;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.processor.runtime.Processor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
public class BinaryArithmeticProcessor extends BinaryProcessor {
|
||||
public class BinaryArithmeticProcessor extends BinaryNumericProcessor<BinaryArithmeticOperation> {
|
||||
|
||||
public enum BinaryArithmeticOperation {
|
||||
public enum BinaryArithmeticOperation implements BiFunction<Number, Number, Number> {
|
||||
|
||||
ADD(Arithmetics::add, "+"),
|
||||
SUB(Arithmetics::sub, "-"),
|
||||
|
@ -38,6 +36,7 @@ public class BinaryArithmeticProcessor extends BinaryProcessor {
|
|||
return symbol;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Number apply(Number left, Number right) {
|
||||
return process.apply(left, right);
|
||||
}
|
||||
|
@ -50,66 +49,21 @@ public class BinaryArithmeticProcessor extends BinaryProcessor {
|
|||
|
||||
public static final String NAME = "ab";
|
||||
|
||||
private final BinaryArithmeticOperation operation;
|
||||
|
||||
public BinaryArithmeticProcessor(Processor left, Processor right, BinaryArithmeticOperation operation) {
|
||||
super(left, right);
|
||||
this.operation = operation;
|
||||
super(left, right, operation);
|
||||
}
|
||||
|
||||
public BinaryArithmeticProcessor(StreamInput in) throws IOException {
|
||||
super(in);
|
||||
operation = in.readEnum(BinaryArithmeticOperation.class);
|
||||
super(in, i -> i.readEnum(BinaryArithmeticOperation.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doWrite(StreamOutput out) throws IOException {
|
||||
out.writeEnum(operation());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWriteableName() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doWrite(StreamOutput out) throws IOException {
|
||||
out.writeEnum(operation);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object doProcess(Object left, Object right) {
|
||||
if (left == null || right == null) {
|
||||
return null;
|
||||
}
|
||||
if (!(left instanceof Number)) {
|
||||
throw new SqlIllegalArgumentException("A number is required; received {}", left);
|
||||
}
|
||||
if (!(right instanceof Number)) {
|
||||
throw new SqlIllegalArgumentException("A number is required; received {}", right);
|
||||
}
|
||||
|
||||
return operation.apply((Number) left, (Number) right);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return operation.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BinaryArithmeticProcessor other = (BinaryArithmeticProcessor) obj;
|
||||
return Objects.equals(operation, other.operation)
|
||||
&& Objects.equals(left(), other.left())
|
||||
&& Objects.equals(right(), other.right());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format(Locale.ROOT, "(%s %s %s)", left(), operation, right());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,7 +13,7 @@ import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
|||
|
||||
/**
|
||||
* <a href="https://en.wikipedia.org/wiki/Inverse_trigonometric_functions">Arc cosine</a>
|
||||
* fuction.
|
||||
* function.
|
||||
*/
|
||||
public class ACos extends MathFunction {
|
||||
public ACos(Location location, Expression field) {
|
||||
|
|
|
@ -12,7 +12,7 @@ import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
|||
|
||||
/**
|
||||
* <a href="https://en.wikipedia.org/wiki/Inverse_trigonometric_functions">Arc sine</a>
|
||||
* fuction.
|
||||
* function.
|
||||
*/
|
||||
public class ASin extends MathFunction {
|
||||
public ASin(Location location, Expression field) {
|
||||
|
|
|
@ -12,7 +12,7 @@ import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
|||
|
||||
/**
|
||||
* <a href="https://en.wikipedia.org/wiki/Inverse_trigonometric_functions">Arc tangent</a>
|
||||
* fuction.
|
||||
* function.
|
||||
*/
|
||||
public class ATan extends MathFunction {
|
||||
public ATan(Location location, Expression field) {
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.xpack.sql.expression.function.scalar.math;
|
||||
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.math.BinaryMathProcessor.BinaryMathOperation;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.processor.definition.ProcessorDefinition;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.processor.definition.ProcessorDefinitions;
|
||||
import org.elasticsearch.xpack.sql.tree.Location;
|
||||
import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
/**
|
||||
* <a href="https://en.wikipedia.org/wiki/Atan2">Multi-valued inverse tangent</a>
|
||||
* function.
|
||||
*/
|
||||
public class ATan2 extends BinaryNumericFunction {
|
||||
|
||||
public ATan2(Location location, Expression left, Expression right) {
|
||||
super(location, left, right);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BiFunction<Number, Number, Number> operation() {
|
||||
return BinaryMathOperation.ATAN2;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NodeInfo<? extends Expression> info() {
|
||||
return NodeInfo.create(this, ATan2::new, left(), right());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ATan2 replaceChildren(Expression newLeft, Expression newRight) {
|
||||
return new ATan2(location(), newLeft, newRight);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ProcessorDefinition makeProcessorDefinition() {
|
||||
return new BinaryMathProcessorDefinition(location(), this,
|
||||
ProcessorDefinitions.toProcessorDefinition(left()),
|
||||
ProcessorDefinitions.toProcessorDefinition(right()),
|
||||
BinaryMathOperation.ATAN2);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.xpack.sql.expression.function.scalar.math;
|
||||
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.math.BinaryMathProcessor.BinaryMathOperation;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.math.MathProcessor.MathOperation;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.processor.runtime.Processor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
/**
|
||||
* Binary math operations. Sister class to {@link MathOperation}.
|
||||
*/
|
||||
public class BinaryMathProcessor extends BinaryNumericProcessor<BinaryMathOperation> {
|
||||
|
||||
public enum BinaryMathOperation implements BiFunction<Number, Number, Number> {
|
||||
|
||||
ATAN2((l, r) -> Math.atan2(l.doubleValue(), r.doubleValue())),
|
||||
POWER((l, r) -> Math.pow(l.doubleValue(), r.doubleValue()));
|
||||
|
||||
private final BiFunction<Number, Number, Number> process;
|
||||
|
||||
BinaryMathOperation(BiFunction<Number, Number, Number> process) {
|
||||
this.process = process;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Number apply(Number left, Number right) {
|
||||
return process.apply(left, right);
|
||||
}
|
||||
}
|
||||
|
||||
public static final String NAME = "mb";
|
||||
|
||||
public BinaryMathProcessor(Processor left, Processor right, BinaryMathOperation operation) {
|
||||
super(left, right, operation);
|
||||
}
|
||||
|
||||
public BinaryMathProcessor(StreamInput in) throws IOException {
|
||||
super(in, i -> i.readEnum(BinaryMathOperation.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doWrite(StreamOutput out) throws IOException {
|
||||
out.writeEnum(operation());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWriteableName() {
|
||||
return NAME;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.xpack.sql.expression.function.scalar.math;
|
||||
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.math.BinaryMathProcessor.BinaryMathOperation;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.processor.definition.BinaryProcessorDefinition;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.processor.definition.ProcessorDefinition;
|
||||
import org.elasticsearch.xpack.sql.tree.Location;
|
||||
import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Processor definition for math operations requiring two arguments.
|
||||
*/
|
||||
public class BinaryMathProcessorDefinition extends BinaryProcessorDefinition {
|
||||
|
||||
private final BinaryMathOperation operation;
|
||||
|
||||
public BinaryMathProcessorDefinition(Location location, Expression expression, ProcessorDefinition left,
|
||||
ProcessorDefinition right, BinaryMathOperation operation) {
|
||||
super(location, expression, left, right);
|
||||
this.operation = operation;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NodeInfo<BinaryMathProcessorDefinition> info() {
|
||||
return NodeInfo.create(this, BinaryMathProcessorDefinition::new, expression(), left(), right(), operation);
|
||||
}
|
||||
|
||||
public BinaryMathOperation operation() {
|
||||
return operation;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BinaryProcessorDefinition replaceChildren(ProcessorDefinition left, ProcessorDefinition right) {
|
||||
return new BinaryMathProcessorDefinition(location(), expression(), left, right, operation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BinaryMathProcessor asProcessor() {
|
||||
return new BinaryMathProcessor(left().asProcessor(), right().asProcessor(), operation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(left(), right(), operation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BinaryMathProcessorDefinition other = (BinaryMathProcessorDefinition) obj;
|
||||
return Objects.equals(operation, other.operation)
|
||||
&& Objects.equals(left(), other.left())
|
||||
&& Objects.equals(right(), other.right());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.xpack.sql.expression.function.scalar.math;
|
||||
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.BinaryScalarFunction;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.script.ScriptTemplate;
|
||||
import org.elasticsearch.xpack.sql.tree.Location;
|
||||
import org.elasticsearch.xpack.sql.type.DataType;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static org.elasticsearch.xpack.sql.expression.function.scalar.script.ParamsBuilder.paramsBuilder;
|
||||
|
||||
public abstract class BinaryNumericFunction extends BinaryScalarFunction {
|
||||
|
||||
protected BinaryNumericFunction(Location location, Expression left, Expression right) {
|
||||
super(location, left, right);
|
||||
}
|
||||
|
||||
protected abstract BiFunction<Number, Number, Number> operation();
|
||||
|
||||
@Override
|
||||
public DataType dataType() {
|
||||
return DataType.DOUBLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TypeResolution resolveType() {
|
||||
if (!childrenResolved()) {
|
||||
return new TypeResolution("Unresolved children");
|
||||
}
|
||||
|
||||
TypeResolution resolution = resolveInputType(left().dataType());
|
||||
|
||||
if (resolution == TypeResolution.TYPE_RESOLVED) {
|
||||
return resolveInputType(right().dataType());
|
||||
}
|
||||
return resolution;
|
||||
}
|
||||
|
||||
protected TypeResolution resolveInputType(DataType inputType) {
|
||||
return inputType.isNumeric() ?
|
||||
TypeResolution.TYPE_RESOLVED :
|
||||
new TypeResolution("'%s' requires a numeric type, received %s", mathFunction(), inputType.esType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object fold() {
|
||||
return operation().apply((Number) left().fold(), (Number) right().fold());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ScriptTemplate asScriptFrom(ScriptTemplate leftScript, ScriptTemplate rightScript) {
|
||||
return new ScriptTemplate(format(Locale.ROOT, "Math.%s(%s,%s)", mathFunction(), leftScript.template(), rightScript.template()),
|
||||
paramsBuilder()
|
||||
.script(leftScript.params()).script(rightScript.params())
|
||||
.build(), dataType());
|
||||
}
|
||||
|
||||
protected String mathFunction() {
|
||||
return getClass().getSimpleName().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(left(), right(), operation());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null || obj.getClass() != getClass()) {
|
||||
return false;
|
||||
}
|
||||
BinaryNumericFunction other = (BinaryNumericFunction) obj;
|
||||
return Objects.equals(other.left(), left())
|
||||
&& Objects.equals(other.right(), right())
|
||||
&& Objects.equals(other.operation(), operation());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.xpack.sql.expression.function.scalar.math;
|
||||
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.xpack.sql.SqlIllegalArgumentException;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.processor.runtime.BinaryProcessor;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.processor.runtime.Processor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
public abstract class BinaryNumericProcessor<O extends Enum<?> & BiFunction<Number, Number, Number>> extends BinaryProcessor {
|
||||
|
||||
private final O operation;
|
||||
|
||||
protected BinaryNumericProcessor(Processor left, Processor right, O operation) {
|
||||
super(left, right);
|
||||
this.operation = operation;
|
||||
}
|
||||
|
||||
protected BinaryNumericProcessor(StreamInput in, Reader<O> reader) throws IOException {
|
||||
super(in);
|
||||
operation = reader.read(in);
|
||||
}
|
||||
|
||||
protected O operation() {
|
||||
return operation;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object doProcess(Object left, Object right) {
|
||||
if (left == null || right == null) {
|
||||
return null;
|
||||
}
|
||||
if (!(left instanceof Number)) {
|
||||
throw new SqlIllegalArgumentException("A number is required; received {}", left);
|
||||
}
|
||||
if (!(right instanceof Number)) {
|
||||
throw new SqlIllegalArgumentException("A number is required; received {}", right);
|
||||
}
|
||||
|
||||
return operation.apply((Number) left, (Number) right);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(operation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BinaryNumericProcessor<?> other = (BinaryNumericProcessor<?>) obj;
|
||||
return Objects.equals(operation, other.operation)
|
||||
&& Objects.equals(left(), other.left())
|
||||
&& Objects.equals(right(), other.right());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format(Locale.ROOT, "(%s %s %s)", left(), operation, right());
|
||||
}
|
||||
}
|
|
@ -31,6 +31,11 @@ public class Ceil extends MathFunction {
|
|||
return new Ceil(location(), newChild);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Number fold() {
|
||||
return DataTypeConversion.toInteger((double) super.fold(), dataType());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MathOperation operation() {
|
||||
return MathOperation.CEIL;
|
||||
|
|
|
@ -31,6 +31,11 @@ public class Floor extends MathFunction {
|
|||
return new Floor(location(), newChild);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object fold() {
|
||||
return DataTypeConversion.toInteger((double) super.fold(), dataType());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MathOperation operation() {
|
||||
return MathOperation.FLOOR;
|
||||
|
|
|
@ -52,6 +52,16 @@ public abstract class MathFunction extends UnaryScalarFunction {
|
|||
public DataType dataType() {
|
||||
return DataType.DOUBLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TypeResolution resolveType() {
|
||||
if (!childrenResolved()) {
|
||||
return new TypeResolution("Unresolved children");
|
||||
}
|
||||
|
||||
return field().dataType().isNumeric() ? TypeResolution.TYPE_RESOLVED
|
||||
: new TypeResolution("'%s' requires a numeric type, received %s", operation(), field().dataType().esType);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final ProcessorDefinition makeProcessorDefinition() {
|
||||
|
@ -74,4 +84,4 @@ public abstract class MathFunction extends UnaryScalarFunction {
|
|||
public int hashCode() {
|
||||
return Objects.hash(field());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.math;
|
|||
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.xpack.sql.SqlIllegalArgumentException;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.processor.runtime.Processor;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -93,6 +94,10 @@ public class MathProcessor implements Processor {
|
|||
|
||||
@Override
|
||||
public Object process(Object input) {
|
||||
if (input != null && !(input instanceof Number)) {
|
||||
throw new SqlIllegalArgumentException("A number is required; received [{}]", input);
|
||||
}
|
||||
|
||||
return processor.apply(input);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.xpack.sql.expression.function.scalar.math;
|
||||
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.math.BinaryMathProcessor.BinaryMathOperation;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.processor.definition.ProcessorDefinition;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.processor.definition.ProcessorDefinitions;
|
||||
import org.elasticsearch.xpack.sql.tree.Location;
|
||||
import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
public class Power extends BinaryNumericFunction {
|
||||
|
||||
public Power(Location location, Expression left, Expression right) {
|
||||
super(location, left, right);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BiFunction<Number, Number, Number> operation() {
|
||||
return BinaryMathOperation.POWER;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NodeInfo<? extends Expression> info() {
|
||||
return NodeInfo.create(this, Power::new, left(), right());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Power replaceChildren(Expression newLeft, Expression newRight) {
|
||||
return new Power(location(), newLeft, newRight);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ProcessorDefinition makeProcessorDefinition() {
|
||||
return new BinaryMathProcessorDefinition(location(), this,
|
||||
ProcessorDefinitions.toProcessorDefinition(left()),
|
||||
ProcessorDefinitions.toProcessorDefinition(right()),
|
||||
BinaryMathOperation.POWER);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String mathFunction() {
|
||||
return "pow";
|
||||
}
|
||||
}
|
|
@ -12,7 +12,7 @@ import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
|||
|
||||
/**
|
||||
* <a href="https://en.wikipedia.org/wiki/Trigonometric_functions#sine">Tangent</a>
|
||||
* fuction.
|
||||
* function.
|
||||
*/
|
||||
public class Tan extends MathFunction {
|
||||
public Tan(Location location, Expression field) {
|
||||
|
|
|
@ -22,6 +22,7 @@ import static org.elasticsearch.xpack.sql.type.DataType.BOOLEAN;
|
|||
import static org.elasticsearch.xpack.sql.type.DataType.DATE;
|
||||
import static org.elasticsearch.xpack.sql.type.DataType.LONG;
|
||||
import static org.elasticsearch.xpack.sql.type.DataType.NULL;
|
||||
|
||||
/**
|
||||
* Conversions from one Elasticsearch data type to another Elasticsearch data types.
|
||||
* <p>
|
||||
|
@ -316,6 +317,21 @@ public abstract class DataTypeConversion {
|
|||
return Math.round(x);
|
||||
}
|
||||
|
||||
public static Number toInteger(double x, DataType dataType) {
|
||||
long l = safeToLong(x);
|
||||
|
||||
switch (dataType) {
|
||||
case BYTE:
|
||||
return safeToByte(l);
|
||||
case SHORT:
|
||||
return safeToShort(l);
|
||||
case INTEGER:
|
||||
return safeToInt(l);
|
||||
default:
|
||||
return l;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean convertToBoolean(String val) {
|
||||
String lowVal = val.toLowerCase(Locale.ROOT);
|
||||
if (Booleans.isBoolean(lowVal) == false) {
|
||||
|
|
|
@ -111,8 +111,8 @@ public class VerifierErrorMessagesTests extends ESTestCase {
|
|||
}
|
||||
|
||||
public void testGroupByOrderByScalarOverNonGrouped() {
|
||||
assertEquals("1:50: Cannot order by non-grouped column [bool], expected [text]",
|
||||
verify("SELECT MAX(int) FROM test GROUP BY text ORDER BY ABS(bool)"));
|
||||
assertEquals("1:50: Cannot order by non-grouped column [date], expected [text]",
|
||||
verify("SELECT MAX(int) FROM test GROUP BY text ORDER BY YEAR(date)"));
|
||||
}
|
||||
|
||||
public void testGroupByHavingNonGrouped() {
|
||||
|
|
|
@ -40,29 +40,29 @@ public class BinaryArithmeticProcessorTests extends AbstractWireSerializingTestC
|
|||
}
|
||||
|
||||
public void testAdd() {
|
||||
BinaryArithmeticProcessor ba = new Add(EMPTY, l(7), l(3)).makeProcessorDefinition().asProcessor();
|
||||
Processor ba = new Add(EMPTY, l(7), l(3)).makeProcessorDefinition().asProcessor();
|
||||
assertEquals(10, ba.process(null));
|
||||
}
|
||||
|
||||
public void testSub() {
|
||||
BinaryArithmeticProcessor ba = new Sub(EMPTY, l(7), l(3)).makeProcessorDefinition().asProcessor();
|
||||
Processor ba = new Sub(EMPTY, l(7), l(3)).makeProcessorDefinition().asProcessor();
|
||||
assertEquals(4, ba.process(null));
|
||||
}
|
||||
|
||||
public void testMul() {
|
||||
BinaryArithmeticProcessor ba = new Mul(EMPTY, l(7), l(3)).makeProcessorDefinition().asProcessor();
|
||||
Processor ba = new Mul(EMPTY, l(7), l(3)).makeProcessorDefinition().asProcessor();
|
||||
assertEquals(21, ba.process(null));
|
||||
}
|
||||
|
||||
public void testDiv() {
|
||||
BinaryArithmeticProcessor ba = new Div(EMPTY, l(7), l(3)).makeProcessorDefinition().asProcessor();
|
||||
Processor ba = new Div(EMPTY, l(7), l(3)).makeProcessorDefinition().asProcessor();
|
||||
assertEquals(2, ((Number) ba.process(null)).longValue());
|
||||
ba = new Div(EMPTY, l((double) 7), l(3)).makeProcessorDefinition().asProcessor();
|
||||
assertEquals(2.33, ((Number) ba.process(null)).doubleValue(), 0.01d);
|
||||
}
|
||||
|
||||
public void testMod() {
|
||||
BinaryArithmeticProcessor ba = new Mod(EMPTY, l(7), l(3)).makeProcessorDefinition().asProcessor();
|
||||
Processor ba = new Mod(EMPTY, l(7), l(3)).makeProcessorDefinition().asProcessor();
|
||||
assertEquals(1, ba.process(null));
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ package org.elasticsearch.xpack.sql.expression.function.scalar.math;
|
|||
|
||||
import org.elasticsearch.common.io.stream.Writeable.Reader;
|
||||
import org.elasticsearch.test.AbstractWireSerializingTestCase;
|
||||
import org.elasticsearch.xpack.sql.SqlIllegalArgumentException;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.math.MathProcessor.MathOperation;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -34,7 +35,6 @@ public class MathFunctionProcessorTests extends AbstractWireSerializingTestCase<
|
|||
public void testApply() {
|
||||
MathProcessor proc = new MathProcessor(MathOperation.E);
|
||||
assertEquals(Math.E, proc.process(null));
|
||||
assertEquals(Math.E, proc.process("cat"));
|
||||
assertEquals(Math.E, proc.process(Math.PI));
|
||||
|
||||
proc = new MathProcessor(MathOperation.SQRT);
|
||||
|
@ -42,4 +42,11 @@ public class MathFunctionProcessorTests extends AbstractWireSerializingTestCase<
|
|||
assertEquals(3.0, (double) proc.process(9d), 0);
|
||||
assertEquals(1.77, (double) proc.process(3.14), 0.01);
|
||||
}
|
||||
|
||||
public void testNumberCheck() {
|
||||
MathProcessor proc = new MathProcessor(MathOperation.E);
|
||||
SqlIllegalArgumentException siae = expectThrows(SqlIllegalArgumentException.class, () -> proc.process("string"));
|
||||
assertEquals("A number is required; received [string]", siae.getMessage());
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ ABS |SCALAR
|
|||
ACOS |SCALAR
|
||||
ASIN |SCALAR
|
||||
ATAN |SCALAR
|
||||
ATAN2 |SCALAR
|
||||
CBRT |SCALAR
|
||||
CEIL |SCALAR
|
||||
COS |SCALAR
|
||||
|
@ -53,7 +54,9 @@ EXPM1 |SCALAR
|
|||
FLOOR |SCALAR
|
||||
LOG |SCALAR
|
||||
LOG10 |SCALAR
|
||||
MOD |SCALAR
|
||||
PI |SCALAR
|
||||
POWER |SCALAR
|
||||
RADIANS |SCALAR
|
||||
ROUND |SCALAR
|
||||
SIN |SCALAR
|
||||
|
@ -80,6 +83,7 @@ ABS |SCALAR
|
|||
ACOS |SCALAR
|
||||
ASIN |SCALAR
|
||||
ATAN |SCALAR
|
||||
ATAN2 |SCALAR
|
||||
;
|
||||
|
||||
showFunctionsWithPatternChar
|
||||
|
|
|
@ -112,3 +112,15 @@ mathConstantPI
|
|||
SELECT ABS(emp_no) m, PI() as pi, first_name FROM "test_emp" WHERE emp_no < 10010 ORDER BY emp_no;
|
||||
mathConstant
|
||||
SELECT 5 + 2 * 3 / 2 % 2 AS c, PI() as e, first_name FROM "test_emp" WHERE emp_no < 10010 ORDER BY emp_no;
|
||||
|
||||
//
|
||||
// binary functions
|
||||
//
|
||||
mathATan2
|
||||
// tag::atan2
|
||||
SELECT ATAN2(emp_no, emp_no) m, first_name FROM "test_emp" WHERE emp_no < 10010 ORDER BY emp_no;
|
||||
// end::atan2
|
||||
mathPower
|
||||
// tag::power
|
||||
SELECT POWER(emp_no, 2) m, first_name FROM "test_emp" WHERE emp_no < 10010 ORDER BY emp_no;
|
||||
// end::power
|
||||
|
|
Loading…
Reference in New Issue