diff --git a/docs/changelog/131775.yaml b/docs/changelog/131775.yaml new file mode 100644 index 0000000000000..029e768778e92 --- /dev/null +++ b/docs/changelog/131775.yaml @@ -0,0 +1,5 @@ +pr: 131775 +summary: Replace "representable" type error messages +area: ES|QL +type: enhancement +issues: [] diff --git a/docs/reference/query-languages/esql/_snippets/functions/types/count.md b/docs/reference/query-languages/esql/_snippets/functions/types/count.md index 4f0b8b174bc95..364b0c0353491 100644 --- a/docs/reference/query-languages/esql/_snippets/functions/types/count.md +++ b/docs/reference/query-languages/esql/_snippets/functions/types/count.md @@ -6,9 +6,12 @@ | --- | --- | | boolean | long | | cartesian_point | long | +| cartesian_shape | long | | date | long | +| date_nanos | long | | double | long | | geo_point | long | +| geo_shape | long | | integer | long | | ip | long | | keyword | long | diff --git a/docs/reference/query-languages/esql/_snippets/functions/types/count_over_time.md b/docs/reference/query-languages/esql/_snippets/functions/types/count_over_time.md index 4f0b8b174bc95..364b0c0353491 100644 --- a/docs/reference/query-languages/esql/_snippets/functions/types/count_over_time.md +++ b/docs/reference/query-languages/esql/_snippets/functions/types/count_over_time.md @@ -6,9 +6,12 @@ | --- | --- | | boolean | long | | cartesian_point | long | +| cartesian_shape | long | | date | long | +| date_nanos | long | | double | long | | geo_point | long | +| geo_shape | long | | integer | long | | ip | long | | keyword | long | diff --git a/docs/reference/query-languages/esql/_snippets/functions/types/sample.md b/docs/reference/query-languages/esql/_snippets/functions/types/sample.md index 0103811129d36..8e9d0114da060 100644 --- a/docs/reference/query-languages/esql/_snippets/functions/types/sample.md +++ b/docs/reference/query-languages/esql/_snippets/functions/types/sample.md @@ -17,5 +17,6 @@ | keyword | integer | keyword | | long | integer | long | | text | integer | keyword | +| unsigned_long | integer | unsigned_long | | version | integer | version | diff --git a/docs/reference/query-languages/esql/_snippets/functions/types/values.md b/docs/reference/query-languages/esql/_snippets/functions/types/values.md index 521d4300deed1..a287b900efccf 100644 --- a/docs/reference/query-languages/esql/_snippets/functions/types/values.md +++ b/docs/reference/query-languages/esql/_snippets/functions/types/values.md @@ -17,5 +17,6 @@ | keyword | keyword | | long | long | | text | keyword | +| unsigned_long | unsigned_long | | version | version | diff --git a/docs/reference/query-languages/esql/kibana/definition/functions/count.json b/docs/reference/query-languages/esql/kibana/definition/functions/count.json index dfaf5594d27a5..09da0ee7dbb28 100644 --- a/docs/reference/query-languages/esql/kibana/definition/functions/count.json +++ b/docs/reference/query-languages/esql/kibana/definition/functions/count.json @@ -28,6 +28,18 @@ "variadic" : false, "returnType" : "long" }, + { + "params" : [ + { + "name" : "field", + "type" : "cartesian_shape", + "optional" : true, + "description" : "Expression that outputs values to be counted. If omitted, equivalent to `COUNT(*)` (the number of rows)." + } + ], + "variadic" : false, + "returnType" : "long" + }, { "params" : [ { @@ -40,6 +52,18 @@ "variadic" : false, "returnType" : "long" }, + { + "params" : [ + { + "name" : "field", + "type" : "date_nanos", + "optional" : true, + "description" : "Expression that outputs values to be counted. If omitted, equivalent to `COUNT(*)` (the number of rows)." + } + ], + "variadic" : false, + "returnType" : "long" + }, { "params" : [ { @@ -64,6 +88,18 @@ "variadic" : false, "returnType" : "long" }, + { + "params" : [ + { + "name" : "field", + "type" : "geo_shape", + "optional" : true, + "description" : "Expression that outputs values to be counted. If omitted, equivalent to `COUNT(*)` (the number of rows)." + } + ], + "variadic" : false, + "returnType" : "long" + }, { "params" : [ { diff --git a/docs/reference/query-languages/esql/kibana/definition/functions/count_over_time.json b/docs/reference/query-languages/esql/kibana/definition/functions/count_over_time.json index 7097e91a74873..724bd3abd6b99 100644 --- a/docs/reference/query-languages/esql/kibana/definition/functions/count_over_time.json +++ b/docs/reference/query-languages/esql/kibana/definition/functions/count_over_time.json @@ -29,6 +29,18 @@ "variadic" : false, "returnType" : "long" }, + { + "params" : [ + { + "name" : "field", + "type" : "cartesian_shape", + "optional" : false, + "description" : "" + } + ], + "variadic" : false, + "returnType" : "long" + }, { "params" : [ { @@ -41,6 +53,18 @@ "variadic" : false, "returnType" : "long" }, + { + "params" : [ + { + "name" : "field", + "type" : "date_nanos", + "optional" : false, + "description" : "" + } + ], + "variadic" : false, + "returnType" : "long" + }, { "params" : [ { @@ -65,6 +89,18 @@ "variadic" : false, "returnType" : "long" }, + { + "params" : [ + { + "name" : "field", + "type" : "geo_shape", + "optional" : false, + "description" : "" + } + ], + "variadic" : false, + "returnType" : "long" + }, { "params" : [ { diff --git a/docs/reference/query-languages/esql/kibana/definition/functions/sample.json b/docs/reference/query-languages/esql/kibana/definition/functions/sample.json index 88ac36e71c430..65af7520c2d96 100644 --- a/docs/reference/query-languages/esql/kibana/definition/functions/sample.json +++ b/docs/reference/query-languages/esql/kibana/definition/functions/sample.json @@ -238,6 +238,24 @@ "variadic" : false, "returnType" : "keyword" }, + { + "params" : [ + { + "name" : "field", + "type" : "unsigned_long", + "optional" : false, + "description" : "The field to collect sample values for." + }, + { + "name" : "limit", + "type" : "integer", + "optional" : false, + "description" : "The maximum number of values to collect." + } + ], + "variadic" : false, + "returnType" : "unsigned_long" + }, { "params" : [ { diff --git a/docs/reference/query-languages/esql/kibana/definition/functions/values.json b/docs/reference/query-languages/esql/kibana/definition/functions/values.json index b88e303a8d533..8212384822c11 100644 --- a/docs/reference/query-languages/esql/kibana/definition/functions/values.json +++ b/docs/reference/query-languages/esql/kibana/definition/functions/values.json @@ -160,6 +160,18 @@ "variadic" : false, "returnType" : "keyword" }, + { + "params" : [ + { + "name" : "field", + "type" : "unsigned_long", + "optional" : false, + "description" : "" + } + ], + "variadic" : false, + "returnType" : "unsigned_long" + }, { "params" : [ { diff --git a/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/expression/TypeResolutions.java b/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/expression/TypeResolutions.java index f6e081771cbf5..814adfde50e74 100644 --- a/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/expression/TypeResolutions.java +++ b/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/expression/TypeResolutions.java @@ -22,6 +22,7 @@ import static org.elasticsearch.xpack.esql.core.type.DataType.DATETIME; import static org.elasticsearch.xpack.esql.core.type.DataType.IP; import static org.elasticsearch.xpack.esql.core.type.DataType.NULL; +import static org.elasticsearch.xpack.esql.core.type.DataType.isSpatial; public final class TypeResolutions { @@ -71,6 +72,23 @@ public static TypeResolution isDate(Expression e, String operationName, ParamOrd return isType(e, dt -> dt == DATETIME, operationName, paramOrd, "datetime"); } + /** + * @see DataType#isRepresentable(DataType) + */ + public static TypeResolution isRepresentableExceptCounters(Expression e, String operationName, ParamOrdinal paramOrd) { + return isType(e, DataType::isRepresentable, operationName, paramOrd, "any type except counter types"); + } + + public static TypeResolution isRepresentableExceptCountersAndSpatial(Expression e, String operationName, ParamOrdinal paramOrd) { + return isType( + e, + (t) -> isSpatial(t) == false && DataType.isRepresentable(t), + operationName, + paramOrd, + "any type except counter and spatial types" + ); + } + public static TypeResolution isExact(Expression e, String message) { if (e instanceof FieldAttribute fa) { EsField.Exact exact = fa.getExactInfo(); diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/stats_sample.csv-spec b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/stats_sample.csv-spec index d464a57319592..caf429531f530 100644 --- a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/stats_sample.csv-spec +++ b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/stats_sample.csv-spec @@ -64,6 +64,17 @@ sample_boolean:boolean | sample_datetime:datetime [false, true, true] | [1985-11-21T00:00:00.000Z, 1986-06-26T00:00:00.000Z, 1986-08-28T00:00:00.000Z] | [1.83, 2.03, 2.08] | [10001, 10002, 10003] | [Bezalel, Georgi, Parto] | [2, 4, 5] ; +sample unsigned_long +required_capability: agg_values_sample_unsigned_long + +FROM ul_logs +| STATS v = MV_SLICE(MV_SORT(SAMPLE(bytes_in, 200)), 3, 5) +; + +v:ul +[0, 74330435873664882, 154551962150890561] +; + multivalued required_capability: agg_sample diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/unsigned_long.csv-spec b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/unsigned_long.csv-spec index fbddb3d0e6989..04dc203e00d6f 100644 --- a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/unsigned_long.csv-spec +++ b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/unsigned_long.csv-spec @@ -194,3 +194,14 @@ s:double | bytes_in:ul | bytes_out:ul 1.0 | 1957665857956635540 | 352442273299370793 1.0 | 2408213296071189837 | 419872666232023984 ; + +values +required_capability: agg_values_sample_unsigned_long +from ul_logs | stats v = values(bytes_in) | MV_EXPAND v | SORT v | LIMIT 3; + +v:ul +0 +74330435873664882 +154551962150890561 +; + 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 1641afd6307ab..8aa224f6fc875 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 @@ -97,6 +97,11 @@ public enum Cap { */ AGG_MAX_MIN_UNSIGNED_LONG, + /** + * Accept unsigned longs on VALUES and SAMPLE aggregations. + */ + AGG_VALUES_SAMPLE_UNSIGNED_LONG, + /** * Does ESQL support async queries. */ diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/Count.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/Count.java index aef221ab6a7e9..f5d58296a7a74 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/Count.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/Count.java @@ -77,9 +77,12 @@ public Count( "aggregate_metric_double", "boolean", "cartesian_point", + "cartesian_shape", "date", + "date_nanos", "double", "geo_point", + "geo_shape", "integer", "ip", "keyword", diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/CountOverTime.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/CountOverTime.java index 9defb19d13ab3..6da92bc2ec3b5 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/CountOverTime.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/CountOverTime.java @@ -52,9 +52,12 @@ public CountOverTime( "aggregate_metric_double", "boolean", "cartesian_point", + "cartesian_shape", "date", + "date_nanos", "double", "geo_point", + "geo_shape", "integer", "ip", "keyword", diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/Sample.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/Sample.java index 9030824dd2f01..a988fb16be6a5 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/Sample.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/Sample.java @@ -40,6 +40,7 @@ import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.ParamOrdinal.FIRST; import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.ParamOrdinal.SECOND; import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isNotNull; +import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isRepresentableExceptCounters; import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isType; import static org.elasticsearch.xpack.esql.expression.function.FunctionUtils.TypeResolutionValidator.forPostOptimizationValidation; import static org.elasticsearch.xpack.esql.expression.function.FunctionUtils.TypeResolutionValidator.forPreOptimizationValidation; @@ -62,6 +63,7 @@ public class Sample extends AggregateFunction implements ToAggregator, PostOptim "ip", "keyword", "long", + "unsigned_long", "version" }, description = "Collects sample values for a field.", type = FunctionType.AGGREGATE, @@ -86,6 +88,7 @@ public Sample( "ip", "keyword", "long", + "unsigned_long", "text", "version" }, description = "The field to collect sample values for." @@ -117,9 +120,8 @@ protected TypeResolution resolveType() { if (childrenResolved() == false) { return new TypeResolution("Unresolved children"); } - var typeResolution = isType(field(), dt -> dt != DataType.UNSIGNED_LONG, sourceText(), FIRST, "any type except unsigned_long").and( - isNotNull(limitField(), sourceText(), SECOND) - ).and(isType(limitField(), dt -> dt == DataType.INTEGER, sourceText(), SECOND, "integer")); + var typeResolution = isRepresentableExceptCounters(field(), sourceText(), FIRST).and(isNotNull(limitField(), sourceText(), SECOND)) + .and(isType(limitField(), dt -> dt == DataType.INTEGER, sourceText(), SECOND, "integer")); if (typeResolution.unresolved()) { return typeResolution; } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/Values.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/Values.java index 5eaab1fc77514..cbf82bb005974 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/Values.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/Values.java @@ -44,6 +44,7 @@ public class Values extends AggregateFunction implements ToAggregator { private static final Map> SUPPLIERS = Map.ofEntries( Map.entry(DataType.INTEGER, ValuesIntAggregatorFunctionSupplier::new), Map.entry(DataType.LONG, ValuesLongAggregatorFunctionSupplier::new), + Map.entry(DataType.UNSIGNED_LONG, ValuesLongAggregatorFunctionSupplier::new), Map.entry(DataType.DATETIME, ValuesLongAggregatorFunctionSupplier::new), Map.entry(DataType.DATE_NANOS, ValuesLongAggregatorFunctionSupplier::new), Map.entry(DataType.DOUBLE, ValuesDoubleAggregatorFunctionSupplier::new), @@ -72,6 +73,7 @@ public class Values extends AggregateFunction implements ToAggregator { "ip", "keyword", "long", + "unsigned_long", "version" }, preview = true, appliesTo = { @FunctionAppliesTo(lifeCycle = FunctionAppliesToLifecycle.PREVIEW) }, @@ -111,6 +113,7 @@ public Values( "ip", "keyword", "long", + "unsigned_long", "text", "version" } ) Expression v @@ -153,7 +156,7 @@ public DataType dataType() { @Override protected TypeResolution resolveType() { - return TypeResolutions.isType(field(), SUPPLIERS::containsKey, sourceText(), DEFAULT, "any type except unsigned_long"); + return TypeResolutions.isRepresentableExceptCounters(field(), sourceText(), DEFAULT); } @Override diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvAppend.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvAppend.java index c60fb07704e2e..d2043c6530503 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvAppend.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvAppend.java @@ -39,6 +39,7 @@ import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.ParamOrdinal.FIRST; import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.ParamOrdinal.SECOND; +import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isRepresentableExceptCounters; import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isType; /** @@ -137,14 +138,14 @@ protected TypeResolution resolveType() { return new TypeResolution("Unresolved children"); } - TypeResolution resolution = isType(field1, DataType::isRepresentable, sourceText(), FIRST, "representable"); + TypeResolution resolution = isRepresentableExceptCounters(field1, sourceText(), FIRST); if (resolution.unresolved()) { return resolution; } dataType = field1.dataType().noText(); if (dataType == DataType.NULL) { dataType = field2.dataType().noText(); - return isType(field2, DataType::isRepresentable, sourceText(), SECOND, "representable"); + return isRepresentableExceptCounters(field2, sourceText(), SECOND); } return isType(field2, t -> t.noText() == dataType, sourceText(), SECOND, dataType.typeName()); } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvCount.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvCount.java index f36d121ef104f..57edb9a66ec74 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvCount.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvCount.java @@ -24,7 +24,8 @@ import java.io.IOException; import java.util.List; -import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isType; +import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.ParamOrdinal.DEFAULT; +import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isRepresentableExceptCounters; /** * Reduce a multivalued field to a single valued field containing the count of values. @@ -74,7 +75,7 @@ public String getWriteableName() { @Override protected TypeResolution resolveFieldType() { - return isType(field(), DataType::isRepresentable, sourceText(), null, "representable"); + return isRepresentableExceptCounters(field(), sourceText(), DEFAULT); } @Override diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvDedupe.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvDedupe.java index 2826335403075..228395a60aad0 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvDedupe.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvDedupe.java @@ -14,7 +14,6 @@ import org.elasticsearch.xpack.esql.core.expression.Expression; 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; @@ -23,7 +22,8 @@ import java.io.IOException; import java.util.List; -import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isType; +import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.ParamOrdinal.DEFAULT; +import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isRepresentableExceptCounters; /** * Removes duplicate values from a multivalued field. @@ -89,7 +89,7 @@ public String getWriteableName() { @Override protected TypeResolution resolveFieldType() { - return isType(field(), DataType::isRepresentable, sourceText(), null, "representable"); + return isRepresentableExceptCounters(field(), sourceText(), DEFAULT); } @Override diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvFirst.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvFirst.java index a741fbd1175b3..2325c3c10b47f 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvFirst.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvFirst.java @@ -22,7 +22,6 @@ import org.elasticsearch.xpack.esql.core.expression.Expression; 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; @@ -31,7 +30,8 @@ import java.io.IOException; import java.util.List; -import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isType; +import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.ParamOrdinal.DEFAULT; +import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isRepresentableExceptCounters; /** * Reduce a multivalued field to a single valued field containing the minimum value. @@ -104,7 +104,7 @@ public String getWriteableName() { @Override protected TypeResolution resolveFieldType() { - return isType(field(), DataType::isRepresentable, sourceText(), null, "representable"); + return isRepresentableExceptCounters(field(), sourceText(), DEFAULT); } @Override diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvLast.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvLast.java index e65d7fca2221b..610f76eb6f65b 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvLast.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvLast.java @@ -22,7 +22,6 @@ import org.elasticsearch.xpack.esql.core.expression.Expression; 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; @@ -31,7 +30,8 @@ import java.io.IOException; import java.util.List; -import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isType; +import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.ParamOrdinal.DEFAULT; +import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isRepresentableExceptCounters; /** * Reduce a multivalued field to a single valued field containing the minimum value. @@ -104,7 +104,7 @@ public String getWriteableName() { @Override protected TypeResolution resolveFieldType() { - return isType(field(), DataType::isRepresentable, sourceText(), null, "representable"); + return isRepresentableExceptCounters(field(), sourceText(), DEFAULT); } @Override diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMax.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMax.java index 49ff7c88c636b..fc8281188c067 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMax.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMax.java @@ -25,9 +25,8 @@ import java.io.IOException; import java.util.List; -import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isType; -import static org.elasticsearch.xpack.esql.core.type.DataType.isRepresentable; -import static org.elasticsearch.xpack.esql.core.type.DataType.isSpatial; +import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.ParamOrdinal.DEFAULT; +import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isRepresentableExceptCountersAndSpatial; /** * Reduce a multivalued field to a single valued field containing the maximum value. @@ -69,18 +68,7 @@ public String getWriteableName() { @Override protected TypeResolution resolveFieldType() { - return isType( - field(), - t -> isSpatial(t) == false && isRepresentable(t), - sourceText(), - null, - "boolean", - "date", - "ip", - "string", - "version", - "numeric except counter types" - ); + return isRepresentableExceptCountersAndSpatial(field(), sourceText(), DEFAULT); } @Override diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMin.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMin.java index 27831e2a660ec..36dac6658aa5a 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMin.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMin.java @@ -25,9 +25,8 @@ import java.io.IOException; import java.util.List; -import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isType; -import static org.elasticsearch.xpack.esql.core.type.DataType.isRepresentable; -import static org.elasticsearch.xpack.esql.core.type.DataType.isSpatial; +import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.ParamOrdinal.DEFAULT; +import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isRepresentableExceptCountersAndSpatial; /** * Reduce a multivalued field to a single valued field containing the minimum value. @@ -69,18 +68,7 @@ public String getWriteableName() { @Override protected TypeResolution resolveFieldType() { - return isType( - field(), - t -> isSpatial(t) == false && isRepresentable(t), - sourceText(), - null, - "boolean", - "date", - "ip", - "string", - "version", - "numeric except counter types" - ); + return isRepresentableExceptCountersAndSpatial(field(), sourceText(), DEFAULT); } @Override diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSlice.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSlice.java index c50cb941c97f6..8e33d759ea9e8 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSlice.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSlice.java @@ -41,7 +41,7 @@ import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.ParamOrdinal.FIRST; import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.ParamOrdinal.SECOND; import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.ParamOrdinal.THIRD; -import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isType; +import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isRepresentableExceptCounters; import static org.elasticsearch.xpack.esql.core.type.DataType.INTEGER; import static org.elasticsearch.xpack.esql.type.EsqlDataTypeConverter.stringToInt; @@ -161,7 +161,7 @@ protected TypeResolution resolveType() { return new TypeResolution("Unresolved children"); } - TypeResolution resolution = isType(field, DataType::isRepresentable, sourceText(), FIRST, "representable"); + TypeResolution resolution = isRepresentableExceptCounters(field, sourceText(), FIRST); if (resolution.unresolved()) { return resolution; } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSort.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSort.java index 4653b5f74dc40..b3c6110dc2b83 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSort.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSort.java @@ -53,8 +53,8 @@ import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.ParamOrdinal.FIRST; import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.ParamOrdinal.SECOND; +import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isRepresentableExceptCounters; import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isString; -import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isType; import static org.elasticsearch.xpack.esql.expression.Validations.isFoldable; /** @@ -128,7 +128,7 @@ protected TypeResolution resolveType() { return new TypeResolution("Unresolved children"); } - TypeResolution resolution = isType(field, DataType::isRepresentable, sourceText(), FIRST, "representable"); + TypeResolution resolution = isRepresentableExceptCounters(field, sourceText(), FIRST); if (resolution.unresolved()) { return resolution; diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/MultiRowTestCaseSupplier.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/MultiRowTestCaseSupplier.java index bb0d2e57c3440..ed32fcd480fd5 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/MultiRowTestCaseSupplier.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/MultiRowTestCaseSupplier.java @@ -36,6 +36,14 @@ public final class MultiRowTestCaseSupplier { private MultiRowTestCaseSupplier() {} + public static List nullCases(int minRows, int maxRows) { + List cases = new ArrayList<>(); + + addSuppliers(cases, minRows, maxRows, "null", DataType.NULL, () -> null); + + return cases; + } + public static List intCases(int minRows, int maxRows, int min, int max, boolean includeZero) { List cases = new ArrayList<>(); diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/aggregate/CountErrorTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/aggregate/CountErrorTests.java new file mode 100644 index 0000000000000..e634994572680 --- /dev/null +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/aggregate/CountErrorTests.java @@ -0,0 +1,43 @@ +/* + * 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.aggregate; + +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 CountErrorTests extends ErrorsForCasesWithoutExamplesTestCase { + @Override + protected List cases() { + return paramsToSuppliers(CountTests.parameters()); + } + + @Override + protected Expression build(Source source, List args) { + return new Count(source, args.get(0)); + } + + @Override + protected Matcher expectedTypeErrorMatcher(List> validPerPosition, List signature) { + assert false : "All checked types must work"; + return null; + } + + @Override + protected void assertNumberOfCheckedSignatures(int checked) { + assertThat(checked, equalTo(0)); + } +} diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/aggregate/CountTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/aggregate/CountTests.java index 6ab1d96c929a5..863a8cc5f255b 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/aggregate/CountTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/aggregate/CountTests.java @@ -21,6 +21,7 @@ import java.math.BigInteger; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -37,26 +38,31 @@ public static Iterable parameters() { var suppliers = new ArrayList(); Stream.of( + MultiRowTestCaseSupplier.nullCases(1, 1000), MultiRowTestCaseSupplier.intCases(1, 1000, Integer.MIN_VALUE, Integer.MAX_VALUE, true), MultiRowTestCaseSupplier.longCases(1, 1000, Long.MIN_VALUE, Long.MAX_VALUE, true), MultiRowTestCaseSupplier.ulongCases(1, 1000, BigInteger.ZERO, UNSIGNED_LONG_MAX, true), MultiRowTestCaseSupplier.doubleCases(1, 1000, -Double.MAX_VALUE, Double.MAX_VALUE, true), MultiRowTestCaseSupplier.dateCases(1, 1000), + MultiRowTestCaseSupplier.dateNanosCases(1, 1000), MultiRowTestCaseSupplier.booleanCases(1, 1000), MultiRowTestCaseSupplier.ipCases(1, 1000), MultiRowTestCaseSupplier.versionCases(1, 1000), MultiRowTestCaseSupplier.geoPointCases(1, 1000, IncludingAltitude.YES), - MultiRowTestCaseSupplier.cartesianPointCases(1, 1000, IncludingAltitude.YES), + MultiRowTestCaseSupplier.geoShapeCasesWithoutCircle(1, 1000, IncludingAltitude.YES), + MultiRowTestCaseSupplier.cartesianShapeCasesWithoutCircle(1, 1000, IncludingAltitude.YES), MultiRowTestCaseSupplier.stringCases(1, 1000, DataType.KEYWORD), MultiRowTestCaseSupplier.stringCases(1, 1000, DataType.TEXT) ).flatMap(List::stream).map(CountTests::makeSupplier).collect(Collectors.toCollection(() -> suppliers)); // No rows for (var dataType : List.of( + DataType.NULL, DataType.INTEGER, DataType.LONG, DataType.DOUBLE, DataType.DATETIME, + DataType.DATE_NANOS, DataType.BOOLEAN, DataType.IP, DataType.VERSION, @@ -92,13 +98,13 @@ protected Expression build(Source source, List args) { private static TestCaseSupplier makeSupplier(TestCaseSupplier.TypedDataSupplier fieldSupplier) { return new TestCaseSupplier(fieldSupplier.name(), List.of(fieldSupplier.type()), () -> { var fieldTypedData = fieldSupplier.get(); - var rowCount = fieldTypedData.multiRowData().size(); + var rowCount = fieldTypedData.multiRowData().stream().filter(Objects::nonNull).count(); return new TestCaseSupplier.TestCase( List.of(fieldTypedData), "Count[field=Attribute[channel=0]]", DataType.LONG, - equalTo((long) rowCount) + equalTo(rowCount) ); }); } diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/aggregate/SampleErrorTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/aggregate/SampleErrorTests.java new file mode 100644 index 0000000000000..a343faa50f1ad --- /dev/null +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/aggregate/SampleErrorTests.java @@ -0,0 +1,47 @@ +/* + * 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.aggregate; + +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 SampleErrorTests extends ErrorsForCasesWithoutExamplesTestCase { + @Override + protected List cases() { + return paramsToSuppliers(SampleTests.parameters()); + } + + @Override + protected Expression build(Source source, List args) { + return new Sample(source, args.get(0), args.get(1)); + } + + @Override + protected Matcher expectedTypeErrorMatcher(List> validPerPosition, List signature) { + if (signature.get(1).equals(DataType.NULL)) { + return equalTo("second argument of [" + sourceForSignature(signature) + "] cannot be null, received []"); + } + return equalTo( + typeErrorMessage( + true, + validPerPosition, + signature, + (v, p) -> p == 1 ? "integer" : "boolean, date, ip, string, version, aggregate_metric_double or numeric except counter types" + ) + ); + } +} diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/aggregate/SampleTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/aggregate/SampleTests.java index 8249532d5bcc9..8b66da40ddb27 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/aggregate/SampleTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/aggregate/SampleTests.java @@ -21,9 +21,11 @@ import org.hamcrest.Description; import org.hamcrest.Matcher; +import java.math.BigInteger; import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.Objects; import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -42,8 +44,10 @@ public static Iterable parameters() { for (var limitCaseSupplier : TestCaseSupplier.intCases(1, 100, false)) { Stream.of( + MultiRowTestCaseSupplier.nullCases(1, 1000), MultiRowTestCaseSupplier.intCases(1, 1000, Integer.MIN_VALUE, Integer.MAX_VALUE, true), MultiRowTestCaseSupplier.longCases(1, 1000, Long.MIN_VALUE, Long.MAX_VALUE, true), + MultiRowTestCaseSupplier.ulongCases(1, 1000, BigInteger.ZERO, UNSIGNED_LONG_MAX, true), MultiRowTestCaseSupplier.doubleCases(1, 1000, -Double.MAX_VALUE, Double.MAX_VALUE, true), MultiRowTestCaseSupplier.dateCases(1, 1000), MultiRowTestCaseSupplier.dateNanosCases(1, 1000), @@ -78,7 +82,7 @@ private static TestCaseSupplier makeSupplier( var limitTypedData = limitCaseSupplier.get().forceLiteral(); var limit = (int) limitTypedData.getValue(); - var rows = fieldTypedData.multiRowData(); + var rows = fieldTypedData.multiRowData().stream().filter(Objects::nonNull).toList(); return new TestCaseSupplier.TestCase( List.of(fieldTypedData, limitTypedData), diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/aggregate/ValuesErrorTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/aggregate/ValuesErrorTests.java index d34fe1df29827..1467aa129b467 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/aggregate/ValuesErrorTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/aggregate/ValuesErrorTests.java @@ -32,6 +32,12 @@ protected Expression build(Source source, List args) { @Override protected Matcher expectedTypeErrorMatcher(List> validPerPosition, List signature) { - return equalTo(typeErrorMessage(false, validPerPosition, signature, (v, p) -> "any type except unsigned_long")); + assert false : "All checked types must work"; + return null; + } + + @Override + protected void assertNumberOfCheckedSignatures(int checked) { + assertThat(checked, equalTo(0)); } } diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/aggregate/ValuesTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/aggregate/ValuesTests.java index 11c7476eb4b03..f386f623e17dc 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/aggregate/ValuesTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/aggregate/ValuesTests.java @@ -20,6 +20,7 @@ import org.hamcrest.Description; import org.hamcrest.Matcher; +import java.math.BigInteger; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; @@ -44,6 +45,7 @@ public static Iterable parameters() { Stream.of( MultiRowTestCaseSupplier.intCases(1, 1000, Integer.MIN_VALUE, Integer.MAX_VALUE, true), MultiRowTestCaseSupplier.longCases(1, 1000, Long.MIN_VALUE, Long.MAX_VALUE, true), + MultiRowTestCaseSupplier.ulongCases(1, 1000, BigInteger.ZERO, UNSIGNED_LONG_MAX, true), MultiRowTestCaseSupplier.doubleCases(1, 1000, -Double.MAX_VALUE, Double.MAX_VALUE, true), MultiRowTestCaseSupplier.dateCases(1, 1000), MultiRowTestCaseSupplier.dateNanosCases(1, 1000), diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxErrorTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxErrorTests.java index 7d635917fe154..6eee825de85fe 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxErrorTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxErrorTests.java @@ -32,13 +32,6 @@ protected Expression build(Source source, List args) { @Override protected Matcher expectedTypeErrorMatcher(List> validPerPosition, List signature) { - return equalTo( - typeErrorMessage( - false, - validPerPosition, - signature, - (v, p) -> "boolean, date, ip, string, version or numeric except counter types" - ) - ); + return equalTo(typeErrorMessage(false, validPerPosition, signature, (v, p) -> "any type except counter and spatial types")); } } diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinErrorTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinErrorTests.java index 405d0822bb72f..b9d97d7bb4cb8 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinErrorTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinErrorTests.java @@ -32,13 +32,6 @@ protected Expression build(Source source, List args) { @Override protected Matcher expectedTypeErrorMatcher(List> validPerPosition, List signature) { - return equalTo( - typeErrorMessage( - false, - validPerPosition, - signature, - (v, p) -> "boolean, date, ip, string, version or numeric except counter types" - ) - ); + return equalTo(typeErrorMessage(false, validPerPosition, signature, (v, p) -> "any type except counter and spatial types")); } }