ESQL: ROUND_TO function (#128278)

Creates a `ROUND_TO` function that rounds it's input to one of the
provided values. Like so:
```
ROUND_TO(v, 0, 5000, 10000, 20000, 40000, 100000)

   v   | ROUND_TO
     0 | 0
   100 | 0
  6000 | 5000
 45001 | 40000
999999 | 100000
```

For some sequences of numbers you could do this with the `/` operator -
but for arbitrary sequences of numbers you needed `CASE` which is quite
slow. And hard to read!

Rewriting the example above would look like:
```
CASE (
  v <   5000,     0,
  v <  10000,  5000,
  v <  20000, 10000,
  v <  40000, 20000,
  v < 100000, 40000,
  100000
)
```

Even better, this is *fast*:
```
        (operation)  Mode  Cnt    Score   Error  Units
round_to_4_via_case  avgt    7  138.124 ± 0.738  ns/op
         round_to_4  avgt    7    0.805 ± 0.011  ns/op
         round_to_3  avgt    7    0.739 ± 0.011  ns/op
         round_to_2  avgt    7    0.651 ± 0.009  ns/op
         date_trunc  avgt    7    2.425 ± 0.018  ns/op
```

I've included a comparison to `DATE_TRUNC` above because we should be
able to rewrite `DATE_TRUNC` into `ROUND_TO` when we know the date range
of the index. This doesn't do it now, but it should be possible.
This commit is contained in:
Nik Everett 2025-05-23 10:14:30 -04:00 committed by GitHub
parent 0c3d47de10
commit 45bfaab448
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
42 changed files with 4082 additions and 4 deletions

View file

@ -13,6 +13,7 @@ import org.apache.lucene.util.BytesRef;
import org.elasticsearch.common.breaker.NoopCircuitBreaker; import org.elasticsearch.common.breaker.NoopCircuitBreaker;
import org.elasticsearch.common.logging.LogConfigurator; import org.elasticsearch.common.logging.LogConfigurator;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.ByteSizeUnit;
import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.common.util.BigArrays;
import org.elasticsearch.compute.data.Block; import org.elasticsearch.compute.data.Block;
import org.elasticsearch.compute.data.BlockFactory; 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.conditional.Case;
import org.elasticsearch.xpack.esql.expression.function.scalar.date.DateTrunc; 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.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.multivalue.MvMin;
import org.elasticsearch.xpack.esql.expression.function.scalar.nulls.Coalesce; import org.elasticsearch.xpack.esql.expression.function.scalar.nulls.Coalesce;
import org.elasticsearch.xpack.esql.expression.function.scalar.string.RLike; 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.function.scalar.string.ToUpper;
import org.elasticsearch.xpack.esql.expression.predicate.operator.arithmetic.Add; 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.Equals;
import org.elasticsearch.xpack.esql.expression.predicate.operator.comparison.LessThan;
import org.elasticsearch.xpack.esql.planner.Layout; import org.elasticsearch.xpack.esql.planner.Layout;
import org.elasticsearch.xpack.esql.plugin.EsqlPlugin; import org.elasticsearch.xpack.esql.plugin.EsqlPlugin;
import org.elasticsearch.xpack.esql.session.Configuration; import org.elasticsearch.xpack.esql.session.Configuration;
@ -128,6 +131,10 @@ public class EvalBenchmark {
"long_equal_to_int", "long_equal_to_int",
"mv_min", "mv_min",
"mv_min_ascending", "mv_min_ascending",
"round_to_4_via_case",
"round_to_2",
"round_to_3",
"round_to_4",
"rlike", "rlike",
"to_lower", "to_lower",
"to_lower_ords", "to_lower_ords",
@ -240,6 +247,65 @@ public class EvalBenchmark {
RLike rlike = new RLike(Source.EMPTY, keywordField, new RLikePattern(".ar")); RLike rlike = new RLike(Source.EMPTY, keywordField, new RLikePattern(".ar"));
yield EvalMapper.toEvaluator(FOLD_CONTEXT, rlike, layout(keywordField)).get(driverContext); 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" -> { case "to_lower", "to_lower_ords" -> {
FieldAttribute keywordField = keywordField(); FieldAttribute keywordField = keywordField();
ToLower toLower = new ToLower(Source.EMPTY, keywordField, configuration()); 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.<LongBlock>getBlock(0).asVector();
LongVector result = actual.<LongBlock>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.<LongBlock>getBlock(0).asVector();
LongVector result = actual.<LongBlock>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.<LongBlock>getBlock(0).asVector();
LongVector result = actual.<LongBlock>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" -> 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_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") }); 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) { private static Page page(String operation) {
return switch (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); var builder = blockFactory.newLongBlockBuilder(BLOCK_LENGTH);
for (int i = 0; i < BLOCK_LENGTH; i++) { for (int i = 0; i < BLOCK_LENGTH; i++) {
builder.appendLong(i * 100_000); 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 @Benchmark
@OperationsPerInvocation(1024 * BLOCK_LENGTH) @OperationsPerInvocation(1024 * BLOCK_LENGTH)
public void run() { public void run() {

View file

@ -0,0 +1,5 @@
pr: 128278
summary: ROUND_TO function
area: ES|QL
type: enhancement
issues: []

View file

@ -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.

View file

@ -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 |

View file

@ -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
:::

View file

@ -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.

View file

@ -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 |

View file

@ -0,0 +1 @@
<svg version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" width="484" height="61" viewbox="0 0 484 61"><defs><style type="text/css">.c{fill:none;stroke:#222222;}.k{fill:#000000;font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;font-size:20px;}.s{fill:#e4f4ff;stroke:#222222;}.syn{fill:#8D8D8D;font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;font-size:20px;}</style></defs><path class="c" d="M0 46h5m116 0h10m32 0h10m80 0h30m-5 0q-5 0-5-5v-26q0-5 5-5h144q5 0 5 5v26q0 5-5 5m-107 0h10m92 0h30m32 0h5"/><rect class="s" x="5" y="20" width="116" height="36"/><text class="k" x="15" y="46">ROUND_TO</text><rect class="s" x="131" y="20" width="32" height="36" rx="7"/><text class="syn" x="141" y="46">(</text><rect class="s" x="173" y="20" width="80" height="36" rx="7"/><text class="k" x="183" y="46">field</text><rect class="s" x="283" y="20" width="32" height="36" rx="7"/><text class="syn" x="293" y="46">,</text><rect class="s" x="325" y="20" width="92" height="36" rx="7"/><text class="k" x="335" y="46">points</text><rect class="s" x="447" y="20" width="32" height="36" rx="7"/><text class="syn" x="457" y="46">)</text></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -0,0 +1,211 @@
{
"comment" : "This is generated by ESQLs 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
}

View file

@ -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
```

View file

@ -449,4 +449,21 @@ tasks.named('stringTemplates').configure {
it.inputFile = coalesceInputFile it.inputFile = coalesceInputFile
it.outputFile = "org/elasticsearch/xpack/esql/expression/function/scalar/nulls/CoalesceBytesRefEvaluator.java" 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"
}
} }

View file

@ -793,6 +793,146 @@ ul:ul
null 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 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; 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;

View file

@ -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.
* <p>
* We have specializations for when there are very few rounding points because
* those are very fast and quite common.
* </p>
* 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;
}
}

View file

@ -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.
* <p>
* We have specializations for when there are very few rounding points because
* those are very fast and quite common.
* </p>
* 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;
}
}

View file

@ -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.
* <p>
* We have specializations for when there are very few rounding points because
* those are very fast and quite common.
* </p>
* 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;
}
}

View file

@ -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 + "]";
}
}
}

View file

@ -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 + "]";
}
}
}

View file

@ -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 + "]";
}
}
}

View file

@ -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 + "]";
}
}
}

View file

@ -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 + "]";
}
}
}

View file

@ -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 + "]";
}
}
}

View file

@ -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 + "]";
}
}
}

View file

@ -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 + "]";
}
}
}

View file

@ -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 + "]";
}
}
}

View file

@ -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 + "]";
}
}
}

View file

@ -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 + "]";
}
}
}

View file

@ -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 + "]";
}
}
}

View file

@ -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 + "]";
}
}
}

View file

@ -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 + "]";
}
}
}

View file

@ -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 + "]";
}
}
}

View file

@ -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 + "]";
}
}
}

View file

@ -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 + "]";
}
}
}

View file

@ -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 + "]";
}
}
}

View file

@ -1102,7 +1102,12 @@ public class EsqlCapabilities {
* Avid GROK and DISSECT attributes being removed when resolving fields. * Avid GROK and DISSECT attributes being removed when resolving fields.
* see <a href="https://github.com/elastic/elasticsearch/issues/127468"> ES|QL: Grok only supports KEYWORD or TEXT values, found expression [type] type [INTEGER] #127468 </a> * see <a href="https://github.com/elastic/elasticsearch/issues/127468"> ES|QL: Grok only supports KEYWORD or TEXT values, found expression [type] type [INTEGER] #127468 </a>
*/ */
KEEP_REGEX_EXTRACT_ATTRIBUTES; KEEP_REGEX_EXTRACT_ATTRIBUTES,
/**
* The {@code ROUND_TO} function.
*/
ROUND_TO;
private final boolean enabled; private final boolean enabled;

View file

@ -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.Pi;
import org.elasticsearch.xpack.esql.expression.function.scalar.math.Pow; 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.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.Scalb;
import org.elasticsearch.xpack.esql.expression.function.scalar.math.Signum; import org.elasticsearch.xpack.esql.expression.function.scalar.math.Signum;
import org.elasticsearch.xpack.esql.expression.function.scalar.math.Sin; import org.elasticsearch.xpack.esql.expression.function.scalar.math.Sin;
@ -326,6 +327,7 @@ public class EsqlFunctionRegistry {
def(Pi.class, Pi::new, "pi"), def(Pi.class, Pi::new, "pi"),
def(Pow.class, Pow::new, "pow"), def(Pow.class, Pow::new, "pow"),
def(Round.class, Round::new, "round"), def(Round.class, Round::new, "round"),
def(RoundTo.class, RoundTo::new, "round_to"),
def(Scalb.class, Scalb::new, "scalb"), def(Scalb.class, Scalb::new, "scalb"),
def(Signum.class, Signum::new, "signum"), def(Signum.class, Signum::new, "signum"),
def(Sin.class, Sin::new, "sin"), def(Sin.class, Sin::new, "sin"),

View file

@ -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.Pi;
import org.elasticsearch.xpack.esql.expression.function.scalar.math.Pow; 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.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.math.Tau;
import org.elasticsearch.xpack.esql.expression.function.scalar.nulls.Coalesce; import org.elasticsearch.xpack.esql.expression.function.scalar.nulls.Coalesce;
import org.elasticsearch.xpack.esql.expression.function.scalar.string.BitLength; import org.elasticsearch.xpack.esql.expression.function.scalar.string.BitLength;
@ -94,6 +95,7 @@ public class ScalarFunctionWritables {
entries.add(Replace.ENTRY); entries.add(Replace.ENTRY);
entries.add(Reverse.ENTRY); entries.add(Reverse.ENTRY);
entries.add(Round.ENTRY); entries.add(Round.ENTRY);
entries.add(RoundTo.ENTRY);
entries.add(Sha1.ENTRY); entries.add(Sha1.ENTRY);
entries.add(Sha256.ENTRY); entries.add(Sha256.ENTRY);
entries.add(Split.ENTRY); entries.add(Split.ENTRY);

View file

@ -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<Expression> 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<Expression> 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<Expression> newChildren) {
return new RoundTo(source(), newChildren.get(0), newChildren.subList(1, newChildren.size()));
}
@Override
protected NodeInfo<? extends Expression> info() {
return NodeInfo.create(this, RoundTo::new, field(), points());
}
public Expression field() {
return field;
}
public List<Expression> 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<Object> 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<Object> points);
}
private static final Map<DataType, Build> 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)
);
}

View file

@ -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.
* <p>
* We have specializations for when there are very few rounding points because
* those are very fast and quite common.
* </p>
* 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;
}
}

View file

@ -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<TestCaseSupplier> 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<Expression> args) {
return new RoundTo(source, args.getFirst(), args.subList(1, args.size()));
}
@Override
protected Matcher<String> expectedTypeErrorMatcher(List<Set<DataType>> validPerPosition, List<DataType> signature) {
return equalTo("all arguments must be numeric, date, or data_nanos");
}
}

View file

@ -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<RoundTo> {
@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<Expression> 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<Expression> randomPoints(DataType type) {
int length = between(1, 100);
List<Expression> 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<Expression> 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);
}
}

View file

@ -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.TestCase> testCaseSupplier) {
this.testCase = testCaseSupplier.get();
}
@ParametersFactory
public static Iterable<Object[]> parameters() {
List<TestCaseSupplier> suppliers = new ArrayList<>();
for (int p = 1; p < 20; p++) {
int points = p;
suppliers.add(
doubles(
"<double, " + points + " 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(
"<double, " + points + " longs>",
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(
"<double, " + points + " ints>",
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(
"<long, " + points + " 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(
"<int, " + points + " 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(
"<long, " + points + " 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(
"<int, " + points + " 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(
"<date, " + points + " dates>",
DataType.DATETIME,
ESTestCase::randomMillisUpToYear9999,
IntStream.range(0, points).mapToObj(i -> DataType.DATETIME).toList(),
() -> IntStream.range(0, points).mapToObj(i -> randomMillisUpToYear9999()).toList()
)
);
suppliers.add(
longs(
"<date_nanos, " + points + " date_nanos>",
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(
"<long, " + points + " ints>",
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(
"<int, " + points + " 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<DataType> 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<String> 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<Number> fieldSupplier,
List<DataType> pointsTypes,
Supplier<List<Double>> 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<Number> fieldSupplier,
List<DataType> pointsTypes,
Supplier<List<Long>> 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<Number> fieldSupplier,
List<DataType> pointsTypes,
Supplier<List<Integer>> 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 <P> TestCaseSupplier supplier(
String name,
DataType fieldType,
Supplier<Number> fieldSupplier,
List<DataType> pointsTypes,
Supplier<List<P>> pointsSupplier,
BiFunction<Number, List<P>, Number> expected
) {
List<DataType> types = new ArrayList<>(pointsTypes.size() + 1);
types.add(fieldType);
types.addAll(pointsTypes);
return new TestCaseSupplier(name, types, () -> {
Number field = fieldSupplier.get();
List<P> points = pointsSupplier.get();
List<TestCaseSupplier.TypedData> 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<String> 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<DataType> 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<Expression> args) {
return new RoundTo(source, args.getFirst(), args.subList(1, args.size()));
}
}

View file

@ -123,7 +123,7 @@ setup:
- match: {esql.functions.coalesce: $functions_coalesce} - match: {esql.functions.coalesce: $functions_coalesce}
- gt: {esql.functions.categorize: $functions_categorize} - 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. # 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": "Basic ESQL usage output (telemetry) non-snapshot version":
@ -221,7 +221,7 @@ setup:
- gt: {esql.functions.to_long: $functions_to_long} - gt: {esql.functions.to_long: $functions_to_long}
- match: {esql.functions.coalesce: $functions_coalesce} - match: {esql.functions.coalesce: $functions_coalesce}
- gt: {esql.functions.categorize: $functions_categorize} - 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: took: