|
@@ -11,11 +11,17 @@ import com.carrotsearch.randomizedtesting.annotations.Name;
|
|
|
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
|
|
|
|
|
|
import org.elasticsearch.xpack.esql.expression.function.scalar.AbstractScalarFunctionTestCase;
|
|
|
+import org.elasticsearch.xpack.esql.type.EsqlDataTypes;
|
|
|
import org.elasticsearch.xpack.ql.expression.Expression;
|
|
|
+import org.elasticsearch.xpack.ql.expression.Literal;
|
|
|
import org.elasticsearch.xpack.ql.tree.Source;
|
|
|
import org.elasticsearch.xpack.ql.type.DataType;
|
|
|
import org.elasticsearch.xpack.ql.type.DataTypes;
|
|
|
|
|
|
+import java.time.Duration;
|
|
|
+import java.time.Period;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.Arrays;
|
|
|
import java.util.List;
|
|
|
import java.util.function.Supplier;
|
|
|
|
|
@@ -56,6 +62,22 @@ public class NegTests extends AbstractScalarFunctionTestCase {
|
|
|
DataTypes.DOUBLE,
|
|
|
equalTo(-arg)
|
|
|
);
|
|
|
+ }), new TestCaseSupplier("Duration", () -> {
|
|
|
+ Duration arg = (Duration) randomLiteral(EsqlDataTypes.TIME_DURATION).value();
|
|
|
+ return new TestCase(
|
|
|
+ List.of(new TypedData(arg, EsqlDataTypes.TIME_DURATION, "arg")),
|
|
|
+ "NegDurationEvaluator[v=Attribute[channel=0]]",
|
|
|
+ EsqlDataTypes.TIME_DURATION,
|
|
|
+ equalTo(arg.negated())
|
|
|
+ );
|
|
|
+ }), new TestCaseSupplier("Period", () -> {
|
|
|
+ Period arg = (Period) randomLiteral(EsqlDataTypes.DATE_PERIOD).value();
|
|
|
+ return new TestCase(
|
|
|
+ List.of(new TypedData(arg, EsqlDataTypes.DATE_PERIOD, "arg")),
|
|
|
+ "NegPeriodEvaluator[v=Attribute[channel=0]]",
|
|
|
+ EsqlDataTypes.DATE_PERIOD,
|
|
|
+ equalTo(arg.negated())
|
|
|
+ );
|
|
|
})));
|
|
|
}
|
|
|
|
|
@@ -67,7 +89,10 @@ public class NegTests extends AbstractScalarFunctionTestCase {
|
|
|
@Override
|
|
|
protected List<ArgumentSpec> argSpec() {
|
|
|
// More precisely: numerics without unsigned longs; however, `Neg::resolveType` uses `numeric`.
|
|
|
- return List.of(required(numerics()));
|
|
|
+ List<DataType> types = new ArrayList<>(Arrays.asList(numerics()));
|
|
|
+ types.add(EsqlDataTypes.DATE_PERIOD);
|
|
|
+ types.add(EsqlDataTypes.TIME_DURATION);
|
|
|
+ return List.of(required(types.toArray(DataType[]::new)));
|
|
|
}
|
|
|
|
|
|
@Override
|
|
@@ -112,14 +137,47 @@ public class NegTests extends AbstractScalarFunctionTestCase {
|
|
|
|
|
|
return;
|
|
|
}
|
|
|
+ if (testCaseType == EsqlDataTypes.DATE_PERIOD) {
|
|
|
+ Period minPeriod = Period.of(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE);
|
|
|
+ assertNull(process(minPeriod));
|
|
|
+ assertCriticalWarnings(
|
|
|
+ "Line -1:-1: evaluation of [] failed, treating result as null. Only first 20 failures recorded.",
|
|
|
+ "java.lang.ArithmeticException: integer overflow"
|
|
|
+ );
|
|
|
+
|
|
|
+ Period maxPeriod = Period.of(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);
|
|
|
+ Period negatedMaxPeriod = Period.of(-Integer.MAX_VALUE, -Integer.MAX_VALUE, -Integer.MAX_VALUE);
|
|
|
+ assertEquals(negatedMaxPeriod, process(maxPeriod));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (testCaseType == EsqlDataTypes.TIME_DURATION) {
|
|
|
+ Duration minDuration = Duration.ofSeconds(Long.MIN_VALUE, 0);
|
|
|
+ assertNull(process(minDuration));
|
|
|
+ assertCriticalWarnings(
|
|
|
+ "Line -1:-1: evaluation of [] failed, treating result as null. Only first 20 failures recorded.",
|
|
|
+ "java.lang.ArithmeticException: Exceeds capacity of Duration: 9223372036854775808000000000"
|
|
|
+ );
|
|
|
+
|
|
|
+ Duration maxDuration = Duration.ofSeconds(Long.MAX_VALUE, 0);
|
|
|
+ Duration negatedMaxDuration = Duration.ofSeconds(-Long.MAX_VALUE, 0);
|
|
|
+ assertEquals(negatedMaxDuration, process(maxDuration));
|
|
|
+
|
|
|
+ return;
|
|
|
+ }
|
|
|
throw new AssertionError("Edge cases not tested for negation with type [" + testCaseType.typeName() + "]");
|
|
|
}
|
|
|
|
|
|
- private Object process(Number val) {
|
|
|
- return toJavaObject(evaluator(new Neg(Source.EMPTY, field("val", typeOf(val)))).get().eval(row(List.of(val))), 0);
|
|
|
+ private Object process(Object val) {
|
|
|
+ if (testCase.allTypesAreRepresentable()) {
|
|
|
+ Neg neg = new Neg(Source.EMPTY, field("val", typeOf(val)));
|
|
|
+ return toJavaObject(evaluator(neg).get().eval(row(List.of(val))), 0);
|
|
|
+ } else { // just fold if type is not representable
|
|
|
+ Neg neg = new Neg(Source.EMPTY, new Literal(Source.EMPTY, val, typeOf(val)));
|
|
|
+ return neg.fold();
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- private DataType typeOf(Number val) {
|
|
|
+ private static DataType typeOf(Object val) {
|
|
|
if (val instanceof Integer) {
|
|
|
return DataTypes.INTEGER;
|
|
|
}
|
|
@@ -129,6 +187,12 @@ public class NegTests extends AbstractScalarFunctionTestCase {
|
|
|
if (val instanceof Double) {
|
|
|
return DataTypes.DOUBLE;
|
|
|
}
|
|
|
+ if (val instanceof Duration) {
|
|
|
+ return EsqlDataTypes.TIME_DURATION;
|
|
|
+ }
|
|
|
+ if (val instanceof Period) {
|
|
|
+ return EsqlDataTypes.DATE_PERIOD;
|
|
|
+ }
|
|
|
throw new UnsupportedOperationException("unsupported type [" + val.getClass() + "]");
|
|
|
}
|
|
|
}
|