diff --git a/processing/src/main/java/org/apache/druid/math/expr/ExprEval.java b/processing/src/main/java/org/apache/druid/math/expr/ExprEval.java index 72910836026..4bd29a19d0d 100644 --- a/processing/src/main/java/org/apache/druid/math/expr/ExprEval.java +++ b/processing/src/main/java/org/apache/druid/math/expr/ExprEval.java @@ -235,6 +235,9 @@ public abstract class ExprEval */ private static Class convertType(@Nullable Class existing, Class next) { + if (existing != null && existing.equals(Object.class)) { + return existing; + } if (Number.class.isAssignableFrom(next) || next == String.class || next == Boolean.class) { // coerce booleans if (next == Boolean.class) { @@ -866,7 +869,7 @@ public abstract class ExprEval @Override public Object[] asArray() { - return isNumericNull() ? null : new Object[] {value.doubleValue()}; + return value == null ? null : new Object[]{valueOrDefault().doubleValue()}; } @Override @@ -888,11 +891,13 @@ public abstract class ExprEval case DOUBLE: return ExprEval.ofDoubleArray(asArray()); case LONG: - return ExprEval.ofLongArray(value == null ? null : new Object[] {value.longValue()}); + return ExprEval.ofLongArray(value == null ? null : new Object[]{value.longValue()}); case STRING: - return ExprEval.ofStringArray(value == null ? null : new Object[] {value.toString()}); + return ExprEval.ofStringArray(value == null ? null : new Object[]{value.toString()}); + default: + ExpressionType elementType = (ExpressionType) castTo.getElementType(); + return new ArrayExprEval(castTo, new Object[]{castTo(elementType).value()}); } - break; case COMPLEX: if (ExpressionType.NESTED_DATA.equals(castTo)) { return new NestedDataExprEval(value); @@ -945,7 +950,7 @@ public abstract class ExprEval @Override public Object[] asArray() { - return isNumericNull() ? null : new Object[] {value.longValue()}; + return value == null ? null : new Object[]{valueOrDefault().longValue()}; } @Override @@ -963,15 +968,20 @@ public abstract class ExprEval case STRING: return ExprEval.of(asString()); case ARRAY: + if (value == null) { + return new ArrayExprEval(castTo, null); + } switch (castTo.getElementType().getType()) { case DOUBLE: - return ExprEval.ofDoubleArray(value == null ? null : new Object[] {value.doubleValue()}); + return ExprEval.ofDoubleArray(new Object[]{value.doubleValue()}); case LONG: return ExprEval.ofLongArray(asArray()); case STRING: - return ExprEval.ofStringArray(value == null ? null : new Object[] {value.toString()}); + return ExprEval.ofStringArray(new Object[]{value.toString()}); + default: + ExpressionType elementType = (ExpressionType) castTo.getElementType(); + return new ArrayExprEval(castTo, new Object[]{castTo(elementType).value()}); } - break; case COMPLEX: if (ExpressionType.NESTED_DATA.equals(castTo)) { return new NestedDataExprEval(value); @@ -1062,7 +1072,7 @@ public abstract class ExprEval @Override public Object[] asArray() { - return value == null ? null : new Object[] {value}; + return value == null ? null : new Object[]{value}; } private int computeInt() @@ -1134,18 +1144,24 @@ public abstract class ExprEval case STRING: return this; case ARRAY: + if (value == null) { + return new ArrayExprEval(castTo, null); + } final Number number = computeNumber(); switch (castTo.getElementType().getType()) { case DOUBLE: return ExprEval.ofDoubleArray( - value == null ? null : new Object[] {number == null ? null : number.doubleValue()} + new Object[]{number == null ? null : number.doubleValue()} ); case LONG: return ExprEval.ofLongArray( - value == null ? null : new Object[] {number == null ? null : number.longValue()} + new Object[]{number == null ? null : number.longValue()} ); case STRING: - return ExprEval.ofStringArray(value == null ? null : new Object[] {value}); + return ExprEval.ofStringArray(new Object[]{value}); + default: + ExpressionType elementType = (ExpressionType) castTo.getElementType(); + return new ArrayExprEval(castTo, new Object[]{castTo(elementType).value()}); } case COMPLEX: if (ExpressionType.NESTED_DATA.equals(castTo)) { diff --git a/processing/src/test/java/org/apache/druid/math/expr/EvalTest.java b/processing/src/test/java/org/apache/druid/math/expr/EvalTest.java index 45191ade72e..16ecc39c0cc 100644 --- a/processing/src/test/java/org/apache/druid/math/expr/EvalTest.java +++ b/processing/src/test/java/org/apache/druid/math/expr/EvalTest.java @@ -451,10 +451,30 @@ public class EvalTest extends InitializedNullHandlingTest ).asArray() ); - Assert.assertThrows(IAE.class, () -> ExprEval.ofLong(1234L).castTo(nestedArray)); - Assert.assertThrows(IAE.class, () -> ExprEval.of("hello").castTo(nestedArray)); - Assert.assertThrows(IAE.class, () -> ExprEval.ofDouble(1.234).castTo(nestedArray)); - Assert.assertThrows(IAE.class, () -> ExprEval.ofComplex(ExpressionType.NESTED_DATA, 1234L).castTo(nestedArray)); + cast = ExprEval.ofLong(1234L).castTo(nestedArray); + Assert.assertEquals(nestedArray, cast.type()); + Assert.assertArrayEquals( + new Object[]{1234L}, + cast.asArray() + ); + cast = ExprEval.of("hello").castTo(nestedArray); + Assert.assertEquals(nestedArray, cast.type()); + Assert.assertArrayEquals( + new Object[]{"hello"}, + cast.asArray() + ); + cast = ExprEval.ofDouble(1.234).castTo(nestedArray); + Assert.assertEquals(nestedArray, cast.type()); + Assert.assertArrayEquals( + new Object[]{1.234}, + cast.asArray() + ); + cast = ExprEval.ofComplex(ExpressionType.NESTED_DATA, 1234L).castTo(nestedArray); + Assert.assertArrayEquals( + new Object[]{1234L}, + cast.asArray() + ); + Assert.assertEquals(nestedArray, cast.type()); } @Test @@ -1146,145 +1166,145 @@ public class EvalTest extends InitializedNullHandlingTest Assert.assertEquals("notbase64", eval.value()); // arrays - eval = ExprEval.ofType(ExpressionType.LONG_ARRAY, new Object[] {1L, 2L, 3L}); + eval = ExprEval.ofType(ExpressionType.LONG_ARRAY, new Object[]{1L, 2L, 3L}); Assert.assertEquals(ExpressionType.LONG_ARRAY, eval.type()); - Assert.assertArrayEquals(new Object[] {1L, 2L, 3L}, (Object[]) eval.value()); + Assert.assertArrayEquals(new Object[]{1L, 2L, 3L}, (Object[]) eval.value()); eval = ExprEval.ofType(ExpressionType.LONG_ARRAY, ImmutableList.of(1L, 2L, 3L)); Assert.assertEquals(ExpressionType.LONG_ARRAY, eval.type()); - Assert.assertArrayEquals(new Object[] {1L, 2L, 3L}, (Object[]) eval.value()); + Assert.assertArrayEquals(new Object[]{1L, 2L, 3L}, (Object[]) eval.value()); eval = ExprEval.ofType(ExpressionType.LONG_ARRAY, new Long[]{1L, 2L, 3L}); Assert.assertEquals(ExpressionType.LONG_ARRAY, eval.type()); - Assert.assertArrayEquals(new Object[] {1L, 2L, 3L}, (Object[]) eval.value()); + Assert.assertArrayEquals(new Object[]{1L, 2L, 3L}, (Object[]) eval.value()); eval = ExprEval.ofType(ExpressionType.LONG_ARRAY, new long[]{1L, 2L, 3L}); Assert.assertEquals(ExpressionType.LONG_ARRAY, eval.type()); - Assert.assertArrayEquals(new Object[] {1L, 2L, 3L}, (Object[]) eval.value()); + Assert.assertArrayEquals(new Object[]{1L, 2L, 3L}, (Object[]) eval.value()); eval = ExprEval.ofType(ExpressionType.LONG_ARRAY, new int[]{1, 2, 3}); Assert.assertEquals(ExpressionType.LONG_ARRAY, eval.type()); - Assert.assertArrayEquals(new Object[] {1L, 2L, 3L}, (Object[]) eval.value()); + Assert.assertArrayEquals(new Object[]{1L, 2L, 3L}, (Object[]) eval.value()); - eval = ExprEval.ofType(ExpressionType.LONG_ARRAY, new Object[] {1L, 2L, null, 3L}); + eval = ExprEval.ofType(ExpressionType.LONG_ARRAY, new Object[]{1L, 2L, null, 3L}); Assert.assertEquals(ExpressionType.LONG_ARRAY, eval.type()); - Assert.assertArrayEquals(new Object[] {1L, 2L, null, 3L}, (Object[]) eval.value()); + Assert.assertArrayEquals(new Object[]{1L, 2L, null, 3L}, (Object[]) eval.value()); // arrays might have to fall back to using 'bestEffortOf', but will cast it to the expected output type - eval = ExprEval.ofType(ExpressionType.LONG_ARRAY, new Object[] {"1", "2", "3"}); + eval = ExprEval.ofType(ExpressionType.LONG_ARRAY, new Object[]{"1", "2", "3"}); Assert.assertEquals(ExpressionType.LONG_ARRAY, eval.type()); - Assert.assertArrayEquals(new Object[] {1L, 2L, 3L}, (Object[]) eval.value()); + Assert.assertArrayEquals(new Object[]{1L, 2L, 3L}, (Object[]) eval.value()); - eval = ExprEval.ofType(ExpressionType.LONG_ARRAY, new String[] {"1", "2", "3"}); + eval = ExprEval.ofType(ExpressionType.LONG_ARRAY, new String[]{"1", "2", "3"}); Assert.assertEquals(ExpressionType.LONG_ARRAY, eval.type()); - Assert.assertArrayEquals(new Object[] {1L, 2L, 3L}, (Object[]) eval.value()); + Assert.assertArrayEquals(new Object[]{1L, 2L, 3L}, (Object[]) eval.value()); - eval = ExprEval.ofType(ExpressionType.LONG_ARRAY, new Object[] {"1", "2", "wat", "3"}); + eval = ExprEval.ofType(ExpressionType.LONG_ARRAY, new Object[]{"1", "2", "wat", "3"}); Assert.assertEquals(ExpressionType.LONG_ARRAY, eval.type()); - Assert.assertArrayEquals(new Object[] {1L, 2L, null, 3L}, (Object[]) eval.value()); + Assert.assertArrayEquals(new Object[]{1L, 2L, null, 3L}, (Object[]) eval.value()); - eval = ExprEval.ofType(ExpressionType.LONG_ARRAY, new Object[] {1.0, 2.0, 3.0}); + eval = ExprEval.ofType(ExpressionType.LONG_ARRAY, new Object[]{1.0, 2.0, 3.0}); Assert.assertEquals(ExpressionType.LONG_ARRAY, eval.type()); - Assert.assertArrayEquals(new Object[] {1L, 2L, 3L}, (Object[]) eval.value()); + Assert.assertArrayEquals(new Object[]{1L, 2L, 3L}, (Object[]) eval.value()); - eval = ExprEval.ofType(ExpressionType.LONG_ARRAY, new double[] {1.0, 2.0, 3.0}); + eval = ExprEval.ofType(ExpressionType.LONG_ARRAY, new double[]{1.0, 2.0, 3.0}); Assert.assertEquals(ExpressionType.LONG_ARRAY, eval.type()); - Assert.assertArrayEquals(new Object[] {1L, 2L, 3L}, (Object[]) eval.value()); + Assert.assertArrayEquals(new Object[]{1L, 2L, 3L}, (Object[]) eval.value()); - eval = ExprEval.ofType(ExpressionType.LONG_ARRAY, new Object[] {1.0, 2.0, null, 3.0}); + eval = ExprEval.ofType(ExpressionType.LONG_ARRAY, new Object[]{1.0, 2.0, null, 3.0}); Assert.assertEquals(ExpressionType.LONG_ARRAY, eval.type()); - Assert.assertArrayEquals(new Object[] {1L, 2L, null, 3L}, (Object[]) eval.value()); + Assert.assertArrayEquals(new Object[]{1L, 2L, null, 3L}, (Object[]) eval.value()); - eval = ExprEval.ofType(ExpressionType.LONG_ARRAY, new Object[] {1.0, 2L, "3", true, false}); + eval = ExprEval.ofType(ExpressionType.LONG_ARRAY, new Object[]{1.0, 2L, "3", true, false}); Assert.assertEquals(ExpressionType.LONG_ARRAY, eval.type()); - Assert.assertArrayEquals(new Object[] {1L, 2L, 3L, 1L, 0L}, (Object[]) eval.value()); + Assert.assertArrayEquals(new Object[]{1L, 2L, 3L, 1L, 0L}, (Object[]) eval.value()); - eval = ExprEval.ofType(ExpressionType.LONG_ARRAY, new float[] {1.0f, 2.0f, 3.0f}); + eval = ExprEval.ofType(ExpressionType.LONG_ARRAY, new float[]{1.0f, 2.0f, 3.0f}); Assert.assertEquals(ExpressionType.LONG_ARRAY, eval.type()); - Assert.assertArrayEquals(new Object[] {1L, 2L, 3L}, (Object[]) eval.value()); + Assert.assertArrayEquals(new Object[]{1L, 2L, 3L}, (Object[]) eval.value()); // etc - eval = ExprEval.ofType(ExpressionType.DOUBLE_ARRAY, new Object[] {1.0, 2.0, 3.0}); + eval = ExprEval.ofType(ExpressionType.DOUBLE_ARRAY, new Object[]{1.0, 2.0, 3.0}); Assert.assertEquals(ExpressionType.DOUBLE_ARRAY, eval.type()); - Assert.assertArrayEquals(new Object[] {1.0, 2.0, 3.0}, (Object[]) eval.value()); + Assert.assertArrayEquals(new Object[]{1.0, 2.0, 3.0}, (Object[]) eval.value()); - eval = ExprEval.ofType(ExpressionType.DOUBLE_ARRAY, new Double[] {1.0, 2.0, 3.0}); + eval = ExprEval.ofType(ExpressionType.DOUBLE_ARRAY, new Double[]{1.0, 2.0, 3.0}); Assert.assertEquals(ExpressionType.DOUBLE_ARRAY, eval.type()); - Assert.assertArrayEquals(new Object[] {1.0, 2.0, 3.0}, (Object[]) eval.value()); + Assert.assertArrayEquals(new Object[]{1.0, 2.0, 3.0}, (Object[]) eval.value()); - eval = ExprEval.ofType(ExpressionType.DOUBLE_ARRAY, new double[] {1.0, 2.0, 3.0}); + eval = ExprEval.ofType(ExpressionType.DOUBLE_ARRAY, new double[]{1.0, 2.0, 3.0}); Assert.assertEquals(ExpressionType.DOUBLE_ARRAY, eval.type()); - Assert.assertArrayEquals(new Object[] {1.0, 2.0, 3.0}, (Object[]) eval.value()); + Assert.assertArrayEquals(new Object[]{1.0, 2.0, 3.0}, (Object[]) eval.value()); - eval = ExprEval.ofType(ExpressionType.DOUBLE_ARRAY, new Object[] {"1", "2", "3"}); + eval = ExprEval.ofType(ExpressionType.DOUBLE_ARRAY, new Object[]{"1", "2", "3"}); Assert.assertEquals(ExpressionType.DOUBLE_ARRAY, eval.type()); - Assert.assertArrayEquals(new Object[] {1.0, 2.0, 3.0}, (Object[]) eval.value()); + Assert.assertArrayEquals(new Object[]{1.0, 2.0, 3.0}, (Object[]) eval.value()); - eval = ExprEval.ofType(ExpressionType.DOUBLE_ARRAY, new Object[] {"1", "2", "wat", "3"}); + eval = ExprEval.ofType(ExpressionType.DOUBLE_ARRAY, new Object[]{"1", "2", "wat", "3"}); Assert.assertEquals(ExpressionType.DOUBLE_ARRAY, eval.type()); - Assert.assertArrayEquals(new Object[] {1.0, 2.0, null, 3.0}, (Object[]) eval.value()); + Assert.assertArrayEquals(new Object[]{1.0, 2.0, null, 3.0}, (Object[]) eval.value()); - eval = ExprEval.ofType(ExpressionType.DOUBLE_ARRAY, new Object[] {1L, 2L, 3L}); + eval = ExprEval.ofType(ExpressionType.DOUBLE_ARRAY, new Object[]{1L, 2L, 3L}); Assert.assertEquals(ExpressionType.DOUBLE_ARRAY, eval.type()); - Assert.assertArrayEquals(new Object[] {1.0, 2.0, 3.0}, (Object[]) eval.value()); + Assert.assertArrayEquals(new Object[]{1.0, 2.0, 3.0}, (Object[]) eval.value()); - eval = ExprEval.ofType(ExpressionType.DOUBLE_ARRAY, new long[] {1L, 2L, 3L}); + eval = ExprEval.ofType(ExpressionType.DOUBLE_ARRAY, new long[]{1L, 2L, 3L}); Assert.assertEquals(ExpressionType.DOUBLE_ARRAY, eval.type()); - Assert.assertArrayEquals(new Object[] {1.0, 2.0, 3.0}, (Object[]) eval.value()); + Assert.assertArrayEquals(new Object[]{1.0, 2.0, 3.0}, (Object[]) eval.value()); - eval = ExprEval.ofType(ExpressionType.DOUBLE_ARRAY, new Object[] {1L, 2L, null, 3L}); + eval = ExprEval.ofType(ExpressionType.DOUBLE_ARRAY, new Object[]{1L, 2L, null, 3L}); Assert.assertEquals(ExpressionType.DOUBLE_ARRAY, eval.type()); - Assert.assertArrayEquals(new Object[] {1.0, 2.0, null, 3.0}, (Object[]) eval.value()); + Assert.assertArrayEquals(new Object[]{1.0, 2.0, null, 3.0}, (Object[]) eval.value()); - eval = ExprEval.ofType(ExpressionType.DOUBLE_ARRAY, new Object[] {1.0, 2L, "3", true, false}); + eval = ExprEval.ofType(ExpressionType.DOUBLE_ARRAY, new Object[]{1.0, 2L, "3", true, false}); Assert.assertEquals(ExpressionType.DOUBLE_ARRAY, eval.type()); - Assert.assertArrayEquals(new Object[] {1.0, 2.0, 3.0, 1.0, 0.0}, (Object[]) eval.value()); + Assert.assertArrayEquals(new Object[]{1.0, 2.0, 3.0, 1.0, 0.0}, (Object[]) eval.value()); - eval = ExprEval.ofType(ExpressionType.DOUBLE_ARRAY, new Float[] {1.0f, 2.0f, 3.0f}); + eval = ExprEval.ofType(ExpressionType.DOUBLE_ARRAY, new Float[]{1.0f, 2.0f, 3.0f}); Assert.assertEquals(ExpressionType.DOUBLE_ARRAY, eval.type()); - Assert.assertArrayEquals(new Object[] {1.0, 2.0, 3.0}, (Object[]) eval.value()); + Assert.assertArrayEquals(new Object[]{1.0, 2.0, 3.0}, (Object[]) eval.value()); - eval = ExprEval.ofType(ExpressionType.DOUBLE_ARRAY, new float[] {1.0f, 2.0f, 3.0f}); + eval = ExprEval.ofType(ExpressionType.DOUBLE_ARRAY, new float[]{1.0f, 2.0f, 3.0f}); Assert.assertEquals(ExpressionType.DOUBLE_ARRAY, eval.type()); - Assert.assertArrayEquals(new Object[] {1.0, 2.0, 3.0}, (Object[]) eval.value()); + Assert.assertArrayEquals(new Object[]{1.0, 2.0, 3.0}, (Object[]) eval.value()); - eval = ExprEval.ofType(ExpressionType.STRING_ARRAY, new Object[] {"1", "2", "3"}); + eval = ExprEval.ofType(ExpressionType.STRING_ARRAY, new Object[]{"1", "2", "3"}); Assert.assertEquals(ExpressionType.STRING_ARRAY, eval.type()); - Assert.assertArrayEquals(new Object[] {"1", "2", "3"}, (Object[]) eval.value()); + Assert.assertArrayEquals(new Object[]{"1", "2", "3"}, (Object[]) eval.value()); - eval = ExprEval.ofType(ExpressionType.STRING_ARRAY, new Object[] {1L, 2L, 3L}); + eval = ExprEval.ofType(ExpressionType.STRING_ARRAY, new Object[]{1L, 2L, 3L}); Assert.assertEquals(ExpressionType.STRING_ARRAY, eval.type()); - Assert.assertArrayEquals(new Object[] {"1", "2", "3"}, (Object[]) eval.value()); + Assert.assertArrayEquals(new Object[]{"1", "2", "3"}, (Object[]) eval.value()); - eval = ExprEval.ofType(ExpressionType.STRING_ARRAY, new Object[] {1.0, 2.0, 3.0}); + eval = ExprEval.ofType(ExpressionType.STRING_ARRAY, new Object[]{1.0, 2.0, 3.0}); Assert.assertEquals(ExpressionType.STRING_ARRAY, eval.type()); - Assert.assertArrayEquals(new Object[] {"1.0", "2.0", "3.0"}, (Object[]) eval.value()); + Assert.assertArrayEquals(new Object[]{"1.0", "2.0", "3.0"}, (Object[]) eval.value()); - eval = ExprEval.ofType(ExpressionType.STRING_ARRAY, new Object[] {1.0, 2L, "3", true, false}); + eval = ExprEval.ofType(ExpressionType.STRING_ARRAY, new Object[]{1.0, 2L, "3", true, false}); Assert.assertEquals(ExpressionType.STRING_ARRAY, eval.type()); - Assert.assertArrayEquals(new Object[] {"1.0", "2", "3", "true", "false"}, (Object[]) eval.value()); + Assert.assertArrayEquals(new Object[]{"1.0", "2", "3", "true", "false"}, (Object[]) eval.value()); // nested arrays ExpressionType nestedLongArray = ExpressionTypeFactory.getInstance().ofArray(ExpressionType.LONG_ARRAY); final Object[] expectedLongArray = new Object[]{ - new Object[] {1L, 2L, 3L}, - new Object[] {5L, null, 9L}, + new Object[]{1L, 2L, 3L}, + new Object[]{5L, null, 9L}, null, - new Object[] {2L, 4L, 6L} + new Object[]{2L, 4L, 6L} }; List longArrayInputs = Arrays.asList( new Object[]{ - new Object[] {1L, 2L, 3L}, - new Object[] {5L, null, 9L}, + new Object[]{1L, 2L, 3L}, + new Object[]{5L, null, 9L}, null, - new Object[] {2L, 4L, 6L} + new Object[]{2L, 4L, 6L} }, Arrays.asList( - new Object[] {1L, 2L, 3L}, - new Object[] {5L, null, 9L}, + new Object[]{1L, 2L, 3L}, + new Object[]{5L, null, 9L}, null, - new Object[] {2L, 4L, 6L} + new Object[]{2L, 4L, 6L} ), Arrays.asList( Arrays.asList(1L, 2L, 3L), @@ -1312,18 +1332,18 @@ public class EvalTest extends InitializedNullHandlingTest ExpressionType nestedDoubleArray = ExpressionTypeFactory.getInstance().ofArray(ExpressionType.DOUBLE_ARRAY); final Object[] expectedDoubleArray = new Object[]{ - new Object[] {1.1, 2.2, 3.3}, - new Object[] {5.5, null, 9.9}, + new Object[]{1.1, 2.2, 3.3}, + new Object[]{5.5, null, 9.9}, null, - new Object[] {2.2, 4.4, 6.6} + new Object[]{2.2, 4.4, 6.6} }; List doubleArrayInputs = Arrays.asList( new Object[]{ - new Object[] {1.1, 2.2, 3.3}, - new Object[] {5.5, null, 9.9}, + new Object[]{1.1, 2.2, 3.3}, + new Object[]{5.5, null, 9.9}, null, - new Object[] {2.2, 4.4, 6.6} + new Object[]{2.2, 4.4, 6.6} }, new Object[]{ Arrays.asList(1.1, 2.2, 3.3), @@ -1338,10 +1358,10 @@ public class EvalTest extends InitializedNullHandlingTest Arrays.asList(2.2, 4.4, 6.6) ), new Object[]{ - new Object[] {"1.1", "2.2", "3.3"}, + new Object[]{"1.1", "2.2", "3.3"}, Arrays.asList("5.5", null, "9.9"), null, - new String[] {"2.2", "4.4", "6.6"} + new String[]{"2.2", "4.4", "6.6"} } ); @@ -1392,38 +1412,38 @@ public class EvalTest extends InitializedNullHandlingTest assertBestEffortOf(1.0f, ExpressionType.DOUBLE, 1.0); // arrays - assertBestEffortOf(new Object[] {1L, 2L, 3L}, ExpressionType.LONG_ARRAY, new Object[] {1L, 2L, 3L}); - assertBestEffortOf(new Object[] {1L, 2L, null, 3L}, ExpressionType.LONG_ARRAY, new Object[] {1L, 2L, null, 3L}); - assertBestEffortOf(ImmutableList.of(1L, 2L, 3L), ExpressionType.LONG_ARRAY, new Object[] {1L, 2L, 3L}); - assertBestEffortOf(new long[] {1L, 2L, 3L}, ExpressionType.LONG_ARRAY, new Object[] {1L, 2L, 3L}); - assertBestEffortOf(new Object[] {1, 2, 3}, ExpressionType.LONG_ARRAY, new Object[] {1L, 2L, 3L}); - assertBestEffortOf(new Integer[] {1, 2, 3}, ExpressionType.LONG_ARRAY, new Object[] {1L, 2L, 3L}); - assertBestEffortOf(new int[] {1, 2, 3}, ExpressionType.LONG_ARRAY, new Object[] {1L, 2L, 3L}); + assertBestEffortOf(new Object[]{1L, 2L, 3L}, ExpressionType.LONG_ARRAY, new Object[]{1L, 2L, 3L}); + assertBestEffortOf(new Object[]{1L, 2L, null, 3L}, ExpressionType.LONG_ARRAY, new Object[]{1L, 2L, null, 3L}); + assertBestEffortOf(ImmutableList.of(1L, 2L, 3L), ExpressionType.LONG_ARRAY, new Object[]{1L, 2L, 3L}); + assertBestEffortOf(new long[]{1L, 2L, 3L}, ExpressionType.LONG_ARRAY, new Object[]{1L, 2L, 3L}); + assertBestEffortOf(new Object[]{1, 2, 3}, ExpressionType.LONG_ARRAY, new Object[]{1L, 2L, 3L}); + assertBestEffortOf(new Integer[]{1, 2, 3}, ExpressionType.LONG_ARRAY, new Object[]{1L, 2L, 3L}); + assertBestEffortOf(new int[]{1, 2, 3}, ExpressionType.LONG_ARRAY, new Object[]{1L, 2L, 3L}); - assertBestEffortOf(new Object[] {1.0, 2.0, 3.0}, ExpressionType.DOUBLE_ARRAY, new Object[] {1.0, 2.0, 3.0}); + assertBestEffortOf(new Object[]{1.0, 2.0, 3.0}, ExpressionType.DOUBLE_ARRAY, new Object[]{1.0, 2.0, 3.0}); assertBestEffortOf( - new Object[] {null, 1.0, 2.0, 3.0}, + new Object[]{null, 1.0, 2.0, 3.0}, ExpressionType.DOUBLE_ARRAY, - new Object[] {null, 1.0, 2.0, 3.0} + new Object[]{null, 1.0, 2.0, 3.0} ); - assertBestEffortOf(new Double[] {1.0, 2.0, 3.0}, ExpressionType.DOUBLE_ARRAY, new Object[] {1.0, 2.0, 3.0}); - assertBestEffortOf(new double[] {1.0, 2.0, 3.0}, ExpressionType.DOUBLE_ARRAY, new Object[] {1.0, 2.0, 3.0}); - assertBestEffortOf(new Object[] {1.0f, 2.0f, 3.0f}, ExpressionType.DOUBLE_ARRAY, new Object[] {1.0, 2.0, 3.0}); - assertBestEffortOf(new Float[] {1.0f, 2.0f, 3.0f}, ExpressionType.DOUBLE_ARRAY, new Object[] {1.0, 2.0, 3.0}); - assertBestEffortOf(new float[] {1.0f, 2.0f, 3.0f}, ExpressionType.DOUBLE_ARRAY, new Object[] {1.0, 2.0, 3.0}); + assertBestEffortOf(new Double[]{1.0, 2.0, 3.0}, ExpressionType.DOUBLE_ARRAY, new Object[]{1.0, 2.0, 3.0}); + assertBestEffortOf(new double[]{1.0, 2.0, 3.0}, ExpressionType.DOUBLE_ARRAY, new Object[]{1.0, 2.0, 3.0}); + assertBestEffortOf(new Object[]{1.0f, 2.0f, 3.0f}, ExpressionType.DOUBLE_ARRAY, new Object[]{1.0, 2.0, 3.0}); + assertBestEffortOf(new Float[]{1.0f, 2.0f, 3.0f}, ExpressionType.DOUBLE_ARRAY, new Object[]{1.0, 2.0, 3.0}); + assertBestEffortOf(new float[]{1.0f, 2.0f, 3.0f}, ExpressionType.DOUBLE_ARRAY, new Object[]{1.0, 2.0, 3.0}); - assertBestEffortOf(new Object[] {"1", "2", "3"}, ExpressionType.STRING_ARRAY, new Object[] {"1", "2", "3"}); - assertBestEffortOf(new String[] {"1", "2", "3"}, ExpressionType.STRING_ARRAY, new Object[] {"1", "2", "3"}); - assertBestEffortOf(ImmutableList.of("1", "2", "3"), ExpressionType.STRING_ARRAY, new Object[] {"1", "2", "3"}); + assertBestEffortOf(new Object[]{"1", "2", "3"}, ExpressionType.STRING_ARRAY, new Object[]{"1", "2", "3"}); + assertBestEffortOf(new String[]{"1", "2", "3"}, ExpressionType.STRING_ARRAY, new Object[]{"1", "2", "3"}); + assertBestEffortOf(ImmutableList.of("1", "2", "3"), ExpressionType.STRING_ARRAY, new Object[]{"1", "2", "3"}); // arrays end up as the least restrictive type - assertBestEffortOf(new Object[] {1.0, 2L}, ExpressionType.DOUBLE_ARRAY, new Object[] {1.0, 2.0}); + assertBestEffortOf(new Object[]{1.0, 2L}, ExpressionType.DOUBLE_ARRAY, new Object[]{1.0, 2.0}); // arrays end up as the least restrictive type assertBestEffortOf( - new Object[] {1.0, 2L, "3", true, false}, + new Object[]{1.0, 2L, "3", true, false}, ExpressionType.STRING_ARRAY, - new Object[] {"1.0", "2", "3", "true", "false"} + new Object[]{"1.0", "2", "3", "true", "false"} ); assertBestEffortOf( diff --git a/processing/src/test/java/org/apache/druid/math/expr/ExprEvalTest.java b/processing/src/test/java/org/apache/druid/math/expr/ExprEvalTest.java index 99acecd3099..e624ed04e50 100644 --- a/processing/src/test/java/org/apache/druid/math/expr/ExprEvalTest.java +++ b/processing/src/test/java/org/apache/druid/math/expr/ExprEvalTest.java @@ -22,6 +22,7 @@ package org.apache.druid.math.expr; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import org.apache.druid.collections.SerializablePair; +import org.apache.druid.java.util.common.IAE; import org.apache.druid.java.util.common.ISE; import org.apache.druid.java.util.common.NonnullPair; import org.apache.druid.java.util.common.StringUtils; @@ -333,6 +334,49 @@ public class ExprEvalTest extends InitializedNullHandlingTest coerced.rhs ); + Map nested1 = ImmutableMap.of("x", 1L, "y", 2L); + List mixedObject = ImmutableList.of( + "a", + 1L, + 3.0, + nested1 + ); + coerced = ExprEval.coerceListToArray(mixedObject, false); + Assert.assertEquals( + ExpressionTypeFactory.getInstance().ofArray(ExpressionType.NESTED_DATA), + coerced.lhs + ); + Assert.assertArrayEquals( + new Object[]{ + "a", + 1L, + 3.0, + nested1 + }, + coerced.rhs + ); + + List mixedObject2 = ImmutableList.of( + nested1, + "a", + 1L, + 3.0 + ); + coerced = ExprEval.coerceListToArray(mixedObject2, false); + Assert.assertEquals( + ExpressionTypeFactory.getInstance().ofArray(ExpressionType.NESTED_DATA), + coerced.lhs + ); + Assert.assertArrayEquals( + new Object[]{ + nested1, + "a", + 1L, + 3.0 + }, + coerced.rhs + ); + List> nestedLists = ImmutableList.of( ImmutableList.of("a", "b", "c"), ImmutableList.of("d", "e", "f") @@ -344,7 +388,7 @@ public class ExprEvalTest extends InitializedNullHandlingTest coerced.rhs ); - Map nested1 = ImmutableMap.of("x", 1L, "y", 2L); + Map nested2 = ImmutableMap.of("x", 4L, "y", 5L); List> listUnknownComplex = ImmutableList.of(nested1, nested2); coerced = ExprEval.coerceListToArray(listUnknownComplex, false); @@ -421,6 +465,66 @@ public class ExprEvalTest extends InitializedNullHandlingTest coerced.rhs ); + List mixedNested2 = ImmutableList.of( + "a", + 1L, + 3.0, + ImmutableList.of("a", "b", "c"), + ImmutableList.of(1L, 2L, 3L), + ImmutableList.of(3.0, 4.0, 5.0), + ImmutableList.of(nested1, nested2, nested3) + ); + coerced = ExprEval.coerceListToArray(mixedNested2, false); + Assert.assertEquals( + ExpressionTypeFactory.getInstance().ofArray( + ExpressionTypeFactory.getInstance().ofArray(ExpressionType.NESTED_DATA) + ), + coerced.lhs + ); + Assert.assertArrayEquals( + new Object[]{ + new Object[]{"a"}, + new Object[]{1L}, + new Object[]{3.0}, + new Object[]{"a", "b", "c"}, + new Object[]{1L, 2L, 3L}, + new Object[]{3.0, 4.0, 5.0}, + new Object[]{nested1, nested2, nested3} + }, + coerced.rhs + ); + + + List mixedNested3 = ImmutableList.of( + "a", + 1L, + 3.0, + nested1, + ImmutableList.of("a", "b", "c"), + ImmutableList.of(1L, 2L, 3L), + ImmutableList.of(3.0, 4.0, 5.0), + ImmutableList.of(nested1, nested2, nested3) + ); + coerced = ExprEval.coerceListToArray(mixedNested3, false); + // this one is only ARRAY> instead of ARRAY> because of a COMPLEX element.. + Assert.assertEquals( + ExpressionTypeFactory.getInstance().ofArray(ExpressionType.NESTED_DATA), + coerced.lhs + ); + Assert.assertArrayEquals( + new Object[]{ + "a", + 1L, + 3.0, + nested1, + new Object[]{"a", "b", "c"}, + new Object[]{1L, 2L, 3L}, + new Object[]{3.0, 4.0, 5.0}, + new Object[]{nested1, nested2, nested3} + }, + coerced.rhs + ); + List> mixedUnknown = ImmutableList.of( ImmutableList.of("a", "b", "c"), ImmutableList.of(1L, 2L, 3L), @@ -435,17 +539,209 @@ public class ExprEvalTest extends InitializedNullHandlingTest } @Test - public void testStringArrayToNumberArray() + public void testCastString() { - ExprEval someStringArray = ExprEval.ofStringArray(new String[]{"1", "2", "foo", null, "3.3"}); - Assert.assertArrayEquals( - new Object[]{1L, 2L, null, null, 3L}, - someStringArray.castTo(ExpressionType.LONG_ARRAY).asArray() - ); - Assert.assertArrayEquals( - new Object[]{1.0, 2.0, null, null, 3.3}, - someStringArray.castTo(ExpressionType.DOUBLE_ARRAY).asArray() + ExprEval eval = ExprEval.of("hello"); + + ExprEval cast = eval.castTo(ExpressionType.DOUBLE); + Assert.assertNull(cast.value()); + + cast = eval.castTo(ExpressionType.LONG); + Assert.assertNull(cast.value()); + + cast = eval.castTo(ExpressionType.STRING_ARRAY); + Assert.assertArrayEquals(new Object[]{"hello"}, (Object[]) cast.value()); + + cast = eval.castTo(ExpressionType.LONG_ARRAY); + Assert.assertArrayEquals(new Object[]{null}, (Object[]) cast.value()); + + cast = eval.castTo(ExpressionType.DOUBLE_ARRAY); + Assert.assertArrayEquals(new Object[]{null}, (Object[]) cast.value()); + + cast = eval.castTo(ExpressionType.NESTED_DATA); + Assert.assertEquals("hello", cast.value()); + + cast = eval.castTo(ExpressionTypeFactory.getInstance().ofArray(ExpressionType.NESTED_DATA)); + Assert.assertArrayEquals(new Object[]{"hello"}, (Object[]) cast.value()); + + eval = ExprEval.of("1234.3"); + + cast = eval.castTo(ExpressionType.DOUBLE); + Assert.assertEquals(1234.3, cast.value()); + + cast = eval.castTo(ExpressionType.LONG); + Assert.assertEquals(1234L, cast.value()); + + cast = eval.castTo(ExpressionType.DOUBLE_ARRAY); + Assert.assertArrayEquals(new Object[]{1234.3}, (Object[]) cast.value()); + + cast = eval.castTo(ExpressionType.LONG_ARRAY); + Assert.assertArrayEquals(new Object[]{1234L}, (Object[]) cast.value()); + + eval = ExprEval.ofType(ExpressionType.STRING, null); + + cast = eval.castTo(ExpressionType.DOUBLE); + Assert.assertNull(cast.value()); + + cast = eval.castTo(ExpressionType.LONG); + Assert.assertNull(cast.value()); + + cast = eval.castTo(ExpressionType.STRING_ARRAY); + Assert.assertNull(cast.value()); + + cast = eval.castTo(ExpressionType.LONG_ARRAY); + Assert.assertNull(cast.value()); + + cast = eval.castTo(ExpressionType.DOUBLE_ARRAY); + Assert.assertNull(cast.value()); + + cast = eval.castTo(ExpressionType.NESTED_DATA); + Assert.assertNull(cast.value()); + } + + @Test + public void testCastDouble() + { + ExprEval eval = ExprEval.of(123.4); + + ExprEval cast = eval.castTo(ExpressionType.STRING); + Assert.assertEquals("123.4", cast.value()); + + cast = eval.castTo(ExpressionType.LONG); + Assert.assertEquals(123L, cast.value()); + + cast = eval.castTo(ExpressionType.STRING_ARRAY); + Assert.assertArrayEquals(new Object[]{"123.4"}, (Object[]) cast.value()); + + cast = eval.castTo(ExpressionType.LONG_ARRAY); + Assert.assertArrayEquals(new Object[]{123L}, (Object[]) cast.value()); + + cast = eval.castTo(ExpressionType.DOUBLE_ARRAY); + Assert.assertArrayEquals(new Object[]{123.4}, (Object[]) cast.value()); + + cast = eval.castTo(ExpressionType.NESTED_DATA); + Assert.assertEquals(123.4, cast.value()); + + eval = ExprEval.ofType(ExpressionType.DOUBLE, null); + + cast = eval.castTo(ExpressionType.STRING); + Assert.assertNull(cast.value()); + + cast = eval.castTo(ExpressionType.LONG); + Assert.assertNull(cast.value()); + + cast = eval.castTo(ExpressionType.STRING_ARRAY); + Assert.assertNull(cast.value()); + + cast = eval.castTo(ExpressionType.LONG_ARRAY); + Assert.assertNull(cast.value()); + + cast = eval.castTo(ExpressionType.DOUBLE_ARRAY); + Assert.assertNull(cast.value()); + + cast = eval.castTo(ExpressionType.NESTED_DATA); + Assert.assertNull(cast.value()); + } + + @Test + public void testCastLong() + { + ExprEval eval = ExprEval.of(1234L); + + ExprEval cast = eval.castTo(ExpressionType.STRING); + Assert.assertEquals("1234", cast.value()); + + cast = eval.castTo(ExpressionType.DOUBLE); + Assert.assertEquals(1234.0, cast.value()); + + cast = eval.castTo(ExpressionType.STRING_ARRAY); + Assert.assertArrayEquals(new Object[]{"1234"}, (Object[]) cast.value()); + + cast = eval.castTo(ExpressionType.LONG_ARRAY); + Assert.assertArrayEquals(new Object[]{1234L}, (Object[]) cast.value()); + + cast = eval.castTo(ExpressionType.DOUBLE_ARRAY); + Assert.assertArrayEquals(new Object[]{1234.0}, (Object[]) cast.value()); + + cast = eval.castTo(ExpressionType.NESTED_DATA); + Assert.assertEquals(1234L, cast.value()); + + eval = ExprEval.ofType(ExpressionType.LONG, null); + + cast = eval.castTo(ExpressionType.STRING); + Assert.assertNull(cast.value()); + + cast = eval.castTo(ExpressionType.LONG); + Assert.assertNull(cast.value()); + + cast = eval.castTo(ExpressionType.STRING_ARRAY); + Assert.assertNull(cast.value()); + + cast = eval.castTo(ExpressionType.LONG_ARRAY); + Assert.assertNull(cast.value()); + + cast = eval.castTo(ExpressionType.DOUBLE_ARRAY); + Assert.assertNull(cast.value()); + + cast = eval.castTo(ExpressionType.NESTED_DATA); + Assert.assertNull(cast.value()); + } + + @Test + public void testCastArray() + { + ExprEval eval = ExprEval.ofStringArray(new String[]{"1", "2", "foo", null, "3.3"}); + + ExprEval cast = eval.castTo(ExpressionType.LONG_ARRAY); + Assert.assertArrayEquals(new Object[]{1L, 2L, null, null, 3L}, (Object[]) cast.value()); + + cast = eval.castTo(ExpressionType.DOUBLE_ARRAY); + Assert.assertArrayEquals(new Object[]{1.0, 2.0, null, null, 3.3}, (Object[]) cast.value()); + + cast = eval.castTo(ExpressionType.NESTED_DATA); + Assert.assertArrayEquals(new Object[]{"1", "2", "foo", null, "3.3"}, (Object[]) cast.value()); + + cast = eval.castTo(ExpressionTypeFactory.getInstance().ofArray(ExpressionType.NESTED_DATA)); + Assert.assertArrayEquals(new Object[]{"1", "2", "foo", null, "3.3"}, (Object[]) cast.value()); + + ExprEval finalEval = eval; + Throwable t = Assert.assertThrows(IAE.class, () -> finalEval.castTo(ExpressionType.LONG)); + Assert.assertEquals("Invalid type, cannot cast [ARRAY] to [LONG]", t.getMessage()); + + t = Assert.assertThrows(IAE.class, () -> finalEval.castTo(ExpressionType.DOUBLE)); + Assert.assertEquals("Invalid type, cannot cast [ARRAY] to [DOUBLE]", t.getMessage()); + + t = Assert.assertThrows(IAE.class, () -> finalEval.castTo(ExpressionType.STRING)); + Assert.assertEquals("Invalid type, cannot cast [ARRAY] to [STRING]", t.getMessage()); + + eval = ExprEval.ofType(ExpressionType.LONG_ARRAY, new Object[]{1234L}); + + cast = eval.castTo(ExpressionType.DOUBLE_ARRAY); + Assert.assertArrayEquals(new Object[]{1234.0}, (Object[]) cast.value()); + + cast = eval.castTo(ExpressionType.STRING_ARRAY); + Assert.assertArrayEquals(new Object[]{"1234"}, (Object[]) cast.value()); + + cast = eval.castTo(ExpressionType.STRING); + Assert.assertEquals("1234", cast.value()); + + cast = eval.castTo(ExpressionType.DOUBLE); + Assert.assertEquals(1234.0, cast.value()); + + cast = eval.castTo(ExpressionType.LONG); + Assert.assertEquals(1234L, cast.value()); + } + + @Test + public void testCastNestedData() + { + ExprEval eval = ExprEval.ofType(ExpressionType.NESTED_DATA, ImmutableMap.of("x", 1234L, "y", 12.34)); + Assert.assertEquals( + ImmutableMap.of("x", 1234L, "y", 12.34), + eval.castTo(ExpressionType.NESTED_DATA).value() ); + Throwable t = Assert.assertThrows(IAE.class, () -> eval.castTo(ExpressionType.LONG)); + Assert.assertEquals("Invalid type, cannot cast [COMPLEX] to [LONG]", t.getMessage()); } @Test