SQL: implement COT, RANDOM, SIGN math functions (elastic/x-pack-elasticsearch#4394)
Add a number of missing math functions Original commit: elastic/x-pack-elasticsearch@a26d9d2454
This commit is contained in:
parent
043a877a31
commit
03ecbc0ccb
|
@ -40,6 +40,7 @@ import org.elasticsearch.xpack.sql.expression.function.scalar.math.Cbrt;
|
|||
import org.elasticsearch.xpack.sql.expression.function.scalar.math.Ceil;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.math.Cos;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.math.Cosh;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.math.Cot;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.math.Degrees;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.math.E;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.math.Exp;
|
||||
|
@ -50,7 +51,9 @@ 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.Random;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.math.Round;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.math.Sign;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.math.Sin;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.math.Sinh;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.math.Sqrt;
|
||||
|
@ -109,9 +112,10 @@ public class FunctionRegistry {
|
|||
def(ATan.class, ATan::new),
|
||||
def(ATan2.class, ATan2::new),
|
||||
def(Cbrt.class, Cbrt::new),
|
||||
def(Ceil.class, Ceil::new),
|
||||
def(Ceil.class, Ceil::new, "CEILING"),
|
||||
def(Cos.class, Cos::new),
|
||||
def(Cosh.class, Cosh::new),
|
||||
def(Cot.class, Cot::new),
|
||||
def(Degrees.class, Degrees::new),
|
||||
def(E.class, E::new),
|
||||
def(Exp.class, Exp::new),
|
||||
|
@ -124,7 +128,9 @@ public class FunctionRegistry {
|
|||
def(Pi.class, Pi::new),
|
||||
def(Power.class, Power::new),
|
||||
def(Radians.class, Radians::new),
|
||||
def(Random.class, Random::new, "RAND"),
|
||||
def(Round.class, Round::new),
|
||||
def(Sign.class, Sign::new, "SIGNUM"),
|
||||
def(Sin.class, Sin::new),
|
||||
def(Sinh.class, Sinh::new),
|
||||
def(Sqrt.class, Sqrt::new),
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* 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.MathProcessor.MathOperation;
|
||||
import org.elasticsearch.xpack.sql.tree.Location;
|
||||
import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import static java.lang.String.format;
|
||||
|
||||
/**
|
||||
* <a href="https://en.wikipedia.org/wiki/Trigonometric_functions#Cosecant,_secant,_and_cotangent">Cotangent</a>
|
||||
* function.
|
||||
*/
|
||||
public class Cot extends MathFunction {
|
||||
public Cot(Location location, Expression field) {
|
||||
super(location, field);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NodeInfo<Cot> info() {
|
||||
return NodeInfo.create(this, Cot::new, field());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Cot replaceChild(Expression newChild) {
|
||||
return new Cot(location(), newChild);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String formatScript(String template) {
|
||||
return super.formatScript(format(Locale.ROOT, "1.0 / Math.tan(%s)", template));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MathOperation operation() {
|
||||
return MathOperation.COT;
|
||||
}
|
||||
}
|
|
@ -5,12 +5,14 @@
|
|||
*/
|
||||
package org.elasticsearch.xpack.sql.expression.function.scalar.math;
|
||||
|
||||
import org.elasticsearch.common.Randomness;
|
||||
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;
|
||||
import java.util.Random;
|
||||
import java.util.function.DoubleFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
@ -26,6 +28,7 @@ public class MathProcessor implements Processor {
|
|||
return Math.abs(((Double) l).doubleValue());
|
||||
}
|
||||
long lo = ((Number) l).longValue();
|
||||
//handles the corner-case of Long.MIN_VALUE
|
||||
return lo >= 0 ? lo : lo == Long.MIN_VALUE ? Long.MAX_VALUE : -lo;
|
||||
}),
|
||||
|
||||
|
@ -36,6 +39,7 @@ public class MathProcessor implements Processor {
|
|||
CEIL(Math::ceil),
|
||||
COS(Math::cos),
|
||||
COSH(Math::cosh),
|
||||
COT((Object l) -> 1.0d / Math.tan(((Number) l).doubleValue())),
|
||||
DEGREES(Math::toDegrees),
|
||||
E(() -> Math.E),
|
||||
EXP(Math::exp),
|
||||
|
@ -45,7 +49,11 @@ public class MathProcessor implements Processor {
|
|||
LOG10(Math::log10),
|
||||
PI(() -> Math.PI),
|
||||
RADIANS(Math::toRadians),
|
||||
RANDOM((Object l) -> l != null ?
|
||||
new Random(((Number) l).longValue()).nextDouble() :
|
||||
Randomness.get().nextDouble(), true),
|
||||
ROUND((DoubleFunction<Object>) Math::round),
|
||||
SIGN((DoubleFunction<Object>) Math::signum),
|
||||
SIN(Math::sin),
|
||||
SINH(Math::sinh),
|
||||
SQRT(Math::sqrt),
|
||||
|
@ -54,8 +62,21 @@ public class MathProcessor implements Processor {
|
|||
private final Function<Object, Object> apply;
|
||||
|
||||
MathOperation(Function<Object, Object> apply) {
|
||||
this(apply, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for nulls around the given function.
|
||||
* If true, nulls are passed through, otherwise the function is short-circuited
|
||||
* and null returned.
|
||||
*/
|
||||
MathOperation(Function<Object, Object> apply, boolean nullAware) {
|
||||
if (nullAware) {
|
||||
this.apply = apply;
|
||||
} else {
|
||||
this.apply = l -> l == null ? null : apply.apply(l);
|
||||
}
|
||||
}
|
||||
|
||||
MathOperation(DoubleFunction<Object> apply) {
|
||||
this.apply = (Object l) -> l == null ? null : apply.apply(((Number) l).doubleValue());
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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.MathProcessor.MathOperation;
|
||||
import org.elasticsearch.xpack.sql.tree.Location;
|
||||
import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import static java.lang.String.format;
|
||||
|
||||
/**
|
||||
* Returns a random double (using the given seed).
|
||||
*/
|
||||
public class Random extends MathFunction {
|
||||
|
||||
public Random(Location location, Expression field) {
|
||||
super(location, field);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NodeInfo<Random> info() {
|
||||
return NodeInfo.create(this, Random::new, field());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Random replaceChild(Expression newChild) {
|
||||
return new Random(location(), newChild);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String formatScript(String template) {
|
||||
//TODO: Painless script uses Random since Randomness is not whitelisted
|
||||
return super.formatScript(
|
||||
format(Locale.ROOT, "%s != null ? new Random((long) %s).nextDouble() : Math.random()", template, template));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MathOperation operation() {
|
||||
return MathOperation.RANDOM;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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.MathProcessor.MathOperation;
|
||||
import org.elasticsearch.xpack.sql.tree.Location;
|
||||
import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
||||
import org.elasticsearch.xpack.sql.type.DataType;
|
||||
|
||||
/**
|
||||
* Returns the sign of the given expression:
|
||||
* <ul>
|
||||
* <li>-1 if it is negative</li>
|
||||
* <li> 0 if it is zero</li>
|
||||
* <li>+1 if it is positive</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class Sign extends MathFunction {
|
||||
public Sign(Location location, Expression field) {
|
||||
super(location, field);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NodeInfo<Sign> info() {
|
||||
return NodeInfo.create(this, Sign::new, field());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Sign replaceChild(Expression newChild) {
|
||||
return new Sign(location(), newChild);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String mathFunction() {
|
||||
return "signum";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MathOperation operation() {
|
||||
return MathOperation.SIGN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType dataType() {
|
||||
return DataType.INTEGER;
|
||||
}
|
||||
}
|
|
@ -55,7 +55,7 @@ public class VerifierErrorMessagesTests extends ESTestCase {
|
|||
}
|
||||
|
||||
public void testMisspelledFunction() {
|
||||
assertEquals("1:8: Unknown function [COONT], did you mean [COUNT]?", verify("SELECT COONT(bool) FROM test"));
|
||||
assertEquals("1:8: Unknown function [COONT], did you mean any of [COUNT, COT]?", verify("SELECT COONT(bool) FROM test"));
|
||||
}
|
||||
|
||||
public void testMissingColumnInGroupBy() {
|
||||
|
|
|
@ -49,4 +49,10 @@ public class MathFunctionProcessorTests extends AbstractWireSerializingTestCase<
|
|||
assertEquals("A number is required; received [string]", siae.getMessage());
|
||||
|
||||
}
|
||||
|
||||
public void testRandom() {
|
||||
MathProcessor proc = new MathProcessor(MathOperation.RANDOM);
|
||||
assertNotNull(proc.process(null));
|
||||
assertNotNull(proc.process(randomLong()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,8 +45,10 @@ ATAN |SCALAR
|
|||
ATAN2 |SCALAR
|
||||
CBRT |SCALAR
|
||||
CEIL |SCALAR
|
||||
CEILING |SCALAR
|
||||
COS |SCALAR
|
||||
COSH |SCALAR
|
||||
COT |SCALAR
|
||||
DEGREES |SCALAR
|
||||
E |SCALAR
|
||||
EXP |SCALAR
|
||||
|
@ -58,7 +60,11 @@ MOD |SCALAR
|
|||
PI |SCALAR
|
||||
POWER |SCALAR
|
||||
RADIANS |SCALAR
|
||||
RANDOM |SCALAR
|
||||
RAND |SCALAR
|
||||
ROUND |SCALAR
|
||||
SIGN |SCALAR
|
||||
SIGNUM |SCALAR
|
||||
SIN |SCALAR
|
||||
SINH |SCALAR
|
||||
SQRT |SCALAR
|
||||
|
|
|
@ -31,6 +31,10 @@ mathCosh
|
|||
// tag::cosh
|
||||
SELECT COSH(emp_no) m, first_name FROM "test_emp" WHERE emp_no < 10010 ORDER BY emp_no;
|
||||
// end::cosh
|
||||
mathCot
|
||||
// tag::cot
|
||||
SELECT COT(emp_no) m, first_name FROM "test_emp" WHERE emp_no < 10010 ORDER BY emp_no;
|
||||
// end::cot
|
||||
mathDegrees
|
||||
// tag::degrees
|
||||
SELECT DEGREES(emp_no) m, first_name FROM "test_emp" WHERE emp_no < 10010 ORDER BY emp_no;
|
||||
|
@ -59,6 +63,10 @@ SELECT RADIANS(emp_no) m, first_name FROM "test_emp" WHERE emp_no < 10010 ORDER
|
|||
// end::radians
|
||||
mathRound
|
||||
SELECT CAST(ROUND(emp_no) AS INT) m, first_name FROM "test_emp" WHERE emp_no < 10010 ORDER BY emp_no;
|
||||
mathSign
|
||||
// tag::sign
|
||||
SELECT SIGN(emp_no) m, first_name FROM "test_emp" WHERE emp_no < 10010 ORDER BY emp_no;
|
||||
// end::sign
|
||||
mathSin
|
||||
// tag::sin
|
||||
SELECT SIN(emp_no) m, first_name FROM "test_emp" WHERE emp_no < 10010 ORDER BY emp_no;
|
||||
|
|
Loading…
Reference in New Issue