diff --git a/benchmarks/src/main/java/org/elasticsearch/benchmark/compute/operator/EvalBenchmark.java b/benchmarks/src/main/java/org/elasticsearch/benchmark/compute/operator/EvalBenchmark.java index 6fa015df4aac..30821e68cd2d 100644 --- a/benchmarks/src/main/java/org/elasticsearch/benchmark/compute/operator/EvalBenchmark.java +++ b/benchmarks/src/main/java/org/elasticsearch/benchmark/compute/operator/EvalBenchmark.java @@ -13,6 +13,7 @@ import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.breaker.NoopCircuitBreaker; import org.elasticsearch.common.logging.LogConfigurator; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.unit.ByteSizeUnit; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.compute.data.Block; import org.elasticsearch.compute.data.BlockFactory; @@ -44,6 +45,7 @@ import org.elasticsearch.xpack.esql.evaluator.EvalMapper; import org.elasticsearch.xpack.esql.expression.function.scalar.conditional.Case; import org.elasticsearch.xpack.esql.expression.function.scalar.date.DateTrunc; import org.elasticsearch.xpack.esql.expression.function.scalar.math.Abs; +import org.elasticsearch.xpack.esql.expression.function.scalar.math.RoundTo; import org.elasticsearch.xpack.esql.expression.function.scalar.multivalue.MvMin; import org.elasticsearch.xpack.esql.expression.function.scalar.nulls.Coalesce; import org.elasticsearch.xpack.esql.expression.function.scalar.string.RLike; @@ -51,6 +53,7 @@ import org.elasticsearch.xpack.esql.expression.function.scalar.string.ToLower; import org.elasticsearch.xpack.esql.expression.function.scalar.string.ToUpper; import org.elasticsearch.xpack.esql.expression.predicate.operator.arithmetic.Add; import org.elasticsearch.xpack.esql.expression.predicate.operator.comparison.Equals; +import org.elasticsearch.xpack.esql.expression.predicate.operator.comparison.LessThan; import org.elasticsearch.xpack.esql.planner.Layout; import org.elasticsearch.xpack.esql.plugin.EsqlPlugin; import org.elasticsearch.xpack.esql.session.Configuration; @@ -128,6 +131,10 @@ public class EvalBenchmark { "long_equal_to_int", "mv_min", "mv_min_ascending", + "round_to_4_via_case", + "round_to_2", + "round_to_3", + "round_to_4", "rlike", "to_lower", "to_lower_ords", @@ -240,6 +247,65 @@ public class EvalBenchmark { RLike rlike = new RLike(Source.EMPTY, keywordField, new RLikePattern(".ar")); yield EvalMapper.toEvaluator(FOLD_CONTEXT, rlike, layout(keywordField)).get(driverContext); } + case "round_to_4_via_case" -> { + FieldAttribute f = longField(); + + Expression ltkb = new LessThan(Source.EMPTY, f, kb()); + Expression ltmb = new LessThan(Source.EMPTY, f, mb()); + Expression ltgb = new LessThan(Source.EMPTY, f, gb()); + EvalOperator.ExpressionEvaluator evaluator = EvalMapper.toEvaluator( + FOLD_CONTEXT, + new Case(Source.EMPTY, ltkb, List.of(b(), ltmb, kb(), ltgb, mb(), gb())), + layout(f) + ).get(driverContext); + String desc = "CaseLazyEvaluator"; + if (evaluator.toString().contains(desc) == false) { + throw new IllegalArgumentException("Evaluator was [" + evaluator + "] but expected one containing [" + desc + "]"); + } + yield evaluator; + } + case "round_to_2" -> { + FieldAttribute f = longField(); + + EvalOperator.ExpressionEvaluator evaluator = EvalMapper.toEvaluator( + FOLD_CONTEXT, + new RoundTo(Source.EMPTY, f, List.of(b(), kb())), + layout(f) + ).get(driverContext); + String desc = "RoundToLong2"; + if (evaluator.toString().contains(desc) == false) { + throw new IllegalArgumentException("Evaluator was [" + evaluator + "] but expected one containing [" + desc + "]"); + } + yield evaluator; + } + case "round_to_3" -> { + FieldAttribute f = longField(); + + EvalOperator.ExpressionEvaluator evaluator = EvalMapper.toEvaluator( + FOLD_CONTEXT, + new RoundTo(Source.EMPTY, f, List.of(b(), kb(), mb())), + layout(f) + ).get(driverContext); + String desc = "RoundToLong3"; + if (evaluator.toString().contains(desc) == false) { + throw new IllegalArgumentException("Evaluator was [" + evaluator + "] but expected one containing [" + desc + "]"); + } + yield evaluator; + } + case "round_to_4" -> { + FieldAttribute f = longField(); + + EvalOperator.ExpressionEvaluator evaluator = EvalMapper.toEvaluator( + FOLD_CONTEXT, + new RoundTo(Source.EMPTY, f, List.of(b(), kb(), mb(), gb())), + layout(f) + ).get(driverContext); + String desc = "RoundToLong4"; + if (evaluator.toString().contains(desc) == false) { + throw new IllegalArgumentException("Evaluator was [" + evaluator + "] but expected one containing [" + desc + "]"); + } + yield evaluator; + } case "to_lower", "to_lower_ords" -> { FieldAttribute keywordField = keywordField(); ToLower toLower = new ToLower(Source.EMPTY, keywordField, configuration()); @@ -419,6 +485,69 @@ public class EvalBenchmark { } } } + case "round_to_4_via_case", "round_to_4" -> { + long b = 1; + long kb = ByteSizeUnit.KB.toBytes(1); + long mb = ByteSizeUnit.MB.toBytes(1); + long gb = ByteSizeUnit.GB.toBytes(1); + + LongVector f = actual.getBlock(0).asVector(); + LongVector result = actual.getBlock(1).asVector(); + for (int i = 0; i < BLOCK_LENGTH; i++) { + long expected = f.getLong(i); + if (expected < kb) { + expected = b; + } else if (expected < mb) { + expected = kb; + } else if (expected < gb) { + expected = mb; + } else { + expected = gb; + } + if (result.getLong(i) != expected) { + throw new AssertionError("[" + operation + "] expected [" + expected + "] but was [" + result.getLong(i) + "]"); + } + } + } + case "round_to_3" -> { + long b = 1; + long kb = ByteSizeUnit.KB.toBytes(1); + long mb = ByteSizeUnit.MB.toBytes(1); + + LongVector f = actual.getBlock(0).asVector(); + LongVector result = actual.getBlock(1).asVector(); + for (int i = 0; i < BLOCK_LENGTH; i++) { + long expected = f.getLong(i); + if (expected < kb) { + expected = b; + } else if (expected < mb) { + expected = kb; + } else { + expected = mb; + } + if (result.getLong(i) != expected) { + throw new AssertionError("[" + operation + "] expected [" + expected + "] but was [" + result.getLong(i) + "]"); + } + } + } + case "round_to_2" -> { + long b = 1; + long kb = ByteSizeUnit.KB.toBytes(1); + + LongVector f = actual.getBlock(0).asVector(); + LongVector result = actual.getBlock(1).asVector(); + for (int i = 0; i < BLOCK_LENGTH; i++) { + long expected = f.getLong(i); + if (expected < kb) { + expected = b; + } else { + expected = kb; + } + if (result.getLong(i) != expected) { + throw new AssertionError("[" + operation + "] expected [" + expected + "] but was [" + result.getLong(i) + "]"); + } + } + } case "to_lower" -> checkBytes(operation, actual, false, new BytesRef[] { new BytesRef("foo"), new BytesRef("bar") }); case "to_lower_ords" -> checkBytes(operation, actual, true, new BytesRef[] { new BytesRef("foo"), new BytesRef("bar") }); case "to_upper" -> checkBytes(operation, actual, false, new BytesRef[] { new BytesRef("FOO"), new BytesRef("BAR") }); @@ -450,7 +579,7 @@ public class EvalBenchmark { private static Page page(String operation) { return switch (operation) { - case "abs", "add", "date_trunc", "equal_to_const" -> { + case "abs", "add", "date_trunc", "equal_to_const", "round_to_4_via_case", "round_to_2", "round_to_3", "round_to_4" -> { var builder = blockFactory.newLongBlockBuilder(BLOCK_LENGTH); for (int i = 0; i < BLOCK_LENGTH; i++) { builder.appendLong(i * 100_000); @@ -540,6 +669,26 @@ public class EvalBenchmark { }; } + private static Literal b() { + return lit(1L); + } + + private static Literal kb() { + return lit(ByteSizeUnit.KB.toBytes(1)); + } + + private static Literal mb() { + return lit(ByteSizeUnit.MB.toBytes(1)); + } + + private static Literal gb() { + return lit(ByteSizeUnit.GB.toBytes(1)); + } + + private static Literal lit(long v) { + return new Literal(Source.EMPTY, v, DataType.LONG); + } + @Benchmark @OperationsPerInvocation(1024 * BLOCK_LENGTH) public void run() { diff --git a/docs/changelog/128278.yaml b/docs/changelog/128278.yaml new file mode 100644 index 000000000000..e8cc008a393d --- /dev/null +++ b/docs/changelog/128278.yaml @@ -0,0 +1,5 @@ +pr: 128278 +summary: ROUND_TO function +area: ES|QL +type: enhancement +issues: [] diff --git a/docs/reference/query-languages/esql/_snippets/functions/description/round_to.md b/docs/reference/query-languages/esql/_snippets/functions/description/round_to.md new file mode 100644 index 000000000000..d45b64cae285 --- /dev/null +++ b/docs/reference/query-languages/esql/_snippets/functions/description/round_to.md @@ -0,0 +1,6 @@ +% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it. + +**Description** + +Rounds down to one of a list of fixed points. + diff --git a/docs/reference/query-languages/esql/_snippets/functions/examples/round_to.md b/docs/reference/query-languages/esql/_snippets/functions/examples/round_to.md new file mode 100644 index 000000000000..1ec17de8b355 --- /dev/null +++ b/docs/reference/query-languages/esql/_snippets/functions/examples/round_to.md @@ -0,0 +1,29 @@ +% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it. + +**Example** + +```esql +required_capability: round_to +FROM employees +| STATS COUNT(*) BY birth_window=ROUND_TO( + birth_date, + "1900-01-01T00:00:00Z"::DATETIME, + "1950-01-01T00:00:00Z"::DATETIME, + "1955-01-01T00:00:00Z"::DATETIME, + "1960-01-01T00:00:00Z"::DATETIME, + "1965-01-01T00:00:00Z"::DATETIME, + "1970-01-01T00:00:00Z"::DATETIME, + "1975-01-01T00:00:00Z"::DATETIME +) +| SORT birth_window ASC +``` + +| COUNT(*):long | birth_window:datetime | +| --- | --- | +| 27 | 1950-01-01T00:00:00Z | +| 29 | 1955-01-01T00:00:00Z | +| 33 | 1960-01-01T00:00:00Z | +| 1 | 1965-01-01T00:00:00Z | +| 10 | null | + + diff --git a/docs/reference/query-languages/esql/_snippets/functions/layout/round_to.md b/docs/reference/query-languages/esql/_snippets/functions/layout/round_to.md new file mode 100644 index 000000000000..3e8fed6a784a --- /dev/null +++ b/docs/reference/query-languages/esql/_snippets/functions/layout/round_to.md @@ -0,0 +1,23 @@ +% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it. + +## `ROUND_TO` [esql-round_to] + +**Syntax** + +:::{image} ../../../images/functions/round_to.svg +:alt: Embedded +:class: text-center +::: + + +:::{include} ../parameters/round_to.md +::: + +:::{include} ../description/round_to.md +::: + +:::{include} ../types/round_to.md +::: + +:::{include} ../examples/round_to.md +::: diff --git a/docs/reference/query-languages/esql/_snippets/functions/parameters/round_to.md b/docs/reference/query-languages/esql/_snippets/functions/parameters/round_to.md new file mode 100644 index 000000000000..4ed8bea31465 --- /dev/null +++ b/docs/reference/query-languages/esql/_snippets/functions/parameters/round_to.md @@ -0,0 +1,10 @@ +% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it. + +**Parameters** + +`field` +: The numeric value to round. If `null`, the function returns `null`. + +`points` +: Remaining rounding points. Must be constants. + diff --git a/docs/reference/query-languages/esql/_snippets/functions/types/round_to.md b/docs/reference/query-languages/esql/_snippets/functions/types/round_to.md new file mode 100644 index 000000000000..ca49489a87c7 --- /dev/null +++ b/docs/reference/query-languages/esql/_snippets/functions/types/round_to.md @@ -0,0 +1,18 @@ +% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it. + +**Supported types** + +| field | points | result | +| --- | --- | --- | +| date | date | date | +| date_nanos | date_nanos | date_nanos | +| double | double | double | +| double | integer | double | +| double | long | double | +| integer | double | double | +| integer | integer | integer | +| integer | long | long | +| long | double | double | +| long | integer | long | +| long | long | long | + diff --git a/docs/reference/query-languages/esql/images/functions/round_to.svg b/docs/reference/query-languages/esql/images/functions/round_to.svg new file mode 100644 index 000000000000..6aad0249e250 --- /dev/null +++ b/docs/reference/query-languages/esql/images/functions/round_to.svg @@ -0,0 +1 @@ +ROUND_TO(field,points) \ No newline at end of file diff --git a/docs/reference/query-languages/esql/kibana/definition/functions/round_to.json b/docs/reference/query-languages/esql/kibana/definition/functions/round_to.json new file mode 100644 index 000000000000..0111f8bac877 --- /dev/null +++ b/docs/reference/query-languages/esql/kibana/definition/functions/round_to.json @@ -0,0 +1,211 @@ +{ + "comment" : "This is generated by ESQL’s AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.", + "type" : "scalar", + "name" : "round_to", + "description" : "Rounds down to one of a list of fixed points.", + "signatures" : [ + { + "params" : [ + { + "name" : "field", + "type" : "date", + "optional" : false, + "description" : "The numeric value to round. If `null`, the function returns `null`." + }, + { + "name" : "points", + "type" : "date", + "optional" : false, + "description" : "Remaining rounding points. Must be constants." + } + ], + "variadic" : true, + "returnType" : "date" + }, + { + "params" : [ + { + "name" : "field", + "type" : "date_nanos", + "optional" : false, + "description" : "The numeric value to round. If `null`, the function returns `null`." + }, + { + "name" : "points", + "type" : "date_nanos", + "optional" : false, + "description" : "Remaining rounding points. Must be constants." + } + ], + "variadic" : true, + "returnType" : "date_nanos" + }, + { + "params" : [ + { + "name" : "field", + "type" : "double", + "optional" : false, + "description" : "The numeric value to round. If `null`, the function returns `null`." + }, + { + "name" : "points", + "type" : "double", + "optional" : false, + "description" : "Remaining rounding points. Must be constants." + } + ], + "variadic" : true, + "returnType" : "double" + }, + { + "params" : [ + { + "name" : "field", + "type" : "double", + "optional" : false, + "description" : "The numeric value to round. If `null`, the function returns `null`." + }, + { + "name" : "points", + "type" : "integer", + "optional" : false, + "description" : "Remaining rounding points. Must be constants." + } + ], + "variadic" : true, + "returnType" : "double" + }, + { + "params" : [ + { + "name" : "field", + "type" : "double", + "optional" : false, + "description" : "The numeric value to round. If `null`, the function returns `null`." + }, + { + "name" : "points", + "type" : "long", + "optional" : false, + "description" : "Remaining rounding points. Must be constants." + } + ], + "variadic" : true, + "returnType" : "double" + }, + { + "params" : [ + { + "name" : "field", + "type" : "integer", + "optional" : false, + "description" : "The numeric value to round. If `null`, the function returns `null`." + }, + { + "name" : "points", + "type" : "double", + "optional" : false, + "description" : "Remaining rounding points. Must be constants." + } + ], + "variadic" : true, + "returnType" : "double" + }, + { + "params" : [ + { + "name" : "field", + "type" : "integer", + "optional" : false, + "description" : "The numeric value to round. If `null`, the function returns `null`." + }, + { + "name" : "points", + "type" : "integer", + "optional" : false, + "description" : "Remaining rounding points. Must be constants." + } + ], + "variadic" : true, + "returnType" : "integer" + }, + { + "params" : [ + { + "name" : "field", + "type" : "integer", + "optional" : false, + "description" : "The numeric value to round. If `null`, the function returns `null`." + }, + { + "name" : "points", + "type" : "long", + "optional" : false, + "description" : "Remaining rounding points. Must be constants." + } + ], + "variadic" : true, + "returnType" : "long" + }, + { + "params" : [ + { + "name" : "field", + "type" : "long", + "optional" : false, + "description" : "The numeric value to round. If `null`, the function returns `null`." + }, + { + "name" : "points", + "type" : "double", + "optional" : false, + "description" : "Remaining rounding points. Must be constants." + } + ], + "variadic" : true, + "returnType" : "double" + }, + { + "params" : [ + { + "name" : "field", + "type" : "long", + "optional" : false, + "description" : "The numeric value to round. If `null`, the function returns `null`." + }, + { + "name" : "points", + "type" : "integer", + "optional" : false, + "description" : "Remaining rounding points. Must be constants." + } + ], + "variadic" : true, + "returnType" : "long" + }, + { + "params" : [ + { + "name" : "field", + "type" : "long", + "optional" : false, + "description" : "The numeric value to round. If `null`, the function returns `null`." + }, + { + "name" : "points", + "type" : "long", + "optional" : false, + "description" : "Remaining rounding points. Must be constants." + } + ], + "variadic" : true, + "returnType" : "long" + } + ], + "examples" : [ + "required_capability: round_to\nFROM employees\n| STATS COUNT(*) BY birth_window=ROUND_TO(\n birth_date,\n \"1900-01-01T00:00:00Z\"::DATETIME,\n \"1950-01-01T00:00:00Z\"::DATETIME,\n \"1955-01-01T00:00:00Z\"::DATETIME,\n \"1960-01-01T00:00:00Z\"::DATETIME,\n \"1965-01-01T00:00:00Z\"::DATETIME,\n \"1970-01-01T00:00:00Z\"::DATETIME,\n \"1975-01-01T00:00:00Z\"::DATETIME\n)\n| SORT birth_window ASC" + ], + "preview" : false, + "snapshot_only" : false +} diff --git a/docs/reference/query-languages/esql/kibana/docs/functions/round_to.md b/docs/reference/query-languages/esql/kibana/docs/functions/round_to.md new file mode 100644 index 000000000000..afde1faab089 --- /dev/null +++ b/docs/reference/query-languages/esql/kibana/docs/functions/round_to.md @@ -0,0 +1,20 @@ +% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it. + +### ROUND TO +Rounds down to one of a list of fixed points. + +```esql +required_capability: round_to +FROM employees +| STATS COUNT(*) BY birth_window=ROUND_TO( + birth_date, + "1900-01-01T00:00:00Z"::DATETIME, + "1950-01-01T00:00:00Z"::DATETIME, + "1955-01-01T00:00:00Z"::DATETIME, + "1960-01-01T00:00:00Z"::DATETIME, + "1965-01-01T00:00:00Z"::DATETIME, + "1970-01-01T00:00:00Z"::DATETIME, + "1975-01-01T00:00:00Z"::DATETIME +) +| SORT birth_window ASC +``` diff --git a/x-pack/plugin/esql/build.gradle b/x-pack/plugin/esql/build.gradle index 28c3e4d2b20c..70f64cb43b71 100644 --- a/x-pack/plugin/esql/build.gradle +++ b/x-pack/plugin/esql/build.gradle @@ -449,4 +449,21 @@ tasks.named('stringTemplates').configure { it.inputFile = coalesceInputFile it.outputFile = "org/elasticsearch/xpack/esql/expression/function/scalar/nulls/CoalesceBytesRefEvaluator.java" } + + File roundToInput = file("src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/X-RoundTo.java.st") + template { + it.properties = intProperties + it.inputFile = roundToInput + it.outputFile = "org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToInt.java" + } + template { + it.properties = longProperties + it.inputFile = roundToInput + it.outputFile = "org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToLong.java" + } + template { + it.properties = doubleProperties + it.inputFile = roundToInput + it.outputFile = "org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToDouble.java" + } } diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/math.csv-spec b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/math.csv-spec index c80827c372ee..ae6929326254 100644 --- a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/math.csv-spec +++ b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/math.csv-spec @@ -793,6 +793,146 @@ ul:ul null ; +roundToInt +required_capability: round_to +ROW v = 1 +| EVAL r1 = ROUND_TO(v, 0, 1, 2) + , r2 = ROUND_TO(v, 2, 3) + , r3 = ROUND_TO(v, 0, 10, 20, 30, 40, 50, 60, 70, 90) + , rl = ROUND_TO(v, 0::LONG, 100) + , rd = ROUND_TO(v, 0, 1.1, 2.2); + +v:integer | r1:integer | r2:integer | r3:integer | rl:long | rd:double + 1 | 1 | 2 | 0 | 0 | 0.0 +; + +roundToLong +required_capability: round_to +ROW v = 1000000000000::LONG +| EVAL r1 = ROUND_TO(v, 0, 1000000000000, 2000000000000) + , r2 = ROUND_TO(v, 2000000000000, 3000000000000) + , r3 = ROUND_TO(v, 0, 1000000000000, 2000000000000, 3000000000000, 4000000000000, 5000000000000, 6000000000000) + , rd = ROUND_TO(v, 0, 1.1, 2.2); + + v:long | r1:long | r2:long | r3:long | rd:double +1000000000000 | 1000000000000 | 2000000000000 | 1000000000000 | 2.2 +; + +roundToDouble +required_capability: round_to +ROW v = 1.1 +| EVAL r1 = ROUND_TO(v, 0, 1, 2) + , r2 = ROUND_TO(v, 2, 3) + , r3 = ROUND_TO(v, 0, 10, 20, 30, 40, 50, 60, 70, 90) + , rl = ROUND_TO(v, 0::LONG, 100) + , rd = ROUND_TO(v, 0, 1.1, 2.2); + +v:double | r1:double | r2:double | r3:double | rl:double | rd:double + 1.1 | 1.0 | 2.0 | 0.0 | 0.0 | 1.1 +; + +roundToDate +required_capability: round_to +ROW v = "2025-02-02T01:00:00Z"::DATE +| EVAL r1 = ROUND_TO(v, "2025-01-01T00:00:00Z"::DATE, "2025-02-01T00:00:00Z"::DATE, "2025-03-01T00:00:00Z"::DATE) + , r2 = ROUND_TO(v, "2025-04-01T00:00:00Z"::DATE, "2025-05-01T00:00:00Z"::DATE, "2025-06-01T00:00:00Z"::DATE) + , r3 = ROUND_TO(v, "2025-01-01T00:00:00Z"::DATE, "2025-02-01T00:00:00Z"::DATE, "2025-03-01T00:00:00Z"::DATE, "2025-04-01T00:00:00Z"::DATE, "2025-05-01T00:00:00Z"::DATE, "2025-06-01T00:00:00Z"::DATE); + + v:date | r1:date | r2:date | r3:date +2025-02-02T01:00:00Z | 2025-02-01T00:00:00Z | 2025-04-01T00:00:00Z | 2025-02-01T00:00:00Z +; + +roundToDateNanos +required_capability: round_to +ROW v = "2025-02-02T01:00:00.543123456Z"::DATE_NANOS +| EVAL r1 = ROUND_TO(v, "2025-01-01T00:00:00Z"::DATE_NANOS, "2025-02-01T00:00:00Z"::DATE_NANOS, "2025-03-01T00:00:00Z"::DATE_NANOS) + , r2 = ROUND_TO(v, "2025-04-01T00:00:00Z"::DATE_NANOS, "2025-05-01T00:00:00Z"::DATE_NANOS, "2025-06-01T00:00:00Z"::DATE_NANOS) + , r3 = ROUND_TO(v, "2025-01-01T00:00:00Z"::DATE_NANOS, "2025-02-01T00:00:00Z"::DATE_NANOS, "2025-03-01T00:00:00Z"::DATE_NANOS, "2025-04-01T00:00:00Z"::DATE_NANOS, "2025-05-01T00:00:00Z"::DATE_NANOS, "2025-06-01T00:00:00Z"::DATE_NANOS); + + v:date_nanos | r1:date_nanos | r2:date_nanos | r3:date_nanos +2025-02-02T01:00:00.543123456Z | 2025-02-01T00:00:00.000000000Z | 2025-04-01T00:00:00.000000000Z | 2025-02-01T00:00:00.000000000Z +; + +roundToBirthWindow +// tag::round_to[] +required_capability: round_to +FROM employees +| STATS COUNT(*) BY birth_window=ROUND_TO( + birth_date, + "1900-01-01T00:00:00Z"::DATETIME, + "1950-01-01T00:00:00Z"::DATETIME, + "1955-01-01T00:00:00Z"::DATETIME, + "1960-01-01T00:00:00Z"::DATETIME, + "1965-01-01T00:00:00Z"::DATETIME, + "1970-01-01T00:00:00Z"::DATETIME, + "1975-01-01T00:00:00Z"::DATETIME +) +| SORT birth_window ASC +// end::round_to[] +; + +// tag::round_to-result[] +COUNT(*):long | birth_window:datetime + 27 | 1950-01-01T00:00:00Z + 29 | 1955-01-01T00:00:00Z + 33 | 1960-01-01T00:00:00Z + 1 | 1965-01-01T00:00:00Z + 10 | null +// end::round_to-result[] +; + +roundToSalaryWindow +required_capability: round_to +FROM employees +| STATS COUNT(*) BY salary=ROUND_TO( + salary, + 0, + 20000, + 25000, + 30000, + 40000, + 50000, + 70000, + 90000 +) +| SORT salary ASC +; + +COUNT(*):long | salary:integer + 9 | 25000 + 27 | 30000 + 22 | 40000 + 34 | 50000 + 8 | 70000 +; + +roundToNanos +required_capability: round_to +FROM sample_data_ts_nanos +| STATS COUNT(*) BY @timestamp=ROUND_TO( + @timestamp, + "2023-10-23T13:33:34.937123456Z"::DATE_NANOS, + "2023-10-23T13:52:55.015123456Z"::DATE_NANOS, + "2023-10-23T13:55:01.543123456Z"::DATE_NANOS +) +| SORT @timestamp ASC +; + +COUNT(*):long | @timestamp:date_nanos + 4 | 2023-10-23T13:33:34.937123456Z + 2 | 2023-10-23T13:52:55.015123456Z + 1 | 2023-10-23T13:55:01.543123456Z +; + +roundToUnsorted +required_capability: round_to +ROW v = 1 +| EVAL r = ROUND_TO(v, 2, -1, 9, 0, 100); + +v:integer | r:integer + 1 | 0 +; + mvAvg from employees | where emp_no > 10008 | eval salary_change = mv_avg(salary_change) | sort emp_no | keep emp_no, salary_change.int, salary_change | limit 7; diff --git a/x-pack/plugin/esql/src/main/generated-src/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToDouble.java b/x-pack/plugin/esql/src/main/generated-src/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToDouble.java new file mode 100644 index 000000000000..623962d29319 --- /dev/null +++ b/x-pack/plugin/esql/src/main/generated-src/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToDouble.java @@ -0,0 +1,105 @@ +/* + * 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; + +// begin generated imports +import org.elasticsearch.compute.ann.Evaluator; +import org.elasticsearch.compute.ann.Fixed; + +import java.util.Arrays; +// end generated imports + +/** + * Implementations of {@link RoundTo} for specific types. + *

+ * We have specializations for when there are very few rounding points because + * those are very fast and quite common. + *

+ * This class is generated. Edit {@code X-RoundTo.java.st} instead. + */ +class RoundToDouble { + static final RoundTo.Build BUILD = (source, field, points) -> { + double[] f = points.stream().mapToDouble(p -> ((Number) p).doubleValue()).toArray(); + Arrays.sort(f); + return switch (f.length) { + // TODO should be a consistent way to do the 0 version - is CASE(MV_COUNT(f) == 1, f[0]) + case 1 -> new RoundToDouble1Evaluator.Factory(source, field, f[0]); + /* + * These hand-unrolled implementations are even faster than the linear scan implementations. + */ + case 2 -> new RoundToDouble2Evaluator.Factory(source, field, f[0], f[1]); + case 3 -> new RoundToDouble3Evaluator.Factory(source, field, f[0], f[1], f[2]); + case 4 -> new RoundToDouble4Evaluator.Factory(source, field, f[0], f[1], f[2], f[3]); + /* + * Break point of 10 experimentally derived on Nik's laptop (13th Gen Intel(R) Core(TM) i7-1370P) + * on 2025-05-22. + */ + case 5, 6, 7, 8, 9, 10 -> new RoundToDoubleLinearSearchEvaluator.Factory(source, field, f); + default -> new RoundToDoubleBinarySearchEvaluator.Factory(source, field, f); + }; + }; + + /** + * Search the points array for the match linearly. This is faster for smaller arrays even + * when finding a position late in the array. Presumably because this is super-SIMD-able. + */ + @Evaluator(extraName = "LinearSearch") + static double processLinear(double field, @Fixed(includeInToString = false) double[] points) { + // points is always longer than 3 or we use one of the specialized methods below + for (int i = 1; i < points.length; i++) { + if (field < points[i]) { + return points[i - 1]; + } + } + return points[points.length - 1]; + } + + @Evaluator(extraName = "BinarySearch") + static double process(double field, @Fixed(includeInToString = false) double[] points) { + int idx = Arrays.binarySearch(points, field); + return points[idx >= 0 ? idx : Math.max(0, -idx - 2)]; + } + + @Evaluator(extraName = "1") + static double process(double field, @Fixed double p0) { + return p0; + } + + @Evaluator(extraName = "2") + static double process(double field, @Fixed double p0, @Fixed double p1) { + if (field < p1) { + return p0; + } + return p1; + } + + @Evaluator(extraName = "3") + static double process(double field, @Fixed double p0, @Fixed double p1, @Fixed double p2) { + if (field < p1) { + return p0; + } + if (field < p2) { + return p1; + } + return p2; + } + + @Evaluator(extraName = "4") + static double process(double field, @Fixed double p0, @Fixed double p1, @Fixed double p2, @Fixed double p3) { + if (field < p1) { + return p0; + } + if (field < p2) { + return p1; + } + if (field < p3) { + return p2; + } + return p3; + } +} diff --git a/x-pack/plugin/esql/src/main/generated-src/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToInt.java b/x-pack/plugin/esql/src/main/generated-src/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToInt.java new file mode 100644 index 000000000000..1b97e175a651 --- /dev/null +++ b/x-pack/plugin/esql/src/main/generated-src/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToInt.java @@ -0,0 +1,105 @@ +/* + * 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; + +// begin generated imports +import org.elasticsearch.compute.ann.Evaluator; +import org.elasticsearch.compute.ann.Fixed; + +import java.util.Arrays; +// end generated imports + +/** + * Implementations of {@link RoundTo} for specific types. + *

+ * We have specializations for when there are very few rounding points because + * those are very fast and quite common. + *

+ * This class is generated. Edit {@code X-RoundTo.java.st} instead. + */ +class RoundToInt { + static final RoundTo.Build BUILD = (source, field, points) -> { + int[] f = points.stream().mapToInt(p -> ((Number) p).intValue()).toArray(); + Arrays.sort(f); + return switch (f.length) { + // TODO should be a consistent way to do the 0 version - is CASE(MV_COUNT(f) == 1, f[0]) + case 1 -> new RoundToInt1Evaluator.Factory(source, field, f[0]); + /* + * These hand-unrolled implementations are even faster than the linear scan implementations. + */ + case 2 -> new RoundToInt2Evaluator.Factory(source, field, f[0], f[1]); + case 3 -> new RoundToInt3Evaluator.Factory(source, field, f[0], f[1], f[2]); + case 4 -> new RoundToInt4Evaluator.Factory(source, field, f[0], f[1], f[2], f[3]); + /* + * Break point of 10 experimentally derived on Nik's laptop (13th Gen Intel(R) Core(TM) i7-1370P) + * on 2025-05-22. + */ + case 5, 6, 7, 8, 9, 10 -> new RoundToIntLinearSearchEvaluator.Factory(source, field, f); + default -> new RoundToIntBinarySearchEvaluator.Factory(source, field, f); + }; + }; + + /** + * Search the points array for the match linearly. This is faster for smaller arrays even + * when finding a position late in the array. Presumably because this is super-SIMD-able. + */ + @Evaluator(extraName = "LinearSearch") + static int processLinear(int field, @Fixed(includeInToString = false) int[] points) { + // points is always longer than 3 or we use one of the specialized methods below + for (int i = 1; i < points.length; i++) { + if (field < points[i]) { + return points[i - 1]; + } + } + return points[points.length - 1]; + } + + @Evaluator(extraName = "BinarySearch") + static int process(int field, @Fixed(includeInToString = false) int[] points) { + int idx = Arrays.binarySearch(points, field); + return points[idx >= 0 ? idx : Math.max(0, -idx - 2)]; + } + + @Evaluator(extraName = "1") + static int process(int field, @Fixed int p0) { + return p0; + } + + @Evaluator(extraName = "2") + static int process(int field, @Fixed int p0, @Fixed int p1) { + if (field < p1) { + return p0; + } + return p1; + } + + @Evaluator(extraName = "3") + static int process(int field, @Fixed int p0, @Fixed int p1, @Fixed int p2) { + if (field < p1) { + return p0; + } + if (field < p2) { + return p1; + } + return p2; + } + + @Evaluator(extraName = "4") + static int process(int field, @Fixed int p0, @Fixed int p1, @Fixed int p2, @Fixed int p3) { + if (field < p1) { + return p0; + } + if (field < p2) { + return p1; + } + if (field < p3) { + return p2; + } + return p3; + } +} diff --git a/x-pack/plugin/esql/src/main/generated-src/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToLong.java b/x-pack/plugin/esql/src/main/generated-src/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToLong.java new file mode 100644 index 000000000000..24821b772b35 --- /dev/null +++ b/x-pack/plugin/esql/src/main/generated-src/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToLong.java @@ -0,0 +1,105 @@ +/* + * 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; + +// begin generated imports +import org.elasticsearch.compute.ann.Evaluator; +import org.elasticsearch.compute.ann.Fixed; + +import java.util.Arrays; +// end generated imports + +/** + * Implementations of {@link RoundTo} for specific types. + *

+ * We have specializations for when there are very few rounding points because + * those are very fast and quite common. + *

+ * This class is generated. Edit {@code X-RoundTo.java.st} instead. + */ +class RoundToLong { + static final RoundTo.Build BUILD = (source, field, points) -> { + long[] f = points.stream().mapToLong(p -> ((Number) p).longValue()).toArray(); + Arrays.sort(f); + return switch (f.length) { + // TODO should be a consistent way to do the 0 version - is CASE(MV_COUNT(f) == 1, f[0]) + case 1 -> new RoundToLong1Evaluator.Factory(source, field, f[0]); + /* + * These hand-unrolled implementations are even faster than the linear scan implementations. + */ + case 2 -> new RoundToLong2Evaluator.Factory(source, field, f[0], f[1]); + case 3 -> new RoundToLong3Evaluator.Factory(source, field, f[0], f[1], f[2]); + case 4 -> new RoundToLong4Evaluator.Factory(source, field, f[0], f[1], f[2], f[3]); + /* + * Break point of 10 experimentally derived on Nik's laptop (13th Gen Intel(R) Core(TM) i7-1370P) + * on 2025-05-22. + */ + case 5, 6, 7, 8, 9, 10 -> new RoundToLongLinearSearchEvaluator.Factory(source, field, f); + default -> new RoundToLongBinarySearchEvaluator.Factory(source, field, f); + }; + }; + + /** + * Search the points array for the match linearly. This is faster for smaller arrays even + * when finding a position late in the array. Presumably because this is super-SIMD-able. + */ + @Evaluator(extraName = "LinearSearch") + static long processLinear(long field, @Fixed(includeInToString = false) long[] points) { + // points is always longer than 3 or we use one of the specialized methods below + for (int i = 1; i < points.length; i++) { + if (field < points[i]) { + return points[i - 1]; + } + } + return points[points.length - 1]; + } + + @Evaluator(extraName = "BinarySearch") + static long process(long field, @Fixed(includeInToString = false) long[] points) { + int idx = Arrays.binarySearch(points, field); + return points[idx >= 0 ? idx : Math.max(0, -idx - 2)]; + } + + @Evaluator(extraName = "1") + static long process(long field, @Fixed long p0) { + return p0; + } + + @Evaluator(extraName = "2") + static long process(long field, @Fixed long p0, @Fixed long p1) { + if (field < p1) { + return p0; + } + return p1; + } + + @Evaluator(extraName = "3") + static long process(long field, @Fixed long p0, @Fixed long p1, @Fixed long p2) { + if (field < p1) { + return p0; + } + if (field < p2) { + return p1; + } + return p2; + } + + @Evaluator(extraName = "4") + static long process(long field, @Fixed long p0, @Fixed long p1, @Fixed long p2, @Fixed long p3) { + if (field < p1) { + return p0; + } + if (field < p2) { + return p1; + } + if (field < p3) { + return p2; + } + return p3; + } +} diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToDouble1Evaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToDouble1Evaluator.java new file mode 100644 index 000000000000..baf436c31448 --- /dev/null +++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToDouble1Evaluator.java @@ -0,0 +1,128 @@ +// 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 java.lang.IllegalArgumentException; +import java.lang.Override; +import java.lang.String; +import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.DoubleBlock; +import org.elasticsearch.compute.data.DoubleVector; +import org.elasticsearch.compute.data.Page; +import org.elasticsearch.compute.operator.DriverContext; +import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.compute.operator.Warnings; +import org.elasticsearch.core.Releasables; +import org.elasticsearch.xpack.esql.core.tree.Source; + +/** + * {@link EvalOperator.ExpressionEvaluator} implementation for {@link RoundToDouble}. + * This class is generated. Edit {@code EvaluatorImplementer} instead. + */ +public final class RoundToDouble1Evaluator implements EvalOperator.ExpressionEvaluator { + private final Source source; + + private final EvalOperator.ExpressionEvaluator field; + + private final double p0; + + private final DriverContext driverContext; + + private Warnings warnings; + + public RoundToDouble1Evaluator(Source source, EvalOperator.ExpressionEvaluator field, double p0, + DriverContext driverContext) { + this.source = source; + this.field = field; + this.p0 = p0; + this.driverContext = driverContext; + } + + @Override + public Block eval(Page page) { + try (DoubleBlock fieldBlock = (DoubleBlock) field.eval(page)) { + DoubleVector fieldVector = fieldBlock.asVector(); + if (fieldVector == null) { + return eval(page.getPositionCount(), fieldBlock); + } + return eval(page.getPositionCount(), fieldVector).asBlock(); + } + } + + public DoubleBlock eval(int positionCount, DoubleBlock fieldBlock) { + try(DoubleBlock.Builder result = driverContext.blockFactory().newDoubleBlockBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + if (fieldBlock.isNull(p)) { + result.appendNull(); + continue position; + } + if (fieldBlock.getValueCount(p) != 1) { + if (fieldBlock.getValueCount(p) > 1) { + warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value")); + } + result.appendNull(); + continue position; + } + result.appendDouble(RoundToDouble.process(fieldBlock.getDouble(fieldBlock.getFirstValueIndex(p)), this.p0)); + } + return result.build(); + } + } + + public DoubleVector eval(int positionCount, DoubleVector fieldVector) { + try(DoubleVector.FixedBuilder result = driverContext.blockFactory().newDoubleVectorFixedBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + result.appendDouble(p, RoundToDouble.process(fieldVector.getDouble(p), this.p0)); + } + return result.build(); + } + } + + @Override + public String toString() { + return "RoundToDouble1Evaluator[" + "field=" + field + ", p0=" + p0 + "]"; + } + + @Override + public void close() { + Releasables.closeExpectNoException(field); + } + + private Warnings warnings() { + if (warnings == null) { + this.warnings = Warnings.createWarnings( + driverContext.warningsMode(), + source.source().getLineNumber(), + source.source().getColumnNumber(), + source.text() + ); + } + return warnings; + } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + private final double p0; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory field, double p0) { + this.source = source; + this.field = field; + this.p0 = p0; + } + + @Override + public RoundToDouble1Evaluator get(DriverContext context) { + return new RoundToDouble1Evaluator(source, field.get(context), p0, context); + } + + @Override + public String toString() { + return "RoundToDouble1Evaluator[" + "field=" + field + ", p0=" + p0 + "]"; + } + } +} diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToDouble2Evaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToDouble2Evaluator.java new file mode 100644 index 000000000000..5c61e26a2d2c --- /dev/null +++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToDouble2Evaluator.java @@ -0,0 +1,135 @@ +// 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 java.lang.IllegalArgumentException; +import java.lang.Override; +import java.lang.String; +import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.DoubleBlock; +import org.elasticsearch.compute.data.DoubleVector; +import org.elasticsearch.compute.data.Page; +import org.elasticsearch.compute.operator.DriverContext; +import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.compute.operator.Warnings; +import org.elasticsearch.core.Releasables; +import org.elasticsearch.xpack.esql.core.tree.Source; + +/** + * {@link EvalOperator.ExpressionEvaluator} implementation for {@link RoundToDouble}. + * This class is generated. Edit {@code EvaluatorImplementer} instead. + */ +public final class RoundToDouble2Evaluator implements EvalOperator.ExpressionEvaluator { + private final Source source; + + private final EvalOperator.ExpressionEvaluator field; + + private final double p0; + + private final double p1; + + private final DriverContext driverContext; + + private Warnings warnings; + + public RoundToDouble2Evaluator(Source source, EvalOperator.ExpressionEvaluator field, double p0, + double p1, DriverContext driverContext) { + this.source = source; + this.field = field; + this.p0 = p0; + this.p1 = p1; + this.driverContext = driverContext; + } + + @Override + public Block eval(Page page) { + try (DoubleBlock fieldBlock = (DoubleBlock) field.eval(page)) { + DoubleVector fieldVector = fieldBlock.asVector(); + if (fieldVector == null) { + return eval(page.getPositionCount(), fieldBlock); + } + return eval(page.getPositionCount(), fieldVector).asBlock(); + } + } + + public DoubleBlock eval(int positionCount, DoubleBlock fieldBlock) { + try(DoubleBlock.Builder result = driverContext.blockFactory().newDoubleBlockBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + if (fieldBlock.isNull(p)) { + result.appendNull(); + continue position; + } + if (fieldBlock.getValueCount(p) != 1) { + if (fieldBlock.getValueCount(p) > 1) { + warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value")); + } + result.appendNull(); + continue position; + } + result.appendDouble(RoundToDouble.process(fieldBlock.getDouble(fieldBlock.getFirstValueIndex(p)), this.p0, this.p1)); + } + return result.build(); + } + } + + public DoubleVector eval(int positionCount, DoubleVector fieldVector) { + try(DoubleVector.FixedBuilder result = driverContext.blockFactory().newDoubleVectorFixedBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + result.appendDouble(p, RoundToDouble.process(fieldVector.getDouble(p), this.p0, this.p1)); + } + return result.build(); + } + } + + @Override + public String toString() { + return "RoundToDouble2Evaluator[" + "field=" + field + ", p0=" + p0 + ", p1=" + p1 + "]"; + } + + @Override + public void close() { + Releasables.closeExpectNoException(field); + } + + private Warnings warnings() { + if (warnings == null) { + this.warnings = Warnings.createWarnings( + driverContext.warningsMode(), + source.source().getLineNumber(), + source.source().getColumnNumber(), + source.text() + ); + } + return warnings; + } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + private final double p0; + + private final double p1; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory field, double p0, + double p1) { + this.source = source; + this.field = field; + this.p0 = p0; + this.p1 = p1; + } + + @Override + public RoundToDouble2Evaluator get(DriverContext context) { + return new RoundToDouble2Evaluator(source, field.get(context), p0, p1, context); + } + + @Override + public String toString() { + return "RoundToDouble2Evaluator[" + "field=" + field + ", p0=" + p0 + ", p1=" + p1 + "]"; + } + } +} diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToDouble3Evaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToDouble3Evaluator.java new file mode 100644 index 000000000000..002f05385d3b --- /dev/null +++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToDouble3Evaluator.java @@ -0,0 +1,141 @@ +// 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 java.lang.IllegalArgumentException; +import java.lang.Override; +import java.lang.String; +import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.DoubleBlock; +import org.elasticsearch.compute.data.DoubleVector; +import org.elasticsearch.compute.data.Page; +import org.elasticsearch.compute.operator.DriverContext; +import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.compute.operator.Warnings; +import org.elasticsearch.core.Releasables; +import org.elasticsearch.xpack.esql.core.tree.Source; + +/** + * {@link EvalOperator.ExpressionEvaluator} implementation for {@link RoundToDouble}. + * This class is generated. Edit {@code EvaluatorImplementer} instead. + */ +public final class RoundToDouble3Evaluator implements EvalOperator.ExpressionEvaluator { + private final Source source; + + private final EvalOperator.ExpressionEvaluator field; + + private final double p0; + + private final double p1; + + private final double p2; + + private final DriverContext driverContext; + + private Warnings warnings; + + public RoundToDouble3Evaluator(Source source, EvalOperator.ExpressionEvaluator field, double p0, + double p1, double p2, DriverContext driverContext) { + this.source = source; + this.field = field; + this.p0 = p0; + this.p1 = p1; + this.p2 = p2; + this.driverContext = driverContext; + } + + @Override + public Block eval(Page page) { + try (DoubleBlock fieldBlock = (DoubleBlock) field.eval(page)) { + DoubleVector fieldVector = fieldBlock.asVector(); + if (fieldVector == null) { + return eval(page.getPositionCount(), fieldBlock); + } + return eval(page.getPositionCount(), fieldVector).asBlock(); + } + } + + public DoubleBlock eval(int positionCount, DoubleBlock fieldBlock) { + try(DoubleBlock.Builder result = driverContext.blockFactory().newDoubleBlockBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + if (fieldBlock.isNull(p)) { + result.appendNull(); + continue position; + } + if (fieldBlock.getValueCount(p) != 1) { + if (fieldBlock.getValueCount(p) > 1) { + warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value")); + } + result.appendNull(); + continue position; + } + result.appendDouble(RoundToDouble.process(fieldBlock.getDouble(fieldBlock.getFirstValueIndex(p)), this.p0, this.p1, this.p2)); + } + return result.build(); + } + } + + public DoubleVector eval(int positionCount, DoubleVector fieldVector) { + try(DoubleVector.FixedBuilder result = driverContext.blockFactory().newDoubleVectorFixedBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + result.appendDouble(p, RoundToDouble.process(fieldVector.getDouble(p), this.p0, this.p1, this.p2)); + } + return result.build(); + } + } + + @Override + public String toString() { + return "RoundToDouble3Evaluator[" + "field=" + field + ", p0=" + p0 + ", p1=" + p1 + ", p2=" + p2 + "]"; + } + + @Override + public void close() { + Releasables.closeExpectNoException(field); + } + + private Warnings warnings() { + if (warnings == null) { + this.warnings = Warnings.createWarnings( + driverContext.warningsMode(), + source.source().getLineNumber(), + source.source().getColumnNumber(), + source.text() + ); + } + return warnings; + } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + private final double p0; + + private final double p1; + + private final double p2; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory field, double p0, + double p1, double p2) { + this.source = source; + this.field = field; + this.p0 = p0; + this.p1 = p1; + this.p2 = p2; + } + + @Override + public RoundToDouble3Evaluator get(DriverContext context) { + return new RoundToDouble3Evaluator(source, field.get(context), p0, p1, p2, context); + } + + @Override + public String toString() { + return "RoundToDouble3Evaluator[" + "field=" + field + ", p0=" + p0 + ", p1=" + p1 + ", p2=" + p2 + "]"; + } + } +} diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToDouble4Evaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToDouble4Evaluator.java new file mode 100644 index 000000000000..58dac768f76f --- /dev/null +++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToDouble4Evaluator.java @@ -0,0 +1,147 @@ +// 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 java.lang.IllegalArgumentException; +import java.lang.Override; +import java.lang.String; +import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.DoubleBlock; +import org.elasticsearch.compute.data.DoubleVector; +import org.elasticsearch.compute.data.Page; +import org.elasticsearch.compute.operator.DriverContext; +import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.compute.operator.Warnings; +import org.elasticsearch.core.Releasables; +import org.elasticsearch.xpack.esql.core.tree.Source; + +/** + * {@link EvalOperator.ExpressionEvaluator} implementation for {@link RoundToDouble}. + * This class is generated. Edit {@code EvaluatorImplementer} instead. + */ +public final class RoundToDouble4Evaluator implements EvalOperator.ExpressionEvaluator { + private final Source source; + + private final EvalOperator.ExpressionEvaluator field; + + private final double p0; + + private final double p1; + + private final double p2; + + private final double p3; + + private final DriverContext driverContext; + + private Warnings warnings; + + public RoundToDouble4Evaluator(Source source, EvalOperator.ExpressionEvaluator field, double p0, + double p1, double p2, double p3, DriverContext driverContext) { + this.source = source; + this.field = field; + this.p0 = p0; + this.p1 = p1; + this.p2 = p2; + this.p3 = p3; + this.driverContext = driverContext; + } + + @Override + public Block eval(Page page) { + try (DoubleBlock fieldBlock = (DoubleBlock) field.eval(page)) { + DoubleVector fieldVector = fieldBlock.asVector(); + if (fieldVector == null) { + return eval(page.getPositionCount(), fieldBlock); + } + return eval(page.getPositionCount(), fieldVector).asBlock(); + } + } + + public DoubleBlock eval(int positionCount, DoubleBlock fieldBlock) { + try(DoubleBlock.Builder result = driverContext.blockFactory().newDoubleBlockBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + if (fieldBlock.isNull(p)) { + result.appendNull(); + continue position; + } + if (fieldBlock.getValueCount(p) != 1) { + if (fieldBlock.getValueCount(p) > 1) { + warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value")); + } + result.appendNull(); + continue position; + } + result.appendDouble(RoundToDouble.process(fieldBlock.getDouble(fieldBlock.getFirstValueIndex(p)), this.p0, this.p1, this.p2, this.p3)); + } + return result.build(); + } + } + + public DoubleVector eval(int positionCount, DoubleVector fieldVector) { + try(DoubleVector.FixedBuilder result = driverContext.blockFactory().newDoubleVectorFixedBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + result.appendDouble(p, RoundToDouble.process(fieldVector.getDouble(p), this.p0, this.p1, this.p2, this.p3)); + } + return result.build(); + } + } + + @Override + public String toString() { + return "RoundToDouble4Evaluator[" + "field=" + field + ", p0=" + p0 + ", p1=" + p1 + ", p2=" + p2 + ", p3=" + p3 + "]"; + } + + @Override + public void close() { + Releasables.closeExpectNoException(field); + } + + private Warnings warnings() { + if (warnings == null) { + this.warnings = Warnings.createWarnings( + driverContext.warningsMode(), + source.source().getLineNumber(), + source.source().getColumnNumber(), + source.text() + ); + } + return warnings; + } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + private final double p0; + + private final double p1; + + private final double p2; + + private final double p3; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory field, double p0, + double p1, double p2, double p3) { + this.source = source; + this.field = field; + this.p0 = p0; + this.p1 = p1; + this.p2 = p2; + this.p3 = p3; + } + + @Override + public RoundToDouble4Evaluator get(DriverContext context) { + return new RoundToDouble4Evaluator(source, field.get(context), p0, p1, p2, p3, context); + } + + @Override + public String toString() { + return "RoundToDouble4Evaluator[" + "field=" + field + ", p0=" + p0 + ", p1=" + p1 + ", p2=" + p2 + ", p3=" + p3 + "]"; + } + } +} diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToDoubleBinarySearchEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToDoubleBinarySearchEvaluator.java new file mode 100644 index 000000000000..da49b9c9a099 --- /dev/null +++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToDoubleBinarySearchEvaluator.java @@ -0,0 +1,128 @@ +// 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 java.lang.IllegalArgumentException; +import java.lang.Override; +import java.lang.String; +import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.DoubleBlock; +import org.elasticsearch.compute.data.DoubleVector; +import org.elasticsearch.compute.data.Page; +import org.elasticsearch.compute.operator.DriverContext; +import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.compute.operator.Warnings; +import org.elasticsearch.core.Releasables; +import org.elasticsearch.xpack.esql.core.tree.Source; + +/** + * {@link EvalOperator.ExpressionEvaluator} implementation for {@link RoundToDouble}. + * This class is generated. Edit {@code EvaluatorImplementer} instead. + */ +public final class RoundToDoubleBinarySearchEvaluator implements EvalOperator.ExpressionEvaluator { + private final Source source; + + private final EvalOperator.ExpressionEvaluator field; + + private final double[] points; + + private final DriverContext driverContext; + + private Warnings warnings; + + public RoundToDoubleBinarySearchEvaluator(Source source, EvalOperator.ExpressionEvaluator field, + double[] points, DriverContext driverContext) { + this.source = source; + this.field = field; + this.points = points; + this.driverContext = driverContext; + } + + @Override + public Block eval(Page page) { + try (DoubleBlock fieldBlock = (DoubleBlock) field.eval(page)) { + DoubleVector fieldVector = fieldBlock.asVector(); + if (fieldVector == null) { + return eval(page.getPositionCount(), fieldBlock); + } + return eval(page.getPositionCount(), fieldVector).asBlock(); + } + } + + public DoubleBlock eval(int positionCount, DoubleBlock fieldBlock) { + try(DoubleBlock.Builder result = driverContext.blockFactory().newDoubleBlockBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + if (fieldBlock.isNull(p)) { + result.appendNull(); + continue position; + } + if (fieldBlock.getValueCount(p) != 1) { + if (fieldBlock.getValueCount(p) > 1) { + warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value")); + } + result.appendNull(); + continue position; + } + result.appendDouble(RoundToDouble.process(fieldBlock.getDouble(fieldBlock.getFirstValueIndex(p)), this.points)); + } + return result.build(); + } + } + + public DoubleVector eval(int positionCount, DoubleVector fieldVector) { + try(DoubleVector.FixedBuilder result = driverContext.blockFactory().newDoubleVectorFixedBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + result.appendDouble(p, RoundToDouble.process(fieldVector.getDouble(p), this.points)); + } + return result.build(); + } + } + + @Override + public String toString() { + return "RoundToDoubleBinarySearchEvaluator[" + "field=" + field + "]"; + } + + @Override + public void close() { + Releasables.closeExpectNoException(field); + } + + private Warnings warnings() { + if (warnings == null) { + this.warnings = Warnings.createWarnings( + driverContext.warningsMode(), + source.source().getLineNumber(), + source.source().getColumnNumber(), + source.text() + ); + } + return warnings; + } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + private final double[] points; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory field, double[] points) { + this.source = source; + this.field = field; + this.points = points; + } + + @Override + public RoundToDoubleBinarySearchEvaluator get(DriverContext context) { + return new RoundToDoubleBinarySearchEvaluator(source, field.get(context), points, context); + } + + @Override + public String toString() { + return "RoundToDoubleBinarySearchEvaluator[" + "field=" + field + "]"; + } + } +} diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToDoubleLinearSearchEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToDoubleLinearSearchEvaluator.java new file mode 100644 index 000000000000..8485e6576f82 --- /dev/null +++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToDoubleLinearSearchEvaluator.java @@ -0,0 +1,128 @@ +// 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 java.lang.IllegalArgumentException; +import java.lang.Override; +import java.lang.String; +import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.DoubleBlock; +import org.elasticsearch.compute.data.DoubleVector; +import org.elasticsearch.compute.data.Page; +import org.elasticsearch.compute.operator.DriverContext; +import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.compute.operator.Warnings; +import org.elasticsearch.core.Releasables; +import org.elasticsearch.xpack.esql.core.tree.Source; + +/** + * {@link EvalOperator.ExpressionEvaluator} implementation for {@link RoundToDouble}. + * This class is generated. Edit {@code EvaluatorImplementer} instead. + */ +public final class RoundToDoubleLinearSearchEvaluator implements EvalOperator.ExpressionEvaluator { + private final Source source; + + private final EvalOperator.ExpressionEvaluator field; + + private final double[] points; + + private final DriverContext driverContext; + + private Warnings warnings; + + public RoundToDoubleLinearSearchEvaluator(Source source, EvalOperator.ExpressionEvaluator field, + double[] points, DriverContext driverContext) { + this.source = source; + this.field = field; + this.points = points; + this.driverContext = driverContext; + } + + @Override + public Block eval(Page page) { + try (DoubleBlock fieldBlock = (DoubleBlock) field.eval(page)) { + DoubleVector fieldVector = fieldBlock.asVector(); + if (fieldVector == null) { + return eval(page.getPositionCount(), fieldBlock); + } + return eval(page.getPositionCount(), fieldVector).asBlock(); + } + } + + public DoubleBlock eval(int positionCount, DoubleBlock fieldBlock) { + try(DoubleBlock.Builder result = driverContext.blockFactory().newDoubleBlockBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + if (fieldBlock.isNull(p)) { + result.appendNull(); + continue position; + } + if (fieldBlock.getValueCount(p) != 1) { + if (fieldBlock.getValueCount(p) > 1) { + warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value")); + } + result.appendNull(); + continue position; + } + result.appendDouble(RoundToDouble.processLinear(fieldBlock.getDouble(fieldBlock.getFirstValueIndex(p)), this.points)); + } + return result.build(); + } + } + + public DoubleVector eval(int positionCount, DoubleVector fieldVector) { + try(DoubleVector.FixedBuilder result = driverContext.blockFactory().newDoubleVectorFixedBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + result.appendDouble(p, RoundToDouble.processLinear(fieldVector.getDouble(p), this.points)); + } + return result.build(); + } + } + + @Override + public String toString() { + return "RoundToDoubleLinearSearchEvaluator[" + "field=" + field + "]"; + } + + @Override + public void close() { + Releasables.closeExpectNoException(field); + } + + private Warnings warnings() { + if (warnings == null) { + this.warnings = Warnings.createWarnings( + driverContext.warningsMode(), + source.source().getLineNumber(), + source.source().getColumnNumber(), + source.text() + ); + } + return warnings; + } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + private final double[] points; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory field, double[] points) { + this.source = source; + this.field = field; + this.points = points; + } + + @Override + public RoundToDoubleLinearSearchEvaluator get(DriverContext context) { + return new RoundToDoubleLinearSearchEvaluator(source, field.get(context), points, context); + } + + @Override + public String toString() { + return "RoundToDoubleLinearSearchEvaluator[" + "field=" + field + "]"; + } + } +} diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToInt1Evaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToInt1Evaluator.java new file mode 100644 index 000000000000..37d2ecc276d0 --- /dev/null +++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToInt1Evaluator.java @@ -0,0 +1,128 @@ +// 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 java.lang.IllegalArgumentException; +import java.lang.Override; +import java.lang.String; +import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.IntBlock; +import org.elasticsearch.compute.data.IntVector; +import org.elasticsearch.compute.data.Page; +import org.elasticsearch.compute.operator.DriverContext; +import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.compute.operator.Warnings; +import org.elasticsearch.core.Releasables; +import org.elasticsearch.xpack.esql.core.tree.Source; + +/** + * {@link EvalOperator.ExpressionEvaluator} implementation for {@link RoundToInt}. + * This class is generated. Edit {@code EvaluatorImplementer} instead. + */ +public final class RoundToInt1Evaluator implements EvalOperator.ExpressionEvaluator { + private final Source source; + + private final EvalOperator.ExpressionEvaluator field; + + private final int p0; + + private final DriverContext driverContext; + + private Warnings warnings; + + public RoundToInt1Evaluator(Source source, EvalOperator.ExpressionEvaluator field, int p0, + DriverContext driverContext) { + this.source = source; + this.field = field; + this.p0 = p0; + this.driverContext = driverContext; + } + + @Override + public Block eval(Page page) { + try (IntBlock fieldBlock = (IntBlock) field.eval(page)) { + IntVector fieldVector = fieldBlock.asVector(); + if (fieldVector == null) { + return eval(page.getPositionCount(), fieldBlock); + } + return eval(page.getPositionCount(), fieldVector).asBlock(); + } + } + + public IntBlock eval(int positionCount, IntBlock fieldBlock) { + try(IntBlock.Builder result = driverContext.blockFactory().newIntBlockBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + if (fieldBlock.isNull(p)) { + result.appendNull(); + continue position; + } + if (fieldBlock.getValueCount(p) != 1) { + if (fieldBlock.getValueCount(p) > 1) { + warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value")); + } + result.appendNull(); + continue position; + } + result.appendInt(RoundToInt.process(fieldBlock.getInt(fieldBlock.getFirstValueIndex(p)), this.p0)); + } + return result.build(); + } + } + + public IntVector eval(int positionCount, IntVector fieldVector) { + try(IntVector.FixedBuilder result = driverContext.blockFactory().newIntVectorFixedBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + result.appendInt(p, RoundToInt.process(fieldVector.getInt(p), this.p0)); + } + return result.build(); + } + } + + @Override + public String toString() { + return "RoundToInt1Evaluator[" + "field=" + field + ", p0=" + p0 + "]"; + } + + @Override + public void close() { + Releasables.closeExpectNoException(field); + } + + private Warnings warnings() { + if (warnings == null) { + this.warnings = Warnings.createWarnings( + driverContext.warningsMode(), + source.source().getLineNumber(), + source.source().getColumnNumber(), + source.text() + ); + } + return warnings; + } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + private final int p0; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory field, int p0) { + this.source = source; + this.field = field; + this.p0 = p0; + } + + @Override + public RoundToInt1Evaluator get(DriverContext context) { + return new RoundToInt1Evaluator(source, field.get(context), p0, context); + } + + @Override + public String toString() { + return "RoundToInt1Evaluator[" + "field=" + field + ", p0=" + p0 + "]"; + } + } +} diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToInt2Evaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToInt2Evaluator.java new file mode 100644 index 000000000000..e0fc9bd130fe --- /dev/null +++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToInt2Evaluator.java @@ -0,0 +1,134 @@ +// 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 java.lang.IllegalArgumentException; +import java.lang.Override; +import java.lang.String; +import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.IntBlock; +import org.elasticsearch.compute.data.IntVector; +import org.elasticsearch.compute.data.Page; +import org.elasticsearch.compute.operator.DriverContext; +import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.compute.operator.Warnings; +import org.elasticsearch.core.Releasables; +import org.elasticsearch.xpack.esql.core.tree.Source; + +/** + * {@link EvalOperator.ExpressionEvaluator} implementation for {@link RoundToInt}. + * This class is generated. Edit {@code EvaluatorImplementer} instead. + */ +public final class RoundToInt2Evaluator implements EvalOperator.ExpressionEvaluator { + private final Source source; + + private final EvalOperator.ExpressionEvaluator field; + + private final int p0; + + private final int p1; + + private final DriverContext driverContext; + + private Warnings warnings; + + public RoundToInt2Evaluator(Source source, EvalOperator.ExpressionEvaluator field, int p0, int p1, + DriverContext driverContext) { + this.source = source; + this.field = field; + this.p0 = p0; + this.p1 = p1; + this.driverContext = driverContext; + } + + @Override + public Block eval(Page page) { + try (IntBlock fieldBlock = (IntBlock) field.eval(page)) { + IntVector fieldVector = fieldBlock.asVector(); + if (fieldVector == null) { + return eval(page.getPositionCount(), fieldBlock); + } + return eval(page.getPositionCount(), fieldVector).asBlock(); + } + } + + public IntBlock eval(int positionCount, IntBlock fieldBlock) { + try(IntBlock.Builder result = driverContext.blockFactory().newIntBlockBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + if (fieldBlock.isNull(p)) { + result.appendNull(); + continue position; + } + if (fieldBlock.getValueCount(p) != 1) { + if (fieldBlock.getValueCount(p) > 1) { + warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value")); + } + result.appendNull(); + continue position; + } + result.appendInt(RoundToInt.process(fieldBlock.getInt(fieldBlock.getFirstValueIndex(p)), this.p0, this.p1)); + } + return result.build(); + } + } + + public IntVector eval(int positionCount, IntVector fieldVector) { + try(IntVector.FixedBuilder result = driverContext.blockFactory().newIntVectorFixedBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + result.appendInt(p, RoundToInt.process(fieldVector.getInt(p), this.p0, this.p1)); + } + return result.build(); + } + } + + @Override + public String toString() { + return "RoundToInt2Evaluator[" + "field=" + field + ", p0=" + p0 + ", p1=" + p1 + "]"; + } + + @Override + public void close() { + Releasables.closeExpectNoException(field); + } + + private Warnings warnings() { + if (warnings == null) { + this.warnings = Warnings.createWarnings( + driverContext.warningsMode(), + source.source().getLineNumber(), + source.source().getColumnNumber(), + source.text() + ); + } + return warnings; + } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + private final int p0; + + private final int p1; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory field, int p0, int p1) { + this.source = source; + this.field = field; + this.p0 = p0; + this.p1 = p1; + } + + @Override + public RoundToInt2Evaluator get(DriverContext context) { + return new RoundToInt2Evaluator(source, field.get(context), p0, p1, context); + } + + @Override + public String toString() { + return "RoundToInt2Evaluator[" + "field=" + field + ", p0=" + p0 + ", p1=" + p1 + "]"; + } + } +} diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToInt3Evaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToInt3Evaluator.java new file mode 100644 index 000000000000..d426c15c38ee --- /dev/null +++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToInt3Evaluator.java @@ -0,0 +1,141 @@ +// 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 java.lang.IllegalArgumentException; +import java.lang.Override; +import java.lang.String; +import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.IntBlock; +import org.elasticsearch.compute.data.IntVector; +import org.elasticsearch.compute.data.Page; +import org.elasticsearch.compute.operator.DriverContext; +import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.compute.operator.Warnings; +import org.elasticsearch.core.Releasables; +import org.elasticsearch.xpack.esql.core.tree.Source; + +/** + * {@link EvalOperator.ExpressionEvaluator} implementation for {@link RoundToInt}. + * This class is generated. Edit {@code EvaluatorImplementer} instead. + */ +public final class RoundToInt3Evaluator implements EvalOperator.ExpressionEvaluator { + private final Source source; + + private final EvalOperator.ExpressionEvaluator field; + + private final int p0; + + private final int p1; + + private final int p2; + + private final DriverContext driverContext; + + private Warnings warnings; + + public RoundToInt3Evaluator(Source source, EvalOperator.ExpressionEvaluator field, int p0, int p1, + int p2, DriverContext driverContext) { + this.source = source; + this.field = field; + this.p0 = p0; + this.p1 = p1; + this.p2 = p2; + this.driverContext = driverContext; + } + + @Override + public Block eval(Page page) { + try (IntBlock fieldBlock = (IntBlock) field.eval(page)) { + IntVector fieldVector = fieldBlock.asVector(); + if (fieldVector == null) { + return eval(page.getPositionCount(), fieldBlock); + } + return eval(page.getPositionCount(), fieldVector).asBlock(); + } + } + + public IntBlock eval(int positionCount, IntBlock fieldBlock) { + try(IntBlock.Builder result = driverContext.blockFactory().newIntBlockBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + if (fieldBlock.isNull(p)) { + result.appendNull(); + continue position; + } + if (fieldBlock.getValueCount(p) != 1) { + if (fieldBlock.getValueCount(p) > 1) { + warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value")); + } + result.appendNull(); + continue position; + } + result.appendInt(RoundToInt.process(fieldBlock.getInt(fieldBlock.getFirstValueIndex(p)), this.p0, this.p1, this.p2)); + } + return result.build(); + } + } + + public IntVector eval(int positionCount, IntVector fieldVector) { + try(IntVector.FixedBuilder result = driverContext.blockFactory().newIntVectorFixedBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + result.appendInt(p, RoundToInt.process(fieldVector.getInt(p), this.p0, this.p1, this.p2)); + } + return result.build(); + } + } + + @Override + public String toString() { + return "RoundToInt3Evaluator[" + "field=" + field + ", p0=" + p0 + ", p1=" + p1 + ", p2=" + p2 + "]"; + } + + @Override + public void close() { + Releasables.closeExpectNoException(field); + } + + private Warnings warnings() { + if (warnings == null) { + this.warnings = Warnings.createWarnings( + driverContext.warningsMode(), + source.source().getLineNumber(), + source.source().getColumnNumber(), + source.text() + ); + } + return warnings; + } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + private final int p0; + + private final int p1; + + private final int p2; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory field, int p0, int p1, + int p2) { + this.source = source; + this.field = field; + this.p0 = p0; + this.p1 = p1; + this.p2 = p2; + } + + @Override + public RoundToInt3Evaluator get(DriverContext context) { + return new RoundToInt3Evaluator(source, field.get(context), p0, p1, p2, context); + } + + @Override + public String toString() { + return "RoundToInt3Evaluator[" + "field=" + field + ", p0=" + p0 + ", p1=" + p1 + ", p2=" + p2 + "]"; + } + } +} diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToInt4Evaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToInt4Evaluator.java new file mode 100644 index 000000000000..8f47de82b63a --- /dev/null +++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToInt4Evaluator.java @@ -0,0 +1,147 @@ +// 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 java.lang.IllegalArgumentException; +import java.lang.Override; +import java.lang.String; +import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.IntBlock; +import org.elasticsearch.compute.data.IntVector; +import org.elasticsearch.compute.data.Page; +import org.elasticsearch.compute.operator.DriverContext; +import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.compute.operator.Warnings; +import org.elasticsearch.core.Releasables; +import org.elasticsearch.xpack.esql.core.tree.Source; + +/** + * {@link EvalOperator.ExpressionEvaluator} implementation for {@link RoundToInt}. + * This class is generated. Edit {@code EvaluatorImplementer} instead. + */ +public final class RoundToInt4Evaluator implements EvalOperator.ExpressionEvaluator { + private final Source source; + + private final EvalOperator.ExpressionEvaluator field; + + private final int p0; + + private final int p1; + + private final int p2; + + private final int p3; + + private final DriverContext driverContext; + + private Warnings warnings; + + public RoundToInt4Evaluator(Source source, EvalOperator.ExpressionEvaluator field, int p0, int p1, + int p2, int p3, DriverContext driverContext) { + this.source = source; + this.field = field; + this.p0 = p0; + this.p1 = p1; + this.p2 = p2; + this.p3 = p3; + this.driverContext = driverContext; + } + + @Override + public Block eval(Page page) { + try (IntBlock fieldBlock = (IntBlock) field.eval(page)) { + IntVector fieldVector = fieldBlock.asVector(); + if (fieldVector == null) { + return eval(page.getPositionCount(), fieldBlock); + } + return eval(page.getPositionCount(), fieldVector).asBlock(); + } + } + + public IntBlock eval(int positionCount, IntBlock fieldBlock) { + try(IntBlock.Builder result = driverContext.blockFactory().newIntBlockBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + if (fieldBlock.isNull(p)) { + result.appendNull(); + continue position; + } + if (fieldBlock.getValueCount(p) != 1) { + if (fieldBlock.getValueCount(p) > 1) { + warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value")); + } + result.appendNull(); + continue position; + } + result.appendInt(RoundToInt.process(fieldBlock.getInt(fieldBlock.getFirstValueIndex(p)), this.p0, this.p1, this.p2, this.p3)); + } + return result.build(); + } + } + + public IntVector eval(int positionCount, IntVector fieldVector) { + try(IntVector.FixedBuilder result = driverContext.blockFactory().newIntVectorFixedBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + result.appendInt(p, RoundToInt.process(fieldVector.getInt(p), this.p0, this.p1, this.p2, this.p3)); + } + return result.build(); + } + } + + @Override + public String toString() { + return "RoundToInt4Evaluator[" + "field=" + field + ", p0=" + p0 + ", p1=" + p1 + ", p2=" + p2 + ", p3=" + p3 + "]"; + } + + @Override + public void close() { + Releasables.closeExpectNoException(field); + } + + private Warnings warnings() { + if (warnings == null) { + this.warnings = Warnings.createWarnings( + driverContext.warningsMode(), + source.source().getLineNumber(), + source.source().getColumnNumber(), + source.text() + ); + } + return warnings; + } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + private final int p0; + + private final int p1; + + private final int p2; + + private final int p3; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory field, int p0, int p1, + int p2, int p3) { + this.source = source; + this.field = field; + this.p0 = p0; + this.p1 = p1; + this.p2 = p2; + this.p3 = p3; + } + + @Override + public RoundToInt4Evaluator get(DriverContext context) { + return new RoundToInt4Evaluator(source, field.get(context), p0, p1, p2, p3, context); + } + + @Override + public String toString() { + return "RoundToInt4Evaluator[" + "field=" + field + ", p0=" + p0 + ", p1=" + p1 + ", p2=" + p2 + ", p3=" + p3 + "]"; + } + } +} diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToIntBinarySearchEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToIntBinarySearchEvaluator.java new file mode 100644 index 000000000000..e0bb8becdcca --- /dev/null +++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToIntBinarySearchEvaluator.java @@ -0,0 +1,128 @@ +// 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 java.lang.IllegalArgumentException; +import java.lang.Override; +import java.lang.String; +import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.IntBlock; +import org.elasticsearch.compute.data.IntVector; +import org.elasticsearch.compute.data.Page; +import org.elasticsearch.compute.operator.DriverContext; +import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.compute.operator.Warnings; +import org.elasticsearch.core.Releasables; +import org.elasticsearch.xpack.esql.core.tree.Source; + +/** + * {@link EvalOperator.ExpressionEvaluator} implementation for {@link RoundToInt}. + * This class is generated. Edit {@code EvaluatorImplementer} instead. + */ +public final class RoundToIntBinarySearchEvaluator implements EvalOperator.ExpressionEvaluator { + private final Source source; + + private final EvalOperator.ExpressionEvaluator field; + + private final int[] points; + + private final DriverContext driverContext; + + private Warnings warnings; + + public RoundToIntBinarySearchEvaluator(Source source, EvalOperator.ExpressionEvaluator field, + int[] points, DriverContext driverContext) { + this.source = source; + this.field = field; + this.points = points; + this.driverContext = driverContext; + } + + @Override + public Block eval(Page page) { + try (IntBlock fieldBlock = (IntBlock) field.eval(page)) { + IntVector fieldVector = fieldBlock.asVector(); + if (fieldVector == null) { + return eval(page.getPositionCount(), fieldBlock); + } + return eval(page.getPositionCount(), fieldVector).asBlock(); + } + } + + public IntBlock eval(int positionCount, IntBlock fieldBlock) { + try(IntBlock.Builder result = driverContext.blockFactory().newIntBlockBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + if (fieldBlock.isNull(p)) { + result.appendNull(); + continue position; + } + if (fieldBlock.getValueCount(p) != 1) { + if (fieldBlock.getValueCount(p) > 1) { + warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value")); + } + result.appendNull(); + continue position; + } + result.appendInt(RoundToInt.process(fieldBlock.getInt(fieldBlock.getFirstValueIndex(p)), this.points)); + } + return result.build(); + } + } + + public IntVector eval(int positionCount, IntVector fieldVector) { + try(IntVector.FixedBuilder result = driverContext.blockFactory().newIntVectorFixedBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + result.appendInt(p, RoundToInt.process(fieldVector.getInt(p), this.points)); + } + return result.build(); + } + } + + @Override + public String toString() { + return "RoundToIntBinarySearchEvaluator[" + "field=" + field + "]"; + } + + @Override + public void close() { + Releasables.closeExpectNoException(field); + } + + private Warnings warnings() { + if (warnings == null) { + this.warnings = Warnings.createWarnings( + driverContext.warningsMode(), + source.source().getLineNumber(), + source.source().getColumnNumber(), + source.text() + ); + } + return warnings; + } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + private final int[] points; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory field, int[] points) { + this.source = source; + this.field = field; + this.points = points; + } + + @Override + public RoundToIntBinarySearchEvaluator get(DriverContext context) { + return new RoundToIntBinarySearchEvaluator(source, field.get(context), points, context); + } + + @Override + public String toString() { + return "RoundToIntBinarySearchEvaluator[" + "field=" + field + "]"; + } + } +} diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToIntLinearSearchEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToIntLinearSearchEvaluator.java new file mode 100644 index 000000000000..ed703c3c994f --- /dev/null +++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToIntLinearSearchEvaluator.java @@ -0,0 +1,128 @@ +// 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 java.lang.IllegalArgumentException; +import java.lang.Override; +import java.lang.String; +import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.IntBlock; +import org.elasticsearch.compute.data.IntVector; +import org.elasticsearch.compute.data.Page; +import org.elasticsearch.compute.operator.DriverContext; +import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.compute.operator.Warnings; +import org.elasticsearch.core.Releasables; +import org.elasticsearch.xpack.esql.core.tree.Source; + +/** + * {@link EvalOperator.ExpressionEvaluator} implementation for {@link RoundToInt}. + * This class is generated. Edit {@code EvaluatorImplementer} instead. + */ +public final class RoundToIntLinearSearchEvaluator implements EvalOperator.ExpressionEvaluator { + private final Source source; + + private final EvalOperator.ExpressionEvaluator field; + + private final int[] points; + + private final DriverContext driverContext; + + private Warnings warnings; + + public RoundToIntLinearSearchEvaluator(Source source, EvalOperator.ExpressionEvaluator field, + int[] points, DriverContext driverContext) { + this.source = source; + this.field = field; + this.points = points; + this.driverContext = driverContext; + } + + @Override + public Block eval(Page page) { + try (IntBlock fieldBlock = (IntBlock) field.eval(page)) { + IntVector fieldVector = fieldBlock.asVector(); + if (fieldVector == null) { + return eval(page.getPositionCount(), fieldBlock); + } + return eval(page.getPositionCount(), fieldVector).asBlock(); + } + } + + public IntBlock eval(int positionCount, IntBlock fieldBlock) { + try(IntBlock.Builder result = driverContext.blockFactory().newIntBlockBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + if (fieldBlock.isNull(p)) { + result.appendNull(); + continue position; + } + if (fieldBlock.getValueCount(p) != 1) { + if (fieldBlock.getValueCount(p) > 1) { + warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value")); + } + result.appendNull(); + continue position; + } + result.appendInt(RoundToInt.processLinear(fieldBlock.getInt(fieldBlock.getFirstValueIndex(p)), this.points)); + } + return result.build(); + } + } + + public IntVector eval(int positionCount, IntVector fieldVector) { + try(IntVector.FixedBuilder result = driverContext.blockFactory().newIntVectorFixedBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + result.appendInt(p, RoundToInt.processLinear(fieldVector.getInt(p), this.points)); + } + return result.build(); + } + } + + @Override + public String toString() { + return "RoundToIntLinearSearchEvaluator[" + "field=" + field + "]"; + } + + @Override + public void close() { + Releasables.closeExpectNoException(field); + } + + private Warnings warnings() { + if (warnings == null) { + this.warnings = Warnings.createWarnings( + driverContext.warningsMode(), + source.source().getLineNumber(), + source.source().getColumnNumber(), + source.text() + ); + } + return warnings; + } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + private final int[] points; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory field, int[] points) { + this.source = source; + this.field = field; + this.points = points; + } + + @Override + public RoundToIntLinearSearchEvaluator get(DriverContext context) { + return new RoundToIntLinearSearchEvaluator(source, field.get(context), points, context); + } + + @Override + public String toString() { + return "RoundToIntLinearSearchEvaluator[" + "field=" + field + "]"; + } + } +} diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToLong1Evaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToLong1Evaluator.java new file mode 100644 index 000000000000..d753cefa6a59 --- /dev/null +++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToLong1Evaluator.java @@ -0,0 +1,128 @@ +// 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 java.lang.IllegalArgumentException; +import java.lang.Override; +import java.lang.String; +import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.LongBlock; +import org.elasticsearch.compute.data.LongVector; +import org.elasticsearch.compute.data.Page; +import org.elasticsearch.compute.operator.DriverContext; +import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.compute.operator.Warnings; +import org.elasticsearch.core.Releasables; +import org.elasticsearch.xpack.esql.core.tree.Source; + +/** + * {@link EvalOperator.ExpressionEvaluator} implementation for {@link RoundToLong}. + * This class is generated. Edit {@code EvaluatorImplementer} instead. + */ +public final class RoundToLong1Evaluator implements EvalOperator.ExpressionEvaluator { + private final Source source; + + private final EvalOperator.ExpressionEvaluator field; + + private final long p0; + + private final DriverContext driverContext; + + private Warnings warnings; + + public RoundToLong1Evaluator(Source source, EvalOperator.ExpressionEvaluator field, long p0, + DriverContext driverContext) { + this.source = source; + this.field = field; + this.p0 = p0; + this.driverContext = driverContext; + } + + @Override + public Block eval(Page page) { + try (LongBlock fieldBlock = (LongBlock) field.eval(page)) { + LongVector fieldVector = fieldBlock.asVector(); + if (fieldVector == null) { + return eval(page.getPositionCount(), fieldBlock); + } + return eval(page.getPositionCount(), fieldVector).asBlock(); + } + } + + public LongBlock eval(int positionCount, LongBlock fieldBlock) { + try(LongBlock.Builder result = driverContext.blockFactory().newLongBlockBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + if (fieldBlock.isNull(p)) { + result.appendNull(); + continue position; + } + if (fieldBlock.getValueCount(p) != 1) { + if (fieldBlock.getValueCount(p) > 1) { + warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value")); + } + result.appendNull(); + continue position; + } + result.appendLong(RoundToLong.process(fieldBlock.getLong(fieldBlock.getFirstValueIndex(p)), this.p0)); + } + return result.build(); + } + } + + public LongVector eval(int positionCount, LongVector fieldVector) { + try(LongVector.FixedBuilder result = driverContext.blockFactory().newLongVectorFixedBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + result.appendLong(p, RoundToLong.process(fieldVector.getLong(p), this.p0)); + } + return result.build(); + } + } + + @Override + public String toString() { + return "RoundToLong1Evaluator[" + "field=" + field + ", p0=" + p0 + "]"; + } + + @Override + public void close() { + Releasables.closeExpectNoException(field); + } + + private Warnings warnings() { + if (warnings == null) { + this.warnings = Warnings.createWarnings( + driverContext.warningsMode(), + source.source().getLineNumber(), + source.source().getColumnNumber(), + source.text() + ); + } + return warnings; + } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + private final long p0; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory field, long p0) { + this.source = source; + this.field = field; + this.p0 = p0; + } + + @Override + public RoundToLong1Evaluator get(DriverContext context) { + return new RoundToLong1Evaluator(source, field.get(context), p0, context); + } + + @Override + public String toString() { + return "RoundToLong1Evaluator[" + "field=" + field + ", p0=" + p0 + "]"; + } + } +} diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToLong2Evaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToLong2Evaluator.java new file mode 100644 index 000000000000..742e69ed8d8d --- /dev/null +++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToLong2Evaluator.java @@ -0,0 +1,135 @@ +// 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 java.lang.IllegalArgumentException; +import java.lang.Override; +import java.lang.String; +import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.LongBlock; +import org.elasticsearch.compute.data.LongVector; +import org.elasticsearch.compute.data.Page; +import org.elasticsearch.compute.operator.DriverContext; +import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.compute.operator.Warnings; +import org.elasticsearch.core.Releasables; +import org.elasticsearch.xpack.esql.core.tree.Source; + +/** + * {@link EvalOperator.ExpressionEvaluator} implementation for {@link RoundToLong}. + * This class is generated. Edit {@code EvaluatorImplementer} instead. + */ +public final class RoundToLong2Evaluator implements EvalOperator.ExpressionEvaluator { + private final Source source; + + private final EvalOperator.ExpressionEvaluator field; + + private final long p0; + + private final long p1; + + private final DriverContext driverContext; + + private Warnings warnings; + + public RoundToLong2Evaluator(Source source, EvalOperator.ExpressionEvaluator field, long p0, + long p1, DriverContext driverContext) { + this.source = source; + this.field = field; + this.p0 = p0; + this.p1 = p1; + this.driverContext = driverContext; + } + + @Override + public Block eval(Page page) { + try (LongBlock fieldBlock = (LongBlock) field.eval(page)) { + LongVector fieldVector = fieldBlock.asVector(); + if (fieldVector == null) { + return eval(page.getPositionCount(), fieldBlock); + } + return eval(page.getPositionCount(), fieldVector).asBlock(); + } + } + + public LongBlock eval(int positionCount, LongBlock fieldBlock) { + try(LongBlock.Builder result = driverContext.blockFactory().newLongBlockBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + if (fieldBlock.isNull(p)) { + result.appendNull(); + continue position; + } + if (fieldBlock.getValueCount(p) != 1) { + if (fieldBlock.getValueCount(p) > 1) { + warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value")); + } + result.appendNull(); + continue position; + } + result.appendLong(RoundToLong.process(fieldBlock.getLong(fieldBlock.getFirstValueIndex(p)), this.p0, this.p1)); + } + return result.build(); + } + } + + public LongVector eval(int positionCount, LongVector fieldVector) { + try(LongVector.FixedBuilder result = driverContext.blockFactory().newLongVectorFixedBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + result.appendLong(p, RoundToLong.process(fieldVector.getLong(p), this.p0, this.p1)); + } + return result.build(); + } + } + + @Override + public String toString() { + return "RoundToLong2Evaluator[" + "field=" + field + ", p0=" + p0 + ", p1=" + p1 + "]"; + } + + @Override + public void close() { + Releasables.closeExpectNoException(field); + } + + private Warnings warnings() { + if (warnings == null) { + this.warnings = Warnings.createWarnings( + driverContext.warningsMode(), + source.source().getLineNumber(), + source.source().getColumnNumber(), + source.text() + ); + } + return warnings; + } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + private final long p0; + + private final long p1; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory field, long p0, + long p1) { + this.source = source; + this.field = field; + this.p0 = p0; + this.p1 = p1; + } + + @Override + public RoundToLong2Evaluator get(DriverContext context) { + return new RoundToLong2Evaluator(source, field.get(context), p0, p1, context); + } + + @Override + public String toString() { + return "RoundToLong2Evaluator[" + "field=" + field + ", p0=" + p0 + ", p1=" + p1 + "]"; + } + } +} diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToLong3Evaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToLong3Evaluator.java new file mode 100644 index 000000000000..9f2eebd25cae --- /dev/null +++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToLong3Evaluator.java @@ -0,0 +1,141 @@ +// 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 java.lang.IllegalArgumentException; +import java.lang.Override; +import java.lang.String; +import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.LongBlock; +import org.elasticsearch.compute.data.LongVector; +import org.elasticsearch.compute.data.Page; +import org.elasticsearch.compute.operator.DriverContext; +import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.compute.operator.Warnings; +import org.elasticsearch.core.Releasables; +import org.elasticsearch.xpack.esql.core.tree.Source; + +/** + * {@link EvalOperator.ExpressionEvaluator} implementation for {@link RoundToLong}. + * This class is generated. Edit {@code EvaluatorImplementer} instead. + */ +public final class RoundToLong3Evaluator implements EvalOperator.ExpressionEvaluator { + private final Source source; + + private final EvalOperator.ExpressionEvaluator field; + + private final long p0; + + private final long p1; + + private final long p2; + + private final DriverContext driverContext; + + private Warnings warnings; + + public RoundToLong3Evaluator(Source source, EvalOperator.ExpressionEvaluator field, long p0, + long p1, long p2, DriverContext driverContext) { + this.source = source; + this.field = field; + this.p0 = p0; + this.p1 = p1; + this.p2 = p2; + this.driverContext = driverContext; + } + + @Override + public Block eval(Page page) { + try (LongBlock fieldBlock = (LongBlock) field.eval(page)) { + LongVector fieldVector = fieldBlock.asVector(); + if (fieldVector == null) { + return eval(page.getPositionCount(), fieldBlock); + } + return eval(page.getPositionCount(), fieldVector).asBlock(); + } + } + + public LongBlock eval(int positionCount, LongBlock fieldBlock) { + try(LongBlock.Builder result = driverContext.blockFactory().newLongBlockBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + if (fieldBlock.isNull(p)) { + result.appendNull(); + continue position; + } + if (fieldBlock.getValueCount(p) != 1) { + if (fieldBlock.getValueCount(p) > 1) { + warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value")); + } + result.appendNull(); + continue position; + } + result.appendLong(RoundToLong.process(fieldBlock.getLong(fieldBlock.getFirstValueIndex(p)), this.p0, this.p1, this.p2)); + } + return result.build(); + } + } + + public LongVector eval(int positionCount, LongVector fieldVector) { + try(LongVector.FixedBuilder result = driverContext.blockFactory().newLongVectorFixedBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + result.appendLong(p, RoundToLong.process(fieldVector.getLong(p), this.p0, this.p1, this.p2)); + } + return result.build(); + } + } + + @Override + public String toString() { + return "RoundToLong3Evaluator[" + "field=" + field + ", p0=" + p0 + ", p1=" + p1 + ", p2=" + p2 + "]"; + } + + @Override + public void close() { + Releasables.closeExpectNoException(field); + } + + private Warnings warnings() { + if (warnings == null) { + this.warnings = Warnings.createWarnings( + driverContext.warningsMode(), + source.source().getLineNumber(), + source.source().getColumnNumber(), + source.text() + ); + } + return warnings; + } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + private final long p0; + + private final long p1; + + private final long p2; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory field, long p0, long p1, + long p2) { + this.source = source; + this.field = field; + this.p0 = p0; + this.p1 = p1; + this.p2 = p2; + } + + @Override + public RoundToLong3Evaluator get(DriverContext context) { + return new RoundToLong3Evaluator(source, field.get(context), p0, p1, p2, context); + } + + @Override + public String toString() { + return "RoundToLong3Evaluator[" + "field=" + field + ", p0=" + p0 + ", p1=" + p1 + ", p2=" + p2 + "]"; + } + } +} diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToLong4Evaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToLong4Evaluator.java new file mode 100644 index 000000000000..f479de31dd6a --- /dev/null +++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToLong4Evaluator.java @@ -0,0 +1,147 @@ +// 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 java.lang.IllegalArgumentException; +import java.lang.Override; +import java.lang.String; +import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.LongBlock; +import org.elasticsearch.compute.data.LongVector; +import org.elasticsearch.compute.data.Page; +import org.elasticsearch.compute.operator.DriverContext; +import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.compute.operator.Warnings; +import org.elasticsearch.core.Releasables; +import org.elasticsearch.xpack.esql.core.tree.Source; + +/** + * {@link EvalOperator.ExpressionEvaluator} implementation for {@link RoundToLong}. + * This class is generated. Edit {@code EvaluatorImplementer} instead. + */ +public final class RoundToLong4Evaluator implements EvalOperator.ExpressionEvaluator { + private final Source source; + + private final EvalOperator.ExpressionEvaluator field; + + private final long p0; + + private final long p1; + + private final long p2; + + private final long p3; + + private final DriverContext driverContext; + + private Warnings warnings; + + public RoundToLong4Evaluator(Source source, EvalOperator.ExpressionEvaluator field, long p0, + long p1, long p2, long p3, DriverContext driverContext) { + this.source = source; + this.field = field; + this.p0 = p0; + this.p1 = p1; + this.p2 = p2; + this.p3 = p3; + this.driverContext = driverContext; + } + + @Override + public Block eval(Page page) { + try (LongBlock fieldBlock = (LongBlock) field.eval(page)) { + LongVector fieldVector = fieldBlock.asVector(); + if (fieldVector == null) { + return eval(page.getPositionCount(), fieldBlock); + } + return eval(page.getPositionCount(), fieldVector).asBlock(); + } + } + + public LongBlock eval(int positionCount, LongBlock fieldBlock) { + try(LongBlock.Builder result = driverContext.blockFactory().newLongBlockBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + if (fieldBlock.isNull(p)) { + result.appendNull(); + continue position; + } + if (fieldBlock.getValueCount(p) != 1) { + if (fieldBlock.getValueCount(p) > 1) { + warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value")); + } + result.appendNull(); + continue position; + } + result.appendLong(RoundToLong.process(fieldBlock.getLong(fieldBlock.getFirstValueIndex(p)), this.p0, this.p1, this.p2, this.p3)); + } + return result.build(); + } + } + + public LongVector eval(int positionCount, LongVector fieldVector) { + try(LongVector.FixedBuilder result = driverContext.blockFactory().newLongVectorFixedBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + result.appendLong(p, RoundToLong.process(fieldVector.getLong(p), this.p0, this.p1, this.p2, this.p3)); + } + return result.build(); + } + } + + @Override + public String toString() { + return "RoundToLong4Evaluator[" + "field=" + field + ", p0=" + p0 + ", p1=" + p1 + ", p2=" + p2 + ", p3=" + p3 + "]"; + } + + @Override + public void close() { + Releasables.closeExpectNoException(field); + } + + private Warnings warnings() { + if (warnings == null) { + this.warnings = Warnings.createWarnings( + driverContext.warningsMode(), + source.source().getLineNumber(), + source.source().getColumnNumber(), + source.text() + ); + } + return warnings; + } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + private final long p0; + + private final long p1; + + private final long p2; + + private final long p3; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory field, long p0, long p1, + long p2, long p3) { + this.source = source; + this.field = field; + this.p0 = p0; + this.p1 = p1; + this.p2 = p2; + this.p3 = p3; + } + + @Override + public RoundToLong4Evaluator get(DriverContext context) { + return new RoundToLong4Evaluator(source, field.get(context), p0, p1, p2, p3, context); + } + + @Override + public String toString() { + return "RoundToLong4Evaluator[" + "field=" + field + ", p0=" + p0 + ", p1=" + p1 + ", p2=" + p2 + ", p3=" + p3 + "]"; + } + } +} diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToLongBinarySearchEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToLongBinarySearchEvaluator.java new file mode 100644 index 000000000000..affd383be6f4 --- /dev/null +++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToLongBinarySearchEvaluator.java @@ -0,0 +1,128 @@ +// 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 java.lang.IllegalArgumentException; +import java.lang.Override; +import java.lang.String; +import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.LongBlock; +import org.elasticsearch.compute.data.LongVector; +import org.elasticsearch.compute.data.Page; +import org.elasticsearch.compute.operator.DriverContext; +import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.compute.operator.Warnings; +import org.elasticsearch.core.Releasables; +import org.elasticsearch.xpack.esql.core.tree.Source; + +/** + * {@link EvalOperator.ExpressionEvaluator} implementation for {@link RoundToLong}. + * This class is generated. Edit {@code EvaluatorImplementer} instead. + */ +public final class RoundToLongBinarySearchEvaluator implements EvalOperator.ExpressionEvaluator { + private final Source source; + + private final EvalOperator.ExpressionEvaluator field; + + private final long[] points; + + private final DriverContext driverContext; + + private Warnings warnings; + + public RoundToLongBinarySearchEvaluator(Source source, EvalOperator.ExpressionEvaluator field, + long[] points, DriverContext driverContext) { + this.source = source; + this.field = field; + this.points = points; + this.driverContext = driverContext; + } + + @Override + public Block eval(Page page) { + try (LongBlock fieldBlock = (LongBlock) field.eval(page)) { + LongVector fieldVector = fieldBlock.asVector(); + if (fieldVector == null) { + return eval(page.getPositionCount(), fieldBlock); + } + return eval(page.getPositionCount(), fieldVector).asBlock(); + } + } + + public LongBlock eval(int positionCount, LongBlock fieldBlock) { + try(LongBlock.Builder result = driverContext.blockFactory().newLongBlockBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + if (fieldBlock.isNull(p)) { + result.appendNull(); + continue position; + } + if (fieldBlock.getValueCount(p) != 1) { + if (fieldBlock.getValueCount(p) > 1) { + warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value")); + } + result.appendNull(); + continue position; + } + result.appendLong(RoundToLong.process(fieldBlock.getLong(fieldBlock.getFirstValueIndex(p)), this.points)); + } + return result.build(); + } + } + + public LongVector eval(int positionCount, LongVector fieldVector) { + try(LongVector.FixedBuilder result = driverContext.blockFactory().newLongVectorFixedBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + result.appendLong(p, RoundToLong.process(fieldVector.getLong(p), this.points)); + } + return result.build(); + } + } + + @Override + public String toString() { + return "RoundToLongBinarySearchEvaluator[" + "field=" + field + "]"; + } + + @Override + public void close() { + Releasables.closeExpectNoException(field); + } + + private Warnings warnings() { + if (warnings == null) { + this.warnings = Warnings.createWarnings( + driverContext.warningsMode(), + source.source().getLineNumber(), + source.source().getColumnNumber(), + source.text() + ); + } + return warnings; + } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + private final long[] points; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory field, long[] points) { + this.source = source; + this.field = field; + this.points = points; + } + + @Override + public RoundToLongBinarySearchEvaluator get(DriverContext context) { + return new RoundToLongBinarySearchEvaluator(source, field.get(context), points, context); + } + + @Override + public String toString() { + return "RoundToLongBinarySearchEvaluator[" + "field=" + field + "]"; + } + } +} diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToLongLinearSearchEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToLongLinearSearchEvaluator.java new file mode 100644 index 000000000000..62a6c81d148e --- /dev/null +++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToLongLinearSearchEvaluator.java @@ -0,0 +1,128 @@ +// 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 java.lang.IllegalArgumentException; +import java.lang.Override; +import java.lang.String; +import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.LongBlock; +import org.elasticsearch.compute.data.LongVector; +import org.elasticsearch.compute.data.Page; +import org.elasticsearch.compute.operator.DriverContext; +import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.compute.operator.Warnings; +import org.elasticsearch.core.Releasables; +import org.elasticsearch.xpack.esql.core.tree.Source; + +/** + * {@link EvalOperator.ExpressionEvaluator} implementation for {@link RoundToLong}. + * This class is generated. Edit {@code EvaluatorImplementer} instead. + */ +public final class RoundToLongLinearSearchEvaluator implements EvalOperator.ExpressionEvaluator { + private final Source source; + + private final EvalOperator.ExpressionEvaluator field; + + private final long[] points; + + private final DriverContext driverContext; + + private Warnings warnings; + + public RoundToLongLinearSearchEvaluator(Source source, EvalOperator.ExpressionEvaluator field, + long[] points, DriverContext driverContext) { + this.source = source; + this.field = field; + this.points = points; + this.driverContext = driverContext; + } + + @Override + public Block eval(Page page) { + try (LongBlock fieldBlock = (LongBlock) field.eval(page)) { + LongVector fieldVector = fieldBlock.asVector(); + if (fieldVector == null) { + return eval(page.getPositionCount(), fieldBlock); + } + return eval(page.getPositionCount(), fieldVector).asBlock(); + } + } + + public LongBlock eval(int positionCount, LongBlock fieldBlock) { + try(LongBlock.Builder result = driverContext.blockFactory().newLongBlockBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + if (fieldBlock.isNull(p)) { + result.appendNull(); + continue position; + } + if (fieldBlock.getValueCount(p) != 1) { + if (fieldBlock.getValueCount(p) > 1) { + warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value")); + } + result.appendNull(); + continue position; + } + result.appendLong(RoundToLong.processLinear(fieldBlock.getLong(fieldBlock.getFirstValueIndex(p)), this.points)); + } + return result.build(); + } + } + + public LongVector eval(int positionCount, LongVector fieldVector) { + try(LongVector.FixedBuilder result = driverContext.blockFactory().newLongVectorFixedBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + result.appendLong(p, RoundToLong.processLinear(fieldVector.getLong(p), this.points)); + } + return result.build(); + } + } + + @Override + public String toString() { + return "RoundToLongLinearSearchEvaluator[" + "field=" + field + "]"; + } + + @Override + public void close() { + Releasables.closeExpectNoException(field); + } + + private Warnings warnings() { + if (warnings == null) { + this.warnings = Warnings.createWarnings( + driverContext.warningsMode(), + source.source().getLineNumber(), + source.source().getColumnNumber(), + source.text() + ); + } + return warnings; + } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + private final long[] points; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory field, long[] points) { + this.source = source; + this.field = field; + this.points = points; + } + + @Override + public RoundToLongLinearSearchEvaluator get(DriverContext context) { + return new RoundToLongLinearSearchEvaluator(source, field.get(context), points, context); + } + + @Override + public String toString() { + return "RoundToLongLinearSearchEvaluator[" + "field=" + field + "]"; + } + } +} diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java index 30b55e9f06a2..7b624edc64c2 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java @@ -1102,7 +1102,12 @@ public class EsqlCapabilities { * Avid GROK and DISSECT attributes being removed when resolving fields. * see ES|QL: Grok only supports KEYWORD or TEXT values, found expression [type] type [INTEGER] #127468 */ - KEEP_REGEX_EXTRACT_ATTRIBUTES; + KEEP_REGEX_EXTRACT_ATTRIBUTES, + + /** + * The {@code ROUND_TO} function. + */ + ROUND_TO; private final boolean enabled; diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/EsqlFunctionRegistry.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/EsqlFunctionRegistry.java index 67d11197efe5..6ed102428c5f 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/EsqlFunctionRegistry.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/EsqlFunctionRegistry.java @@ -100,6 +100,7 @@ import org.elasticsearch.xpack.esql.expression.function.scalar.math.Log10; 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.RoundTo; import org.elasticsearch.xpack.esql.expression.function.scalar.math.Scalb; import org.elasticsearch.xpack.esql.expression.function.scalar.math.Signum; import org.elasticsearch.xpack.esql.expression.function.scalar.math.Sin; @@ -326,6 +327,7 @@ public class EsqlFunctionRegistry { def(Pi.class, Pi::new, "pi"), def(Pow.class, Pow::new, "pow"), def(Round.class, Round::new, "round"), + def(RoundTo.class, RoundTo::new, "round_to"), def(Scalb.class, Scalb::new, "scalb"), def(Signum.class, Signum::new, "signum"), def(Sin.class, Sin::new, "sin"), diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/ScalarFunctionWritables.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/ScalarFunctionWritables.java index 90152d546097..8cd0faade9cb 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/ScalarFunctionWritables.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/ScalarFunctionWritables.java @@ -28,6 +28,7 @@ import org.elasticsearch.xpack.esql.expression.function.scalar.math.Log; 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.RoundTo; import org.elasticsearch.xpack.esql.expression.function.scalar.math.Tau; import org.elasticsearch.xpack.esql.expression.function.scalar.nulls.Coalesce; import org.elasticsearch.xpack.esql.expression.function.scalar.string.BitLength; @@ -94,6 +95,7 @@ public class ScalarFunctionWritables { entries.add(Replace.ENTRY); entries.add(Reverse.ENTRY); entries.add(Round.ENTRY); + entries.add(RoundTo.ENTRY); entries.add(Sha1.ENTRY); entries.add(Sha256.ENTRY); entries.add(Split.ENTRY); diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundTo.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundTo.java new file mode 100644 index 000000000000..38a6e2af5c67 --- /dev/null +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundTo.java @@ -0,0 +1,191 @@ +/* + * 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.collect.Iterators; +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.operator.EvalOperator.ExpressionEvaluator; +import org.elasticsearch.xpack.esql.core.expression.Expression; +import org.elasticsearch.xpack.esql.core.expression.Foldables; +import org.elasticsearch.xpack.esql.core.expression.Nullability; +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 java.util.Map; + +import static org.elasticsearch.common.logging.LoggerMessageFormat.format; +import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isFoldable; +import static org.elasticsearch.xpack.esql.core.type.DataType.DATETIME; +import static org.elasticsearch.xpack.esql.core.type.DataType.DATE_NANOS; +import static org.elasticsearch.xpack.esql.core.type.DataType.DOUBLE; +import static org.elasticsearch.xpack.esql.core.type.DataType.INTEGER; +import static org.elasticsearch.xpack.esql.core.type.DataType.LONG; +import static org.elasticsearch.xpack.esql.core.type.DataType.UNSIGNED_LONG; +import static org.elasticsearch.xpack.esql.type.EsqlDataTypeConverter.commonType; + +/** + * Round down to one of a list of values. + */ +public class RoundTo extends EsqlScalarFunction { + public static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry(Expression.class, "RoundTo", RoundTo::new); + + private final Expression field; + private final List points; + + private DataType resultType; + + @FunctionInfo(returnType = { "double", "integer", "long", "date", "date_nanos" }, description = """ + Rounds down to one of a list of fixed points.""", examples = @Example(file = "math", tag = "round_to")) + public RoundTo( + Source source, + @Param( + name = "field", + type = { "double", "integer", "long", "date", "date_nanos" }, + description = "The numeric value to round. If `null`, the function returns `null`." + ) Expression field, + @Param( + name = "points", + type = { "double", "integer", "long", "date", "date_nanos" }, + description = "Remaining rounding points. Must be constants." + ) List points + ) { + super(source, Iterators.toList(Iterators.concat(Iterators.single(field), points.iterator()))); + this.field = field; + this.points = points; + } + + private RoundTo(StreamInput in) throws IOException { + this( + Source.readFrom((PlanStreamInput) in), + in.readNamedWriteable(Expression.class), + in.readNamedWriteableCollectionAsList(Expression.class) + ); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + source().writeTo(out); + out.writeNamedWriteable(field); + out.writeNamedWriteableCollection(points); + } + + @Override + public String getWriteableName() { + return ENTRY.name; + } + + @Override + protected TypeResolution resolveType() { + if (childrenResolved() == false) { + return new TypeResolution("Unresolved children"); + } + + int index = 1; + for (Expression f : points) { + if (f.dataType() == DataType.NULL) { + continue; + } + TypeResolution resolution = isFoldable(f, sourceText(), TypeResolutions.ParamOrdinal.fromIndex(index)); + if (resolution.unresolved()) { + return resolution; + } + } + + DataType dataType = dataType(); + if (dataType == null || SIGNATURES.containsKey(dataType) == false) { + return new TypeResolution(format(null, "all arguments must be numeric, date, or data_nanos")); + } + + return TypeResolution.TYPE_RESOLVED; + } + + @Override + public DataType dataType() { + if (resultType != null) { + return resultType; + } + resultType = field.dataType(); + for (Expression f : points) { + if (resultType == DataType.UNSIGNED_LONG || resultType == null || f.dataType() == UNSIGNED_LONG) { + return null; + } + resultType = commonType(resultType, f.dataType()); + } + return resultType; + } + + @Override + public boolean foldable() { + for (Expression c : children()) { + if (c.foldable() == false) { + return false; + } + } + return true; + } + + @Override + public Nullability nullable() { + return Nullability.TRUE; + } + + @Override + public final Expression replaceChildren(List newChildren) { + return new RoundTo(source(), newChildren.get(0), newChildren.subList(1, newChildren.size())); + } + + @Override + protected NodeInfo info() { + return NodeInfo.create(this, RoundTo::new, field(), points()); + } + + public Expression field() { + return field; + } + + public List points() { + return points; + } + + @Override + public ExpressionEvaluator.Factory toEvaluator(ToEvaluator toEvaluator) { + DataType dataType = dataType(); + Build build = SIGNATURES.get(dataType); + if (build == null) { + throw new IllegalStateException("unsupported type"); + } + + ExpressionEvaluator.Factory field = toEvaluator.apply(field()); + field = Cast.cast(source(), field().dataType(), dataType, field); + List points = Iterators.toList(Iterators.map(points().iterator(), p -> Foldables.valueOf(toEvaluator.foldCtx(), p))); + return build.build(source(), field, points); + } + + interface Build { + ExpressionEvaluator.Factory build(Source source, ExpressionEvaluator.Factory field, List points); + } + + private static final Map SIGNATURES = Map.ofEntries( + Map.entry(DATETIME, RoundToLong.BUILD), + Map.entry(DATE_NANOS, RoundToLong.BUILD), + Map.entry(INTEGER, RoundToInt.BUILD), + Map.entry(LONG, RoundToLong.BUILD), + Map.entry(DOUBLE, RoundToDouble.BUILD) + ); +} diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/X-RoundTo.java.st b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/X-RoundTo.java.st new file mode 100644 index 000000000000..74ace0a5c4c0 --- /dev/null +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/X-RoundTo.java.st @@ -0,0 +1,105 @@ +/* + * 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; + +// begin generated imports +import org.elasticsearch.compute.ann.Evaluator; +import org.elasticsearch.compute.ann.Fixed; + +import java.util.Arrays; +// end generated imports + +/** + * Implementations of {@link RoundTo} for specific types. + *

+ * We have specializations for when there are very few rounding points because + * those are very fast and quite common. + *

+ * This class is generated. Edit {@code X-RoundTo.java.st} instead. + */ +class RoundTo$Type$ { + static final RoundTo.Build BUILD = (source, field, points) -> { + $type$[] f = points.stream().mapTo$Type$(p -> ((Number) p).$type$Value()).toArray(); + Arrays.sort(f); + return switch (f.length) { + // TODO should be a consistent way to do the 0 version - is CASE(MV_COUNT(f) == 1, f[0]) + case 1 -> new RoundTo$Type$1Evaluator.Factory(source, field, f[0]); + /* + * These hand-unrolled implementations are even faster than the linear scan implementations. + */ + case 2 -> new RoundTo$Type$2Evaluator.Factory(source, field, f[0], f[1]); + case 3 -> new RoundTo$Type$3Evaluator.Factory(source, field, f[0], f[1], f[2]); + case 4 -> new RoundTo$Type$4Evaluator.Factory(source, field, f[0], f[1], f[2], f[3]); + /* + * Break point of 10 experimentally derived on Nik's laptop (13th Gen Intel(R) Core(TM) i7-1370P) + * on 2025-05-22. + */ + case 5, 6, 7, 8, 9, 10 -> new RoundTo$Type$LinearSearchEvaluator.Factory(source, field, f); + default -> new RoundTo$Type$BinarySearchEvaluator.Factory(source, field, f); + }; + }; + + /** + * Search the points array for the match linearly. This is faster for smaller arrays even + * when finding a position late in the array. Presumably because this is super-SIMD-able. + */ + @Evaluator(extraName = "LinearSearch") + static $type$ processLinear($type$ field, @Fixed(includeInToString = false) $type$[] points) { + // points is always longer than 3 or we use one of the specialized methods below + for (int i = 1; i < points.length; i++) { + if (field < points[i]) { + return points[i - 1]; + } + } + return points[points.length - 1]; + } + + @Evaluator(extraName = "BinarySearch") + static $type$ process($type$ field, @Fixed(includeInToString = false) $type$[] points) { + int idx = Arrays.binarySearch(points, field); + return points[idx >= 0 ? idx : Math.max(0, -idx - 2)]; + } + + @Evaluator(extraName = "1") + static $type$ process($type$ field, @Fixed $type$ p0) { + return p0; + } + + @Evaluator(extraName = "2") + static $type$ process($type$ field, @Fixed $type$ p0, @Fixed $type$ p1) { + if (field < p1) { + return p0; + } + return p1; + } + + @Evaluator(extraName = "3") + static $type$ process($type$ field, @Fixed $type$ p0, @Fixed $type$ p1, @Fixed $type$ p2) { + if (field < p1) { + return p0; + } + if (field < p2) { + return p1; + } + return p2; + } + + @Evaluator(extraName = "4") + static $type$ process($type$ field, @Fixed $type$ p0, @Fixed $type$ p1, @Fixed $type$ p2, @Fixed $type$ p3) { + if (field < p1) { + return p0; + } + if (field < p2) { + return p1; + } + if (field < p3) { + return p2; + } + return p3; + } +} diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToErrorTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToErrorTests.java new file mode 100644 index 000000000000..71f0fb64125a --- /dev/null +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToErrorTests.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.esql.core.expression.Expression; +import org.elasticsearch.xpack.esql.core.tree.Source; +import org.elasticsearch.xpack.esql.core.type.DataType; +import org.elasticsearch.xpack.esql.expression.function.ErrorsForCasesWithoutExamplesTestCase; +import org.elasticsearch.xpack.esql.expression.function.TestCaseSupplier; +import org.hamcrest.Matcher; + +import java.util.List; +import java.util.Set; + +import static org.hamcrest.Matchers.equalTo; + +public class RoundToErrorTests extends ErrorsForCasesWithoutExamplesTestCase { + @Override + protected List cases() { + return paramsToSuppliers(RoundToTests.parameters()).stream() + /* + * We pick the common type across all parameters, but we don't + * test mixes with more than three parameters. We test cases + * with more than three parameters - just not mixes with more + * than three. + */ + .filter(s -> s.types().size() < 3) + .toList(); + } + + @Override + protected Expression build(Source source, List args) { + return new RoundTo(source, args.getFirst(), args.subList(1, args.size())); + } + + @Override + protected Matcher expectedTypeErrorMatcher(List> validPerPosition, List signature) { + return equalTo("all arguments must be numeric, date, or data_nanos"); + } +} diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToSerializationTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToSerializationTests.java new file mode 100644 index 000000000000..9d67158eb305 --- /dev/null +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToSerializationTests.java @@ -0,0 +1,59 @@ +/* + * 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.esql.core.expression.Expression; +import org.elasticsearch.xpack.esql.core.expression.ReferenceAttribute; +import org.elasticsearch.xpack.esql.core.tree.Source; +import org.elasticsearch.xpack.esql.core.type.DataType; +import org.elasticsearch.xpack.esql.expression.AbstractExpressionSerializationTests; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import static org.elasticsearch.xpack.esql.EsqlTestUtils.randomLiteral; + +public class RoundToSerializationTests extends AbstractExpressionSerializationTests { + @Override + protected RoundTo createTestInstance() { + Source source = randomSource(); + DataType type = randomFrom(DataType.INTEGER, DataType.LONG, DataType.DOUBLE, DataType.DATETIME, DataType.DATE_NANOS); + Expression field = randomField(type); + List points = randomPoints(type); + return new RoundTo(source, field, points); + } + + private Expression randomField(DataType type) { + return new ReferenceAttribute(Source.EMPTY, randomAlphanumericOfLength(4), randomLiteral(type).dataType()); + } + + private List randomPoints(DataType type) { + int length = between(1, 100); + List points = new ArrayList<>(length); + while (points.size() < length) { + points.add(randomLiteral(type)); + } + ; + return points; + } + + @Override + protected RoundTo mutateInstance(RoundTo instance) throws IOException { + Source source = instance.source(); + Expression field = instance.field(); + List points = instance.points(); + DataType type = field.dataType(); + if (randomBoolean()) { + field = randomValueOtherThan(field, () -> randomField(type)); + } else { + points = randomValueOtherThan(points, () -> randomPoints(type)); + } + return new RoundTo(source, field, points); + } +} diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToTests.java new file mode 100644 index 000000000000..677367885dd8 --- /dev/null +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundToTests.java @@ -0,0 +1,305 @@ +/* + * 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 com.carrotsearch.randomizedtesting.annotations.Name; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.xpack.esql.core.expression.Expression; +import org.elasticsearch.xpack.esql.core.tree.Source; +import org.elasticsearch.xpack.esql.core.type.DataType; +import org.elasticsearch.xpack.esql.expression.function.AbstractScalarFunctionTestCase; +import org.elasticsearch.xpack.esql.expression.function.TestCaseSupplier; +import org.hamcrest.Matcher; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.function.BiFunction; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.startsWith; + +public class RoundToTests extends AbstractScalarFunctionTestCase { + public RoundToTests(@Name("TestCase") Supplier testCaseSupplier) { + this.testCase = testCaseSupplier.get(); + } + + @ParametersFactory + public static Iterable parameters() { + List suppliers = new ArrayList<>(); + + for (int p = 1; p < 20; p++) { + int points = p; + suppliers.add( + doubles( + "", + DataType.DOUBLE, + () -> randomDoubleBetween(-Double.MAX_VALUE, Double.MAX_VALUE, true), + IntStream.range(0, points).mapToObj(i -> DataType.DOUBLE).toList(), + () -> IntStream.range(0, points).mapToObj(i -> randomDoubleBetween(-Double.MAX_VALUE, Double.MAX_VALUE, true)).toList() + ) + ); + suppliers.add( + doubles( + "", + DataType.DOUBLE, + () -> randomDoubleBetween(-Double.MAX_VALUE, Double.MAX_VALUE, true), + IntStream.range(0, points).mapToObj(i -> DataType.LONG).toList(), + () -> IntStream.range(0, points).mapToObj(i -> (double) randomLong()).toList() + ) + ); + suppliers.add( + doubles( + "", + DataType.DOUBLE, + () -> randomDoubleBetween(-Double.MAX_VALUE, Double.MAX_VALUE, true), + IntStream.range(0, points).mapToObj(i -> DataType.INTEGER).toList(), + () -> IntStream.range(0, points).mapToObj(i -> (double) randomInt()).toList() + ) + ); + suppliers.add( + doubles( + "", + DataType.LONG, + ESTestCase::randomLong, + IntStream.range(0, points).mapToObj(i -> DataType.DOUBLE).toList(), + () -> IntStream.range(0, points).mapToObj(i -> randomDoubleBetween(-Double.MAX_VALUE, Double.MAX_VALUE, true)).toList() + ) + ); + suppliers.add( + doubles( + "", + DataType.INTEGER, + ESTestCase::randomInt, + IntStream.range(0, points).mapToObj(i -> DataType.DOUBLE).toList(), + () -> IntStream.range(0, points).mapToObj(i -> randomDoubleBetween(-Double.MAX_VALUE, Double.MAX_VALUE, true)).toList() + ) + ); + suppliers.add( + longs( + "", + DataType.LONG, + ESTestCase::randomLong, + IntStream.range(0, points).mapToObj(i -> DataType.LONG).toList(), + () -> IntStream.range(0, points).mapToObj(i -> randomLong()).toList() + ) + ); + suppliers.add( + longs( + "", + DataType.INTEGER, + ESTestCase::randomInt, + IntStream.range(0, points).mapToObj(i -> DataType.LONG).toList(), + () -> IntStream.range(0, points).mapToObj(i -> randomLong()).toList() + ) + ); + suppliers.add( + longs( + "", + DataType.DATETIME, + ESTestCase::randomMillisUpToYear9999, + IntStream.range(0, points).mapToObj(i -> DataType.DATETIME).toList(), + () -> IntStream.range(0, points).mapToObj(i -> randomMillisUpToYear9999()).toList() + ) + ); + suppliers.add( + longs( + "", + DataType.DATE_NANOS, + () -> randomLongBetween(0, Long.MAX_VALUE), + IntStream.range(0, points).mapToObj(i -> DataType.DATE_NANOS).toList(), + () -> IntStream.range(0, points).mapToObj(i -> randomLongBetween(0, Long.MAX_VALUE)).toList() + ) + ); + suppliers.add( + longs( + "", + DataType.LONG, + ESTestCase::randomLong, + IntStream.range(0, points).mapToObj(i -> DataType.INTEGER).toList(), + () -> IntStream.range(0, points).mapToObj(i -> (long) randomInt()).toList() + ) + ); + suppliers.add( + ints( + "", + DataType.INTEGER, + ESTestCase::randomInt, + IntStream.range(0, points).mapToObj(i -> DataType.INTEGER).toList(), + () -> IntStream.range(0, points).mapToObj(i -> randomInt()).toList() + ) + ); + } + suppliers.add(supplier(1.0, 0.0, 0.0, 100.0)); + suppliers.add(supplier(1.0, 1.0, 0.0, 1.0, 100.0)); + suppliers.add(supplier(0.5, 0.0, 0.0, 1.0, 100.0)); + suppliers.add(supplier(1.5, 1.0, 0.0, 1.0, 100.0)); + suppliers.add(supplier(200, 100, 0.0, 1.0, 100.0)); + + return parameterSuppliersFromTypedDataWithDefaultChecksNoErrors( + (int nullPosition, DataType nullValueDataType, TestCaseSupplier.TestCase original) -> { + if (nullValueDataType != DataType.NULL) { + return original.expectedType(); + } + List types = original.getData().stream().map(TestCaseSupplier.TypedData::type).collect(Collectors.toList()); + types.set(nullPosition, DataType.NULL); + return expectedType(types); + }, + (int nullPosition, TestCaseSupplier.TypedData nullData, Matcher original) -> { + if (nullPosition == 0) { + return original; + } + return equalTo("LiteralsEvaluator[lit=null]"); + }, + randomizeBytesRefsOffset(suppliers) + ); + } + + private static TestCaseSupplier supplier(double f, double expected, double... points) { + StringBuilder name = new StringBuilder("round("); + name.append(f); + for (double p : points) { + name.append(", ").append(p); + } + name.append(") -> ").append(expected); + return supplier( + name.toString(), + DataType.DOUBLE, + () -> f, + IntStream.range(0, points.length).mapToObj(i -> DataType.DOUBLE).toList(), + () -> Arrays.stream(points).boxed().toList(), + (value, de) -> expected + ); + } + + private static TestCaseSupplier doubles( + String name, + DataType fieldType, + Supplier fieldSupplier, + List pointsTypes, + Supplier> pointsSupplier + ) { + return supplier(name, fieldType, fieldSupplier, pointsTypes, pointsSupplier, (f, p) -> { + double max = p.stream().mapToDouble(d -> d).min().getAsDouble(); + for (double d : p) { + if (d > max && f.doubleValue() > d) { + max = d; + } + } + return max; + }); + } + + private static TestCaseSupplier longs( + String name, + DataType fieldType, + Supplier fieldSupplier, + List pointsTypes, + Supplier> pointsSupplier + ) { + return supplier(name, fieldType, fieldSupplier, pointsTypes, pointsSupplier, (f, p) -> { + long max = p.stream().mapToLong(l -> l).min().getAsLong(); + for (long l : p) { + if (l > max && f.doubleValue() > l) { + max = l; + } + } + return max; + }); + } + + private static TestCaseSupplier ints( + String name, + DataType fieldType, + Supplier fieldSupplier, + List pointsTypes, + Supplier> pointsSupplier + ) { + return supplier(name, fieldType, fieldSupplier, pointsTypes, pointsSupplier, (f, p) -> { + int max = p.stream().mapToInt(i -> i).min().getAsInt(); + for (int l : p) { + if (l > max && f.doubleValue() > l) { + max = l; + } + } + return max; + }); + } + + private static

TestCaseSupplier supplier( + String name, + DataType fieldType, + Supplier fieldSupplier, + List pointsTypes, + Supplier> pointsSupplier, + BiFunction, Number> expected + ) { + List types = new ArrayList<>(pointsTypes.size() + 1); + types.add(fieldType); + types.addAll(pointsTypes); + return new TestCaseSupplier(name, types, () -> { + Number field = fieldSupplier.get(); + List

points = pointsSupplier.get(); + + List params = new ArrayList<>(1 + points.size()); + params.add(new TestCaseSupplier.TypedData(field, fieldType, "field")); + for (int i = 0; i < points.size(); i++) { + params.add(new TestCaseSupplier.TypedData(points.get(i), pointsTypes.get(i), "point" + i).forceLiteral()); + } + + DataType expectedType = expectedType(types); + String type = switch (expectedType) { + case DOUBLE -> "Double"; + case INTEGER -> "Int"; + case DATETIME, DATE_NANOS, LONG -> "Long"; + default -> throw new UnsupportedOperationException(); + }; + Matcher expectedEvaluatorName = startsWith("RoundTo" + type + specialization(points.size()) + "Evaluator"); + return new TestCaseSupplier.TestCase(params, expectedEvaluatorName, expectedType, equalTo(expected.apply(field, points))); + }); + } + + private static String specialization(int pointsSize) { + if (pointsSize < 5) { + return Integer.toString(pointsSize); + } + if (pointsSize < 11) { + return "LinearSearch"; + } + return "BinarySearch"; + } + + private static DataType expectedType(List types) { + if (types.stream().anyMatch(t -> t == DataType.DOUBLE)) { + return DataType.DOUBLE; + } + if (types.stream().anyMatch(t -> t == DataType.LONG)) { + return DataType.LONG; + } + if (types.stream().anyMatch(t -> t == DataType.INTEGER)) { + return DataType.INTEGER; + } + if (types.stream().anyMatch(t -> t == DataType.DATETIME)) { + return DataType.DATETIME; + } + if (types.stream().anyMatch(t -> t == DataType.DATE_NANOS)) { + return DataType.DATE_NANOS; + } + throw new UnsupportedOperationException("can't build expected types for " + types); + } + + @Override + protected Expression build(Source source, List args) { + return new RoundTo(source, args.getFirst(), args.subList(1, args.size())); + } +} diff --git a/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/esql/60_usage.yml b/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/esql/60_usage.yml index affbab8701f8..3553381349d0 100644 --- a/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/esql/60_usage.yml +++ b/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/esql/60_usage.yml @@ -123,7 +123,7 @@ setup: - match: {esql.functions.coalesce: $functions_coalesce} - gt: {esql.functions.categorize: $functions_categorize} # Testing for the entire function set isn't feasible, so we just check that we return the correct count as an approximation. - - length: {esql.functions: 142} # check the "sister" test below for a likely update to the same esql.functions length check + - length: {esql.functions: 143} # check the "sister" test below for a likely update to the same esql.functions length check --- "Basic ESQL usage output (telemetry) non-snapshot version": @@ -221,7 +221,7 @@ setup: - gt: {esql.functions.to_long: $functions_to_long} - match: {esql.functions.coalesce: $functions_coalesce} - gt: {esql.functions.categorize: $functions_categorize} - - length: {esql.functions: 133} # check the "sister" test above for a likely update to the same esql.functions length check + - length: {esql.functions: 134} # check the "sister" test above for a likely update to the same esql.functions length check --- took: