blob: a5a9fce54ea25330a800dc0a56ad891995faa35d [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.druid.math.expr;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import org.apache.druid.common.config.NullHandling;
import org.apache.druid.java.util.common.IAE;
import org.apache.druid.java.util.common.Pair;
import org.apache.druid.java.util.common.StringUtils;
import org.apache.druid.testing.InitializedNullHandlingTest;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import javax.annotation.Nullable;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Locale;
import java.util.Set;
public class FunctionTest extends InitializedNullHandlingTest
{
@Rule
public ExpectedException expectedException = ExpectedException.none();
private Expr.ObjectBinding bindings;
@Before
public void setup()
{
ImmutableMap.Builder<String, Object> builder = ImmutableMap.<String, Object>builder()
.put("x", "foo")
.put("y", 2)
.put("z", 3.1)
.put("d", 34.56D)
.put("maxLong", Long.MAX_VALUE)
.put("minLong", Long.MIN_VALUE)
.put("f", 12.34F)
.put("nan", Double.NaN)
.put("inf", Double.POSITIVE_INFINITY)
.put("-inf", Double.NEGATIVE_INFINITY)
.put("o", 0)
.put("od", 0D)
.put("of", 0F)
.put("a", new String[] {"foo", "bar", "baz", "foobar"})
.put("b", new Long[] {1L, 2L, 3L, 4L, 5L})
.put("c", new Double[] {3.1, 4.2, 5.3});
bindings = InputBindings.withMap(builder.build());
}
@Test
public void testCaseSimple()
{
assertExpr("case_simple(x,'baz','is baz','foo','is foo','is other')", "is foo");
assertExpr("case_simple(x,'baz','is baz','bar','is bar','is other')", "is other");
assertExpr("case_simple(y,2,'is 2',3,'is 3','is other')", "is 2");
assertExpr("case_simple(z,2,'is 2',3,'is 3','is other')", "is other");
}
@Test
public void testCaseSearched()
{
assertExpr("case_searched(x=='baz','is baz',x=='foo','is foo','is other')", "is foo");
assertExpr("case_searched(x=='baz','is baz',x=='bar','is bar','is other')", "is other");
assertExpr("case_searched(y==2,'is 2',y==3,'is 3','is other')", "is 2");
assertExpr("case_searched(z==2,'is 2',z==3,'is 3','is other')", "is other");
}
@Test
public void testConcat()
{
assertExpr("concat(x,' ',y)", "foo 2");
if (NullHandling.replaceWithDefault()) {
assertExpr("concat(x,' ',nonexistent,' ',y)", "foo 2");
} else {
assertArrayExpr("concat(x,' ',nonexistent,' ',y)", null);
}
assertExpr("concat(z)", "3.1");
assertArrayExpr("concat()", null);
}
@Test
public void testReplace()
{
assertExpr("replace(x,'oo','ab')", "fab");
assertExpr("replace(x,x,'ab')", "ab");
assertExpr("replace(x,'oo',y)", "f2");
}
@Test
public void testSubstring()
{
assertExpr("substring(x,0,2)", "fo");
assertExpr("substring(x,1,2)", "oo");
assertExpr("substring(x,y,1)", "o");
assertExpr("substring(x,0,-1)", "foo");
assertExpr("substring(x,0,100)", "foo");
}
@Test
public void testStrlen()
{
assertExpr("strlen(x)", 3L);
assertExpr("strlen(nonexistent)", NullHandling.defaultLongValue());
}
@Test
public void testStrpos()
{
assertExpr("strpos(x, 'o')", 1L);
assertExpr("strpos(x, 'o', 0)", 1L);
assertExpr("strpos(x, 'o', 1)", 1L);
assertExpr("strpos(x, 'o', 2)", 2L);
assertExpr("strpos(x, 'o', 3)", -1L);
assertExpr("strpos(x, '')", 0L);
assertExpr("strpos(x, 'x')", -1L);
}
@Test
public void testLower()
{
assertExpr("lower('FOO')", "foo");
}
@Test
public void testUpper()
{
assertExpr("upper(x)", "FOO");
}
@Test
public void testIsNull()
{
assertExpr("isnull(null)", 1L);
assertExpr("isnull('abc')", 0L);
}
@Test
public void testIsNotNull()
{
assertExpr("notnull(null)", 0L);
assertExpr("notnull('abc')", 1L);
}
@Test
public void testLpad()
{
assertExpr("lpad(x, 5, 'ab')", "abfoo");
assertExpr("lpad(x, 4, 'ab')", "afoo");
assertExpr("lpad(x, 2, 'ab')", "fo");
assertExpr("lpad(x, -1, 'ab')", NullHandling.replaceWithDefault() ? null : "");
assertExpr("lpad(null, 5, 'ab')", null);
assertExpr("lpad(x, 2, '')", NullHandling.replaceWithDefault() ? null : "fo");
assertExpr("lpad(x, 6, '')", NullHandling.replaceWithDefault() ? null : "foo");
assertExpr("lpad('', 3, '*')", NullHandling.replaceWithDefault() ? null : "***");
assertExpr("lpad(x, 2, null)", null);
assertExpr("lpad(a, 4, '*')", "[foo");
assertExpr("lpad(a, 2, '*')", "[f");
assertExpr("lpad(a, 2, '')", NullHandling.replaceWithDefault() ? null : "[f");
assertExpr("lpad(b, 4, '*')", "[1, ");
assertExpr("lpad(b, 2, '')", NullHandling.replaceWithDefault() ? null : "[1");
assertExpr("lpad(b, 2, null)", null);
assertExpr("lpad(x, 5, x)", "fofoo");
assertExpr("lpad(x, 5, y)", "22foo");
assertExpr("lpad(x, 5, z)", "3.foo");
assertExpr("lpad(y, 5, x)", "foof2");
assertExpr("lpad(z, 5, y)", "223.1");
}
@Test
public void testRpad()
{
assertExpr("rpad(x, 5, 'ab')", "fooab");
assertExpr("rpad(x, 4, 'ab')", "fooa");
assertExpr("rpad(x, 2, 'ab')", "fo");
assertExpr("rpad(x, -1, 'ab')", NullHandling.replaceWithDefault() ? null : "");
assertExpr("rpad(null, 5, 'ab')", null);
assertExpr("rpad(x, 2, '')", NullHandling.replaceWithDefault() ? null : "fo");
assertExpr("rpad(x, 6, '')", NullHandling.replaceWithDefault() ? null : "foo");
assertExpr("rpad('', 3, '*')", NullHandling.replaceWithDefault() ? null : "***");
assertExpr("rpad(x, 2, null)", null);
assertExpr("rpad(a, 2, '*')", "[f");
assertExpr("rpad(a, 2, '')", NullHandling.replaceWithDefault() ? null : "[f");
assertExpr("rpad(b, 4, '*')", "[1, ");
assertExpr("rpad(b, 2, '')", NullHandling.replaceWithDefault() ? null : "[1");
assertExpr("rpad(b, 2, null)", null);
assertExpr("rpad(x, 5, x)", "foofo");
assertExpr("rpad(x, 5, y)", "foo22");
assertExpr("rpad(x, 5, z)", "foo3.");
assertExpr("rpad(y, 5, x)", "2foof");
assertExpr("rpad(z, 5, y)", "3.122");
}
@Test
public void testArrayConstructor()
{
assertArrayExpr("array(1, 2, 3, 4)", new Long[]{1L, 2L, 3L, 4L});
assertArrayExpr("array(1, 2, 3, 'bar')", new Long[]{1L, 2L, 3L, null});
assertArrayExpr("array(1.0)", new Double[]{1.0});
assertArrayExpr("array('foo', 'bar')", new String[]{"foo", "bar"});
}
@Test
public void testArrayLength()
{
assertExpr("array_length([1,2,3])", 3L);
assertExpr("array_length(a)", 4L);
}
@Test
public void testArrayOffset()
{
assertExpr("array_offset([1, 2, 3], 2)", 3L);
assertArrayExpr("array_offset([1, 2, 3], 3)", null);
assertExpr("array_offset(a, 2)", "baz");
}
@Test
public void testArrayOrdinal()
{
assertExpr("array_ordinal([1, 2, 3], 3)", 3L);
assertArrayExpr("array_ordinal([1, 2, 3], 4)", null);
assertExpr("array_ordinal(a, 3)", "baz");
}
@Test
public void testArrayOffsetOf()
{
assertExpr("array_offset_of([1, 2, 3], 3)", 2L);
assertExpr("array_offset_of([1, 2, 3], 4)", NullHandling.replaceWithDefault() ? -1L : null);
assertExpr("array_offset_of(a, 'baz')", 2L);
}
@Test
public void testArrayOrdinalOf()
{
assertExpr("array_ordinal_of([1, 2, 3], 3)", 3L);
assertExpr("array_ordinal_of([1, 2, 3], 4)", NullHandling.replaceWithDefault() ? -1L : null);
assertExpr("array_ordinal_of(a, 'baz')", 3L);
}
@Test
public void testArrayContains()
{
assertExpr("array_contains([1, 2, 3], 2)", 1L);
assertExpr("array_contains([1, 2, 3], 4)", 0L);
assertExpr("array_contains([1, 2, 3], [2, 3])", 1L);
assertExpr("array_contains([1, 2, 3], [3, 4])", 0L);
assertExpr("array_contains(b, [3, 4])", 1L);
}
@Test
public void testArrayOverlap()
{
assertExpr("array_overlap([1, 2, 3], [2, 4, 6])", 1L);
assertExpr("array_overlap([1, 2, 3], [4, 5, 6])", 0L);
}
@Test
public void testArrayAppend()
{
assertArrayExpr("array_append([1, 2, 3], 4)", new Long[]{1L, 2L, 3L, 4L});
assertArrayExpr("array_append([1, 2, 3], 'bar')", new Long[]{1L, 2L, 3L, null});
assertArrayExpr("array_append([], 1)", new String[]{"1"});
assertArrayExpr("array_append(<LONG>[], 1)", new Long[]{1L});
}
@Test
public void testArrayConcat()
{
assertArrayExpr("array_concat([1, 2, 3], [2, 4, 6])", new Long[]{1L, 2L, 3L, 2L, 4L, 6L});
assertArrayExpr("array_concat([1, 2, 3], 4)", new Long[]{1L, 2L, 3L, 4L});
assertArrayExpr("array_concat(0, [1, 2, 3])", new Long[]{0L, 1L, 2L, 3L});
assertArrayExpr("array_concat(map(y -> y * 3, b), [1, 2, 3])", new Long[]{3L, 6L, 9L, 12L, 15L, 1L, 2L, 3L});
assertArrayExpr("array_concat(0, 1)", new Long[]{0L, 1L});
}
@Test
public void testArraySetAdd()
{
assertArrayExpr("array_set_add([1, 2, 3], 4)", new Long[]{1L, 2L, 3L, 4L});
assertArrayExpr("array_set_add([1, 2, 3], 'bar')", new Long[]{null, 1L, 2L, 3L});
assertArrayExpr("array_set_add([1, 2, 2], 1)", new Long[]{1L, 2L});
assertArrayExpr("array_set_add([], 1)", new String[]{"1"});
assertArrayExpr("array_set_add(<LONG>[], 1)", new Long[]{1L});
assertArrayExpr("array_set_add(<LONG>[], null)", new Long[]{null});
}
@Test
public void testArraySetAddAll()
{
assertArrayExpr("array_set_add_all([1, 2, 3], [2, 4, 6])", new Long[]{1L, 2L, 3L, 4L, 6L});
assertArrayExpr("array_set_add_all([1, 2, 3], 4)", new Long[]{1L, 2L, 3L, 4L});
assertArrayExpr("array_set_add_all(0, [1, 2, 3])", new Long[]{0L, 1L, 2L, 3L});
assertArrayExpr("array_set_add_all(map(y -> y * 3, b), [1, 2, 3])", new Long[]{1L, 2L, 3L, 6L, 9L, 12L, 15L});
assertArrayExpr("array_set_add_all(0, 1)", new Long[]{0L, 1L});
}
@Test
public void testArrayToString()
{
assertExpr("array_to_string([1, 2, 3], ',')", "1,2,3");
assertExpr("array_to_string([1], '|')", "1");
assertExpr("array_to_string(a, '|')", "foo|bar|baz|foobar");
}
@Test
public void testStringToArray()
{
assertArrayExpr("string_to_array('1,2,3', ',')", new String[]{"1", "2", "3"});
assertArrayExpr("string_to_array('1', ',')", new String[]{"1"});
assertArrayExpr("string_to_array(array_to_string(a, ','), ',')", new String[]{"foo", "bar", "baz", "foobar"});
}
@Test
public void testArrayCast()
{
assertArrayExpr("cast([1, 2, 3], 'STRING_ARRAY')", new String[]{"1", "2", "3"});
assertArrayExpr("cast([1, 2, 3], 'DOUBLE_ARRAY')", new Double[]{1.0, 2.0, 3.0});
assertArrayExpr("cast(c, 'LONG_ARRAY')", new Long[]{3L, 4L, 5L});
assertArrayExpr("cast(string_to_array(array_to_string(b, ','), ','), 'LONG_ARRAY')", new Long[]{1L, 2L, 3L, 4L, 5L});
assertArrayExpr("cast(['1.0', '2.0', '3.0'], 'LONG_ARRAY')", new Long[]{1L, 2L, 3L});
}
@Test
public void testArraySlice()
{
assertArrayExpr("array_slice([1, 2, 3, 4], 1, 3)", new Long[] {2L, 3L});
assertArrayExpr("array_slice([1.0, 2.1, 3.2, 4.3], 2)", new Double[] {3.2, 4.3});
assertArrayExpr("array_slice(['a', 'b', 'c', 'd'], 4, 6)", new String[] {null, null});
assertArrayExpr("array_slice([1, 2, 3, 4], 2, 2)", new Long[] {});
assertArrayExpr("array_slice([1, 2, 3, 4], 5, 7)", null);
assertArrayExpr("array_slice([1, 2, 3, 4], 2, 1)", null);
}
@Test
public void testArrayPrepend()
{
assertArrayExpr("array_prepend(4, [1, 2, 3])", new Long[]{4L, 1L, 2L, 3L});
assertArrayExpr("array_prepend('bar', [1, 2, 3])", new Long[]{null, 1L, 2L, 3L});
assertArrayExpr("array_prepend(1, [])", new String[]{"1"});
assertArrayExpr("array_prepend(1, <LONG>[])", new Long[]{1L});
assertArrayExpr("array_prepend(1, <DOUBLE>[])", new Double[]{1.0});
}
@Test
public void testRoundWithNonNumericValuesShouldReturn0()
{
assertExpr("round(nan)", 0D);
assertExpr("round(nan, 5)", 0D);
//CHECKSTYLE.OFF: Regexp
assertExpr("round(inf)", Double.MAX_VALUE);
assertExpr("round(inf, 4)", Double.MAX_VALUE);
assertExpr("round(-inf)", -1 * Double.MAX_VALUE);
assertExpr("round(-inf, 3)", -1 * Double.MAX_VALUE);
assertExpr("round(-inf, -5)", -1 * Double.MAX_VALUE);
//CHECKSTYLE.ON: Regexp
// Calculations that result in non numeric numbers
assertExpr("round(0/od)", 0D);
assertExpr("round(od/od)", 0D);
//CHECKSTYLE.OFF: Regexp
assertExpr("round(1/od)", Double.MAX_VALUE);
assertExpr("round(-1/od)", -1 * Double.MAX_VALUE);
//CHECKSTYLE.ON: Regexp
assertExpr("round(0/of)", 0D);
assertExpr("round(of/of)", 0D);
//CHECKSTYLE.OFF: Regexp
assertExpr("round(1/of)", Double.MAX_VALUE);
assertExpr("round(-1/of)", -1 * Double.MAX_VALUE);
//CHECKSTYLE.ON: Regexp
}
@Test
public void testRoundWithLong()
{
assertExpr("round(y)", 2L);
assertExpr("round(y, 2)", 2L);
assertExpr("round(y, -1)", 0L);
}
@Test
public void testRoundWithDouble()
{
assertExpr("round(d)", 35D);
assertExpr("round(d, 2)", 34.56D);
assertExpr("round(d, y)", 34.56D);
assertExpr("round(d, 1)", 34.6D);
assertExpr("round(d, -1)", 30D);
}
@Test
public void testRoundWithFloat()
{
assertExpr("round(f)", 12D);
assertExpr("round(f, 2)", 12.34D);
assertExpr("round(f, y)", 12.34D);
assertExpr("round(f, 1)", 12.3D);
assertExpr("round(f, -1)", 10D);
}
@Test
public void testRoundWithExtremeNumbers()
{
assertExpr("round(maxLong)", BigDecimal.valueOf(Long.MAX_VALUE).setScale(0, RoundingMode.HALF_UP).longValue());
assertExpr("round(minLong)", BigDecimal.valueOf(Long.MIN_VALUE).setScale(0, RoundingMode.HALF_UP).longValue());
// overflow
assertExpr("round(maxLong + 1, 1)", BigDecimal.valueOf(Long.MIN_VALUE).setScale(1, RoundingMode.HALF_UP).longValue());
// underflow
assertExpr("round(minLong - 1, -2)", BigDecimal.valueOf(Long.MAX_VALUE).setScale(-2, RoundingMode.HALF_UP).longValue());
assertExpr("round(CAST(maxLong, 'DOUBLE') + 1, 1)", BigDecimal.valueOf(((double) Long.MAX_VALUE) + 1).setScale(1, RoundingMode.HALF_UP).doubleValue());
assertExpr("round(CAST(minLong, 'DOUBLE') - 1, -2)", BigDecimal.valueOf(((double) Long.MIN_VALUE) - 1).setScale(-2, RoundingMode.HALF_UP).doubleValue());
}
@Test
public void testRoundWithNullValueOrInvalid()
{
Set<Pair<String, String>> invalidArguments = ImmutableSet.of(
Pair.of("null", "STRING"),
Pair.of("x", "STRING"),
Pair.of("b", "LONG_ARRAY"),
Pair.of("c", "DOUBLE_ARRAY"),
Pair.of("a", "STRING_ARRAY")
);
for (Pair<String, String> argAndType : invalidArguments) {
if (NullHandling.sqlCompatible()) {
assertExpr(StringUtils.format("round(%s)", argAndType.lhs), null);
} else {
try {
assertExpr(StringUtils.format("round(%s)", argAndType.lhs), null);
Assert.fail("Did not throw IllegalArgumentException");
}
catch (IllegalArgumentException e) {
Assert.assertEquals(
StringUtils.format(
"The first argument to the function[round] should be integer or double type but got the type: %s",
argAndType.rhs
),
e.getMessage()
);
}
}
}
}
@Test
public void testRoundWithInvalidSecondArgument()
{
Set<Pair<String, String>> invalidArguments = ImmutableSet.of(
Pair.of("1.2", "DOUBLE"),
Pair.of("x", "STRING"),
Pair.of("a", "STRING_ARRAY"),
Pair.of("c", "DOUBLE_ARRAY")
);
for (Pair<String, String> argAndType : invalidArguments) {
try {
assertExpr(String.format(Locale.ENGLISH, "round(d, %s)", argAndType.lhs), null);
Assert.fail("Did not throw IllegalArgumentException");
}
catch (IllegalArgumentException e) {
Assert.assertEquals(
StringUtils.format(
"The second argument to the function[round] should be integer type but got the type: %s",
argAndType.rhs
),
e.getMessage()
);
}
}
}
@Test
public void testGreatest()
{
// Same types
assertExpr("greatest(y, 0)", 2L);
assertExpr("greatest(34.0, z, 5.0, 767.0)", 767.0);
assertExpr("greatest('B', x, 'A')", "foo");
// Different types
assertExpr("greatest(-1, z, 'A')", "A");
assertExpr("greatest(-1, z)", 3.1);
assertExpr("greatest(1, 'A')", "A");
// Invalid types
try {
assertExpr("greatest(1, ['A'])", null);
Assert.fail("Did not throw IllegalArgumentException");
}
catch (IllegalArgumentException e) {
Assert.assertEquals("Function[greatest] does not accept STRING_ARRAY types", e.getMessage());
}
// Null handling
assertExpr("greatest()", null);
assertExpr("greatest(null, null)", null);
assertExpr("greatest(1, null, 'A')", "A");
}
@Test
public void testLeast()
{
// Same types
assertExpr("least(y, 0)", 0L);
assertExpr("least(34.0, z, 5.0, 767.0)", 3.1);
assertExpr("least('B', x, 'A')", "A");
// Different types
assertExpr("least(-1, z, 'A')", "-1");
assertExpr("least(-1, z)", -1.0);
assertExpr("least(1, 'A')", "1");
// Invalid types
try {
assertExpr("least(1, [2, 3])", null);
Assert.fail("Did not throw IllegalArgumentException");
}
catch (IllegalArgumentException e) {
Assert.assertEquals("Function[least] does not accept LONG_ARRAY types", e.getMessage());
}
// Null handling
assertExpr("least()", null);
assertExpr("least(null, null)", null);
assertExpr("least(1, null, 'A')", "1");
}
@Test
public void testSizeFormat()
{
assertExpr("human_readable_binary_byte_format(-1024)", "-1.00 KiB");
assertExpr("human_readable_binary_byte_format(1024)", "1.00 KiB");
assertExpr("human_readable_binary_byte_format(1024*1024)", "1.00 MiB");
assertExpr("human_readable_binary_byte_format(1024*1024*1024)", "1.00 GiB");
assertExpr("human_readable_binary_byte_format(1024*1024*1024*1024)", "1.00 TiB");
assertExpr("human_readable_binary_byte_format(1024*1024*1024*1024*1024)", "1.00 PiB");
assertExpr("human_readable_decimal_byte_format(-1000)", "-1.00 KB");
assertExpr("human_readable_decimal_byte_format(1000)", "1.00 KB");
assertExpr("human_readable_decimal_byte_format(1000*1000)", "1.00 MB");
assertExpr("human_readable_decimal_byte_format(1000*1000*1000)", "1.00 GB");
assertExpr("human_readable_decimal_byte_format(1000*1000*1000*1000)", "1.00 TB");
assertExpr("human_readable_decimal_format(-1000)", "-1.00 K");
assertExpr("human_readable_decimal_format(1000)", "1.00 K");
assertExpr("human_readable_decimal_format(1000*1000)", "1.00 M");
assertExpr("human_readable_decimal_format(1000*1000*1000)", "1.00 G");
assertExpr("human_readable_decimal_format(1000*1000*1000*1000)", "1.00 T");
}
@Test
public void testSizeFormatWithDifferentPrecision()
{
assertExpr("human_readable_binary_byte_format(1024, 0)", "1 KiB");
assertExpr("human_readable_binary_byte_format(1024*1024, 1)", "1.0 MiB");
assertExpr("human_readable_binary_byte_format(1024*1024*1024, 2)", "1.00 GiB");
assertExpr("human_readable_binary_byte_format(1024*1024*1024*1024, 3)", "1.000 TiB");
assertExpr("human_readable_decimal_byte_format(1234, 0)", "1 KB");
assertExpr("human_readable_decimal_byte_format(1234*1000, 1)", "1.2 MB");
assertExpr("human_readable_decimal_byte_format(1234*1000*1000, 2)", "1.23 GB");
assertExpr("human_readable_decimal_byte_format(1234*1000*1000*1000, 3)", "1.234 TB");
assertExpr("human_readable_decimal_format(1234, 0)", "1 K");
assertExpr("human_readable_decimal_format(1234*1000,1)", "1.2 M");
assertExpr("human_readable_decimal_format(1234*1000*1000,2)", "1.23 G");
assertExpr("human_readable_decimal_format(1234*1000*1000*1000,3)", "1.234 T");
}
@Test
public void testSizeFormatWithEdgeCases()
{
//a nonexist value is null which is treated as 0
assertExpr("human_readable_binary_byte_format(nonexist)", NullHandling.sqlCompatible() ? null : "0 B");
//f = 12.34
assertExpr("human_readable_binary_byte_format(f)", "12 B");
//nan is Double.NaN
assertExpr("human_readable_binary_byte_format(nan)", "0 B");
//inf = Double.POSITIVE_INFINITY
assertExpr("human_readable_binary_byte_format(inf)", "8.00 EiB");
//inf = Double.NEGATIVE_INFINITY
assertExpr("human_readable_binary_byte_format(-inf)", "-8.00 EiB");
// o = 0
assertExpr("human_readable_binary_byte_format(o)", "0 B");
// od = 0D
assertExpr("human_readable_binary_byte_format(od)", "0 B");
// of = 0F
assertExpr("human_readable_binary_byte_format(of)", "0 B");
}
@Test
public void testSizeForatInvalidArgumentType()
{
try {
//x = "foo"
Parser.parse("human_readable_binary_byte_format(x)", ExprMacroTable.nil())
.eval(bindings);
// for sqlCompatible, function above returns null and goes here
// but for non-sqlCompatible, it must not go to here
Assert.assertTrue(NullHandling.sqlCompatible() ? true : false);
}
catch (IAE e) {
Assert.assertEquals("Function[human_readable_binary_byte_format] needs a number as its first argument", e.getMessage());
}
try {
//x = "foo"
Parser.parse("human_readable_binary_byte_format(1024, x)", ExprMacroTable.nil())
.eval(bindings);
//must not go to here
Assert.assertTrue(false);
}
catch (IAE e) {
Assert.assertEquals("Function[human_readable_binary_byte_format] needs an integer as its second argument", e.getMessage());
}
try {
//of = 0F
Parser.parse("human_readable_binary_byte_format(1024, of)", ExprMacroTable.nil())
.eval(bindings);
//must not go to here
Assert.assertTrue(false);
}
catch (IAE e) {
Assert.assertEquals("Function[human_readable_binary_byte_format] needs an integer as its second argument", e.getMessage());
}
try {
//of = 0F
Parser.parse("human_readable_binary_byte_format(1024, nonexist)", ExprMacroTable.nil())
.eval(bindings);
//must not go to here
Assert.assertTrue(false);
}
catch (IAE e) {
Assert.assertEquals("Function[human_readable_binary_byte_format] needs an integer as its second argument", e.getMessage());
}
}
@Test
public void testSizeFormatInvalidPrecision()
{
try {
Parser.parse("human_readable_binary_byte_format(1024, maxLong)", ExprMacroTable.nil())
.eval(bindings);
Assert.assertTrue(false);
}
catch (IAE e) {
Assert.assertEquals("Given precision[9223372036854775807] of Function[human_readable_binary_byte_format] must be in the range of [0,3]", e.getMessage());
}
try {
Parser.parse("human_readable_binary_byte_format(1024, minLong)", ExprMacroTable.nil())
.eval(bindings);
Assert.assertTrue(false);
}
catch (IAE e) {
Assert.assertEquals("Given precision[-9223372036854775808] of Function[human_readable_binary_byte_format] must be in the range of [0,3]", e.getMessage());
}
try {
Parser.parse("human_readable_binary_byte_format(1024, -1)", ExprMacroTable.nil())
.eval(bindings);
Assert.assertTrue(false);
}
catch (IAE e) {
Assert.assertEquals("Given precision[-1] of Function[human_readable_binary_byte_format] must be in the range of [0,3]", e.getMessage());
}
try {
Parser.parse("human_readable_binary_byte_format(1024, 4)", ExprMacroTable.nil())
.eval(bindings);
Assert.assertTrue(false);
}
catch (IAE e) {
Assert.assertEquals("Given precision[4] of Function[human_readable_binary_byte_format] must be in the range of [0,3]", e.getMessage());
}
}
@Test
public void testSizeFormatInvalidArgumentSize()
{
expectedException.expect(IAE.class);
expectedException.expectMessage("Function[human_readable_binary_byte_format] needs 1 or 2 arguments");
Parser.parse("human_readable_binary_byte_format(1024, 2, 3)", ExprMacroTable.nil())
.eval(bindings);
}
@Test
public void testBitwise()
{
// happy path maths
assertExpr("bitwiseAnd(3, 1)", 1L);
assertExpr("bitwiseAnd(2, 1)", 0L);
assertExpr("bitwiseOr(3, 1)", 3L);
assertExpr("bitwiseOr(2, 1)", 3L);
assertExpr("bitwiseXor(3, 1)", 2L);
assertExpr("bitwiseXor(2, 1)", 3L);
assertExpr("bitwiseShiftLeft(2, 1)", 4L);
assertExpr("bitwiseShiftRight(2, 1)", 1L);
assertExpr("bitwiseAnd(bitwiseComplement(1), 7)", 6L);
// funny types
// two strings is sad
assertExpr("bitwiseAnd('2', '1')", null);
// but one is ok, druid forgives you
assertExpr("bitwiseAnd(3, '1')", 1L);
assertExpr("bitwiseAnd(2, null)", NullHandling.replaceWithDefault() ? 0L : null);
// unary doesn't accept any slop
assertExpr("bitwiseComplement('1')", null);
assertExpr("bitwiseComplement(null)", null);
// doubles are cast
assertExpr("bitwiseOr(2.345, 1)", 3L);
assertExpr("bitwiseOr(2, 1.3)", 3L);
assertExpr("bitwiseAnd(2.345, 2.0)", 2L);
// but can be converted to be double-like
assertExpr(
"bitwiseAnd(bitwiseConvertDoubleToLongBits(2.345), bitwiseConvertDoubleToLongBits(2.0))",
4611686018427387904L
);
assertExpr(
"bitwiseConvertLongBitsToDouble(bitwiseAnd(bitwiseConvertDoubleToLongBits(2.345), bitwiseConvertDoubleToLongBits(2.0)))",
2.0
);
assertExpr("bitwiseConvertDoubleToLongBits(2.0)", 4611686018427387904L);
assertExpr("bitwiseConvertDoubleToLongBits(bitwiseConvertDoubleToLongBits(2.0))", 4886405595696988160L);
assertExpr("bitwiseConvertLongBitsToDouble(4611686018427387904)", 2.0);
assertExpr("bitwiseConvertLongBitsToDouble(bitwiseConvertLongBitsToDouble(4611686018427387904))", 1.0E-323);
// conversion returns null if nonsense inputs
assertExpr("bitwiseConvertLongBitsToDouble('wat')", null);
assertExpr("bitwiseConvertLongBitsToDouble('1')", null);
assertExpr("bitwiseConvertLongBitsToDouble(null)", null);
assertExpr("bitwiseConvertDoubleToLongBits('wat')", null);
assertExpr("bitwiseConvertDoubleToLongBits('1.0')", null);
assertExpr("bitwiseConvertDoubleToLongBits(null)", null);
}
@Test
public void testRepeat()
{
assertExpr("repeat('hello', 2)", "hellohello");
assertExpr("repeat('hello', -1)", null);
assertExpr("repeat(null, 10)", null);
assertExpr("repeat(nonexistent, 10)", null);
}
private void assertExpr(final String expression, @Nullable final Object expectedResult)
{
final Expr expr = Parser.parse(expression, ExprMacroTable.nil());
Assert.assertEquals(expression, expectedResult, expr.eval(bindings).value());
final Expr exprNoFlatten = Parser.parse(expression, ExprMacroTable.nil(), false);
final Expr roundTrip = Parser.parse(exprNoFlatten.stringify(), ExprMacroTable.nil());
Assert.assertEquals(expr.stringify(), expectedResult, roundTrip.eval(bindings).value());
final Expr roundTripFlatten = Parser.parse(expr.stringify(), ExprMacroTable.nil());
Assert.assertEquals(expr.stringify(), expectedResult, roundTripFlatten.eval(bindings).value());
Assert.assertEquals(expr.stringify(), roundTrip.stringify());
Assert.assertEquals(expr.stringify(), roundTripFlatten.stringify());
Assert.assertArrayEquals(expr.getCacheKey(), roundTrip.getCacheKey());
Assert.assertArrayEquals(expr.getCacheKey(), roundTripFlatten.getCacheKey());
}
private void assertArrayExpr(final String expression, @Nullable final Object[] expectedResult)
{
final Expr expr = Parser.parse(expression, ExprMacroTable.nil());
Assert.assertArrayEquals(expression, expectedResult, expr.eval(bindings).asArray());
final Expr exprNoFlatten = Parser.parse(expression, ExprMacroTable.nil(), false);
final Expr roundTrip = Parser.parse(exprNoFlatten.stringify(), ExprMacroTable.nil());
Assert.assertArrayEquals(expression, expectedResult, roundTrip.eval(bindings).asArray());
final Expr roundTripFlatten = Parser.parse(expr.stringify(), ExprMacroTable.nil());
Assert.assertArrayEquals(expression, expectedResult, roundTripFlatten.eval(bindings).asArray());
Assert.assertEquals(expr.stringify(), roundTrip.stringify());
Assert.assertEquals(expr.stringify(), roundTripFlatten.stringify());
Assert.assertArrayEquals(expr.getCacheKey(), roundTrip.getCacheKey());
Assert.assertArrayEquals(expr.getCacheKey(), roundTripFlatten.getCacheKey());
}
}