|
@@ -0,0 +1,130 @@
|
|
|
+/*
|
|
|
+ * 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.common.io.stream.NamedWriteableRegistry;
|
|
|
+import org.elasticsearch.common.io.stream.StreamInput;
|
|
|
+import org.elasticsearch.common.io.stream.StreamOutput;
|
|
|
+import org.elasticsearch.compute.ann.Evaluator;
|
|
|
+import org.elasticsearch.compute.operator.EvalOperator;
|
|
|
+import org.elasticsearch.xpack.esql.core.expression.Expression;
|
|
|
+import org.elasticsearch.xpack.esql.core.expression.Expressions;
|
|
|
+import org.elasticsearch.xpack.esql.core.expression.TypeResolutions;
|
|
|
+import org.elasticsearch.xpack.esql.core.tree.NodeInfo;
|
|
|
+import org.elasticsearch.xpack.esql.core.tree.Source;
|
|
|
+import org.elasticsearch.xpack.esql.core.type.DataType;
|
|
|
+import org.elasticsearch.xpack.esql.expression.function.Example;
|
|
|
+import org.elasticsearch.xpack.esql.expression.function.FunctionInfo;
|
|
|
+import org.elasticsearch.xpack.esql.expression.function.Param;
|
|
|
+import org.elasticsearch.xpack.esql.expression.function.scalar.EsqlScalarFunction;
|
|
|
+import org.elasticsearch.xpack.esql.io.stream.PlanStreamInput;
|
|
|
+
|
|
|
+import java.io.IOException;
|
|
|
+import java.util.List;
|
|
|
+
|
|
|
+import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isNumeric;
|
|
|
+
|
|
|
+/**
|
|
|
+ * Returns the hypotenuse of the numbers given as parameters.
|
|
|
+ */
|
|
|
+public class Hypot extends EsqlScalarFunction {
|
|
|
+ public static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry(Expression.class, "Hypot", Hypot::new);
|
|
|
+
|
|
|
+ private final Expression n1;
|
|
|
+ private final Expression n2;
|
|
|
+
|
|
|
+ @FunctionInfo(returnType = "double", description = """
|
|
|
+ Returns the hypotenuse of two numbers. The input can be any numeric values, the return value is always a double.
|
|
|
+ Hypotenuses of infinities are null.""", examples = @Example(file = "math", tag = "hypot"))
|
|
|
+ public Hypot(
|
|
|
+ Source source,
|
|
|
+ @Param(
|
|
|
+ name = "number1",
|
|
|
+ type = { "double", "integer", "long", "unsigned_long" },
|
|
|
+ description = "Numeric expression. If `null`, the function returns `null`."
|
|
|
+ ) Expression n1,
|
|
|
+ @Param(
|
|
|
+ name = "number2",
|
|
|
+ type = { "double", "integer", "long", "unsigned_long" },
|
|
|
+ description = "Numeric expression. If `null`, the function returns `null`."
|
|
|
+ ) Expression n2
|
|
|
+ ) {
|
|
|
+ super(source, List.of(n1, n2));
|
|
|
+ this.n1 = n1;
|
|
|
+ this.n2 = n2;
|
|
|
+ }
|
|
|
+
|
|
|
+ private Hypot(StreamInput in) throws IOException {
|
|
|
+ this(Source.readFrom((PlanStreamInput) in), in.readNamedWriteable(Expression.class), in.readNamedWriteable(Expression.class));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void writeTo(StreamOutput out) throws IOException {
|
|
|
+ source().writeTo(out);
|
|
|
+ out.writeNamedWriteable(n1);
|
|
|
+ out.writeNamedWriteable(n2);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String getWriteableName() {
|
|
|
+ return ENTRY.name;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Expression replaceChildren(List<Expression> newChildren) {
|
|
|
+ return new Hypot(source(), newChildren.get(0), newChildren.get(1));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ protected NodeInfo<? extends Expression> info() {
|
|
|
+ return NodeInfo.create(this, Hypot::new, n1, n2);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Evaluator
|
|
|
+ static double process(double n1, double n2) {
|
|
|
+ return Math.hypot(n1, n2);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public DataType dataType() {
|
|
|
+ return DataType.DOUBLE;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ protected TypeResolution resolveType() {
|
|
|
+ if (childrenResolved() == false) {
|
|
|
+ return new TypeResolution("Unresolved children");
|
|
|
+ }
|
|
|
+
|
|
|
+ TypeResolution resolution = isNumeric(n1, sourceText(), TypeResolutions.ParamOrdinal.FIRST);
|
|
|
+ if (resolution.unresolved()) {
|
|
|
+ return resolution;
|
|
|
+ }
|
|
|
+ return isNumeric(n2, sourceText(), TypeResolutions.ParamOrdinal.SECOND);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean foldable() {
|
|
|
+ return Expressions.foldable(children());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public EvalOperator.ExpressionEvaluator.Factory toEvaluator(ToEvaluator toEvaluator) {
|
|
|
+ var n1Eval = Cast.cast(source(), n1.dataType(), DataType.DOUBLE, toEvaluator.apply(n1));
|
|
|
+ var n2Eval = Cast.cast(source(), n2.dataType(), DataType.DOUBLE, toEvaluator.apply(n2));
|
|
|
+ return new HypotEvaluator.Factory(source(), n1Eval, n2Eval);
|
|
|
+ }
|
|
|
+
|
|
|
+ public Expression n1() {
|
|
|
+ return n1;
|
|
|
+ }
|
|
|
+
|
|
|
+ public Expression n2() {
|
|
|
+ return n2;
|
|
|
+ }
|
|
|
+}
|