Skip to content

Commit 03ecbc0

Browse files
authored
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@a26d9d2
1 parent 043a877 commit 03ecbc0

File tree

9 files changed

+193
-3
lines changed

9 files changed

+193
-3
lines changed

plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/FunctionRegistry.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import org.elasticsearch.xpack.sql.expression.function.scalar.math.Ceil;
4141
import org.elasticsearch.xpack.sql.expression.function.scalar.math.Cos;
4242
import org.elasticsearch.xpack.sql.expression.function.scalar.math.Cosh;
43+
import org.elasticsearch.xpack.sql.expression.function.scalar.math.Cot;
4344
import org.elasticsearch.xpack.sql.expression.function.scalar.math.Degrees;
4445
import org.elasticsearch.xpack.sql.expression.function.scalar.math.E;
4546
import org.elasticsearch.xpack.sql.expression.function.scalar.math.Exp;
@@ -50,7 +51,9 @@
5051
import org.elasticsearch.xpack.sql.expression.function.scalar.math.Pi;
5152
import org.elasticsearch.xpack.sql.expression.function.scalar.math.Power;
5253
import org.elasticsearch.xpack.sql.expression.function.scalar.math.Radians;
54+
import org.elasticsearch.xpack.sql.expression.function.scalar.math.Random;
5355
import org.elasticsearch.xpack.sql.expression.function.scalar.math.Round;
56+
import org.elasticsearch.xpack.sql.expression.function.scalar.math.Sign;
5457
import org.elasticsearch.xpack.sql.expression.function.scalar.math.Sin;
5558
import org.elasticsearch.xpack.sql.expression.function.scalar.math.Sinh;
5659
import org.elasticsearch.xpack.sql.expression.function.scalar.math.Sqrt;
@@ -109,9 +112,10 @@ public class FunctionRegistry {
109112
def(ATan.class, ATan::new),
110113
def(ATan2.class, ATan2::new),
111114
def(Cbrt.class, Cbrt::new),
112-
def(Ceil.class, Ceil::new),
115+
def(Ceil.class, Ceil::new, "CEILING"),
113116
def(Cos.class, Cos::new),
114117
def(Cosh.class, Cosh::new),
118+
def(Cot.class, Cot::new),
115119
def(Degrees.class, Degrees::new),
116120
def(E.class, E::new),
117121
def(Exp.class, Exp::new),
@@ -124,7 +128,9 @@ public class FunctionRegistry {
124128
def(Pi.class, Pi::new),
125129
def(Power.class, Power::new),
126130
def(Radians.class, Radians::new),
131+
def(Random.class, Random::new, "RAND"),
127132
def(Round.class, Round::new),
133+
def(Sign.class, Sign::new, "SIGNUM"),
128134
def(Sin.class, Sin::new),
129135
def(Sinh.class, Sinh::new),
130136
def(Sqrt.class, Sqrt::new),
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
package org.elasticsearch.xpack.sql.expression.function.scalar.math;
7+
8+
import org.elasticsearch.xpack.sql.expression.Expression;
9+
import org.elasticsearch.xpack.sql.expression.function.scalar.math.MathProcessor.MathOperation;
10+
import org.elasticsearch.xpack.sql.tree.Location;
11+
import org.elasticsearch.xpack.sql.tree.NodeInfo;
12+
13+
import java.util.Locale;
14+
15+
import static java.lang.String.format;
16+
17+
/**
18+
* <a href="https://en.wikipedia.org/wiki/Trigonometric_functions#Cosecant,_secant,_and_cotangent">Cotangent</a>
19+
* function.
20+
*/
21+
public class Cot extends MathFunction {
22+
public Cot(Location location, Expression field) {
23+
super(location, field);
24+
}
25+
26+
@Override
27+
protected NodeInfo<Cot> info() {
28+
return NodeInfo.create(this, Cot::new, field());
29+
}
30+
31+
@Override
32+
protected Cot replaceChild(Expression newChild) {
33+
return new Cot(location(), newChild);
34+
}
35+
36+
@Override
37+
protected String formatScript(String template) {
38+
return super.formatScript(format(Locale.ROOT, "1.0 / Math.tan(%s)", template));
39+
}
40+
41+
@Override
42+
protected MathOperation operation() {
43+
return MathOperation.COT;
44+
}
45+
}

plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/MathProcessor.java

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@
55
*/
66
package org.elasticsearch.xpack.sql.expression.function.scalar.math;
77

8+
import org.elasticsearch.common.Randomness;
89
import org.elasticsearch.common.io.stream.StreamInput;
910
import org.elasticsearch.common.io.stream.StreamOutput;
1011
import org.elasticsearch.xpack.sql.SqlIllegalArgumentException;
1112
import org.elasticsearch.xpack.sql.expression.function.scalar.processor.runtime.Processor;
1213

1314
import java.io.IOException;
15+
import java.util.Random;
1416
import java.util.function.DoubleFunction;
1517
import java.util.function.Function;
1618
import java.util.function.Supplier;
@@ -26,6 +28,7 @@ public enum MathOperation {
2628
return Math.abs(((Double) l).doubleValue());
2729
}
2830
long lo = ((Number) l).longValue();
31+
//handles the corner-case of Long.MIN_VALUE
2932
return lo >= 0 ? lo : lo == Long.MIN_VALUE ? Long.MAX_VALUE : -lo;
3033
}),
3134

@@ -36,6 +39,7 @@ public enum MathOperation {
3639
CEIL(Math::ceil),
3740
COS(Math::cos),
3841
COSH(Math::cosh),
42+
COT((Object l) -> 1.0d / Math.tan(((Number) l).doubleValue())),
3943
DEGREES(Math::toDegrees),
4044
E(() -> Math.E),
4145
EXP(Math::exp),
@@ -45,7 +49,11 @@ public enum MathOperation {
4549
LOG10(Math::log10),
4650
PI(() -> Math.PI),
4751
RADIANS(Math::toRadians),
52+
RANDOM((Object l) -> l != null ?
53+
new Random(((Number) l).longValue()).nextDouble() :
54+
Randomness.get().nextDouble(), true),
4855
ROUND((DoubleFunction<Object>) Math::round),
56+
SIGN((DoubleFunction<Object>) Math::signum),
4957
SIN(Math::sin),
5058
SINH(Math::sinh),
5159
SQRT(Math::sqrt),
@@ -54,7 +62,20 @@ public enum MathOperation {
5462
private final Function<Object, Object> apply;
5563

5664
MathOperation(Function<Object, Object> apply) {
57-
this.apply = l -> l == null ? null : apply.apply(l);
65+
this(apply, false);
66+
}
67+
68+
/**
69+
* Wrapper for nulls around the given function.
70+
* If true, nulls are passed through, otherwise the function is short-circuited
71+
* and null returned.
72+
*/
73+
MathOperation(Function<Object, Object> apply, boolean nullAware) {
74+
if (nullAware) {
75+
this.apply = apply;
76+
} else {
77+
this.apply = l -> l == null ? null : apply.apply(l);
78+
}
5879
}
5980

6081
MathOperation(DoubleFunction<Object> apply) {
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
package org.elasticsearch.xpack.sql.expression.function.scalar.math;
7+
8+
import org.elasticsearch.xpack.sql.expression.Expression;
9+
import org.elasticsearch.xpack.sql.expression.function.scalar.math.MathProcessor.MathOperation;
10+
import org.elasticsearch.xpack.sql.tree.Location;
11+
import org.elasticsearch.xpack.sql.tree.NodeInfo;
12+
13+
import java.util.Locale;
14+
15+
import static java.lang.String.format;
16+
17+
/**
18+
* Returns a random double (using the given seed).
19+
*/
20+
public class Random extends MathFunction {
21+
22+
public Random(Location location, Expression field) {
23+
super(location, field);
24+
}
25+
26+
@Override
27+
protected NodeInfo<Random> info() {
28+
return NodeInfo.create(this, Random::new, field());
29+
}
30+
31+
@Override
32+
protected Random replaceChild(Expression newChild) {
33+
return new Random(location(), newChild);
34+
}
35+
36+
@Override
37+
protected String formatScript(String template) {
38+
//TODO: Painless script uses Random since Randomness is not whitelisted
39+
return super.formatScript(
40+
format(Locale.ROOT, "%s != null ? new Random((long) %s).nextDouble() : Math.random()", template, template));
41+
}
42+
43+
@Override
44+
protected MathOperation operation() {
45+
return MathOperation.RANDOM;
46+
}
47+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
package org.elasticsearch.xpack.sql.expression.function.scalar.math;
7+
8+
import org.elasticsearch.xpack.sql.expression.Expression;
9+
import org.elasticsearch.xpack.sql.expression.function.scalar.math.MathProcessor.MathOperation;
10+
import org.elasticsearch.xpack.sql.tree.Location;
11+
import org.elasticsearch.xpack.sql.tree.NodeInfo;
12+
import org.elasticsearch.xpack.sql.type.DataType;
13+
14+
/**
15+
* Returns the sign of the given expression:
16+
* <ul>
17+
* <li>-1 if it is negative</li>
18+
* <li> 0 if it is zero</li>
19+
* <li>+1 if it is positive</li>
20+
* </ul>
21+
*/
22+
public class Sign extends MathFunction {
23+
public Sign(Location location, Expression field) {
24+
super(location, field);
25+
}
26+
27+
@Override
28+
protected NodeInfo<Sign> info() {
29+
return NodeInfo.create(this, Sign::new, field());
30+
}
31+
32+
@Override
33+
protected Sign replaceChild(Expression newChild) {
34+
return new Sign(location(), newChild);
35+
}
36+
37+
@Override
38+
protected String mathFunction() {
39+
return "signum";
40+
}
41+
42+
@Override
43+
protected MathOperation operation() {
44+
return MathOperation.SIGN;
45+
}
46+
47+
@Override
48+
public DataType dataType() {
49+
return DataType.INTEGER;
50+
}
51+
}

plugin/sql/src/test/java/org/elasticsearch/xpack/sql/analysis/analyzer/VerifierErrorMessagesTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public void testMissingFunction() {
5555
}
5656

5757
public void testMisspelledFunction() {
58-
assertEquals("1:8: Unknown function [COONT], did you mean [COUNT]?", verify("SELECT COONT(bool) FROM test"));
58+
assertEquals("1:8: Unknown function [COONT], did you mean any of [COUNT, COT]?", verify("SELECT COONT(bool) FROM test"));
5959
}
6060

6161
public void testMissingColumnInGroupBy() {

plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/MathFunctionProcessorTests.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,10 @@ public void testNumberCheck() {
4949
assertEquals("A number is required; received [string]", siae.getMessage());
5050

5151
}
52+
53+
public void testRandom() {
54+
MathProcessor proc = new MathProcessor(MathOperation.RANDOM);
55+
assertNotNull(proc.process(null));
56+
assertNotNull(proc.process(randomLong()));
57+
}
5258
}

qa/sql/src/main/resources/command.csv-spec

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,10 @@ ATAN |SCALAR
4545
ATAN2 |SCALAR
4646
CBRT |SCALAR
4747
CEIL |SCALAR
48+
CEILING |SCALAR
4849
COS |SCALAR
4950
COSH |SCALAR
51+
COT |SCALAR
5052
DEGREES |SCALAR
5153
E |SCALAR
5254
EXP |SCALAR
@@ -58,7 +60,11 @@ MOD |SCALAR
5860
PI |SCALAR
5961
POWER |SCALAR
6062
RADIANS |SCALAR
63+
RANDOM |SCALAR
64+
RAND |SCALAR
6165
ROUND |SCALAR
66+
SIGN |SCALAR
67+
SIGNUM |SCALAR
6268
SIN |SCALAR
6369
SINH |SCALAR
6470
SQRT |SCALAR

qa/sql/src/main/resources/math.sql-spec

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ mathCosh
3131
// tag::cosh
3232
SELECT COSH(emp_no) m, first_name FROM "test_emp" WHERE emp_no < 10010 ORDER BY emp_no;
3333
// end::cosh
34+
mathCot
35+
// tag::cot
36+
SELECT COT(emp_no) m, first_name FROM "test_emp" WHERE emp_no < 10010 ORDER BY emp_no;
37+
// end::cot
3438
mathDegrees
3539
// tag::degrees
3640
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
5963
// end::radians
6064
mathRound
6165
SELECT CAST(ROUND(emp_no) AS INT) m, first_name FROM "test_emp" WHERE emp_no < 10010 ORDER BY emp_no;
66+
mathSign
67+
// tag::sign
68+
SELECT SIGN(emp_no) m, first_name FROM "test_emp" WHERE emp_no < 10010 ORDER BY emp_no;
69+
// end::sign
6270
mathSin
6371
// tag::sin
6472
SELECT SIN(emp_no) m, first_name FROM "test_emp" WHERE emp_no < 10010 ORDER BY emp_no;

0 commit comments

Comments
 (0)