diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java index 8b4a126d8a0c7..d6f3b61cb19dd 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java @@ -49,6 +49,7 @@ import org.elasticsearch.xpack.ql.type.DataType; import org.elasticsearch.xpack.ql.type.DataTypes; import org.elasticsearch.xpack.ql.type.EsField; +import org.elasticsearch.xpack.ql.util.NumericUtils; import org.elasticsearch.xpack.ql.util.StringUtils; import org.elasticsearch.xpack.versionfield.Version; import org.hamcrest.Matcher; @@ -92,6 +93,7 @@ import static org.elasticsearch.xpack.ql.util.SpatialCoordinateTypes.GEO; import static org.hamcrest.Matchers.either; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.Matchers.sameInstance; @@ -241,7 +243,7 @@ private void testEvaluate(boolean readFloating) { Object result; try (ExpressionEvaluator evaluator = evaluator(expression).get(driverContext())) { try (Block block = evaluator.eval(row(testCase.getDataValues()))) { - result = toJavaObject(block, 0); + result = toJavaObjectUnsignedLongAware(block, 0); } } assertThat(result, not(equalTo(Double.NaN))); @@ -255,6 +257,16 @@ private void testEvaluate(boolean readFloating) { } } + private Object toJavaObjectUnsignedLongAware(Block block, int position) { + Object result; + result = toJavaObject(block, position); + if (result != null && testCase.expectedType == DataTypes.UNSIGNED_LONG) { + assertThat(result, instanceOf(Long.class)); + result = NumericUtils.unsignedLongAsBigInteger((Long) result); + } + return result; + } + /** * Evaluates a {@link Block} of values, all copied from the input pattern, read directly from the page. *

@@ -398,7 +410,7 @@ private void testEvaluateBlock(BlockFactory inputBlockFactory, DriverContext con assertThat(toJavaObject(block, p), allNullsMatcher()); continue; } - assertThat(toJavaObject(block, p), testCase.getMatcher()); + assertThat(toJavaObjectUnsignedLongAware(block, p), testCase.getMatcher()); } assertThat( "evaluates to tracked block", @@ -472,7 +484,7 @@ public final void testEvaluateInManyThreads() throws ExecutionException, Interru try (EvalOperator.ExpressionEvaluator eval = evalSupplier.get(driverContext())) { for (int c = 0; c < count; c++) { try (Block block = eval.eval(page)) { - assertThat(toJavaObject(block, 0), testCase.getMatcher()); + assertThat(toJavaObjectUnsignedLongAware(block, 0), testCase.getMatcher()); } } } @@ -513,7 +525,12 @@ public final void testFold() { expression = new FoldNull().rule(expression); assertThat(expression.dataType(), equalTo(testCase.expectedType)); assertTrue(expression.foldable()); - assertThat(expression.fold(), testCase.getMatcher()); + Object result = expression.fold(); + // Decode unsigned longs into BigIntegers + if (testCase.expectedType == DataTypes.UNSIGNED_LONG && result != null) { + result = NumericUtils.unsignedLongAsBigInteger((Long) result); + } + assertThat(result, testCase.getMatcher()); if (testCase.getExpectedWarnings() != null) { assertWarnings(testCase.getExpectedWarnings()); } diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/TestCaseSupplier.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/TestCaseSupplier.java index faf10d499127a..9eab8cbef5fed 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/TestCaseSupplier.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/TestCaseSupplier.java @@ -10,6 +10,8 @@ import org.apache.lucene.document.InetAddressPoint; import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.network.InetAddresses; +import org.elasticsearch.logging.LogManager; +import org.elasticsearch.logging.Logger; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xpack.esql.expression.function.scalar.convert.AbstractConvertFunction; import org.elasticsearch.xpack.esql.type.EsqlDataTypes; @@ -48,6 +50,8 @@ public record TestCaseSupplier(String name, List types, Supplier supplier) implements Supplier { + + private static Logger logger = LogManager.getLogger(TestCaseSupplier.class); /** * Build a test case without types. * @@ -530,6 +534,8 @@ public static void unary( supplier.type(), "value" ); + logger.info("Value is " + value + " of type " + value.getClass()); + logger.info("expectedValue is " + expectedValue.apply(value)); TestCase testCase = new TestCase( List.of(typed), expectedEvaluatorToString, @@ -949,6 +955,9 @@ public TypedData(Object data, String name) { @Override public String toString() { + if (type == DataTypes.UNSIGNED_LONG && data instanceof Long longData) { + return type.toString() + "(" + NumericUtils.unsignedLongAsBigInteger(longData).toString() + ")"; + } return type.toString() + "(" + (data == null ? "null" : data.toString()) + ")"; } } diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLongTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLongTests.java index 080424602703d..8d5ee002a8f78 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLongTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLongTests.java @@ -16,7 +16,6 @@ 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.BigDecimal; import java.math.BigInteger; @@ -26,10 +25,7 @@ import java.util.function.Supplier; import static org.elasticsearch.xpack.ql.type.DataTypeConverter.safeToUnsignedLong; -import static org.elasticsearch.xpack.ql.util.NumericUtils.ONE_AS_UNSIGNED_LONG; import static org.elasticsearch.xpack.ql.util.NumericUtils.UNSIGNED_LONG_MAX_AS_DOUBLE; -import static org.elasticsearch.xpack.ql.util.NumericUtils.ZERO_AS_UNSIGNED_LONG; -import static org.elasticsearch.xpack.ql.util.NumericUtils.asLongUnsigned; public class ToUnsignedLongTests extends AbstractFunctionTestCase { public ToUnsignedLongTests(@Name("TestCase") Supplier testCaseSupplier) { @@ -47,7 +43,7 @@ public static Iterable parameters() { suppliers, read, DataTypes.UNSIGNED_LONG, - NumericUtils::asLongUnsigned, + n -> n, BigInteger.ZERO, UNSIGNED_LONG_MAX, List.of() @@ -57,7 +53,7 @@ public static Iterable parameters() { suppliers, evaluatorName.apply("Boolean"), DataTypes.UNSIGNED_LONG, - b -> b ? ONE_AS_UNSIGNED_LONG : ZERO_AS_UNSIGNED_LONG, + b -> b ? BigInteger.ONE : BigInteger.ZERO, List.of() ); @@ -66,7 +62,7 @@ public static Iterable parameters() { suppliers, evaluatorName.apply("Long"), DataTypes.UNSIGNED_LONG, - instant -> asLongUnsigned(instant.toEpochMilli()), + instant -> BigInteger.valueOf(instant.toEpochMilli()), List.of() ); // random strings that don't look like an unsigned_long @@ -85,7 +81,7 @@ public static Iterable parameters() { suppliers, evaluatorName.apply("Double"), DataTypes.UNSIGNED_LONG, - d -> asLongUnsigned(BigDecimal.valueOf(d).toBigInteger()), // note: not: new BigDecimal(d).toBigInteger + d -> BigDecimal.valueOf(d).toBigInteger(), // note: not: new BigDecimal(d).toBigInteger 0d, UNSIGNED_LONG_MAX_AS_DOUBLE, List.of() @@ -122,7 +118,7 @@ public static Iterable parameters() { suppliers, evaluatorName.apply("Long"), DataTypes.UNSIGNED_LONG, - NumericUtils::asLongUnsigned, + BigInteger::valueOf, 0L, Long.MAX_VALUE, List.of() @@ -146,7 +142,7 @@ public static Iterable parameters() { suppliers, evaluatorName.apply("Int"), DataTypes.UNSIGNED_LONG, - NumericUtils::asLongUnsigned, + BigInteger::valueOf, 0, Integer.MAX_VALUE, List.of() @@ -180,7 +176,7 @@ public static Iterable parameters() { ) .toList(), DataTypes.UNSIGNED_LONG, - bytesRef -> asLongUnsigned(safeToUnsignedLong(((BytesRef) bytesRef).utf8ToString())), + bytesRef -> safeToUnsignedLong(((BytesRef) bytesRef).utf8ToString()), List.of() ); // strings of random doubles within unsigned_long's range @@ -198,7 +194,7 @@ public static Iterable parameters() { ) .toList(), DataTypes.UNSIGNED_LONG, - bytesRef -> asLongUnsigned(safeToUnsignedLong(((BytesRef) bytesRef).utf8ToString())), + bytesRef -> safeToUnsignedLong(((BytesRef) bytesRef).utf8ToString()), List.of() ); // strings of random doubles outside unsigned_long's range, negative diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/AbsTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/AbsTests.java index e6621fdf78408..491680d537f37 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/AbsTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/AbsTests.java @@ -17,6 +17,7 @@ import org.elasticsearch.xpack.ql.type.DataType; import org.elasticsearch.xpack.ql.type.DataTypes; +import java.math.BigInteger; import java.util.ArrayList; import java.util.List; import java.util.function.Supplier; @@ -36,15 +37,15 @@ public static Iterable parameters() { equalTo(Math.abs(arg)) ); })); - suppliers.add(new TestCaseSupplier(List.of(DataTypes.UNSIGNED_LONG), () -> { - long arg = randomLong(); - return new TestCaseSupplier.TestCase( - List.of(new TestCaseSupplier.TypedData(arg, DataTypes.UNSIGNED_LONG, "arg")), - "Attribute[channel=0]", - DataTypes.UNSIGNED_LONG, - equalTo(arg) - ); - })); + TestCaseSupplier.forUnaryUnsignedLong( + suppliers, + "Attribute[channel=0]", + DataTypes.UNSIGNED_LONG, + (n) -> n, + BigInteger.ZERO, + UNSIGNED_LONG_MAX, + List.of() + ); suppliers.add(new TestCaseSupplier(List.of(DataTypes.LONG), () -> { long arg = randomLong(); return new TestCaseSupplier.TestCase( diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/CeilTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/CeilTests.java index f4e03de146c54..cbc7e99bf6c09 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/CeilTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/CeilTests.java @@ -17,6 +17,8 @@ import org.elasticsearch.xpack.ql.type.DataType; import org.elasticsearch.xpack.ql.type.DataTypes; +import java.math.BigInteger; +import java.util.ArrayList; import java.util.List; import java.util.function.Supplier; @@ -29,7 +31,8 @@ public CeilTests(@Name("TestCase") Supplier testCaseS @ParametersFactory public static Iterable parameters() { - return parameterSuppliersFromTypedData(List.of(new TestCaseSupplier("large double value", () -> { + List suppliers = new ArrayList<>(); + suppliers.addAll(List.of(new TestCaseSupplier("large double value", () -> { double arg = 1 / randomDouble(); return new TestCaseSupplier.TestCase( List.of(new TestCaseSupplier.TypedData(arg, DataTypes.DOUBLE, "arg")), @@ -53,15 +56,18 @@ public static Iterable parameters() { DataTypes.LONG, equalTo(arg) ); - }), new TestCaseSupplier("unsigned long value", () -> { - long arg = randomLong(); - return new TestCaseSupplier.TestCase( - List.of(new TestCaseSupplier.TypedData(arg, DataTypes.UNSIGNED_LONG, "arg")), - "Attribute[channel=0]", - DataTypes.UNSIGNED_LONG, - equalTo(arg) - ); }))); + + TestCaseSupplier.forUnaryUnsignedLong( + suppliers, + "Attribute[channel=0]", + DataTypes.UNSIGNED_LONG, + (n) -> n, + BigInteger.ZERO, + UNSIGNED_LONG_MAX, + List.of() + ); + return parameterSuppliersFromTypedData(suppliers); } @Override diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/FloorTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/FloorTests.java index f41b7c5de38ad..9a185172c9972 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/FloorTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/FloorTests.java @@ -15,7 +15,6 @@ 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; @@ -37,7 +36,7 @@ public static Iterable parameters() { suppliers, read, DataTypes.UNSIGNED_LONG, - ul -> NumericUtils.asLongUnsigned(ul), + ul -> ul, BigInteger.ZERO, UNSIGNED_LONG_MAX, List.of() diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxTests.java index 25764a9029bfd..c477cad17904d 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMaxTests.java @@ -14,7 +14,6 @@ import org.elasticsearch.xpack.ql.expression.Expression; import org.elasticsearch.xpack.ql.tree.Source; import org.elasticsearch.xpack.ql.type.DataType; -import org.elasticsearch.xpack.ql.util.NumericUtils; import java.math.BigInteger; import java.util.ArrayList; @@ -37,12 +36,7 @@ public static Iterable parameters() { doubles(cases, "mv_max", "MvMax", (size, values) -> equalTo(values.max().getAsDouble())); ints(cases, "mv_max", "MvMax", (size, values) -> equalTo(values.max().getAsInt())); longs(cases, "mv_max", "MvMax", (size, values) -> equalTo(values.max().getAsLong())); - unsignedLongs( - cases, - "mv_max", - "MvMax", - (size, values) -> equalTo(NumericUtils.asLongUnsigned(values.reduce(BigInteger::max).get())) - ); + unsignedLongs(cases, "mv_max", "MvMax", (size, values) -> equalTo(values.reduce(BigInteger::max).get())); dateTimes(cases, "mv_max", "MvMax", (size, values) -> equalTo(values.max().getAsLong())); return parameterSuppliersFromTypedData(errorsForCasesWithoutExamples(anyNullIsNull(false, cases))); } diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianTests.java index ce83c4bb8f786..43e8467147279 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMedianTests.java @@ -15,7 +15,6 @@ import org.elasticsearch.xpack.ql.tree.Source; import org.elasticsearch.xpack.ql.type.DataType; import org.elasticsearch.xpack.ql.type.DataTypes; -import org.elasticsearch.xpack.ql.util.NumericUtils; import java.math.BigInteger; import java.util.ArrayList; @@ -62,12 +61,12 @@ public static Iterable parameters() { unsignedLongs(cases, "mv_median", "MvMedian", (size, values) -> { int middle = size / 2; if (size % 2 == 1) { - return equalTo(NumericUtils.asLongUnsigned(values.sorted().skip(middle).findFirst().get())); + return equalTo(values.sorted().skip(middle).findFirst().get()); } var s = values.sorted().skip(middle - 1).limit(2).iterator(); BigInteger a = s.next(); BigInteger b = s.next(); - return equalTo(NumericUtils.asLongUnsigned(a.add(b.subtract(a).divide(BigInteger.valueOf(2))))); + return equalTo(a.add(b.subtract(a).divide(BigInteger.valueOf(2)))); }); cases.add( diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinTests.java index 5556755cfe125..2e47f7c24bb54 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvMinTests.java @@ -14,7 +14,6 @@ import org.elasticsearch.xpack.ql.expression.Expression; import org.elasticsearch.xpack.ql.tree.Source; import org.elasticsearch.xpack.ql.type.DataType; -import org.elasticsearch.xpack.ql.util.NumericUtils; import java.math.BigInteger; import java.util.ArrayList; @@ -37,12 +36,7 @@ public static Iterable parameters() { doubles(cases, "mv_min", "MvMin", (size, values) -> equalTo(values.min().getAsDouble())); ints(cases, "mv_min", "MvMin", (size, values) -> equalTo(values.min().getAsInt())); longs(cases, "mv_min", "MvMin", (size, values) -> equalTo(values.min().getAsLong())); - unsignedLongs( - cases, - "mv_min", - "MvMin", - (size, values) -> equalTo(NumericUtils.asLongUnsigned(values.reduce(BigInteger::min).get())) - ); + unsignedLongs(cases, "mv_min", "MvMin", (size, values) -> equalTo(values.reduce(BigInteger::min).get())); dateTimes(cases, "mv_min", "MvMin", (size, values) -> equalTo(values.min().getAsLong())); return parameterSuppliersFromTypedData(errorsForCasesWithoutExamples(anyNullIsNull(false, cases))); } diff --git a/x-pack/plugin/ql/src/test/java/org/elasticsearch/xpack/ql/util/NumericUtilsTests.java b/x-pack/plugin/ql/src/test/java/org/elasticsearch/xpack/ql/util/NumericUtilsTests.java index 45ab0e732305c..80be578ed2647 100644 --- a/x-pack/plugin/ql/src/test/java/org/elasticsearch/xpack/ql/util/NumericUtilsTests.java +++ b/x-pack/plugin/ql/src/test/java/org/elasticsearch/xpack/ql/util/NumericUtilsTests.java @@ -13,6 +13,7 @@ import java.util.function.BiFunction; import static org.elasticsearch.xpack.ql.util.NumericUtils.asLongUnsigned; +import static org.elasticsearch.xpack.ql.util.NumericUtils.unsignedLongAsBigInteger; import static org.elasticsearch.xpack.ql.util.NumericUtils.unsignedLongAsNumber; import static org.elasticsearch.xpack.ql.util.StringUtils.parseIntegral; import static org.hamcrest.Matchers.equalTo; @@ -115,6 +116,11 @@ public void testUnsignedLongMultiplyExact() { assertThat(mulExact("0", "0"), equalTo("0")); } + public void testRoundTripConversion() { + BigInteger b = randomUnsignedLongBetween(BigInteger.ZERO, UNSIGNED_LONG_MAX); + assertThat(b, equalTo(unsignedLongAsBigInteger(asLongUnsigned(b)))); + } + private static String addExact(String x, String y) { return exactOperation(x, y, NumericUtils::unsignedLongAddExact); }