mirror of
https://github.com/honeymoose/OpenSearch.git
synced 2025-02-17 10:25:15 +00:00
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.Ceil;
|
||||||
import org.elasticsearch.xpack.sql.expression.function.scalar.math.Cos;
|
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.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.Degrees;
|
||||||
import org.elasticsearch.xpack.sql.expression.function.scalar.math.E;
|
import org.elasticsearch.xpack.sql.expression.function.scalar.math.E;
|
||||||
import org.elasticsearch.xpack.sql.expression.function.scalar.math.Exp;
|
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.Pi;
|
||||||
import org.elasticsearch.xpack.sql.expression.function.scalar.math.Power;
|
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.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.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.Sin;
|
||||||
import org.elasticsearch.xpack.sql.expression.function.scalar.math.Sinh;
|
import org.elasticsearch.xpack.sql.expression.function.scalar.math.Sinh;
|
||||||
import org.elasticsearch.xpack.sql.expression.function.scalar.math.Sqrt;
|
import org.elasticsearch.xpack.sql.expression.function.scalar.math.Sqrt;
|
||||||
@ -109,9 +112,10 @@ public class FunctionRegistry {
|
|||||||
def(ATan.class, ATan::new),
|
def(ATan.class, ATan::new),
|
||||||
def(ATan2.class, ATan2::new),
|
def(ATan2.class, ATan2::new),
|
||||||
def(Cbrt.class, Cbrt::new),
|
def(Cbrt.class, Cbrt::new),
|
||||||
def(Ceil.class, Ceil::new),
|
def(Ceil.class, Ceil::new, "CEILING"),
|
||||||
def(Cos.class, Cos::new),
|
def(Cos.class, Cos::new),
|
||||||
def(Cosh.class, Cosh::new),
|
def(Cosh.class, Cosh::new),
|
||||||
|
def(Cot.class, Cot::new),
|
||||||
def(Degrees.class, Degrees::new),
|
def(Degrees.class, Degrees::new),
|
||||||
def(E.class, E::new),
|
def(E.class, E::new),
|
||||||
def(Exp.class, Exp::new),
|
def(Exp.class, Exp::new),
|
||||||
@ -124,7 +128,9 @@ public class FunctionRegistry {
|
|||||||
def(Pi.class, Pi::new),
|
def(Pi.class, Pi::new),
|
||||||
def(Power.class, Power::new),
|
def(Power.class, Power::new),
|
||||||
def(Radians.class, Radians::new),
|
def(Radians.class, Radians::new),
|
||||||
|
def(Random.class, Random::new, "RAND"),
|
||||||
def(Round.class, Round::new),
|
def(Round.class, Round::new),
|
||||||
|
def(Sign.class, Sign::new, "SIGNUM"),
|
||||||
def(Sin.class, Sin::new),
|
def(Sin.class, Sin::new),
|
||||||
def(Sinh.class, Sinh::new),
|
def(Sinh.class, Sinh::new),
|
||||||
def(Sqrt.class, Sqrt::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;
|
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.StreamInput;
|
||||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||||
import org.elasticsearch.xpack.sql.SqlIllegalArgumentException;
|
import org.elasticsearch.xpack.sql.SqlIllegalArgumentException;
|
||||||
import org.elasticsearch.xpack.sql.expression.function.scalar.processor.runtime.Processor;
|
import org.elasticsearch.xpack.sql.expression.function.scalar.processor.runtime.Processor;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Random;
|
||||||
import java.util.function.DoubleFunction;
|
import java.util.function.DoubleFunction;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
@ -26,6 +28,7 @@ public class MathProcessor implements Processor {
|
|||||||
return Math.abs(((Double) l).doubleValue());
|
return Math.abs(((Double) l).doubleValue());
|
||||||
}
|
}
|
||||||
long lo = ((Number) l).longValue();
|
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;
|
return lo >= 0 ? lo : lo == Long.MIN_VALUE ? Long.MAX_VALUE : -lo;
|
||||||
}),
|
}),
|
||||||
|
|
||||||
@ -36,6 +39,7 @@ public class MathProcessor implements Processor {
|
|||||||
CEIL(Math::ceil),
|
CEIL(Math::ceil),
|
||||||
COS(Math::cos),
|
COS(Math::cos),
|
||||||
COSH(Math::cosh),
|
COSH(Math::cosh),
|
||||||
|
COT((Object l) -> 1.0d / Math.tan(((Number) l).doubleValue())),
|
||||||
DEGREES(Math::toDegrees),
|
DEGREES(Math::toDegrees),
|
||||||
E(() -> Math.E),
|
E(() -> Math.E),
|
||||||
EXP(Math::exp),
|
EXP(Math::exp),
|
||||||
@ -45,7 +49,11 @@ public class MathProcessor implements Processor {
|
|||||||
LOG10(Math::log10),
|
LOG10(Math::log10),
|
||||||
PI(() -> Math.PI),
|
PI(() -> Math.PI),
|
||||||
RADIANS(Math::toRadians),
|
RADIANS(Math::toRadians),
|
||||||
|
RANDOM((Object l) -> l != null ?
|
||||||
|
new Random(((Number) l).longValue()).nextDouble() :
|
||||||
|
Randomness.get().nextDouble(), true),
|
||||||
ROUND((DoubleFunction<Object>) Math::round),
|
ROUND((DoubleFunction<Object>) Math::round),
|
||||||
|
SIGN((DoubleFunction<Object>) Math::signum),
|
||||||
SIN(Math::sin),
|
SIN(Math::sin),
|
||||||
SINH(Math::sinh),
|
SINH(Math::sinh),
|
||||||
SQRT(Math::sqrt),
|
SQRT(Math::sqrt),
|
||||||
@ -54,7 +62,20 @@ public class MathProcessor implements Processor {
|
|||||||
private final Function<Object, Object> apply;
|
private final Function<Object, Object> apply;
|
||||||
|
|
||||||
MathOperation(Function<Object, Object> apply) {
|
MathOperation(Function<Object, Object> apply) {
|
||||||
this.apply = l -> l == null ? null : apply.apply(l);
|
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) {
|
MathOperation(DoubleFunction<Object> apply) {
|
||||||
|
@ -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() {
|
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() {
|
public void testMissingColumnInGroupBy() {
|
||||||
|
@ -49,4 +49,10 @@ public class MathFunctionProcessorTests extends AbstractWireSerializingTestCase<
|
|||||||
assertEquals("A number is required; received [string]", siae.getMessage());
|
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
|
ATAN2 |SCALAR
|
||||||
CBRT |SCALAR
|
CBRT |SCALAR
|
||||||
CEIL |SCALAR
|
CEIL |SCALAR
|
||||||
|
CEILING |SCALAR
|
||||||
COS |SCALAR
|
COS |SCALAR
|
||||||
COSH |SCALAR
|
COSH |SCALAR
|
||||||
|
COT |SCALAR
|
||||||
DEGREES |SCALAR
|
DEGREES |SCALAR
|
||||||
E |SCALAR
|
E |SCALAR
|
||||||
EXP |SCALAR
|
EXP |SCALAR
|
||||||
@ -58,7 +60,11 @@ MOD |SCALAR
|
|||||||
PI |SCALAR
|
PI |SCALAR
|
||||||
POWER |SCALAR
|
POWER |SCALAR
|
||||||
RADIANS |SCALAR
|
RADIANS |SCALAR
|
||||||
|
RANDOM |SCALAR
|
||||||
|
RAND |SCALAR
|
||||||
ROUND |SCALAR
|
ROUND |SCALAR
|
||||||
|
SIGN |SCALAR
|
||||||
|
SIGNUM |SCALAR
|
||||||
SIN |SCALAR
|
SIN |SCALAR
|
||||||
SINH |SCALAR
|
SINH |SCALAR
|
||||||
SQRT |SCALAR
|
SQRT |SCALAR
|
||||||
|
@ -31,6 +31,10 @@ mathCosh
|
|||||||
// tag::cosh
|
// tag::cosh
|
||||||
SELECT COSH(emp_no) m, first_name FROM "test_emp" WHERE emp_no < 10010 ORDER BY emp_no;
|
SELECT COSH(emp_no) m, first_name FROM "test_emp" WHERE emp_no < 10010 ORDER BY emp_no;
|
||||||
// end::cosh
|
// 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
|
mathDegrees
|
||||||
// tag::degrees
|
// tag::degrees
|
||||||
SELECT DEGREES(emp_no) m, first_name FROM "test_emp" WHERE emp_no < 10010 ORDER BY emp_no;
|
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
|
// end::radians
|
||||||
mathRound
|
mathRound
|
||||||
SELECT CAST(ROUND(emp_no) AS INT) m, first_name FROM "test_emp" WHERE emp_no < 10010 ORDER BY emp_no;
|
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
|
mathSin
|
||||||
// tag::sin
|
// tag::sin
|
||||||
SELECT SIN(emp_no) m, first_name FROM "test_emp" WHERE emp_no < 10010 ORDER BY emp_no;
|
SELECT SIN(emp_no) m, first_name FROM "test_emp" WHERE emp_no < 10010 ORDER BY emp_no;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user