From caaeed4aad8fee6994c85f6a8b998e49902d7c47 Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Thu, 13 Mar 2025 11:00:25 -0400 Subject: [PATCH] ESQL: TO_LOWER process all values (#124676) Make `TO_LOWER` and `TO_UPPER` process all values it received. This is quite large because it borrows a lot of code from the regular evaluator generator to generate conversions so we can use the Locale. That change propagates to the order of some parameters and to the `toString` and a few more places. Closes #124002 --- docs/changelog/124676.yaml | 6 + .../esql/functions/examples/to_lower.asciidoc | 10 +- .../functions/kibana/definition/to_lower.json | 7 +- .../functions/kibana/definition/to_upper.json | 4 +- .../functions/parameters/to_lower.asciidoc | 2 +- .../functions/parameters/to_upper.asciidoc | 2 +- .../src/main/resources/string.csv-spec | 28 +++++ .../scalar/string/ChangeCaseEvaluator.java | 110 +++++++++--------- .../xpack/esql/action/EsqlCapabilities.java | 7 +- ...ingFromAggregateMetricDoubleEvaluator.java | 3 +- .../function/scalar/string/ChangeCase.java | 4 +- .../function/scalar/string/ToLower.java | 14 +-- .../function/scalar/string/ToUpper.java | 12 +- .../scalar/convert/ToDegreesTests.java | 17 ++- .../scalar/convert/ToDoubleTests.java | 25 ++-- .../scalar/convert/ToIntegerTests.java | 39 ++++--- .../function/scalar/convert/ToLongTests.java | 33 +++--- .../scalar/convert/ToRadiansTests.java | 16 ++- .../scalar/convert/ToUnsignedLongTests.java | 59 +++++----- .../function/scalar/string/ToLowerTests.java | 28 +++-- .../function/scalar/string/ToUpperTests.java | 28 +++-- 21 files changed, 256 insertions(+), 198 deletions(-) create mode 100644 docs/changelog/124676.yaml diff --git a/docs/changelog/124676.yaml b/docs/changelog/124676.yaml new file mode 100644 index 0000000000000..94b52127e92e5 --- /dev/null +++ b/docs/changelog/124676.yaml @@ -0,0 +1,6 @@ +pr: 124676 +summary: TO_LOWER processes all values +area: ES|QL +type: bug +issues: + - 124002 diff --git a/docs/reference/esql/functions/examples/to_lower.asciidoc b/docs/reference/esql/functions/examples/to_lower.asciidoc index 7106bd553826c..0e32b0b1fa4c3 100644 --- a/docs/reference/esql/functions/examples/to_lower.asciidoc +++ b/docs/reference/esql/functions/examples/to_lower.asciidoc @@ -1,6 +1,6 @@ // This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it. -*Example* +*Examples* [source.merge.styled,esql] ---- @@ -10,4 +10,12 @@ include::{esql-specs}/string.csv-spec[tag=to_lower] |=== include::{esql-specs}/string.csv-spec[tag=to_lower-result] |=== +[source.merge.styled,esql] +---- +include::{esql-specs}/string.csv-spec[tag=to_lower_mv] +---- +[%header.monospaced.styled,format=dsv,separator=|] +|=== +include::{esql-specs}/string.csv-spec[tag=to_lower_mv-result] +|=== diff --git a/docs/reference/esql/functions/kibana/definition/to_lower.json b/docs/reference/esql/functions/kibana/definition/to_lower.json index 07bb057fe080d..85b8b9f2b4574 100644 --- a/docs/reference/esql/functions/kibana/definition/to_lower.json +++ b/docs/reference/esql/functions/kibana/definition/to_lower.json @@ -10,7 +10,7 @@ "name" : "str", "type" : "keyword", "optional" : false, - "description" : "String expression. If `null`, the function returns `null`." + "description" : "String expression. If `null`, the function returns `null`. The input can be a single- or multi-valued column or an expression." } ], "variadic" : false, @@ -22,7 +22,7 @@ "name" : "str", "type" : "text", "optional" : false, - "description" : "String expression. If `null`, the function returns `null`." + "description" : "String expression. If `null`, the function returns `null`. The input can be a single- or multi-valued column or an expression." } ], "variadic" : false, @@ -30,7 +30,8 @@ } ], "examples" : [ - "ROW message = \"Some Text\"\n| EVAL message_lower = TO_LOWER(message)" + "ROW message = \"Some Text\"\n| EVAL message_lower = TO_LOWER(message)", + "ROW v = TO_LOWER([\"Some\", \"Text\"])" ], "preview" : false, "snapshot_only" : false diff --git a/docs/reference/esql/functions/kibana/definition/to_upper.json b/docs/reference/esql/functions/kibana/definition/to_upper.json index caa9d563b08b1..62286c4e8aece 100644 --- a/docs/reference/esql/functions/kibana/definition/to_upper.json +++ b/docs/reference/esql/functions/kibana/definition/to_upper.json @@ -10,7 +10,7 @@ "name" : "str", "type" : "keyword", "optional" : false, - "description" : "String expression. If `null`, the function returns `null`." + "description" : "String expression. If `null`, the function returns `null`. The input can be a single- or multi-valued column or an expression." } ], "variadic" : false, @@ -22,7 +22,7 @@ "name" : "str", "type" : "text", "optional" : false, - "description" : "String expression. If `null`, the function returns `null`." + "description" : "String expression. If `null`, the function returns `null`. The input can be a single- or multi-valued column or an expression." } ], "variadic" : false, diff --git a/docs/reference/esql/functions/parameters/to_lower.asciidoc b/docs/reference/esql/functions/parameters/to_lower.asciidoc index d56d115662491..b8d435da4425a 100644 --- a/docs/reference/esql/functions/parameters/to_lower.asciidoc +++ b/docs/reference/esql/functions/parameters/to_lower.asciidoc @@ -3,4 +3,4 @@ *Parameters* `str`:: -String expression. If `null`, the function returns `null`. +String expression. If `null`, the function returns `null`. The input can be a single- or multi-valued column or an expression. diff --git a/docs/reference/esql/functions/parameters/to_upper.asciidoc b/docs/reference/esql/functions/parameters/to_upper.asciidoc index d56d115662491..b8d435da4425a 100644 --- a/docs/reference/esql/functions/parameters/to_upper.asciidoc +++ b/docs/reference/esql/functions/parameters/to_upper.asciidoc @@ -3,4 +3,4 @@ *Parameters* `str`:: -String expression. If `null`, the function returns `null`. +String expression. If `null`, the function returns `null`. The input can be a single- or multi-valued column or an expression. diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/string.csv-spec b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/string.csv-spec index 55aeb3ac3969c..96da725ea4b83 100644 --- a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/string.csv-spec +++ b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/string.csv-spec @@ -1399,6 +1399,20 @@ emp_no:integer | first_name:keyword | name_lower:keyword ; +toLowerMv +required_capability: to_lower_mv +// tag::to_lower_mv[] +ROW v = TO_LOWER(["Some", "Text"]) +// end::to_lower_mv[] +; + +// tag::to_lower_mv-result[] + v:keyword +["some", "text"] +// end::to_lower_mv-result[] +; + + toUpperRow#[skip:-8.12.99] // tag::to_upper[] ROW message = "Some Text" @@ -1421,6 +1435,20 @@ emp_no:integer | first_name:keyword | name_upper:keyword ; +toUpperMv +required_capability: to_lower_mv +// tag::to_upper_mv[] +ROW v = TO_UPPER(["Some", "Text"]) +// end::to_upper_mv[] +; + +// tag::to_upper_mv-result[] + v:keyword +["SOME", "TEXT"] +// end::to_upper_mv-result[] +; + + toUpperLowerUnicode#[skip:-8.12.99] row a = "π/2 + a + B + Λ ºC" | eval lower = to_lower(a), upper = to_upper(a) | keep a, upper, lower; diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/ChangeCaseEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/ChangeCaseEvaluator.java index 735bddc9918dd..6bc611d78ebda 100644 --- a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/ChangeCaseEvaluator.java +++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/ChangeCaseEvaluator.java @@ -4,7 +4,6 @@ // 2.0. package org.elasticsearch.xpack.esql.expression.function.scalar.string; -import java.lang.IllegalArgumentException; import java.lang.Override; import java.lang.String; import java.util.Locale; @@ -12,79 +11,92 @@ import org.elasticsearch.compute.data.Block; import org.elasticsearch.compute.data.BytesRefBlock; import org.elasticsearch.compute.data.BytesRefVector; -import org.elasticsearch.compute.data.Page; +import org.elasticsearch.compute.data.Vector; 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; +import org.elasticsearch.xpack.esql.expression.function.scalar.convert.AbstractConvertFunction; /** * {@link EvalOperator.ExpressionEvaluator} implementation for {@link ChangeCase}. - * This class is generated. Edit {@code EvaluatorImplementer} instead. + * This class is generated. Edit {@code ConvertEvaluatorImplementer} instead. */ -public final class ChangeCaseEvaluator implements EvalOperator.ExpressionEvaluator { - private final Source source; - +public final class ChangeCaseEvaluator extends AbstractConvertFunction.AbstractEvaluator { private final EvalOperator.ExpressionEvaluator val; private final Locale locale; private final ChangeCase.Case caseType; - private final DriverContext driverContext; - - private Warnings warnings; - public ChangeCaseEvaluator(Source source, EvalOperator.ExpressionEvaluator val, Locale locale, ChangeCase.Case caseType, DriverContext driverContext) { - this.source = source; + super(driverContext, source); this.val = val; this.locale = locale; this.caseType = caseType; - this.driverContext = driverContext; } @Override - public Block eval(Page page) { - try (BytesRefBlock valBlock = (BytesRefBlock) val.eval(page)) { - BytesRefVector valVector = valBlock.asVector(); - if (valVector == null) { - return eval(page.getPositionCount(), valBlock); + public EvalOperator.ExpressionEvaluator next() { + return val; + } + + @Override + public Block evalVector(Vector v) { + BytesRefVector vector = (BytesRefVector) v; + int positionCount = v.getPositionCount(); + BytesRef scratchPad = new BytesRef(); + if (vector.isConstant()) { + return driverContext.blockFactory().newConstantBytesRefBlockWith(evalValue(vector, 0, scratchPad), positionCount); + } + try (BytesRefBlock.Builder builder = driverContext.blockFactory().newBytesRefBlockBuilder(positionCount)) { + for (int p = 0; p < positionCount; p++) { + builder.appendBytesRef(evalValue(vector, p, scratchPad)); } - return eval(page.getPositionCount(), valVector).asBlock(); + return builder.build(); } } - public BytesRefBlock eval(int positionCount, BytesRefBlock valBlock) { - try(BytesRefBlock.Builder result = driverContext.blockFactory().newBytesRefBlockBuilder(positionCount)) { - BytesRef valScratch = new BytesRef(); - position: for (int p = 0; p < positionCount; p++) { - if (valBlock.isNull(p)) { - result.appendNull(); - continue position; - } - if (valBlock.getValueCount(p) != 1) { - if (valBlock.getValueCount(p) > 1) { - warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value")); + private BytesRef evalValue(BytesRefVector container, int index, BytesRef scratchPad) { + BytesRef value = container.getBytesRef(index, scratchPad); + return ChangeCase.process(value, this.locale, this.caseType); + } + + @Override + public Block evalBlock(Block b) { + BytesRefBlock block = (BytesRefBlock) b; + int positionCount = block.getPositionCount(); + try (BytesRefBlock.Builder builder = driverContext.blockFactory().newBytesRefBlockBuilder(positionCount)) { + BytesRef scratchPad = new BytesRef(); + for (int p = 0; p < positionCount; p++) { + int valueCount = block.getValueCount(p); + int start = block.getFirstValueIndex(p); + int end = start + valueCount; + boolean positionOpened = false; + boolean valuesAppended = false; + for (int i = start; i < end; i++) { + BytesRef value = evalValue(block, i, scratchPad); + if (positionOpened == false && valueCount > 1) { + builder.beginPositionEntry(); + positionOpened = true; } - result.appendNull(); - continue position; + builder.appendBytesRef(value); + valuesAppended = true; + } + if (valuesAppended == false) { + builder.appendNull(); + } else if (positionOpened) { + builder.endPositionEntry(); } - result.appendBytesRef(ChangeCase.process(valBlock.getBytesRef(valBlock.getFirstValueIndex(p), valScratch), this.locale, this.caseType)); } - return result.build(); + return builder.build(); } } - public BytesRefVector eval(int positionCount, BytesRefVector valVector) { - try(BytesRefVector.Builder result = driverContext.blockFactory().newBytesRefVectorBuilder(positionCount)) { - BytesRef valScratch = new BytesRef(); - position: for (int p = 0; p < positionCount; p++) { - result.appendBytesRef(ChangeCase.process(valVector.getBytesRef(p, valScratch), this.locale, this.caseType)); - } - return result.build(); - } + private BytesRef evalValue(BytesRefBlock container, int index, BytesRef scratchPad) { + BytesRef value = container.getBytesRef(index, scratchPad); + return ChangeCase.process(value, this.locale, this.caseType); } @Override @@ -97,19 +109,7 @@ public void close() { Releasables.closeExpectNoException(val); } - 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 { + public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { private final Source source; private final EvalOperator.ExpressionEvaluator.Factory val; diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java index ba3f0316c3c8a..aa54766c28115 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java @@ -838,7 +838,12 @@ public enum Cap { * During resolution (pre-analysis) we have to consider that joins or enriches can override EVALuated values * https://github.com/elastic/elasticsearch/issues/126419 */ - FIX_JOIN_MASKING_EVAL; + FIX_JOIN_MASKING_EVAL, + + /** + * Do {@code TO_LOWER} and {@code TO_UPPER} process all field values? + */ + TO_LOWER_MV; private final boolean enabled; diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromAggregateMetricDoubleEvaluator.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromAggregateMetricDoubleEvaluator.java index 00c02e8131277..69a6eac844475 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromAggregateMetricDoubleEvaluator.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringFromAggregateMetricDoubleEvaluator.java @@ -14,6 +14,7 @@ import org.elasticsearch.compute.data.Vector; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.core.Releasables; import org.elasticsearch.xpack.esql.core.tree.Source; import static org.elasticsearch.xpack.esql.type.EsqlDataTypeConverter.aggregateMetricDoubleBlockToString; @@ -63,7 +64,7 @@ public Block evalBlock(Block b) { @Override public void close() { - field.close(); + Releasables.closeExpectNoException(field); } public static class Factory implements EvalOperator.ExpressionEvaluator.Factory { diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ChangeCase.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ChangeCase.java index fe9a2d5beb3cf..dbf2c7a35dc20 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ChangeCase.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ChangeCase.java @@ -9,7 +9,7 @@ import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.lucene.BytesRefs; -import org.elasticsearch.compute.ann.Evaluator; +import org.elasticsearch.compute.ann.ConvertEvaluator; import org.elasticsearch.compute.ann.Fixed; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.xpack.esql.core.expression.Expression; @@ -99,7 +99,7 @@ public Expression replaceChildren(List newChildren) { return replaceChild(newChildren.get(0)); } - @Evaluator + @ConvertEvaluator static BytesRef process(BytesRef val, @Fixed Locale locale, @Fixed Case caseType) { return BytesRefs.toBytesRef(caseType.process(val.utf8ToString(), locale)); } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ToLower.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ToLower.java index 084afb1b69996..384ddd6ac1d2c 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ToLower.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ToLower.java @@ -27,17 +27,11 @@ public class ToLower extends ChangeCase { @FunctionInfo( returnType = { "keyword" }, description = "Returns a new string representing the input string converted to lower case.", - examples = @Example(file = "string", tag = "to_lower") + examples = { @Example(file = "string", tag = "to_lower"), @Example(file = "string", tag = "to_lower_mv"), } ) - public ToLower( - Source source, - @Param( - name = "str", - type = { "keyword", "text" }, - description = "String expression. If `null`, the function returns `null`." - ) Expression field, - Configuration configuration - ) { + public ToLower(Source source, @Param(name = "str", type = { "keyword", "text" }, description = """ + String expression. If `null`, the function returns `null`. + The input can be a single- or multi-valued column or an expression.""") Expression field, Configuration configuration) { super(source, field, configuration, Case.LOWER); } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ToUpper.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ToUpper.java index 4509404754f36..0941324be3522 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ToUpper.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ToUpper.java @@ -29,15 +29,9 @@ public class ToUpper extends ChangeCase { description = "Returns a new string representing the input string converted to upper case.", examples = @Example(file = "string", tag = "to_upper") ) - public ToUpper( - Source source, - @Param( - name = "str", - type = { "keyword", "text" }, - description = "String expression. If `null`, the function returns `null`." - ) Expression field, - Configuration configuration - ) { + public ToUpper(Source source, @Param(name = "str", type = { "keyword", "text" }, description = """ + String expression. If `null`, the function returns `null`. + The input can be a single- or multi-valued column or an expression.""") Expression field, Configuration configuration) { super(source, field, configuration, Case.UPPER); } diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDegreesTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDegreesTests.java index 92dbbdb57efa9..8ed2aba370605 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDegreesTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDegreesTests.java @@ -19,7 +19,6 @@ import java.math.BigInteger; import java.util.ArrayList; import java.util.List; -import java.util.function.BiFunction; import java.util.function.Supplier; public class ToDegreesTests extends AbstractScalarFunctionTestCase { @@ -30,16 +29,10 @@ public ToDegreesTests(@Name("TestCase") Supplier test @ParametersFactory public static Iterable parameters() { // TODO multivalue fields - BiFunction evaluatorName = (eval, f) -> "ToDegreesEvaluator[deg=" - + eval - + "[" - + f - + "=Attribute[channel=0]]]"; List suppliers = new ArrayList<>(); - TestCaseSupplier.forUnaryInt( suppliers, - evaluatorName.apply("ToDoubleFromIntEvaluator", "i"), + evaluatorName("ToDoubleFromIntEvaluator", "i"), DataType.DOUBLE, Math::toDegrees, Integer.MIN_VALUE, @@ -48,7 +41,7 @@ public static Iterable parameters() { ); TestCaseSupplier.forUnaryLong( suppliers, - evaluatorName.apply("ToDoubleFromLongEvaluator", "l"), + evaluatorName("ToDoubleFromLongEvaluator", "l"), DataType.DOUBLE, Math::toDegrees, Long.MIN_VALUE, @@ -57,7 +50,7 @@ public static Iterable parameters() { ); TestCaseSupplier.forUnaryUnsignedLong( suppliers, - evaluatorName.apply("ToDoubleFromUnsignedLongEvaluator", "l"), + evaluatorName("ToDoubleFromUnsignedLongEvaluator", "l"), DataType.DOUBLE, ul -> Math.toDegrees(ul.doubleValue()), BigInteger.ZERO, @@ -96,6 +89,10 @@ public static Iterable parameters() { return parameterSuppliersFromTypedDataWithDefaultChecksNoErrors(true, suppliers); } + private static String evaluatorName(String inner, String next) { + return "ToDegreesEvaluator[deg=" + inner + "[" + next + "=Attribute[channel=0]]]"; + } + @Override protected Expression build(Source source, List args) { return new ToDegrees(source, args.get(0)); diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDoubleTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDoubleTests.java index ddefde86b76a3..c0f16c5b9a3c0 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDoubleTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDoubleTests.java @@ -24,7 +24,6 @@ import java.time.Instant; import java.util.ArrayList; import java.util.List; -import java.util.function.BiFunction; import java.util.function.Supplier; public class ToDoubleTests extends AbstractScalarFunctionTestCase { @@ -36,7 +35,6 @@ public ToDoubleTests(@Name("TestCase") Supplier testC public static Iterable parameters() { // TODO multivalue fields String read = "Attribute[channel=0]"; - BiFunction evaluatorName = (s, f) -> "ToDoubleFrom" + s + "Evaluator[" + f + "=" + read + "]"; List suppliers = new ArrayList<>(); TestCaseSupplier.forUnaryDouble( @@ -49,17 +47,17 @@ public static Iterable parameters() { List.of() ); - TestCaseSupplier.forUnaryBoolean(suppliers, evaluatorName.apply("Boolean", "bool"), DataType.DOUBLE, b -> b ? 1d : 0d, List.of()); + TestCaseSupplier.forUnaryBoolean(suppliers, evaluatorName("Boolean", "bool"), DataType.DOUBLE, b -> b ? 1d : 0d, List.of()); TestCaseSupplier.unary( suppliers, - evaluatorName.apply("Long", "l"), + evaluatorName("Long", "l"), TestCaseSupplier.dateCases(), DataType.DOUBLE, i -> (double) ((Instant) i).toEpochMilli(), List.of() ); // random strings that don't look like a double - TestCaseSupplier.forUnaryStrings(suppliers, evaluatorName.apply("String", "in"), DataType.DOUBLE, bytesRef -> null, bytesRef -> { + TestCaseSupplier.forUnaryStrings(suppliers, evaluatorName("String", "in"), DataType.DOUBLE, bytesRef -> null, bytesRef -> { var exception = expectThrows( InvalidArgumentException.class, () -> EsqlDataTypeConverter.stringToDouble(bytesRef.utf8ToString()) @@ -71,7 +69,7 @@ public static Iterable parameters() { }); TestCaseSupplier.forUnaryUnsignedLong( suppliers, - evaluatorName.apply("UnsignedLong", "l"), + evaluatorName("UnsignedLong", "l"), DataType.DOUBLE, BigInteger::doubleValue, BigInteger.ZERO, @@ -80,7 +78,7 @@ public static Iterable parameters() { ); TestCaseSupplier.forUnaryLong( suppliers, - evaluatorName.apply("Long", "l"), + evaluatorName("Long", "l"), DataType.DOUBLE, l -> (double) l, Long.MIN_VALUE, @@ -89,7 +87,7 @@ public static Iterable parameters() { ); TestCaseSupplier.forUnaryInt( suppliers, - evaluatorName.apply("Int", "i"), + evaluatorName("Int", "i"), DataType.DOUBLE, i -> (double) i, Integer.MIN_VALUE, @@ -100,7 +98,7 @@ public static Iterable parameters() { // strings of random numbers TestCaseSupplier.unary( suppliers, - evaluatorName.apply("String", "in"), + evaluatorName("String", "in"), TestCaseSupplier.castToDoubleSuppliersFromRange(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY) .stream() .map( @@ -126,7 +124,7 @@ public static Iterable parameters() { ); TestCaseSupplier.unary( suppliers, - evaluatorName.apply("Int", "i"), + evaluatorName("Int", "i"), List.of(new TestCaseSupplier.TypedDataSupplier("counter", () -> randomInt(1000), DataType.COUNTER_INTEGER)), DataType.DOUBLE, l -> ((Integer) l).doubleValue(), @@ -134,7 +132,7 @@ public static Iterable parameters() { ); TestCaseSupplier.unary( suppliers, - evaluatorName.apply("Long", "l"), + evaluatorName("Long", "l"), List.of(new TestCaseSupplier.TypedDataSupplier("counter", () -> randomLongBetween(1, 1000), DataType.COUNTER_LONG)), DataType.DOUBLE, l -> ((Long) l).doubleValue(), @@ -144,6 +142,11 @@ public static Iterable parameters() { return parameterSuppliersFromTypedDataWithDefaultChecksNoErrors(true, suppliers); } + private static String evaluatorName(String inner, String next) { + String read = "Attribute[channel=0]"; + return "ToDoubleFrom" + inner + "Evaluator[" + next + "=" + read + "]"; + } + @Override protected Expression build(Source source, List args) { return new ToDouble(source, args.get(0)); diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIntegerTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIntegerTests.java index 23928a08a1032..51262b43d0ec6 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIntegerTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIntegerTests.java @@ -22,7 +22,6 @@ import java.time.Instant; import java.util.ArrayList; import java.util.List; -import java.util.function.BiFunction; import java.util.function.Supplier; import static org.elasticsearch.xpack.esql.core.type.DataTypeConverter.safeToInt; @@ -36,17 +35,16 @@ public ToIntegerTests(@Name("TestCase") Supplier test public static Iterable parameters() { // TODO multivalue fields String read = "Attribute[channel=0]"; - BiFunction evaluatorName = (s, f) -> "ToIntegerFrom" + s + "Evaluator[" + f + "=" + read + "]"; List suppliers = new ArrayList<>(); TestCaseSupplier.forUnaryInt(suppliers, read, DataType.INTEGER, i -> i, Integer.MIN_VALUE, Integer.MAX_VALUE, List.of()); - TestCaseSupplier.forUnaryBoolean(suppliers, evaluatorName.apply("Boolean", "bool"), DataType.INTEGER, b -> b ? 1 : 0, List.of()); + TestCaseSupplier.forUnaryBoolean(suppliers, evaluatorName("Boolean", "bool"), DataType.INTEGER, b -> b ? 1 : 0, List.of()); // datetimes that fall within Integer's range TestCaseSupplier.unary( suppliers, - evaluatorName.apply("Long", "lng"), + evaluatorName("Long", "lng"), dateCases(0, Integer.MAX_VALUE), DataType.INTEGER, l -> Long.valueOf(((Instant) l).toEpochMilli()).intValue(), @@ -55,7 +53,7 @@ public static Iterable parameters() { // datetimes that fall outside Integer's range TestCaseSupplier.unary( suppliers, - evaluatorName.apply("Long", "lng"), + evaluatorName("Long", "lng"), dateCases(Integer.MAX_VALUE + 1L, Long.MAX_VALUE), DataType.INTEGER, l -> null, @@ -69,7 +67,7 @@ public static Iterable parameters() { // random strings that don't look like an Integer TestCaseSupplier.forUnaryStrings( suppliers, - evaluatorName.apply("String", "in"), + evaluatorName("String", "in"), DataType.INTEGER, bytesRef -> null, bytesRef -> List.of( @@ -82,7 +80,7 @@ public static Iterable parameters() { // from doubles within Integer's range TestCaseSupplier.forUnaryDouble( suppliers, - evaluatorName.apply("Double", "dbl"), + evaluatorName("Double", "dbl"), DataType.INTEGER, d -> safeToInt(Math.round(d)), Integer.MIN_VALUE, @@ -92,7 +90,7 @@ public static Iterable parameters() { // from doubles outside Integer's range, negative TestCaseSupplier.forUnaryDouble( suppliers, - evaluatorName.apply("Double", "dbl"), + evaluatorName("Double", "dbl"), DataType.INTEGER, d -> null, Double.NEGATIVE_INFINITY, @@ -105,7 +103,7 @@ public static Iterable parameters() { // from doubles outside Integer's range, positive TestCaseSupplier.forUnaryDouble( suppliers, - evaluatorName.apply("Double", "dbl"), + evaluatorName("Double", "dbl"), DataType.INTEGER, d -> null, Integer.MAX_VALUE + 1d, @@ -119,7 +117,7 @@ public static Iterable parameters() { // from unsigned_long within Integer's range TestCaseSupplier.forUnaryUnsignedLong( suppliers, - evaluatorName.apply("UnsignedLong", "ul"), + evaluatorName("UnsignedLong", "ul"), DataType.INTEGER, BigInteger::intValue, BigInteger.ZERO, @@ -129,7 +127,7 @@ public static Iterable parameters() { // from unsigned_long outside Integer's range TestCaseSupplier.forUnaryUnsignedLong( suppliers, - evaluatorName.apply("UnsignedLong", "ul"), + evaluatorName("UnsignedLong", "ul"), DataType.INTEGER, ul -> null, BigInteger.valueOf(Integer.MAX_VALUE).add(BigInteger.ONE), @@ -144,7 +142,7 @@ public static Iterable parameters() { // from long, within Integer's range TestCaseSupplier.forUnaryLong( suppliers, - evaluatorName.apply("Long", "lng"), + evaluatorName("Long", "lng"), DataType.INTEGER, l -> (int) l, Integer.MIN_VALUE, @@ -154,7 +152,7 @@ public static Iterable parameters() { // from long, outside Integer's range, negative TestCaseSupplier.forUnaryLong( suppliers, - evaluatorName.apply("Long", "lng"), + evaluatorName("Long", "lng"), DataType.INTEGER, l -> null, Long.MIN_VALUE, @@ -168,7 +166,7 @@ public static Iterable parameters() { // from long, outside Integer's range, positive TestCaseSupplier.forUnaryLong( suppliers, - evaluatorName.apply("Long", "lng"), + evaluatorName("Long", "lng"), DataType.INTEGER, l -> null, Integer.MAX_VALUE + 1L, @@ -182,7 +180,7 @@ public static Iterable parameters() { // strings of random ints within Integer's range TestCaseSupplier.unary( suppliers, - evaluatorName.apply("String", "in"), + evaluatorName("String", "in"), TestCaseSupplier.intCases(Integer.MIN_VALUE, Integer.MAX_VALUE, true) .stream() .map( @@ -200,7 +198,7 @@ public static Iterable parameters() { // strings of random doubles within Integer's range TestCaseSupplier.unary( suppliers, - evaluatorName.apply("String", "in"), + evaluatorName("String", "in"), TestCaseSupplier.doubleCases(Integer.MIN_VALUE, Integer.MAX_VALUE, true) .stream() .map( @@ -218,7 +216,7 @@ public static Iterable parameters() { // strings of random doubles outside Integer's range, negative TestCaseSupplier.unary( suppliers, - evaluatorName.apply("String", "in"), + evaluatorName("String", "in"), TestCaseSupplier.doubleCases(Double.NEGATIVE_INFINITY, Integer.MIN_VALUE - 1d, true) .stream() .map( @@ -241,7 +239,7 @@ public static Iterable parameters() { // strings of random doubles outside Integer's range, positive TestCaseSupplier.unary( suppliers, - evaluatorName.apply("String", "in"), + evaluatorName("String", "in"), TestCaseSupplier.doubleCases(Integer.MAX_VALUE + 1d, Double.POSITIVE_INFINITY, true) .stream() .map( @@ -274,6 +272,11 @@ public static Iterable parameters() { return parameterSuppliersFromTypedDataWithDefaultChecksNoErrors(true, suppliers); } + private static String evaluatorName(String inner, String next) { + String read = "Attribute[channel=0]"; + return "ToIntegerFrom" + inner + "Evaluator[" + next + "=" + read + "]"; + } + @Override protected Expression build(Source source, List args) { return new ToInteger(source, args.get(0)); diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLongTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLongTests.java index 6224f51482847..50748d582ed1f 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLongTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLongTests.java @@ -23,7 +23,6 @@ import java.time.Instant; import java.util.ArrayList; import java.util.List; -import java.util.function.BiFunction; import java.util.function.Supplier; public class ToLongTests extends AbstractScalarFunctionTestCase { @@ -35,12 +34,11 @@ public ToLongTests(@Name("TestCase") Supplier testCas public static Iterable parameters() { // TODO multivalue fields String read = "Attribute[channel=0]"; - BiFunction evaluatorName = (s, f) -> "ToLongFrom" + s + "Evaluator[" + f + "=" + read + "]"; List suppliers = new ArrayList<>(); TestCaseSupplier.forUnaryLong(suppliers, read, DataType.LONG, l -> l, Long.MIN_VALUE, Long.MAX_VALUE, List.of()); - TestCaseSupplier.forUnaryBoolean(suppliers, evaluatorName.apply("Boolean", "bool"), DataType.LONG, b -> b ? 1L : 0L, List.of()); + TestCaseSupplier.forUnaryBoolean(suppliers, evaluatorName("Boolean", "bool"), DataType.LONG, b -> b ? 1L : 0L, List.of()); // datetimes TestCaseSupplier.unary( @@ -62,7 +60,7 @@ public static Iterable parameters() { // random strings that don't look like a long TestCaseSupplier.forUnaryStrings( suppliers, - evaluatorName.apply("String", "in"), + evaluatorName("String", "in"), DataType.LONG, bytesRef -> null, bytesRef -> List.of( @@ -75,7 +73,7 @@ public static Iterable parameters() { // from doubles within long's range TestCaseSupplier.forUnaryDouble( suppliers, - evaluatorName.apply("Double", "dbl"), + evaluatorName("Double", "dbl"), DataType.LONG, Math::round, Long.MIN_VALUE, @@ -85,7 +83,7 @@ public static Iterable parameters() { // from doubles outside long's range, negative TestCaseSupplier.forUnaryDouble( suppliers, - evaluatorName.apply("Double", "dbl"), + evaluatorName("Double", "dbl"), DataType.LONG, d -> null, Double.NEGATIVE_INFINITY, @@ -98,7 +96,7 @@ public static Iterable parameters() { // from doubles outside long's range, positive TestCaseSupplier.forUnaryDouble( suppliers, - evaluatorName.apply("Double", "dbl"), + evaluatorName("Double", "dbl"), DataType.LONG, d -> null, Long.MAX_VALUE + 1d, @@ -112,7 +110,7 @@ public static Iterable parameters() { // from unsigned_long within long's range TestCaseSupplier.forUnaryUnsignedLong( suppliers, - evaluatorName.apply("UnsignedLong", "ul"), + evaluatorName("UnsignedLong", "ul"), DataType.LONG, BigInteger::longValue, BigInteger.ZERO, @@ -121,7 +119,7 @@ public static Iterable parameters() { ); TestCaseSupplier.forUnaryUnsignedLong( suppliers, - evaluatorName.apply("UnsignedLong", "ul"), + evaluatorName("UnsignedLong", "ul"), DataType.LONG, ul -> null, BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.ONE), @@ -136,7 +134,7 @@ public static Iterable parameters() { // from integer TestCaseSupplier.forUnaryInt( suppliers, - evaluatorName.apply("Int", "i"), + evaluatorName("Int", "i"), DataType.LONG, l -> (long) l, Integer.MIN_VALUE, @@ -147,7 +145,7 @@ public static Iterable parameters() { // strings of random longs TestCaseSupplier.unary( suppliers, - evaluatorName.apply("String", "in"), + evaluatorName("String", "in"), TestCaseSupplier.longCases(Long.MIN_VALUE, Long.MAX_VALUE, true) .stream() .map( @@ -165,7 +163,7 @@ public static Iterable parameters() { // strings of random doubles within long's range TestCaseSupplier.unary( suppliers, - evaluatorName.apply("String", "in"), + evaluatorName("String", "in"), TestCaseSupplier.doubleCases(Long.MIN_VALUE, Long.MAX_VALUE, true) .stream() .map( @@ -183,7 +181,7 @@ public static Iterable parameters() { // strings of random doubles outside integer's range, negative TestCaseSupplier.unary( suppliers, - evaluatorName.apply("String", "in"), + evaluatorName("String", "in"), TestCaseSupplier.doubleCases(Double.NEGATIVE_INFINITY, Long.MIN_VALUE - 1d, true) .stream() .map( @@ -206,7 +204,7 @@ public static Iterable parameters() { // strings of random doubles outside integer's range, positive TestCaseSupplier.unary( suppliers, - evaluatorName.apply("String", "in"), + evaluatorName("String", "in"), TestCaseSupplier.doubleCases(Long.MAX_VALUE + 1d, Double.POSITIVE_INFINITY, true) .stream() .map( @@ -237,7 +235,7 @@ public static Iterable parameters() { ); TestCaseSupplier.unary( suppliers, - evaluatorName.apply("Int", "i"), + evaluatorName("Int", "i"), List.of(new TestCaseSupplier.TypedDataSupplier("counter", ESTestCase::randomInt, DataType.COUNTER_INTEGER)), DataType.LONG, l -> ((Integer) l).longValue(), @@ -246,6 +244,11 @@ public static Iterable parameters() { return parameterSuppliersFromTypedDataWithDefaultChecksNoErrors(true, suppliers); } + private static String evaluatorName(String next, String inner) { + String read = "Attribute[channel=0]"; + return "ToLongFrom" + next + "Evaluator[" + inner + "=" + read + "]"; + } + @Override protected Expression build(Source source, List args) { return new ToLong(source, args.get(0)); diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToRadiansTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToRadiansTests.java index e1834d6dfc2d7..ae2080c29eb57 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToRadiansTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToRadiansTests.java @@ -19,7 +19,6 @@ import java.math.BigInteger; import java.util.ArrayList; import java.util.List; -import java.util.function.BiFunction; import java.util.function.Supplier; public class ToRadiansTests extends AbstractScalarFunctionTestCase { @@ -30,16 +29,11 @@ public ToRadiansTests(@Name("TestCase") Supplier test @ParametersFactory public static Iterable parameters() { // TODO multivalue fields - BiFunction evaluatorName = (eval, f) -> "ToRadiansEvaluator[deg=" - + eval - + "[" - + f - + "=Attribute[channel=0]]]"; List suppliers = new ArrayList<>(); TestCaseSupplier.forUnaryInt( suppliers, - evaluatorName.apply("ToDoubleFromIntEvaluator", "i"), + evaluatorName("ToDoubleFromIntEvaluator", "i"), DataType.DOUBLE, Math::toRadians, Integer.MIN_VALUE, @@ -48,7 +42,7 @@ public static Iterable parameters() { ); TestCaseSupplier.forUnaryLong( suppliers, - evaluatorName.apply("ToDoubleFromLongEvaluator", "l"), + evaluatorName("ToDoubleFromLongEvaluator", "l"), DataType.DOUBLE, Math::toRadians, Long.MIN_VALUE, @@ -57,7 +51,7 @@ public static Iterable parameters() { ); TestCaseSupplier.forUnaryUnsignedLong( suppliers, - evaluatorName.apply("ToDoubleFromUnsignedLongEvaluator", "l"), + evaluatorName("ToDoubleFromUnsignedLongEvaluator", "l"), DataType.DOUBLE, ul -> Math.toRadians(ul.doubleValue()), BigInteger.ZERO, @@ -77,6 +71,10 @@ public static Iterable parameters() { return parameterSuppliersFromTypedDataWithDefaultChecksNoErrors(true, suppliers); } + private static String evaluatorName(String inner, String next) { + return "ToRadiansEvaluator[deg=" + inner + "[" + next + "=Attribute[channel=0]]]"; + } + @Override protected Expression build(Source source, List args) { return new ToRadians(source, args.get(0)); diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLongTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLongTests.java index 9d8e3daaef0ef..57b73eaccff87 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLongTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLongTests.java @@ -22,7 +22,6 @@ import java.time.Instant; import java.util.ArrayList; import java.util.List; -import java.util.function.BiFunction; import java.util.function.Supplier; import static org.elasticsearch.xpack.esql.core.type.DataTypeConverter.safeToUnsignedLong; @@ -37,7 +36,6 @@ public ToUnsignedLongTests(@Name("TestCase") Supplier public static Iterable parameters() { // TODO multivalue fields String read = "Attribute[channel=0]"; - BiFunction evaluatorName = (s, f) -> "ToUnsignedLongFrom" + s + "Evaluator[" + f + "=" + read + "]"; List suppliers = new ArrayList<>(); TestCaseSupplier.forUnaryUnsignedLong( @@ -52,7 +50,7 @@ public static Iterable parameters() { TestCaseSupplier.forUnaryBoolean( suppliers, - evaluatorName.apply("Boolean", "bool"), + evaluatorName("Boolean", "bool"), DataType.UNSIGNED_LONG, b -> b ? BigInteger.ONE : BigInteger.ZERO, List.of() @@ -61,33 +59,27 @@ public static Iterable parameters() { // datetimes TestCaseSupplier.unary( suppliers, - evaluatorName.apply("Long", "lng"), + evaluatorName("Long", "lng"), TestCaseSupplier.dateCases(), DataType.UNSIGNED_LONG, instant -> BigInteger.valueOf(((Instant) instant).toEpochMilli()), List.of() ); // random strings that don't look like an unsigned_long - TestCaseSupplier.forUnaryStrings( - suppliers, - evaluatorName.apply("String", "in"), - DataType.UNSIGNED_LONG, - bytesRef -> null, - bytesRef -> { - // BigDecimal, used to parse unsigned_longs will throw NFEs with different messages depending on empty string, first - // non-number character after a number-looking like prefix, or string starting with "e", maybe others -- safer to take - // this shortcut here. - Exception e = expectThrows(NumberFormatException.class, () -> new BigDecimal(bytesRef.utf8ToString())); - return List.of( - "Line 1:1: evaluation of [source] failed, treating result as null. Only first 20 failures recorded.", - "Line 1:1: java.lang.NumberFormatException: " + e.getMessage() - ); - } - ); + TestCaseSupplier.forUnaryStrings(suppliers, evaluatorName("String", "in"), DataType.UNSIGNED_LONG, bytesRef -> null, bytesRef -> { + // BigDecimal, used to parse unsigned_longs will throw NFEs with different messages depending on empty string, first + // non-number character after a number-looking like prefix, or string starting with "e", maybe others -- safer to take + // this shortcut here. + Exception e = expectThrows(NumberFormatException.class, () -> new BigDecimal(bytesRef.utf8ToString())); + return List.of( + "Line 1:1: evaluation of [source] failed, treating result as null. Only first 20 failures recorded.", + "Line 1:1: java.lang.NumberFormatException: " + e.getMessage() + ); + }); // from doubles within unsigned_long's range TestCaseSupplier.forUnaryDouble( suppliers, - evaluatorName.apply("Double", "dbl"), + evaluatorName("Double", "dbl"), DataType.UNSIGNED_LONG, d -> BigDecimal.valueOf(d).toBigInteger(), // note: not: new BigDecimal(d).toBigInteger 0d, @@ -97,7 +89,7 @@ public static Iterable parameters() { // from doubles outside unsigned_long's range, negative TestCaseSupplier.forUnaryDouble( suppliers, - evaluatorName.apply("Double", "dbl"), + evaluatorName("Double", "dbl"), DataType.UNSIGNED_LONG, d -> null, Double.NEGATIVE_INFINITY, @@ -110,7 +102,7 @@ public static Iterable parameters() { // from doubles outside Long's range, positive TestCaseSupplier.forUnaryDouble( suppliers, - evaluatorName.apply("Double", "dbl"), + evaluatorName("Double", "dbl"), DataType.UNSIGNED_LONG, d -> null, UNSIGNED_LONG_MAX_AS_DOUBLE + 10e5, @@ -124,7 +116,7 @@ public static Iterable parameters() { // from long within unsigned_long's range TestCaseSupplier.forUnaryLong( suppliers, - evaluatorName.apply("Long", "lng"), + evaluatorName("Long", "lng"), DataType.UNSIGNED_LONG, BigInteger::valueOf, 0L, @@ -134,7 +126,7 @@ public static Iterable parameters() { // from long outside unsigned_long's range TestCaseSupplier.forUnaryLong( suppliers, - evaluatorName.apply("Long", "lng"), + evaluatorName("Long", "lng"), DataType.UNSIGNED_LONG, unused -> null, Long.MIN_VALUE, @@ -148,7 +140,7 @@ public static Iterable parameters() { // from int within unsigned_long's range TestCaseSupplier.forUnaryInt( suppliers, - evaluatorName.apply("Int", "i"), + evaluatorName("Int", "i"), DataType.UNSIGNED_LONG, BigInteger::valueOf, 0, @@ -158,7 +150,7 @@ public static Iterable parameters() { // from int outside unsigned_long's range TestCaseSupplier.forUnaryInt( suppliers, - evaluatorName.apply("Int", "i"), + evaluatorName("Int", "i"), DataType.UNSIGNED_LONG, unused -> null, Integer.MIN_VALUE, @@ -172,7 +164,7 @@ public static Iterable parameters() { // strings of random unsigned_longs TestCaseSupplier.unary( suppliers, - evaluatorName.apply("String", "in"), + evaluatorName("String", "in"), TestCaseSupplier.ulongCases(BigInteger.ZERO, UNSIGNED_LONG_MAX, true) .stream() .map( @@ -190,7 +182,7 @@ public static Iterable parameters() { // strings of random doubles within unsigned_long's range TestCaseSupplier.unary( suppliers, - evaluatorName.apply("String", "in"), + evaluatorName("String", "in"), TestCaseSupplier.doubleCases(0, UNSIGNED_LONG_MAX_AS_DOUBLE, true) .stream() .map( @@ -208,7 +200,7 @@ public static Iterable parameters() { // strings of random doubles outside unsigned_long's range, negative TestCaseSupplier.unary( suppliers, - evaluatorName.apply("String", "in"), + evaluatorName("String", "in"), TestCaseSupplier.doubleCases(Double.NEGATIVE_INFINITY, -1d, true) .stream() .map( @@ -231,7 +223,7 @@ public static Iterable parameters() { // strings of random doubles outside Integer's range, positive TestCaseSupplier.unary( suppliers, - evaluatorName.apply("String", "in"), + evaluatorName("String", "in"), TestCaseSupplier.doubleCases(UNSIGNED_LONG_MAX_AS_DOUBLE + 10e5, Double.POSITIVE_INFINITY, true) .stream() .map( @@ -255,6 +247,11 @@ public static Iterable parameters() { return parameterSuppliersFromTypedDataWithDefaultChecksNoErrors(true, suppliers); } + private static String evaluatorName(String inner, String next) { + String read = "Attribute[channel=0]"; + return "ToUnsignedLongFrom" + inner + "Evaluator[" + next + "=" + read + "]"; + } + @Override protected Expression build(Source source, List args) { return new ToUnsignedLong(source, args.get(0)); diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ToLowerTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ToLowerTests.java index a846ab7cb4aa7..838e862669177 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ToLowerTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ToLowerTests.java @@ -42,12 +42,12 @@ public ToLowerTests(@Name("TestCase") Supplier testCa public static Iterable parameters() { List suppliers = new ArrayList<>(); - suppliers.add(supplier("keyword ascii", DataType.KEYWORD, () -> randomAlphaOfLengthBetween(1, 10))); - suppliers.add(supplier("keyword unicode", DataType.KEYWORD, () -> randomUnicodeOfLengthBetween(1, 10))); - suppliers.add(supplier("text ascii", DataType.TEXT, () -> randomAlphaOfLengthBetween(1, 10))); - suppliers.add(supplier("text unicode", DataType.TEXT, () -> randomUnicodeOfLengthBetween(1, 10))); - suppliers.add(supplier("semantic_text ascii", DataType.SEMANTIC_TEXT, () -> randomAlphaOfLengthBetween(1, 10))); - suppliers.add(supplier("semantic_text unicode", DataType.SEMANTIC_TEXT, () -> randomUnicodeOfLengthBetween(1, 10))); + suppliers(suppliers, "keyword ascii", DataType.KEYWORD, () -> randomAlphaOfLengthBetween(1, 10)); + suppliers(suppliers, "keyword unicode", DataType.KEYWORD, () -> randomUnicodeOfLengthBetween(1, 10)); + suppliers(suppliers, "text ascii", DataType.TEXT, () -> randomAlphaOfLengthBetween(1, 10)); + suppliers(suppliers, "text unicode", DataType.TEXT, () -> randomUnicodeOfLengthBetween(1, 10)); + suppliers(suppliers, "semantic_text ascii", DataType.SEMANTIC_TEXT, () -> randomAlphaOfLengthBetween(1, 10)); + suppliers(suppliers, "semantic_text unicode", DataType.SEMANTIC_TEXT, () -> randomUnicodeOfLengthBetween(1, 10)); return parameterSuppliersFromTypedDataWithDefaultChecksNoErrors(true, suppliers); } @@ -80,8 +80,8 @@ protected Expression buildWithConfiguration(Source source, List args return new ToLower(source, args.get(0), configuration); } - private static TestCaseSupplier supplier(String name, DataType type, Supplier valueSupplier) { - return new TestCaseSupplier(name, List.of(type), () -> { + private static void suppliers(List suppliers, String name, DataType type, Supplier valueSupplier) { + suppliers.add(new TestCaseSupplier(name, List.of(type), () -> { List values = new ArrayList<>(); String expectedToString = "ChangeCaseEvaluator[val=Attribute[channel=0], locale=en_US, caseType=LOWER]"; @@ -90,6 +90,16 @@ private static TestCaseSupplier supplier(String name, DataType type, Supplier { + List values = new ArrayList<>(); + String expectedToString = "ChangeCaseEvaluator[val=Attribute[channel=0], locale=en_US, caseType=LOWER]"; + + List strings = randomList(2, 10, valueSupplier); + values.add(new TestCaseSupplier.TypedData(strings.stream().map(BytesRef::new).toList(), type, "0")); + + List expectedValue = strings.stream().map(s -> new BytesRef(s.toLowerCase(EsqlTestUtils.TEST_CFG.locale()))).toList(); + return new TestCaseSupplier.TestCase(values, expectedToString, type, equalTo(expectedValue)); + })); } } diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ToUpperTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ToUpperTests.java index da963f76b8dd6..a95b264831e31 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ToUpperTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ToUpperTests.java @@ -42,12 +42,12 @@ public ToUpperTests(@Name("TestCase") Supplier testCa public static Iterable parameters() { List suppliers = new ArrayList<>(); - suppliers.add(supplier("keyword ascii", DataType.KEYWORD, () -> randomAlphaOfLengthBetween(1, 10))); - suppliers.add(supplier("keyword unicode", DataType.KEYWORD, () -> randomUnicodeOfLengthBetween(1, 10))); - suppliers.add(supplier("text ascii", DataType.TEXT, () -> randomAlphaOfLengthBetween(1, 10))); - suppliers.add(supplier("text unicode", DataType.TEXT, () -> randomUnicodeOfLengthBetween(1, 10))); - suppliers.add(supplier("semantic_text ascii", DataType.SEMANTIC_TEXT, () -> randomAlphaOfLengthBetween(1, 10))); - suppliers.add(supplier("semantic_text unicode", DataType.SEMANTIC_TEXT, () -> randomUnicodeOfLengthBetween(1, 10))); + supplier(suppliers, "keyword ascii", DataType.KEYWORD, () -> randomAlphaOfLengthBetween(1, 10)); + supplier(suppliers, "keyword unicode", DataType.KEYWORD, () -> randomUnicodeOfLengthBetween(1, 10)); + supplier(suppliers, "text ascii", DataType.TEXT, () -> randomAlphaOfLengthBetween(1, 10)); + supplier(suppliers, "text unicode", DataType.TEXT, () -> randomUnicodeOfLengthBetween(1, 10)); + supplier(suppliers, "semantic_text ascii", DataType.SEMANTIC_TEXT, () -> randomAlphaOfLengthBetween(1, 10)); + supplier(suppliers, "semantic_text unicode", DataType.SEMANTIC_TEXT, () -> randomUnicodeOfLengthBetween(1, 10)); return parameterSuppliersFromTypedDataWithDefaultChecksNoErrors(true, suppliers); } @@ -80,8 +80,8 @@ protected Expression buildWithConfiguration(Source source, List args return new ToUpper(source, args.get(0), configuration); } - private static TestCaseSupplier supplier(String name, DataType type, Supplier valueSupplier) { - return new TestCaseSupplier(name, List.of(type), () -> { + private static void supplier(List suppliers, String name, DataType type, Supplier valueSupplier) { + suppliers.add(new TestCaseSupplier(name, List.of(type), () -> { List values = new ArrayList<>(); String expectedToString = "ChangeCaseEvaluator[val=Attribute[channel=0], locale=en_US, caseType=UPPER]"; @@ -90,6 +90,16 @@ private static TestCaseSupplier supplier(String name, DataType type, Supplier { + List values = new ArrayList<>(); + String expectedToString = "ChangeCaseEvaluator[val=Attribute[channel=0], locale=en_US, caseType=UPPER]"; + + List strings = randomList(2, 10, valueSupplier); + values.add(new TestCaseSupplier.TypedData(strings.stream().map(BytesRef::new).toList(), type, "0")); + + List expectedValue = strings.stream().map(s -> new BytesRef(s.toUpperCase(EsqlTestUtils.TEST_CFG.locale()))).toList(); + return new TestCaseSupplier.TestCase(values, expectedToString, type, equalTo(expectedValue)); + })); } }