diff --git a/core/src/main/java/org/apache/druid/math/expr/Function.java b/core/src/main/java/org/apache/druid/math/expr/Function.java index 24247558c133..2b03bfc10380 100644 --- a/core/src/main/java/org/apache/druid/math/expr/Function.java +++ b/core/src/main/java/org/apache/druid/math/expr/Function.java @@ -122,6 +122,27 @@ protected ExprEval eval(double x, double y) } } + class Pi implements Function + { + private static final double PI = Math.PI; + + @Override + public String name() + { + return "pi"; + } + + @Override + public ExprEval apply(List args, Expr.ObjectBinding bindings) + { + if (args.size() >= 1) { + throw new IAE("Function[%s] needs 0 argument", name()); + } + + return ExprEval.of(PI); + } + } + class Abs extends SingleParamMath { @Override @@ -248,6 +269,22 @@ protected ExprEval eval(double param) } } + class Cot extends SingleParamMath + { + @Override + public String name() + { + return "cot"; + } + + @Override + protected ExprEval eval(double param) + { + return ExprEval.of(Math.cos(param) / Math.sin(param)); + } + } + + class Div extends DoubleParamMath { @Override diff --git a/docs/content/misc/math-expr.md b/docs/content/misc/math-expr.md index ff749eae9433..a1166828c86c 100644 --- a/docs/content/misc/math-expr.md +++ b/docs/content/misc/math-expr.md @@ -109,6 +109,7 @@ See javadoc of java.lang.Math for detailed explanation for each function. |copysign|copysign(x) would return the first floating-point argument with the sign of the second floating-point argument| |cos|cos(x) would return the trigonometric cosine of x| |cosh|cosh(x) would return the hyperbolic cosine of x| +|cot|cot(x) would return the trigonometric cotangent of an angle x| |div|div(x,y) is integer division of x by y| |exp|exp(x) would return Euler's number raised to the power of x| |expm1|expm1(x) would return e^x-1| @@ -122,6 +123,7 @@ See javadoc of java.lang.Math for detailed explanation for each function. |min|min(x, y) would return the smaller of two values| |nextafter|nextafter(x, y) would return the floating-point number adjacent to the x in the direction of the y| |nextUp|nextUp(x) would return the floating-point value adjacent to x in the direction of positive infinity| +|pi|pi would return the constant value of the π | |pow|pow(x, y) would return the value of the x raised to the power of y| |remainder|remainder(x, y) would return the remainder operation on two arguments as prescribed by the IEEE 754 standard| |rint|rint(x) would return value that is closest in value to x and is equal to a mathematical integer| diff --git a/docs/content/querying/sql.md b/docs/content/querying/sql.md index 31e0109a9a71..d708850f0947 100644 --- a/docs/content/querying/sql.md +++ b/docs/content/querying/sql.md @@ -147,6 +147,14 @@ Numeric functions will return 64 bit integers or 64 bit floats, depending on the |`x * y`|Multiplication.| |`x / y`|Division.| |`MOD(x, y)`|Modulo (remainder of x divided by y).| +|`SIN(expr)`|Trigonometric sine of an angle expr.| +|`COS(expr)`|Trigonometric cosine of an angle expr.| +|`TAN(expr)`|Trigonometric tangent of an angle expr.| +|`COT(expr)`|Trigonometric cotangent of an angle expr.| +|`ASIN(expr)`|Arc sine of expr.| +|`ACOS(expr)`|Arc cosine of expr.| +|`ATAN(expr)`|Arc tangent of expr.| +|`ATAN2(y, x)`|Angle theta from the conversion of rectangular coordinates (x, y) to polar * coordinates (r, theta).| ### String functions diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/planner/DruidOperatorTable.java b/sql/src/main/java/org/apache/druid/sql/calcite/planner/DruidOperatorTable.java index 601303dd2d1f..b355095da741 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/planner/DruidOperatorTable.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/planner/DruidOperatorTable.java @@ -121,6 +121,15 @@ public class DruidOperatorTable implements SqlOperatorTable .add(new DirectOperatorConversion(SqlStdOperatorTable.REPLACE, "replace")) .add(new DirectOperatorConversion(SqlStdOperatorTable.SQRT, "sqrt")) .add(new DirectOperatorConversion(SqlStdOperatorTable.UPPER, "upper")) + .add(new DirectOperatorConversion(SqlStdOperatorTable.PI, "pi")) + .add(new DirectOperatorConversion(SqlStdOperatorTable.SIN, "sin")) + .add(new DirectOperatorConversion(SqlStdOperatorTable.COS, "cos")) + .add(new DirectOperatorConversion(SqlStdOperatorTable.TAN, "tan")) + .add(new DirectOperatorConversion(SqlStdOperatorTable.COT, "cot")) + .add(new DirectOperatorConversion(SqlStdOperatorTable.ASIN, "asin")) + .add(new DirectOperatorConversion(SqlStdOperatorTable.ACOS, "acos")) + .add(new DirectOperatorConversion(SqlStdOperatorTable.ATAN, "atan")) + .add(new DirectOperatorConversion(SqlStdOperatorTable.ATAN2, "atan2")) .add(new UnaryPrefixOperatorConversion(SqlStdOperatorTable.NOT, "!")) .add(new UnaryPrefixOperatorConversion(SqlStdOperatorTable.UNARY_MINUS, "-")) .add(new UnaryFunctionOperatorConversion(SqlStdOperatorTable.IS_NULL, "isnull")) diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java index ad6030fb8d73..839d8927ad9f 100644 --- a/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java +++ b/sql/src/test/java/org/apache/druid/sql/calcite/CalciteQueryTest.java @@ -7598,4 +7598,51 @@ public void testFilterLongDimension() throws Exception ) ); } + + @Test + public void testTrigonometricFunction() throws Exception + { + testQuery( + PLANNER_CONFIG_DEFAULT, + QUERY_CONTEXT_DONT_SKIP_EMPTY_BUCKETS, + "SELECT exp(count(*)) + 10, sin(pi / 6), cos(pi / 6), tan(pi / 6), cot(pi / 6)," + + "asin(exp(count(*)) / 2), acos(exp(count(*)) / 2), atan(exp(count(*)) / 2), atan2(exp(count(*)), 1) " + + "FROM druid.foo WHERE dim2 = 0", + CalciteTests.REGULAR_USER_AUTH_RESULT, + ImmutableList.of(Druids.newTimeseriesQueryBuilder() + .dataSource(CalciteTests.DATASOURCE1) + .intervals(querySegmentSpec(Filtration.eternity())) + .filters(selector("dim2", "0", null)) + .granularity(Granularities.ALL) + .aggregators(aggregators( + new CountAggregatorFactory("a0") + )) + .postAggregators( + expresionPostAgg("p0", "(exp(\"a0\") + 10)"), + expresionPostAgg("p1", "sin((pi() / 6))"), + expresionPostAgg("p2", "cos((pi() / 6))"), + expresionPostAgg("p3", "tan((pi() / 6))"), + expresionPostAgg("p4", "cot((pi() / 6))"), + expresionPostAgg("p5", "asin((exp(\"a0\") / 2))"), + expresionPostAgg("p6", "acos((exp(\"a0\") / 2))"), + expresionPostAgg("p7", "atan((exp(\"a0\") / 2))"), + expresionPostAgg("p8", "atan2(exp(\"a0\"),1)") + ) + .context(QUERY_CONTEXT_DONT_SKIP_EMPTY_BUCKETS) + .build()), + ImmutableList.of( + new Object[]{ + 11.0, + Math.sin(Math.PI / 6), + Math.cos(Math.PI / 6), + Math.tan(Math.PI / 6), + Math.cos(Math.PI / 6) / Math.sin(Math.PI / 6), + Math.asin(0.5), + Math.acos(0.5), + Math.atan(0.5), + Math.atan2(1, 1) + } + ) + ); + } }