Browse Source

Add `PI` and `TAU` functions (ESQL-1357)

Adds functions for the constants `PI` and it's big brother `TAU`.
Nik Everett 2 years ago
parent
commit
f3b20067a3

+ 4 - 0
docs/reference/esql/esql-functions.asciidoc

@@ -32,11 +32,13 @@ these functions:
 * <<esql-mv_min>>
 * <<esql-mv_sum>>
 * <<esql-now>>
+* <<esql-pi>>
 * <<esql-pow>>
 * <<esql-round>>
 * <<esql-split>>
 * <<esql-starts_with>>
 * <<esql-substring>>
+* <<esql-tau>>
 * <<esql-to_boolean>>
 * <<esql-to_datetime>>
 * <<esql-to_double>>
@@ -69,11 +71,13 @@ include::functions/mv_median.asciidoc[]
 include::functions/mv_min.asciidoc[]
 include::functions/mv_sum.asciidoc[]
 include::functions/now.asciidoc[]
+include::functions/pi.asciidoc[]
 include::functions/pow.asciidoc[]
 include::functions/round.asciidoc[]
 include::functions/split.asciidoc[]
 include::functions/starts_with.asciidoc[]
 include::functions/substring.asciidoc[]
+include::functions/tau.asciidoc[]
 include::functions/to_boolean.asciidoc[]
 include::functions/to_datetime.asciidoc[]
 include::functions/to_double.asciidoc[]

+ 12 - 0
docs/reference/esql/functions/pi.asciidoc

@@ -0,0 +1,12 @@
+[[esql-pi]]
+=== `PI`
+The {wikipedia}/Pi[ratio] of a circle's circumference to its diameter.
+
+[source.merge.styled,esql]
+----
+include::{esql-specs}/math.csv-spec[tag=pi]
+----
+[%header.monospaced.styled,format=dsv,separator=|]
+|===
+include::{esql-specs}/math.csv-spec[tag=pi-result]
+|===

+ 12 - 0
docs/reference/esql/functions/tau.asciidoc

@@ -0,0 +1,12 @@
+[[esql-tau]]
+=== `TAU`
+The https://tauday.com/tau-manifesto[ratio] of a circle's circumference to its radius.
+
+[source.merge.styled,esql]
+----
+include::{esql-specs}/math.csv-spec[tag=tau]
+----
+[%header.monospaced.styled,format=dsv,separator=|]
+|===
+include::{esql-specs}/math.csv-spec[tag=tau-result]
+|===

+ 24 - 0
x-pack/plugin/esql/qa/testFixtures/src/main/resources/math.csv-spec

@@ -566,3 +566,27 @@ row ul = [18446744073709551615, 0, 1, 9223372036854775807, 9223372036854775808,
 mv_median(ul):ul
 4611686018427387904
 ;
+
+pi
+// tag::pi[]
+ROW PI()
+// end::pi[]
+;
+
+// tag::pi-result[]
+PI():double
+3.141592653589793
+// end::pi-result[]
+;
+
+tau
+// tag::tau[]
+ROW TAU()
+// end::tau[]
+;
+
+// tag::tau-result[]
+TAU():double
+6.283185307179586
+// end::tau-result[]
+;

+ 2 - 0
x-pack/plugin/esql/qa/testFixtures/src/main/resources/show.csv-spec

@@ -41,12 +41,14 @@ mv_min                   |mv_min(arg1)
 mv_sum                   |mv_sum(arg1)
 now                      |now()
 percentile               |percentile(arg1, arg2)
+pi                       |pi()
 pow                      |pow(arg1, arg2)
 round                    |round(arg1, arg2)
 split                    |split(arg1, arg2)
 starts_with              |starts_with(arg1, arg2)
 substring                |substring(arg1, arg2, arg3)
 sum                      |sum(arg1)
+tau                      |tau()
 to_bool                  |to_bool(arg1)
 to_boolean               |to_boolean(arg1)
 to_datetime              |to_datetime(arg1)

+ 5 - 1
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/EsqlFunctionRegistry.java

@@ -37,8 +37,10 @@ import org.elasticsearch.xpack.esql.expression.function.scalar.math.E;
 import org.elasticsearch.xpack.esql.expression.function.scalar.math.IsFinite;
 import org.elasticsearch.xpack.esql.expression.function.scalar.math.IsInfinite;
 import org.elasticsearch.xpack.esql.expression.function.scalar.math.IsNaN;
+import org.elasticsearch.xpack.esql.expression.function.scalar.math.Pi;
 import org.elasticsearch.xpack.esql.expression.function.scalar.math.Pow;
 import org.elasticsearch.xpack.esql.expression.function.scalar.math.Round;
+import org.elasticsearch.xpack.esql.expression.function.scalar.math.Tau;
 import org.elasticsearch.xpack.esql.expression.function.scalar.metadata.Metadata;
 import org.elasticsearch.xpack.esql.expression.function.scalar.multivalue.MvAvg;
 import org.elasticsearch.xpack.esql.expression.function.scalar.multivalue.MvConcat;
@@ -90,8 +92,10 @@ public class EsqlFunctionRegistry extends FunctionRegistry {
                 def(IsFinite.class, IsFinite::new, "is_finite"),
                 def(IsInfinite.class, IsInfinite::new, "is_infinite"),
                 def(IsNaN.class, IsNaN::new, "is_nan"),
+                def(Pi.class, Pi::new, "pi"),
+                def(Pow.class, Pow::new, "pow"),
                 def(Round.class, Round::new, "round"),
-                def(Pow.class, Pow::new, "pow") },
+                def(Tau.class, Tau::new, "tau") },
             // string
             new FunctionDefinition[] {
                 def(Length.class, Length::new, "length"),

+ 45 - 0
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/DoubleConstantFunction.java

@@ -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
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+package org.elasticsearch.xpack.esql.expression.function.scalar.math;
+
+import org.elasticsearch.xpack.ql.expression.Expression;
+import org.elasticsearch.xpack.ql.expression.function.scalar.ScalarFunction;
+import org.elasticsearch.xpack.ql.expression.gen.script.ScriptTemplate;
+import org.elasticsearch.xpack.ql.tree.NodeInfo;
+import org.elasticsearch.xpack.ql.tree.Source;
+import org.elasticsearch.xpack.ql.type.DataType;
+import org.elasticsearch.xpack.ql.type.DataTypes;
+
+/**
+ * Function that emits Euler's number.
+ */
+public abstract class DoubleConstantFunction extends ScalarFunction {
+    protected DoubleConstantFunction(Source source) {
+        super(source);
+    }
+
+    @Override
+    public final boolean foldable() {
+        return true;
+    }
+
+    @Override
+    public final DataType dataType() {
+        return DataTypes.DOUBLE;
+    }
+
+    @Override
+    public final ScriptTemplate asScript() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    protected final NodeInfo<? extends Expression> info() {
+        return NodeInfo.create(this);
+    }
+}

+ 1 - 26
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/E.java

@@ -8,50 +8,25 @@
 package org.elasticsearch.xpack.esql.expression.function.scalar.math;
 
 import org.elasticsearch.xpack.ql.expression.Expression;
-import org.elasticsearch.xpack.ql.expression.function.scalar.ScalarFunction;
-import org.elasticsearch.xpack.ql.expression.gen.script.ScriptTemplate;
-import org.elasticsearch.xpack.ql.tree.NodeInfo;
 import org.elasticsearch.xpack.ql.tree.Source;
-import org.elasticsearch.xpack.ql.type.DataType;
-import org.elasticsearch.xpack.ql.type.DataTypes;
 
 import java.util.List;
 
 /**
  * Function that emits Euler's number.
  */
-public class E extends ScalarFunction {
+public class E extends DoubleConstantFunction {
     public E(Source source) {
         super(source);
     }
 
-    @Override
-    public boolean foldable() {
-        return true;
-    }
-
     @Override
     public Object fold() {
         return Math.E;
     }
 
-    @Override
-    public DataType dataType() {
-        return DataTypes.DOUBLE;
-    }
-
-    @Override
-    public ScriptTemplate asScript() {
-        throw new UnsupportedOperationException();
-    }
-
     @Override
     public Expression replaceChildren(List<Expression> newChildren) {
         return new E(source());
     }
-
-    @Override
-    protected NodeInfo<? extends Expression> info() {
-        return NodeInfo.create(this);
-    }
 }

+ 32 - 0
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Pi.java

@@ -0,0 +1,32 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+package org.elasticsearch.xpack.esql.expression.function.scalar.math;
+
+import org.elasticsearch.xpack.ql.expression.Expression;
+import org.elasticsearch.xpack.ql.tree.Source;
+
+import java.util.List;
+
+/**
+ * Function that emits pi.
+ */
+public class Pi extends DoubleConstantFunction {
+    public Pi(Source source) {
+        super(source);
+    }
+
+    @Override
+    public Object fold() {
+        return Math.PI;
+    }
+
+    @Override
+    public Expression replaceChildren(List<Expression> newChildren) {
+        return new Pi(source());
+    }
+}

+ 34 - 0
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Tau.java

@@ -0,0 +1,34 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+package org.elasticsearch.xpack.esql.expression.function.scalar.math;
+
+import org.elasticsearch.xpack.ql.expression.Expression;
+import org.elasticsearch.xpack.ql.tree.Source;
+
+import java.util.List;
+
+/**
+ * Function that emits tau, also known as 2 * pi.
+ */
+public class Tau extends DoubleConstantFunction {
+    public static final double TAU = Math.PI * 2;
+
+    public Tau(Source source) {
+        super(source);
+    }
+
+    @Override
+    public Object fold() {
+        return TAU;
+    }
+
+    @Override
+    public Expression replaceChildren(List<Expression> newChildren) {
+        return new Tau(source());
+    }
+}

+ 9 - 1
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/io/stream/PlanNamedTypes.java

@@ -47,8 +47,10 @@ import org.elasticsearch.xpack.esql.expression.function.scalar.math.E;
 import org.elasticsearch.xpack.esql.expression.function.scalar.math.IsFinite;
 import org.elasticsearch.xpack.esql.expression.function.scalar.math.IsInfinite;
 import org.elasticsearch.xpack.esql.expression.function.scalar.math.IsNaN;
+import org.elasticsearch.xpack.esql.expression.function.scalar.math.Pi;
 import org.elasticsearch.xpack.esql.expression.function.scalar.math.Pow;
 import org.elasticsearch.xpack.esql.expression.function.scalar.math.Round;
+import org.elasticsearch.xpack.esql.expression.function.scalar.math.Tau;
 import org.elasticsearch.xpack.esql.expression.function.scalar.metadata.Metadata;
 import org.elasticsearch.xpack.esql.expression.function.scalar.multivalue.AbstractMultivalueFunction;
 import org.elasticsearch.xpack.esql.expression.function.scalar.multivalue.MvAvg;
@@ -269,7 +271,9 @@ public final class PlanNamedTypes {
             of(ESQL_UNARY_SCLR_CLS, IsFinite.class, PlanNamedTypes::writeESQLUnaryScalar, PlanNamedTypes::readESQLUnaryScalar),
             of(ESQL_UNARY_SCLR_CLS, IsInfinite.class, PlanNamedTypes::writeESQLUnaryScalar, PlanNamedTypes::readESQLUnaryScalar),
             of(ESQL_UNARY_SCLR_CLS, IsNaN.class, PlanNamedTypes::writeESQLUnaryScalar, PlanNamedTypes::readESQLUnaryScalar),
+            of(ScalarFunction.class, Pi.class, PlanNamedTypes::writeNoArgScalar, PlanNamedTypes::readNoArgScalar),
             of(ESQL_UNARY_SCLR_CLS, Metadata.class, PlanNamedTypes::writeESQLUnaryScalar, PlanNamedTypes::readESQLUnaryScalar),
+            of(ScalarFunction.class, Tau.class, PlanNamedTypes::writeNoArgScalar, PlanNamedTypes::readNoArgScalar),
             of(ESQL_UNARY_SCLR_CLS, ToBoolean.class, PlanNamedTypes::writeESQLUnaryScalar, PlanNamedTypes::readESQLUnaryScalar),
             of(ESQL_UNARY_SCLR_CLS, ToDatetime.class, PlanNamedTypes::writeESQLUnaryScalar, PlanNamedTypes::readESQLUnaryScalar),
             of(ESQL_UNARY_SCLR_CLS, ToDouble.class, PlanNamedTypes::writeESQLUnaryScalar, PlanNamedTypes::readESQLUnaryScalar),
@@ -945,7 +949,11 @@ public final class PlanNamedTypes {
         out.writeExpression(function.field());
     }
 
-    static final Map<String, Function<Source, ScalarFunction>> NO_ARG_SCALAR_CTRS = Map.ofEntries(entry(name(E.class), E::new));
+    static final Map<String, Function<Source, ScalarFunction>> NO_ARG_SCALAR_CTRS = Map.ofEntries(
+        entry(name(E.class), E::new),
+        entry(name(Pi.class), Pi::new),
+        entry(name(Tau.class), Tau::new)
+    );
 
     static ScalarFunction readNoArgScalar(PlanStreamInput in, String name) throws IOException {
         var ctr = NO_ARG_SCALAR_CTRS.get(name);

+ 69 - 0
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/PiTests.java

@@ -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
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+package org.elasticsearch.xpack.esql.expression.function.scalar.math;
+
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.DoubleBlock;
+import org.elasticsearch.xpack.esql.expression.function.scalar.AbstractScalarFunctionTestCase;
+import org.elasticsearch.xpack.ql.expression.Expression;
+import org.elasticsearch.xpack.ql.expression.Literal;
+import org.elasticsearch.xpack.ql.tree.Source;
+import org.elasticsearch.xpack.ql.type.DataType;
+import org.elasticsearch.xpack.ql.type.DataTypes;
+import org.hamcrest.Matcher;
+
+import java.util.List;
+
+import static org.hamcrest.Matchers.equalTo;
+
+public class PiTests extends AbstractScalarFunctionTestCase {
+    @Override
+    protected List<Object> simpleData() {
+        return List.of(1); // Need to put some data in the input page or it'll fail to build
+    }
+
+    @Override
+    protected Expression expressionForSimpleData() {
+        return new Pi(Source.EMPTY);
+    }
+
+    @Override
+    protected Matcher<Object> resultMatcher(List<Object> data, DataType dataType) {
+        return equalTo(Math.PI);
+    }
+
+    @Override
+    protected String expectedEvaluatorSimpleToString() {
+        return "LiteralsEvaluator[block=3.141592653589793]";
+    }
+
+    @Override
+    protected Expression constantFoldable(List<Object> data) {
+        return expressionForSimpleData();
+    }
+
+    @Override
+    protected Expression build(Source source, List<Literal> args) {
+        return expressionForSimpleData();
+    }
+
+    @Override
+    protected List<ArgumentSpec> argSpec() {
+        return List.of();
+    }
+
+    @Override
+    protected DataType expectedType(List<DataType> argTypes) {
+        return DataTypes.DOUBLE;
+    }
+
+    @Override
+    protected void assertSimpleWithNulls(List<Object> data, Block value, int nullBlock) {
+        assertThat(((DoubleBlock) value).asVector().getDouble(0), equalTo(Math.PI));
+    }
+}

+ 69 - 0
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/TauTests.java

@@ -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
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+package org.elasticsearch.xpack.esql.expression.function.scalar.math;
+
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.DoubleBlock;
+import org.elasticsearch.xpack.esql.expression.function.scalar.AbstractScalarFunctionTestCase;
+import org.elasticsearch.xpack.ql.expression.Expression;
+import org.elasticsearch.xpack.ql.expression.Literal;
+import org.elasticsearch.xpack.ql.tree.Source;
+import org.elasticsearch.xpack.ql.type.DataType;
+import org.elasticsearch.xpack.ql.type.DataTypes;
+import org.hamcrest.Matcher;
+
+import java.util.List;
+
+import static org.hamcrest.Matchers.equalTo;
+
+public class TauTests extends AbstractScalarFunctionTestCase {
+    @Override
+    protected List<Object> simpleData() {
+        return List.of(1); // Need to put some data in the input page or it'll fail to build
+    }
+
+    @Override
+    protected Expression expressionForSimpleData() {
+        return new Tau(Source.EMPTY);
+    }
+
+    @Override
+    protected Matcher<Object> resultMatcher(List<Object> data, DataType dataType) {
+        return equalTo(Tau.TAU);
+    }
+
+    @Override
+    protected String expectedEvaluatorSimpleToString() {
+        return "LiteralsEvaluator[block=6.283185307179586]";
+    }
+
+    @Override
+    protected Expression constantFoldable(List<Object> data) {
+        return expressionForSimpleData();
+    }
+
+    @Override
+    protected Expression build(Source source, List<Literal> args) {
+        return expressionForSimpleData();
+    }
+
+    @Override
+    protected List<ArgumentSpec> argSpec() {
+        return List.of();
+    }
+
+    @Override
+    protected DataType expectedType(List<DataType> argTypes) {
+        return DataTypes.DOUBLE;
+    }
+
+    @Override
+    protected void assertSimpleWithNulls(List<Object> data, Block value, int nullBlock) {
+        assertThat(((DoubleBlock) value).asVector().getDouble(0), equalTo(Tau.TAU));
+    }
+}