Browse Source

ESQL: Accept unsigned longs on MAX and MIN aggregations (#131694)

Solved https://github.com/elastic/elasticsearch/issues/112006, but I'll change other uses of "representable" in another PR, so keeping it open.

- Fix aggregation tests to accept unsigned longs (They weren't correctly handling BigIntegers)
- Added unsigned long support for Max and Min (No new logics, reusing the existing Long aggregators)
- Test and docs
Iván Cea Fontenla 2 months ago
parent
commit
2c0e66ef86
29 changed files with 282 additions and 29 deletions
  1. 5 0
      docs/changelog/131694.yaml
  2. 1 0
      docs/reference/query-languages/esql/_snippets/functions/types/max.md
  3. 1 0
      docs/reference/query-languages/esql/_snippets/functions/types/max_over_time.md
  4. 1 0
      docs/reference/query-languages/esql/_snippets/functions/types/min.md
  5. 1 0
      docs/reference/query-languages/esql/_snippets/functions/types/min_over_time.md
  6. 12 0
      docs/reference/query-languages/esql/kibana/definition/functions/max.json
  7. 12 0
      docs/reference/query-languages/esql/kibana/definition/functions/max_over_time.json
  8. 12 0
      docs/reference/query-languages/esql/kibana/definition/functions/min.json
  9. 12 0
      docs/reference/query-languages/esql/kibana/definition/functions/min_over_time.json
  10. 23 0
      x-pack/plugin/esql/qa/testFixtures/src/main/resources/stats.csv-spec
  11. 5 0
      x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java
  12. 10 3
      x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/Max.java
  13. 2 2
      x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/MaxOverTime.java
  14. 10 3
      x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/Min.java
  15. 2 2
      x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/MinOverTime.java
  16. 12 1
      x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMax.java
  17. 12 1
      x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMin.java
  18. 1 5
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/analysis/AnalyzerTests.java
  19. 2 2
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/analysis/VerifierTests.java
  20. 2 3
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractAggregationTestCase.java
  21. 6 3
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/TestCaseSupplier.java
  22. 6 1
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/aggregate/MaxErrorTests.java
  23. 44 0
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/aggregate/MaxOverTimeErrorTests.java
  24. 11 0
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/aggregate/MaxTests.java
  25. 6 1
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/aggregate/MinErrorTests.java
  26. 44 0
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/aggregate/MinOverTimeErrorTests.java
  27. 11 0
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/aggregate/MinTests.java
  28. 8 1
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxErrorTests.java
  29. 8 1
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinErrorTests.java

+ 5 - 0
docs/changelog/131694.yaml

@@ -0,0 +1,5 @@
+pr: 131694
+summary: Accept unsigned longs on MAX and MIN aggregations
+area: ES|QL
+type: enhancement
+issues: []

+ 1 - 0
docs/reference/query-languages/esql/_snippets/functions/types/max.md

@@ -13,5 +13,6 @@
 | keyword | keyword |
 | long | long |
 | text | keyword |
+| unsigned_long | unsigned_long |
 | version | version |
 

+ 1 - 0
docs/reference/query-languages/esql/_snippets/functions/types/max_over_time.md

@@ -13,5 +13,6 @@
 | keyword | keyword |
 | long | long |
 | text | keyword |
+| unsigned_long | unsigned_long |
 | version | version |
 

+ 1 - 0
docs/reference/query-languages/esql/_snippets/functions/types/min.md

@@ -13,5 +13,6 @@
 | keyword | keyword |
 | long | long |
 | text | keyword |
+| unsigned_long | unsigned_long |
 | version | version |
 

+ 1 - 0
docs/reference/query-languages/esql/_snippets/functions/types/min_over_time.md

@@ -13,5 +13,6 @@
 | keyword | keyword |
 | long | long |
 | text | keyword |
+| unsigned_long | unsigned_long |
 | version | version |
 

+ 12 - 0
docs/reference/query-languages/esql/kibana/definition/functions/max.json

@@ -112,6 +112,18 @@
       "variadic" : false,
       "returnType" : "keyword"
     },
+    {
+      "params" : [
+        {
+          "name" : "field",
+          "type" : "unsigned_long",
+          "optional" : false,
+          "description" : ""
+        }
+      ],
+      "variadic" : false,
+      "returnType" : "unsigned_long"
+    },
     {
       "params" : [
         {

+ 12 - 0
docs/reference/query-languages/esql/kibana/definition/functions/max_over_time.json

@@ -113,6 +113,18 @@
       "variadic" : false,
       "returnType" : "keyword"
     },
+    {
+      "params" : [
+        {
+          "name" : "field",
+          "type" : "unsigned_long",
+          "optional" : false,
+          "description" : ""
+        }
+      ],
+      "variadic" : false,
+      "returnType" : "unsigned_long"
+    },
     {
       "params" : [
         {

+ 12 - 0
docs/reference/query-languages/esql/kibana/definition/functions/min.json

@@ -112,6 +112,18 @@
       "variadic" : false,
       "returnType" : "keyword"
     },
+    {
+      "params" : [
+        {
+          "name" : "field",
+          "type" : "unsigned_long",
+          "optional" : false,
+          "description" : ""
+        }
+      ],
+      "variadic" : false,
+      "returnType" : "unsigned_long"
+    },
     {
       "params" : [
         {

+ 12 - 0
docs/reference/query-languages/esql/kibana/definition/functions/min_over_time.json

@@ -113,6 +113,18 @@
       "variadic" : false,
       "returnType" : "keyword"
     },
+    {
+      "params" : [
+        {
+          "name" : "field",
+          "type" : "unsigned_long",
+          "optional" : false,
+          "description" : ""
+        }
+      ],
+      "variadic" : false,
+      "returnType" : "unsigned_long"
+    },
     {
       "params" : [
         {

+ 23 - 0
x-pack/plugin/esql/qa/testFixtures/src/main/resources/stats.csv-spec

@@ -5,6 +5,14 @@ l:long
 5
 ;
 
+maxOfUnsignedLong
+required_capability: agg_max_min_unsigned_long
+from ul_logs | stats ul = max(bytes_in);
+
+ul:ul
+18446744073709551615
+;
+
 maxOfInteger
 // tag::max[]
 FROM employees
@@ -18,6 +26,21 @@ MAX(languages):integer
 // end::max-result[]
 ;
 
+minOfLong
+from employees | stats l = min(languages.long);
+
+l:long
+1
+;
+
+minOfUnsignedLong
+required_capability: agg_max_min_unsigned_long
+from ul_logs | stats ul = min(bytes_in);
+
+ul:ul
+0
+;
+
 minOfInteger
 // tag::min[]
 FROM employees

+ 5 - 0
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java

@@ -92,6 +92,11 @@ public class EsqlCapabilities {
          */
         AGG_VALUES_SPATIAL,
 
+        /**
+         * Accept unsigned longs on MAX and MIN aggregations.
+         */
+        AGG_MAX_MIN_UNSIGNED_LONG,
+
         /**
          * Does ESQL support async queries.
          */

+ 10 - 3
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/Max.java

@@ -47,6 +47,7 @@ public class Max extends AggregateFunction implements ToAggregator, SurrogateExp
     private static final Map<DataType, Supplier<AggregatorFunctionSupplier>> SUPPLIERS = Map.ofEntries(
         Map.entry(DataType.BOOLEAN, MaxBooleanAggregatorFunctionSupplier::new),
         Map.entry(DataType.LONG, MaxLongAggregatorFunctionSupplier::new),
+        Map.entry(DataType.UNSIGNED_LONG, MaxLongAggregatorFunctionSupplier::new),
         Map.entry(DataType.DATETIME, MaxLongAggregatorFunctionSupplier::new),
         Map.entry(DataType.DATE_NANOS, MaxLongAggregatorFunctionSupplier::new),
         Map.entry(DataType.INTEGER, MaxIntAggregatorFunctionSupplier::new),
@@ -58,7 +59,7 @@ public class Max extends AggregateFunction implements ToAggregator, SurrogateExp
     );
 
     @FunctionInfo(
-        returnType = { "boolean", "double", "integer", "long", "date", "date_nanos", "ip", "keyword", "long", "version" },
+        returnType = { "boolean", "double", "integer", "long", "date", "date_nanos", "ip", "keyword", "unsigned_long", "version" },
         description = "The maximum value of a field.",
         type = FunctionType.AGGREGATE,
         examples = {
@@ -86,7 +87,7 @@ public class Max extends AggregateFunction implements ToAggregator, SurrogateExp
                 "ip",
                 "keyword",
                 "text",
-                "long",
+                "unsigned_long",
                 "version" }
         ) Expression field
     ) {
@@ -128,7 +129,13 @@ public class Max extends AggregateFunction implements ToAggregator, SurrogateExp
             dt -> SUPPLIERS.containsKey(dt) || dt == DataType.AGGREGATE_METRIC_DOUBLE,
             sourceText(),
             DEFAULT,
-            "representable except unsigned_long and spatial types"
+            "boolean",
+            "date",
+            "ip",
+            "string",
+            "version",
+            "aggregate_metric_double",
+            "numeric except counter types"
         );
     }
 

+ 2 - 2
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/MaxOverTime.java

@@ -37,7 +37,7 @@ public class MaxOverTime extends TimeSeriesAggregateFunction {
     );
 
     @FunctionInfo(
-        returnType = { "boolean", "double", "integer", "long", "date", "date_nanos", "ip", "keyword", "long", "version" },
+        returnType = { "boolean", "double", "integer", "long", "date", "date_nanos", "ip", "keyword", "unsigned_long", "version" },
         description = "The maximum over time value of a field.",
         type = FunctionType.TIME_SERIES_AGGREGATE,
         appliesTo = { @FunctionAppliesTo(lifeCycle = FunctionAppliesToLifecycle.UNAVAILABLE) },
@@ -59,7 +59,7 @@ public class MaxOverTime extends TimeSeriesAggregateFunction {
                 "ip",
                 "keyword",
                 "text",
-                "long",
+                "unsigned_long",
                 "version" }
         ) Expression field
     ) {

+ 10 - 3
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/Min.java

@@ -47,6 +47,7 @@ public class Min extends AggregateFunction implements ToAggregator, SurrogateExp
     private static final Map<DataType, Supplier<AggregatorFunctionSupplier>> SUPPLIERS = Map.ofEntries(
         Map.entry(DataType.BOOLEAN, MinBooleanAggregatorFunctionSupplier::new),
         Map.entry(DataType.LONG, MinLongAggregatorFunctionSupplier::new),
+        Map.entry(DataType.UNSIGNED_LONG, MinLongAggregatorFunctionSupplier::new),
         Map.entry(DataType.DATETIME, MinLongAggregatorFunctionSupplier::new),
         Map.entry(DataType.DATE_NANOS, MinLongAggregatorFunctionSupplier::new),
         Map.entry(DataType.INTEGER, MinIntAggregatorFunctionSupplier::new),
@@ -58,7 +59,7 @@ public class Min extends AggregateFunction implements ToAggregator, SurrogateExp
     );
 
     @FunctionInfo(
-        returnType = { "boolean", "double", "integer", "long", "date", "date_nanos", "ip", "keyword", "long", "version" },
+        returnType = { "boolean", "double", "integer", "long", "date", "date_nanos", "ip", "keyword", "unsigned_long", "version" },
         description = "The minimum value of a field.",
         type = FunctionType.AGGREGATE,
         examples = {
@@ -86,7 +87,7 @@ public class Min extends AggregateFunction implements ToAggregator, SurrogateExp
                 "ip",
                 "keyword",
                 "text",
-                "long",
+                "unsigned_long",
                 "version" }
         ) Expression field
     ) {
@@ -128,7 +129,13 @@ public class Min extends AggregateFunction implements ToAggregator, SurrogateExp
             dt -> SUPPLIERS.containsKey(dt) || dt == DataType.AGGREGATE_METRIC_DOUBLE,
             sourceText(),
             DEFAULT,
-            "representable except unsigned_long and spatial types"
+            "boolean",
+            "date",
+            "ip",
+            "string",
+            "version",
+            "aggregate_metric_double",
+            "numeric except counter types"
         );
     }
 

+ 2 - 2
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/MinOverTime.java

@@ -37,7 +37,7 @@ public class MinOverTime extends TimeSeriesAggregateFunction {
     );
 
     @FunctionInfo(
-        returnType = { "boolean", "double", "integer", "long", "date", "date_nanos", "ip", "keyword", "long", "version" },
+        returnType = { "boolean", "double", "integer", "long", "date", "date_nanos", "ip", "keyword", "unsigned_long", "version" },
         description = "The minimum over time value of a field.",
         type = FunctionType.TIME_SERIES_AGGREGATE,
         appliesTo = { @FunctionAppliesTo(lifeCycle = FunctionAppliesToLifecycle.UNAVAILABLE) },
@@ -59,7 +59,7 @@ public class MinOverTime extends TimeSeriesAggregateFunction {
                 "ip",
                 "keyword",
                 "text",
-                "long",
+                "unsigned_long",
                 "version" }
         ) Expression field
     ) {

+ 12 - 1
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMax.java

@@ -69,7 +69,18 @@ public class MvMax extends AbstractMultivalueFunction {
 
     @Override
     protected TypeResolution resolveFieldType() {
-        return isType(field(), t -> isSpatial(t) == false && isRepresentable(t), sourceText(), null, "representableNonSpatial");
+        return isType(
+            field(),
+            t -> isSpatial(t) == false && isRepresentable(t),
+            sourceText(),
+            null,
+            "boolean",
+            "date",
+            "ip",
+            "string",
+            "version",
+            "numeric except counter types"
+        );
     }
 
     @Override

+ 12 - 1
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMin.java

@@ -69,7 +69,18 @@ public class MvMin extends AbstractMultivalueFunction {
 
     @Override
     protected TypeResolution resolveFieldType() {
-        return isType(field(), t -> isSpatial(t) == false && isRepresentable(t), sourceText(), null, "representableNonSpatial");
+        return isType(
+            field(),
+            t -> isSpatial(t) == false && isRepresentable(t),
+            sourceText(),
+            null,
+            "boolean",
+            "date",
+            "ip",
+            "string",
+            "version",
+            "numeric except counter types"
+        );
     }
 
     @Override

+ 1 - 5
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/analysis/AnalyzerTests.java

@@ -2008,19 +2008,15 @@ public class AnalyzerTests extends ESTestCase {
               row x = to_unsigned_long(\"10\")
               | stats  avg(x), count_distinct(x), max(x), median(x), median_absolute_deviation(x), min(x), percentile(x, 10), sum(x)
             """, """
-            Found 8 problems
+            Found 6 problems
             line 2:12: argument of [avg(x)] must be [aggregate_metric_double or numeric except unsigned_long or counter types],\
              found value [x] type [unsigned_long]
             line 2:20: argument of [count_distinct(x)] must be [any exact type except unsigned_long, _source, or counter types],\
              found value [x] type [unsigned_long]
-            line 2:39: argument of [max(x)] must be [representable except unsigned_long and spatial types],\
-             found value [x] type [unsigned_long]
             line 2:47: argument of [median(x)] must be [numeric except unsigned_long or counter types],\
              found value [x] type [unsigned_long]
             line 2:58: argument of [median_absolute_deviation(x)] must be [numeric except unsigned_long or counter types],\
              found value [x] type [unsigned_long]
-            line 2:88: argument of [min(x)] must be [representable except unsigned_long and spatial types],\
-             found value [x] type [unsigned_long]
             line 2:96: first argument of [percentile(x, 10)] must be [numeric except unsigned_long],\
              found value [x] type [unsigned_long]
             line 2:115: argument of [sum(x)] must be [aggregate_metric_double or numeric except unsigned_long or counter types],\

+ 2 - 2
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/analysis/VerifierTests.java

@@ -1086,7 +1086,7 @@ public class VerifierTests extends ESTestCase {
             error("FROM tests | STATS min(network.bytes_in)", tsdb),
             equalTo(
                 "1:20: argument of [min(network.bytes_in)] must be"
-                    + " [representable except unsigned_long and spatial types],"
+                    + " [boolean, date, ip, string, version, aggregate_metric_double or numeric except counter types],"
                     + " found value [network.bytes_in] type [counter_long]"
             )
         );
@@ -1095,7 +1095,7 @@ public class VerifierTests extends ESTestCase {
             error("FROM tests | STATS max(network.bytes_in)", tsdb),
             equalTo(
                 "1:20: argument of [max(network.bytes_in)] must be"
-                    + " [representable except unsigned_long and spatial types],"
+                    + " [boolean, date, ip, string, version, aggregate_metric_double or numeric except counter types],"
                     + " found value [network.bytes_in] type [counter_long]"
             )
         );

+ 2 - 3
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractAggregationTestCase.java

@@ -41,7 +41,6 @@ import java.util.function.Consumer;
 import java.util.stream.Collectors;
 import java.util.stream.IntStream;
 
-import static org.elasticsearch.compute.data.BlockUtils.toJavaObject;
 import static org.elasticsearch.xpack.esql.EsqlTestUtils.unboundLogicalOptimizerContext;
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.instanceOf;
@@ -341,7 +340,7 @@ public abstract class AbstractAggregationTestCase extends AbstractFunctionTestCa
             // For null blocks, the element type is NULL, so if the provided matcher matches, the type works too
             assertThat(block.elementType(), is(oneOf(expectedElementType, ElementType.NULL)));
 
-            return toJavaObject(blocks[resultBlockIndex], 0);
+            return toJavaObjectUnsignedLongAware(blocks[resultBlockIndex], 0);
         } finally {
             Releasables.close(blocks);
         }
@@ -363,7 +362,7 @@ public abstract class AbstractAggregationTestCase extends AbstractFunctionTestCa
             assertThat(block.elementType(), is(oneOf(expectedElementType, ElementType.NULL)));
 
             return IntStream.range(resultBlockIndex, groupCount)
-                .mapToObj(position -> toJavaObject(blocks[resultBlockIndex], position))
+                .mapToObj(position -> toJavaObjectUnsignedLongAware(blocks[resultBlockIndex], position))
                 .toList();
         } finally {
             Releasables.close(blocks);

+ 6 - 3
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/TestCaseSupplier.java

@@ -1887,15 +1887,18 @@ public record TestCaseSupplier(String name, List<DataType> types, Supplier<TestC
                     throw new IllegalStateException("Multirow values require exactly 1 element to be a literal, got " + values.size());
                 }
 
-                return new Literal(Source.synthetic(name), stringToBytesRef(values.get(0), type), type);
+                return new Literal(Source.synthetic(name), convertLiterals(values.get(0), type), type);
             }
-            return new Literal(Source.synthetic(name), stringToBytesRef(data, type), type);
+            return new Literal(Source.synthetic(name), convertLiterals(data, type), type);
         }
 
-        private Object stringToBytesRef(Object o, DataType type) {
+        private Object convertLiterals(Object o, DataType type) {
             if ((type == DataType.KEYWORD || type == DataType.TEXT) && o instanceof String s) {
                 return BytesRefs.toBytesRef(s);
             }
+            if (type == DataType.UNSIGNED_LONG && o instanceof BigInteger bi) {
+                return NumericUtils.asLongUnsigned(bi);
+            }
             return o;
         }
 

+ 6 - 1
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/aggregate/MaxErrorTests.java

@@ -33,7 +33,12 @@ public class MaxErrorTests extends ErrorsForCasesWithoutExamplesTestCase {
     @Override
     protected Matcher<String> expectedTypeErrorMatcher(List<Set<DataType>> validPerPosition, List<DataType> signature) {
         return equalTo(
-            typeErrorMessage(false, validPerPosition, signature, (v, p) -> "representable except unsigned_long and spatial types")
+            typeErrorMessage(
+                false,
+                validPerPosition,
+                signature,
+                (v, p) -> "boolean, date, ip, string, version, aggregate_metric_double or numeric except counter types"
+            )
         );
     }
 }

+ 44 - 0
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/aggregate/MaxOverTimeErrorTests.java

@@ -0,0 +1,44 @@
+/*
+ * 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 MaxOverTimeErrorTests extends ErrorsForCasesWithoutExamplesTestCase {
+    @Override
+    protected List<TestCaseSupplier> cases() {
+        return paramsToSuppliers(MaxOverTimeTests.parameters());
+    }
+
+    @Override
+    protected Expression build(Source source, List<Expression> args) {
+        return new MaxOverTime(source, args.get(0));
+    }
+
+    @Override
+    protected Matcher<String> expectedTypeErrorMatcher(List<Set<DataType>> validPerPosition, List<DataType> signature) {
+        return equalTo(
+            typeErrorMessage(
+                false,
+                validPerPosition,
+                signature,
+                (v, p) -> "boolean, date, ip, string, version, aggregate_metric_double or numeric except counter types"
+            )
+        );
+    }
+}

+ 11 - 0
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/aggregate/MaxTests.java

@@ -21,6 +21,7 @@ import org.elasticsearch.xpack.esql.expression.function.MultiRowTestCaseSupplier
 import org.elasticsearch.xpack.esql.expression.function.TestCaseSupplier;
 import org.elasticsearch.xpack.versionfield.Version;
 
+import java.math.BigInteger;
 import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.List;
@@ -42,6 +43,7 @@ public class MaxTests extends AbstractAggregationTestCase {
         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.booleanCases(1, 1000),
@@ -72,6 +74,15 @@ public class MaxTests extends AbstractAggregationTestCase {
                         equalTo(200L)
                     )
                 ),
+                new TestCaseSupplier(
+                    List.of(DataType.UNSIGNED_LONG),
+                    () -> new TestCaseSupplier.TestCase(
+                        List.of(TestCaseSupplier.TypedData.multiRow(List.of(new BigInteger("200")), DataType.UNSIGNED_LONG, "field")),
+                        "Max[field=Attribute[channel=0]]",
+                        DataType.UNSIGNED_LONG,
+                        equalTo(new BigInteger("200"))
+                    )
+                ),
                 new TestCaseSupplier(
                     List.of(DataType.DOUBLE),
                     () -> new TestCaseSupplier.TestCase(

+ 6 - 1
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/aggregate/MinErrorTests.java

@@ -33,7 +33,12 @@ public class MinErrorTests extends ErrorsForCasesWithoutExamplesTestCase {
     @Override
     protected Matcher<String> expectedTypeErrorMatcher(List<Set<DataType>> validPerPosition, List<DataType> signature) {
         return equalTo(
-            typeErrorMessage(false, validPerPosition, signature, (v, p) -> "representable except unsigned_long and spatial types")
+            typeErrorMessage(
+                false,
+                validPerPosition,
+                signature,
+                (v, p) -> "boolean, date, ip, string, version, aggregate_metric_double or numeric except counter types"
+            )
         );
     }
 }

+ 44 - 0
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/aggregate/MinOverTimeErrorTests.java

@@ -0,0 +1,44 @@
+/*
+ * 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 MinOverTimeErrorTests extends ErrorsForCasesWithoutExamplesTestCase {
+    @Override
+    protected List<TestCaseSupplier> cases() {
+        return paramsToSuppliers(MinOverTimeTests.parameters());
+    }
+
+    @Override
+    protected Expression build(Source source, List<Expression> args) {
+        return new MinOverTime(source, args.get(0));
+    }
+
+    @Override
+    protected Matcher<String> expectedTypeErrorMatcher(List<Set<DataType>> validPerPosition, List<DataType> signature) {
+        return equalTo(
+            typeErrorMessage(
+                false,
+                validPerPosition,
+                signature,
+                (v, p) -> "boolean, date, ip, string, version, aggregate_metric_double or numeric except counter types"
+            )
+        );
+    }
+}

+ 11 - 0
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/aggregate/MinTests.java

@@ -21,6 +21,7 @@ import org.elasticsearch.xpack.esql.expression.function.MultiRowTestCaseSupplier
 import org.elasticsearch.xpack.esql.expression.function.TestCaseSupplier;
 import org.elasticsearch.xpack.versionfield.Version;
 
+import java.math.BigInteger;
 import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.List;
@@ -42,6 +43,7 @@ public class MinTests extends AbstractAggregationTestCase {
         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.booleanCases(1, 1000),
@@ -72,6 +74,15 @@ public class MinTests extends AbstractAggregationTestCase {
                         equalTo(200L)
                     )
                 ),
+                new TestCaseSupplier(
+                    List.of(DataType.UNSIGNED_LONG),
+                    () -> new TestCaseSupplier.TestCase(
+                        List.of(TestCaseSupplier.TypedData.multiRow(List.of(new BigInteger("200")), DataType.UNSIGNED_LONG, "field")),
+                        "Max[field=Attribute[channel=0]]",
+                        DataType.UNSIGNED_LONG,
+                        equalTo(new BigInteger("200"))
+                    )
+                ),
                 new TestCaseSupplier(
                     List.of(DataType.DOUBLE),
                     () -> new TestCaseSupplier.TestCase(

+ 8 - 1
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxErrorTests.java

@@ -32,6 +32,13 @@ public class MvMaxErrorTests extends ErrorsForCasesWithoutExamplesTestCase {
 
     @Override
     protected Matcher<String> expectedTypeErrorMatcher(List<Set<DataType>> validPerPosition, List<DataType> signature) {
-        return equalTo(typeErrorMessage(false, validPerPosition, signature, (v, p) -> "representableNonSpatial"));
+        return equalTo(
+            typeErrorMessage(
+                false,
+                validPerPosition,
+                signature,
+                (v, p) -> "boolean, date, ip, string, version or numeric except counter types"
+            )
+        );
     }
 }

+ 8 - 1
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinErrorTests.java

@@ -32,6 +32,13 @@ public class MvMinErrorTests extends ErrorsForCasesWithoutExamplesTestCase {
 
     @Override
     protected Matcher<String> expectedTypeErrorMatcher(List<Set<DataType>> validPerPosition, List<DataType> signature) {
-        return equalTo(typeErrorMessage(false, validPerPosition, signature, (v, p) -> "representableNonSpatial"));
+        return equalTo(
+            typeErrorMessage(
+                false,
+                validPerPosition,
+                signature,
+                (v, p) -> "boolean, date, ip, string, version or numeric except counter types"
+            )
+        );
     }
 }