Browse Source

[ESQL] Add the ability to assert warnings in the new test generation logic (#99381)

This extends the test case generation functions to take expected warnings, and demonstrates the use of that functionality by testing expected nulls for log10. We can build on this to get proper null handling and tests for the rest of the math functions.

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
Mark Tozzi 2 years ago
parent
commit
6ab6b237c6
20 changed files with 324 additions and 110 deletions
  1. 15 23
      x-pack/plugin/esql/qa/testFixtures/src/main/resources/math.csv-spec
  2. 22 6
      x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10DoubleEvaluator.java
  3. 22 7
      x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10IntEvaluator.java
  4. 22 7
      x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10LongEvaluator.java
  5. 22 7
      x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10UnsignedLongEvaluator.java
  6. 20 8
      x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10.java
  7. 3 0
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java
  8. 51 26
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/TestCaseSupplier.java
  9. 7 1
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/AcosTests.java
  10. 7 1
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/AsinTests.java
  11. 7 1
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/AtanTests.java
  12. 7 1
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/CosTests.java
  13. 7 1
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/CoshTests.java
  14. 6 4
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/FloorTests.java
  15. 68 8
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10Tests.java
  16. 7 1
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/SinTests.java
  17. 7 1
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/SinhTests.java
  18. 10 5
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/SqrtTests.java
  19. 7 1
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/TanTests.java
  20. 7 1
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/TanhTests.java

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

@@ -266,38 +266,30 @@ d: double | s:double
 ;
 
 log10ofNegative
-row d = -1.0 | eval s = is_nan(log10(d));
+row d = -1.0 | eval s = log10(d);
+warning:Line 1:25: evaluation of [log10(d)] failed, treating result as null. Only first 20 failures recorded.
+warning:java.lang.ArithmeticException: Log of non-positive number
 
-d:double | s:boolean
--1.0     | true
-;
-
-log10ofNan
-row d = 0.0/0.0 | eval s = is_nan(log10(d));
-
-d:double | s:boolean
-NaN      | true
+d:double | s:double
+-1.0     | null
 ;
 
 log10ofZero
-row d = 0.0 |eval s = is_infinite(log10(d));
+row d = 0.0 | eval s = log10(d);
+warning:Line 1:24: evaluation of [log10(d)] failed, treating result as null. Only first 20 failures recorded.
+warning:java.lang.ArithmeticException: Log of non-positive number
 
-d:double | s:boolean
-0.0     | true
+d:double | s:double
+0.0     | null
 ;
 
 log10ofNegativeZero
-row d = -0.0 |eval s = is_infinite(log10(d));
-
-d:double | s:boolean
--0.0     | true
-;
+row d = -0.0 | eval s = log10(d);
+warning:Line 1:25: evaluation of [log10(d)] failed, treating result as null. Only first 20 failures recorded.
+warning:java.lang.ArithmeticException: Log of non-positive number
 
-log10ofInfinite
-row d = 1/0.0 | eval s = is_infinite(log10(d));
-
-d:double | s:boolean
-Infinity | true
+d:double | s:double
+-0.0     | null
 ;
 
 log10ofLong

+ 22 - 6
x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10DoubleEvaluator.java

@@ -4,6 +4,7 @@
 // 2.0.
 package org.elasticsearch.xpack.esql.expression.function.scalar.math;
 
+import java.lang.ArithmeticException;
 import java.lang.Override;
 import java.lang.String;
 import org.elasticsearch.compute.data.Block;
@@ -11,15 +12,20 @@ import org.elasticsearch.compute.data.DoubleBlock;
 import org.elasticsearch.compute.data.DoubleVector;
 import org.elasticsearch.compute.data.Page;
 import org.elasticsearch.compute.operator.EvalOperator;
+import org.elasticsearch.xpack.esql.expression.function.Warnings;
+import org.elasticsearch.xpack.ql.tree.Source;
 
 /**
  * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Log10}.
  * This class is generated. Do not edit it.
  */
 public final class Log10DoubleEvaluator implements EvalOperator.ExpressionEvaluator {
+  private final Warnings warnings;
+
   private final EvalOperator.ExpressionEvaluator val;
 
-  public Log10DoubleEvaluator(EvalOperator.ExpressionEvaluator val) {
+  public Log10DoubleEvaluator(Source source, EvalOperator.ExpressionEvaluator val) {
+    this.warnings = new Warnings(source);
     this.val = val;
   }
 
@@ -34,7 +40,7 @@ public final class Log10DoubleEvaluator implements EvalOperator.ExpressionEvalua
     if (valVector == null) {
       return eval(page.getPositionCount(), valBlock);
     }
-    return eval(page.getPositionCount(), valVector).asBlock();
+    return eval(page.getPositionCount(), valVector);
   }
 
   public DoubleBlock eval(int positionCount, DoubleBlock valBlock) {
@@ -44,15 +50,25 @@ public final class Log10DoubleEvaluator implements EvalOperator.ExpressionEvalua
         result.appendNull();
         continue position;
       }
-      result.appendDouble(Log10.process(valBlock.getDouble(valBlock.getFirstValueIndex(p))));
+      try {
+        result.appendDouble(Log10.process(valBlock.getDouble(valBlock.getFirstValueIndex(p))));
+      } catch (ArithmeticException e) {
+        warnings.registerException(e);
+        result.appendNull();
+      }
     }
     return result.build();
   }
 
-  public DoubleVector eval(int positionCount, DoubleVector valVector) {
-    DoubleVector.Builder result = DoubleVector.newVectorBuilder(positionCount);
+  public DoubleBlock eval(int positionCount, DoubleVector valVector) {
+    DoubleBlock.Builder result = DoubleBlock.newBlockBuilder(positionCount);
     position: for (int p = 0; p < positionCount; p++) {
-      result.appendDouble(Log10.process(valVector.getDouble(p)));
+      try {
+        result.appendDouble(Log10.process(valVector.getDouble(p)));
+      } catch (ArithmeticException e) {
+        warnings.registerException(e);
+        result.appendNull();
+      }
     }
     return result.build();
   }

+ 22 - 7
x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10IntEvaluator.java

@@ -4,24 +4,29 @@
 // 2.0.
 package org.elasticsearch.xpack.esql.expression.function.scalar.math;
 
+import java.lang.ArithmeticException;
 import java.lang.Override;
 import java.lang.String;
 import org.elasticsearch.compute.data.Block;
 import org.elasticsearch.compute.data.DoubleBlock;
-import org.elasticsearch.compute.data.DoubleVector;
 import org.elasticsearch.compute.data.IntBlock;
 import org.elasticsearch.compute.data.IntVector;
 import org.elasticsearch.compute.data.Page;
 import org.elasticsearch.compute.operator.EvalOperator;
+import org.elasticsearch.xpack.esql.expression.function.Warnings;
+import org.elasticsearch.xpack.ql.tree.Source;
 
 /**
  * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Log10}.
  * This class is generated. Do not edit it.
  */
 public final class Log10IntEvaluator implements EvalOperator.ExpressionEvaluator {
+  private final Warnings warnings;
+
   private final EvalOperator.ExpressionEvaluator val;
 
-  public Log10IntEvaluator(EvalOperator.ExpressionEvaluator val) {
+  public Log10IntEvaluator(Source source, EvalOperator.ExpressionEvaluator val) {
+    this.warnings = new Warnings(source);
     this.val = val;
   }
 
@@ -36,7 +41,7 @@ public final class Log10IntEvaluator implements EvalOperator.ExpressionEvaluator
     if (valVector == null) {
       return eval(page.getPositionCount(), valBlock);
     }
-    return eval(page.getPositionCount(), valVector).asBlock();
+    return eval(page.getPositionCount(), valVector);
   }
 
   public DoubleBlock eval(int positionCount, IntBlock valBlock) {
@@ -46,15 +51,25 @@ public final class Log10IntEvaluator implements EvalOperator.ExpressionEvaluator
         result.appendNull();
         continue position;
       }
-      result.appendDouble(Log10.process(valBlock.getInt(valBlock.getFirstValueIndex(p))));
+      try {
+        result.appendDouble(Log10.process(valBlock.getInt(valBlock.getFirstValueIndex(p))));
+      } catch (ArithmeticException e) {
+        warnings.registerException(e);
+        result.appendNull();
+      }
     }
     return result.build();
   }
 
-  public DoubleVector eval(int positionCount, IntVector valVector) {
-    DoubleVector.Builder result = DoubleVector.newVectorBuilder(positionCount);
+  public DoubleBlock eval(int positionCount, IntVector valVector) {
+    DoubleBlock.Builder result = DoubleBlock.newBlockBuilder(positionCount);
     position: for (int p = 0; p < positionCount; p++) {
-      result.appendDouble(Log10.process(valVector.getInt(p)));
+      try {
+        result.appendDouble(Log10.process(valVector.getInt(p)));
+      } catch (ArithmeticException e) {
+        warnings.registerException(e);
+        result.appendNull();
+      }
     }
     return result.build();
   }

+ 22 - 7
x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10LongEvaluator.java

@@ -4,24 +4,29 @@
 // 2.0.
 package org.elasticsearch.xpack.esql.expression.function.scalar.math;
 
+import java.lang.ArithmeticException;
 import java.lang.Override;
 import java.lang.String;
 import org.elasticsearch.compute.data.Block;
 import org.elasticsearch.compute.data.DoubleBlock;
-import org.elasticsearch.compute.data.DoubleVector;
 import org.elasticsearch.compute.data.LongBlock;
 import org.elasticsearch.compute.data.LongVector;
 import org.elasticsearch.compute.data.Page;
 import org.elasticsearch.compute.operator.EvalOperator;
+import org.elasticsearch.xpack.esql.expression.function.Warnings;
+import org.elasticsearch.xpack.ql.tree.Source;
 
 /**
  * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Log10}.
  * This class is generated. Do not edit it.
  */
 public final class Log10LongEvaluator implements EvalOperator.ExpressionEvaluator {
+  private final Warnings warnings;
+
   private final EvalOperator.ExpressionEvaluator val;
 
-  public Log10LongEvaluator(EvalOperator.ExpressionEvaluator val) {
+  public Log10LongEvaluator(Source source, EvalOperator.ExpressionEvaluator val) {
+    this.warnings = new Warnings(source);
     this.val = val;
   }
 
@@ -36,7 +41,7 @@ public final class Log10LongEvaluator implements EvalOperator.ExpressionEvaluato
     if (valVector == null) {
       return eval(page.getPositionCount(), valBlock);
     }
-    return eval(page.getPositionCount(), valVector).asBlock();
+    return eval(page.getPositionCount(), valVector);
   }
 
   public DoubleBlock eval(int positionCount, LongBlock valBlock) {
@@ -46,15 +51,25 @@ public final class Log10LongEvaluator implements EvalOperator.ExpressionEvaluato
         result.appendNull();
         continue position;
       }
-      result.appendDouble(Log10.process(valBlock.getLong(valBlock.getFirstValueIndex(p))));
+      try {
+        result.appendDouble(Log10.process(valBlock.getLong(valBlock.getFirstValueIndex(p))));
+      } catch (ArithmeticException e) {
+        warnings.registerException(e);
+        result.appendNull();
+      }
     }
     return result.build();
   }
 
-  public DoubleVector eval(int positionCount, LongVector valVector) {
-    DoubleVector.Builder result = DoubleVector.newVectorBuilder(positionCount);
+  public DoubleBlock eval(int positionCount, LongVector valVector) {
+    DoubleBlock.Builder result = DoubleBlock.newBlockBuilder(positionCount);
     position: for (int p = 0; p < positionCount; p++) {
-      result.appendDouble(Log10.process(valVector.getLong(p)));
+      try {
+        result.appendDouble(Log10.process(valVector.getLong(p)));
+      } catch (ArithmeticException e) {
+        warnings.registerException(e);
+        result.appendNull();
+      }
     }
     return result.build();
   }

+ 22 - 7
x-pack/plugin/esql/src/main/java/generated/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10UnsignedLongEvaluator.java

@@ -4,24 +4,29 @@
 // 2.0.
 package org.elasticsearch.xpack.esql.expression.function.scalar.math;
 
+import java.lang.ArithmeticException;
 import java.lang.Override;
 import java.lang.String;
 import org.elasticsearch.compute.data.Block;
 import org.elasticsearch.compute.data.DoubleBlock;
-import org.elasticsearch.compute.data.DoubleVector;
 import org.elasticsearch.compute.data.LongBlock;
 import org.elasticsearch.compute.data.LongVector;
 import org.elasticsearch.compute.data.Page;
 import org.elasticsearch.compute.operator.EvalOperator;
+import org.elasticsearch.xpack.esql.expression.function.Warnings;
+import org.elasticsearch.xpack.ql.tree.Source;
 
 /**
  * {@link EvalOperator.ExpressionEvaluator} implementation for {@link Log10}.
  * This class is generated. Do not edit it.
  */
 public final class Log10UnsignedLongEvaluator implements EvalOperator.ExpressionEvaluator {
+  private final Warnings warnings;
+
   private final EvalOperator.ExpressionEvaluator val;
 
-  public Log10UnsignedLongEvaluator(EvalOperator.ExpressionEvaluator val) {
+  public Log10UnsignedLongEvaluator(Source source, EvalOperator.ExpressionEvaluator val) {
+    this.warnings = new Warnings(source);
     this.val = val;
   }
 
@@ -36,7 +41,7 @@ public final class Log10UnsignedLongEvaluator implements EvalOperator.Expression
     if (valVector == null) {
       return eval(page.getPositionCount(), valBlock);
     }
-    return eval(page.getPositionCount(), valVector).asBlock();
+    return eval(page.getPositionCount(), valVector);
   }
 
   public DoubleBlock eval(int positionCount, LongBlock valBlock) {
@@ -46,15 +51,25 @@ public final class Log10UnsignedLongEvaluator implements EvalOperator.Expression
         result.appendNull();
         continue position;
       }
-      result.appendDouble(Log10.processUnsignedLong(valBlock.getLong(valBlock.getFirstValueIndex(p))));
+      try {
+        result.appendDouble(Log10.processUnsignedLong(valBlock.getLong(valBlock.getFirstValueIndex(p))));
+      } catch (ArithmeticException e) {
+        warnings.registerException(e);
+        result.appendNull();
+      }
     }
     return result.build();
   }
 
-  public DoubleVector eval(int positionCount, LongVector valVector) {
-    DoubleVector.Builder result = DoubleVector.newVectorBuilder(positionCount);
+  public DoubleBlock eval(int positionCount, LongVector valVector) {
+    DoubleBlock.Builder result = DoubleBlock.newBlockBuilder(positionCount);
     position: for (int p = 0; p < positionCount; p++) {
-      result.appendDouble(Log10.processUnsignedLong(valVector.getLong(p)));
+      try {
+        result.appendDouble(Log10.processUnsignedLong(valVector.getLong(p)));
+      } catch (ArithmeticException e) {
+        warnings.registerException(e);
+        result.appendNull();
+      }
     }
     return result.build();
   }

+ 20 - 8
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10.java

@@ -41,38 +41,50 @@ public class Log10 extends UnaryScalarFunction implements EvaluatorMapper {
         var eval = field.get();
 
         if (fieldType == DataTypes.DOUBLE) {
-            return () -> new Log10DoubleEvaluator(eval);
+            return () -> new Log10DoubleEvaluator(source(), eval);
         }
         if (fieldType == DataTypes.INTEGER) {
-            return () -> new Log10IntEvaluator(eval);
+            return () -> new Log10IntEvaluator(source(), eval);
         }
         if (fieldType == DataTypes.LONG) {
-            return () -> new Log10LongEvaluator(eval);
+            return () -> new Log10LongEvaluator(source(), eval);
         }
         if (fieldType == DataTypes.UNSIGNED_LONG) {
-            return () -> new Log10UnsignedLongEvaluator(eval);
+            return () -> new Log10UnsignedLongEvaluator(source(), eval);
         }
 
         throw EsqlIllegalArgumentException.illegalDataType(fieldType);
     }
 
-    @Evaluator(extraName = "Double")
+    @Evaluator(extraName = "Double", warnExceptions = ArithmeticException.class)
     static double process(double val) {
+        if (val <= 0d) {
+            throw new ArithmeticException("Log of non-positive number");
+        }
         return Math.log10(val);
     }
 
-    @Evaluator(extraName = "Long")
+    @Evaluator(extraName = "Long", warnExceptions = ArithmeticException.class)
     static double process(long val) {
+        if (val <= 0L) {
+            throw new ArithmeticException("Log of non-positive number");
+        }
         return Math.log10(val);
     }
 
-    @Evaluator(extraName = "UnsignedLong")
+    @Evaluator(extraName = "UnsignedLong", warnExceptions = ArithmeticException.class)
     static double processUnsignedLong(long val) {
+        if (val == NumericUtils.ZERO_AS_UNSIGNED_LONG) {
+            throw new ArithmeticException("Log of non-positive number");
+        }
         return Math.log10(NumericUtils.unsignedLongToDouble(val));
     }
 
-    @Evaluator(extraName = "Int")
+    @Evaluator(extraName = "Int", warnExceptions = ArithmeticException.class)
     static double process(int val) {
+        if (val <= 0) {
+            throw new ArithmeticException("Log of non-positive number");
+        }
         return Math.log10(val);
     }
 

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

@@ -164,6 +164,9 @@ public abstract class AbstractFunctionTestCase extends ESTestCase {
 
     public final void testEvaluate() {
         assumeTrue("All test data types must be representable in order to build fields", testCase.allTypesAreRepresentable());
+        logger.info(
+            "Test Values: " + testCase.getData().stream().map(TestCaseSupplier.TypedData::toString).collect(Collectors.joining(","))
+        );
         Expression expression = buildFieldExpression(testCase);
         if (testCase.getExpectedTypeError() != null) {
             assertTrue("expected unresolved", expression.typeResolved().unresolved());

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

@@ -40,7 +40,7 @@ public record TestCaseSupplier(String name, List<DataType> types, Supplier<TestC
     implements
         Supplier<TestCaseSupplier.TestCase> {
 
-    public static final BigInteger MAX_UNSIGNED_LONG = BigInteger.valueOf(1 << 64).subtract(BigInteger.ONE);
+    public static final BigInteger MAX_UNSIGNED_LONG = NumericUtils.UNSIGNED_LONG_MAX;
     /**
      * Build a test case without types.
      *
@@ -84,7 +84,13 @@ public record TestCaseSupplier(String name, List<DataType> types, Supplier<TestC
      * Generate positive test cases for unary functions that operate on an {@code numeric}
      * fields by casting them to {@link DataTypes#DOUBLE}s.
      */
-    public static List<TestCaseSupplier> forUnaryCastingToDouble(String name, String argName, DoubleUnaryOperator expected) {
+    public static List<TestCaseSupplier> forUnaryCastingToDouble(
+        String name,
+        String argName,
+        DoubleUnaryOperator expected,
+        Double min,
+        Double max
+    ) {
         String read = "Attribute[channel=0]";
         String eval = name + "[" + argName + "=";
         List<TestCaseSupplier> suppliers = new ArrayList<>();
@@ -93,33 +99,29 @@ public record TestCaseSupplier(String name, List<DataType> types, Supplier<TestC
             eval + castToDoubleEvaluator(read, DataTypes.INTEGER) + "]",
             DataTypes.DOUBLE,
             i -> expected.applyAsDouble(i),
-            Integer.MIN_VALUE,
-            Integer.MAX_VALUE
+            min.intValue(),
+            max.intValue(),
+            List.of()
         );
         forUnaryLong(
             suppliers,
             eval + castToDoubleEvaluator(read, DataTypes.LONG) + "]",
             DataTypes.DOUBLE,
             l -> expected.applyAsDouble(l),
-            Long.MIN_VALUE,
-            Long.MAX_VALUE
+            min.longValue(),
+            max.longValue(),
+            List.of()
         );
         forUnaryUnsignedLong(
             suppliers,
             eval + castToDoubleEvaluator(read, DataTypes.UNSIGNED_LONG) + "]",
             DataTypes.DOUBLE,
             ul -> expected.applyAsDouble(ul.doubleValue()),
-            BigInteger.ZERO,
-            MAX_UNSIGNED_LONG
-        );
-        forUnaryDouble(
-            suppliers,
-            eval + read + "]",
-            DataTypes.DOUBLE,
-            i -> expected.applyAsDouble(i),
-            Double.NEGATIVE_INFINITY,
-            Double.POSITIVE_INFINITY
+            BigInteger.valueOf((int) Math.ceil(min)),
+            BigInteger.valueOf((int) Math.floor(max)),
+            List.of()
         );
+        forUnaryDouble(suppliers, eval + read + "]", DataTypes.DOUBLE, i -> expected.applyAsDouble(i), min, max, List.of());
         return suppliers;
     }
 
@@ -184,7 +186,8 @@ public record TestCaseSupplier(String name, List<DataType> types, Supplier<TestC
         DataType expectedType,
         IntFunction<Object> expectedValue,
         int lowerBound,
-        int upperBound
+        int upperBound,
+        List<String> warnings
     ) {
         unaryNumeric(
             suppliers,
@@ -192,7 +195,8 @@ public record TestCaseSupplier(String name, List<DataType> types, Supplier<TestC
             DataTypes.INTEGER,
             intCases(lowerBound, upperBound),
             expectedType,
-            n -> expectedValue.apply(n.intValue())
+            n -> expectedValue.apply(n.intValue()),
+            warnings
         );
     }
 
@@ -205,7 +209,8 @@ public record TestCaseSupplier(String name, List<DataType> types, Supplier<TestC
         DataType expectedType,
         LongFunction<Object> expectedValue,
         long lowerBound,
-        long upperBound
+        long upperBound,
+        List<String> warnings
     ) {
         unaryNumeric(
             suppliers,
@@ -213,7 +218,8 @@ public record TestCaseSupplier(String name, List<DataType> types, Supplier<TestC
             DataTypes.LONG,
             longCases(lowerBound, upperBound),
             expectedType,
-            n -> expectedValue.apply(n.longValue())
+            n -> expectedValue.apply(n.longValue()),
+            warnings
         );
     }
 
@@ -226,7 +232,8 @@ public record TestCaseSupplier(String name, List<DataType> types, Supplier<TestC
         DataType expectedType,
         Function<BigInteger, Object> expectedValue,
         BigInteger lowerBound,
-        BigInteger upperBound
+        BigInteger upperBound,
+        List<String> warnings
     ) {
         unaryNumeric(
             suppliers,
@@ -234,7 +241,8 @@ public record TestCaseSupplier(String name, List<DataType> types, Supplier<TestC
             DataTypes.UNSIGNED_LONG,
             ulongCases(lowerBound, upperBound),
             expectedType,
-            n -> expectedValue.apply((BigInteger) n)
+            n -> expectedValue.apply((BigInteger) n),
+            warnings
         );
     }
 
@@ -247,7 +255,8 @@ public record TestCaseSupplier(String name, List<DataType> types, Supplier<TestC
         DataType expectedType,
         DoubleFunction<Object> expectedValue,
         double lowerBound,
-        double upperBound
+        double upperBound,
+        List<String> warnings
     ) {
         unaryNumeric(
             suppliers,
@@ -255,7 +264,8 @@ public record TestCaseSupplier(String name, List<DataType> types, Supplier<TestC
             DataTypes.DOUBLE,
             doubleCases(lowerBound, upperBound),
             expectedType,
-            n -> expectedValue.apply(n.doubleValue())
+            n -> expectedValue.apply(n.doubleValue()),
+            warnings
         );
     }
 
@@ -265,7 +275,8 @@ public record TestCaseSupplier(String name, List<DataType> types, Supplier<TestC
         DataType inputType,
         List<Map.Entry<String, Supplier<Object>>> valueSuppliers,
         DataType expectedOutputType,
-        Function<Number, Object> expected
+        Function<Number, Object> expected,
+        List<String> warnings
     ) {
         for (Map.Entry<String, Supplier<Object>> supplier : valueSuppliers) {
             suppliers.add(new TestCaseSupplier(supplier.getKey(), List.of(inputType), () -> {
@@ -276,7 +287,16 @@ public record TestCaseSupplier(String name, List<DataType> types, Supplier<TestC
                     inputType,
                     "value"
                 );
-                return new TestCase(List.of(typed), expectedEvaluatorToString, expectedOutputType, equalTo(expected.apply(value)));
+                TestCase testCase = new TestCase(
+                    List.of(typed),
+                    expectedEvaluatorToString,
+                    expectedOutputType,
+                    equalTo(expected.apply(value))
+                );
+                for (String warning : warnings) {
+                    testCase = testCase.withWarning(warning);
+                }
+                return testCase;
             }));
         }
     }
@@ -586,5 +606,10 @@ public record TestCaseSupplier(String name, List<DataType> types, Supplier<TestC
         public TypedData(Object data, String name) {
             this(data, EsqlDataTypes.fromJava(data), name);
         }
+
+        @Override
+        public String toString() {
+            return type.toString() + "(" + (data == null ? "null" : data.toString()) + ")";
+        }
     }
 }

+ 7 - 1
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/AcosTests.java

@@ -25,7 +25,13 @@ public class AcosTests extends AbstractFunctionTestCase {
 
     @ParametersFactory
     public static Iterable<Object[]> parameters() {
-        List<TestCaseSupplier> suppliers = TestCaseSupplier.forUnaryCastingToDouble("AcosEvaluator", "val", Math::acos);
+        List<TestCaseSupplier> suppliers = TestCaseSupplier.forUnaryCastingToDouble(
+            "AcosEvaluator",
+            "val",
+            Math::acos,
+            Double.NEGATIVE_INFINITY,
+            Double.POSITIVE_INFINITY
+        );
         return parameterSuppliersFromTypedData(errorsForCasesWithoutExamples(anyNullIsNull(true, suppliers)));
     }
 

+ 7 - 1
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/AsinTests.java

@@ -25,7 +25,13 @@ public class AsinTests extends AbstractFunctionTestCase {
 
     @ParametersFactory
     public static Iterable<Object[]> parameters() {
-        List<TestCaseSupplier> suppliers = TestCaseSupplier.forUnaryCastingToDouble("AsinEvaluator", "val", Math::asin);
+        List<TestCaseSupplier> suppliers = TestCaseSupplier.forUnaryCastingToDouble(
+            "AsinEvaluator",
+            "val",
+            Math::asin,
+            Double.NEGATIVE_INFINITY,
+            Double.POSITIVE_INFINITY
+        );
         return parameterSuppliersFromTypedData(errorsForCasesWithoutExamples(anyNullIsNull(true, suppliers)));
     }
 

+ 7 - 1
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/AtanTests.java

@@ -25,7 +25,13 @@ public class AtanTests extends AbstractFunctionTestCase {
 
     @ParametersFactory
     public static Iterable<Object[]> parameters() {
-        List<TestCaseSupplier> suppliers = TestCaseSupplier.forUnaryCastingToDouble("AtanEvaluator", "val", Math::atan);
+        List<TestCaseSupplier> suppliers = TestCaseSupplier.forUnaryCastingToDouble(
+            "AtanEvaluator",
+            "val",
+            Math::atan,
+            Double.NEGATIVE_INFINITY,
+            Double.POSITIVE_INFINITY
+        );
         return parameterSuppliersFromTypedData(errorsForCasesWithoutExamples(anyNullIsNull(true, suppliers)));
     }
 

+ 7 - 1
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/CosTests.java

@@ -25,7 +25,13 @@ public class CosTests extends AbstractFunctionTestCase {
 
     @ParametersFactory
     public static Iterable<Object[]> parameters() {
-        List<TestCaseSupplier> suppliers = TestCaseSupplier.forUnaryCastingToDouble("CosEvaluator", "val", Math::cos);
+        List<TestCaseSupplier> suppliers = TestCaseSupplier.forUnaryCastingToDouble(
+            "CosEvaluator",
+            "val",
+            Math::cos,
+            Double.NEGATIVE_INFINITY,
+            Double.POSITIVE_INFINITY
+        );
         return parameterSuppliersFromTypedData(errorsForCasesWithoutExamples(anyNullIsNull(true, suppliers)));
     }
 

+ 7 - 1
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/CoshTests.java

@@ -25,7 +25,13 @@ public class CoshTests extends AbstractFunctionTestCase {
 
     @ParametersFactory
     public static Iterable<Object[]> parameters() {
-        List<TestCaseSupplier> suppliers = TestCaseSupplier.forUnaryCastingToDouble("CoshEvaluator", "val", Math::cosh);
+        List<TestCaseSupplier> suppliers = TestCaseSupplier.forUnaryCastingToDouble(
+            "CoshEvaluator",
+            "val",
+            Math::cosh,
+            Double.NEGATIVE_INFINITY,
+            Double.POSITIVE_INFINITY
+        );
         return parameterSuppliersFromTypedData(errorsForCasesWithoutExamples(anyNullIsNull(true, suppliers)));
     }
 

+ 6 - 4
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/FloorTests.java

@@ -33,15 +33,16 @@ public class FloorTests extends AbstractFunctionTestCase {
     public static Iterable<Object[]> parameters() {
         String read = "Attribute[channel=0]";
         List<TestCaseSupplier> suppliers = new ArrayList<>();
-        TestCaseSupplier.forUnaryInt(suppliers, read, DataTypes.INTEGER, i -> i, Integer.MIN_VALUE, Integer.MAX_VALUE);
-        TestCaseSupplier.forUnaryLong(suppliers, read, DataTypes.LONG, l -> l, Long.MIN_VALUE, Long.MAX_VALUE);
+        TestCaseSupplier.forUnaryInt(suppliers, read, DataTypes.INTEGER, i -> i, Integer.MIN_VALUE, Integer.MAX_VALUE, List.of());
+        TestCaseSupplier.forUnaryLong(suppliers, read, DataTypes.LONG, l -> l, Long.MIN_VALUE, Long.MAX_VALUE, List.of());
         TestCaseSupplier.forUnaryUnsignedLong(
             suppliers,
             read,
             DataTypes.UNSIGNED_LONG,
             ul -> NumericUtils.asLongUnsigned(ul),
             BigInteger.ZERO,
-            MAX_UNSIGNED_LONG
+            MAX_UNSIGNED_LONG,
+            List.of()
         );
         TestCaseSupplier.forUnaryDouble(
             suppliers,
@@ -49,7 +50,8 @@ public class FloorTests extends AbstractFunctionTestCase {
             DataTypes.DOUBLE,
             Math::floor,
             Double.NEGATIVE_INFINITY,
-            Double.POSITIVE_INFINITY
+            Double.POSITIVE_INFINITY,
+            List.of()
         );
         return parameterSuppliersFromTypedData(errorsForCasesWithoutExamples(anyNullIsNull(false, suppliers)));
     }

+ 68 - 8
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/Log10Tests.java

@@ -15,6 +15,7 @@ import org.elasticsearch.xpack.esql.expression.function.TestCaseSupplier;
 import org.elasticsearch.xpack.ql.expression.Expression;
 import org.elasticsearch.xpack.ql.tree.Source;
 import org.elasticsearch.xpack.ql.type.DataTypes;
+import org.elasticsearch.xpack.ql.util.NumericUtils;
 
 import java.math.BigInteger;
 import java.util.ArrayList;
@@ -32,39 +33,98 @@ public class Log10Tests extends AbstractFunctionTestCase {
     public static Iterable<Object[]> parameters() {
         String read = "Attribute[channel=0]";
         List<TestCaseSupplier> suppliers = new ArrayList<>();
+        // Cases in valid range
         TestCaseSupplier.forUnaryInt(
             suppliers,
             "Log10IntEvaluator[val=" + read + "]",
             DataTypes.DOUBLE,
             Math::log10,
-            Integer.MIN_VALUE,
-            Integer.MAX_VALUE
+            1,
+            Integer.MAX_VALUE,
+            List.of()
         );
         TestCaseSupplier.forUnaryLong(
             suppliers,
             "Log10LongEvaluator[val=" + read + "]",
             DataTypes.DOUBLE,
             Math::log10,
+            1L,
+            Long.MAX_VALUE,
+            List.of()
+        );
+        TestCaseSupplier.forUnaryUnsignedLong(
+            suppliers,
+            "Log10UnsignedLongEvaluator[val=" + read + "]",
+            DataTypes.DOUBLE,
+            ul -> Math.log10(ul == null ? null : NumericUtils.unsignedLongToDouble(NumericUtils.asLongUnsigned(ul))),
+            BigInteger.ONE,
+            MAX_UNSIGNED_LONG,
+            List.of()
+        );
+        TestCaseSupplier.forUnaryDouble(
+            suppliers,
+            "Log10DoubleEvaluator[val=" + read + "]",
+            DataTypes.DOUBLE,
+            Math::log10,
+            Double.MIN_VALUE,
+            Double.POSITIVE_INFINITY,
+            List.of()
+        );
+
+        // Add in null cases here; the out of range cases won't set the right warnings on a null input.
+        suppliers = anyNullIsNull(true, suppliers);
+
+        // Cases with invalid inputs
+        TestCaseSupplier.forUnaryInt(
+            suppliers,
+            "Log10IntEvaluator[val=" + read + "]",
+            DataTypes.DOUBLE,
+            k -> null,
+            Integer.MIN_VALUE,
+            0,
+            List.of(
+                "Line -1:-1: evaluation of [] failed, treating result as null. Only first 20 failures recorded.",
+                "java.lang.ArithmeticException: Log of non-positive number"
+            )
+        );
+        TestCaseSupplier.forUnaryLong(
+            suppliers,
+            "Log10LongEvaluator[val=" + read + "]",
+            DataTypes.DOUBLE,
+            k -> null,
             Long.MIN_VALUE,
-            Long.MAX_VALUE
+            0L,
+            List.of(
+                "Line -1:-1: evaluation of [] failed, treating result as null. Only first 20 failures recorded.",
+                "java.lang.ArithmeticException: Log of non-positive number"
+            )
         );
         TestCaseSupplier.forUnaryUnsignedLong(
             suppliers,
             "Log10UnsignedLongEvaluator[val=" + read + "]",
             DataTypes.DOUBLE,
-            ul -> Math.log10(ul.doubleValue()),
+            k -> null,
             BigInteger.ZERO,
-            MAX_UNSIGNED_LONG
+            BigInteger.ZERO,
+            List.of(
+                "Line -1:-1: evaluation of [] failed, treating result as null. Only first 20 failures recorded.",
+                "java.lang.ArithmeticException: Log of non-positive number"
+            )
         );
         TestCaseSupplier.forUnaryDouble(
             suppliers,
             "Log10DoubleEvaluator[val=" + read + "]",
             DataTypes.DOUBLE,
-            Math::log10,
+            k -> null,
             Double.NEGATIVE_INFINITY,
-            Double.POSITIVE_INFINITY
+            0d,
+            List.of(
+                "Line -1:-1: evaluation of [] failed, treating result as null. Only first 20 failures recorded.",
+                "java.lang.ArithmeticException: Log of non-positive number"
+            )
         );
-        return parameterSuppliersFromTypedData(errorsForCasesWithoutExamples(anyNullIsNull(true, suppliers)));
+
+        return parameterSuppliersFromTypedData(errorsForCasesWithoutExamples(suppliers));
     }
 
     @Override

+ 7 - 1
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/SinTests.java

@@ -25,7 +25,13 @@ public class SinTests extends AbstractFunctionTestCase {
 
     @ParametersFactory
     public static Iterable<Object[]> parameters() {
-        List<TestCaseSupplier> suppliers = TestCaseSupplier.forUnaryCastingToDouble("SinEvaluator", "val", Math::sin);
+        List<TestCaseSupplier> suppliers = TestCaseSupplier.forUnaryCastingToDouble(
+            "SinEvaluator",
+            "val",
+            Math::sin,
+            Double.NEGATIVE_INFINITY,
+            Double.POSITIVE_INFINITY
+        );
         return parameterSuppliersFromTypedData(errorsForCasesWithoutExamples(anyNullIsNull(true, suppliers)));
     }
 

+ 7 - 1
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/SinhTests.java

@@ -25,7 +25,13 @@ public class SinhTests extends AbstractFunctionTestCase {
 
     @ParametersFactory
     public static Iterable<Object[]> parameters() {
-        List<TestCaseSupplier> suppliers = TestCaseSupplier.forUnaryCastingToDouble("SinhEvaluator", "val", Math::sinh);
+        List<TestCaseSupplier> suppliers = TestCaseSupplier.forUnaryCastingToDouble(
+            "SinhEvaluator",
+            "val",
+            Math::sinh,
+            Double.NEGATIVE_INFINITY,
+            Double.POSITIVE_INFINITY
+        );
         return parameterSuppliersFromTypedData(errorsForCasesWithoutExamples(anyNullIsNull(true, suppliers)));
     }
 

+ 10 - 5
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/SqrtTests.java

@@ -15,6 +15,7 @@ import org.elasticsearch.xpack.esql.expression.function.TestCaseSupplier;
 import org.elasticsearch.xpack.ql.expression.Expression;
 import org.elasticsearch.xpack.ql.tree.Source;
 import org.elasticsearch.xpack.ql.type.DataTypes;
+import org.elasticsearch.xpack.ql.util.NumericUtils;
 
 import java.math.BigInteger;
 import java.util.ArrayList;
@@ -38,7 +39,8 @@ public class SqrtTests extends AbstractFunctionTestCase {
             DataTypes.DOUBLE,
             Math::sqrt,
             Integer.MIN_VALUE,
-            Integer.MAX_VALUE
+            Integer.MAX_VALUE,
+            List.of()
         );
         TestCaseSupplier.forUnaryLong(
             suppliers,
@@ -46,15 +48,17 @@ public class SqrtTests extends AbstractFunctionTestCase {
             DataTypes.DOUBLE,
             Math::sqrt,
             Long.MIN_VALUE,
-            Long.MAX_VALUE
+            Long.MAX_VALUE,
+            List.of()
         );
         TestCaseSupplier.forUnaryUnsignedLong(
             suppliers,
             "SqrtUnsignedLongEvaluator[val=" + read + "]",
             DataTypes.DOUBLE,
-            ul -> Math.sqrt(ul.doubleValue()),
+            ul -> Math.sqrt(ul == null ? null : NumericUtils.unsignedLongToDouble(NumericUtils.asLongUnsigned(ul))),
             BigInteger.ZERO,
-            MAX_UNSIGNED_LONG
+            MAX_UNSIGNED_LONG,
+            List.of()
         );
         TestCaseSupplier.forUnaryDouble(
             suppliers,
@@ -62,7 +66,8 @@ public class SqrtTests extends AbstractFunctionTestCase {
             DataTypes.DOUBLE,
             Math::sqrt,
             Double.NEGATIVE_INFINITY,
-            Double.POSITIVE_INFINITY
+            Double.POSITIVE_INFINITY,
+            List.of()
         );
         return parameterSuppliersFromTypedData(errorsForCasesWithoutExamples(anyNullIsNull(true, suppliers)));
     }

+ 7 - 1
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/TanTests.java

@@ -25,7 +25,13 @@ public class TanTests extends AbstractFunctionTestCase {
 
     @ParametersFactory
     public static Iterable<Object[]> parameters() {
-        List<TestCaseSupplier> suppliers = TestCaseSupplier.forUnaryCastingToDouble("TanEvaluator", "val", Math::tan);
+        List<TestCaseSupplier> suppliers = TestCaseSupplier.forUnaryCastingToDouble(
+            "TanEvaluator",
+            "val",
+            Math::tan,
+            Double.NEGATIVE_INFINITY,
+            Double.POSITIVE_INFINITY
+        );
         return parameterSuppliersFromTypedData(errorsForCasesWithoutExamples(anyNullIsNull(true, suppliers)));
     }
 

+ 7 - 1
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/TanhTests.java

@@ -25,7 +25,13 @@ public class TanhTests extends AbstractFunctionTestCase {
 
     @ParametersFactory
     public static Iterable<Object[]> parameters() {
-        List<TestCaseSupplier> suppliers = TestCaseSupplier.forUnaryCastingToDouble("TanhEvaluator", "val", Math::tanh);
+        List<TestCaseSupplier> suppliers = TestCaseSupplier.forUnaryCastingToDouble(
+            "TanhEvaluator",
+            "val",
+            Math::tanh,
+            Double.NEGATIVE_INFINITY,
+            Double.POSITIVE_INFINITY
+        );
         return parameterSuppliersFromTypedData(errorsForCasesWithoutExamples(anyNullIsNull(true, suppliers)));
     }