diff --git a/core/trino-main/src/test/java/io/trino/operator/scalar/AbstractTestFunctions.java b/core/trino-main/src/test/java/io/trino/operator/scalar/AbstractTestFunctions.java index b872b577ab65..039aa68d7c6f 100644 --- a/core/trino-main/src/test/java/io/trino/operator/scalar/AbstractTestFunctions.java +++ b/core/trino-main/src/test/java/io/trino/operator/scalar/AbstractTestFunctions.java @@ -38,9 +38,7 @@ import static io.trino.metadata.OperatorNameUtil.mangleOperatorName; import static io.trino.operator.scalar.timestamp.VarcharToTimestampCast.castToLongTimestamp; import static io.trino.spi.StandardErrorCode.INVALID_FUNCTION_ARGUMENT; -import static io.trino.spi.StandardErrorCode.NOT_SUPPORTED; import static io.trino.spi.type.DecimalType.createDecimalType; -import static io.trino.testing.assertions.TrinoExceptionAssert.assertTrinoExceptionThrownBy; import static java.lang.String.format; import static java.util.Objects.requireNonNull; import static org.testng.Assert.fail; @@ -151,13 +149,6 @@ protected void assertCachedInstanceHasBoundedRetainedSize(String projection) functionAssertions.assertCachedInstanceHasBoundedRetainedSize(projection); } - protected void assertNotSupported(String projection, String message) - { - assertTrinoExceptionThrownBy(() -> functionAssertions.executeProjectionWithFullEngine(projection)) - .hasErrorCode(NOT_SUPPORTED) - .hasMessage(message); - } - protected void tryEvaluateWithAll(String projection, Type expectedType) { functionAssertions.tryEvaluateWithAll(projection, expectedType); diff --git a/core/trino-main/src/test/java/io/trino/operator/scalar/FunctionAssertions.java b/core/trino-main/src/test/java/io/trino/operator/scalar/FunctionAssertions.java index afbb1280bdb9..00fb009a4900 100644 --- a/core/trino-main/src/test/java/io/trino/operator/scalar/FunctionAssertions.java +++ b/core/trino-main/src/test/java/io/trino/operator/scalar/FunctionAssertions.java @@ -220,11 +220,6 @@ public final class FunctionAssertions private final LocalQueryRunner runner; private final TestingFunctionResolution testingFunctionResolution; - public FunctionAssertions() - { - this(TEST_SESSION); - } - public FunctionAssertions(Session session) { this(session, new FeaturesConfig()); @@ -635,11 +630,6 @@ private Object selectSingleValue(Operator operator, Type type) return type.getObjectValue(session.toConnectorSession(), block, 0); } - public void assertFilter(String filter, boolean expected, boolean withNoInputColumns) - { - assertFilter(filter, expected, withNoInputColumns, runner.getExpressionCompiler()); - } - private void assertFilter(String filter, boolean expected, boolean withNoInputColumns, ExpressionCompiler compiler) { List results = executeFilterWithAll(filter, TEST_SESSION, withNoInputColumns, compiler); diff --git a/core/trino-main/src/test/java/io/trino/operator/scalar/TestDateTimeFunctions.java b/core/trino-main/src/test/java/io/trino/operator/scalar/TestDateTimeFunctions.java index 771d939dff38..74ddc3a46910 100644 --- a/core/trino-main/src/test/java/io/trino/operator/scalar/TestDateTimeFunctions.java +++ b/core/trino-main/src/test/java/io/trino/operator/scalar/TestDateTimeFunctions.java @@ -15,125 +15,74 @@ import com.google.common.collect.ImmutableList; import io.trino.Session; -import io.trino.spi.type.BigintType; -import io.trino.spi.type.DateType; -import io.trino.spi.type.SqlDate; -import io.trino.spi.type.SqlTimestampWithTimeZone; -import io.trino.spi.type.TimeType; import io.trino.spi.type.TimeZoneKey; -import io.trino.spi.type.Type; +import io.trino.sql.query.QueryAssertions; import io.trino.testing.TestingConnectorSession; import io.trino.testing.TestingSession; import io.trino.type.SqlIntervalDayTime; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; -import org.joda.time.Hours; -import org.joda.time.Minutes; -import org.joda.time.ReadableInstant; -import org.joda.time.Seconds; import org.joda.time.chrono.ISOChronology; -import org.testng.annotations.Test; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; -import java.time.Duration; import java.time.Instant; import java.time.LocalDate; -import java.time.LocalTime; -import java.time.OffsetTime; import java.time.ZoneId; -import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.util.Locale; import java.util.concurrent.TimeUnit; import static io.trino.operator.scalar.DateTimeFunctions.currentDate; import static io.trino.spi.StandardErrorCode.INVALID_FUNCTION_ARGUMENT; -import static io.trino.spi.type.BigintType.BIGINT; -import static io.trino.spi.type.TimeType.createTimeType; -import static io.trino.spi.type.TimeWithTimeZoneType.TIME_WITH_TIME_ZONE; -import static io.trino.spi.type.TimeZoneKey.UTC_KEY; import static io.trino.spi.type.TimeZoneKey.getTimeZoneKey; -import static io.trino.spi.type.TimeZoneKey.getTimeZoneKeyForOffset; -import static io.trino.spi.type.TimestampType.TIMESTAMP_MILLIS; -import static io.trino.spi.type.TimestampType.createTimestampType; -import static io.trino.spi.type.TimestampWithTimeZoneType.TIMESTAMP_TZ_MILLIS; -import static io.trino.spi.type.TimestampWithTimeZoneType.TIMESTAMP_TZ_NANOS; -import static io.trino.spi.type.TimestampWithTimeZoneType.createTimestampWithTimeZoneType; import static io.trino.spi.type.VarcharType.VARCHAR; import static io.trino.spi.type.VarcharType.createVarcharType; -import static io.trino.testing.DateTimeTestingUtils.sqlTimeOf; -import static io.trino.testing.DateTimeTestingUtils.sqlTimestampOf; -import static io.trino.testing.TestingSession.testSessionBuilder; +import static io.trino.testing.assertions.TrinoExceptionAssert.assertTrinoExceptionThrownBy; import static io.trino.type.IntervalDayTimeType.INTERVAL_DAY_TIME; -import static io.trino.util.DateTimeZoneIndex.getDateTimeZone; -import static java.lang.Math.toIntExact; -import static java.lang.String.format; -import static java.time.temporal.ChronoField.MILLI_OF_SECOND; -import static java.util.Objects.requireNonNull; import static java.util.concurrent.TimeUnit.DAYS; import static java.util.concurrent.TimeUnit.HOURS; -import static java.util.concurrent.TimeUnit.MILLISECONDS; -import static java.util.concurrent.TimeUnit.NANOSECONDS; import static java.util.concurrent.TimeUnit.SECONDS; -import static org.joda.time.DateTimeUtils.getInstantChronology; -import static org.joda.time.Days.daysBetween; -import static org.joda.time.DurationFieldType.millis; -import static org.joda.time.Months.monthsBetween; -import static org.joda.time.Weeks.weeksBetween; -import static org.joda.time.Years.yearsBetween; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS; import static org.testng.Assert.assertEquals; +@TestInstance(PER_CLASS) public class TestDateTimeFunctions - extends AbstractTestFunctions { - protected static final TimeZoneKey TIME_ZONE_KEY = TestingSession.DEFAULT_TIME_ZONE_KEY; - protected static final DateTimeZone DATE_TIME_ZONE = getDateTimeZone(TIME_ZONE_KEY); - protected static final DateTimeZone UTC_TIME_ZONE = getDateTimeZone(UTC_KEY); - protected static final DateTimeZone DATE_TIME_ZONE_NUMERICAL = getDateTimeZone(getTimeZoneKey("-11:00")); - protected static final TimeZoneKey KATHMANDU_ZONE_KEY = getTimeZoneKey("Asia/Kathmandu"); - protected static final DateTimeZone KATHMANDU_ZONE = getDateTimeZone(KATHMANDU_ZONE_KEY); - protected static final ZoneOffset WEIRD_ZONE = ZoneOffset.ofHoursMinutes(7, 9); - protected static final DateTimeZone WEIRD_DATE_TIME_ZONE = DateTimeZone.forID(WEIRD_ZONE.getId()); - - protected static final DateTime DATE = new DateTime(2001, 8, 22, 0, 0, 0, 0, DateTimeZone.UTC); - protected static final String DATE_LITERAL = "DATE '2001-08-22'"; - protected static final String DATE_ISO8601_STRING = "2001-08-22"; - - protected static final LocalTime TIME = LocalTime.of(3, 4, 5, 321_000_000); - protected static final String TIME_LITERAL = "TIME '03:04:05.321'"; - protected static final OffsetTime WEIRD_TIME = OffsetTime.of(3, 4, 5, 321_000_000, WEIRD_ZONE); - protected static final String WEIRD_TIME_LITERAL = "TIME '03:04:05.321 +07:09'"; - - protected static final DateTime TIMESTAMP = new DateTime(2001, 8, 22, 3, 4, 5, 321, UTC_TIME_ZONE); // This is TIMESTAMP w/o TZ - protected static final DateTime TIMESTAMP_WITH_NUMERICAL_ZONE = new DateTime(2001, 8, 22, 3, 4, 5, 321, DATE_TIME_ZONE_NUMERICAL); - protected static final String TIMESTAMP_LITERAL = "TIMESTAMP '2001-08-22 03:04:05.321'"; - protected static final String TIMESTAMP_ISO8601_STRING = "2001-08-22T03:04:05.321-11:00"; - protected static final String TIMESTAMP_ISO8601_STRING_NO_TIME_ZONE = "2001-08-22T03:04:05.321"; - protected static final DateTime WEIRD_TIMESTAMP = new DateTime(2001, 8, 22, 3, 4, 5, 321, WEIRD_DATE_TIME_ZONE); - protected static final String WEIRD_TIMESTAMP_LITERAL = "TIMESTAMP '2001-08-22 03:04:05.321 +07:09'"; - protected static final String WEIRD_TIMESTAMP_ISO8601_STRING = "2001-08-22T03:04:05.321+07:09"; - - protected static final String INTERVAL_LITERAL = "INTERVAL '90061.234' SECOND"; - protected static final Duration DAY_TO_SECOND_INTERVAL = Duration.ofMillis(90061234); - - public TestDateTimeFunctions() + private QueryAssertions assertions; + + @BeforeAll + public void init() { - super(testSessionBuilder() - .setTimeZoneKey(TIME_ZONE_KEY) - .setStart(Instant.ofEpochMilli(new DateTime(2017, 4, 1, 12, 34, 56, 789, UTC_TIME_ZONE).getMillis())) - .build()); + assertions = new QueryAssertions(); + } + + @AfterAll + public void teardown() + { + assertions.close(); + assertions = null; } @Test public void testToIso8601ForTimestampWithoutTimeZone() { - assertFunction("to_iso8601(" + TIMESTAMP_LITERAL + ")", createVarcharType(26), TIMESTAMP_ISO8601_STRING_NO_TIME_ZONE); + assertThat(assertions.function("to_iso8601", "TIMESTAMP '2001-08-22 03:04:05.321'")) + .hasType(createVarcharType(26)) + .isEqualTo("2001-08-22T03:04:05.321"); } @Test public void testCurrentDate() { + Session session = Session.builder(assertions.getDefaultSession()) + .setStart(ZonedDateTime.of(2017, 4, 1, 12, 34, 56, 789, ZoneId.of("UTC")).toInstant()) + .build(); + // current date is the time at midnight in the session time zone - assertFunction("CURRENT_DATE", DateType.DATE, new SqlDate(toIntExact(epochDaysInZone(TIME_ZONE_KEY, session.getStart())))); + assertThat(assertions.expression("current_date", session)) + .matches("DATE '2017-04-02'"); } @Test @@ -151,7 +100,7 @@ public void testCurrentDateTimezone() assertCurrentDateAtInstant(kievTimeZoneKey, instant); assertCurrentDateAtInstant(bahiaBanderasTimeZoneKey, instant); assertCurrentDateAtInstant(montrealTimeZoneKey, instant); - assertCurrentDateAtInstant(TIME_ZONE_KEY, instant); + assertCurrentDateAtInstant(TestingSession.DEFAULT_TIME_ZONE_KEY, instant); } } @@ -171,1062 +120,1006 @@ private static long epochDaysInZone(TimeZoneKey timeZoneKey, Instant instant) return LocalDate.from(instant.atZone(timeZoneKey.getZoneId())).toEpochDay(); } - @Test - public void testLocalTime() - { - functionAssertions.assertFunctionString("localtime", TimeType.TIME_MILLIS, "02:34:56.789"); - - Session localSession = Session.builder(session) - .setStart(Instant.ofEpochMilli(new DateTime(2017, 3, 1, 14, 30, 0, 0, DATE_TIME_ZONE).getMillis())) - .build(); - try (FunctionAssertions localAssertion = new FunctionAssertions(localSession)) { - localAssertion.assertFunctionString("localtime", TimeType.TIME_MILLIS, "14:30:00.000"); - } - - localSession = Session.builder(session) - // we use Asia/Kathmandu here, as it has different zone offset on 2017-03-01 and on 1970-01-01 - .setTimeZoneKey(KATHMANDU_ZONE_KEY) - .setStart(Instant.ofEpochMilli(new DateTime(2017, 3, 1, 15, 45, 0, 0, KATHMANDU_ZONE).getMillis())) - .build(); - try (FunctionAssertions localAssertion = new FunctionAssertions(localSession)) { - localAssertion.assertFunctionString("localtime", TimeType.TIME_MILLIS, "15:45:00.000"); - } - } - - @Test - public void testCurrentTime() - { - functionAssertions.assertFunctionString("current_time", TIME_WITH_TIME_ZONE, "02:34:56.789+14:00"); - - Session localSession = Session.builder(session) - // we use Asia/Kathmandu here, as it has different zone offset on 2017-03-01 and on 1970-01-01 - .setTimeZoneKey(KATHMANDU_ZONE_KEY) - .setStart(Instant.ofEpochMilli(new DateTime(2017, 3, 1, 15, 45, 0, 0, KATHMANDU_ZONE).getMillis())) - .build(); - try (FunctionAssertions localAssertion = new FunctionAssertions(localSession)) { - localAssertion.assertFunctionString("current_time", TIME_WITH_TIME_ZONE, "15:45:00.000+05:45"); - } - } - @Test public void testFromUnixTime() { - DateTime dateTime = new DateTime(2001, 1, 22, 3, 4, 5, 0, DATE_TIME_ZONE); - double seconds = dateTime.getMillis() / 1000.0; - assertFunction("from_unixtime(" + seconds + ")", TIMESTAMP_TZ_MILLIS, SqlTimestampWithTimeZone.newInstance(3, dateTime.getMillis(), 0, TIME_ZONE_KEY)); + assertThat(assertions.function("from_unixtime", "980172245")) + .matches("TIMESTAMP '2001-01-22 03:04:05.000 Pacific/Apia'"); - dateTime = new DateTime(2001, 1, 22, 3, 4, 5, 888, DATE_TIME_ZONE); - seconds = dateTime.getMillis() / 1000.0; - assertFunction("from_unixtime(" + seconds + ")", TIMESTAMP_TZ_MILLIS, SqlTimestampWithTimeZone.newInstance(3, dateTime.getMillis(), 0, TIME_ZONE_KEY)); + assertThat(assertions.function("from_unixtime", "980172245.888")) + .matches("TIMESTAMP '2001-01-22 03:04:05.888 Pacific/Apia'"); } @Test public void testFromUnixTimeNanos() { // long - assertFunction("from_unixtime_nanos(1234567890123456789)", TIMESTAMP_TZ_NANOS, SqlTimestampWithTimeZone.newInstance(9, 1234567890_123L, 456789000, TIME_ZONE_KEY)); - assertFunction("from_unixtime_nanos(999999999)", TIMESTAMP_TZ_NANOS, SqlTimestampWithTimeZone.newInstance(9, 999, 999999000, TIME_ZONE_KEY)); - assertFunction("from_unixtime_nanos(-1234567890123456789)", TIMESTAMP_TZ_NANOS, SqlTimestampWithTimeZone.newInstance(9, -1234567890_124L, 543211000, TIME_ZONE_KEY)); - assertFunction("from_unixtime_nanos(-999999999)", TIMESTAMP_TZ_NANOS, SqlTimestampWithTimeZone.newInstance(9, -1000, 1000, TIME_ZONE_KEY)); + assertThat(assertions.function("from_unixtime_nanos", "1234567890123456789")) + .matches("TIMESTAMP '2009-02-13 12:31:30.123456789 Pacific/Apia'"); + + assertThat(assertions.function("from_unixtime_nanos", "999999999")) + .matches("TIMESTAMP '1969-12-31 13:00:00.999999999 Pacific/Apia'"); + + assertThat(assertions.function("from_unixtime_nanos", "-1234567890123456789")) + .matches("TIMESTAMP '1930-11-17 12:58:29.876543211 Pacific/Apia'"); + + assertThat(assertions.function("from_unixtime_nanos", "-999999999")) + .matches("TIMESTAMP '1969-12-31 12:59:59.000000001 Pacific/Apia'"); // short decimal - assertFunction("from_unixtime_nanos(DECIMAL '1234')", TIMESTAMP_TZ_NANOS, SqlTimestampWithTimeZone.newInstance(9, 0, 1234000, TIME_ZONE_KEY)); - assertFunction("from_unixtime_nanos(DECIMAL '1234.0')", TIMESTAMP_TZ_NANOS, SqlTimestampWithTimeZone.newInstance(9, 0, 1234000, TIME_ZONE_KEY)); - assertFunction("from_unixtime_nanos(DECIMAL '1234.499')", TIMESTAMP_TZ_NANOS, SqlTimestampWithTimeZone.newInstance(9, 0, 1234000, TIME_ZONE_KEY)); - assertFunction("from_unixtime_nanos(DECIMAL '1234.500')", TIMESTAMP_TZ_NANOS, SqlTimestampWithTimeZone.newInstance(9, 0, 1235000, TIME_ZONE_KEY)); + assertThat(assertions.function("from_unixtime_nanos", "DECIMAL '1234'")) + .matches("TIMESTAMP '1969-12-31 13:00:00.000001234 Pacific/Apia'"); + + assertThat(assertions.function("from_unixtime_nanos", "DECIMAL '1234.0'")) + .matches("TIMESTAMP '1969-12-31 13:00:00.000001234 Pacific/Apia'"); - assertFunction("from_unixtime_nanos(DECIMAL '-1234')", TIMESTAMP_TZ_NANOS, SqlTimestampWithTimeZone.newInstance(9, -1, 998766000, TIME_ZONE_KEY)); - assertFunction("from_unixtime_nanos(DECIMAL '-1234.0')", TIMESTAMP_TZ_NANOS, SqlTimestampWithTimeZone.newInstance(9, -1, 998766000, TIME_ZONE_KEY)); - assertFunction("from_unixtime_nanos(DECIMAL '-1234.499')", TIMESTAMP_TZ_NANOS, SqlTimestampWithTimeZone.newInstance(9, -1, 998766000, TIME_ZONE_KEY)); - assertFunction("from_unixtime_nanos(DECIMAL '-1234.500')", TIMESTAMP_TZ_NANOS, SqlTimestampWithTimeZone.newInstance(9, -1, 998765000, TIME_ZONE_KEY)); + assertThat(assertions.function("from_unixtime_nanos", "DECIMAL '1234.499'")) + .matches("TIMESTAMP '1969-12-31 13:00:00.000001234 Pacific/Apia'"); + + assertThat(assertions.function("from_unixtime_nanos", "DECIMAL '1234.500'")) + .matches("TIMESTAMP '1969-12-31 13:00:00.000001235 Pacific/Apia'"); + + assertThat(assertions.function("from_unixtime_nanos", "DECIMAL '-1234'")) + .matches("TIMESTAMP '1969-12-31 12:59:59.999998766 Pacific/Apia'"); + + assertThat(assertions.function("from_unixtime_nanos", "DECIMAL '-1234.0'")) + .matches("TIMESTAMP '1969-12-31 12:59:59.999998766 Pacific/Apia'"); + + assertThat(assertions.function("from_unixtime_nanos", "DECIMAL '-1234.499'")) + .matches("TIMESTAMP '1969-12-31 12:59:59.999998766 Pacific/Apia'"); + + assertThat(assertions.function("from_unixtime_nanos", "DECIMAL '-1234.500'")) + .matches("TIMESTAMP '1969-12-31 12:59:59.999998765 Pacific/Apia'"); // long decimal - assertFunction("from_unixtime_nanos(DECIMAL '12345678900123456789')", TIMESTAMP_TZ_NANOS, SqlTimestampWithTimeZone.newInstance(9, 12345678900_123L, 456789000, TIME_ZONE_KEY)); - assertFunction("from_unixtime_nanos(DECIMAL '12345678900123456789.000000')", TIMESTAMP_TZ_NANOS, SqlTimestampWithTimeZone.newInstance(9, 12345678900_123L, 456789000, TIME_ZONE_KEY)); - assertFunction("from_unixtime_nanos(DECIMAL '12345678900123456789.499')", TIMESTAMP_TZ_NANOS, SqlTimestampWithTimeZone.newInstance(9, 12345678900_123L, 456789000, TIME_ZONE_KEY)); - assertFunction("from_unixtime_nanos(DECIMAL '12345678900123456789.500')", TIMESTAMP_TZ_NANOS, SqlTimestampWithTimeZone.newInstance(9, 12345678900_123L, 456790000, TIME_ZONE_KEY)); - assertFunction("from_unixtime_nanos(DECIMAL '-12345678900123456789')", TIMESTAMP_TZ_NANOS, SqlTimestampWithTimeZone.newInstance(9, -12345678900_124L, 543211000, TIME_ZONE_KEY)); - assertFunction("from_unixtime_nanos(DECIMAL '-12345678900123456789.000000')", TIMESTAMP_TZ_NANOS, SqlTimestampWithTimeZone.newInstance(9, -12345678900_124L, 543211000, TIME_ZONE_KEY)); - assertFunction("from_unixtime_nanos(DECIMAL '-12345678900123456789.499')", TIMESTAMP_TZ_NANOS, SqlTimestampWithTimeZone.newInstance(9, -12345678900_124L, 543211000, TIME_ZONE_KEY)); - assertFunction("from_unixtime_nanos(DECIMAL '-12345678900123456789.500')", TIMESTAMP_TZ_NANOS, SqlTimestampWithTimeZone.newInstance(9, -12345678900_124L, 543210000, TIME_ZONE_KEY)); + assertThat(assertions.function("from_unixtime_nanos", "DECIMAL '12345678900123456789'")) + .matches("TIMESTAMP '2361-03-22 08:15:00.123456789 Pacific/Apia'"); + + assertThat(assertions.function("from_unixtime_nanos", "DECIMAL '12345678900123456789.000000'")) + .matches("TIMESTAMP '2361-03-22 08:15:00.123456789 Pacific/Apia'"); + + assertThat(assertions.function("from_unixtime_nanos", "DECIMAL '12345678900123456789.499'")) + .matches("TIMESTAMP '2361-03-22 08:15:00.123456789 Pacific/Apia'"); + + assertThat(assertions.function("from_unixtime_nanos", "DECIMAL '12345678900123456789.500'")) + .matches("TIMESTAMP '2361-03-22 08:15:00.123456790 Pacific/Apia'"); + + assertThat(assertions.function("from_unixtime_nanos", "DECIMAL '-12345678900123456789'")) + .matches("TIMESTAMP '1578-10-13 17:18:03.876543211 Pacific/Apia'"); + + assertThat(assertions.function("from_unixtime_nanos", "DECIMAL '-12345678900123456789.000000'")) + .matches("TIMESTAMP '1578-10-13 17:18:03.876543211 Pacific/Apia'"); + + assertThat(assertions.function("from_unixtime_nanos", "DECIMAL '-12345678900123456789.499'")) + .matches("TIMESTAMP '1578-10-13 17:18:03.876543211 Pacific/Apia'"); + + assertThat(assertions.function("from_unixtime_nanos", "DECIMAL '-12345678900123456789.500'")) + .matches("TIMESTAMP '1578-10-13 17:18:03.876543210 Pacific/Apia'"); } @Test public void testFromUnixTimeWithOffset() { - DateTime dateTime = new DateTime(2001, 1, 22, 3, 4, 5, 0, DATE_TIME_ZONE); - double seconds = dateTime.getMillis() / 1000.0; + assertThat(assertions.function("from_unixtime", "980172245", "1", "10")) + .matches("TIMESTAMP '2001-01-22 15:14:05.000 +01:10'"); - int timeZoneHoursOffset = 1; - int timezoneMinutesOffset = 10; + // test invalid minute offsets + assertTrinoExceptionThrownBy(() -> assertions.function("from_unixtime", "0", "1", "10000").evaluate()) + .hasErrorCode(INVALID_FUNCTION_ARGUMENT); - DateTime expected = new DateTime(dateTime, getDateTimeZone(getTimeZoneKeyForOffset((timeZoneHoursOffset * 60L) + timezoneMinutesOffset))); - assertFunction("from_unixtime(" + seconds + ", " + timeZoneHoursOffset + ", " + timezoneMinutesOffset + ")", TIMESTAMP_TZ_MILLIS, toTimestampWithTimeZone(expected)); + assertTrinoExceptionThrownBy(() -> assertions.function("from_unixtime", "0", "10000", "0").evaluate()) + .hasErrorCode(INVALID_FUNCTION_ARGUMENT); - // test invalid minute offsets - assertInvalidFunction("from_unixtime(0, 1, 10000)", INVALID_FUNCTION_ARGUMENT); - assertInvalidFunction("from_unixtime(0, 10000, 0)", INVALID_FUNCTION_ARGUMENT); - assertInvalidFunction("from_unixtime(0, -100, 100)", INVALID_FUNCTION_ARGUMENT); + assertTrinoExceptionThrownBy(() -> assertions.function("from_unixtime", "0", "-100", "100").evaluate()) + .hasErrorCode(INVALID_FUNCTION_ARGUMENT); } @Test public void testFromUnixTimeWithTimeZone() { - String zoneId = "Asia/Shanghai"; - DateTime expected = new DateTime(1970, 1, 1, 10, 0, 0, DateTimeZone.forID(zoneId)); - assertFunction(format("from_unixtime(7200, '%s')", zoneId), TIMESTAMP_TZ_MILLIS, toTimestampWithTimeZone(expected)); + assertThat(assertions.function("from_unixtime", "7200", "'Asia/Shanghai'")) + .matches("TIMESTAMP '1970-01-01 10:00:00.000 Asia/Shanghai'"); - zoneId = "Asia/Tokyo"; - expected = new DateTime(1970, 1, 1, 11, 0, 0, DateTimeZone.forID(zoneId)); - assertFunction(format("from_unixtime(7200, '%s')", zoneId), TIMESTAMP_TZ_MILLIS, toTimestampWithTimeZone(expected)); + assertThat(assertions.function("from_unixtime", "7200", "'Asia/Tokyo'")) + .matches("TIMESTAMP '1970-01-01 11:00:00.000 Asia/Tokyo'"); - zoneId = "Europe/Moscow"; - expected = new DateTime(1970, 1, 1, 5, 0, 0, DateTimeZone.forID(zoneId)); - assertFunction(format("from_unixtime(7200, '%s')", zoneId), TIMESTAMP_TZ_MILLIS, toTimestampWithTimeZone(expected)); + assertThat(assertions.function("from_unixtime", "7200", "'Europe/Moscow'")) + .matches("TIMESTAMP '1970-01-01 05:00:00.000 Europe/Moscow'"); - zoneId = "America/New_York"; - expected = new DateTime(1969, 12, 31, 21, 0, 0, DateTimeZone.forID(zoneId)); - assertFunction(format("from_unixtime(7200, '%s')", zoneId), TIMESTAMP_TZ_MILLIS, toTimestampWithTimeZone(expected)); + assertThat(assertions.function("from_unixtime", "7200", "'America/New_York'")) + .matches("TIMESTAMP '1969-12-31 21:00:00.000 America/New_York'"); - zoneId = "America/Chicago"; - expected = new DateTime(1969, 12, 31, 20, 0, 0, DateTimeZone.forID(zoneId)); - assertFunction(format("from_unixtime(7200, '%s')", zoneId), TIMESTAMP_TZ_MILLIS, toTimestampWithTimeZone(expected)); + assertThat(assertions.function("from_unixtime", "7200", "'America/Chicago'")) + .matches("TIMESTAMP '1969-12-31 20:00:00.000 America/Chicago'"); - zoneId = "America/Los_Angeles"; - expected = new DateTime(1969, 12, 31, 18, 0, 0, DateTimeZone.forID(zoneId)); - assertFunction(format("from_unixtime(7200, '%s')", zoneId), TIMESTAMP_TZ_MILLIS, toTimestampWithTimeZone(expected)); + assertThat(assertions.function("from_unixtime", "7200", "'America/Los_Angeles'")) + .matches("TIMESTAMP '1969-12-31 18:00:00.000 America/Los_Angeles'"); } @Test public void testDate() { - assertFunction("date('" + DATE_ISO8601_STRING + "')", DateType.DATE, toDate(DATE)); - assertFunction("date(" + WEIRD_TIMESTAMP_LITERAL + ")", DateType.DATE, toDate(DATE)); - assertFunction("date(" + TIMESTAMP_LITERAL + ")", DateType.DATE, toDate(DATE)); + assertThat(assertions.function("date", "'2001-08-22'")) + .matches("DATE '2001-08-22'"); + + assertThat(assertions.function("date", "TIMESTAMP '2001-08-22 03:04:05.321 +07:09'")) + .matches("DATE '2001-08-22'"); + + assertThat(assertions.function("date", "TIMESTAMP '2001-08-22 03:04:05.321'")) + .matches("DATE '2001-08-22'"); } @Test public void testFromISO8601() { - assertFunction("from_iso8601_timestamp('" + TIMESTAMP_ISO8601_STRING + "')", TIMESTAMP_TZ_MILLIS, toTimestampWithTimeZone(TIMESTAMP_WITH_NUMERICAL_ZONE)); - assertFunction("from_iso8601_timestamp('" + WEIRD_TIMESTAMP_ISO8601_STRING + "')", TIMESTAMP_TZ_MILLIS, toTimestampWithTimeZone(WEIRD_TIMESTAMP)); - assertFunction("from_iso8601_date('" + DATE_ISO8601_STRING + "')", DateType.DATE, toDate(DATE)); + assertThat(assertions.function("from_iso8601_timestamp", "'2001-08-22T03:04:05.321-11:00'")) + .matches("TIMESTAMP '2001-08-22 03:04:05.321 -11:00'"); + + assertThat(assertions.function("from_iso8601_timestamp", "'2001-08-22T03:04:05.321+07:09'")) + .matches("TIMESTAMP '2001-08-22 03:04:05.321 +07:09'"); + + assertThat(assertions.function("from_iso8601_date", "'2001-08-22'")) + .matches("DATE '2001-08-22'"); } @Test public void testFromIso8601Nanos() { - Instant instant = ZonedDateTime.of(2001, 8, 22, 12, 34, 56, 123456789, ZoneId.of("UTC")).toInstant(); + assertThat(assertions.function("from_iso8601_timestamp_nanos", "'2001-08-22T12:34:56.123456789Z'")) + .matches("TIMESTAMP '2001-08-22 12:34:56.123456789 UTC'"); - assertFunction("from_iso8601_timestamp_nanos('2001-08-22T12:34:56.123456789Z')", TIMESTAMP_TZ_NANOS, - SqlTimestampWithTimeZone.fromInstant(9, instant, ZoneId.of("UTC"))); - assertFunction("from_iso8601_timestamp_nanos('2001-08-22T07:34:56.123456789-05:00')", TIMESTAMP_TZ_NANOS, - SqlTimestampWithTimeZone.fromInstant(9, instant, ZoneId.of("-0500"))); - assertFunction("from_iso8601_timestamp_nanos('2001-08-22T13:34:56.123456789+01:00')", TIMESTAMP_TZ_NANOS, - SqlTimestampWithTimeZone.fromInstant(9, instant, ZoneId.of("+0100"))); - assertFunction("from_iso8601_timestamp_nanos('2001-08-22T12:34:56.123Z')", TIMESTAMP_TZ_NANOS, - SqlTimestampWithTimeZone.fromInstant(9, instant.minusNanos(456789), ZoneId.of("UTC"))); + assertThat(assertions.function("from_iso8601_timestamp_nanos", "'2001-08-22T07:34:56.123456789-05:00'")) + .matches("TIMESTAMP '2001-08-22 07:34:56.123456789 -05:00'"); + + assertThat(assertions.function("from_iso8601_timestamp_nanos", "'2001-08-22T13:34:56.123456789+01:00'")) + .matches("TIMESTAMP '2001-08-22 13:34:56.123456789 +01:00'"); + + assertThat(assertions.function("from_iso8601_timestamp_nanos", "'2001-08-22T12:34:56.123Z'")) + .matches("TIMESTAMP '2001-08-22 12:34:56.123000000 UTC'"); // make sure that strings without a timezone are parsed in the session local time - ZoneId nineHoursBehindZone = ZoneId.of("-0900"); - Session localSession = Session.builder(session) - .setTimeZoneKey(TimeZoneKey.getTimeZoneKey(nineHoursBehindZone.getId())) + Session session = Session.builder(assertions.getDefaultSession()) + .setTimeZoneKey(TimeZoneKey.getTimeZoneKey(ZoneId.of("-0900").getId())) .build(); - try (FunctionAssertions localAssertion = new FunctionAssertions(localSession)) { - localAssertion.assertFunction("from_iso8601_timestamp_nanos('2001-08-22T03:34:56.123456789')", TIMESTAMP_TZ_NANOS, - SqlTimestampWithTimeZone.fromInstant(9, instant, nineHoursBehindZone)); - } + + assertThat(assertions.expression("from_iso8601_timestamp_nanos('2001-08-22T03:34:56.123456789')", session)) + .matches("TIMESTAMP '2001-08-22 03:34:56.123456789 -09:00'"); } @Test public void testToIso8601() { - assertFunction("to_iso8601(" + WEIRD_TIMESTAMP_LITERAL + ")", createVarcharType(32), WEIRD_TIMESTAMP_ISO8601_STRING); - assertFunction("to_iso8601(" + DATE_LITERAL + ")", createVarcharType(16), DATE_ISO8601_STRING); + assertThat(assertions.function("to_iso8601", "DATE '2001-08-22'")) + .hasType(createVarcharType(16)) + .isEqualTo("2001-08-22"); } @Test public void testTimeZone() { - assertFunction("hour(" + TIMESTAMP_LITERAL + ")", BIGINT, (long) TIMESTAMP.getHourOfDay()); - assertFunction("minute(" + TIMESTAMP_LITERAL + ")", BIGINT, (long) TIMESTAMP.getMinuteOfHour()); - assertFunction("hour(" + WEIRD_TIMESTAMP_LITERAL + ")", BIGINT, (long) WEIRD_TIMESTAMP.getHourOfDay()); - assertFunction("minute(" + WEIRD_TIMESTAMP_LITERAL + ")", BIGINT, (long) WEIRD_TIMESTAMP.getMinuteOfHour()); - assertFunction("current_timezone()", VARCHAR, TIME_ZONE_KEY.getId()); + assertThat(assertions.expression("current_timezone()")) + .hasType(VARCHAR) + .isEqualTo("Pacific/Apia"); } @Test public void testPartFunctions() { - assertFunction("millisecond(" + TIMESTAMP_LITERAL + ")", BIGINT, (long) TIMESTAMP.getMillisOfSecond()); - assertFunction("second(" + TIMESTAMP_LITERAL + ")", BIGINT, (long) TIMESTAMP.getSecondOfMinute()); - assertFunction("minute(" + TIMESTAMP_LITERAL + ")", BIGINT, (long) TIMESTAMP.getMinuteOfHour()); - assertFunction("hour(" + TIMESTAMP_LITERAL + ")", BIGINT, (long) TIMESTAMP.getHourOfDay()); - assertFunction("day_of_week(" + TIMESTAMP_LITERAL + ")", BIGINT, (long) TIMESTAMP.dayOfWeek().get()); - assertFunction("dow(" + TIMESTAMP_LITERAL + ")", BIGINT, (long) TIMESTAMP.dayOfWeek().get()); - assertFunction("day(" + TIMESTAMP_LITERAL + ")", BIGINT, (long) TIMESTAMP.getDayOfMonth()); - assertFunction("day_of_month(" + TIMESTAMP_LITERAL + ")", BIGINT, (long) TIMESTAMP.getDayOfMonth()); - assertFunction("day_of_year(" + TIMESTAMP_LITERAL + ")", BIGINT, (long) TIMESTAMP.dayOfYear().get()); - assertFunction("doy(" + TIMESTAMP_LITERAL + ")", BIGINT, (long) TIMESTAMP.dayOfYear().get()); - assertFunction("week(" + TIMESTAMP_LITERAL + ")", BIGINT, (long) TIMESTAMP.weekOfWeekyear().get()); - assertFunction("week_of_year(" + TIMESTAMP_LITERAL + ")", BIGINT, (long) TIMESTAMP.weekOfWeekyear().get()); - assertFunction("month(" + TIMESTAMP_LITERAL + ")", BIGINT, (long) TIMESTAMP.getMonthOfYear()); - assertFunction("quarter(" + TIMESTAMP_LITERAL + ")", BIGINT, (long) TIMESTAMP.getMonthOfYear() / 4 + 1); - assertFunction("year(" + TIMESTAMP_LITERAL + ")", BIGINT, (long) TIMESTAMP.getYear()); - assertFunction("timezone_minute(" + TIMESTAMP_LITERAL + ")", BIGINT, 0L); - assertFunction("timezone_hour(" + TIMESTAMP_LITERAL + ")", BIGINT, -11L); - - assertFunction("timezone_hour(localtimestamp)", BIGINT, 14L); - assertFunction("timezone_hour(current_timestamp)", BIGINT, 14L); - - assertFunction("millisecond(" + WEIRD_TIMESTAMP_LITERAL + ")", BIGINT, (long) WEIRD_TIMESTAMP.getMillisOfSecond()); - assertFunction("second(" + WEIRD_TIMESTAMP_LITERAL + ")", BIGINT, (long) WEIRD_TIMESTAMP.getSecondOfMinute()); - assertFunction("minute(" + WEIRD_TIMESTAMP_LITERAL + ")", BIGINT, (long) WEIRD_TIMESTAMP.getMinuteOfHour()); - assertFunction("hour(" + WEIRD_TIMESTAMP_LITERAL + ")", BIGINT, (long) WEIRD_TIMESTAMP.getHourOfDay()); - assertFunction("day_of_week(" + WEIRD_TIMESTAMP_LITERAL + ")", BIGINT, (long) WEIRD_TIMESTAMP.dayOfWeek().get()); - assertFunction("dow(" + WEIRD_TIMESTAMP_LITERAL + ")", BIGINT, (long) WEIRD_TIMESTAMP.dayOfWeek().get()); - assertFunction("day(" + WEIRD_TIMESTAMP_LITERAL + ")", BIGINT, (long) WEIRD_TIMESTAMP.getDayOfMonth()); - assertFunction("day_of_month(" + WEIRD_TIMESTAMP_LITERAL + ")", BIGINT, (long) WEIRD_TIMESTAMP.getDayOfMonth()); - assertFunction("day_of_year(" + WEIRD_TIMESTAMP_LITERAL + ")", BIGINT, (long) WEIRD_TIMESTAMP.dayOfYear().get()); - assertFunction("doy(" + WEIRD_TIMESTAMP_LITERAL + ")", BIGINT, (long) WEIRD_TIMESTAMP.dayOfYear().get()); - assertFunction("week(" + WEIRD_TIMESTAMP_LITERAL + ")", BIGINT, (long) WEIRD_TIMESTAMP.weekOfWeekyear().get()); - assertFunction("week_of_year(" + WEIRD_TIMESTAMP_LITERAL + ")", BIGINT, (long) WEIRD_TIMESTAMP.weekOfWeekyear().get()); - assertFunction("month(" + WEIRD_TIMESTAMP_LITERAL + ")", BIGINT, (long) WEIRD_TIMESTAMP.getMonthOfYear()); - assertFunction("quarter(" + WEIRD_TIMESTAMP_LITERAL + ")", BIGINT, (long) WEIRD_TIMESTAMP.getMonthOfYear() / 4 + 1); - assertFunction("year(" + WEIRD_TIMESTAMP_LITERAL + ")", BIGINT, (long) WEIRD_TIMESTAMP.getYear()); - assertFunction("timezone_minute(" + WEIRD_TIMESTAMP_LITERAL + ")", BIGINT, 9L); - assertFunction("timezone_hour(" + WEIRD_TIMESTAMP_LITERAL + ")", BIGINT, 7L); - - assertFunction("millisecond(" + TIME_LITERAL + ")", BIGINT, TIME.getLong(MILLI_OF_SECOND)); - assertFunction("second(" + TIME_LITERAL + ")", BIGINT, (long) TIME.getSecond()); - assertFunction("minute(" + TIME_LITERAL + ")", BIGINT, (long) TIME.getMinute()); - assertFunction("hour(" + TIME_LITERAL + ")", BIGINT, (long) TIME.getHour()); - - assertFunction("millisecond(" + WEIRD_TIME_LITERAL + ")", BIGINT, WEIRD_TIME.getLong(MILLI_OF_SECOND)); - assertFunction("second(" + WEIRD_TIME_LITERAL + ")", BIGINT, (long) WEIRD_TIME.getSecond()); - assertFunction("minute(" + WEIRD_TIME_LITERAL + ")", BIGINT, (long) WEIRD_TIME.getMinute()); - assertFunction("hour(" + WEIRD_TIME_LITERAL + ")", BIGINT, (long) WEIRD_TIME.getHour()); - - assertFunction("millisecond(" + INTERVAL_LITERAL + ")", BIGINT, (long) DAY_TO_SECOND_INTERVAL.getNano() / 1_000_000); - assertFunction("second(" + INTERVAL_LITERAL + ")", BIGINT, DAY_TO_SECOND_INTERVAL.getSeconds() % 60); - assertFunction("minute(" + INTERVAL_LITERAL + ")", BIGINT, DAY_TO_SECOND_INTERVAL.getSeconds() / 60 % 60); - assertFunction("hour(" + INTERVAL_LITERAL + ")", BIGINT, DAY_TO_SECOND_INTERVAL.getSeconds() / 3600 % 24); + assertThat(assertions.function("millisecond", "INTERVAL '90061.234' SECOND")) + .isEqualTo(234L); + + assertThat(assertions.function("second", "INTERVAL '90061.234' SECOND")) + .isEqualTo(1L); + + assertThat(assertions.function("minute", "INTERVAL '90061.234' SECOND")) + .isEqualTo(1L); + + assertThat(assertions.function("hour", "INTERVAL '90061.234' SECOND")) + .isEqualTo(1L); } @Test public void testYearOfWeek() { - assertFunction("year_of_week(DATE '2001-08-22')", BIGINT, 2001L); - assertFunction("yow(DATE '2001-08-22')", BIGINT, 2001L); - assertFunction("year_of_week(DATE '2005-01-02')", BIGINT, 2004L); - assertFunction("year_of_week(DATE '2008-12-28')", BIGINT, 2008L); - assertFunction("year_of_week(DATE '2008-12-29')", BIGINT, 2009L); - assertFunction("year_of_week(DATE '2009-12-31')", BIGINT, 2009L); - assertFunction("year_of_week(DATE '2010-01-03')", BIGINT, 2009L); - assertFunction("year_of_week(TIMESTAMP '2001-08-22 03:04:05.321 +07:09')", BIGINT, 2001L); - assertFunction("year_of_week(TIMESTAMP '2010-01-03 03:04:05.321')", BIGINT, 2009L); + assertThat(assertions.function("year_of_week", "DATE '2001-08-22'")) + .isEqualTo(2001L); + + assertThat(assertions.function("yow", "DATE '2001-08-22'")) + .isEqualTo(2001L); + + assertThat(assertions.function("year_of_week", "DATE '2005-01-02'")) + .isEqualTo(2004L); + + assertThat(assertions.function("year_of_week", "DATE '2008-12-28'")) + .isEqualTo(2008L); + + assertThat(assertions.function("year_of_week", "DATE '2008-12-29'")) + .isEqualTo(2009L); + + assertThat(assertions.function("year_of_week", "DATE '2009-12-31'")) + .isEqualTo(2009L); + + assertThat(assertions.function("year_of_week", "DATE '2010-01-03'")) + .isEqualTo(2009L); } @Test public void testLastDayOfMonth() { - assertFunction("last_day_of_month(" + DATE_LITERAL + ")", DateType.DATE, toDate(DATE.withDayOfMonth(31))); - assertFunction("last_day_of_month(DATE '2019-08-01')", DateType.DATE, toDate(LocalDate.of(2019, 8, 31))); - assertFunction("last_day_of_month(DATE '2019-08-31')", DateType.DATE, toDate(LocalDate.of(2019, 8, 31))); + assertThat(assertions.function("last_day_of_month", "DATE '2001-08-22'")) + .matches("DATE '2001-08-31'"); - assertFunction("last_day_of_month(" + TIMESTAMP_LITERAL + ")", DateType.DATE, toDate(DATE.withDayOfMonth(31))); - assertFunction("last_day_of_month(TIMESTAMP '2019-08-01 00:00:00.000')", DateType.DATE, toDate(LocalDate.of(2019, 8, 31))); - assertFunction("last_day_of_month(TIMESTAMP '2019-08-01 17:00:00.000')", DateType.DATE, toDate(LocalDate.of(2019, 8, 31))); - assertFunction("last_day_of_month(TIMESTAMP '2019-08-01 23:59:59.999')", DateType.DATE, toDate(LocalDate.of(2019, 8, 31))); - assertFunction("last_day_of_month(TIMESTAMP '2019-08-31 23:59:59.999')", DateType.DATE, toDate(LocalDate.of(2019, 8, 31))); + assertThat(assertions.function("last_day_of_month", "DATE '2019-08-01'")) + .matches("DATE '2019-08-31'"); - assertFunction("last_day_of_month(" + WEIRD_TIMESTAMP_LITERAL + ")", DateType.DATE, toDate(DATE.withDayOfMonth(31))); - ImmutableList.of("+05:45", "+00:00", "-05:45", "Asia/Tokyo", "Europe/London", "America/Los_Angeles", "America/Bahia_Banderas").forEach(timeZone -> { - assertFunction("last_day_of_month(TIMESTAMP '2018-12-31 17:00:00.000 " + timeZone + "')", DateType.DATE, toDate(LocalDate.of(2018, 12, 31))); - assertFunction("last_day_of_month(TIMESTAMP '2018-12-31 20:00:00.000 " + timeZone + "')", DateType.DATE, toDate(LocalDate.of(2018, 12, 31))); - assertFunction("last_day_of_month(TIMESTAMP '2018-12-31 23:59:59.999 " + timeZone + "')", DateType.DATE, toDate(LocalDate.of(2018, 12, 31))); - assertFunction("last_day_of_month(TIMESTAMP '2019-01-01 00:00:00.000 " + timeZone + "')", DateType.DATE, toDate(LocalDate.of(2019, 1, 31))); - assertFunction("last_day_of_month(TIMESTAMP '2019-01-01 00:00:00.001 " + timeZone + "')", DateType.DATE, toDate(LocalDate.of(2019, 1, 31))); - assertFunction("last_day_of_month(TIMESTAMP '2019-01-01 03:00:00.000 " + timeZone + "')", DateType.DATE, toDate(LocalDate.of(2019, 1, 31))); - assertFunction("last_day_of_month(TIMESTAMP '2019-01-01 06:00:00.000 " + timeZone + "')", DateType.DATE, toDate(LocalDate.of(2019, 1, 31))); - assertFunction("last_day_of_month(TIMESTAMP '2019-08-01 00:00:00.000 " + timeZone + "')", DateType.DATE, toDate(LocalDate.of(2019, 8, 31))); - assertFunction("last_day_of_month(TIMESTAMP '2019-08-01 17:00:00.000 " + timeZone + "')", DateType.DATE, toDate(LocalDate.of(2019, 8, 31))); - assertFunction("last_day_of_month(TIMESTAMP '2019-08-01 23:59:59.999 " + timeZone + "')", DateType.DATE, toDate(LocalDate.of(2019, 8, 31))); - assertFunction("last_day_of_month(TIMESTAMP '2019-08-31 23:59:59.999 " + timeZone + "')", DateType.DATE, toDate(LocalDate.of(2019, 8, 31))); - }); - } + assertThat(assertions.function("last_day_of_month", "DATE '2019-08-31'")) + .matches("DATE '2019-8-31'"); - @Test - public void testExtractFromTimestamp() - { - assertFunction("extract(second FROM " + TIMESTAMP_LITERAL + ")", BIGINT, (long) TIMESTAMP.getSecondOfMinute()); - assertFunction("extract(minute FROM " + TIMESTAMP_LITERAL + ")", BIGINT, (long) TIMESTAMP.getMinuteOfHour()); - assertFunction("extract(hour FROM " + TIMESTAMP_LITERAL + ")", BIGINT, (long) TIMESTAMP.getHourOfDay()); - assertFunction("extract(day_of_week FROM " + TIMESTAMP_LITERAL + ")", BIGINT, (long) TIMESTAMP.getDayOfWeek()); - assertFunction("extract(dow FROM " + TIMESTAMP_LITERAL + ")", BIGINT, (long) TIMESTAMP.getDayOfWeek()); - assertFunction("extract(day FROM " + TIMESTAMP_LITERAL + ")", BIGINT, (long) TIMESTAMP.getDayOfMonth()); - assertFunction("extract(day_of_month FROM " + TIMESTAMP_LITERAL + ")", BIGINT, (long) TIMESTAMP.getDayOfMonth()); - assertFunction("extract(day_of_year FROM " + TIMESTAMP_LITERAL + ")", BIGINT, (long) TIMESTAMP.getDayOfYear()); - assertFunction("extract(year_of_week FROM " + TIMESTAMP_LITERAL + ")", BIGINT, 2001L); - assertFunction("extract(doy FROM " + TIMESTAMP_LITERAL + ")", BIGINT, (long) TIMESTAMP.getDayOfYear()); - assertFunction("extract(week FROM " + TIMESTAMP_LITERAL + ")", BIGINT, (long) TIMESTAMP.getWeekOfWeekyear()); - assertFunction("extract(month FROM " + TIMESTAMP_LITERAL + ")", BIGINT, (long) TIMESTAMP.getMonthOfYear()); - assertFunction("extract(quarter FROM " + TIMESTAMP_LITERAL + ")", BIGINT, (long) TIMESTAMP.getMonthOfYear() / 4 + 1); - assertFunction("extract(year FROM " + TIMESTAMP_LITERAL + ")", BIGINT, (long) TIMESTAMP.getYear()); - - assertFunction("extract(second FROM " + WEIRD_TIMESTAMP_LITERAL + ")", BIGINT, (long) WEIRD_TIMESTAMP.getSecondOfMinute()); - assertFunction("extract(minute FROM " + WEIRD_TIMESTAMP_LITERAL + ")", BIGINT, (long) WEIRD_TIMESTAMP.getMinuteOfHour()); - assertFunction("extract(hour FROM " + WEIRD_TIMESTAMP_LITERAL + ")", BIGINT, (long) WEIRD_TIMESTAMP.getHourOfDay()); - assertFunction("extract(day_of_week FROM " + WEIRD_TIMESTAMP_LITERAL + ")", BIGINT, (long) WEIRD_TIMESTAMP.getDayOfWeek()); - assertFunction("extract(dow FROM " + WEIRD_TIMESTAMP_LITERAL + ")", BIGINT, (long) WEIRD_TIMESTAMP.getDayOfWeek()); - assertFunction("extract(day FROM " + WEIRD_TIMESTAMP_LITERAL + ")", BIGINT, (long) WEIRD_TIMESTAMP.getDayOfMonth()); - assertFunction("extract(day_of_month FROM " + WEIRD_TIMESTAMP_LITERAL + ")", BIGINT, (long) WEIRD_TIMESTAMP.getDayOfMonth()); - assertFunction("extract(day_of_year FROM " + WEIRD_TIMESTAMP_LITERAL + ")", BIGINT, (long) WEIRD_TIMESTAMP.getDayOfYear()); - assertFunction("extract(doy FROM " + WEIRD_TIMESTAMP_LITERAL + ")", BIGINT, (long) WEIRD_TIMESTAMP.getDayOfYear()); - assertFunction("extract(week FROM " + WEIRD_TIMESTAMP_LITERAL + ")", BIGINT, (long) WEIRD_TIMESTAMP.getWeekOfWeekyear()); - assertFunction("extract(month FROM " + WEIRD_TIMESTAMP_LITERAL + ")", BIGINT, (long) WEIRD_TIMESTAMP.getMonthOfYear()); - assertFunction("extract(quarter FROM " + WEIRD_TIMESTAMP_LITERAL + ")", BIGINT, (long) WEIRD_TIMESTAMP.getMonthOfYear() / 4 + 1); - assertFunction("extract(year FROM " + WEIRD_TIMESTAMP_LITERAL + ")", BIGINT, (long) WEIRD_TIMESTAMP.getYear()); - assertFunction("extract(timezone_minute FROM " + WEIRD_TIMESTAMP_LITERAL + ")", BIGINT, 9L); - assertFunction("extract(timezone_hour FROM " + WEIRD_TIMESTAMP_LITERAL + ")", BIGINT, 7L); - } + assertThat(assertions.expression("last_day_of_month(TIMESTAMP '2001-08-22 03:04:05.321')")) + .matches("DATE '2001-08-31'"); - @Test - public void testExtractFromTime() - { - assertFunction("extract(second FROM " + TIME_LITERAL + ")", BIGINT, 5L); - assertFunction("extract(minute FROM " + TIME_LITERAL + ")", BIGINT, 4L); - assertFunction("extract(hour FROM " + TIME_LITERAL + ")", BIGINT, 3L); + assertThat(assertions.function("last_day_of_month", "TIMESTAMP '2019-08-01 00:00:00.000'")) + .matches("DATE '2019-8-31'"); - assertFunction("extract(second FROM " + WEIRD_TIME_LITERAL + ")", BIGINT, 5L); - assertFunction("extract(minute FROM " + WEIRD_TIME_LITERAL + ")", BIGINT, 4L); - assertFunction("extract(hour FROM " + WEIRD_TIME_LITERAL + ")", BIGINT, 3L); - } + assertThat(assertions.function("last_day_of_month", "TIMESTAMP '2019-08-01 17:00:00.000'")) + .matches("DATE '2019-8-31'"); - @Test - public void testExtractFromDate() - { - assertFunction("extract(day_of_week FROM " + DATE_LITERAL + ")", BIGINT, 3L); - assertFunction("extract(dow FROM " + DATE_LITERAL + ")", BIGINT, 3L); - assertFunction("extract(day FROM " + DATE_LITERAL + ")", BIGINT, 22L); - assertFunction("extract(day_of_month FROM " + DATE_LITERAL + ")", BIGINT, 22L); - assertFunction("extract(day_of_year FROM " + DATE_LITERAL + ")", BIGINT, 234L); - assertFunction("extract(doy FROM " + DATE_LITERAL + ")", BIGINT, 234L); - assertFunction("extract(year_of_week FROM " + DATE_LITERAL + ")", BIGINT, 2001L); - assertFunction("extract(yow FROM " + DATE_LITERAL + ")", BIGINT, 2001L); - assertFunction("extract(week FROM " + DATE_LITERAL + ")", BIGINT, 34L); - assertFunction("extract(month FROM " + DATE_LITERAL + ")", BIGINT, 8L); - assertFunction("extract(quarter FROM " + DATE_LITERAL + ")", BIGINT, 3L); - assertFunction("extract(year FROM " + DATE_LITERAL + ")", BIGINT, 2001L); - - assertFunction("extract(quarter FROM DATE '2001-01-01')", BIGINT, 1L); - assertFunction("extract(quarter FROM DATE '2001-03-31')", BIGINT, 1L); - assertFunction("extract(quarter FROM DATE '2001-04-01')", BIGINT, 2L); - assertFunction("extract(quarter FROM DATE '2001-06-30')", BIGINT, 2L); - assertFunction("extract(quarter FROM DATE '2001-07-01')", BIGINT, 3L); - assertFunction("extract(quarter FROM DATE '2001-09-30')", BIGINT, 3L); - assertFunction("extract(quarter FROM DATE '2001-10-01')", BIGINT, 4L); - assertFunction("extract(quarter FROM DATE '2001-12-31')", BIGINT, 4L); - - assertFunction("extract(quarter FROM TIMESTAMP '2001-01-01 00:00:00.000')", BIGINT, 1L); - assertFunction("extract(quarter FROM TIMESTAMP '2001-03-31 23:59:59.999')", BIGINT, 1L); - assertFunction("extract(quarter FROM TIMESTAMP '2001-04-01 00:00:00.000')", BIGINT, 2L); - assertFunction("extract(quarter FROM TIMESTAMP '2001-06-30 23:59:59.999')", BIGINT, 2L); - assertFunction("extract(quarter FROM TIMESTAMP '2001-07-01 00:00:00.000')", BIGINT, 3L); - assertFunction("extract(quarter FROM TIMESTAMP '2001-09-30 23:59:59.999')", BIGINT, 3L); - assertFunction("extract(quarter FROM TIMESTAMP '2001-10-01 00:00:00.000')", BIGINT, 4L); - assertFunction("extract(quarter FROM TIMESTAMP '2001-12-31 23:59:59.999')", BIGINT, 4L); - - assertFunction("extract(quarter FROM TIMESTAMP '2001-01-01 00:00:00.000 +06:00')", BIGINT, 1L); - assertFunction("extract(quarter FROM TIMESTAMP '2001-03-31 23:59:59.999 +06:00')", BIGINT, 1L); - assertFunction("extract(quarter FROM TIMESTAMP '2001-04-01 00:00:00.000 +06:00')", BIGINT, 2L); - assertFunction("extract(quarter FROM TIMESTAMP '2001-06-30 23:59:59.999 +06:00')", BIGINT, 2L); - assertFunction("extract(quarter FROM TIMESTAMP '2001-07-01 00:00:00.000 +06:00')", BIGINT, 3L); - assertFunction("extract(quarter FROM TIMESTAMP '2001-09-30 23:59:59.999 +06:00')", BIGINT, 3L); - assertFunction("extract(quarter FROM TIMESTAMP '2001-10-01 00:00:00.000 +06:00')", BIGINT, 4L); - assertFunction("extract(quarter FROM TIMESTAMP '2001-12-31 23:59:59.999 +06:00')", BIGINT, 4L); - } + assertThat(assertions.function("last_day_of_month", "TIMESTAMP '2019-08-01 23:59:59.999'")) + .matches("DATE '2019-8-31'"); - @Test - public void testExtractFromInterval() - { - assertFunction("extract(second FROM INTERVAL '5' SECOND)", BIGINT, 5L); - assertFunction("extract(second FROM INTERVAL '65' SECOND)", BIGINT, 5L); + assertThat(assertions.function("last_day_of_month", "TIMESTAMP '2019-08-31 23:59:59.999'")) + .matches("DATE '2019-8-31'"); - assertFunction("extract(minute FROM INTERVAL '4' MINUTE)", BIGINT, 4L); - assertFunction("extract(minute FROM INTERVAL '64' MINUTE)", BIGINT, 4L); - assertFunction("extract(minute FROM INTERVAL '247' SECOND)", BIGINT, 4L); + assertThat(assertions.function("last_day_of_month", "TIMESTAMP '2001-08-22 03:04:05.321 +07:09'")) + .matches("DATE '2001-08-31'"); - assertFunction("extract(hour FROM INTERVAL '3' HOUR)", BIGINT, 3L); - assertFunction("extract(hour FROM INTERVAL '27' HOUR)", BIGINT, 3L); - assertFunction("extract(hour FROM INTERVAL '187' MINUTE)", BIGINT, 3L); + ImmutableList.of("+05:45", "+00:00", "-05:45", "Asia/Tokyo", "Europe/London", "America/Los_Angeles", "America/Bahia_Banderas").forEach(timeZone -> { + assertThat(assertions.function("last_day_of_month", "TIMESTAMP '2018-12-31 17:00:00.000 " + timeZone + "'")) + .matches("DATE '2018-12-31'"); - assertFunction("extract(day FROM INTERVAL '2' DAY)", BIGINT, 2L); - assertFunction("extract(day FROM INTERVAL '55' HOUR)", BIGINT, 2L); + assertThat(assertions.function("last_day_of_month", "TIMESTAMP '2018-12-31 20:00:00.000 " + timeZone + "'")) + .matches("DATE '2018-12-31'"); - assertFunction("extract(month FROM INTERVAL '3' MONTH)", BIGINT, 3L); - assertFunction("extract(month FROM INTERVAL '15' MONTH)", BIGINT, 3L); + assertThat(assertions.function("last_day_of_month", "TIMESTAMP '2018-12-31 23:59:59.999 " + timeZone + "'")) + .matches("DATE '2018-12-31'"); - assertFunction("extract(year FROM INTERVAL '2' YEAR)", BIGINT, 2L); - assertFunction("extract(year FROM INTERVAL '29' MONTH)", BIGINT, 2L); - } + assertThat(assertions.function("last_day_of_month", "TIMESTAMP '2019-01-01 00:00:00.000 " + timeZone + "'")) + .matches("DATE '2019-1-31'"); - @Test - public void testTruncateTimestamp() - { - DateTime result = TIMESTAMP; - result = result.withMillisOfSecond(0); - assertFunction("date_trunc('second', " + TIMESTAMP_LITERAL + ")", TIMESTAMP_MILLIS, sqlTimestampOf(result)); + assertThat(assertions.function("last_day_of_month", "TIMESTAMP '2019-01-01 00:00:00.001 " + timeZone + "'")) + .matches("DATE '2019-1-31'"); - result = result.withSecondOfMinute(0); - assertFunction("date_trunc('minute', " + TIMESTAMP_LITERAL + ")", TIMESTAMP_MILLIS, sqlTimestampOf(result)); + assertThat(assertions.function("last_day_of_month", "TIMESTAMP '2019-01-01 03:00:00.000 " + timeZone + "'")) + .matches("DATE '2019-1-31'"); - result = result.withMinuteOfHour(0); - assertFunction("date_trunc('hour', " + TIMESTAMP_LITERAL + ")", TIMESTAMP_MILLIS, sqlTimestampOf(result)); + assertThat(assertions.function("last_day_of_month", "TIMESTAMP '2019-01-01 06:00:00.000 " + timeZone + "'")) + .matches("DATE '2019-1-31'"); - result = result.withHourOfDay(0); - assertFunction("date_trunc('day', " + TIMESTAMP_LITERAL + ")", TIMESTAMP_MILLIS, sqlTimestampOf(result)); + assertThat(assertions.function("last_day_of_month", "TIMESTAMP '2019-08-01 00:00:00.000 " + timeZone + "'")) + .matches("DATE '2019-8-31'"); - result = result.withDayOfMonth(20); - assertFunction("date_trunc('week', " + TIMESTAMP_LITERAL + ")", TIMESTAMP_MILLIS, sqlTimestampOf(result)); + assertThat(assertions.function("last_day_of_month", "TIMESTAMP '2019-08-01 17:00:00.000 " + timeZone + "'")) + .matches("DATE '2019-8-31'"); + + assertThat(assertions.function("last_day_of_month", "TIMESTAMP '2019-08-01 23:59:59.999 " + timeZone + "'")) + .matches("DATE '2019-8-31'"); + + assertThat(assertions.function("last_day_of_month", "TIMESTAMP '2019-08-31 23:59:59.999 " + timeZone + "'")) + .matches("DATE '2019-8-31'"); + }); + } - result = result.withDayOfMonth(1); - assertFunction("date_trunc('month', " + TIMESTAMP_LITERAL + ")", TIMESTAMP_MILLIS, sqlTimestampOf(result)); + @Test + public void testExtractFromInterval() + { + assertThat(assertions.expression("extract(second FROM INTERVAL '5' SECOND)")) + .isEqualTo(5L); - result = result.withMonthOfYear(7); - assertFunction("date_trunc('quarter', " + TIMESTAMP_LITERAL + ")", TIMESTAMP_MILLIS, sqlTimestampOf(result)); + assertThat(assertions.expression("extract(second FROM INTERVAL '65' SECOND)")) + .isEqualTo(5L); - result = result.withMonthOfYear(1); - assertFunction("date_trunc('year', " + TIMESTAMP_LITERAL + ")", TIMESTAMP_MILLIS, sqlTimestampOf(result)); + assertThat(assertions.expression("extract(minute FROM INTERVAL '4' MINUTE)")) + .isEqualTo(4L); - result = WEIRD_TIMESTAMP; - result = result.withMillisOfSecond(0); - assertFunction("date_trunc('second', " + WEIRD_TIMESTAMP_LITERAL + ")", TIMESTAMP_TZ_MILLIS, toTimestampWithTimeZone(result)); + assertThat(assertions.expression("extract(minute FROM INTERVAL '64' MINUTE)")) + .isEqualTo(4L); - result = result.withSecondOfMinute(0); - assertFunction("date_trunc('minute', " + WEIRD_TIMESTAMP_LITERAL + ")", TIMESTAMP_TZ_MILLIS, toTimestampWithTimeZone(result)); + assertThat(assertions.expression("extract(minute FROM INTERVAL '247' SECOND)")) + .isEqualTo(4L); - result = result.withMinuteOfHour(0); - assertFunction("date_trunc('hour', " + WEIRD_TIMESTAMP_LITERAL + ")", TIMESTAMP_TZ_MILLIS, toTimestampWithTimeZone(result)); + assertThat(assertions.expression("extract(hour FROM INTERVAL '3' HOUR)")) + .isEqualTo(3L); - result = result.withHourOfDay(0); - assertFunction("date_trunc('day', " + WEIRD_TIMESTAMP_LITERAL + ")", TIMESTAMP_TZ_MILLIS, toTimestampWithTimeZone(result)); + assertThat(assertions.expression("extract(hour FROM INTERVAL '27' HOUR)")) + .isEqualTo(3L); - result = result.withDayOfMonth(20); - assertFunction("date_trunc('week', " + WEIRD_TIMESTAMP_LITERAL + ")", TIMESTAMP_TZ_MILLIS, toTimestampWithTimeZone(result)); + assertThat(assertions.expression("extract(hour FROM INTERVAL '187' MINUTE)")) + .isEqualTo(3L); - result = result.withDayOfMonth(1); - assertFunction("date_trunc('month', " + WEIRD_TIMESTAMP_LITERAL + ")", TIMESTAMP_TZ_MILLIS, toTimestampWithTimeZone(result)); + assertThat(assertions.expression("extract(day FROM INTERVAL '2' DAY)")) + .isEqualTo(2L); - result = result.withMonthOfYear(7); - assertFunction("date_trunc('quarter', " + WEIRD_TIMESTAMP_LITERAL + ")", TIMESTAMP_TZ_MILLIS, toTimestampWithTimeZone(result)); + assertThat(assertions.expression("extract(day FROM INTERVAL '55' HOUR)")) + .isEqualTo(2L); - result = result.withMonthOfYear(1); - assertFunction("date_trunc('year', " + WEIRD_TIMESTAMP_LITERAL + ")", TIMESTAMP_TZ_MILLIS, toTimestampWithTimeZone(result)); - } + assertThat(assertions.expression("extract(month FROM INTERVAL '3' MONTH)")) + .isEqualTo(3L); - @Test - public void testTruncateTime() - { - LocalTime result = TIME; - result = result.withNano(0); - assertFunction("date_trunc('second', " + TIME_LITERAL + ")", TimeType.TIME_MILLIS, sqlTimeOf(result)); + assertThat(assertions.expression("extract(month FROM INTERVAL '15' MONTH)")) + .isEqualTo(3L); - result = result.withSecond(0); - assertFunction("date_trunc('minute', " + TIME_LITERAL + ")", TimeType.TIME_MILLIS, sqlTimeOf(result)); + assertThat(assertions.expression("extract(year FROM INTERVAL '2' YEAR)")) + .isEqualTo(2L); - result = result.withMinute(0); - assertFunction("date_trunc('hour', " + TIME_LITERAL + ")", TimeType.TIME_MILLIS, sqlTimeOf(result)); + assertThat(assertions.expression("extract(year FROM INTERVAL '29' MONTH)")) + .isEqualTo(2L); } @Test public void testTruncateDate() { - DateTime result = DATE; - assertFunction("date_trunc('day', " + DATE_LITERAL + ")", DateType.DATE, toDate(result)); - - result = result.withDayOfMonth(20); - assertFunction("date_trunc('week', " + DATE_LITERAL + ")", DateType.DATE, toDate(result)); + assertThat(assertions.expression("date_trunc('day', DATE '2001-08-22')")) + .matches("DATE '2001-08-22'"); - result = result.withDayOfMonth(1); - assertFunction("date_trunc('month', " + DATE_LITERAL + ")", DateType.DATE, toDate(result)); + assertThat(assertions.expression("date_trunc('week', DATE '2001-08-22')")) + .matches("DATE '2001-08-20'"); - result = result.withMonthOfYear(7); - assertFunction("date_trunc('quarter', " + DATE_LITERAL + ")", DateType.DATE, toDate(result)); + assertThat(assertions.expression("date_trunc('month', DATE '2001-08-22')")) + .matches("DATE '2001-08-01'"); - result = result.withMonthOfYear(1); - assertFunction("date_trunc('year', " + DATE_LITERAL + ")", DateType.DATE, toDate(result)); - } + assertThat(assertions.expression("date_trunc('quarter', DATE '2001-08-22')")) + .matches("DATE '2001-07-01'"); - @Test - public void testAddFieldToTimestamp() - { - assertFunction("date_add('millisecond', 3, " + TIMESTAMP_LITERAL + ")", TIMESTAMP_MILLIS, sqlTimestampOf(TIMESTAMP.plusMillis(3))); - assertFunction("date_add('second', 3, " + TIMESTAMP_LITERAL + ")", TIMESTAMP_MILLIS, sqlTimestampOf(TIMESTAMP.plusSeconds(3))); - assertFunction("date_add('minute', 3, " + TIMESTAMP_LITERAL + ")", TIMESTAMP_MILLIS, sqlTimestampOf(TIMESTAMP.plusMinutes(3))); - assertFunction("date_add('hour', 3, " + TIMESTAMP_LITERAL + ")", TIMESTAMP_MILLIS, sqlTimestampOf(TIMESTAMP.plusHours(3))); - assertFunction("date_add('hour', 23, " + TIMESTAMP_LITERAL + ")", TIMESTAMP_MILLIS, sqlTimestampOf(TIMESTAMP.plusHours(23))); - assertFunction("date_add('hour', -4, " + TIMESTAMP_LITERAL + ")", TIMESTAMP_MILLIS, sqlTimestampOf(TIMESTAMP.minusHours(4))); - assertFunction("date_add('hour', -23, " + TIMESTAMP_LITERAL + ")", TIMESTAMP_MILLIS, sqlTimestampOf(TIMESTAMP.minusHours(23))); - assertFunction("date_add('day', 3, " + TIMESTAMP_LITERAL + ")", TIMESTAMP_MILLIS, sqlTimestampOf(TIMESTAMP.plusDays(3))); - assertFunction("date_add('week', 3, " + TIMESTAMP_LITERAL + ")", TIMESTAMP_MILLIS, sqlTimestampOf(TIMESTAMP.plusWeeks(3))); - assertFunction("date_add('month', 3, " + TIMESTAMP_LITERAL + ")", TIMESTAMP_MILLIS, sqlTimestampOf(TIMESTAMP.plusMonths(3))); - assertFunction("date_add('quarter', 3, " + TIMESTAMP_LITERAL + ")", TIMESTAMP_MILLIS, sqlTimestampOf(TIMESTAMP.plusMonths(3 * 3))); - assertFunction("date_add('year', 3, " + TIMESTAMP_LITERAL + ")", TIMESTAMP_MILLIS, sqlTimestampOf(TIMESTAMP.plusYears(3))); - - assertFunction("date_add('millisecond', 3, " + WEIRD_TIMESTAMP_LITERAL + ")", TIMESTAMP_TZ_MILLIS, toTimestampWithTimeZone(WEIRD_TIMESTAMP.plusMillis(3))); - assertFunction("date_add('second', 3, " + WEIRD_TIMESTAMP_LITERAL + ")", TIMESTAMP_TZ_MILLIS, toTimestampWithTimeZone(WEIRD_TIMESTAMP.plusSeconds(3))); - assertFunction("date_add('minute', 3, " + WEIRD_TIMESTAMP_LITERAL + ")", TIMESTAMP_TZ_MILLIS, toTimestampWithTimeZone(WEIRD_TIMESTAMP.plusMinutes(3))); - assertFunction("date_add('hour', 3, " + WEIRD_TIMESTAMP_LITERAL + ")", TIMESTAMP_TZ_MILLIS, toTimestampWithTimeZone(WEIRD_TIMESTAMP.plusHours(3))); - assertFunction("date_add('day', 3, " + WEIRD_TIMESTAMP_LITERAL + ")", TIMESTAMP_TZ_MILLIS, toTimestampWithTimeZone(WEIRD_TIMESTAMP.plusDays(3))); - assertFunction("date_add('week', 3, " + WEIRD_TIMESTAMP_LITERAL + ")", TIMESTAMP_TZ_MILLIS, toTimestampWithTimeZone(WEIRD_TIMESTAMP.plusWeeks(3))); - assertFunction("date_add('month', 3, " + WEIRD_TIMESTAMP_LITERAL + ")", TIMESTAMP_TZ_MILLIS, toTimestampWithTimeZone(WEIRD_TIMESTAMP.plusMonths(3))); - assertFunction("date_add('quarter', 3, " + WEIRD_TIMESTAMP_LITERAL + ")", TIMESTAMP_TZ_MILLIS, toTimestampWithTimeZone(WEIRD_TIMESTAMP.plusMonths(3 * 3))); - assertFunction("date_add('year', 3, " + WEIRD_TIMESTAMP_LITERAL + ")", TIMESTAMP_TZ_MILLIS, toTimestampWithTimeZone(WEIRD_TIMESTAMP.plusYears(3))); + assertThat(assertions.expression("date_trunc('year', DATE '2001-08-22')")) + .matches("DATE '2001-01-01'"); } @Test public void testAddFieldToDate() { - assertFunction("date_add('day', 0, " + DATE_LITERAL + ")", DateType.DATE, toDate(DATE)); - assertFunction("date_add('day', 3, " + DATE_LITERAL + ")", DateType.DATE, toDate(DATE.plusDays(3))); - assertFunction("date_add('week', 3, " + DATE_LITERAL + ")", DateType.DATE, toDate(DATE.plusWeeks(3))); - assertFunction("date_add('month', 3, " + DATE_LITERAL + ")", DateType.DATE, toDate(DATE.plusMonths(3))); - assertFunction("date_add('quarter', 3, " + DATE_LITERAL + ")", DateType.DATE, toDate(DATE.plusMonths(3 * 3))); - assertFunction("date_add('year', 3, " + DATE_LITERAL + ")", DateType.DATE, toDate(DATE.plusYears(3))); - } + assertThat(assertions.function("date_add", "'day'", "0", "DATE '2001-08-22'")) + .matches("DATE '2001-08-22'"); - @Test - public void testAddFieldToTime() - { - assertFunction("date_add('millisecond', 0, " + TIME_LITERAL + ")", TimeType.TIME_MILLIS, sqlTimeOf(TIME)); - assertFunction("date_add('millisecond', 3, " + TIME_LITERAL + ")", TimeType.TIME_MILLIS, sqlTimeOf(TIME.plusNanos(3_000_000))); - assertFunction("date_add('second', 3, " + TIME_LITERAL + ")", TimeType.TIME_MILLIS, sqlTimeOf(TIME.plusSeconds(3))); - assertFunction("date_add('minute', 3, " + TIME_LITERAL + ")", TimeType.TIME_MILLIS, sqlTimeOf(TIME.plusMinutes(3))); - assertFunction("date_add('hour', 3, " + TIME_LITERAL + ")", TimeType.TIME_MILLIS, sqlTimeOf(TIME.plusHours(3))); - assertFunction("date_add('hour', 23, " + TIME_LITERAL + ")", TimeType.TIME_MILLIS, sqlTimeOf(TIME.plusHours(23))); - assertFunction("date_add('hour', -4, " + TIME_LITERAL + ")", TimeType.TIME_MILLIS, sqlTimeOf(TIME.minusHours(4))); - assertFunction("date_add('hour', -23, " + TIME_LITERAL + ")", TimeType.TIME_MILLIS, sqlTimeOf(TIME.minusHours(23))); - } + assertThat(assertions.function("date_add", "'day'", "3", "DATE '2001-08-22'")) + .matches("DATE '2001-08-25'"); - @Test - public void testDateDiffTimestamp() - { - DateTime baseDateTime = new DateTime(1960, 5, 3, 7, 2, 9, 678, UTC_TIME_ZONE); - String baseDateTimeLiteral = "TIMESTAMP '1960-05-03 07:02:09.678'"; - - assertFunction("date_diff('millisecond', " + baseDateTimeLiteral + ", " + TIMESTAMP_LITERAL + ")", BIGINT, millisBetween(baseDateTime, TIMESTAMP)); - assertFunction("date_diff('second', " + baseDateTimeLiteral + ", " + TIMESTAMP_LITERAL + ")", BIGINT, (long) secondsBetween(baseDateTime, TIMESTAMP).getSeconds()); - assertFunction("date_diff('minute', " + baseDateTimeLiteral + ", " + TIMESTAMP_LITERAL + ")", BIGINT, (long) minutesBetween(baseDateTime, TIMESTAMP).getMinutes()); - assertFunction("date_diff('hour', " + baseDateTimeLiteral + ", " + TIMESTAMP_LITERAL + ")", BIGINT, (long) hoursBetween(baseDateTime, TIMESTAMP).getHours()); - assertFunction("date_diff('day', " + baseDateTimeLiteral + ", " + TIMESTAMP_LITERAL + ")", BIGINT, (long) daysBetween(baseDateTime, TIMESTAMP).getDays()); - assertFunction("date_diff('week', " + baseDateTimeLiteral + ", " + TIMESTAMP_LITERAL + ")", BIGINT, (long) weeksBetween(baseDateTime, TIMESTAMP).getWeeks()); - assertFunction("date_diff('month', " + baseDateTimeLiteral + ", " + TIMESTAMP_LITERAL + ")", BIGINT, (long) monthsBetween(baseDateTime, TIMESTAMP).getMonths()); - assertFunction("date_diff('quarter', " + baseDateTimeLiteral + ", " + TIMESTAMP_LITERAL + ")", BIGINT, (long) monthsBetween(baseDateTime, TIMESTAMP).getMonths() / 3); - assertFunction("date_diff('year', " + baseDateTimeLiteral + ", " + TIMESTAMP_LITERAL + ")", BIGINT, (long) yearsBetween(baseDateTime, TIMESTAMP).getYears()); - - DateTime weirdBaseDateTime = new DateTime(1960, 5, 3, 7, 2, 9, 678, WEIRD_DATE_TIME_ZONE); - String weirdBaseDateTimeLiteral = "TIMESTAMP '1960-05-03 07:02:09.678 +07:09'"; - - assertFunction("date_diff('millisecond', " + weirdBaseDateTimeLiteral + ", " + WEIRD_TIMESTAMP_LITERAL + ")", - BIGINT, - millisBetween(weirdBaseDateTime, WEIRD_TIMESTAMP)); - assertFunction("date_diff('second', " + weirdBaseDateTimeLiteral + ", " + WEIRD_TIMESTAMP_LITERAL + ")", - BIGINT, - (long) secondsBetween(weirdBaseDateTime, WEIRD_TIMESTAMP).getSeconds()); - assertFunction("date_diff('minute', " + weirdBaseDateTimeLiteral + ", " + WEIRD_TIMESTAMP_LITERAL + ")", - BIGINT, - (long) minutesBetween(weirdBaseDateTime, WEIRD_TIMESTAMP).getMinutes()); - assertFunction("date_diff('hour', " + weirdBaseDateTimeLiteral + ", " + WEIRD_TIMESTAMP_LITERAL + ")", - BIGINT, - (long) hoursBetween(weirdBaseDateTime, WEIRD_TIMESTAMP).getHours()); - assertFunction("date_diff('day', " + weirdBaseDateTimeLiteral + ", " + WEIRD_TIMESTAMP_LITERAL + ")", - BIGINT, - (long) daysBetween(weirdBaseDateTime, WEIRD_TIMESTAMP).getDays()); - assertFunction("date_diff('week', " + weirdBaseDateTimeLiteral + ", " + WEIRD_TIMESTAMP_LITERAL + ")", - BIGINT, - (long) weeksBetween(weirdBaseDateTime, WEIRD_TIMESTAMP).getWeeks()); - assertFunction("date_diff('month', " + weirdBaseDateTimeLiteral + ", " + WEIRD_TIMESTAMP_LITERAL + ")", - BIGINT, - (long) monthsBetween(weirdBaseDateTime, WEIRD_TIMESTAMP).getMonths()); - assertFunction("date_diff('quarter', " + weirdBaseDateTimeLiteral + ", " + WEIRD_TIMESTAMP_LITERAL + ")", - BIGINT, - (long) monthsBetween(weirdBaseDateTime, WEIRD_TIMESTAMP).getMonths() / 3); - assertFunction("date_diff('year', " + weirdBaseDateTimeLiteral + ", " + WEIRD_TIMESTAMP_LITERAL + ")", - BIGINT, - (long) yearsBetween(weirdBaseDateTime, WEIRD_TIMESTAMP).getYears()); + assertThat(assertions.function("date_add", "'week'", "3", "DATE '2001-08-22'")) + .matches("DATE '2001-09-12'"); + + assertThat(assertions.function("date_add", "'month'", "3", "DATE '2001-08-22'")) + .matches("DATE '2001-11-22'"); + + assertThat(assertions.function("date_add", "'quarter'", "3", "DATE '2001-08-22'")) + .matches("DATE '2002-05-22'"); + + assertThat(assertions.function("date_add", "'year'", "3", "DATE '2001-08-22'")) + .matches("DATE '2004-08-22'"); } @Test public void testDateDiffDate() { - DateTime baseDateTime = new DateTime(1960, 5, 3, 0, 0, 0, 0, DateTimeZone.UTC); - String baseDateTimeLiteral = "DATE '1960-05-03'"; - - assertFunction("date_diff('day', " + baseDateTimeLiteral + ", " + DATE_LITERAL + ")", BIGINT, (long) daysBetween(baseDateTime, DATE).getDays()); - assertFunction("date_diff('week', " + baseDateTimeLiteral + ", " + DATE_LITERAL + ")", BIGINT, (long) weeksBetween(baseDateTime, DATE).getWeeks()); - assertFunction("date_diff('month', " + baseDateTimeLiteral + ", " + DATE_LITERAL + ")", BIGINT, (long) monthsBetween(baseDateTime, DATE).getMonths()); - assertFunction("date_diff('quarter', " + baseDateTimeLiteral + ", " + DATE_LITERAL + ")", BIGINT, (long) monthsBetween(baseDateTime, DATE).getMonths() / 3); - assertFunction("date_diff('year', " + baseDateTimeLiteral + ", " + DATE_LITERAL + ")", BIGINT, (long) yearsBetween(baseDateTime, DATE).getYears()); - } + assertThat(assertions.function("date_diff", "'day'", "DATE '1960-05-03'", "DATE '2001-08-22'")) + .isEqualTo(15086L); - @Test - public void testDateDiffTime() - { - LocalTime baseDateTime = LocalTime.of(7, 2, 9, 678_000_000); - String baseDateTimeLiteral = "TIME '07:02:09.678'"; + assertThat(assertions.function("date_diff", "'week'", "DATE '1960-05-03'", "DATE '2001-08-22'")) + .isEqualTo(2155L); - assertFunction("date_diff('millisecond', " + baseDateTimeLiteral + ", " + TIME_LITERAL + ")", BIGINT, millisBetween(baseDateTime, TIME)); - assertFunction("date_diff('second', " + baseDateTimeLiteral + ", " + TIME_LITERAL + ")", BIGINT, secondsBetween(baseDateTime, TIME)); - assertFunction("date_diff('minute', " + baseDateTimeLiteral + ", " + TIME_LITERAL + ")", BIGINT, minutesBetween(baseDateTime, TIME)); - assertFunction("date_diff('hour', " + baseDateTimeLiteral + ", " + TIME_LITERAL + ")", BIGINT, hoursBetween(baseDateTime, TIME)); - } + assertThat(assertions.function("date_diff", "'month'", "DATE '1960-05-03'", "DATE '2001-08-22'")) + .isEqualTo(495L); - @Test - public void testDateDiffTimeWithTimeZone() - { - OffsetTime weirdBaseDateTime = OffsetTime.of(7, 2, 9, 678_000_000, WEIRD_ZONE); - String weirdBaseDateTimeLiteral = "TIME '07:02:09.678 +07:09'"; + assertThat(assertions.function("date_diff", "'quarter'", "DATE '1960-05-03'", "DATE '2001-08-22'")) + .isEqualTo(165L); - assertFunction("date_diff('millisecond', " + weirdBaseDateTimeLiteral + ", " + WEIRD_TIME_LITERAL + ")", BIGINT, millisBetween(weirdBaseDateTime, WEIRD_TIME)); - assertFunction("date_diff('second', " + weirdBaseDateTimeLiteral + ", " + WEIRD_TIME_LITERAL + ")", BIGINT, secondsBetween(weirdBaseDateTime, WEIRD_TIME)); - assertFunction("date_diff('minute', " + weirdBaseDateTimeLiteral + ", " + WEIRD_TIME_LITERAL + ")", BIGINT, minutesBetween(weirdBaseDateTime, WEIRD_TIME)); - assertFunction("date_diff('hour', " + weirdBaseDateTimeLiteral + ", " + WEIRD_TIME_LITERAL + ")", BIGINT, hoursBetween(weirdBaseDateTime, WEIRD_TIME)); + assertThat(assertions.function("date_diff", "'year'", "DATE '1960-05-03'", "DATE '2001-08-22'")) + .isEqualTo(41L); } @Test public void testParseDatetime() { // Modern date - assertFunction("parse_datetime('2020-08-18 03:04:05.678', 'yyyy-MM-dd HH:mm:ss.SSS')", - TIMESTAMP_TZ_MILLIS, - toTimestampWithTimeZone(new DateTime(2020, 8, 18, 3, 4, 5, 678, DATE_TIME_ZONE))); + assertThat(assertions.function("parse_datetime", "'2020-08-18 03:04:05.678'", "'yyyy-MM-dd HH:mm:ss.SSS'")) + .matches("TIMESTAMP '2020-08-18 03:04:05.678 Pacific/Apia'"); // Before epoch - assertFunction("parse_datetime('1960/01/22 03:04', 'yyyy/MM/dd HH:mm')", - TIMESTAMP_TZ_MILLIS, - toTimestampWithTimeZone(new DateTime(1960, 1, 22, 3, 4, 0, 0, DATE_TIME_ZONE))); + assertThat(assertions.function("parse_datetime", "'1960/01/22 03:04'", "'yyyy/MM/dd HH:mm'")) + .matches("TIMESTAMP '1960-01-22 03:04:00.000 Pacific/Apia'"); // With named zone - assertFunction("parse_datetime('1960/01/22 03:04 Asia/Oral', 'yyyy/MM/dd HH:mm ZZZZZ')", - TIMESTAMP_TZ_MILLIS, - toTimestampWithTimeZone(new DateTime(1960, 1, 22, 3, 4, 0, 0, DateTimeZone.forID("Asia/Oral")))); + assertThat(assertions.function("parse_datetime", "'1960/01/22 03:04 Asia/Oral'", "'yyyy/MM/dd HH:mm ZZZZZ'")) + .matches("TIMESTAMP '1960-01-22 03:04:00.000 Asia/Oral'"); // With zone offset - assertFunction("parse_datetime('1960/01/22 03:04 +0500', 'yyyy/MM/dd HH:mm Z')", - TIMESTAMP_TZ_MILLIS, - toTimestampWithTimeZone(new DateTime(1960, 1, 22, 3, 4, 0, 0, DateTimeZone.forOffsetHours(5)))); - } - - @Test - public void testFormatDatetime() - { - assertFunction("format_datetime(" + TIMESTAMP_LITERAL + ", 'YYYY/MM/dd HH:mm')", VARCHAR, "2001/08/22 03:04"); - assertFunction("format_datetime(" + WEIRD_TIMESTAMP_LITERAL + ", 'YYYY/MM/dd HH:mm')", VARCHAR, "2001/08/22 03:04"); - assertFunction("format_datetime(" + WEIRD_TIMESTAMP_LITERAL + ", 'YYYY/MM/dd HH:mm ZZZZ')", VARCHAR, "2001/08/22 03:04 +07:09"); + assertThat(assertions.function("parse_datetime", "'1960/01/22 03:04 +0500'", "'yyyy/MM/dd HH:mm Z'")) + .matches("TIMESTAMP '1960-01-22 03:04:00.000 +05:00'"); } @Test public void testDateFormat() { - String dateTimeLiteral = "TIMESTAMP '2001-01-09 13:04:05.321'"; - - assertFunction("date_format(" + dateTimeLiteral + ", '%a')", VARCHAR, "Tue"); - assertFunction("date_format(" + dateTimeLiteral + ", '%b')", VARCHAR, "Jan"); - assertFunction("date_format(" + dateTimeLiteral + ", '%c')", VARCHAR, "1"); - assertFunction("date_format(" + dateTimeLiteral + ", '%d')", VARCHAR, "09"); - assertFunction("date_format(" + dateTimeLiteral + ", '%e')", VARCHAR, "9"); - assertFunction("date_format(" + dateTimeLiteral + ", '%f')", VARCHAR, "321000"); - assertFunction("date_format(" + dateTimeLiteral + ", '%H')", VARCHAR, "13"); - assertFunction("date_format(" + dateTimeLiteral + ", '%h')", VARCHAR, "01"); - assertFunction("date_format(" + dateTimeLiteral + ", '%I')", VARCHAR, "01"); - assertFunction("date_format(" + dateTimeLiteral + ", '%i')", VARCHAR, "04"); - assertFunction("date_format(" + dateTimeLiteral + ", '%j')", VARCHAR, "009"); - assertFunction("date_format(" + dateTimeLiteral + ", '%k')", VARCHAR, "13"); - assertFunction("date_format(" + dateTimeLiteral + ", '%l')", VARCHAR, "1"); - assertFunction("date_format(" + dateTimeLiteral + ", '%M')", VARCHAR, "January"); - assertFunction("date_format(" + dateTimeLiteral + ", '%m')", VARCHAR, "01"); - assertFunction("date_format(" + dateTimeLiteral + ", '%p')", VARCHAR, "PM"); - assertFunction("date_format(" + dateTimeLiteral + ", '%r')", VARCHAR, "01:04:05 PM"); - assertFunction("date_format(" + dateTimeLiteral + ", '%S')", VARCHAR, "05"); - assertFunction("date_format(" + dateTimeLiteral + ", '%s')", VARCHAR, "05"); - assertFunction("date_format(" + dateTimeLiteral + ", '%T')", VARCHAR, "13:04:05"); - assertFunction("date_format(" + dateTimeLiteral + ", '%v')", VARCHAR, "02"); - assertFunction("date_format(" + dateTimeLiteral + ", '%W')", VARCHAR, "Tuesday"); - assertFunction("date_format(" + dateTimeLiteral + ", '%Y')", VARCHAR, "2001"); - assertFunction("date_format(" + dateTimeLiteral + ", '%y')", VARCHAR, "01"); - assertFunction("date_format(" + dateTimeLiteral + ", '%%')", VARCHAR, "%"); - assertFunction("date_format(" + dateTimeLiteral + ", 'foo')", VARCHAR, "foo"); - assertFunction("date_format(" + dateTimeLiteral + ", '%g')", VARCHAR, "g"); - assertFunction("date_format(" + dateTimeLiteral + ", '%4')", VARCHAR, "4"); - assertFunction("date_format(" + dateTimeLiteral + ", '%x %v')", VARCHAR, "2001 02"); - assertFunction("date_format(" + dateTimeLiteral + ", '%Y年%m月%d日')", VARCHAR, "2001年01月09日"); - - String weirdDateTimeLiteral = "TIMESTAMP '2001-01-09 13:04:05.321 +07:09'"; - - assertFunction("date_format(" + weirdDateTimeLiteral + ", '%a')", VARCHAR, "Tue"); - assertFunction("date_format(" + weirdDateTimeLiteral + ", '%b')", VARCHAR, "Jan"); - assertFunction("date_format(" + weirdDateTimeLiteral + ", '%c')", VARCHAR, "1"); - assertFunction("date_format(" + weirdDateTimeLiteral + ", '%d')", VARCHAR, "09"); - assertFunction("date_format(" + weirdDateTimeLiteral + ", '%e')", VARCHAR, "9"); - assertFunction("date_format(" + weirdDateTimeLiteral + ", '%f')", VARCHAR, "321000"); - assertFunction("date_format(" + weirdDateTimeLiteral + ", '%H')", VARCHAR, "13"); - assertFunction("date_format(" + weirdDateTimeLiteral + ", '%h')", VARCHAR, "01"); - assertFunction("date_format(" + weirdDateTimeLiteral + ", '%I')", VARCHAR, "01"); - assertFunction("date_format(" + weirdDateTimeLiteral + ", '%i')", VARCHAR, "04"); - assertFunction("date_format(" + weirdDateTimeLiteral + ", '%j')", VARCHAR, "009"); - assertFunction("date_format(" + weirdDateTimeLiteral + ", '%k')", VARCHAR, "13"); - assertFunction("date_format(" + weirdDateTimeLiteral + ", '%l')", VARCHAR, "1"); - assertFunction("date_format(" + weirdDateTimeLiteral + ", '%M')", VARCHAR, "January"); - assertFunction("date_format(" + weirdDateTimeLiteral + ", '%m')", VARCHAR, "01"); - assertFunction("date_format(" + weirdDateTimeLiteral + ", '%p')", VARCHAR, "PM"); - assertFunction("date_format(" + weirdDateTimeLiteral + ", '%r')", VARCHAR, "01:04:05 PM"); - assertFunction("date_format(" + weirdDateTimeLiteral + ", '%S')", VARCHAR, "05"); - assertFunction("date_format(" + weirdDateTimeLiteral + ", '%s')", VARCHAR, "05"); - assertFunction("date_format(" + weirdDateTimeLiteral + ", '%T')", VARCHAR, "13:04:05"); - assertFunction("date_format(" + weirdDateTimeLiteral + ", '%v')", VARCHAR, "02"); - assertFunction("date_format(" + weirdDateTimeLiteral + ", '%W')", VARCHAR, "Tuesday"); - assertFunction("date_format(" + weirdDateTimeLiteral + ", '%Y')", VARCHAR, "2001"); - assertFunction("date_format(" + weirdDateTimeLiteral + ", '%y')", VARCHAR, "01"); - assertFunction("date_format(" + weirdDateTimeLiteral + ", '%%')", VARCHAR, "%"); - assertFunction("date_format(" + weirdDateTimeLiteral + ", 'foo')", VARCHAR, "foo"); - assertFunction("date_format(" + weirdDateTimeLiteral + ", '%g')", VARCHAR, "g"); - assertFunction("date_format(" + weirdDateTimeLiteral + ", '%4')", VARCHAR, "4"); - assertFunction("date_format(" + weirdDateTimeLiteral + ", '%x %v')", VARCHAR, "2001 02"); - assertFunction("date_format(" + weirdDateTimeLiteral + ", '%Y年%m月%d日')", VARCHAR, "2001年01月09日"); - - assertFunction("date_format(TIMESTAMP '2001-01-09 13:04:05.32', '%f')", VARCHAR, "320000"); - assertFunction("date_format(TIMESTAMP '2001-01-09 00:04:05.32', '%k')", VARCHAR, "0"); - - assertInvalidFunction("date_format(DATE '2001-01-09', '%D')", "%D not supported in date format string"); - assertInvalidFunction("date_format(DATE '2001-01-09', '%U')", "%U not supported in date format string"); - assertInvalidFunction("date_format(DATE '2001-01-09', '%u')", "%u not supported in date format string"); - assertInvalidFunction("date_format(DATE '2001-01-09', '%V')", "%V not supported in date format string"); - assertInvalidFunction("date_format(DATE '2001-01-09', '%w')", "%w not supported in date format string"); - assertInvalidFunction("date_format(DATE '2001-01-09', '%X')", "%X not supported in date format string"); + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321'", "'%a'")) + .hasType(VARCHAR) + .isEqualTo("Tue"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321'", "'%b'")) + .hasType(VARCHAR) + .isEqualTo("Jan"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321'", "'%c'")) + .hasType(VARCHAR) + .isEqualTo("1"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321'", "'%d'")) + .hasType(VARCHAR) + .isEqualTo("09"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321'", "'%e'")) + .hasType(VARCHAR) + .isEqualTo("9"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321'", "'%f'")) + .hasType(VARCHAR) + .isEqualTo("321000"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321'", "'%H'")) + .hasType(VARCHAR) + .isEqualTo("13"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321'", "'%h'")) + .hasType(VARCHAR) + .isEqualTo("01"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321'", "'%I'")) + .hasType(VARCHAR) + .isEqualTo("01"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321'", "'%i'")) + .hasType(VARCHAR) + .isEqualTo("04"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321'", "'%j'")) + .hasType(VARCHAR) + .isEqualTo("009"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321'", "'%k'")) + .hasType(VARCHAR) + .isEqualTo("13"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321'", "'%l'")) + .hasType(VARCHAR) + .isEqualTo("1"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321'", "'%M'")) + .hasType(VARCHAR) + .isEqualTo("January"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321'", "'%m'")) + .hasType(VARCHAR) + .isEqualTo("01"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321'", "'%p'")) + .hasType(VARCHAR) + .isEqualTo("PM"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321'", "'%r'")) + .hasType(VARCHAR) + .isEqualTo("01:04:05 PM"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321'", "'%S'")) + .hasType(VARCHAR) + .isEqualTo("05"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321'", "'%s'")) + .hasType(VARCHAR) + .isEqualTo("05"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321'", "'%T'")) + .hasType(VARCHAR) + .isEqualTo("13:04:05"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321'", "'%v'")) + .hasType(VARCHAR) + .isEqualTo("02"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321'", "'%W'")) + .hasType(VARCHAR) + .isEqualTo("Tuesday"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321'", "'%Y'")) + .hasType(VARCHAR) + .isEqualTo("2001"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321'", "'%y'")) + .hasType(VARCHAR) + .isEqualTo("01"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321'", "'%%'")) + .hasType(VARCHAR) + .isEqualTo("%"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321'", "'foo'")) + .hasType(VARCHAR) + .isEqualTo("foo"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321'", "'%g'")) + .hasType(VARCHAR) + .isEqualTo("g"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321'", "'%4'")) + .hasType(VARCHAR) + .isEqualTo("4"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321'", "'%x %v'")) + .hasType(VARCHAR) + .isEqualTo("2001 02"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321'", "'%Y年%m月%d日'")) + .hasType(VARCHAR) + .isEqualTo("2001年01月09日"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321 +07:09'", "'%a'")) + .hasType(VARCHAR) + .isEqualTo("Tue"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321 +07:09'", "'%b'")) + .hasType(VARCHAR) + .isEqualTo("Jan"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321 +07:09'", "'%c'")) + .hasType(VARCHAR) + .isEqualTo("1"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321 +07:09'", "'%d'")) + .hasType(VARCHAR) + .isEqualTo("09"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321 +07:09'", "'%e'")) + .hasType(VARCHAR) + .isEqualTo("9"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321 +07:09'", "'%f'")) + .hasType(VARCHAR) + .isEqualTo("321000"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321 +07:09'", "'%H'")) + .hasType(VARCHAR) + .isEqualTo("13"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321 +07:09'", "'%h'")) + .hasType(VARCHAR) + .isEqualTo("01"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321 +07:09'", "'%I'")) + .hasType(VARCHAR) + .isEqualTo("01"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321 +07:09'", "'%i'")) + .hasType(VARCHAR) + .isEqualTo("04"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321 +07:09'", "'%j'")) + .hasType(VARCHAR) + .isEqualTo("009"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321 +07:09'", "'%k'")) + .hasType(VARCHAR) + .isEqualTo("13"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321 +07:09'", "'%l'")) + .hasType(VARCHAR) + .isEqualTo("1"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321 +07:09'", "'%M'")) + .hasType(VARCHAR) + .isEqualTo("January"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321 +07:09'", "'%m'")) + .hasType(VARCHAR) + .isEqualTo("01"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321 +07:09'", "'%p'")) + .hasType(VARCHAR) + .isEqualTo("PM"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321 +07:09'", "'%r'")) + .hasType(VARCHAR) + .isEqualTo("01:04:05 PM"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321 +07:09'", "'%S'")) + .hasType(VARCHAR) + .isEqualTo("05"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321 +07:09'", "'%s'")) + .hasType(VARCHAR) + .isEqualTo("05"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321 +07:09'", "'%T'")) + .hasType(VARCHAR) + .isEqualTo("13:04:05"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321 +07:09'", "'%v'")) + .hasType(VARCHAR) + .isEqualTo("02"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321 +07:09'", "'%W'")) + .hasType(VARCHAR) + .isEqualTo("Tuesday"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321 +07:09'", "'%Y'")) + .hasType(VARCHAR) + .isEqualTo("2001"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321 +07:09'", "'%y'")) + .hasType(VARCHAR) + .isEqualTo("01"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321 +07:09'", "'%%'")) + .hasType(VARCHAR) + .isEqualTo("%"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321 +07:09'", "'foo'")) + .hasType(VARCHAR) + .isEqualTo("foo"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321 +07:09'", "'%g'")) + .hasType(VARCHAR) + .isEqualTo("g"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321 +07:09'", "'%4'")) + .hasType(VARCHAR) + .isEqualTo("4"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321 +07:09'", "'%x %v'")) + .hasType(VARCHAR) + .isEqualTo("2001 02"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.321 +07:09'", "'%Y年%m月%d日'")) + .hasType(VARCHAR) + .isEqualTo("2001年01月09日"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 13:04:05.32'", "'%f'")) + .hasType(VARCHAR) + .isEqualTo("320000"); + + assertThat(assertions.function("date_format", "TIMESTAMP '2001-01-09 00:04:05.32'", "'%k'")) + .hasType(VARCHAR) + .isEqualTo("0"); + + assertTrinoExceptionThrownBy(() -> assertions.function("date_format", "DATE '2001-01-09'", "'%D'").evaluate()) + .hasMessage("%D not supported in date format string"); + + assertTrinoExceptionThrownBy(() -> assertions.function("date_format", "DATE '2001-01-09'", "'%U'").evaluate()) + .hasMessage("%U not supported in date format string"); + + assertTrinoExceptionThrownBy(() -> assertions.function("date_format", "DATE '2001-01-09'", "'%u'").evaluate()) + .hasMessage("%u not supported in date format string"); + + assertTrinoExceptionThrownBy(() -> assertions.function("date_format", "DATE '2001-01-09'", "'%V'").evaluate()) + .hasMessage("%V not supported in date format string"); + + assertTrinoExceptionThrownBy(() -> assertions.function("date_format", "DATE '2001-01-09'", "'%w'").evaluate()) + .hasMessage("%w not supported in date format string"); + + assertTrinoExceptionThrownBy(() -> assertions.function("date_format", "DATE '2001-01-09'", "'%X'").evaluate()) + .hasMessage("%X not supported in date format string"); } @Test public void testDateParse() { - assertFunction("date_parse('2013', '%Y')", - TIMESTAMP_MILLIS, - sqlTimestampOf(3, 2013, 1, 1, 0, 0, 0, 0)); - assertFunction("date_parse('2013-05', '%Y-%m')", - TIMESTAMP_MILLIS, - sqlTimestampOf(3, 2013, 5, 1, 0, 0, 0, 0)); - assertFunction("date_parse('2013-05-17', '%Y-%m-%d')", - TIMESTAMP_MILLIS, - sqlTimestampOf(3, 2013, 5, 17, 0, 0, 0, 0)); - assertFunction("date_parse('2013-05-17 12:35:10', '%Y-%m-%d %h:%i:%s')", - TIMESTAMP_MILLIS, - sqlTimestampOf(3, 2013, 5, 17, 0, 35, 10, 0)); - assertFunction("date_parse('2013-05-17 12:35:10 PM', '%Y-%m-%d %h:%i:%s %p')", - TIMESTAMP_MILLIS, - sqlTimestampOf(3, 2013, 5, 17, 12, 35, 10, 0)); - assertFunction("date_parse('2013-05-17 12:35:10 AM', '%Y-%m-%d %h:%i:%s %p')", - TIMESTAMP_MILLIS, - sqlTimestampOf(3, 2013, 5, 17, 0, 35, 10, 0)); - - assertFunction("date_parse('2013-05-17 00:35:10', '%Y-%m-%d %H:%i:%s')", - TIMESTAMP_MILLIS, - sqlTimestampOf(3, 2013, 5, 17, 0, 35, 10, 0)); - assertFunction("date_parse('2013-05-17 23:35:10', '%Y-%m-%d %H:%i:%s')", - TIMESTAMP_MILLIS, - sqlTimestampOf(3, 2013, 5, 17, 23, 35, 10, 0)); - assertFunction("date_parse('abc 2013-05-17 fff 23:35:10 xyz', 'abc %Y-%m-%d fff %H:%i:%s xyz')", - TIMESTAMP_MILLIS, - sqlTimestampOf(3, 2013, 5, 17, 23, 35, 10, 0)); - - assertFunction("date_parse('2013 14', '%Y %y')", - TIMESTAMP_MILLIS, - sqlTimestampOf(3, 2014, 1, 1, 0, 0, 0, 0)); - - assertFunction("date_parse('1998 53', '%x %v')", - TIMESTAMP_MILLIS, - sqlTimestampOf(3, 1998, 12, 28, 0, 0, 0, 0)); - - assertFunction("date_parse('1.1', '%s.%f')", - TIMESTAMP_MILLIS, - sqlTimestampOf(3, 1970, 1, 1, 0, 0, 1, 100)); - assertFunction("date_parse('1.01', '%s.%f')", - TIMESTAMP_MILLIS, - sqlTimestampOf(3, 1970, 1, 1, 0, 0, 1, 10)); - assertFunction("date_parse('1.2006', '%s.%f')", - TIMESTAMP_MILLIS, - sqlTimestampOf(3, 1970, 1, 1, 0, 0, 1, 200)); - assertFunction("date_parse('59.123456789', '%s.%f')", - TIMESTAMP_MILLIS, - sqlTimestampOf(3, 1970, 1, 1, 0, 0, 59, 123)); - - assertFunction("date_parse('0', '%k')", - TIMESTAMP_MILLIS, - sqlTimestampOf(3, 1970, 1, 1, 0, 0, 0, 0)); - - assertFunction("date_parse('28-JAN-16 11.45.46.421000 PM','%d-%b-%y %l.%i.%s.%f %p')", - TIMESTAMP_MILLIS, - sqlTimestampOf(3, 2016, 1, 28, 23, 45, 46, 421)); - assertFunction("date_parse('11-DEC-70 11.12.13.456000 AM','%d-%b-%y %l.%i.%s.%f %p')", - TIMESTAMP_MILLIS, - sqlTimestampOf(3, 1970, 12, 11, 11, 12, 13, 456)); - assertFunction("date_parse('31-MAY-69 04.59.59.999000 AM','%d-%b-%y %l.%i.%s.%f %p')", - TIMESTAMP_MILLIS, - sqlTimestampOf(3, 2069, 5, 31, 4, 59, 59, 999)); - - assertInvalidFunction("date_parse('', '%D')", "%D not supported in date format string"); - assertInvalidFunction("date_parse('', '%U')", "%U not supported in date format string"); - assertInvalidFunction("date_parse('', '%u')", "%u not supported in date format string"); - assertInvalidFunction("date_parse('', '%V')", "%V not supported in date format string"); - assertInvalidFunction("date_parse('', '%w')", "%w not supported in date format string"); - assertInvalidFunction("date_parse('', '%X')", "%X not supported in date format string"); - - assertInvalidFunction("date_parse('3.0123456789', '%s.%f')", "Invalid format: \"3.0123456789\" is malformed at \"9\""); - assertInvalidFunction("date_parse('1970-01-01', '')", "Both printing and parsing not supported"); + assertThat(assertions.function("date_parse", "'2013'", "'%Y'")) + .matches("TIMESTAMP '2013-01-01 00:00:00.000'"); + + assertThat(assertions.function("date_parse", "'2013-05'", "'%Y-%m'")) + .matches("TIMESTAMP '2013-05-01 00:00:00.000'"); + + assertThat(assertions.function("date_parse", "'2013-05-17'", "'%Y-%m-%d'")) + .matches("TIMESTAMP '2013-05-17 00:00:00.000'"); + + assertThat(assertions.function("date_parse", "'2013-05-17 12:35:10'", "'%Y-%m-%d %h:%i:%s'")) + .matches("TIMESTAMP '2013-05-17 00:35:10.000'"); + + assertThat(assertions.function("date_parse", "'2013-05-17 12:35:10 PM'", "'%Y-%m-%d %h:%i:%s %p'")) + .matches("TIMESTAMP '2013-05-17 12:35:10.000'"); + + assertThat(assertions.function("date_parse", "'2013-05-17 12:35:10 AM'", "'%Y-%m-%d %h:%i:%s %p'")) + .matches("TIMESTAMP '2013-05-17 00:35:10.000'"); + + assertThat(assertions.function("date_parse", "'2013-05-17 00:35:10'", "'%Y-%m-%d %H:%i:%s'")) + .matches("TIMESTAMP '2013-05-17 00:35:10.000'"); + + assertThat(assertions.function("date_parse", "'2013-05-17 23:35:10'", "'%Y-%m-%d %H:%i:%s'")) + .matches("TIMESTAMP '2013-05-17 23:35:10.000'"); + + assertThat(assertions.function("date_parse", "'abc 2013-05-17 fff 23:35:10 xyz'", "'abc %Y-%m-%d fff %H:%i:%s xyz'")) + .matches("TIMESTAMP '2013-05-17 23:35:10.000'"); + + assertThat(assertions.function("date_parse", "'2013 14'", "'%Y %y'")) + .matches("TIMESTAMP '2014-01-01 00:00:00.000'"); + + assertThat(assertions.function("date_parse", "'1998 53'", "'%x %v'")) + .matches("TIMESTAMP '1998-12-28 00:00:00.000'"); + + assertThat(assertions.function("date_parse", "'1.1'", "'%s.%f'")) + .matches("TIMESTAMP '1970-01-01 00:00:01.100'"); + + assertThat(assertions.function("date_parse", "'1.01'", "'%s.%f'")) + .matches("TIMESTAMP '1970-01-01 00:00:01.010'"); + + assertThat(assertions.function("date_parse", "'1.2006'", "'%s.%f'")) + .matches("TIMESTAMP '1970-01-01 00:00:01.200'"); + + assertThat(assertions.function("date_parse", "'59.123456789'", "'%s.%f'")) + .matches("TIMESTAMP '1970-01-01 00:00:59.123'"); + + assertThat(assertions.function("date_parse", "'0'", "'%k'")) + .matches("TIMESTAMP '1970-01-01 00:00:00.000'"); + + assertThat(assertions.function("date_parse", "'28-JAN-16 11.45.46.421000 PM'", "'%d-%b-%y %l.%i.%s.%f %p'")) + .matches("TIMESTAMP '2016-01-28 23:45:46.421'"); + + assertThat(assertions.function("date_parse", "'11-DEC-70 11.12.13.456000 AM'", "'%d-%b-%y %l.%i.%s.%f %p'")) + .matches("TIMESTAMP '1970-12-11 11:12:13.456'"); + + assertThat(assertions.function("date_parse", "'31-MAY-69 04.59.59.999000 AM'", "'%d-%b-%y %l.%i.%s.%f %p'")) + .matches("TIMESTAMP '2069-05-31 04:59:59.999'"); + + assertTrinoExceptionThrownBy(() -> assertions.function("date_parse", "''", "'%D'").evaluate()) + .hasMessage("%D not supported in date format string"); + + assertTrinoExceptionThrownBy(() -> assertions.function("date_parse", "''", "'%U'").evaluate()) + .hasMessage("%U not supported in date format string"); + + assertTrinoExceptionThrownBy(() -> assertions.function("date_parse", "''", "'%u'").evaluate()) + .hasMessage("%u not supported in date format string"); + + assertTrinoExceptionThrownBy(() -> assertions.function("date_parse", "''", "'%V'").evaluate()) + .hasMessage("%V not supported in date format string"); + + assertTrinoExceptionThrownBy(() -> assertions.function("date_parse", "''", "'%w'").evaluate()) + .hasMessage("%w not supported in date format string"); + + assertTrinoExceptionThrownBy(() -> assertions.function("date_parse", "''", "'%X'").evaluate()) + .hasMessage("%X not supported in date format string"); + + assertTrinoExceptionThrownBy(() -> assertions.function("date_parse", "'3.0123456789'", "'%s.%f'").evaluate()) + .hasMessage("Invalid format: \"3.0123456789\" is malformed at \"9\""); + + assertTrinoExceptionThrownBy(() -> assertions.function("date_parse", "'1970-01-01'", "''").evaluate()) + .hasMessage("Both printing and parsing not supported"); } @Test public void testLocale() { - Locale locale = Locale.KOREAN; - Session localeSession = Session.builder(this.session) - .setTimeZoneKey(TIME_ZONE_KEY) - .setLocale(locale) + Session session = Session.builder(assertions.getDefaultSession()) + .setLocale(Locale.KOREAN) .build(); - try (FunctionAssertions localeAssertions = new FunctionAssertions(localeSession)) { - String dateTimeLiteral = "TIMESTAMP '2001-01-09 13:04:05.321'"; - - localeAssertions.assertFunction("date_format(" + dateTimeLiteral + ", '%a')", VARCHAR, "화"); - localeAssertions.assertFunction("date_format(" + dateTimeLiteral + ", '%W')", VARCHAR, "화요일"); - localeAssertions.assertFunction("date_format(" + dateTimeLiteral + ", '%p')", VARCHAR, "오후"); - localeAssertions.assertFunction("date_format(" + dateTimeLiteral + ", '%r')", VARCHAR, "01:04:05 오후"); - localeAssertions.assertFunction("date_format(" + dateTimeLiteral + ", '%b')", VARCHAR, "1월"); - localeAssertions.assertFunction("date_format(" + dateTimeLiteral + ", '%M')", VARCHAR, "1월"); - - localeAssertions.assertFunction("format_datetime(" + dateTimeLiteral + ", 'EEE')", VARCHAR, "화"); - localeAssertions.assertFunction("format_datetime(" + dateTimeLiteral + ", 'EEEE')", VARCHAR, "화요일"); - localeAssertions.assertFunction("format_datetime(" + dateTimeLiteral + ", 'a')", VARCHAR, "오후"); - localeAssertions.assertFunction("format_datetime(" + dateTimeLiteral + ", 'MMM')", VARCHAR, "1월"); - localeAssertions.assertFunction("format_datetime(" + dateTimeLiteral + ", 'MMMM')", VARCHAR, "1월"); - - localeAssertions.assertFunction("date_parse('2013-05-17 12:35:10 오후', '%Y-%m-%d %h:%i:%s %p')", - TIMESTAMP_MILLIS, - sqlTimestampOf(3, 2013, 5, 17, 12, 35, 10, 0)); - localeAssertions.assertFunction("date_parse('2013-05-17 12:35:10 오전', '%Y-%m-%d %h:%i:%s %p')", - TIMESTAMP_MILLIS, - sqlTimestampOf(3, 2013, 5, 17, 0, 35, 10, 0)); - - localeAssertions.assertFunction("parse_datetime('2013-05-17 12:35:10 오후', 'yyyy-MM-dd hh:mm:ss a')", - TIMESTAMP_TZ_MILLIS, - toTimestampWithTimeZone(new DateTime(2013, 5, 17, 12, 35, 10, 0, DATE_TIME_ZONE))); - localeAssertions.assertFunction("parse_datetime('2013-05-17 12:35:10 오전', 'yyyy-MM-dd hh:mm:ss aaa')", - TIMESTAMP_TZ_MILLIS, - toTimestampWithTimeZone(new DateTime(2013, 5, 17, 0, 35, 10, 0, DATE_TIME_ZONE))); - } - } + assertThat(assertions.expression("date_format(TIMESTAMP '2001-01-09 13:04:05.321', '%a')", session)) + .hasType(VARCHAR) + .isEqualTo("화"); - @Test - public void testDateTimeOutputString() - { - // SqlDate - assertFunctionString("date '2012-12-31'", DateType.DATE, "2012-12-31"); - assertFunctionString("date '0000-12-31'", DateType.DATE, "0000-12-31"); - assertFunctionString("date '0000-09-23'", DateType.DATE, "0000-09-23"); - assertFunctionString("date '0001-10-25'", DateType.DATE, "0001-10-25"); - assertFunctionString("date '1560-04-29'", DateType.DATE, "1560-04-29"); - - // SqlTime - assertFunctionString("time '00:00:00'", createTimeType(0), "00:00:00"); - assertFunctionString("time '01:02:03'", createTimeType(0), "01:02:03"); - assertFunctionString("time '23:23:23.233'", createTimeType(3), "23:23:23.233"); - assertFunctionString("time '23:59:59.999'", createTimeType(3), "23:59:59.999"); - - // SqlTimestamp - assertFunctionString("timestamp '0000-01-02 01:02:03'", createTimestampType(0), "0000-01-02 01:02:03"); - assertFunctionString("timestamp '2012-12-31 00:00:00'", createTimestampType(0), "2012-12-31 00:00:00"); - assertFunctionString("timestamp '1234-05-06 23:23:23.233'", createTimestampType(3), "1234-05-06 23:23:23.233"); - assertFunctionString("timestamp '2333-02-23 23:59:59.999'", createTimestampType(3), "2333-02-23 23:59:59.999"); - - // SqlTimestampWithTimeZone - assertFunctionString("timestamp '2012-12-31 00:00:00 UTC'", createTimestampWithTimeZoneType(0), "2012-12-31 00:00:00 UTC"); - assertFunctionString("timestamp '0000-01-02 01:02:03 Asia/Shanghai'", createTimestampWithTimeZoneType(0), "0000-01-02 01:02:03 Asia/Shanghai"); - assertFunctionString("timestamp '1234-05-06 23:23:23.233 America/Los_Angeles'", createTimestampWithTimeZoneType(3), "1234-05-06 23:23:23.233 America/Los_Angeles"); - assertFunctionString("timestamp '2333-02-23 23:59:59.999 Asia/Tokyo'", createTimestampWithTimeZoneType(3), "2333-02-23 23:59:59.999 Asia/Tokyo"); + assertThat(assertions.expression("date_format(TIMESTAMP '2001-01-09 13:04:05.321', '%W')", session)) + .hasType(VARCHAR) + .isEqualTo("화요일"); + + assertThat(assertions.expression("date_format(TIMESTAMP '2001-01-09 13:04:05.321', '%p')", session)) + .hasType(VARCHAR) + .isEqualTo("오후"); + + assertThat(assertions.expression("date_format(TIMESTAMP '2001-01-09 13:04:05.321', '%r')", session)) + .hasType(VARCHAR) + .isEqualTo("01:04:05 오후"); + + assertThat(assertions.expression("date_format(TIMESTAMP '2001-01-09 13:04:05.321', '%b')", session)) + .hasType(VARCHAR) + .isEqualTo("1월"); + + assertThat(assertions.expression("date_format(TIMESTAMP '2001-01-09 13:04:05.321', '%M')", session)) + .hasType(VARCHAR) + .isEqualTo("1월"); + + assertThat(assertions.expression("format_datetime(TIMESTAMP '2001-01-09 13:04:05.321', 'EEE')", session)) + .hasType(VARCHAR) + .isEqualTo("화"); + + assertThat(assertions.expression("format_datetime(TIMESTAMP '2001-01-09 13:04:05.321', 'EEEE')", session)) + .hasType(VARCHAR) + .isEqualTo("화요일"); + + assertThat(assertions.expression("format_datetime(TIMESTAMP '2001-01-09 13:04:05.321', 'a')", session)) + .hasType(VARCHAR) + .isEqualTo("오후"); + + assertThat(assertions.expression("format_datetime(TIMESTAMP '2001-01-09 13:04:05.321', 'MMM')", session)) + .hasType(VARCHAR) + .isEqualTo("1월"); + + assertThat(assertions.expression("format_datetime(TIMESTAMP '2001-01-09 13:04:05.321', 'MMMM')", session)) + .hasType(VARCHAR) + .isEqualTo("1월"); + + assertThat(assertions.expression("date_parse('2013-05-17 12:35:10 오후', '%Y-%m-%d %h:%i:%s %p')", session)) + .matches("TIMESTAMP '2013-05-17 12:35:10.000'"); + + assertThat(assertions.expression("date_parse('2013-05-17 12:35:10 오전', '%Y-%m-%d %h:%i:%s %p')", session)) + .matches("TIMESTAMP '2013-05-17 00:35:10.000'"); + + assertThat(assertions.expression("parse_datetime('2013-05-17 12:35:10 오후', 'yyyy-MM-dd hh:mm:ss a')", session)) + .matches("TIMESTAMP '2013-05-17 12:35:10.000 Pacific/Apia'"); + + assertThat(assertions.expression("parse_datetime('2013-05-17 12:35:10 오전', 'yyyy-MM-dd hh:mm:ss aaa')", session)) + .matches("TIMESTAMP '2013-05-17 00:35:10.000 Pacific/Apia'"); } @Test public void testParseDuration() { - assertFunction("parse_duration('1234 ns')", INTERVAL_DAY_TIME, new SqlIntervalDayTime(0, 0, 0, 0, 0)); - assertFunction("parse_duration('1234 us')", INTERVAL_DAY_TIME, new SqlIntervalDayTime(0, 0, 0, 0, 1)); - assertFunction("parse_duration('1234 ms')", INTERVAL_DAY_TIME, new SqlIntervalDayTime(0, 0, 0, 1, 234)); - assertFunction("parse_duration('1234 s')", INTERVAL_DAY_TIME, new SqlIntervalDayTime(0, 0, 20, 34, 0)); - assertFunction("parse_duration('1234 m')", INTERVAL_DAY_TIME, new SqlIntervalDayTime(0, 20, 34, 0, 0)); - assertFunction("parse_duration('1234 h')", INTERVAL_DAY_TIME, new SqlIntervalDayTime(51, 10, 0, 0, 0)); - assertFunction("parse_duration('1234 d')", INTERVAL_DAY_TIME, new SqlIntervalDayTime(1234, 0, 0, 0, 0)); - assertFunction("parse_duration('1234.567 ns')", INTERVAL_DAY_TIME, new SqlIntervalDayTime(0, 0, 0, 0, 0)); - assertFunction("parse_duration('1234.567 ms')", INTERVAL_DAY_TIME, new SqlIntervalDayTime(0, 0, 0, 1, 235)); - assertFunction("parse_duration('1234.567 s')", INTERVAL_DAY_TIME, new SqlIntervalDayTime(0, 0, 0, 1234, 567)); - assertFunction("parse_duration('1234.567 m')", INTERVAL_DAY_TIME, new SqlIntervalDayTime(0, 20, 34, 34, 20)); - assertFunction("parse_duration('1234.567 h')", INTERVAL_DAY_TIME, new SqlIntervalDayTime(51, 10, 34, 1, 200)); - assertFunction("parse_duration('1234.567 d')", INTERVAL_DAY_TIME, new SqlIntervalDayTime(1234, 13, 36, 28, 800)); + assertThat(assertions.function("parse_duration", "'1234 ns'")) + .hasType(INTERVAL_DAY_TIME) + .isEqualTo(new SqlIntervalDayTime(0, 0, 0, 0, 0)); + + assertThat(assertions.function("parse_duration", "'1234 us'")) + .hasType(INTERVAL_DAY_TIME) + .isEqualTo(new SqlIntervalDayTime(0, 0, 0, 0, 1)); + + assertThat(assertions.function("parse_duration", "'1234 ms'")) + .hasType(INTERVAL_DAY_TIME) + .isEqualTo(new SqlIntervalDayTime(0, 0, 0, 1, 234)); + + assertThat(assertions.function("parse_duration", "'1234 s'")) + .hasType(INTERVAL_DAY_TIME) + .isEqualTo(new SqlIntervalDayTime(0, 0, 20, 34, 0)); + + assertThat(assertions.function("parse_duration", "'1234 m'")) + .hasType(INTERVAL_DAY_TIME) + .isEqualTo(new SqlIntervalDayTime(0, 20, 34, 0, 0)); + + assertThat(assertions.function("parse_duration", "'1234 h'")) + .hasType(INTERVAL_DAY_TIME) + .isEqualTo(new SqlIntervalDayTime(51, 10, 0, 0, 0)); + + assertThat(assertions.function("parse_duration", "'1234 d'")) + .hasType(INTERVAL_DAY_TIME) + .isEqualTo(new SqlIntervalDayTime(1234, 0, 0, 0, 0)); + + assertThat(assertions.function("parse_duration", "'1234.567 ns'")) + .hasType(INTERVAL_DAY_TIME) + .isEqualTo(new SqlIntervalDayTime(0, 0, 0, 0, 0)); + + assertThat(assertions.function("parse_duration", "'1234.567 ms'")) + .hasType(INTERVAL_DAY_TIME) + .isEqualTo(new SqlIntervalDayTime(0, 0, 0, 1, 235)); + + assertThat(assertions.function("parse_duration", "'1234.567 s'")) + .hasType(INTERVAL_DAY_TIME) + .isEqualTo(new SqlIntervalDayTime(0, 0, 0, 1234, 567)); + + assertThat(assertions.function("parse_duration", "'1234.567 m'")) + .hasType(INTERVAL_DAY_TIME) + .isEqualTo(new SqlIntervalDayTime(0, 20, 34, 34, 20)); + + assertThat(assertions.function("parse_duration", "'1234.567 h'")) + .hasType(INTERVAL_DAY_TIME) + .isEqualTo(new SqlIntervalDayTime(51, 10, 34, 1, 200)); + + assertThat(assertions.function("parse_duration", "'1234.567 d'")) + .hasType(INTERVAL_DAY_TIME) + .isEqualTo(new SqlIntervalDayTime(1234, 13, 36, 28, 800)); // without space - assertFunction("parse_duration('1234ns')", INTERVAL_DAY_TIME, new SqlIntervalDayTime(0, 0, 0, 0, 0)); - assertFunction("parse_duration('1234us')", INTERVAL_DAY_TIME, new SqlIntervalDayTime(0, 0, 0, 0, 1)); - assertFunction("parse_duration('1234ms')", INTERVAL_DAY_TIME, new SqlIntervalDayTime(0, 0, 0, 1, 234)); - assertFunction("parse_duration('1234s')", INTERVAL_DAY_TIME, new SqlIntervalDayTime(0, 0, 20, 34, 0)); - assertFunction("parse_duration('1234m')", INTERVAL_DAY_TIME, new SqlIntervalDayTime(0, 20, 34, 0, 0)); - assertFunction("parse_duration('1234h')", INTERVAL_DAY_TIME, new SqlIntervalDayTime(51, 10, 0, 0, 0)); - assertFunction("parse_duration('1234d')", INTERVAL_DAY_TIME, new SqlIntervalDayTime(1234, 0, 0, 0, 0)); - assertFunction("parse_duration('1234.567ns')", INTERVAL_DAY_TIME, new SqlIntervalDayTime(0, 0, 0, 0, 0)); - assertFunction("parse_duration('1234.567ms')", INTERVAL_DAY_TIME, new SqlIntervalDayTime(0, 0, 0, 1, 235)); - assertFunction("parse_duration('1234.567s')", INTERVAL_DAY_TIME, new SqlIntervalDayTime(0, 0, 0, 1234, 567)); - assertFunction("parse_duration('1234.567m')", INTERVAL_DAY_TIME, new SqlIntervalDayTime(0, 20, 34, 34, 20)); - assertFunction("parse_duration('1234.567h')", INTERVAL_DAY_TIME, new SqlIntervalDayTime(51, 10, 34, 1, 200)); - assertFunction("parse_duration('1234.567d')", INTERVAL_DAY_TIME, new SqlIntervalDayTime(1234, 13, 36, 28, 800)); + assertThat(assertions.function("parse_duration", "'1234ns'")) + .hasType(INTERVAL_DAY_TIME) + .isEqualTo(new SqlIntervalDayTime(0, 0, 0, 0, 0)); - // invalid function calls - assertInvalidFunction("parse_duration('')", "duration is empty"); - assertInvalidFunction("parse_duration('1f')", "Unknown time unit: f"); - assertInvalidFunction("parse_duration('abc')", "duration is not a valid data duration string: abc"); - } + assertThat(assertions.function("parse_duration", "'1234us'")) + .hasType(INTERVAL_DAY_TIME) + .isEqualTo(new SqlIntervalDayTime(0, 0, 0, 0, 1)); - @Test - public void testIntervalDayToSecondToMilliseconds() - { - assertFunction("to_milliseconds(parse_duration('1ns'))", BigintType.BIGINT, 0L); - assertFunction("to_milliseconds(parse_duration('1ms'))", BigintType.BIGINT, 1L); - assertFunction("to_milliseconds(parse_duration('1s'))", BigintType.BIGINT, SECONDS.toMillis(1)); - assertFunction("to_milliseconds(parse_duration('1h'))", BigintType.BIGINT, HOURS.toMillis(1)); - assertFunction("to_milliseconds(parse_duration('1d'))", BigintType.BIGINT, DAYS.toMillis(1)); - } + assertThat(assertions.function("parse_duration", "'1234ms'")) + .hasType(INTERVAL_DAY_TIME) + .isEqualTo(new SqlIntervalDayTime(0, 0, 0, 1, 234)); - @Test - public void testWithTimezone() - { - assertFunction( - "with_timezone(TIMESTAMP '2001-08-22 03:04:05.321', 'UTC')", - TIMESTAMP_TZ_MILLIS, - toTimestampWithTimeZone(new DateTime(2001, 8, 22, 3, 4, 5, 321, getDateTimeZone(getTimeZoneKey("UTC"))))); - assertFunction( - "with_timezone(TIMESTAMP '2001-08-22 03:04:05.321', '+13')", - TIMESTAMP_TZ_MILLIS, - toTimestampWithTimeZone(new DateTime(2001, 8, 22, 3, 4, 5, 321, getDateTimeZone(getTimeZoneKey("+13"))))); - assertFunction( - "with_timezone(TIMESTAMP '2001-08-22 03:04:05.321', '-14')", - TIMESTAMP_TZ_MILLIS, - toTimestampWithTimeZone(new DateTime(2001, 8, 22, 3, 4, 5, 321, getDateTimeZone(getTimeZoneKey("-14"))))); - assertFunction( - "with_timezone(TIMESTAMP '2001-08-22 03:04:05.321', '+00:45')", - TIMESTAMP_TZ_MILLIS, - toTimestampWithTimeZone(new DateTime(2001, 8, 22, 3, 4, 5, 321, getDateTimeZone(getTimeZoneKey("+00:45"))))); - assertFunction( - "with_timezone(TIMESTAMP '2001-08-22 03:04:05.321', 'Asia/Shanghai')", - TIMESTAMP_TZ_MILLIS, - toTimestampWithTimeZone(new DateTime(2001, 8, 22, 3, 4, 5, 321, getDateTimeZone(getTimeZoneKey("Asia/Shanghai"))))); - assertFunction( - "with_timezone(TIMESTAMP '2001-08-22 03:04:05.321', 'America/New_York')", - TIMESTAMP_TZ_MILLIS, - toTimestampWithTimeZone(new DateTime(2001, 8, 22, 3, 4, 5, 321, getDateTimeZone(getTimeZoneKey("America/New_York"))))); - - assertFunction( - "with_timezone(TIMESTAMP '2001-06-01 03:04:05.321', 'America/Los_Angeles')", - TIMESTAMP_TZ_MILLIS, - toTimestampWithTimeZone(new DateTime(2001, 6, 1, 3, 4, 5, 321, getDateTimeZone(getTimeZoneKey("America/Los_Angeles"))))); - assertFunction( - "with_timezone(TIMESTAMP '2001-12-01 03:04:05.321', 'America/Los_Angeles')", - TIMESTAMP_TZ_MILLIS, - toTimestampWithTimeZone(new DateTime(2001, 12, 1, 3, 4, 5, 321, getDateTimeZone(getTimeZoneKey("America/Los_Angeles"))))); - - assertInvalidFunction("with_timezone(TIMESTAMP '2001-08-22 03:04:05.321', 'invalidzoneid')", "'invalidzoneid' is not a valid time zone"); - } + assertThat(assertions.function("parse_duration", "'1234s'")) + .hasType(INTERVAL_DAY_TIME) + .isEqualTo(new SqlIntervalDayTime(0, 0, 20, 34, 0)); - private void assertFunctionString(String projection, Type expectedType, String expected) - { - functionAssertions.assertFunctionString(projection, expectedType, expected); - } + assertThat(assertions.function("parse_duration", "'1234m'")) + .hasType(INTERVAL_DAY_TIME) + .isEqualTo(new SqlIntervalDayTime(0, 20, 34, 0, 0)); - private static SqlDate toDate(LocalDate localDate) - { - return new SqlDate(toIntExact(localDate.toEpochDay())); - } + assertThat(assertions.function("parse_duration", "'1234h'")) + .hasType(INTERVAL_DAY_TIME) + .isEqualTo(new SqlIntervalDayTime(51, 10, 0, 0, 0)); - private static SqlDate toDate(DateTime dateDate) - { - long millis = dateDate.getMillis(); - return new SqlDate(toIntExact(MILLISECONDS.toDays(millis))); - } + assertThat(assertions.function("parse_duration", "'1234d'")) + .hasType(INTERVAL_DAY_TIME) + .isEqualTo(new SqlIntervalDayTime(1234, 0, 0, 0, 0)); - private static long millisBetween(ReadableInstant start, ReadableInstant end) - { - requireNonNull(start, "start is null"); - requireNonNull(end, "end is null"); - return millis().getField(getInstantChronology(start)).getDifferenceAsLong(end.getMillis(), start.getMillis()); - } + assertThat(assertions.function("parse_duration", "'1234.567ns'")) + .hasType(INTERVAL_DAY_TIME) + .isEqualTo(new SqlIntervalDayTime(0, 0, 0, 0, 0)); - private static Seconds secondsBetween(ReadableInstant start, ReadableInstant end) - { - return Seconds.secondsBetween(start, end); - } + assertThat(assertions.function("parse_duration", "'1234.567ms'")) + .hasType(INTERVAL_DAY_TIME) + .isEqualTo(new SqlIntervalDayTime(0, 0, 0, 1, 235)); - private static Minutes minutesBetween(ReadableInstant start, ReadableInstant end) - { - return Minutes.minutesBetween(start, end); - } + assertThat(assertions.function("parse_duration", "'1234.567s'")) + .hasType(INTERVAL_DAY_TIME) + .isEqualTo(new SqlIntervalDayTime(0, 0, 0, 1234, 567)); - private static Hours hoursBetween(ReadableInstant start, ReadableInstant end) - { - return Hours.hoursBetween(start, end); - } + assertThat(assertions.function("parse_duration", "'1234.567m'")) + .hasType(INTERVAL_DAY_TIME) + .isEqualTo(new SqlIntervalDayTime(0, 20, 34, 34, 20)); - private static long millisBetween(LocalTime start, LocalTime end) - { - return NANOSECONDS.toMillis(end.toNanoOfDay() - start.toNanoOfDay()); - } + assertThat(assertions.function("parse_duration", "'1234.567h'")) + .hasType(INTERVAL_DAY_TIME) + .isEqualTo(new SqlIntervalDayTime(51, 10, 34, 1, 200)); - private static long secondsBetween(LocalTime start, LocalTime end) - { - return NANOSECONDS.toSeconds(end.toNanoOfDay() - start.toNanoOfDay()); - } + assertThat(assertions.function("parse_duration", "'1234.567d'")) + .hasType(INTERVAL_DAY_TIME) + .isEqualTo(new SqlIntervalDayTime(1234, 13, 36, 28, 800)); - private static long minutesBetween(LocalTime start, LocalTime end) - { - return NANOSECONDS.toMinutes(end.toNanoOfDay() - start.toNanoOfDay()); - } + // invalid function calls + assertTrinoExceptionThrownBy(() -> assertions.function("parse_duration", "''").evaluate()) + .hasMessage("duration is empty"); - private static long hoursBetween(LocalTime start, LocalTime end) - { - return NANOSECONDS.toHours(end.toNanoOfDay() - start.toNanoOfDay()); - } + assertTrinoExceptionThrownBy(() -> assertions.function("parse_duration", "'1f'").evaluate()) + .hasMessage("Unknown time unit: f"); - private static long millisBetween(OffsetTime start, OffsetTime end) - { - return millisUtc(end) - millisUtc(start); + assertTrinoExceptionThrownBy(() -> assertions.function("parse_duration", "'abc'").evaluate()) + .hasMessage("duration is not a valid data duration string: abc"); } - private static long secondsBetween(OffsetTime start, OffsetTime end) + @Test + public void testIntervalDayToSecondToMilliseconds() { - return MILLISECONDS.toSeconds(millisBetween(start, end)); - } + assertThat(assertions.function("to_milliseconds", "parse_duration('1ns')")) + .isEqualTo(0L); - private static long minutesBetween(OffsetTime start, OffsetTime end) - { - return MILLISECONDS.toMinutes(millisBetween(start, end)); - } + assertThat(assertions.function("to_milliseconds", "parse_duration('1ms')")) + .isEqualTo(1L); - private static long hoursBetween(OffsetTime start, OffsetTime end) - { - return MILLISECONDS.toHours(millisBetween(start, end)); - } + assertThat(assertions.function("to_milliseconds", "parse_duration('1s')")) + .isEqualTo(SECONDS.toMillis(1)); - private static long millisUtc(OffsetTime offsetTime) - { - return offsetTime.atDate(LocalDate.ofEpochDay(0)).toInstant().toEpochMilli(); + assertThat(assertions.function("to_milliseconds", "parse_duration('1h')")) + .isEqualTo(HOURS.toMillis(1)); + + assertThat(assertions.function("to_milliseconds", "parse_duration('1d')")) + .isEqualTo(DAYS.toMillis(1)); } - private static SqlTimestampWithTimeZone toTimestampWithTimeZone(DateTime dateTime) + @Test + public void testWithTimezone() { - return SqlTimestampWithTimeZone.newInstance(3, dateTime.getMillis(), 0, getTimeZoneKey(dateTime.getZone().getID())); + assertThat(assertions.function("with_timezone", "TIMESTAMP '2001-08-22 03:04:05.321'", "'UTC'")) + .matches("TIMESTAMP '2001-08-22 03:04:05.321 UTC'"); + + assertThat(assertions.function("with_timezone", "TIMESTAMP '2001-08-22 03:04:05.321'", "'+13'")) + .matches("TIMESTAMP '2001-08-22 03:04:05.321 +13:00'"); + + assertThat(assertions.function("with_timezone", "TIMESTAMP '2001-08-22 03:04:05.321'", "'-14'")) + .matches("TIMESTAMP '2001-08-22 03:04:05.321 -14:00'"); + + assertThat(assertions.function("with_timezone", "TIMESTAMP '2001-08-22 03:04:05.321'", "'+00:45'")) + .matches("TIMESTAMP '2001-08-22 03:04:05.321 +00:45'"); + + assertThat(assertions.function("with_timezone", "TIMESTAMP '2001-08-22 03:04:05.321'", "'Asia/Shanghai'")) + .matches("TIMESTAMP '2001-08-22 03:04:05.321 Asia/Shanghai'"); + + assertThat(assertions.function("with_timezone", "TIMESTAMP '2001-08-22 03:04:05.321'", "'America/New_York'")) + .matches("TIMESTAMP '2001-08-22 03:04:05.321 America/New_York'"); + + assertThat(assertions.function("with_timezone", "TIMESTAMP '2001-06-01 03:04:05.321'", "'America/Los_Angeles'")) + .matches("TIMESTAMP '2001-06-01 03:04:05.321 America/Los_Angeles'"); + + assertThat(assertions.function("with_timezone", "TIMESTAMP '2001-12-01 03:04:05.321'", "'America/Los_Angeles'")) + .matches("TIMESTAMP '2001-12-01 03:04:05.321 America/Los_Angeles'"); + + assertTrinoExceptionThrownBy(() -> assertions.function("with_timezone", "TIMESTAMP '2001-08-22 03:04:05.321'", "'invalidzoneid'").evaluate()) + .hasMessage("'invalidzoneid' is not a valid time zone"); } } diff --git a/core/trino-main/src/test/java/io/trino/sql/ExpressionTestUtils.java b/core/trino-main/src/test/java/io/trino/sql/ExpressionTestUtils.java index c84920114415..8923375973ad 100644 --- a/core/trino-main/src/test/java/io/trino/sql/ExpressionTestUtils.java +++ b/core/trino-main/src/test/java/io/trino/sql/ExpressionTestUtils.java @@ -22,12 +22,9 @@ import io.trino.sql.analyzer.ExpressionAnalyzer; import io.trino.sql.analyzer.Scope; import io.trino.sql.parser.SqlParser; -import io.trino.sql.planner.DesugarArrayConstructorRewriter; -import io.trino.sql.planner.DesugarLikeRewriter; import io.trino.sql.planner.TypeProvider; import io.trino.sql.planner.assertions.ExpressionVerifier; import io.trino.sql.planner.assertions.SymbolAliases; -import io.trino.sql.planner.iterative.rule.CanonicalizeExpressionRewriter; import io.trino.sql.tree.Cast; import io.trino.sql.tree.Expression; import io.trino.sql.tree.ExpressionRewriter; @@ -104,11 +101,7 @@ public static Expression planExpression(PlannerContext plannerContext, Session s private static Expression planExpressionInExistingTx(PlannerContext plannerContext, TypeProvider typeProvider, Expression expression, Session transactionSession) { - Expression rewritten = rewriteIdentifiersToSymbolReferences(expression); - rewritten = DesugarLikeRewriter.rewrite(rewritten, transactionSession, plannerContext.getMetadata(), createTestingTypeAnalyzer(plannerContext), typeProvider); - rewritten = DesugarArrayConstructorRewriter.rewrite(rewritten, transactionSession, plannerContext.getMetadata(), createTestingTypeAnalyzer(plannerContext), typeProvider); - rewritten = CanonicalizeExpressionRewriter.rewrite(rewritten, transactionSession, plannerContext, createTestingTypeAnalyzer(plannerContext), typeProvider); - return resolveFunctionCalls(plannerContext, transactionSession, typeProvider, rewritten); + return resolveFunctionCalls(plannerContext, transactionSession, typeProvider, rewriteIdentifiersToSymbolReferences(expression)); } public static Expression resolveFunctionCalls(PlannerContext plannerContext, Session session, TypeProvider typeProvider, Expression expression) diff --git a/core/trino-main/src/test/java/io/trino/sql/gen/TestExpressionCompiler.java b/core/trino-main/src/test/java/io/trino/sql/gen/TestExpressionCompiler.java index 2f4969d194cf..460adf91ef07 100644 --- a/core/trino-main/src/test/java/io/trino/sql/gen/TestExpressionCompiler.java +++ b/core/trino-main/src/test/java/io/trino/sql/gen/TestExpressionCompiler.java @@ -14,19 +14,10 @@ package io.trino.sql.gen; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; import com.google.common.collect.ObjectArrays; -import com.google.common.collect.Sets; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListeningExecutorService; -import io.airlift.log.Logger; -import io.airlift.log.Logging; import io.airlift.slice.Slice; -import io.airlift.units.Duration; -import io.trino.likematcher.LikeMatcher; +import io.trino.Session; import io.trino.operator.scalar.BitwiseFunctions; -import io.trino.operator.scalar.FunctionAssertions; import io.trino.operator.scalar.JoniRegexpFunctions; import io.trino.operator.scalar.JsonFunctions; import io.trino.operator.scalar.JsonPath; @@ -46,40 +37,28 @@ import io.trino.spi.type.ArrayType; import io.trino.spi.type.SqlDecimal; import io.trino.spi.type.SqlTimestampWithTimeZone; -import io.trino.spi.type.TimeZoneKey; import io.trino.spi.type.Type; import io.trino.spi.type.VarcharType; +import io.trino.sql.query.QueryAssertions; import io.trino.sql.tree.Extract.Field; import io.trino.type.LikeFunctions; import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; -import org.testng.annotations.AfterClass; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; - -import java.lang.reflect.Method; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; + import java.math.BigDecimal; -import java.math.BigInteger; import java.text.DecimalFormat; import java.text.NumberFormat; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Locale; import java.util.Objects; -import java.util.Set; import java.util.function.BiPredicate; import java.util.stream.LongStream; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.util.concurrent.MoreExecutors.listeningDecorator; -import static com.google.common.util.concurrent.MoreExecutors.newDirectExecutorService; -import static io.airlift.concurrent.Threads.daemonThreadsNamed; import static io.airlift.slice.Slices.utf8Slice; -import static io.airlift.testing.Closeables.closeAllRuntimeException; -import static io.trino.SessionTestUtils.TEST_SESSION; import static io.trino.operator.scalar.JoniRegexpCasts.joniRegexp; import static io.trino.spi.type.BigintType.BIGINT; import static io.trino.spi.type.BooleanType.BOOLEAN; @@ -89,29 +68,26 @@ import static io.trino.spi.type.SmallintType.SMALLINT; import static io.trino.spi.type.TimestampType.TIMESTAMP_MILLIS; import static io.trino.spi.type.TimestampWithTimeZoneType.TIMESTAMP_TZ_MILLIS; -import static io.trino.spi.type.VarbinaryType.VARBINARY; import static io.trino.spi.type.VarcharType.VARCHAR; import static io.trino.spi.type.VarcharType.createUnboundedVarcharType; import static io.trino.spi.type.VarcharType.createVarcharType; import static io.trino.sql.tree.Extract.Field.TIMEZONE_HOUR; import static io.trino.sql.tree.Extract.Field.TIMEZONE_MINUTE; import static io.trino.testing.DateTimeTestingUtils.sqlTimestampOf; -import static io.trino.testing.SqlVarbinaryTestingUtil.sqlVarbinary; import static io.trino.type.DateTimes.MICROSECONDS_PER_MILLISECOND; import static io.trino.type.JsonType.JSON; import static io.trino.type.UnknownType.UNKNOWN; import static io.trino.util.StructuralTestUtil.mapType; import static java.lang.Math.cos; -import static java.lang.Runtime.getRuntime; import static java.lang.String.format; import static java.util.Collections.singletonList; -import static java.util.concurrent.Executors.newFixedThreadPool; import static java.util.stream.Collectors.joining; import static java.util.stream.IntStream.range; +import static org.assertj.core.api.Assertions.assertThat; import static org.joda.time.DateTimeZone.UTC; -import static org.testng.Assert.assertTrue; +import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS; -@Test(singleThreaded = true) +@TestInstance(PER_CLASS) public class TestExpressionCompiler { private static final Boolean[] booleanValues = {true, false, null}; @@ -135,528 +111,967 @@ public class TestExpressionCompiler private static final BigDecimal[] decimalRights = {new BigDecimal("3.0"), new BigDecimal("-3.0"), new BigDecimal("3.1"), new BigDecimal("-3.1"), null}; private static final BigDecimal[] decimalMiddle = {new BigDecimal("9.0"), new BigDecimal("-3.1"), new BigDecimal("88.0"), null}; - private static final DateTime[] dateTimeValues = { - new DateTime(2001, 1, 22, 3, 4, 5, 321, UTC), - new DateTime(1960, 1, 22, 3, 4, 5, 321, UTC), - new DateTime(1970, 1, 1, 0, 0, 0, 0, UTC), - null - }; - - private static final String[] jsonValues = { - "{}", - "{\"fuu\": {\"bar\": 1}}", - "{\"fuu\": null}", - "{\"fuu\": 1}", - "{\"fuu\": 1, \"bar\": \"abc\"}", - null - }; - private static final String[] jsonPatterns = { - "$", - "$.fuu", - "$.fuu[0]", - "$.bar", - null - }; - - private static final Logger log = Logger.get(TestExpressionCompiler.class); - private static final boolean PARALLEL = false; - - private long start; - private ListeningExecutorService executor; - private FunctionAssertions functionAssertions; - private List> futures; - - @BeforeClass - public void setupClass() - { - Logging.initialize(); - if (PARALLEL) { - executor = listeningDecorator(newFixedThreadPool(getRuntime().availableProcessors() * 2, daemonThreadsNamed("completer-%s"))); - } - else { - executor = newDirectExecutorService(); - } - functionAssertions = new FunctionAssertions(); - } - - @AfterClass(alwaysRun = true) - public void tearDownClass() - { - if (executor != null) { - executor.shutdownNow(); - executor = null; - } - closeAllRuntimeException(functionAssertions); - functionAssertions = null; - } - - @BeforeMethod - public void setUp() - { - start = System.nanoTime(); - futures = new ArrayList<>(); - } - - @AfterMethod(alwaysRun = true) - public void tearDown(Method method) - { - assertTrue(Futures.allAsList(futures).isDone(), "Expression test futures are not complete"); - log.info("FINISHED %s in %s verified %s expressions", method.getName(), Duration.nanosSince(start), futures.size()); - } + private QueryAssertions assertions; - @Test - public void smokedTest() - throws Exception + @BeforeAll + public void setup() { - assertExecute("cast(true as boolean)", BOOLEAN, true); - assertExecute("true", BOOLEAN, true); - assertExecute("false", BOOLEAN, false); - assertExecute("42", INTEGER, 42); - assertExecute("'foo'", createVarcharType(3), "foo"); - assertExecute("4.2E0", DOUBLE, 4.2); - assertExecute("10000000000 + 1", BIGINT, 10000000001L); - assertExecute("4.2", createDecimalType(2, 1), new SqlDecimal(BigInteger.valueOf(42), 2, 1)); - assertExecute("DECIMAL '4.2'", createDecimalType(2, 1), new SqlDecimal(BigInteger.valueOf(42), 2, 1)); - assertExecute("X' 1 f'", VARBINARY, sqlVarbinary(0x1F)); - assertExecute("X' '", VARBINARY, sqlVarbinary()); - assertExecute("bound_integer", INTEGER, 1234); - assertExecute("bound_long", BIGINT, 1234L); - assertExecute("bound_string", VARCHAR, "hello"); - assertExecute("bound_double", DOUBLE, 12.34); - assertExecute("bound_boolean", BOOLEAN, true); - assertExecute("bound_timestamp", BIGINT, new DateTime(2001, 8, 22, 3, 4, 5, 321, UTC).getMillis()); - assertExecute("bound_pattern", VARCHAR, "%el%"); - assertExecute("bound_null_string", VARCHAR, null); - assertExecute("bound_timestamp_with_timezone", TIMESTAMP_TZ_MILLIS, SqlTimestampWithTimeZone.newInstance(3, new DateTime(1970, 1, 1, 0, 1, 0, 999, DateTimeZone.UTC).getMillis(), 0, TimeZoneKey.getTimeZoneKey("Z"))); - assertExecute("bound_binary_literal", VARBINARY, sqlVarbinary(0xAB)); - - // todo enable when null output type is supported - // assertExecute("null", null); - - Futures.allAsList(futures).get(); + assertions = new QueryAssertions(); } - @Test - public void filterFunction() - throws Exception + @AfterAll + public void tearDown() { - assertFilter("true", true); - assertFilter("false", false); - assertFilter("bound_integer = 1234", true); - assertFilter("bound_integer = BIGINT '1234'", true); - assertFilter("bound_long = 1234", true); - assertFilter("bound_long = BIGINT '1234'", true); - assertFilter("bound_long = 5678", false); - assertFilter("bound_null_string is null", true); - assertFilter("bound_null_string = 'foo'", false); - - // todo enable when null output type is supported - // assertFilter("null", false); - assertFilter("cast(null as boolean)", false); - assertFilter("nullif(true, true)", false); - - assertFilter("true AND cast(null as boolean) AND true", false); - - Futures.allAsList(futures).get(); + assertions.close(); + assertions = null; } @Test public void testUnaryOperators() - throws Exception { - assertExecute("cast(null as boolean) is null", BOOLEAN, true); + assertThat(assertions.expression("a IS NULL") + .binding("a", "CAST(null AS boolean)")) + .isEqualTo(true); for (Boolean value : booleanValues) { - assertExecute(generateExpression("%s", value), BOOLEAN, value == null ? null : value); - assertExecute(generateExpression("%s is null", value), BOOLEAN, value == null); - assertExecute(generateExpression("%s is not null", value), BOOLEAN, value != null); + assertThat(assertions.expression(toLiteral(value))) + .hasType(BOOLEAN) + .isEqualTo(value == null ? null : value); + + assertThat(assertions.expression("a IS NULL") + .binding("a", toLiteral(value))) + .isEqualTo(value == null); + + assertThat(assertions.expression("a IS NOT NULL") + .binding("a", toLiteral(value))) + .isEqualTo(value != null); } for (Integer value : intLefts) { Long longValue = value == null ? null : value * 10000000000L; - assertExecute(generateExpression("%s", value), INTEGER, value == null ? null : value); - assertExecute(generateExpression("- (%s)", value), INTEGER, value == null ? null : -value); - assertExecute(generateExpression("%s", longValue), BIGINT, value == null ? null : longValue); - assertExecute(generateExpression("- (%s)", longValue), BIGINT, value == null ? null : -longValue); - assertExecute(generateExpression("%s is null", value), BOOLEAN, value == null); - assertExecute(generateExpression("%s is not null", value), BOOLEAN, value != null); + + assertThat(assertions.expression(toLiteral(value))) + .hasType(INTEGER) + .isEqualTo(value == null ? null : value); + + assertThat(assertions.expression("- a") + .binding("a", toLiteral(value))) + .hasType(INTEGER) + .isEqualTo(value == null ? null : -value); + + assertThat(assertions.expression(toLiteral(longValue))) + .hasType(BIGINT) + .isEqualTo(value == null ? null : longValue); + + assertThat(assertions.expression("- a") + .binding("a", toLiteral(longValue))) + .isEqualTo(value == null ? null : -longValue); + + assertThat(assertions.expression("a IS NULL") + .binding("a", toLiteral(value))) + .isEqualTo(value == null); + + assertThat(assertions.expression("a IS NOT NULL") + .binding("a", toLiteral(value))) + .isEqualTo(value != null); } for (Double value : doubleLefts) { - assertExecute(generateExpression("%s", value), DOUBLE, value == null ? null : value); - assertExecute(generateExpression("- (%s)", value), DOUBLE, value == null ? null : -value); - assertExecute(generateExpression("%s is null", value), BOOLEAN, value == null); - assertExecute(generateExpression("%s is not null", value), BOOLEAN, value != null); + assertThat(assertions.expression(toLiteral(value))) + .hasType(DOUBLE) + .isEqualTo(value == null ? null : value); + + assertThat(assertions.expression("- a") + .binding("a", toLiteral(value))) + .hasType(DOUBLE) + .isEqualTo(value == null ? null : -value); + + assertThat(assertions.expression("a IS NULL") + .binding("a", toLiteral(value))) + .isEqualTo(value == null); + + assertThat(assertions.expression("a IS NOT NULL") + .binding("a", toLiteral(value))) + .isEqualTo(value != null); } for (BigDecimal value : decimalLefts) { - assertExecute(generateExpression("%s", value), value == null ? null : value); - assertExecute(generateExpression("- (%s)", value), value == null ? null : value.negate()); - assertExecute(generateExpression("%s is null", value), BOOLEAN, value == null); - assertExecute(generateExpression("%s is not null", value), BOOLEAN, value != null); - } + assertThat(assertions.expression(toLiteral(value))) + .isEqualTo(value == null ? null : toSqlDecimal(value)); - for (String value : stringLefts) { - assertExecute(generateExpression("%s", value), varcharType(value), value == null ? null : value); - assertExecute(generateExpression("%s is null", value), BOOLEAN, value == null); - assertExecute(generateExpression("%s is not null", value), BOOLEAN, value != null); + assertThat(assertions.expression("- a") + .binding("a", toLiteral(value))) + .isEqualTo(value == null ? null : toSqlDecimal(value.negate())); + + assertThat(assertions.expression("a IS NULL") + .binding("a", toLiteral(value))) + .isEqualTo(value == null); + + assertThat(assertions.expression("a IS NOT NULL") + .binding("a", toLiteral(value))) + .isEqualTo(value != null); } - Futures.allAsList(futures).get(); - } + for (String value : stringLefts) { + assertThat(assertions.expression(toLiteral(value))) + .hasType(varcharType(value)) + .isEqualTo(value == null ? null : value); - @Test - public void testFilterEmptyInput() - throws Exception - { - assertFilterWithNoInputColumns("true", true); + assertThat(assertions.expression("a IS NULL") + .binding("a", toLiteral(value))) + .isEqualTo(value == null); - Futures.allAsList(futures).get(); + assertThat(assertions.expression("a IS NOT NULL") + .binding("a", toLiteral(value))) + .isEqualTo(value != null); + } } @Test public void testBinaryOperatorsBoolean() - throws Exception { - assertExecute("nullif(cast(null as boolean), true)", BOOLEAN, null); + assertThat(assertions.expression("nullif(a, true)") + .binding("a", "CAST(null AS boolean)")) + .isNull(BOOLEAN); + for (Boolean left : booleanValues) { for (Boolean right : booleanValues) { - assertExecute(generateExpression("%s = %s", left, right), BOOLEAN, left == null || right == null ? null : left.equals(right)); - assertExecute(generateExpression("%s <> %s", left, right), BOOLEAN, left == null || right == null ? null : !left.equals(right)); - - assertExecute(generateExpression("nullif(%s, %s)", left, right), BOOLEAN, nullIf(left, right)); - assertExecute(generateExpression("%s is distinct from %s", left, right), BOOLEAN, !Objects.equals(left, right)); + assertThat(assertions.expression("a = b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : left.equals(right)); + + assertThat(assertions.expression("a <> b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : !left.equals(right)); + + assertThat(assertions.expression("nullif(a, b)") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(nullIf(left, right)); + + assertThat(assertions.expression("a IS DISTINCT FROM b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(!Objects.equals(left, right)); } } - - Futures.allAsList(futures).get(); } @Test public void testBinaryOperatorsIntegralIntegral() - throws Exception { for (Integer left : smallInts) { for (Integer right : intRights) { - assertExecute(generateExpression("%s = %s", left, right), BOOLEAN, left == null || right == null ? null : (long) left == right); - assertExecute(generateExpression("%s <> %s", left, right), BOOLEAN, left == null || right == null ? null : (long) left != right); - assertExecute(generateExpression("%s > %s", left, right), BOOLEAN, left == null || right == null ? null : (long) left > right); - assertExecute(generateExpression("%s < %s", left, right), BOOLEAN, left == null || right == null ? null : (long) left < right); - assertExecute(generateExpression("%s >= %s", left, right), BOOLEAN, left == null || right == null ? null : (long) left >= right); - assertExecute(generateExpression("%s <= %s", left, right), BOOLEAN, left == null || right == null ? null : (long) left <= right); - - assertExecute(generateExpression("nullif(%s, %s)", left, right), INTEGER, nullIf(left, right)); - assertExecute(generateExpression("%s is distinct from %s", left, right), BOOLEAN, !Objects.equals(left, right)); - - assertExecute(generateExpression("%s + %s", left, right), INTEGER, left == null || right == null ? null : left + right); - assertExecute(generateExpression("%s - %s", left, right), INTEGER, left == null || right == null ? null : left - right); - assertExecute(generateExpression("%s * %s", left, right), INTEGER, left == null || right == null ? null : left * right); - assertExecute(generateExpression("%s / %s", left, right), INTEGER, left == null || right == null ? null : left / right); - assertExecute(generateExpression("%s %% %s", left, right), INTEGER, left == null || right == null ? null : left % right); + assertThat(assertions.expression("a = b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : (long) left == right); + + assertThat(assertions.expression("a <> b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : (long) left != right); + + assertThat(assertions.expression("a > b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : (long) left > right); + + assertThat(assertions.expression("a < b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : (long) left < right); + + assertThat(assertions.expression("a >= b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : (long) left >= right); + + assertThat(assertions.expression("a <= b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : (long) left <= right); + + assertThat(assertions.function("nullif", toLiteral(left), toLiteral(right))) + .hasType(INTEGER) + .isEqualTo(nullIf(left, right)); + + assertThat(assertions.expression("a IS DISTINCT FROM b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(!Objects.equals(left, right)); + + assertThat(assertions.expression("a + b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(INTEGER) + .isEqualTo(left == null || right == null ? null : left + right); + + assertThat(assertions.expression("a - b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(INTEGER) + .isEqualTo(left == null || right == null ? null : left - right); + + assertThat(assertions.expression("a * b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(INTEGER) + .isEqualTo(left == null || right == null ? null : left * right); + + assertThat(assertions.expression("a / b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(INTEGER) + .isEqualTo(left == null || right == null ? null : left / right); + + assertThat(assertions.expression("a % b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(INTEGER) + .isEqualTo(left == null || right == null ? null : left % right); Long longLeft = left == null ? null : left * 1000000000L; - assertExecute(generateExpression("%s + %s", longLeft, right), BIGINT, longLeft == null || right == null ? null : longLeft + right); - assertExecute(generateExpression("%s - %s", longLeft, right), BIGINT, longLeft == null || right == null ? null : longLeft - right); - assertExecute(generateExpression("%s * %s", longLeft, right), BIGINT, longLeft == null || right == null ? null : longLeft * right); - assertExecute(generateExpression("%s / %s", longLeft, right), BIGINT, longLeft == null || right == null ? null : longLeft / right); - assertExecute(generateExpression("%s %% %s", longLeft, right), BIGINT, longLeft == null || right == null ? null : longLeft % right); + assertThat(assertions.expression("a + b") + .binding("a", toLiteral(longLeft)) + .binding("b", toLiteral(right))) + .hasType(BIGINT) + .isEqualTo(longLeft == null || right == null ? null : longLeft + right); + assertThat(assertions.expression("a - b") + .binding("a", toLiteral(longLeft)) + .binding("b", toLiteral(right))) + .hasType(BIGINT) + .isEqualTo(longLeft == null || right == null ? null : longLeft - right); + assertThat(assertions.expression("a * b") + .binding("a", toLiteral(longLeft)) + .binding("b", toLiteral(right))) + .hasType(BIGINT) + .isEqualTo(longLeft == null || right == null ? null : longLeft * right); + assertThat(assertions.expression("a / b") + .binding("a", toLiteral(longLeft)) + .binding("b", toLiteral(right))) + .hasType(BIGINT) + .isEqualTo(longLeft == null || right == null ? null : longLeft / right); + assertThat(assertions.expression("a % b") + .binding("a", toLiteral(longLeft)) + .binding("b", toLiteral(right))) + .hasType(BIGINT) + .isEqualTo(longLeft == null || right == null ? null : longLeft % right); } } - - Futures.allAsList(futures).get(); } @Test public void testBinaryOperatorsIntegralDouble() - throws Exception { for (Integer left : intLefts) { for (Double right : doubleRights) { - assertExecute(generateExpression("%s = %s", left, right), BOOLEAN, left == null || right == null ? null : (double) left == right); - assertExecute(generateExpression("%s <> %s", left, right), BOOLEAN, left == null || right == null ? null : (double) left != right); - assertExecute(generateExpression("%s > %s", left, right), BOOLEAN, left == null || right == null ? null : (double) left > right); - assertExecute(generateExpression("%s < %s", left, right), BOOLEAN, left == null || right == null ? null : (double) left < right); - assertExecute(generateExpression("%s >= %s", left, right), BOOLEAN, left == null || right == null ? null : (double) left >= right); - assertExecute(generateExpression("%s <= %s", left, right), BOOLEAN, left == null || right == null ? null : (double) left <= right); - - Object expectedNullIf = nullIf(left, right); - for (String expression : generateExpression("nullif(%s, CAST(%s as DOUBLE))", left, right)) { - functionAssertions.assertFunction(expression, INTEGER, expectedNullIf); - } - - assertExecute(generateExpression("%s is distinct from %s", left, right), BOOLEAN, !Objects.equals(left == null ? null : left.doubleValue(), right)); - - assertExecute(generateExpression("%s + %s", left, right), DOUBLE, left == null || right == null ? null : left + right); - assertExecute(generateExpression("%s - %s", left, right), DOUBLE, left == null || right == null ? null : left - right); - assertExecute(generateExpression("%s * %s", left, right), DOUBLE, left == null || right == null ? null : left * right); - assertExecute(generateExpression("%s / %s", left, right), DOUBLE, left == null || right == null ? null : left / right); - assertExecute(generateExpression("%s %% %s", left, right), DOUBLE, left == null || right == null ? null : left % right); + assertThat(assertions.expression("a = b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : (double) left == right); + + assertThat(assertions.expression("a <> b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : (double) left != right); + + assertThat(assertions.expression("a > b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : (double) left > right); + + assertThat(assertions.expression("a < b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : (double) left < right); + + assertThat(assertions.expression("a >= b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : (double) left >= right); + + assertThat(assertions.expression("a <= b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : (double) left <= right); + + assertThat(assertions.expression("nullif(a, b)") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(INTEGER) + .isEqualTo(nullIf(left, right)); + + assertThat(assertions.expression("a IS DISTINCT FROM b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(!Objects.equals(left == null ? null : left.doubleValue(), right)); + + assertThat(assertions.expression("a + b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(DOUBLE) + .isEqualTo(left == null || right == null ? null : left + right); + + assertThat(assertions.expression("a - b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(DOUBLE) + .isEqualTo(left == null || right == null ? null : left - right); + + assertThat(assertions.expression("a * b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(DOUBLE) + .isEqualTo(left == null || right == null ? null : left * right); + + assertThat(assertions.expression("a / b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(DOUBLE) + .isEqualTo(left == null || right == null ? null : left / right); + + assertThat(assertions.expression("a % b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(DOUBLE) + .isEqualTo(left == null || right == null ? null : left % right); } } - - Futures.allAsList(futures).get(); } @Test public void testBinaryOperatorsDoubleIntegral() - throws Exception { for (Double left : doubleLefts) { for (Integer right : intRights) { - assertExecute(generateExpression("CAST(%s as DOUBLE) = %s", left, right), BOOLEAN, left == null || right == null ? null : left == (double) right); - assertExecute(generateExpression("CAST(%s as DOUBLE) <> %s", left, right), BOOLEAN, left == null || right == null ? null : left != (double) right); - assertExecute(generateExpression("CAST(%s as DOUBLE) > %s", left, right), BOOLEAN, left == null || right == null ? null : left > (double) right); - assertExecute(generateExpression("CAST(%s as DOUBLE) < %s", left, right), BOOLEAN, left == null || right == null ? null : left < (double) right); - assertExecute(generateExpression("CAST(%s as DOUBLE) >= %s", left, right), BOOLEAN, left == null || right == null ? null : left >= (double) right); - assertExecute(generateExpression("CAST(%s as DOUBLE) <= %s", left, right), BOOLEAN, left == null || right == null ? null : left <= (double) right); - - assertExecute(generateExpression("nullif(CAST(%s as DOUBLE), %s)", left, right), DOUBLE, nullIf(left, right)); - assertExecute(generateExpression("CAST(%s as DOUBLE) is distinct from %s", left, right), BOOLEAN, !Objects.equals(left, right == null ? null : right.doubleValue())); - - assertExecute(generateExpression("CAST(%s as DOUBLE) + %s", left, right), DOUBLE, left == null || right == null ? null : left + right); - assertExecute(generateExpression("CAST(%s as DOUBLE) - %s", left, right), DOUBLE, left == null || right == null ? null : left - right); - assertExecute(generateExpression("CAST(%s as DOUBLE) * %s", left, right), DOUBLE, left == null || right == null ? null : left * right); - assertExecute(generateExpression("CAST(%s as DOUBLE) / %s", left, right), DOUBLE, left == null || right == null ? null : left / right); - assertExecute(generateExpression("CAST(%s as DOUBLE) %% %s", left, right), DOUBLE, left == null || right == null ? null : left % right); + assertThat(assertions.expression("a = b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : left == (double) right); + + assertThat(assertions.expression("a <> b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : left != (double) right); + + assertThat(assertions.expression("a > b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : left > (double) right); + + assertThat(assertions.expression("a < b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : left < (double) right); + + assertThat(assertions.expression("a >= b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : left >= (double) right); + + assertThat(assertions.expression("a <= b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : left <= (double) right); + + assertThat(assertions.function("nullif", toLiteral(left), toLiteral(right))) + .hasType(DOUBLE) + .isEqualTo(nullIf(left, right)); + + assertThat(assertions.expression("a IS DISTINCT FROM b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .isEqualTo(!Objects.equals(left, right == null ? null : right.doubleValue())); + + assertThat(assertions.expression("a + b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(DOUBLE) + .isEqualTo(left == null || right == null ? null : left + right); + + assertThat(assertions.expression("a - b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(DOUBLE) + .isEqualTo(left == null || right == null ? null : left - right); + + assertThat(assertions.expression("a * b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(DOUBLE) + .isEqualTo(left == null || right == null ? null : left * right); + + assertThat(assertions.expression("a / b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(DOUBLE) + .isEqualTo(left == null || right == null ? null : left / right); + + assertThat(assertions.expression("a % b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(DOUBLE) + .isEqualTo(left == null || right == null ? null : left % right); } } - - Futures.allAsList(futures).get(); } @Test public void testBinaryOperatorsDoubleDouble() - throws Exception { for (Double left : doubleLefts) { for (Double right : doubleRights) { - assertExecute(generateExpression("%s = %s", left, right), BOOLEAN, left == null || right == null ? null : (double) left == right); - assertExecute(generateExpression("%s <> %s", left, right), BOOLEAN, left == null || right == null ? null : (double) left != right); - assertExecute(generateExpression("%s > %s", left, right), BOOLEAN, left == null || right == null ? null : (double) left > right); - assertExecute(generateExpression("%s < %s", left, right), BOOLEAN, left == null || right == null ? null : (double) left < right); - assertExecute(generateExpression("%s >= %s", left, right), BOOLEAN, left == null || right == null ? null : (double) left >= right); - assertExecute(generateExpression("%s <= %s", left, right), BOOLEAN, left == null || right == null ? null : (double) left <= right); - - assertExecute(generateExpression("nullif(%s, %s)", left, right), DOUBLE, nullIf(left, right)); - assertExecute(generateExpression("%s is distinct from %s", left, right), BOOLEAN, !Objects.equals(left, right)); - - assertExecute(generateExpression("%s + %s", left, right), DOUBLE, left == null || right == null ? null : left + right); - assertExecute(generateExpression("%s - %s", left, right), DOUBLE, left == null || right == null ? null : left - right); - assertExecute(generateExpression("%s * %s", left, right), DOUBLE, left == null || right == null ? null : left * right); - assertExecute(generateExpression("%s / %s", left, right), DOUBLE, left == null || right == null ? null : left / right); - assertExecute(generateExpression("%s %% %s", left, right), DOUBLE, left == null || right == null ? null : left % right); + assertThat(assertions.expression("a = b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : (double) left == right); + + assertThat(assertions.expression("a <> b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : (double) left != right); + + assertThat(assertions.expression("a > b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : (double) left > right); + + assertThat(assertions.expression("a < b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : (double) left < right); + + assertThat(assertions.expression("a >= b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : (double) left >= right); + + assertThat(assertions.expression("a <= b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : (double) left <= right); + + assertThat(assertions.function("nullif", toLiteral(left), toLiteral(right))) + .hasType(DOUBLE) + .isEqualTo(nullIf(left, right)); + + assertThat(assertions.expression("a IS DISTINCT FROM b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(!Objects.equals(left, right)); + + assertThat(assertions.expression("a + b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(DOUBLE) + .isEqualTo(left == null || right == null ? null : left + right); + + assertThat(assertions.expression("a - b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(DOUBLE) + .isEqualTo(left == null || right == null ? null : left - right); + + assertThat(assertions.expression("a * b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(DOUBLE) + .isEqualTo(left == null || right == null ? null : left * right); + assertThat(assertions.expression("a / b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(DOUBLE) + .isEqualTo(left == null || right == null ? null : left / right); + assertThat(assertions.expression("a % b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(DOUBLE) + .isEqualTo(left == null || right == null ? null : left % right); } } - - Futures.allAsList(futures).get(); } @Test public void testBinaryOperatorsDecimalBigint() - throws Exception { for (BigDecimal left : decimalLefts) { for (Long right : longRights) { - assertExecute(generateExpression("%s = %s", left, right), BOOLEAN, left == null || right == null ? null : left.equals(new BigDecimal(right))); - assertExecute(generateExpression("%s <> %s", left, right), BOOLEAN, left == null || right == null ? null : !left.equals(new BigDecimal(right))); - assertExecute(generateExpression("%s > %s", left, right), BOOLEAN, left == null || right == null ? null : left.compareTo(new BigDecimal(right)) > 0); - assertExecute(generateExpression("%s < %s", left, right), BOOLEAN, left == null || right == null ? null : left.compareTo(new BigDecimal(right)) < 0); - assertExecute(generateExpression("%s >= %s", left, right), BOOLEAN, left == null || right == null ? null : left.compareTo(new BigDecimal(right)) >= 0); - assertExecute(generateExpression("%s <= %s", left, right), BOOLEAN, left == null || right == null ? null : left.compareTo(new BigDecimal(right)) <= 0); - - assertExecute(generateExpression("nullif(%s, %s)", left, right), BigDecimal.class.cast(nullIf(left, right))); - - assertExecute(generateExpression("%s is distinct from %s", left, right), BOOLEAN, - !Objects.equals(left, right == null ? null : new BigDecimal(right))); + assertThat(assertions.expression("a = b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : left.equals(new BigDecimal(right))); + + assertThat(assertions.expression("a <> b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : !left.equals(new BigDecimal(right))); + + assertThat(assertions.expression("a > b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : left.compareTo(new BigDecimal(right)) > 0); + + assertThat(assertions.expression("a < b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : left.compareTo(new BigDecimal(right)) < 0); + + assertThat(assertions.expression("a >= b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : left.compareTo(new BigDecimal(right)) >= 0); + + assertThat(assertions.expression("a <= b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : left.compareTo(new BigDecimal(right)) <= 0); + + assertThat(assertions.function("nullif", toLiteral(left), toLiteral(right))) + .isEqualTo(toSqlDecimal(BigDecimal.class.cast(nullIf(left, right)))); + + assertThat(assertions.expression("a IS DISTINCT FROM b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(!Objects.equals(left, right == null ? null : new BigDecimal(right))); // arithmetic operators are already tested in TestDecimalOperators } } - - Futures.allAsList(futures).get(); } @Test public void testBinaryOperatorsBigintDecimal() - throws Exception { for (Long left : longLefts) { for (BigDecimal right : decimalRights) { - assertExecute(generateExpression("%s = %s", left, right), BOOLEAN, left == null || right == null ? null : new BigDecimal(left).equals(right)); - assertExecute(generateExpression("%s <> %s", left, right), BOOLEAN, left == null || right == null ? null : !new BigDecimal(left).equals(right)); - assertExecute(generateExpression("%s > %s", left, right), BOOLEAN, left == null || right == null ? null : new BigDecimal(left).compareTo(right) > 0); - assertExecute(generateExpression("%s < %s", left, right), BOOLEAN, left == null || right == null ? null : new BigDecimal(left).compareTo(right) < 0); - assertExecute(generateExpression("%s >= %s", left, right), BOOLEAN, left == null || right == null ? null : new BigDecimal(left).compareTo(right) >= 0); - assertExecute(generateExpression("%s <= %s", left, right), BOOLEAN, left == null || right == null ? null : new BigDecimal(left).compareTo(right) <= 0); - - assertExecute(generateExpression("nullif(%s, %s)", left, right), BIGINT, left); - - assertExecute(generateExpression("%s is distinct from %s", left, right), BOOLEAN, - !Objects.equals(left == null ? null : new BigDecimal(left), right)); + assertThat(assertions.expression("a = b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : new BigDecimal(left).equals(right)); + + assertThat(assertions.expression("a <> b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : !new BigDecimal(left).equals(right)); + + assertThat(assertions.expression("a > b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : new BigDecimal(left).compareTo(right) > 0); + + assertThat(assertions.expression("a < b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : new BigDecimal(left).compareTo(right) < 0); + + assertThat(assertions.expression("a >= b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : new BigDecimal(left).compareTo(right) >= 0); + + assertThat(assertions.expression("a <= b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : new BigDecimal(left).compareTo(right) <= 0); + + assertThat(assertions.function("nullif", toLiteral(left), toLiteral(right))) + .hasType(BIGINT) + .isEqualTo(left); + + assertThat(assertions.expression("a IS DISTINCT FROM b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(!Objects.equals(left == null ? null : new BigDecimal(left), right)); // arithmetic operators are already tested in TestDecimalOperators } } - - Futures.allAsList(futures).get(); } @Test public void testBinaryOperatorsDecimalInteger() - throws Exception { for (BigDecimal left : decimalLefts) { for (Integer right : intRights) { - assertExecute(generateExpression("%s = %s", left, right), BOOLEAN, left == null || right == null ? null : left.equals(new BigDecimal(right))); - assertExecute(generateExpression("%s <> %s", left, right), BOOLEAN, left == null || right == null ? null : !left.equals(new BigDecimal(right))); - assertExecute(generateExpression("%s > %s", left, right), BOOLEAN, left == null || right == null ? null : left.compareTo(new BigDecimal(right)) > 0); - assertExecute(generateExpression("%s < %s", left, right), BOOLEAN, left == null || right == null ? null : left.compareTo(new BigDecimal(right)) < 0); - assertExecute(generateExpression("%s >= %s", left, right), BOOLEAN, left == null || right == null ? null : left.compareTo(new BigDecimal(right)) >= 0); - assertExecute(generateExpression("%s <= %s", left, right), BOOLEAN, left == null || right == null ? null : left.compareTo(new BigDecimal(right)) <= 0); - - assertExecute(generateExpression("nullif(%s, %s)", left, right), BigDecimal.class.cast(nullIf(left, right))); - - assertExecute(generateExpression("%s is distinct from %s", left, right), BOOLEAN, - !Objects.equals(left, right == null ? null : new BigDecimal(right))); + assertThat(assertions.expression("a = b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : left.equals(new BigDecimal(right))); + + assertThat(assertions.expression("a <> b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : !left.equals(new BigDecimal(right))); + + assertThat(assertions.expression("a > b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : left.compareTo(new BigDecimal(right)) > 0); + + assertThat(assertions.expression("a < b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : left.compareTo(new BigDecimal(right)) < 0); + + assertThat(assertions.expression("a >= b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : left.compareTo(new BigDecimal(right)) >= 0); + + assertThat(assertions.expression("a <= b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : left.compareTo(new BigDecimal(right)) <= 0); + + assertThat(assertions.function("nullif", toLiteral(left), toLiteral(right))) + .isEqualTo(toSqlDecimal(BigDecimal.class.cast(nullIf(left, right)))); + + assertThat(assertions.expression("a IS DISTINCT FROM b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(!Objects.equals(left, right == null ? null : new BigDecimal(right))); // arithmetic operators are already tested in TestDecimalOperators } } - - Futures.allAsList(futures).get(); } @Test public void testBinaryOperatorsIntegerDecimal() - throws Exception { for (Integer left : intLefts) { for (BigDecimal right : decimalRights) { - assertExecute(generateExpression("%s = %s", left, right), BOOLEAN, left == null || right == null ? null : new BigDecimal(left).equals(right)); - assertExecute(generateExpression("%s <> %s", left, right), BOOLEAN, left == null || right == null ? null : !new BigDecimal(left).equals(right)); - assertExecute(generateExpression("%s > %s", left, right), BOOLEAN, left == null || right == null ? null : new BigDecimal(left).compareTo(right) > 0); - assertExecute(generateExpression("%s < %s", left, right), BOOLEAN, left == null || right == null ? null : new BigDecimal(left).compareTo(right) < 0); - assertExecute(generateExpression("%s >= %s", left, right), BOOLEAN, left == null || right == null ? null : new BigDecimal(left).compareTo(right) >= 0); - assertExecute(generateExpression("%s <= %s", left, right), BOOLEAN, left == null || right == null ? null : new BigDecimal(left).compareTo(right) <= 0); - - assertExecute(generateExpression("nullif(%s, %s)", left, right), INTEGER, left); - - assertExecute(generateExpression("%s is distinct from %s", left, right), BOOLEAN, - !Objects.equals(left == null ? null : new BigDecimal(left), right)); + assertThat(assertions.expression("a = b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : new BigDecimal(left).equals(right)); + + assertThat(assertions.expression("a <> b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : !new BigDecimal(left).equals(right)); + + assertThat(assertions.expression("a > b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : new BigDecimal(left).compareTo(right) > 0); + + assertThat(assertions.expression("a < b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : new BigDecimal(left).compareTo(right) < 0); + + assertThat(assertions.expression("a >= b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : new BigDecimal(left).compareTo(right) >= 0); + + assertThat(assertions.expression("a <= b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : new BigDecimal(left).compareTo(right) <= 0); + + assertThat(assertions.function("nullif", toLiteral(left), toLiteral(right))) + .hasType(INTEGER) + .isEqualTo(left); + + assertThat(assertions.expression("a IS DISTINCT FROM b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(!Objects.equals(left == null ? null : new BigDecimal(left), right)); // arithmetic operators are already tested in TestDecimalOperators } } - - Futures.allAsList(futures).get(); } @Test public void testBinaryOperatorsDecimalDouble() - throws Exception { for (BigDecimal left : decimalLefts) { for (Double right : doubleRights) { - assertExecute(generateExpression("%s = %s", left, right), BOOLEAN, left == null || right == null ? null : left.doubleValue() == right); - assertExecute(generateExpression("%s <> %s", left, right), BOOLEAN, left == null || right == null ? null : left.doubleValue() != right); - assertExecute(generateExpression("%s > %s", left, right), BOOLEAN, left == null || right == null ? null : left.doubleValue() > right); - assertExecute(generateExpression("%s < %s", left, right), BOOLEAN, left == null || right == null ? null : left.doubleValue() < right); - assertExecute(generateExpression("%s >= %s", left, right), BOOLEAN, left == null || right == null ? null : left.doubleValue() >= right); - assertExecute(generateExpression("%s <= %s", left, right), BOOLEAN, left == null || right == null ? null : left.doubleValue() <= right); - - assertExecute(generateExpression("nullif(%s, %s)", left, right), BigDecimal.class.cast(nullIf(left, right))); - - assertExecute(generateExpression("%s is distinct from %s", left, right), BOOLEAN, !Objects.equals(left == null ? null : left.doubleValue(), right)); - - assertExecute(generateExpression("%s + %s", left, right), DOUBLE, left == null || right == null ? null : left.doubleValue() + right); - assertExecute(generateExpression("%s - %s", left, right), DOUBLE, left == null || right == null ? null : left.doubleValue() - right); - assertExecute(generateExpression("%s * %s", left, right), DOUBLE, left == null || right == null ? null : left.doubleValue() * right); - assertExecute(generateExpression("%s / %s", left, right), DOUBLE, left == null || right == null ? null : left.doubleValue() / right); - assertExecute(generateExpression("%s %% %s", left, right), DOUBLE, left == null || right == null ? null : left.doubleValue() % right); + assertThat(assertions.expression("a = b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : left.doubleValue() == right); + + assertThat(assertions.expression("a <> b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : left.doubleValue() != right); + + assertThat(assertions.expression("a > b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : left.doubleValue() > right); + + assertThat(assertions.expression("a < b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : left.doubleValue() < right); + + assertThat(assertions.expression("a >= b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : left.doubleValue() >= right); + + assertThat(assertions.expression("a <= b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : left.doubleValue() <= right); + + assertThat(assertions.function("nullif", toLiteral(left), toLiteral(right))) + .isEqualTo(toSqlDecimal(BigDecimal.class.cast(nullIf(left, right)))); + + assertThat(assertions.expression("a IS DISTINCT FROM b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(!Objects.equals(left == null ? null : left.doubleValue(), right)); + + assertThat(assertions.expression("a + b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(DOUBLE) + .isEqualTo(left == null || right == null ? null : left.doubleValue() + right); + + assertThat(assertions.expression("a - b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(DOUBLE) + .isEqualTo(left == null || right == null ? null : left.doubleValue() - right); + + assertThat(assertions.expression("a * b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(DOUBLE) + .isEqualTo(left == null || right == null ? null : left.doubleValue() * right); + + assertThat(assertions.expression("a / b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(DOUBLE) + .isEqualTo(left == null || right == null ? null : left.doubleValue() / right); + + assertThat(assertions.expression("a % b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(DOUBLE) + .isEqualTo(left == null || right == null ? null : left.doubleValue() % right); } } - - Futures.allAsList(futures).get(); } @Test public void testBinaryOperatorsDoubleDecimal() - throws Exception { for (Double left : doubleLefts) { for (BigDecimal right : decimalRights) { - assertExecute(generateExpression("%s = %s", left, right), BOOLEAN, left == null || right == null ? null : left == right.doubleValue()); - assertExecute(generateExpression("%s <> %s", left, right), BOOLEAN, left == null || right == null ? null : left != right.doubleValue()); - assertExecute(generateExpression("%s > %s", left, right), BOOLEAN, left == null || right == null ? null : left > right.doubleValue()); - assertExecute(generateExpression("%s < %s", left, right), BOOLEAN, left == null || right == null ? null : left < right.doubleValue()); - assertExecute(generateExpression("%s >= %s", left, right), BOOLEAN, left == null || right == null ? null : left >= right.doubleValue()); - assertExecute(generateExpression("%s <= %s", left, right), BOOLEAN, left == null || right == null ? null : left <= right.doubleValue()); - - assertExecute(generateExpression("nullif(%s, %s)", left, right), DOUBLE, nullIf(left, right)); - assertExecute(generateExpression("%s is distinct from %s", left, right), BOOLEAN, !Objects.equals(left, right == null ? null : right.doubleValue())); - - assertExecute(generateExpression("%s + %s", left, right), DOUBLE, left == null || right == null ? null : left + right.doubleValue()); - assertExecute(generateExpression("%s - %s", left, right), DOUBLE, left == null || right == null ? null : left - right.doubleValue()); - assertExecute(generateExpression("%s * %s", left, right), DOUBLE, left == null || right == null ? null : left * right.doubleValue()); - assertExecute(generateExpression("%s / %s", left, right), DOUBLE, left == null || right == null ? null : left / right.doubleValue()); - assertExecute(generateExpression("%s %% %s", left, right), DOUBLE, left == null || right == null ? null : left % right.doubleValue()); + assertThat(assertions.expression("a = b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : left == right.doubleValue()); + + assertThat(assertions.expression("a <> b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : left != right.doubleValue()); + + assertThat(assertions.expression("a > b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : left > right.doubleValue()); + + assertThat(assertions.expression("a < b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : left < right.doubleValue()); + + assertThat(assertions.expression("a >= b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : left >= right.doubleValue()); + + assertThat(assertions.expression("a <= b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : left <= right.doubleValue()); + + assertThat(assertions.function("nullif", toLiteral(left), toLiteral(right))) + .hasType(DOUBLE) + .isEqualTo(nullIf(left, right)); + + assertThat(assertions.expression("a IS DISTINCT FROM b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(!Objects.equals(left, right == null ? null : right.doubleValue())); + + assertThat(assertions.expression("a + b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(DOUBLE) + .isEqualTo(left == null || right == null ? null : left + right.doubleValue()); + + assertThat(assertions.expression("a - b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(DOUBLE) + .isEqualTo(left == null || right == null ? null : left - right.doubleValue()); + + assertThat(assertions.expression("a * b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(DOUBLE) + .isEqualTo(left == null || right == null ? null : left * right.doubleValue()); + + assertThat(assertions.expression("a / b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(DOUBLE) + .isEqualTo(left == null || right == null ? null : left / right.doubleValue()); + + assertThat(assertions.expression("a % b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(DOUBLE) + .isEqualTo(left == null || right == null ? null : left % right.doubleValue()); } } - - Futures.allAsList(futures).get(); } @Test public void testBinaryOperatorsString() - throws Exception { for (String left : stringLefts) { for (String right : stringRights) { - assertExecute(generateExpression("%s = %s", left, right), BOOLEAN, left == null || right == null ? null : left.equals(right)); - assertExecute(generateExpression("%s <> %s", left, right), BOOLEAN, left == null || right == null ? null : !left.equals(right)); - assertExecute(generateExpression("%s > %s", left, right), BOOLEAN, left == null || right == null ? null : left.compareTo(right) > 0); - assertExecute(generateExpression("%s < %s", left, right), BOOLEAN, left == null || right == null ? null : left.compareTo(right) < 0); - assertExecute(generateExpression("%s >= %s", left, right), BOOLEAN, left == null || right == null ? null : left.compareTo(right) >= 0); - assertExecute(generateExpression("%s <= %s", left, right), BOOLEAN, left == null || right == null ? null : left.compareTo(right) <= 0); - - assertExecute(generateExpression("%s || %s", left, right), VARCHAR, left == null || right == null ? null : left + right); - - assertExecute(generateExpression("%s is distinct from %s", left, right), BOOLEAN, !Objects.equals(left, right)); - - assertExecute(generateExpression("nullif(%s, %s)", left, right), varcharType(left), nullIf(left, right)); + assertThat(assertions.expression("a = b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : left.equals(right)); + + assertThat(assertions.expression("a <> b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : !left.equals(right)); + + assertThat(assertions.expression("a > b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : left.compareTo(right) > 0); + + assertThat(assertions.expression("a < b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : left.compareTo(right) < 0); + + assertThat(assertions.expression("a >= b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : left.compareTo(right) >= 0); + + assertThat(assertions.expression("a <= b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(left == null || right == null ? null : left.compareTo(right) <= 0); + + assertThat(assertions.expression("a || b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(VARCHAR) + .isEqualTo(left == null || right == null ? null : left + right); + + assertThat(assertions.expression("a IS DISTINCT FROM b") + .binding("a", toLiteral(left)) + .binding("b", toLiteral(right))) + .hasType(BOOLEAN) + .isEqualTo(!Objects.equals(left, right)); + + assertThat(assertions.function("nullif", toLiteral(left), toLiteral(right))) + .hasType(varcharType(left)) + .isEqualTo(nullIf(left, right)); } } - - Futures.allAsList(futures).get(); - } - - @Test - public void testNestedColumnFilter() - { - assertFilter("bound_row[1] = 1234", true); - assertFilter("bound_row[1] = 1223", false); - assertFilter("bound_row[2] = 34", true); - assertFilter("bound_row[2] = 33", false); - assertFilter("bound_row[3] = 'hello'", true); - assertFilter("bound_row[3] = 'value1'", false); - assertFilter("bound_row[4] = 12.34", true); - assertFilter("bound_row[4] = 34.34", false); - assertFilter("bound_row[5] = true", true); - assertFilter("bound_row[5] = false", false); - assertFilter("bound_row[7][1] = 'innerFieldValue'", true); - assertFilter("bound_row[7][1] != 'innerFieldValue'", false); - - // combination of types in one filter - assertFilter( - ImmutableList.of( - "bound_row[1] = 1234", "bound_row[8] >= 1234", - "bound_row[2] = 34", "bound_row[9] >= 33", - "bound_row[3] = 'hello'", "bound_row[10] >= 'hello'", - "bound_row[4] = 12.34", "bound_row[11] >= 12.34", - "bound_row[5] = true", "NOT (bound_row[12] = false)", - "bound_row[7][1] = 'innerFieldValue'", "bound_row[14][1] LIKE 'innerFieldValue'") - .stream().collect(joining(" AND ")), - true); } private static VarcharType varcharType(String... values) @@ -694,104 +1109,98 @@ private static Object nullIf(Object left, Object right) @Test public void testTernaryOperatorsLongLong() - throws Exception { for (Integer first : intLefts) { for (Integer second : intLefts) { for (Integer third : intRights) { - assertExecute(generateExpression("%s between %s and %s", first, second, third), - BOOLEAN, - between(first, second, third, (min, value) -> min <= value, (value, max) -> value <= max)); + assertThat(assertions.expression("value BETWEEN low AND high") + .binding("value", toLiteral(first)) + .binding("low", toLiteral(second)) + .binding("high", toLiteral(third))) + .isEqualTo(between(first, second, third, (min, value) -> min <= value, (value, max) -> value <= max)); } } } - - Futures.allAsList(futures).get(); } @Test public void testTernaryOperatorsLongDouble() - throws Exception { for (Integer first : intLefts) { for (Double second : doubleLefts) { for (Integer third : intRights) { - assertExecute(generateExpression("%s between %s and %s", first, second, third), - BOOLEAN, - between(first, second, third, (min, value) -> min <= value, (value, max) -> value <= max)); + assertThat(assertions.expression("value BETWEEN low AND high") + .binding("value", toLiteral(first)) + .binding("low", toLiteral(second)) + .binding("high", toLiteral(third))) + .isEqualTo(between(first, second, third, (min, value) -> min <= value, (value, max) -> value <= max)); } } } - - Futures.allAsList(futures).get(); } @Test public void testTernaryOperatorsDoubleDouble() - throws Exception { for (Double first : doubleLefts) { for (Double second : doubleLefts) { for (Integer third : intRights) { - assertExecute(generateExpression("%s between %s and %s", first, second, third), - BOOLEAN, - between(first, second, third, (min, value) -> min <= value, (value, max) -> value <= max)); + assertThat(assertions.expression("value BETWEEN low AND high") + .binding("value", toLiteral(first)) + .binding("low", toLiteral(second)) + .binding("high", toLiteral(third))) + .isEqualTo(between(first, second, third, (min, value) -> min <= value, (value, max) -> value <= max)); } } } - - Futures.allAsList(futures).get(); } @Test public void testTernaryOperatorsString() - throws Exception { for (String first : stringLefts) { for (String second : stringLefts) { for (String third : stringRights) { - assertExecute(generateExpression("%s between %s and %s", first, second, third), - BOOLEAN, - between(first, second, third, (min, value) -> min.compareTo(value) <= 0, (value, max) -> value.compareTo(max) <= 0)); + assertThat(assertions.expression("value BETWEEN low AND high") + .binding("value", toLiteral(first)) + .binding("low", toLiteral(second)) + .binding("high", toLiteral(third))) + .isEqualTo(between(first, second, third, (min, value) -> min.compareTo(value) <= 0, (value, max) -> value.compareTo(max) <= 0)); } } } - - Futures.allAsList(futures).get(); } @Test public void testTernaryOperatorsLongDecimal() - throws Exception { for (Long first : longLefts) { for (BigDecimal second : decimalMiddle) { for (Long third : longRights) { - assertExecute(generateExpression("%s between %s and %s", first, second, third), - BOOLEAN, - between(first, second, third, (min, value) -> min.compareTo(new BigDecimal(value)) <= 0, (value, max) -> value <= max)); + assertThat(assertions.expression("value BETWEEN low AND high") + .binding("value", toLiteral(first)) + .binding("low", toLiteral(second)) + .binding("high", toLiteral(third))) + .isEqualTo(between(first, second, third, (min, value) -> min.compareTo(new BigDecimal(value)) <= 0, (value, max) -> value <= max)); } } } - - Futures.allAsList(futures).get(); } @Test public void testTernaryOperatorsDecimalDouble() - throws Exception { for (BigDecimal first : decimalLefts) { for (Double second : doubleMiddle) { for (BigDecimal third : decimalRights) { - assertExecute(generateExpression("%s between %s and %s", first, second, third), - BOOLEAN, - between(first, second, third, (min, value) -> min <= value.doubleValue(), (value, max) -> value.compareTo(max) <= 0)); + assertThat(assertions.expression("value BETWEEN low AND high") + .binding("value", toLiteral(first)) + .binding("low", toLiteral(second)) + .binding("high", toLiteral(third))) + .isEqualTo(between(first, second, third, (min, value) -> min <= value.doubleValue(), (value, max) -> value.compareTo(max) <= 0)); } } } - - Futures.allAsList(futures).get(); } private static Boolean between(V value, L min, H max, BiPredicate greaterThanOrEquals, BiPredicate lessThanOrEquals) @@ -814,173 +1223,459 @@ private static Boolean between(V value, L min, H max, BiPredicate= Long.MIN_VALUE && value < Long.MAX_VALUE)) { - assertExecute(generateExpression("cast(%s as bigint)", value), BIGINT, value == null ? null : value.longValue()); + assertThat(assertions.expression("CAST(a AS bigint)") + .binding("a", toLiteral(value))) + .hasType(BIGINT) + .isEqualTo(value == null ? null : value.longValue()); } - assertExecute(generateExpression("cast(%s as double)", value), DOUBLE, value == null ? null : value); - assertExecute(generateExpression("cast(%s as varchar)", value), VARCHAR, value == null ? null : doubleFormat.format(value)); - } - - assertExecute("cast('true' as boolean)", BOOLEAN, true); - assertExecute("cast('true' as BOOLEAN)", BOOLEAN, true); - assertExecute("cast('tRuE' as BOOLEAN)", BOOLEAN, true); - assertExecute("cast('false' as BOOLEAN)", BOOLEAN, false); - assertExecute("cast('fAlSe' as BOOLEAN)", BOOLEAN, false); - assertExecute("cast('t' as BOOLEAN)", BOOLEAN, true); - assertExecute("cast('T' as BOOLEAN)", BOOLEAN, true); - assertExecute("cast('f' as BOOLEAN)", BOOLEAN, false); - assertExecute("cast('F' as BOOLEAN)", BOOLEAN, false); - assertExecute("cast('1' as BOOLEAN)", BOOLEAN, true); - assertExecute("cast('0' as BOOLEAN)", BOOLEAN, false); + + assertThat(assertions.expression("CAST(a AS double)") + .binding("a", toLiteral(value))) + .hasType(DOUBLE) + .isEqualTo(value == null ? null : value); + + assertThat(assertions.expression("CAST(a AS varchar)") + .binding("a", toLiteral(value))) + .hasType(VARCHAR) + .isEqualTo(value == null ? null : doubleFormat.format(value)); + } + + assertThat(assertions.expression("CAST(a AS boolean)") + .binding("a", "'true'")) + .hasType(BOOLEAN) + .isEqualTo(true); + + assertThat(assertions.expression("CAST(a AS BOOLEAN)") + .binding("a", "'true'")) + .hasType(BOOLEAN) + .isEqualTo(true); + + assertThat(assertions.expression("CAST(a AS BOOLEAN)") + .binding("a", "'tRuE'")) + .hasType(BOOLEAN) + .isEqualTo(true); + + assertThat(assertions.expression("CAST(a AS BOOLEAN)") + .binding("a", "'false'")) + .hasType(BOOLEAN) + .isEqualTo(false); + + assertThat(assertions.expression("CAST(a AS BOOLEAN)") + .binding("a", "'fAlSe'")) + .hasType(BOOLEAN) + .isEqualTo(false); + + assertThat(assertions.expression("CAST(a AS BOOLEAN)") + .binding("a", "'t'")) + .hasType(BOOLEAN) + .isEqualTo(true); + + assertThat(assertions.expression("CAST(a AS BOOLEAN)") + .binding("a", "'T'")) + .hasType(BOOLEAN) + .isEqualTo(true); + + assertThat(assertions.expression("CAST(a AS BOOLEAN)") + .binding("a", "'f'")) + .hasType(BOOLEAN) + .isEqualTo(false); + + assertThat(assertions.expression("CAST(a AS BOOLEAN)") + .binding("a", "'F'")) + .hasType(BOOLEAN) + .isEqualTo(false); + + assertThat(assertions.expression("CAST(a AS BOOLEAN)") + .binding("a", "'1'")) + .hasType(BOOLEAN) + .isEqualTo(true); + + assertThat(assertions.expression("CAST(a AS BOOLEAN)") + .binding("a", "'0'")) + .hasType(BOOLEAN) + .isEqualTo(false); for (Integer value : intLefts) { if (value != null) { - assertExecute(generateExpression("cast(%s as integer)", String.valueOf(value)), INTEGER, value == null ? null : value); - assertExecute(generateExpression("cast(%s as bigint)", String.valueOf(value)), BIGINT, value == null ? null : (long) value); + assertThat(assertions.expression("CAST(a AS integer)") + .binding("a", toLiteral(String.valueOf(value)))) + .hasType(INTEGER) + .isEqualTo(value == null ? null : value); + + assertThat(assertions.expression("CAST(a AS bigint)") + .binding("a", toLiteral(String.valueOf(value)))) + .hasType(BIGINT) + .isEqualTo(value == null ? null : (long) value); } } for (Double value : doubleLefts) { if (value != null) { - assertExecute(generateExpression("cast(%s as double)", String.valueOf(value)), DOUBLE, value == null ? null : value); + assertThat(assertions.expression("CAST(a AS double)") + .binding("a", toLiteral(String.valueOf(value)))) + .hasType(DOUBLE) + .isEqualTo(value == null ? null : value); } } for (String value : stringLefts) { - assertExecute(generateExpression("cast(%s as varchar)", value), VARCHAR, value == null ? null : value); + assertThat(assertions.expression("CAST(a AS varchar)") + .binding("a", toLiteral(value))) + .hasType(VARCHAR) + .isEqualTo(value == null ? null : value); } - - Futures.allAsList(futures).get(); } @Test public void testTryCast() - throws Exception { - assertExecute("try_cast(null as integer)", INTEGER, null); - assertExecute("try_cast('123' as integer)", INTEGER, 123); - assertExecute("try_cast(null as bigint)", BIGINT, null); - assertExecute("try_cast('123' as bigint)", BIGINT, 123L); - assertExecute("try_cast('foo' as varchar)", VARCHAR, "foo"); - assertExecute("try_cast('foo' as bigint)", BIGINT, null); - assertExecute("try_cast('foo' as integer)", INTEGER, null); - assertExecute("try_cast('2001-08-22' as timestamp)", TIMESTAMP_MILLIS, sqlTimestampOf(3, 2001, 8, 22, 0, 0, 0, 0)); - assertExecute("try_cast(bound_string as bigint)", BIGINT, null); - assertExecute("try_cast(cast(null as varchar) as bigint)", BIGINT, null); - assertExecute("try_cast(bound_long / 13 as bigint)", BIGINT, 94L); - assertExecute("coalesce(try_cast('123' as bigint), 456)", BIGINT, 123L); - assertExecute("coalesce(try_cast('foo' as bigint), 456)", BIGINT, 456L); - assertExecute("concat('foo', VARCHAR 'bar')", VARCHAR, "foobar"); - assertExecute("try_cast(try_cast(123 as varchar) as bigint)", BIGINT, 123L); - assertExecute("try_cast('foo' as varchar) || try_cast('bar' as varchar)", VARCHAR, "foobar"); - - Futures.allAsList(futures).get(); + assertThat(assertions.expression("try_CAST(a AS integer)") + .binding("a", "null")) + .isNull(INTEGER); + + assertThat(assertions.expression("try_CAST(a AS integer)") + .binding("a", "'123'")) + .hasType(INTEGER) + .isEqualTo(123); + + assertThat(assertions.expression("try_CAST(a AS bigint)") + .binding("a", "null")) + .isNull(BIGINT); + + assertThat(assertions.expression("try_CAST(a AS bigint)") + .binding("a", "'123'")) + .hasType(BIGINT) + .isEqualTo(123L); + + assertThat(assertions.expression("try_CAST(a AS varchar)") + .binding("a", "'foo'")) + .hasType(VARCHAR) + .isEqualTo("foo"); + + assertThat(assertions.expression("try_CAST(a AS bigint)") + .binding("a", "'foo'")) + .isNull(BIGINT); + + assertThat(assertions.expression("try_CAST(a AS integer)") + .binding("a", "'foo'")) + .isNull(INTEGER); + + assertThat(assertions.expression("try_CAST(a AS timestamp)") + .binding("a", "'2001-08-22'")) + .hasType(TIMESTAMP_MILLIS) + .isEqualTo(sqlTimestampOf(3, 2001, 8, 22, 0, 0, 0, 0)); + + assertThat(assertions.expression("try_CAST(a AS bigint)") + .binding("a", "'hello'")) + .isNull(BIGINT); + + assertThat(assertions.expression("try_CAST(a as bigint)") + .binding("a", "CAST(null AS varchar)")) + .isNull(BIGINT); + + assertThat(assertions.expression("try_CAST(a / 13 AS bigint)") + .binding("a", "BIGINT '1234'")) + .hasType(BIGINT) + .isEqualTo(94L); + + assertThat(assertions.function("coalesce", "try_CAST('123' as bigint)", "456")) + .hasType(BIGINT) + .isEqualTo(123L); + + assertThat(assertions.function("coalesce", "try_CAST('foo' as bigint)", "456")) + .hasType(BIGINT) + .isEqualTo(456L); + + assertThat(assertions.expression("try_CAST(try_CAST(a AS varchar) as bigint)") + .binding("a", "123")) + .hasType(BIGINT) + .isEqualTo(123L); + + assertThat(assertions.expression("try_CAST(a AS varchar) || try_CAST(b as varchar)") + .binding("a", "'foo'") + .binding("b", "'bar'")) + .hasType(VARCHAR) + .isEqualTo("foobar"); } @Test public void testAnd() - throws Exception { - assertExecute("true and true", BOOLEAN, true); - assertExecute("true and false", BOOLEAN, false); - assertExecute("false and true", BOOLEAN, false); - assertExecute("false and false", BOOLEAN, false); - - assertExecute("true and cast(null as boolean)", BOOLEAN, null); - assertExecute("false and cast(null as boolean)", BOOLEAN, false); - assertExecute("cast(null as boolean) and true", BOOLEAN, null); - assertExecute("cast(null as boolean) and false", BOOLEAN, false); - assertExecute("cast(null as boolean) and cast(null as boolean)", BOOLEAN, null); - - assertExecute("true and null", BOOLEAN, null); - assertExecute("false and null", BOOLEAN, false); - assertExecute("null and true", BOOLEAN, null); - assertExecute("null and false", BOOLEAN, false); - assertExecute("null and null", BOOLEAN, null); - - Futures.allAsList(futures).get(); + assertThat(assertions.expression("a AND b") + .binding("a", "true") + .binding("b", "true")) + .hasType(BOOLEAN) + .isEqualTo(true); + + assertThat(assertions.expression("a AND b") + .binding("a", "true") + .binding("b", "false")) + .hasType(BOOLEAN) + .isEqualTo(false); + + assertThat(assertions.expression("a AND b") + .binding("a", "false") + .binding("b", "true")) + .hasType(BOOLEAN) + .isEqualTo(false); + + assertThat(assertions.expression("a AND b") + .binding("a", "false") + .binding("b", "false")) + .hasType(BOOLEAN) + .isEqualTo(false); + + assertThat(assertions.expression("a AND b") + .binding("a", "true") + .binding("b", "CAST(null as boolean)")) + .isNull(BOOLEAN); + + assertThat(assertions.expression("a AND b") + .binding("a", "false") + .binding("b", "CAST(null as boolean)")) + .hasType(BOOLEAN) + .isEqualTo(false); + + assertThat(assertions.expression("a AND b") + .binding("a", "CAST(null as boolean)") + .binding("b", "true")) + .isNull(BOOLEAN); + + assertThat(assertions.expression("a AND b") + .binding("a", "CAST(null as boolean)") + .binding("b", "false")) + .hasType(BOOLEAN) + .isEqualTo(false); + + assertThat(assertions.expression("a AND b") + .binding("a", "CAST(null as boolean)") + .binding("b", "CAST(null as boolean)")) + .isNull(BOOLEAN); + + assertThat(assertions.expression("a AND b") + .binding("a", "true") + .binding("b", "null")) + .isNull(BOOLEAN); + + assertThat(assertions.expression("a AND b") + .binding("a", "false") + .binding("b", "null")) + .hasType(BOOLEAN) + .isEqualTo(false); + + assertThat(assertions.expression("a AND b") + .binding("a", "null") + .binding("b", "true")) + .isNull(BOOLEAN); + + assertThat(assertions.expression("a AND b") + .binding("a", "null") + .binding("b", "false")) + .hasType(BOOLEAN) + .isEqualTo(false); + + assertThat(assertions.expression("a AND b") + .binding("a", "null") + .binding("b", "null")) + .isNull(BOOLEAN); } @Test public void testOr() - throws Exception { - assertExecute("true or true", BOOLEAN, true); - assertExecute("true or false", BOOLEAN, true); - assertExecute("false or true", BOOLEAN, true); - assertExecute("false or false", BOOLEAN, false); - - assertExecute("true or cast(null as boolean)", BOOLEAN, true); - assertExecute("false or cast(null as boolean)", BOOLEAN, null); - assertExecute("cast(null as boolean) or true", BOOLEAN, true); - assertExecute("cast(null as boolean) or false", BOOLEAN, null); - assertExecute("cast(null as boolean) or cast(null as boolean)", BOOLEAN, null); - - assertExecute("true or null", BOOLEAN, true); - assertExecute("false or null", BOOLEAN, null); - assertExecute("null or true", BOOLEAN, true); - assertExecute("null or false", BOOLEAN, null); - assertExecute("null or null", BOOLEAN, null); - - Futures.allAsList(futures).get(); + assertThat(assertions.expression("a OR b") + .binding("a", "true") + .binding("b", "true")) + .hasType(BOOLEAN) + .isEqualTo(true); + + assertThat(assertions.expression("a OR b") + .binding("a", "true") + .binding("b", "false")) + .hasType(BOOLEAN) + .isEqualTo(true); + + assertThat(assertions.expression("a OR b") + .binding("a", "false") + .binding("b", "true")) + .hasType(BOOLEAN) + .isEqualTo(true); + + assertThat(assertions.expression("a OR b") + .binding("a", "false") + .binding("b", "false")) + .hasType(BOOLEAN) + .isEqualTo(false); + + assertThat(assertions.expression("a OR b") + .binding("a", "true") + .binding("b", "CAST(null as boolean)")) + .hasType(BOOLEAN) + .isEqualTo(true); + + assertThat(assertions.expression("a OR b") + .binding("a", "false") + .binding("b", "CAST(null as boolean)")) + .isNull(BOOLEAN); + + assertThat(assertions.expression("a OR b") + .binding("a", "CAST(null as boolean)") + .binding("b", "true")) + .hasType(BOOLEAN) + .isEqualTo(true); + + assertThat(assertions.expression("a OR b") + .binding("a", "CAST(null as boolean)") + .binding("b", "false")) + .isNull(BOOLEAN); + + assertThat(assertions.expression("a OR b") + .binding("a", "CAST(null as boolean)") + .binding("b", "CAST(null as boolean)")) + .isNull(BOOLEAN); + + assertThat(assertions.expression("a OR b") + .binding("a", "true") + .binding("b", "null")) + .hasType(BOOLEAN) + .isEqualTo(true); + + assertThat(assertions.expression("a OR b") + .binding("a", "false") + .binding("b", "null")) + .isNull(BOOLEAN); + + assertThat(assertions.expression("a OR b") + .binding("a", "null") + .binding("b", "true")) + .hasType(BOOLEAN) + .isEqualTo(true); + + assertThat(assertions.expression("a OR b") + .binding("a", "null") + .binding("b", "false")) + .isNull(BOOLEAN); + + assertThat(assertions.expression("a OR b") + .binding("a", "null") + .binding("b", "null")) + .isNull(BOOLEAN); } @Test public void testNot() - throws Exception { - assertExecute("not true", BOOLEAN, false); - assertExecute("not false", BOOLEAN, true); + assertThat(assertions.expression("NOT a") + .binding("a", "true")) + .hasType(BOOLEAN) + .isEqualTo(false); - assertExecute("not cast(null as boolean)", BOOLEAN, null); + assertThat(assertions.expression("NOT a") + .binding("a", "false")) + .hasType(BOOLEAN) + .isEqualTo(true); - assertExecute("not null", BOOLEAN, null); + assertThat(assertions.expression("NOT a") + .binding("a", "CAST(null as boolean)")) + .isNull(BOOLEAN); - Futures.allAsList(futures).get(); + assertThat(assertions.expression("NOT a") + .binding("a", "null")) + .isNull(BOOLEAN); } @Test public void testIf() - throws Exception { - assertExecute("if(null and true, BIGINT '1', 0)", BIGINT, 0L); - assertExecute("if(null and true, 1, 0)", INTEGER, 0); + assertThat(assertions.expression("if(a AND b, BIGINT '1', 0)") + .binding("a", "null") + .binding("b", "true")) + .hasType(BIGINT) + .isEqualTo(0L); + + assertThat(assertions.expression("if(a AND b, 1, 0)") + .binding("a", "null") + .binding("b", "true")) + .hasType(INTEGER) + .isEqualTo(0); + for (Boolean condition : booleanValues) { for (String trueValue : stringLefts) { for (String falseValue : stringRights) { - assertExecute( - generateExpression("if(%s, %s, %s)", condition, trueValue, falseValue), - varcharType(trueValue, falseValue), - condition != null && condition ? trueValue : falseValue); + assertThat(assertions.expression("if(a, b, c)") + .binding("a", toLiteral(condition)) + .binding("b", toLiteral(trueValue)) + .binding("c", toLiteral(falseValue))) + .hasType(varcharType(trueValue, falseValue)) + .isEqualTo(condition != null && condition ? trueValue : falseValue); } } } - - Futures.allAsList(futures).get(); } @Test public void testSimpleCase() - throws Exception { for (Double value : doubleLefts) { for (Double firstTest : doubleMiddle) { @@ -998,7 +1693,13 @@ else if (secondTest != null && (double) value == secondTest) { else { expected = "else"; } - assertExecute(generateExpression("case %s when %s then 'first' when %s then 'second' else 'else' end", value, firstTest, secondTest), createVarcharType(6), expected); + + assertThat(assertions.expression("case a when c1 then 'first' when c2 then 'second' else 'else' end") + .binding("a", toLiteral(value)) + .binding("c1", toLiteral(firstTest)) + .binding("c2", toLiteral(secondTest))) + .hasType(createVarcharType(6)) + .isEqualTo(expected); } } } @@ -1018,7 +1719,13 @@ else if (secondTest != null && secondTest.equals(value)) { else { expected = null; } - assertExecute(generateExpression("case %s when %s then 'first' when %s then 'second' end", value, firstTest, secondTest), createVarcharType(6), expected); + + assertThat(assertions.expression("case a when c1 then 'first' when c2 then 'second' end") + .binding("a", toLiteral(value)) + .binding("c1", toLiteral(firstTest)) + .binding("c2", toLiteral(secondTest))) + .hasType(createVarcharType(6)) + .isEqualTo(expected); } } } @@ -1039,21 +1746,38 @@ else if (secondTest != null && secondTest.equals(value)) { else { expected = null; } - assertExecute(generateExpression("case %s when %s then 'first' when %s then 'second' end", value, firstTest, secondTest), createVarcharType(6), expected); + + assertThat(assertions.expression("case a when c1 then 'first' when c2 then 'second' end") + .binding("a", toLiteral(value)) + .binding("c1", toLiteral(firstTest)) + .binding("c2", toLiteral(secondTest))) + .hasType(createVarcharType(6)) + .isEqualTo(expected); } } } - assertExecute("case ARRAY[CAST(1 AS BIGINT)] when ARRAY[CAST(1 AS BIGINT)] then 'matched' else 'not_matched' end", createVarcharType(11), "matched"); - assertExecute("case ARRAY[CAST(2 AS BIGINT)] when ARRAY[CAST(1 AS BIGINT)] then 'matched' else 'not_matched' end", createVarcharType(11), "not_matched"); - assertExecute("case ARRAY[CAST(null AS BIGINT)] when ARRAY[CAST(1 AS BIGINT)] then 'matched' else 'not_matched' end", createVarcharType(11), "not_matched"); + assertThat(assertions.expression("CASE a WHEN b THEN 'matched' ELSE 'not_matched' END") + .binding("a", "ARRAY[CAST(1 AS BIGINT)]") + .binding("b", "ARRAY[CAST(1 AS BIGINT)]")) + .hasType(createVarcharType(11)) + .isEqualTo("matched"); + + assertThat(assertions.expression("CASE a WHEN b THEN 'matched' ELSE 'not_matched' END") + .binding("a", "ARRAY[CAST(2 AS BIGINT)]") + .binding("b", "ARRAY[CAST(1 AS BIGINT)]")) + .hasType(createVarcharType(11)) + .isEqualTo("not_matched"); - Futures.allAsList(futures).get(); + assertThat(assertions.expression("CASE a WHEN b THEN 'matched' ELSE 'not_matched' END") + .binding("a", "ARRAY[CAST(null AS BIGINT)]") + .binding("b", "ARRAY[CAST(1 AS BIGINT)]")) + .hasType(createVarcharType(11)) + .isEqualTo("not_matched"); } @Test public void testSearchCaseSingle() - throws Exception { // assertExecute("case when null and true then 1 else 0 end", 0L); for (Double value : doubleLefts) { @@ -1072,10 +1796,13 @@ else if (secondTest != null && (double) value == secondTest) { else { expected = "else"; } - List expressions = formatExpression("case when %s = %s then 'first' when %s = %s then 'second' else 'else' end", - Arrays.asList(value, firstTest, value, secondTest), - ImmutableList.of("double", "bigint", "double", "double")); - assertExecute(expressions, createVarcharType(6), expected); + + assertThat(assertions.expression("case when v = c1 then 'first' when v = c2 then 'second' else 'else' end") + .binding("v", toLiteral(value)) + .binding("c1", toLiteral(firstTest)) + .binding("c2", toLiteral(secondTest))) + .hasType(createVarcharType(6)) + .isEqualTo(expected); } } } @@ -1096,24 +1823,38 @@ else if (secondTest != null && value == secondTest.doubleValue()) { else { expected = "else"; } - List expressions = formatExpression("case when %s = %s then 'first' when %s = %s then 'second' else 'else' end", - Arrays.asList(value, firstTest, value, secondTest), - ImmutableList.of("double", "bigint", "double", "decimal(1,0)")); - assertExecute(expressions, createVarcharType(6), expected); + + assertThat(assertions.expression("case when v = c1 then 'first' when v = c2 then 'second' else 'else' end") + .binding("v", toLiteral(value)) + .binding("c1", toLiteral(firstTest)) + .binding("c2", toLiteral(secondTest))) + .hasType(createVarcharType(6)) + .isEqualTo(expected); } } } - assertExecute("case when ARRAY[CAST(1 AS BIGINT)] = ARRAY[CAST(1 AS BIGINT)] then 'matched' else 'not_matched' end", createVarcharType(11), "matched"); - assertExecute("case when ARRAY[CAST(2 AS BIGINT)] = ARRAY[CAST(1 AS BIGINT)] then 'matched' else 'not_matched' end", createVarcharType(11), "not_matched"); - assertExecute("case when ARRAY[CAST(null AS BIGINT)] = ARRAY[CAST(1 AS BIGINT)] then 'matched' else 'not_matched' end", createVarcharType(11), "not_matched"); + assertThat(assertions.expression("CASE WHEN a = b THEN 'matched' ELSE 'not_matched' END") + .binding("a", "ARRAY[CAST(1 AS BIGINT)]") + .binding("b", "ARRAY[CAST(1 AS BIGINT)]")) + .hasType(createVarcharType(11)) + .isEqualTo("matched"); + + assertThat(assertions.expression("CASE WHEN a = b THEN 'matched' ELSE 'not_matched' END") + .binding("a", "ARRAY[CAST(2 AS BIGINT)]") + .binding("b", "ARRAY[CAST(1 AS BIGINT)]")) + .hasType(createVarcharType(11)) + .isEqualTo("not_matched"); - Futures.allAsList(futures).get(); + assertThat(assertions.expression("CASE WHEN a = b THEN 'matched' ELSE 'not_matched' END") + .binding("a", "ARRAY[CAST(null AS BIGINT)]") + .binding("b", "ARRAY[CAST(1 AS BIGINT)]")) + .hasType(createVarcharType(11)) + .isEqualTo("not_matched"); } @Test public void testSearchCaseMultiple() - throws Exception { for (Double value : doubleLefts) { for (Integer firstTest : intLefts) { @@ -1131,10 +1872,13 @@ else if (secondTest != null && (double) value == secondTest) { else { expected = null; } - List expressions = formatExpression("case when %s = %s then 'first' when %s = %s then 'second' end", - Arrays.asList(value, firstTest, value, secondTest), - ImmutableList.of("double", "bigint", "double", "double")); - assertExecute(expressions, createVarcharType(6), expected); + + assertThat(assertions.expression("case when v = c1 then 'first' when v = c2 then 'second' end") + .binding("v", toLiteral(value)) + .binding("c1", toLiteral(firstTest)) + .binding("c2", toLiteral(secondTest))) + .hasType(createVarcharType(6)) + .isEqualTo(expected); } } } @@ -1155,879 +1899,957 @@ else if (secondTest != null && value.doubleValue() == secondTest) { else { expected = null; } - List expressions = formatExpression("case when %s = %s then 'first' when %s = %s then 'second' end", - Arrays.asList(value, firstTest, value, secondTest), - ImmutableList.of("decimal(14,4)", "bigint", "decimal(14,4)", "double")); - assertExecute(expressions, createVarcharType(6), expected); + + assertThat(assertions.expression("case when v = c1 then 'first' when v = c2 then 'second' end") + .binding("v", toLiteral(value)) + .binding("c1", toLiteral(firstTest)) + .binding("c2", toLiteral(secondTest))) + .hasType(createVarcharType(6)) + .isEqualTo(expected); } } } - - Futures.allAsList(futures).get(); } @Test public void testIn() - throws Exception { for (Boolean value : booleanValues) { - assertExecute(generateExpression("%s in (true)", value), BOOLEAN, value == null ? null : value == Boolean.TRUE); - assertExecute(generateExpression("%s in (null, true)", value), BOOLEAN, value == null ? null : value == Boolean.TRUE ? true : null); - assertExecute(generateExpression("%s in (true, null)", value), BOOLEAN, value == null ? null : value == Boolean.TRUE ? true : null); - assertExecute(generateExpression("%s in (false)", value), BOOLEAN, value == null ? null : value == Boolean.FALSE); - assertExecute(generateExpression("%s in (null, false)", value), BOOLEAN, value == null ? null : value == Boolean.FALSE ? true : null); - assertExecute(generateExpression("%s in (null)", value), BOOLEAN, null); + assertThat(assertions.expression("a IN (true)") + .binding("a", toLiteral(value))) + .isEqualTo(value == null ? null : value == Boolean.TRUE); + assertThat(assertions.expression("a IN (null, true)") + .binding("a", toLiteral(value))) + .isEqualTo(value == null ? null : value == Boolean.TRUE ? true : null); + assertThat(assertions.expression("a IN (true, null)") + .binding("a", toLiteral(value))) + .isEqualTo(value == null ? null : value == Boolean.TRUE ? true : null); + assertThat(assertions.expression("a IN (false)") + .binding("a", toLiteral(value))) + .isEqualTo(value == null ? null : value == Boolean.FALSE); + assertThat(assertions.expression("a IN (null, false)") + .binding("a", toLiteral(value))) + .isEqualTo(value == null ? null : value == Boolean.FALSE ? true : null); + assertThat(assertions.expression("a IN (null)") + .binding("a", toLiteral(value))) + .isNull(BOOLEAN); } for (Integer value : intLefts) { List testValues = Arrays.asList(33, 9, -9, -33); - assertExecute(generateExpression("%s in (33, 9, -9, -33)", value), - BOOLEAN, - value == null ? null : testValues.contains(value)); - assertExecute(generateExpression("%s in (null, 33, 9, -9, -33)", value), - BOOLEAN, - value == null ? null : testValues.contains(value) ? true : null); - assertExecute(generateExpression("%s in (CAST(null AS BIGINT), 33, 9, -9, -33)", value), - BOOLEAN, - value == null ? null : testValues.contains(value) ? true : null); - assertExecute(generateExpression("%s in (33, null, 9, -9, -33)", value), - BOOLEAN, - value == null ? null : testValues.contains(value) ? true : null); - assertExecute(generateExpression("%s in (33, CAST(null AS BIGINT), 9, -9, -33)", value), - BOOLEAN, - value == null ? null : testValues.contains(value) ? true : null); + + assertThat(assertions.expression("a IN (33, 9, -9, -33)") + .binding("a", toLiteral(value))) + .isEqualTo(value == null ? null : testValues.contains(value)); + + assertThat(assertions.expression("a IN (null, 33, 9, -9, -33)") + .binding("a", toLiteral(value))) + .isEqualTo(value == null ? null : testValues.contains(value) ? true : null); + + assertThat(assertions.expression("a IN (CAST(null AS BIGINT), 33, 9, -9, -33)") + .binding("a", toLiteral(value))) + .isEqualTo(value == null ? null : testValues.contains(value) ? true : null); + + assertThat(assertions.expression("a IN (33, null, 9, -9, -33)") + .binding("a", toLiteral(value))) + .isEqualTo(value == null ? null : testValues.contains(value) ? true : null); + + assertThat(assertions.expression("a IN (33, CAST(null AS BIGINT), 9, -9, -33)") + .binding("a", toLiteral(value))) + .isEqualTo(value == null ? null : testValues.contains(value) ? true : null); // compare a long to in containing doubles - assertExecute(generateExpression("%s in (33, 9.0E0, -9, -33)", value), - BOOLEAN, - value == null ? null : testValues.contains(value)); - assertExecute(generateExpression("%s in (null, 33, 9.0E0, -9, -33)", value), - BOOLEAN, - value == null ? null : testValues.contains(value) ? true : null); - assertExecute(generateExpression("%s in (33.0E0, null, 9.0E0, -9, -33)", value), - BOOLEAN, - value == null ? null : testValues.contains(value) ? true : null); + assertThat(assertions.expression("a IN (33, 9.0E0, -9, -33)") + .binding("a", toLiteral(value))) + .isEqualTo(value == null ? null : testValues.contains(value)); + + assertThat(assertions.expression("a IN (null, 33, 9.0E0, -9, -33)") + .binding("a", toLiteral(value))) + .isEqualTo(value == null ? null : testValues.contains(value) ? true : null); + + assertThat(assertions.expression("a IN (33.0E0, null, 9.0E0, -9, -33)") + .binding("a", toLiteral(value))) + .isEqualTo(value == null ? null : testValues.contains(value) ? true : null); } for (Double value : doubleLefts) { List testValues = Arrays.asList(33.0, 9.0, -9.0, -33.0); - assertExecute(generateExpression("%s in (33.0E0, 9.0E0, -9.0E0, -33.0E0)", value), - BOOLEAN, - value == null ? null : testValues.contains(value)); - assertExecute(generateExpression("%s in (null, 33.0E0, 9.0E0, -9.0E0, -33.0E0)", value), - BOOLEAN, - value == null ? null : testValues.contains(value) ? true : null); - assertExecute(generateExpression("%s in (33.0E0, null, 9.0E0, -9.0E0, -33.0E0)", value), - BOOLEAN, - value == null ? null : testValues.contains(value) ? true : null); + + assertThat(assertions.expression("a IN (33.0E0, 9.0E0, -9.0E0, -33.0E0)") + .binding("a", toLiteral(value))) + .isEqualTo(value == null ? null : testValues.contains(value)); + + assertThat(assertions.expression("a IN (null, 33.0E0, 9.0E0, -9.0E0, -33.0E0)") + .binding("a", toLiteral(value))) + .isEqualTo(value == null ? null : testValues.contains(value) ? true : null); + + assertThat(assertions.expression("a IN (33.0E0, null, 9.0E0, -9.0E0, -33.0E0)") + .binding("a", toLiteral(value))) + .isEqualTo(value == null ? null : testValues.contains(value) ? true : null); // compare a double to in containing longs - assertExecute(generateExpression("%s in (33.0E0, 9, -9, -33.0E0)", value), - BOOLEAN, - value == null ? null : testValues.contains(value)); - assertExecute(generateExpression("%s in (null, 33.0E0, 9, -9, -33.0E0)", value), - BOOLEAN, - value == null ? null : testValues.contains(value) ? true : null); - assertExecute(generateExpression("%s in (33.0E0, null, 9, -9, -33.0E0)", value), - BOOLEAN, - value == null ? null : testValues.contains(value) ? true : null); + assertThat(assertions.expression("a IN (33.0E0, 9, -9, -33.0E0)") + .binding("a", toLiteral(value))) + .isEqualTo(value == null ? null : testValues.contains(value)); + + assertThat(assertions.expression("a IN (null, 33.0E0, 9, -9, -33.0E0)") + .binding("a", toLiteral(value))) + .isEqualTo(value == null ? null : testValues.contains(value) ? true : null); + + assertThat(assertions.expression("a IN (33.0E0, null, 9, -9, -33.0E0)") + .binding("a", toLiteral(value))) + .isEqualTo(value == null ? null : testValues.contains(value) ? true : null); // compare to dynamically computed values - testValues = Arrays.asList(33.0, cos(9.0), cos(-9.0), -33.0); - assertExecute(generateExpression("cos(%s) in (33.0E0, cos(9.0E0), cos(-9.0E0), -33.0E0)", value), - BOOLEAN, - value == null ? null : testValues.contains(cos(value))); - assertExecute(generateExpression("cos(%s) in (null, 33.0E0, cos(9.0E0), cos(-9.0E0), -33.0E0)", value), - BOOLEAN, - value == null ? null : testValues.contains(cos(value)) ? true : null); + List cosines = Arrays.asList(33.0, cos(9.0), cos(-9.0), -33.0); + assertThat(assertions.expression("cos(a) IN (33.0E0, cos(9.0E0), cos(-9.0E0), -33.0E0)") + .binding("a", toLiteral(value))) + .isEqualTo(value == null ? null : cosines.contains(cos(value))); + + assertThat(assertions.expression("cos(a) IN (null, 33.0E0, cos(9.0E0), cos(-9.0E0), -33.0E0)") + .binding("a", toLiteral(value))) + .isEqualTo(value == null ? null : cosines.contains(cos(value)) ? true : null); } for (BigDecimal value : decimalLefts) { List testValues = ImmutableList.of(new BigDecimal("9.0"), new BigDecimal("10.0"), new BigDecimal("-11.0"), new BigDecimal("9223372036.5477")); - assertExecute(generateExpression("%s in (9.0, 10.0, -11.0, 9223372036.5477)", value), - BOOLEAN, - value == null ? null : testValues.contains(value)); - assertExecute(generateExpression("%s in (null, 9.0, 10.0, -11.0, 9223372036.5477)", value), - BOOLEAN, - value == null ? null : testValues.contains(value) ? true : null); - assertExecute(generateExpression("%s in (CAST(null AS DECIMAL(1,0)), 9.0, 10.0, -11.0, 9223372036.5477)", value), - BOOLEAN, - value == null ? null : testValues.contains(value) ? true : null); - assertExecute(generateExpression("%s in (9.0, CAST(null AS DECIMAL(1,0)), 10.0, -11.0, 9223372036.5477)", value), - BOOLEAN, - value == null ? null : testValues.contains(value) ? true : null); - assertExecute(generateExpression("%s in (9.0, null, 10.0, -11.0, 9223372036.5477)", value), - BOOLEAN, - value == null ? null : testValues.contains(value) ? true : null); + + assertThat(assertions.expression("a IN (9.0, 10.0, -11.0, 9223372036.5477)") + .binding("a", toLiteral(value))) + .isEqualTo(value == null ? null : testValues.contains(value)); + + assertThat(assertions.expression("a IN (null, 9.0, 10.0, -11.0, 9223372036.5477)") + .binding("a", toLiteral(value))) + .isEqualTo(value == null ? null : testValues.contains(value) ? true : null); + + assertThat(assertions.expression("a IN (CAST(null AS DECIMAL(1,0)), 9.0, 10.0, -11.0, 9223372036.5477)") + .binding("a", toLiteral(value))) + .isEqualTo(value == null ? null : testValues.contains(value) ? true : null); + + assertThat(assertions.expression("a IN (9.0, CAST(null AS DECIMAL(1,0)), 10.0, -11.0, 9223372036.5477)") + .binding("a", toLiteral(value))) + .isEqualTo(value == null ? null : testValues.contains(value) ? true : null); + + assertThat(assertions.expression("a IN (9.0, null, 10.0, -11.0, 9223372036.5477)") + .binding("a", toLiteral(value))) + .isEqualTo(value == null ? null : testValues.contains(value) ? true : null); // compare a long to in containing doubles - assertExecute(generateExpression("%s in (9.0, 10.0, CAST(-11.0 as DOUBLE), 9223372036.5477)", value), - BOOLEAN, - value == null ? null : testValues.contains(value)); - assertExecute(generateExpression("%s in (null, 9.0, 10.0, CAST(-11.0 as DOUBLE), 9223372036.5477)", value), - BOOLEAN, - value == null ? null : testValues.contains(value) ? true : null); - assertExecute(generateExpression("%s in (null, 9, 10.0, CAST(-11.0 as DOUBLE), 9223372036.5477)", value), - BOOLEAN, - value == null ? null : testValues.contains(value) ? true : null); - assertExecute(generateExpression("%s in (CAST(9.0 as DOUBLE), null, 10.0, CAST(-11.0 as DOUBLE), 9223372036.5477)", value), - BOOLEAN, - value == null ? null : testValues.contains(value) ? true : null); + assertThat(assertions.expression("a IN (9.0, 10.0, CAST(-11.0 as DOUBLE), 9223372036.5477)") + .binding("a", toLiteral(value))) + .isEqualTo(value == null ? null : testValues.contains(value)); + + assertThat(assertions.expression("a IN (null, 9.0, 10.0, CAST(-11.0 as DOUBLE), 9223372036.5477)") + .binding("a", toLiteral(value))) + .isEqualTo(value == null ? null : testValues.contains(value) ? true : null); + + assertThat(assertions.expression("a IN (null, 9, 10.0, CAST(-11.0 as DOUBLE), 9223372036.5477)") + .binding("a", toLiteral(value))) + .isEqualTo(value == null ? null : testValues.contains(value) ? true : null); + + assertThat(assertions.expression("a IN (CAST(9.0 as DOUBLE), null, 10.0, CAST(-11.0 as DOUBLE), 9223372036.5477)") + .binding("a", toLiteral(value))) + .isEqualTo(value == null ? null : testValues.contains(value) ? true : null); } for (String value : stringLefts) { List testValues = Arrays.asList("what?", "foo", "mellow", "end"); - assertExecute(generateExpression("%s in ('what?', 'foo', 'mellow', 'end')", value), - BOOLEAN, - value == null ? null : testValues.contains(value)); - assertExecute(generateExpression("%s in (null, 'what?', 'foo', 'mellow', 'end')", value), - BOOLEAN, - value == null ? null : testValues.contains(value) ? true : null); - assertExecute(generateExpression("%s in ('what?', null, 'foo', 'mellow', 'end')", value), - BOOLEAN, - value == null ? null : testValues.contains(value) ? true : null); + + assertThat(assertions.expression("a IN ('what?', 'foo', 'mellow', 'end')") + .binding("a", toLiteral(value))) + .isEqualTo(value == null ? null : testValues.contains(value)); + + assertThat(assertions.expression("a IN (null, 'what?', 'foo', 'mellow', 'end')") + .binding("a", toLiteral(value))) + .isEqualTo(value == null ? null : testValues.contains(value) ? true : null); + + assertThat(assertions.expression("a IN ('what?', null, 'foo', 'mellow', 'end')") + .binding("a", toLiteral(value))) + .isEqualTo(value == null ? null : testValues.contains(value) ? true : null); } // Test null-handling in default case of InCodeGenerator - assertExecute("1 in (100, 101, if(rand()>=0, 1), if(rand()<0, 1))", BOOLEAN, true); - assertExecute("1 in (100, 101, if(rand()<0, 1), if(rand()>=0, 1))", BOOLEAN, true); - assertExecute("2 in (100, 101, if(rand()>=0, 1), if(rand()<0, 1))", BOOLEAN, null); - assertExecute("2 in (100, 101, if(rand()<0, 1), if(rand()>=0, 1))", BOOLEAN, null); + assertThat(assertions.expression("a IN (100, 101, if(rand()>=0, 1), if(rand()<0, 1))") + .binding("a", "1")) + .hasType(BOOLEAN) + .isEqualTo(true); + + assertThat(assertions.expression("a IN (100, 101, if(rand()<0, 1), if(rand()>=0, 1))") + .binding("a", "1")) + .hasType(BOOLEAN) + .isEqualTo(true); - Futures.allAsList(futures).get(); + assertThat(assertions.expression("a IN (100, 101, if(rand()>=0, 1), if(rand()<0, 1))") + .binding("a", "2")) + .isNull(BOOLEAN); + + assertThat(assertions.expression("a IN (100, 101, if(rand()<0, 1), if(rand()>=0, 1))") + .binding("a", "2")) + .isNull(BOOLEAN); } @Test public void testHugeIn() - throws Exception { String intValues = range(2000, 7000) .mapToObj(Integer::toString) .collect(joining(", ")); - assertExecute("bound_integer in (1234, " + intValues + ")", BOOLEAN, true); - assertExecute("bound_integer in (" + intValues + ")", BOOLEAN, false); + + assertThat(assertions.expression("a IN (1234, " + intValues + ")") + .binding("a", "1234")) + .hasType(BOOLEAN) + .isEqualTo(true); + + assertThat(assertions.expression("a IN (" + intValues + ")") + .binding("a", "1234")) + .hasType(BOOLEAN) + .isEqualTo(false); String longValues = LongStream.range(Integer.MAX_VALUE + 1L, Integer.MAX_VALUE + 5000L) .mapToObj(Long::toString) .collect(joining(", ")); - assertExecute("bound_long in (1234, " + longValues + ")", BOOLEAN, true); - assertExecute("bound_long in (" + longValues + ")", BOOLEAN, false); + + assertThat(assertions.expression("a IN (1234, " + longValues + ")") + .binding("a", "BIGINT '1234'")) + .hasType(BOOLEAN) + .isEqualTo(true); + + assertThat(assertions.expression("a IN (" + longValues + ")") + .binding("a", "BIGINT '1234'")) + .hasType(BOOLEAN) + .isEqualTo(false); String doubleValues = range(2000, 7000).asDoubleStream() - .mapToObj(this::formatDoubleToScientificNotation) + .mapToObj(TestExpressionCompiler::formatDoubleToScientificNotation) .collect(joining(", ")); - assertExecute("bound_double in (12.34E0, " + doubleValues + ")", BOOLEAN, true); - assertExecute("bound_double in (" + doubleValues + ")", BOOLEAN, false); + + assertThat(assertions.expression("a IN (12.34E0, " + doubleValues + ")") + .binding("a", "12.34E0")) + .hasType(BOOLEAN) + .isEqualTo(true); + + assertThat(assertions.expression("a IN (" + doubleValues + ")") + .binding("a", "12.34E0")) + .hasType(BOOLEAN) + .isEqualTo(false); String stringValues = range(2000, 7000) .mapToObj(i -> format("'%s'", i)) .collect(joining(", ")); - assertExecute("bound_string in ('hello', " + stringValues + ")", BOOLEAN, true); - assertExecute("bound_string in (" + stringValues + ")", BOOLEAN, false); + + assertThat(assertions.expression("a IN ('hello', " + stringValues + ")") + .binding("a", "'hello'")) + .hasType(BOOLEAN) + .isEqualTo(true); + + assertThat(assertions.expression("a IN (" + stringValues + ")") + .binding("a", "'hello'")) + .hasType(BOOLEAN) + .isEqualTo(false); String timestampValues = range(0, 2_000) .mapToObj(i -> format("TIMESTAMP '1970-01-01 01:01:0%s.%s+01:00'", i / 1000, i % 1000)) .collect(joining(", ")); - assertExecute("bound_timestamp_with_timezone in (" + timestampValues + ")", BOOLEAN, true); - assertExecute("bound_timestamp_with_timezone in (TIMESTAMP '1970-01-01 01:01:00.0+02:00')", BOOLEAN, false); + + assertThat(assertions.expression("a IN (" + timestampValues + ")") + .binding("a", "TIMESTAMP '1970-01-01 00:01:00.999 UTC'")) + .hasType(BOOLEAN) + .isEqualTo(true); + + assertThat(assertions.expression("a IN (TIMESTAMP '1970-01-01 01:01:00.0+02:00')") + .binding("a", "TIMESTAMP '1970-01-01 00:01:00.999'")) + .hasType(BOOLEAN) + .isEqualTo(false); String shortDecimalValues = range(2000, 7000) .mapToObj(value -> format("decimal '%s'", value)) .collect(joining(", ")); - assertExecute("bound_short_decimal in (1234, " + shortDecimalValues + ")", BOOLEAN, true); - assertExecute("bound_short_decimal in (" + shortDecimalValues + ")", BOOLEAN, false); + + assertThat(assertions.expression("a IN (1234, " + shortDecimalValues + ")") + .binding("a", "CAST(1234 AS decimal(14))")) + .hasType(BOOLEAN) + .isEqualTo(true); + + assertThat(assertions.expression("a IN (" + shortDecimalValues + ")") + .binding("a", "CAST(1234 AS decimal(14))")) + .hasType(BOOLEAN) + .isEqualTo(false); String longDecimalValues = range(2000, 7000) .mapToObj(value -> format("decimal '123456789012345678901234567890%s'", value)) .collect(joining(", ")); - assertExecute("bound_long_decimal in (1234, " + longDecimalValues + ")", BOOLEAN, true); - assertExecute("bound_long_decimal in (" + longDecimalValues + ")", BOOLEAN, false); - Futures.allAsList(futures).get(); + assertThat(assertions.expression("a IN (1234, " + longDecimalValues + ")") + .binding("a", "CAST(1234 AS decimal(28))")) + .hasType(BOOLEAN) + .isEqualTo(true); + + assertThat(assertions.expression("a IN (" + longDecimalValues + ")") + .binding("a", "CAST(1234 AS decimal(28))")) + .hasType(BOOLEAN) + .isEqualTo(false); } @Test public void testInComplexTypes() { - assertExecute("ARRAY[1] IN (ARRAY[1])", BOOLEAN, true); - assertExecute("ARRAY[1] IN (ARRAY[2])", BOOLEAN, false); - assertExecute("ARRAY[1] IN (ARRAY[2], ARRAY[1])", BOOLEAN, true); - assertExecute("ARRAY[1] IN (null)", BOOLEAN, null); - assertExecute("ARRAY[1] IN (null, ARRAY[1])", BOOLEAN, true); - assertExecute("ARRAY[1, 2, null] IN (ARRAY[2, null], ARRAY[1, null])", BOOLEAN, false); - assertExecute("ARRAY[1, null] IN (ARRAY[2, null], null)", BOOLEAN, null); - assertExecute("ARRAY[null] IN (ARRAY[null])", BOOLEAN, null); - assertExecute("ARRAY[1] IN (ARRAY[null])", BOOLEAN, null); - assertExecute("ARRAY[null] IN (ARRAY[1])", BOOLEAN, null); - assertExecute("ARRAY[1, null] IN (ARRAY[1, null])", BOOLEAN, null); - assertExecute("ARRAY[1, null] IN (ARRAY[2, null])", BOOLEAN, false); - assertExecute("ARRAY[1, null] IN (ARRAY[1, null], ARRAY[2, null])", BOOLEAN, null); - assertExecute("ARRAY[1, null] IN (ARRAY[1, null], ARRAY[2, null], ARRAY[1, null])", BOOLEAN, null); - - assertExecute("ROW(1) IN (ROW(1))", BOOLEAN, true); - assertExecute("ROW(1) IN (ROW(2))", BOOLEAN, false); - assertExecute("ROW(1) IN (ROW(2), ROW(1), ROW(2))", BOOLEAN, true); - assertExecute("ROW(1) IN (null)", BOOLEAN, null); - assertExecute("ROW(1) IN (null, ROW(1))", BOOLEAN, true); - assertExecute("ROW(1, null) IN (ROW(2, null), null)", BOOLEAN, null); - assertExecute("ROW(null) IN (ROW(null))", BOOLEAN, null); - assertExecute("ROW(1) IN (ROW(null))", BOOLEAN, null); - assertExecute("ROW(null) IN (ROW(1))", BOOLEAN, null); - assertExecute("ROW(1, null) IN (ROW(1, null))", BOOLEAN, null); - assertExecute("ROW(1, null) IN (ROW(2, null))", BOOLEAN, false); - assertExecute("ROW(1, null) IN (ROW(1, null), ROW(2, null))", BOOLEAN, null); - assertExecute("ROW(1, null) IN (ROW(1, null), ROW(2, null), ROW(1, null))", BOOLEAN, null); - - assertExecute("MAP(ARRAY[1], ARRAY[1]) IN (MAP(ARRAY[1], ARRAY[1]))", BOOLEAN, true); - assertExecute("MAP(ARRAY[1], ARRAY[1]) IN (null)", BOOLEAN, null); - assertExecute("MAP(ARRAY[1], ARRAY[1]) IN (null, MAP(ARRAY[1], ARRAY[1]))", BOOLEAN, true); - assertExecute("MAP(ARRAY[1], ARRAY[1]) IN (MAP(ARRAY[1, 2], ARRAY[1, null]))", BOOLEAN, false); - assertExecute("MAP(ARRAY[1, 2], ARRAY[1, null]) IN (MAP(ARRAY[1, 2], ARRAY[2, null]), null)", BOOLEAN, null); - assertExecute("MAP(ARRAY[1, 2], ARRAY[1, null]) IN (MAP(ARRAY[1, 2], ARRAY[1, null]))", BOOLEAN, null); - assertExecute("MAP(ARRAY[1, 2], ARRAY[1, null]) IN (MAP(ARRAY[1, 3], ARRAY[1, null]))", BOOLEAN, false); - assertExecute("MAP(ARRAY[1], ARRAY[null]) IN (MAP(ARRAY[1], ARRAY[null]))", BOOLEAN, null); - assertExecute("MAP(ARRAY[1], ARRAY[1]) IN (MAP(ARRAY[1], ARRAY[null]))", BOOLEAN, null); - assertExecute("MAP(ARRAY[1], ARRAY[null]) IN (MAP(ARRAY[1], ARRAY[1]))", BOOLEAN, null); - assertExecute("MAP(ARRAY[1, 2], ARRAY[1, null]) IN (MAP(ARRAY[1, 2], ARRAY[1, null]))", BOOLEAN, null); - assertExecute("MAP(ARRAY[1, 2], ARRAY[1, null]) IN (MAP(ARRAY[1, 3], ARRAY[1, null]))", BOOLEAN, false); - assertExecute("MAP(ARRAY[1, 2], ARRAY[1, null]) IN (MAP(ARRAY[1, 2], ARRAY[2, null]))", BOOLEAN, false); - assertExecute("MAP(ARRAY[1, 2], ARRAY[1, null]) IN (MAP(ARRAY[1, 2], ARRAY[1, null]), MAP(ARRAY[1, 2], ARRAY[2, null]))", BOOLEAN, null); - assertExecute("MAP(ARRAY[1, 2], ARRAY[1, null]) IN (MAP(ARRAY[1, 2], ARRAY[1, null]), MAP(ARRAY[1, 2], ARRAY[2, null]), MAP(ARRAY[1, 2], ARRAY[1, null]))", BOOLEAN, null); + assertThat(assertions.expression("a IN (ARRAY[1])") + .binding("a", "ARRAY[1]")) + .hasType(BOOLEAN) + .isEqualTo(true); + + assertThat(assertions.expression("a IN (ARRAY[2])") + .binding("a", "ARRAY[1]")) + .hasType(BOOLEAN) + .isEqualTo(false); + + assertThat(assertions.expression("a IN (ARRAY[2], ARRAY[1])") + .binding("a", "ARRAY[1]")) + .hasType(BOOLEAN) + .isEqualTo(true); + + assertThat(assertions.expression("a IN (null)") + .binding("a", "ARRAY[1]")) + .isNull(BOOLEAN); + + assertThat(assertions.expression("a IN (null, ARRAY[1])") + .binding("a", "ARRAY[1]")) + .hasType(BOOLEAN) + .isEqualTo(true); + + assertThat(assertions.expression("a IN (ARRAY[2, null], ARRAY[1, null])") + .binding("a", "ARRAY[1, 2, null]")) + .hasType(BOOLEAN) + .isEqualTo(false); + + assertThat(assertions.expression("a IN (ARRAY[2, null], null)") + .binding("a", "ARRAY[1, null]")) + .isNull(BOOLEAN); + + assertThat(assertions.expression("a IN (ARRAY[null])") + .binding("a", "ARRAY[null]")) + .isNull(BOOLEAN); + + assertThat(assertions.expression("a IN (ARRAY[null])") + .binding("a", "ARRAY[1]")) + .isNull(BOOLEAN); + + assertThat(assertions.expression("a IN (ARRAY[1])") + .binding("a", "ARRAY[null]")) + .isNull(BOOLEAN); + + assertThat(assertions.expression("a IN (ARRAY[1, null])") + .binding("a", "ARRAY[1, null]")) + .isNull(BOOLEAN); + + assertThat(assertions.expression("a IN (ARRAY[2, null])") + .binding("a", "ARRAY[1, null]")) + .hasType(BOOLEAN) + .isEqualTo(false); + + assertThat(assertions.expression("a IN (ARRAY[1, null], ARRAY[2, null])") + .binding("a", "ARRAY[1, null]")) + .isNull(BOOLEAN); + + assertThat(assertions.expression("a IN (ARRAY[1, null], ARRAY[2, null], ARRAY[1, null])") + .binding("a", "ARRAY[1, null]")) + .isNull(BOOLEAN); + + assertThat(assertions.expression("a IN (ROW(1))") + .binding("a", "ROW(1)")) + .hasType(BOOLEAN) + .isEqualTo(true); + + assertThat(assertions.expression("a IN (ROW(2))") + .binding("a", "ROW(1)")) + .hasType(BOOLEAN) + .isEqualTo(false); + + assertThat(assertions.expression("a IN (ROW(2), ROW(1), ROW(2))") + .binding("a", "ROW(1)")) + .hasType(BOOLEAN) + .isEqualTo(true); + + assertThat(assertions.expression("a IN (null)") + .binding("a", "ROW(1)")) + .isNull(BOOLEAN); + + assertThat(assertions.expression("a IN (null, ROW(1))") + .binding("a", "ROW(1)")) + .hasType(BOOLEAN) + .isEqualTo(true); + + assertThat(assertions.expression("a IN (ROW(2, null), null)") + .binding("a", "ROW(1, null)")) + .isNull(BOOLEAN); + + assertThat(assertions.expression("a IN (ROW(null))") + .binding("a", "ROW(null)")) + .isNull(BOOLEAN); + + assertThat(assertions.expression("a IN (ROW(null))") + .binding("a", "ROW(1)")) + .isNull(BOOLEAN); + + assertThat(assertions.expression("a IN (ROW(1))") + .binding("a", "ROW(null)")) + .isNull(BOOLEAN); + + assertThat(assertions.expression("a IN (ROW(1, null))") + .binding("a", "ROW(1, null)")) + .isNull(BOOLEAN); + + assertThat(assertions.expression("a IN (ROW(2, null))") + .binding("a", "ROW(1, null)")) + .hasType(BOOLEAN) + .isEqualTo(false); + + assertThat(assertions.expression("a IN (ROW(1, null), ROW(2, null))") + .binding("a", "ROW(1, null)")) + .isNull(BOOLEAN); + + assertThat(assertions.expression("a IN (ROW(1, null), ROW(2, null), ROW(1, null))") + .binding("a", "ROW(1, null)")) + .isNull(BOOLEAN); + + assertThat(assertions.expression("a IN (MAP(ARRAY[1], ARRAY[1]))") + .binding("a", "MAP(ARRAY[1], ARRAY[1])")) + .hasType(BOOLEAN) + .isEqualTo(true); + + assertThat(assertions.expression("a IN (null)") + .binding("a", "MAP(ARRAY[1], ARRAY[1])")) + .isNull(BOOLEAN); + + assertThat(assertions.expression("a IN (null, MAP(ARRAY[1], ARRAY[1]))") + .binding("a", "MAP(ARRAY[1], ARRAY[1])")) + .hasType(BOOLEAN) + .isEqualTo(true); + + assertThat(assertions.expression("a IN (MAP(ARRAY[1, 2], ARRAY[1, null]))") + .binding("a", "MAP(ARRAY[1], ARRAY[1])")) + .hasType(BOOLEAN) + .isEqualTo(false); + + assertThat(assertions.expression("a IN (MAP(ARRAY[1, 2], ARRAY[2, null]), null)") + .binding("a", "MAP(ARRAY[1, 2], ARRAY[1, null])")) + .isNull(BOOLEAN); + + assertThat(assertions.expression("a IN (MAP(ARRAY[1, 2], ARRAY[1, null]))") + .binding("a", "MAP(ARRAY[1, 2], ARRAY[1, null])")) + .isNull(BOOLEAN); + + assertThat(assertions.expression("a IN (MAP(ARRAY[1, 3], ARRAY[1, null]))") + .binding("a", "MAP(ARRAY[1, 2], ARRAY[1, null])")) + .hasType(BOOLEAN) + .isEqualTo(false); + + assertThat(assertions.expression("a IN (MAP(ARRAY[1], ARRAY[null]))") + .binding("a", "MAP(ARRAY[1], ARRAY[null])")) + .isNull(BOOLEAN); + + assertThat(assertions.expression("a IN (MAP(ARRAY[1], ARRAY[null]))") + .binding("a", "MAP(ARRAY[1], ARRAY[1])")) + .isNull(BOOLEAN); + + assertThat(assertions.expression("a IN (MAP(ARRAY[1], ARRAY[1]))") + .binding("a", "MAP(ARRAY[1], ARRAY[null])")) + .isNull(BOOLEAN); + + assertThat(assertions.expression("a IN (MAP(ARRAY[1, 2], ARRAY[1, null]))") + .binding("a", "MAP(ARRAY[1, 2], ARRAY[1, null])")) + .isNull(BOOLEAN); + + assertThat(assertions.expression("a IN (MAP(ARRAY[1, 3], ARRAY[1, null]))") + .binding("a", "MAP(ARRAY[1, 2], ARRAY[1, null])")) + .hasType(BOOLEAN) + .isEqualTo(false); + + assertThat(assertions.expression("a IN (MAP(ARRAY[1, 2], ARRAY[2, null]))") + .binding("a", "MAP(ARRAY[1, 2], ARRAY[1, null])")) + .hasType(BOOLEAN) + .isEqualTo(false); + + assertThat(assertions.expression("a IN (MAP(ARRAY[1, 2], ARRAY[1, null]), MAP(ARRAY[1, 2], ARRAY[2, null]))") + .binding("a", "MAP(ARRAY[1, 2], ARRAY[1, null])")) + .isNull(BOOLEAN); + + assertThat(assertions.expression("a IN (MAP(ARRAY[1, 2], ARRAY[1, null]), MAP(ARRAY[1, 2], ARRAY[2, null]), MAP(ARRAY[1, 2], ARRAY[1, null]))") + .binding("a", "MAP(ARRAY[1, 2], ARRAY[1, null])")) + .isNull(BOOLEAN); } @Test public void testFunctionCall() - throws Exception { for (Integer left : intLefts) { for (Integer right : intRights) { - assertExecute(generateExpression("bitwise_and(%s, %s)", left, right), BIGINT, left == null || right == null ? null : BitwiseFunctions.bitwiseAnd(left, right)); + assertThat(assertions.function("bitwise_and", toLiteral(left), toLiteral(right))) + .hasType(BIGINT) + .isEqualTo(left == null || right == null ? null : BitwiseFunctions.bitwiseAnd(left, right)); } } for (Integer left : intLefts) { for (Double right : doubleRights) { - assertExecute(generateExpression("mod(%s, %s)", left, right), DOUBLE, left == null || right == null ? null : MathFunctions.mod(left, right)); + assertThat(assertions.function("mod", toLiteral(left), toLiteral(right))) + .hasType(DOUBLE) + .isEqualTo(left == null || right == null ? null : MathFunctions.mod(left, right)); } } for (Double left : doubleLefts) { for (Integer right : intRights) { - assertExecute(generateExpression("mod(%s, %s)", left, right), DOUBLE, left == null || right == null ? null : MathFunctions.mod(left, right)); + assertThat(assertions.function("mod", toLiteral(left), toLiteral(right))) + .hasType(DOUBLE) + .isEqualTo(left == null || right == null ? null : MathFunctions.mod(left, right)); } } for (Double left : doubleLefts) { for (Double right : doubleRights) { - assertExecute(generateExpression("mod(%s, %s)", left, right), DOUBLE, left == null || right == null ? null : MathFunctions.mod(left, right)); + assertThat(assertions.function("mod", toLiteral(left), toLiteral(right))) + .hasType(DOUBLE) + .isEqualTo(left == null || right == null ? null : MathFunctions.mod(left, right)); } } for (Double left : doubleLefts) { for (BigDecimal right : decimalRights) { - assertExecute(generateExpression("mod(%s, %s)", left, right), DOUBLE, left == null || right == null ? null : MathFunctions.mod(left, right.doubleValue())); + assertThat(assertions.function("mod", toLiteral(left), toLiteral(right))) + .hasType(DOUBLE) + .isEqualTo(left == null || right == null ? null : MathFunctions.mod(left, right.doubleValue())); } } for (BigDecimal left : decimalLefts) { for (Long right : longRights) { - assertExecute(generateExpression("power(%s, %s)", left, right), DOUBLE, left == null || right == null ? null : MathFunctions.power(left.doubleValue(), right)); + assertThat(assertions.function("power", toLiteral(left), toLiteral(right))) + .hasType(DOUBLE) + .isEqualTo(left == null || right == null ? null : MathFunctions.power(left.doubleValue(), right)); } } for (String value : stringLefts) { for (Integer start : intLefts) { for (Integer length : intRights) { - String expected; - if (value == null || start == null || length == null) { - expected = null; - } - else { - expected = StringFunctions.substring(utf8Slice(value), start, length).toStringUtf8(); - } - VarcharType expectedType = value != null ? createVarcharType(value.length()) : VARCHAR; - - assertExecute(generateExpression("substr(%s, %s, %s)", value, start, length), expectedType, expected); + assertThat(assertions.function("substr", toLiteral(value), toLiteral(start), toLiteral(length))) + .hasType(value != null ? createVarcharType(value.length()) : VARCHAR) + .isEqualTo(value == null || start == null || length == null ? null : toString(StringFunctions.substring(utf8Slice(value), start, length))); } } } - - Futures.allAsList(futures).get(); } @Test public void testFunctionCallRegexp() - throws Exception { for (String value : stringLefts) { for (String pattern : stringRights) { - assertExecute(generateExpression("regexp_like(%s, %s)", value, pattern), - BOOLEAN, - value == null || pattern == null ? null : JoniRegexpFunctions.regexpLike(utf8Slice(value), joniRegexp(utf8Slice(pattern)))); - assertExecute(generateExpression("regexp_replace(%s, %s)", value, pattern), - value == null ? VARCHAR : createVarcharType(value.length()), - value == null || pattern == null ? null : JoniRegexpFunctions.regexpReplace(utf8Slice(value), joniRegexp(utf8Slice(pattern)))); - assertExecute(generateExpression("regexp_extract(%s, %s)", value, pattern), - value == null ? VARCHAR : createVarcharType(value.length()), - value == null || pattern == null ? null : JoniRegexpFunctions.regexpExtract(utf8Slice(value), joniRegexp(utf8Slice(pattern)))); + assertThat(assertions.function("regexp_like", toLiteral(value), toLiteral(pattern))) + .hasType(BOOLEAN) + .isEqualTo(value == null || pattern == null ? null : JoniRegexpFunctions.regexpLike(utf8Slice(value), joniRegexp(utf8Slice(pattern)))); + + assertThat(assertions.function("regexp_replace", toLiteral(value), toLiteral(pattern))) + .hasType(value == null ? VARCHAR : createVarcharType(value.length())) + .isEqualTo(value == null || pattern == null ? null : JoniRegexpFunctions.regexpReplace(utf8Slice(value), joniRegexp(utf8Slice(pattern))).toStringUtf8()); + + assertThat(assertions.function("regexp_extract", toLiteral(value), toLiteral(pattern))) + .hasType(value == null ? VARCHAR : createVarcharType(value.length())) + .isEqualTo(value == null || pattern == null ? null : toString(JoniRegexpFunctions.regexpExtract(utf8Slice(value), joniRegexp(utf8Slice(pattern))))); } } - - Futures.allAsList(futures).get(); } @Test public void testFunctionCallJson() - throws Exception { + String[] jsonValues = { + "{}", + "{\"fuu\": {\"bar\": 1}}", + "{\"fuu\": null}", + "{\"fuu\": 1}", + "{\"fuu\": 1, \"bar\": \"abc\"}", + null + }; + + String[] jsonPatterns = { + "$", + "$.fuu", + "$.fuu[0]", + "$.bar", + null + }; + for (String value : jsonValues) { for (String pattern : jsonPatterns) { - assertExecute(generateExpression("json_extract(%s, %s)", value, pattern), - JSON, - value == null || pattern == null ? null : JsonFunctions.jsonExtract(utf8Slice(value), new JsonPath(pattern))); - assertExecute(generateExpression("json_extract_scalar(%s, %s)", value, pattern), - value == null ? createUnboundedVarcharType() : createVarcharType(value.length()), - value == null || pattern == null ? null : JsonFunctions.jsonExtractScalar(utf8Slice(value), new JsonPath(pattern))); - - assertExecute(generateExpression("json_extract(%s, %s || '')", value, pattern), - JSON, - value == null || pattern == null ? null : JsonFunctions.jsonExtract(utf8Slice(value), new JsonPath(pattern))); - assertExecute(generateExpression("json_extract_scalar(%s, %s || '')", value, pattern), - value == null ? createUnboundedVarcharType() : createVarcharType(value.length()), - value == null || pattern == null ? null : JsonFunctions.jsonExtractScalar(utf8Slice(value), new JsonPath(pattern))); + assertThat(assertions.function("json_extract", toLiteral(value), toLiteral(pattern))) + .hasType(JSON) + .isEqualTo(value == null || pattern == null ? null : toString(JsonFunctions.jsonExtract(utf8Slice(value), new JsonPath(pattern)))); + + assertThat(assertions.function("json_extract_scalar", toLiteral(value), toLiteral(pattern))) + .hasType(value == null ? createUnboundedVarcharType() : createVarcharType(value.length())) + .isEqualTo(value == null || pattern == null ? null : toString(JsonFunctions.jsonExtractScalar(utf8Slice(value), new JsonPath(pattern)))); } } - assertExecute("json_array_contains('[1, 2, 3]', 2)", BOOLEAN, true); - assertExecute("json_array_contains('[1, 2, 3]', BIGINT '2')", BOOLEAN, true); - assertExecute("json_array_contains('[2.5E0]', 2.5E0)", BOOLEAN, true); - assertExecute("json_array_contains('[false, true]', true)", BOOLEAN, true); - assertExecute("json_array_contains('[5]', 3)", BOOLEAN, false); - assertExecute("json_array_contains('[', 9)", BOOLEAN, null); - assertExecute("json_array_length('[')", BIGINT, null); + assertThat(assertions.function("json_array_contains", "'[1, 2, 3]'", "2")) + .hasType(BOOLEAN) + .isEqualTo(true); + + assertThat(assertions.function("json_array_contains", "'[1, 2, 3]'", "BIGINT '2'")) + .hasType(BOOLEAN) + .isEqualTo(true); + + assertThat(assertions.function("json_array_contains", "'[2.5E0]'", "2.5E0")) + .hasType(BOOLEAN) + .isEqualTo(true); - Futures.allAsList(futures).get(); + assertThat(assertions.function("json_array_contains", "'[false, true]'", "true")) + .hasType(BOOLEAN) + .isEqualTo(true); + + assertThat(assertions.function("json_array_contains", "'[5]'", "3")) + .hasType(BOOLEAN) + .isEqualTo(false); + + assertThat(assertions.function("json_array_contains", "'['", "9")) + .isNull(BOOLEAN); + + assertThat(assertions.function("json_array_length", "'['")) + .isNull(BIGINT); } @Test public void testFunctionWithSessionCall() - throws Exception { - assertExecute("now()", TIMESTAMP_TZ_MILLIS, SqlTimestampWithTimeZone.fromInstant(3, TEST_SESSION.getStart(), TEST_SESSION.getTimeZoneKey().getZoneId())); - assertExecute("current_timestamp", TIMESTAMP_TZ_MILLIS, SqlTimestampWithTimeZone.fromInstant(3, TEST_SESSION.getStart(), TEST_SESSION.getTimeZoneKey().getZoneId())); + Session session = assertions.getDefaultSession(); - Futures.allAsList(futures).get(); + assertThat(assertions.expression("now()")) + .hasType(TIMESTAMP_TZ_MILLIS) + .isEqualTo(SqlTimestampWithTimeZone.fromInstant(3, session.getStart(), session.getTimeZoneKey().getZoneId())); + + assertThat(assertions.expression("current_timestamp")) + .hasType(TIMESTAMP_TZ_MILLIS) + .isEqualTo(SqlTimestampWithTimeZone.fromInstant(3, session.getStart(), session.getTimeZoneKey().getZoneId())); } @Test public void testExtract() - throws Exception { + DateTime[] dateTimeValues = { + new DateTime(2001, 1, 22, 3, 4, 5, 321, UTC), + new DateTime(1960, 1, 22, 3, 4, 5, 321, UTC), + new DateTime(1970, 1, 1, 0, 0, 0, 0, UTC), + null + }; + for (DateTime left : dateTimeValues) { for (Field field : Field.values()) { if (field == TIMEZONE_MINUTE || field == TIMEZONE_HOUR) { continue; } - Long expected = null; - Long micros = null; + Long expected; + Long micros; if (left != null) { micros = left.getMillis() * MICROSECONDS_PER_MILLISECOND; expected = callExtractFunction(micros, field); } - String expressionPattern = format( - "extract(%s from from_unixtime(cast(%s as double) / 1000000, 'UTC'))", - field, - micros); - assertExecute(generateExpression(expressionPattern, micros), BIGINT, expected); + else { + micros = null; + expected = null; + } + + assertThat(assertions.expression("extract(%s from from_unixtime(CAST(a as double) / 1000000, 'UTC'))".formatted(field)) + .binding("a", toLiteral(micros))) + .isEqualTo(expected); } } - - Futures.allAsList(futures).get(); } @SuppressWarnings("fallthrough") private static long callExtractFunction(long value, Field field) { - switch (field) { - case YEAR: - return ExtractYear.extract(value); - case QUARTER: - return ExtractQuarter.extract(value); - case MONTH: - return ExtractMonth.extract(value); - case WEEK: - return ExtractWeekOfYear.extract(value); - case DAY: - case DAY_OF_MONTH: - return ExtractDay.extract(value); - case DAY_OF_WEEK: - case DOW: - return ExtractDayOfWeek.extract(value); - case YEAR_OF_WEEK: - case YOW: - return ExtractYearOfWeek.extract(value); - case DAY_OF_YEAR: - case DOY: - return ExtractDayOfYear.extract(value); - case HOUR: - return ExtractHour.extract(value); - case MINUTE: - return ExtractMinute.extract(value); - case SECOND: - return ExtractSecond.extract(value); - case TIMEZONE_MINUTE: - case TIMEZONE_HOUR: - // TODO test these - } - throw new AssertionError("Unhandled field: " + field); + return switch (field) { + case YEAR -> ExtractYear.extract(value); + case QUARTER -> ExtractQuarter.extract(value); + case MONTH -> ExtractMonth.extract(value); + case WEEK -> ExtractWeekOfYear.extract(value); + case DAY, DAY_OF_MONTH -> ExtractDay.extract(value); + case DAY_OF_WEEK, DOW -> ExtractDayOfWeek.extract(value); + case YEAR_OF_WEEK, YOW -> ExtractYearOfWeek.extract(value); + case DAY_OF_YEAR, DOY -> ExtractDayOfYear.extract(value); + case HOUR -> ExtractHour.extract(value); + case MINUTE -> ExtractMinute.extract(value); + case SECOND -> ExtractSecond.extract(value); + case TIMEZONE_MINUTE, TIMEZONE_HOUR -> throw new AssertionError("Unhandled field: " + field); + }; } @Test public void testLike() - throws Exception { for (String value : stringLefts) { for (String pattern : stringLefts) { - Boolean expected = null; - if (value != null && pattern != null) { - LikeMatcher matcher = LikeFunctions.likePattern(utf8Slice(pattern), utf8Slice("\\")); - expected = LikeFunctions.likeVarchar(utf8Slice(value), matcher); - } - assertExecute(generateExpression("%s like %s", value, pattern), BOOLEAN, expected); + assertThat(assertions.expression("a LIKE b") + .binding("a", toLiteral(value)) + .binding("b", toLiteral(pattern))) + .isEqualTo(value == null || pattern == null ? + null : + LikeFunctions.likeVarchar(utf8Slice(value), LikeFunctions.likePattern(utf8Slice(pattern), utf8Slice("\\")))); } } - - Futures.allAsList(futures).get(); } @Test public void testCoalesce() - throws Exception { - assertExecute("coalesce(9, 1)", INTEGER, 9); - assertExecute("coalesce(9, null)", INTEGER, 9); - assertExecute("coalesce(9, BIGINT '1')", BIGINT, 9L); - assertExecute("coalesce(BIGINT '9', null)", BIGINT, 9L); - assertExecute("coalesce(9, cast(null as bigint))", BIGINT, 9L); - assertExecute("coalesce(null, 9, 1)", INTEGER, 9); - assertExecute("coalesce(null, 9, null)", INTEGER, 9); - assertExecute("coalesce(null, 9, BIGINT '1')", BIGINT, 9L); - assertExecute("coalesce(null, 9, CAST (null AS BIGINT))", BIGINT, 9L); - assertExecute("coalesce(null, 9, cast(null as bigint))", BIGINT, 9L); - assertExecute("coalesce(cast(null as bigint), 9, 1)", BIGINT, 9L); - assertExecute("coalesce(cast(null as bigint), 9, null)", BIGINT, 9L); - assertExecute("coalesce(cast(null as bigint), 9, cast(null as bigint))", BIGINT, 9L); - - assertExecute("coalesce(9.0E0, 1.0E0)", DOUBLE, 9.0); - assertExecute("coalesce(9.0E0, 1)", DOUBLE, 9.0); - assertExecute("coalesce(9.0E0, null)", DOUBLE, 9.0); - assertExecute("coalesce(9.0E0, cast(null as double))", DOUBLE, 9.0); - assertExecute("coalesce(null, 9.0E0, 1)", DOUBLE, 9.0); - assertExecute("coalesce(null, 9.0E0, null)", DOUBLE, 9.0); - assertExecute("coalesce(null, 9.0E0, cast(null as double))", DOUBLE, 9.0); - assertExecute("coalesce(null, 9.0E0, cast(null as bigint))", DOUBLE, 9.0); - assertExecute("coalesce(cast(null as bigint), 9.0E0, 1)", DOUBLE, 9.0); - assertExecute("coalesce(cast(null as bigint), 9.0E0, null)", DOUBLE, 9.0); - assertExecute("coalesce(cast(null as bigint), 9.0E0, cast(null as bigint))", DOUBLE, 9.0); - assertExecute("coalesce(cast(null as double), 9.0E0, cast(null as double))", DOUBLE, 9.0); - - assertExecute("coalesce('foo', 'banana')", createVarcharType(6), "foo"); - assertExecute("coalesce('foo', null)", createVarcharType(3), "foo"); - assertExecute("coalesce('foo', cast(null as varchar))", VARCHAR, "foo"); - assertExecute("coalesce(null, 'foo', 'banana')", createVarcharType(6), "foo"); - assertExecute("coalesce(null, 'foo', null)", createVarcharType(3), "foo"); - assertExecute("coalesce(null, 'foo', cast(null as varchar))", VARCHAR, "foo"); - assertExecute("coalesce(cast(null as varchar), 'foo', 'bar')", VARCHAR, "foo"); - assertExecute("coalesce(cast(null as varchar), 'foo', null)", VARCHAR, "foo"); - assertExecute("coalesce(cast(null as varchar), 'foo', cast(null as varchar))", VARCHAR, "foo"); - - assertExecute("coalesce(cast(null as bigint), null, cast(null as bigint))", BIGINT, null); - - Futures.allAsList(futures).get(); - } + assertThat(assertions.function("coalesce", "9", "1")) + .hasType(INTEGER) + .isEqualTo(9); - @Test - public void testNullif() - throws Exception - { - assertExecute("nullif(NULL, NULL)", UNKNOWN, null); - assertExecute("nullif(NULL, 2)", UNKNOWN, null); - assertExecute("nullif(2, NULL)", INTEGER, 2); - assertExecute("nullif(BIGINT '2', NULL)", BIGINT, 2L); - assertExecute("nullif(ARRAY[CAST(1 AS BIGINT)], ARRAY[CAST(1 AS BIGINT)])", new ArrayType(BIGINT), null); - assertExecute("nullif(ARRAY[CAST(1 AS BIGINT)], ARRAY[CAST(NULL AS BIGINT)])", new ArrayType(BIGINT), ImmutableList.of(1L)); - assertExecute("nullif(ARRAY[CAST(NULL AS BIGINT)], ARRAY[CAST(NULL AS BIGINT)])", new ArrayType(BIGINT), singletonList(null)); + assertThat(assertions.function("coalesce", "9", "null")) + .hasType(INTEGER) + .isEqualTo(9); - // Test coercion in which the CAST function takes ConnectorSession (e.g. MapToMapCast) - assertExecute("nullif(" + - "map(array[1], array[smallint '1']), " + - "map(array[1], array[integer '1']))", - mapType(INTEGER, SMALLINT), - null); + assertThat(assertions.function("coalesce", "9", "BIGINT '1'")) + .hasType(BIGINT) + .isEqualTo(9L); - Futures.allAsList(futures).get(); - } + assertThat(assertions.function("coalesce", "BIGINT '9'", "null")) + .hasType(BIGINT) + .isEqualTo(9L); - private List generateExpression(String expressionPattern, Boolean value) - { - return formatExpression(expressionPattern, value, "boolean"); - } + assertThat(assertions.function("coalesce", "9", "CAST(null as bigint)")) + .hasType(BIGINT) + .isEqualTo(9L); - private List generateExpression(String expressionPattern, Long value) - { - return formatExpression(expressionPattern, value, "bigint"); - } + assertThat(assertions.function("coalesce", "null", "9", "1")) + .hasType(INTEGER) + .isEqualTo(9); - private List generateExpression(String expressionPattern, Integer value) - { - return formatExpression(expressionPattern, value, "integer"); - } + assertThat(assertions.function("coalesce", "null", "9", "null")) + .hasType(INTEGER) + .isEqualTo(9); - private List generateExpression(String expressionPattern, Double value) - { - return formatExpression(expressionPattern, value, "double"); - } + assertThat(assertions.function("coalesce", "null", "9", "BIGINT '1'")) + .hasType(BIGINT) + .isEqualTo(9L); - private List generateExpression(String expressionPattern, String value) - { - return formatExpression(expressionPattern, value, "varchar"); - } - - private List generateExpression(String expressionPattern, BigDecimal value) - { - return formatExpression(expressionPattern, value, getDecimalType(value).toString()); - } + assertThat(assertions.function("coalesce", "null", "9", "CAST (null AS BIGINT)")) + .hasType(BIGINT) + .isEqualTo(9L); - private List generateExpression(String expressionPattern, Boolean left, Boolean right) - { - return formatExpression(expressionPattern, left, "boolean", right, "boolean"); - } + assertThat(assertions.function("coalesce", "null", "9", "CAST(null as bigint)")) + .hasType(BIGINT) + .isEqualTo(9L); - private List generateExpression(String expressionPattern, Long left, Long right) - { - return formatExpression(expressionPattern, left, "bigint", right, "bigint"); - } + assertThat(assertions.function("coalesce", "CAST(null as bigint)", "9", "1")) + .hasType(BIGINT) + .isEqualTo(9L); - private List generateExpression(String expressionPattern, Long left, Integer right) - { - return formatExpression(expressionPattern, left, "bigint", right, "integer"); - } - - private List generateExpression(String expressionPattern, Integer left, Integer right) - { - return formatExpression(expressionPattern, left, "integer", right, "integer"); - } - - private List generateExpression(String expressionPattern, Long left, Double right) - { - return formatExpression(expressionPattern, left, "bigint", right, "double"); - } - - private List generateExpression(String expressionPattern, Integer left, Double right) - { - return formatExpression(expressionPattern, left, "integer", right, "double"); - } + assertThat(assertions.function("coalesce", "CAST(null as bigint)", "9", "null")) + .hasType(BIGINT) + .isEqualTo(9L); - private List generateExpression(String expressionPattern, Double left, Long right) - { - return formatExpression(expressionPattern, left, "double", right, "bigint"); - } + assertThat(assertions.function("coalesce", "CAST(null as bigint)", "9", "CAST(null as bigint)")) + .hasType(BIGINT) + .isEqualTo(9L); - private List generateExpression(String expressionPattern, Double left, Integer right) - { - return formatExpression(expressionPattern, left, "double", right, "integer"); - } + assertThat(assertions.function("coalesce", "9.0E0", "1.0E0")) + .hasType(DOUBLE) + .isEqualTo(9.0); - private List generateExpression(String expressionPattern, Double left, Double right) - { - return formatExpression(expressionPattern, left, "double", right, "double"); - } + assertThat(assertions.function("coalesce", "9.0E0", "1")) + .hasType(DOUBLE) + .isEqualTo(9.0); - private List generateExpression(String expressionPattern, Long left, BigDecimal right) - { - return formatExpression(expressionPattern, left, "bigint", right, getDecimalType(right).toString()); - } + assertThat(assertions.function("coalesce", "9.0E0", "null")) + .hasType(DOUBLE) + .isEqualTo(9.0); - private List generateExpression(String expressionPattern, BigDecimal left, Long right) - { - return formatExpression(expressionPattern, left, getDecimalType(left).toString(), right, "bigint"); - } + assertThat(assertions.function("coalesce", "9.0E0", "CAST(null as double)")) + .hasType(DOUBLE) + .isEqualTo(9.0); - private List generateExpression(String expressionPattern, Integer left, BigDecimal right) - { - return formatExpression(expressionPattern, left, "integer", right, getDecimalType(right).toString()); - } + assertThat(assertions.function("coalesce", "null", "9.0E0", "1")) + .hasType(DOUBLE) + .isEqualTo(9.0); - private List generateExpression(String expressionPattern, BigDecimal left, Integer right) - { - return formatExpression(expressionPattern, left, getDecimalType(left).toString(), right, "integer"); - } + assertThat(assertions.function("coalesce", "null", "9.0E0", "null")) + .hasType(DOUBLE) + .isEqualTo(9.0); - private List generateExpression(String expressionPattern, Double left, BigDecimal right) - { - return formatExpression(expressionPattern, left, "double", right, getDecimalType(right).toString()); - } + assertThat(assertions.function("coalesce", "null", "9.0E0", "CAST(null as double)")) + .hasType(DOUBLE) + .isEqualTo(9.0); - private List generateExpression(String expressionPattern, BigDecimal left, Double right) - { - return formatExpression(expressionPattern, left, getDecimalType(left).toString(), right, "double"); - } + assertThat(assertions.function("coalesce", "null", "9.0E0", "CAST(null as bigint)")) + .hasType(DOUBLE) + .isEqualTo(9.0); - private List generateExpression(String expressionPattern, String left, String right) - { - return formatExpression(expressionPattern, left, "varchar", right, "varchar"); - } + assertThat(assertions.function("coalesce", "CAST(null as bigint)", "9.0E0", "1")) + .hasType(DOUBLE) + .isEqualTo(9.0); - private List generateExpression(String expressionPattern, Long first, Long second, Long third) - { - return formatExpression(expressionPattern, first, "bigint", second, "bigint", third, "bigint"); - } + assertThat(assertions.function("coalesce", "CAST(null as bigint)", "9.0E0", "null")) + .hasType(DOUBLE) + .isEqualTo(9.0); - private List generateExpression(String expressionPattern, Integer first, Integer second, Integer third) - { - return formatExpression(expressionPattern, first, "integer", second, "integer", third, "integer"); - } + assertThat(assertions.function("coalesce", "CAST(null as bigint)", "9.0E0", "CAST(null as bigint)")) + .hasType(DOUBLE) + .isEqualTo(9.0); - private List generateExpression(String expressionPattern, BigDecimal first, BigDecimal second, BigDecimal third) - { - return formatExpression(expressionPattern, first, getDecimalType(first).toString(), second, getDecimalType(second).toString(), third, getDecimalType(third).toString()); - } + assertThat(assertions.function("coalesce", "CAST(null as double)", "9.0E0", "CAST(null as double)")) + .hasType(DOUBLE) + .isEqualTo(9.0); - private List generateExpression(String expressionPattern, Long first, Double second, Long third) - { - return formatExpression(expressionPattern, first, "bigint", second, "double", third, "bigint"); - } + assertThat(assertions.function("coalesce", "'foo'", "'banana'")) + .hasType(createVarcharType(6)) + .isEqualTo("foo"); - private List generateExpression(String expressionPattern, Integer first, Double second, Integer third) - { - return formatExpression(expressionPattern, first, "integer", second, "double", third, "integer"); - } + assertThat(assertions.function("coalesce", "'foo'", "null")) + .hasType(createVarcharType(3)) + .isEqualTo("foo"); - private List generateExpression(String expressionPattern, Double first, Double second, Double third) - { - return formatExpression(expressionPattern, first, "double", second, "double", third, "double"); - } + assertThat(assertions.function("coalesce", "'foo'", "CAST(null as varchar)")) + .hasType(VARCHAR) + .isEqualTo("foo"); - private List generateExpression(String expressionPattern, Double first, Double second, Long third) - { - return formatExpression(expressionPattern, first, "double", second, "double", third, "bigint"); - } + assertThat(assertions.function("coalesce", "null", "'foo'", "'banana'")) + .hasType(createVarcharType(6)) + .isEqualTo("foo"); - private List generateExpression(String expressionPattern, Double first, Double second, Integer third) - { - return formatExpression(expressionPattern, first, "double", second, "double", third, "integer"); - } + assertThat(assertions.function("coalesce", "null", "'foo'", "null")) + .hasType(createVarcharType(3)) + .isEqualTo("foo"); - private List generateExpression(String expressionPattern, Double first, Long second, Double third) - { - return formatExpression(expressionPattern, first, "double", second, "bigint", third, "double"); - } + assertThat(assertions.function("coalesce", "null", "'foo'", "CAST(null as varchar)")) + .hasType(VARCHAR) + .isEqualTo("foo"); - private List generateExpression(String expressionPattern, String first, String second, String third) - { - return formatExpression(expressionPattern, first, "varchar", second, "varchar", third, "varchar"); - } + assertThat(assertions.function("coalesce", "CAST(null as varchar)", "'foo'", "'bar'")) + .hasType(VARCHAR) + .isEqualTo("foo"); - private List generateExpression(String expressionPattern, Boolean first, String second, String third) - { - return formatExpression(expressionPattern, first, "boolean", second, "varchar", third, "varchar"); - } + assertThat(assertions.function("coalesce", "CAST(null as varchar)", "'foo'", "null")) + .hasType(VARCHAR) + .isEqualTo("foo"); - private List generateExpression(String expressionPattern, String first, Long second, Long third) - { - return formatExpression(expressionPattern, first, "varchar", second, "bigint", third, "bigint"); - } + assertThat(assertions.function("coalesce", "CAST(null as varchar)", "'foo'", "CAST(null as varchar)")) + .hasType(VARCHAR) + .isEqualTo("foo"); - private List generateExpression(String expressionPattern, String first, Integer second, Integer third) - { - return formatExpression(expressionPattern, first, "varchar", second, "integer", third, "integer"); + assertThat(assertions.function("coalesce", "CAST(null as bigint)", "null", "CAST(null as bigint)")) + .isNull(BIGINT); } - private List generateExpression(String expressionPattern, Long first, BigDecimal second, Long third) + @Test + public void testNullif() { - return formatExpression(expressionPattern, first, "bigint", second, "decimal(3,1)", third, "bigint"); - } + assertThat(assertions.function("nullif", "NULL", "NULL")) + .isNull(UNKNOWN); - private List generateExpression(String expressionPattern, BigDecimal first, Double second, BigDecimal third) - { - return formatExpression(expressionPattern, first, getDecimalType(first).toString(), second, "double", third, getDecimalType(third).toString()); - } + assertThat(assertions.function("nullif", "NULL", "2")) + .isNull(UNKNOWN); - private static List formatExpression(String expressionPattern, Object value, String type) - { - return formatExpression(expressionPattern, - Arrays.asList(value), - ImmutableList.of(type)); - } + assertThat(assertions.function("nullif", "2", "NULL")) + .hasType(INTEGER) + .isEqualTo(2); - private static List formatExpression(String expressionPattern, Object left, String leftType, Object right, String rightType) - { - return formatExpression(expressionPattern, - Arrays.asList(left, right), - ImmutableList.of(leftType, rightType)); - } + assertThat(assertions.function("nullif", "BIGINT '2'", "NULL")) + .hasType(BIGINT) + .isEqualTo(2L); - private static List formatExpression( - String expressionPattern, - Object first, - String firstType, - Object second, - String secondType, - Object third, - String thirdType) - { - return formatExpression(expressionPattern, - Arrays.asList(first, second, third), - ImmutableList.of(firstType, secondType, thirdType)); - } + assertThat(assertions.function("nullif", "ARRAY[CAST(1 AS BIGINT)]", "ARRAY[CAST(1 AS BIGINT)]")) + .isNull(new ArrayType(BIGINT)); - private static List formatExpression(String expressionPattern, List values, List types) - { - checkArgument(values.size() == types.size()); + assertThat(assertions.function("nullif", "ARRAY[CAST(1 AS BIGINT)]", "ARRAY[CAST(NULL AS BIGINT)]")) + .hasType(new ArrayType(BIGINT)) + .isEqualTo(ImmutableList.of(1L)); - List> unrolledValues = new ArrayList<>(); - for (int i = 0; i < values.size(); i++) { - Object value = values.get(i); - String type = types.get(i); - if (value != null) { - if (type.equals("varchar")) { - value = "'" + value + "'"; - } - else if (type.equals("bigint")) { - value = "CAST( " + value + " AS BIGINT)"; - } - else if (type.equals("double")) { - value = "CAST( " + value + " AS DOUBLE)"; - } - unrolledValues.add(ImmutableSet.of(String.valueOf(value))); - } - else { - // todo enable when null output type is supported - // unrolledValues.add(ImmutableSet.of("null", "cast(null as " + type + ")")); - unrolledValues.add(ImmutableSet.of("cast(null as " + type + ")")); - } - } + assertThat(assertions.function("nullif", "ARRAY[CAST(NULL AS BIGINT)]", "ARRAY[CAST(NULL AS BIGINT)]")) + .hasType(new ArrayType(BIGINT)) + .isEqualTo(singletonList(null)); - ImmutableList.Builder expressions = ImmutableList.builder(); - Set> valueLists = Sets.cartesianProduct(unrolledValues); - for (List valueList : valueLists) { - expressions.add(format(expressionPattern, valueList.toArray(new Object[valueList.size()]))); - } - return expressions.build(); + // Test coercion in which the CAST function takes ConnectorSession (e.g. MapToMapCast) + assertThat(assertions.function("nullif", "map(array[1], array[smallint '1'])", "map(array[1], array[integer '1'])")) + .isNull(mapType(INTEGER, SMALLINT)); } - private String formatDoubleToScientificNotation(Double value) + private static String formatDoubleToScientificNotation(Double value) { DecimalFormat formatter = ((DecimalFormat) NumberFormat.getNumberInstance(Locale.US)); formatter.applyPattern("0.##############E0"); return formatter.format(value); } - private void assertExecute(String expression, Type expectedType, Object expected) + private static SqlDecimal toSqlDecimal(BigDecimal decimal) { - addCallable(new AssertExecuteTask(functionAssertions, expression, expectedType, expected)); + if (decimal == null) { + return null; + } + return new SqlDecimal(decimal.unscaledValue(), decimal.precision(), decimal.scale()); } - private void addCallable(Runnable runnable) + private static Type getDecimalType(BigDecimal decimal) { - if (PARALLEL) { - futures.add(Futures.submit(runnable, executor)); - } - else { - runnable.run(); + if (decimal == null) { + return createDecimalType(1, 0); } + return createDecimalType(decimal.precision(), decimal.scale()); } - private void assertExecute(List expressions, Type expectedType, Object expected) + private static String toLiteral(Boolean value) { - if (expected instanceof Slice) { - expected = ((Slice) expected).toStringUtf8(); - } - for (String expression : expressions) { - assertExecute(expression, expectedType, expected); - } + return toLiteral(value, BOOLEAN); } - private void assertExecute(List expressions, BigDecimal decimal) + private static String toLiteral(Long value) { - Type type = getDecimalType(decimal); - SqlDecimal value = decimal == null ? null : new SqlDecimal(decimal.unscaledValue(), decimal.precision(), decimal.scale()); - for (String expression : expressions) { - assertExecute(expression, type, value); - } + return toLiteral(value, BIGINT); } - private static Type getDecimalType(BigDecimal decimal) + private static String toLiteral(BigDecimal value) { - if (decimal == null) { - return createDecimalType(1, 0); - } - return createDecimalType(decimal.precision(), decimal.scale()); + return toLiteral(value, getDecimalType(value)); } - private static class AssertExecuteTask - implements Runnable + private static String toLiteral(Double value) { - private final FunctionAssertions functionAssertions; - private final String expression; - private final Type expectedType; - private final Object expected; - - public AssertExecuteTask(FunctionAssertions functionAssertions, String expression, Type expectedType, Object expected) - { - this.functionAssertions = functionAssertions; - this.expectedType = expectedType; - this.expression = expression; - this.expected = expected; - } - - @Override - public void run() - { - try { - functionAssertions.assertFunction(expression, expectedType, expected); - } - catch (Throwable e) { - throw new RuntimeException("Error processing " + expression, e); - } - } + return toLiteral(value, DOUBLE); } - private void assertFilterWithNoInputColumns(String filter, boolean expected) + private static String toLiteral(String value) { - addCallable(new AssertFilterTask(functionAssertions, filter, expected, true)); + return toLiteral(value, VARCHAR); } - private void assertFilter(String filter, boolean expected) + private static String toLiteral(Integer value) { - addCallable(new AssertFilterTask(functionAssertions, filter, expected, false)); + return toLiteral(value, INTEGER); } - private static class AssertFilterTask - implements Runnable + private static String toLiteral(Object value, Type type) { - private final FunctionAssertions functionAssertions; - private final String filter; - private final boolean expected; - private final boolean withNoInputColumns; - - public AssertFilterTask(FunctionAssertions functionAssertions, String filter, boolean expected, boolean withNoInputColumns) - { - this.functionAssertions = functionAssertions; - this.filter = filter; - this.expected = expected; - this.withNoInputColumns = withNoInputColumns; - } - - @Override - public void run() - { - try { - functionAssertions.assertFilter(filter, expected, withNoInputColumns); - } - catch (Throwable e) { - throw new RuntimeException("Error processing " + filter, e); - } + if (value == null) { + value = "CAST(null AS %s)".formatted(type); + } + else if (type.equals(VARCHAR)) { + value = "'%s'".formatted(value); + } + else if (type.equals(BIGINT)) { + value = "BIGINT '%s'".formatted(value); + } + else if (type.equals(DOUBLE)) { + value = "DOUBLE '%s'".formatted(value); } + + return String.valueOf(value); + } + + private static String toString(Slice value) + { + return value == null ? null : value.toStringUtf8(); } } diff --git a/core/trino-main/src/test/java/io/trino/sql/query/QueryAssertions.java b/core/trino-main/src/test/java/io/trino/sql/query/QueryAssertions.java index b73cfcdea771..8886378b0eb1 100644 --- a/core/trino-main/src/test/java/io/trino/sql/query/QueryAssertions.java +++ b/core/trino-main/src/test/java/io/trino/sql/query/QueryAssertions.java @@ -18,6 +18,7 @@ import io.trino.Session; import io.trino.execution.warnings.WarningCollector; import io.trino.metadata.FunctionBundle; +import io.trino.spi.Plugin; import io.trino.spi.function.OperatorType; import io.trino.spi.type.SqlTime; import io.trino.spi.type.SqlTimeWithTimeZone; @@ -108,6 +109,11 @@ public void addFunctions(FunctionBundle functionBundle) runner.addFunctions(functionBundle); } + public void addPlugin(Plugin plugin) + { + runner.installPlugin(plugin); + } + public AssertProvider query(@Language("SQL") String query) { return query(runner.getDefaultSession(), query); diff --git a/core/trino-main/src/test/java/io/trino/type/TestRowOperators.java b/core/trino-main/src/test/java/io/trino/type/TestRowOperators.java index ee63bc8ae0bb..66798ae18775 100644 --- a/core/trino-main/src/test/java/io/trino/type/TestRowOperators.java +++ b/core/trino-main/src/test/java/io/trino/type/TestRowOperators.java @@ -19,7 +19,7 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import io.airlift.slice.Slice; -import io.trino.operator.scalar.AbstractTestFunctions; +import io.trino.metadata.InternalFunctionBundle; import io.trino.operator.scalar.CombineHashFunction; import io.trino.spi.StandardErrorCode; import io.trino.spi.function.LiteralParameters; @@ -30,8 +30,12 @@ import io.trino.spi.type.StandardTypes; import io.trino.spi.type.Type; import io.trino.spi.type.TypeSignature; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; +import io.trino.sql.query.QueryAssertions; +import io.trino.testing.LocalQueryRunner; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; import java.util.ArrayList; import java.util.Arrays; @@ -40,12 +44,16 @@ import java.util.Set; import static com.google.common.base.Preconditions.checkArgument; +import static io.trino.operator.scalar.AbstractTestFunctions.asMap; +import static io.trino.spi.StandardErrorCode.INVALID_CAST_ARGUMENT; import static io.trino.spi.StandardErrorCode.TYPE_MISMATCH; import static io.trino.spi.function.InvocationConvention.InvocationArgumentConvention.NEVER_NULL; import static io.trino.spi.function.InvocationConvention.InvocationReturnConvention.FAIL_ON_NULL; import static io.trino.spi.function.InvocationConvention.simpleConvention; +import static io.trino.spi.function.OperatorType.EQUAL; import static io.trino.spi.function.OperatorType.HASH_CODE; import static io.trino.spi.function.OperatorType.INDETERMINATE; +import static io.trino.spi.function.OperatorType.IS_DISTINCT_FROM; import static io.trino.spi.function.OperatorType.XX_HASH_64; import static io.trino.spi.type.BigintType.BIGINT; import static io.trino.spi.type.BooleanType.BOOLEAN; @@ -61,22 +69,36 @@ import static io.trino.spi.type.VarcharType.createUnboundedVarcharType; import static io.trino.spi.type.VarcharType.createVarcharType; import static io.trino.testing.DateTimeTestingUtils.sqlTimestampOf; +import static io.trino.testing.assertions.TrinoExceptionAssert.assertTrinoExceptionThrownBy; import static io.trino.type.JsonType.JSON; import static io.trino.util.StructuralTestUtil.mapType; import static java.lang.String.format; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS; import static org.testng.Assert.assertEquals; +@TestInstance(PER_CLASS) public class TestRowOperators - extends AbstractTestFunctions { - public TestRowOperators() {} + private QueryAssertions assertions; - @BeforeClass - public void setUp() + @BeforeAll + public void init() { - registerScalar(getClass()); + assertions = new QueryAssertions(); + + assertions.addFunctions(InternalFunctionBundle.builder() + .scalars(TestRowOperators.class) + .build()); + } + + @AfterAll + public void teardown() + { + assertions.close(); + assertions = null; } @ScalarFunction @@ -91,7 +113,7 @@ public static Slice uncheckedToJson(@SqlType("varchar(x)") Slice slice) public void testRowTypeLookup() { TypeSignature signature = RowType.from(ImmutableList.of(field("b", BIGINT))).getTypeSignature(); - Type type = functionAssertions.getPlannerContext().getTypeManager().getType(signature); + Type type = ((LocalQueryRunner) assertions.getQueryRunner()).getPlannerContext().getTypeManager().getType(signature); assertEquals(type.getTypeSignature().getParameters().size(), 1); assertEquals(type.getTypeSignature().getParameters().get(0).getNamedTypeSignature().getName().get(), "b"); } @@ -99,132 +121,198 @@ public void testRowTypeLookup() @Test public void testRowToJson() { - assertFunction("cast(cast (null AS ROW(BIGINT, VARCHAR)) AS JSON)", JSON, null); - assertFunction("cast(ROW(null, null) AS json)", JSON, "{\"\":null,\"\":null}"); - - assertFunction("cast(ROW(true, false, null) AS JSON)", JSON, "{\"\":true,\"\":false,\"\":null}"); - - assertFunction( - "cast(cast(ROW(12, 12345, 123456789, 1234567890123456789, null, null, null, null) AS ROW(TINYINT, SMALLINT, INTEGER, BIGINT, TINYINT, SMALLINT, INTEGER, BIGINT)) AS JSON)", - JSON, - "{\"\":12,\"\":12345,\"\":123456789,\"\":1234567890123456789,\"\":null,\"\":null,\"\":null,\"\":null}"); - - assertFunction( - "CAST(ROW(CAST(3.14E0 AS REAL), 3.1415E0, 1e308, DECIMAL '3.14', DECIMAL '12345678901234567890.123456789012345678', CAST(null AS REAL), CAST(null AS DOUBLE), CAST(null AS DECIMAL)) AS JSON)", - JSON, - "{\"\":3.14,\"\":3.1415,\"\":1.0E308,\"\":3.14,\"\":12345678901234567890.123456789012345678,\"\":null,\"\":null,\"\":null}"); - - assertFunction( - "CAST(ROW('a', 'bb', CAST(null AS VARCHAR), JSON '123', JSON '3.14', JSON 'false', JSON '\"abc\"', JSON '[1, \"a\", null]', JSON '{\"a\": 1, \"b\": \"str\", \"c\": null}', JSON 'null', CAST(null AS JSON)) AS JSON)", - JSON, - "{\"\":\"a\",\"\":\"bb\",\"\":null,\"\":123,\"\":3.14,\"\":false,\"\":\"abc\",\"\":[1,\"a\",null],\"\":{\"a\":1,\"b\":\"str\",\"c\":null},\"\":null,\"\":null}"); - assertFunction( - "CAST(ROW(DATE '2001-08-22', DATE '2001-08-23', null) AS JSON)", - JSON, - "{\"\":\"2001-08-22\",\"\":\"2001-08-23\",\"\":null}"); - - assertFunction( - "CAST(ROW(TIMESTAMP '1970-01-01 00:00:01', cast(null AS TIMESTAMP)) AS JSON)", - JSON, - format("{\"\":\"%s\",\"\":null}", sqlTimestampOf(0, 1970, 1, 1, 0, 0, 1, 0))); - - assertFunction( - "cast(ROW(ARRAY[1, 2], ARRAY[3, null], ARRAY[], ARRAY[null, null], CAST(null AS ARRAY(BIGINT))) AS JSON)", - JSON, - "{\"\":[1,2],\"\":[3,null],\"\":[],\"\":[null,null],\"\":null}"); - assertFunction( - "cast(ROW(MAP(ARRAY['b', 'a'], ARRAY[2, 1]), MAP(ARRAY['three', 'none'], ARRAY[3, null]), MAP(), MAP(ARRAY['h2', 'h1'], ARRAY[null, null]), CAST(NULL AS MAP(VARCHAR, BIGINT))) AS JSON)", - JSON, - "{\"\":{\"a\":1,\"b\":2},\"\":{\"none\":null,\"three\":3},\"\":{},\"\":{\"h1\":null,\"h2\":null},\"\":null}"); - assertFunction( - "cast(ROW(ROW(1, 2), ROW(3, CAST(null AS INTEGER)), CAST(ROW(null, null) AS ROW(INTEGER, INTEGER)), null) AS JSON)", - JSON, - "{\"\":{\"\":1,\"\":2},\"\":{\"\":3,\"\":null},\"\":{\"\":null,\"\":null},\"\":null}"); + assertThat(assertions.expression("CAST(a AS JSON)") + .binding("a", "CAST(null as ROW(BIGINT, VARCHAR))")) + .isNull(JSON); + + assertThat(assertions.expression("CAST(a as json)") + .binding("a", "ROW(null, null)")) + .hasType(JSON) + .isEqualTo("{\"\":null,\"\":null}"); + + assertThat(assertions.expression("CAST(a as JSON)") + .binding("a", "ROW(true, false, null)")) + .hasType(JSON) + .isEqualTo("{\"\":true,\"\":false,\"\":null}"); + + assertThat(assertions.expression("CAST(a AS JSON)") + .binding("a", "CAST(ROW(12, 12345, 123456789, 1234567890123456789, null, null, null, null) as ROW(TINYINT, SMALLINT, INTEGER, BIGINT, TINYINT, SMALLINT, INTEGER, BIGINT))")) + .hasType(JSON) + .isEqualTo("{\"\":12,\"\":12345,\"\":123456789,\"\":1234567890123456789,\"\":null,\"\":null,\"\":null,\"\":null}"); + + assertThat(assertions.expression("CAST(a AS JSON)") + .binding("a", "ROW(CAST(3.14E0 as REAL), 3.1415E0, 1e308, DECIMAL '3.14', DECIMAL '12345678901234567890.123456789012345678', CAST(null AS REAL), CAST(null AS DOUBLE), CAST(null AS DECIMAL))")) + .hasType(JSON) + .isEqualTo("{\"\":3.14,\"\":3.1415,\"\":1.0E308,\"\":3.14,\"\":12345678901234567890.123456789012345678,\"\":null,\"\":null,\"\":null}"); + + assertThat(assertions.expression("CAST(a AS JSON)") + .binding("a", "ROW('a', 'bb', CAST(null as VARCHAR), JSON '123', JSON '3.14', JSON 'false', JSON '\"abc\"', JSON '[1, \"a\", null]', JSON '{\"a\": 1, \"b\": \"str\", \"c\": null}', JSON 'null', CAST(null AS JSON))")) + .hasType(JSON) + .isEqualTo("{\"\":\"a\",\"\":\"bb\",\"\":null,\"\":123,\"\":3.14,\"\":false,\"\":\"abc\",\"\":[1,\"a\",null],\"\":{\"a\":1,\"b\":\"str\",\"c\":null},\"\":null,\"\":null}"); + + assertThat(assertions.expression("CAST(a as JSON)") + .binding("a", "ROW(DATE '2001-08-22', DATE '2001-08-23', null)")) + .hasType(JSON) + .isEqualTo("{\"\":\"2001-08-22\",\"\":\"2001-08-23\",\"\":null}"); + + assertThat(assertions.expression("CAST(a AS JSON)") + .binding("a", "ROW(TIMESTAMP '1970-01-01 00:00:01', CAST(null as TIMESTAMP))")) + .hasType(JSON) + .isEqualTo(format("{\"\":\"%s\",\"\":null}", sqlTimestampOf(0, 1970, 1, 1, 0, 0, 1, 0))); + + assertThat(assertions.expression("CAST(a AS JSON)") + .binding("a", "ROW(ARRAY[1, 2], ARRAY[3, null], ARRAY[], ARRAY[null, null], CAST(null as ARRAY(BIGINT)))")) + .hasType(JSON) + .isEqualTo("{\"\":[1,2],\"\":[3,null],\"\":[],\"\":[null,null],\"\":null}"); + + assertThat(assertions.expression("CAST(a AS JSON)") + .binding("a", "ROW(MAP(ARRAY['b', 'a'], ARRAY[2, 1]), MAP(ARRAY['three', 'none'], ARRAY[3, null]), MAP(), MAP(ARRAY['h2', 'h1'], ARRAY[null, null]), CAST(NULL as MAP(VARCHAR, BIGINT)))")) + .hasType(JSON) + .isEqualTo("{\"\":{\"a\":1,\"b\":2},\"\":{\"none\":null,\"three\":3},\"\":{},\"\":{\"h1\":null,\"h2\":null},\"\":null}"); + + assertThat(assertions.expression("CAST(a AS JSON)") + .binding("a", "ROW(ROW(1, 2), ROW(3, CAST(null as INTEGER)), CAST(ROW(null, null) AS ROW(INTEGER, INTEGER)), null)")) + .hasType(JSON) + .isEqualTo("{\"\":{\"\":1,\"\":2},\"\":{\"\":3,\"\":null},\"\":{\"\":null,\"\":null},\"\":null}"); // other miscellaneous tests - assertFunction("CAST(ROW(1, 2) AS JSON)", JSON, "{\"\":1,\"\":2}"); - assertFunction("CAST(CAST(ROW(1, 2) AS ROW(a BIGINT, b BIGINT)) AS JSON)", JSON, "{\"a\":1,\"b\":2}"); - assertFunction("CAST(ROW(1, NULL) AS JSON)", JSON, "{\"\":1,\"\":null}"); - assertFunction("CAST(ROW(1, CAST(NULL AS INTEGER)) AS JSON)", JSON, "{\"\":1,\"\":null}"); - assertFunction("CAST(ROW(1, 2.0E0) AS JSON)", JSON, "{\"\":1,\"\":2.0}"); - assertFunction("CAST(ROW(1.0E0, 2.5E0) AS JSON)", JSON, "{\"\":1.0,\"\":2.5}"); - assertFunction("CAST(ROW(1.0E0, 'kittens') AS JSON)", JSON, "{\"\":1.0,\"\":\"kittens\"}"); - assertFunction("CAST(ROW(TRUE, FALSE) AS JSON)", JSON, "{\"\":true,\"\":false}"); - assertFunction("CAST(ROW(FALSE, ARRAY [1, 2], MAP(ARRAY[1, 3], ARRAY[2.0E0, 4.0E0])) AS JSON)", JSON, "{\"\":false,\"\":[1,2],\"\":{\"1\":2.0,\"3\":4.0}}"); - assertFunction("CAST(row(1.0, 123123123456.6549876543) AS JSON)", JSON, "{\"\":1.0,\"\":123123123456.6549876543}"); + assertThat(assertions.expression("CAST(a as JSON)") + .binding("a", "ROW(1, 2)")) + .hasType(JSON) + .isEqualTo("{\"\":1,\"\":2}"); + + assertThat(assertions.expression("CAST(a AS JSON)") + .binding("a", "CAST(ROW(1, 2) as ROW(a BIGINT, b BIGINT))")) + .hasType(JSON) + .isEqualTo("{\"a\":1,\"b\":2}"); + + assertThat(assertions.expression("CAST(a as JSON)") + .binding("a", "ROW(1, NULL)")) + .hasType(JSON) + .isEqualTo("{\"\":1,\"\":null}"); + + assertThat(assertions.expression("CAST(a AS JSON)") + .binding("a", "ROW(1, CAST(NULL as INTEGER))")) + .hasType(JSON) + .isEqualTo("{\"\":1,\"\":null}"); + + assertThat(assertions.expression("CAST(a as JSON)") + .binding("a", "ROW(1, 2.0E0)")) + .hasType(JSON) + .isEqualTo("{\"\":1,\"\":2.0}"); + + assertThat(assertions.expression("CAST(a as JSON)") + .binding("a", "ROW(1.0E0, 2.5E0)")) + .hasType(JSON) + .isEqualTo("{\"\":1.0,\"\":2.5}"); + + assertThat(assertions.expression("CAST(a as JSON)") + .binding("a", "ROW(1.0E0, 'kittens')")) + .hasType(JSON) + .isEqualTo("{\"\":1.0,\"\":\"kittens\"}"); + + assertThat(assertions.expression("CAST(a as JSON)") + .binding("a", "ROW(TRUE, FALSE)")) + .hasType(JSON) + .isEqualTo("{\"\":true,\"\":false}"); + + assertThat(assertions.expression("CAST(a as JSON)") + .binding("a", "ROW(FALSE, ARRAY [1, 2], MAP(ARRAY[1, 3], ARRAY[2.0E0, 4.0E0]))")) + .hasType(JSON) + .isEqualTo("{\"\":false,\"\":[1,2],\"\":{\"1\":2.0,\"3\":4.0}}"); + + assertThat(assertions.expression("CAST(a as JSON)") + .binding("a", "row(1.0, 123123123456.6549876543)")) + .hasType(JSON) + .isEqualTo("{\"\":1.0,\"\":123123123456.6549876543}"); } @Test public void testJsonToRow() { // special values - assertFunction("CAST(CAST (null AS JSON) AS ROW(BIGINT))", RowType.anonymous(ImmutableList.of(BIGINT)), null); - assertFunction("CAST(JSON 'null' AS ROW(BIGINT))", RowType.anonymous(ImmutableList.of(BIGINT)), null); - assertFunction("CAST(JSON '[null, null]' AS ROW(VARCHAR, BIGINT))", RowType.anonymous(ImmutableList.of(VARCHAR, BIGINT)), Lists.newArrayList(null, null)); - assertFunction( - "CAST(JSON '{\"k2\": null, \"k1\": null}' AS ROW(k1 VARCHAR, k2 BIGINT))", - RowType.from(ImmutableList.of( + assertThat(assertions.expression("CAST(a AS ROW(BIGINT))") + .binding("a", "CAST(null as JSON)")) + .isNull(RowType.anonymous(ImmutableList.of(BIGINT))); + + assertThat(assertions.expression("CAST(a as ROW(BIGINT))") + .binding("a", "JSON 'null'")) + .isNull(RowType.anonymous(ImmutableList.of(BIGINT))); + + assertThat(assertions.expression("CAST(a as ROW(VARCHAR, BIGINT))") + .binding("a", "JSON '[null, null]'")) + .hasType(RowType.anonymous(ImmutableList.of(VARCHAR, BIGINT))) + .isEqualTo(Lists.newArrayList(null, null)); + + assertThat(assertions.expression("CAST(a as ROW(k1 VARCHAR, k2 BIGINT))") + .binding("a", "JSON '{\"k2\": null, \"k1\": null}'")) + .hasType(RowType.from(ImmutableList.of( RowType.field("k1", VARCHAR), - RowType.field("k2", BIGINT))), - Lists.newArrayList(null, null)); + RowType.field("k2", BIGINT)))) + .isEqualTo(Lists.newArrayList(null, null)); // allow json object contains non-exist field names - assertFunction( - "CAST(JSON '{\"k1\": [1, 2], \"used\": 3, \"k2\": [4, 5]}' AS ROW(used BIGINT))", - RowType.from(ImmutableList.of( - RowType.field("used", BIGINT))), - ImmutableList.of(3L)); - assertFunction( - "CAST(JSON '[{\"k1\": [1, 2], \"used\": 3, \"k2\": [4, 5]}]' AS ARRAY(ROW(used BIGINT)))", - new ArrayType(RowType.from(ImmutableList.of( - RowType.field("used", BIGINT)))), - ImmutableList.of(ImmutableList.of(3L))); + assertThat(assertions.expression("CAST(a as ROW(used BIGINT))") + .binding("a", "JSON '{\"k1\": [1, 2], \"used\": 3, \"k2\": [4, 5]}'")) + .hasType(RowType.from(ImmutableList.of( + RowType.field("used", BIGINT)))) + .isEqualTo(ImmutableList.of(3L)); + + assertThat(assertions.expression("CAST(a as ARRAY(ROW(used BIGINT)))") + .binding("a", "JSON '[{\"k1\": [1, 2], \"used\": 3, \"k2\": [4, 5]}]'")) + .hasType(new ArrayType(RowType.from(ImmutableList.of( + RowType.field("used", BIGINT))))) + .isEqualTo(ImmutableList.of(ImmutableList.of(3L))); // allow non-exist fields in json object - assertFunction( - "CAST(JSON '{\"a\":1,\"c\":3}' AS ROW(a BIGINT, b BIGINT, c BIGINT, d BIGINT))", - RowType.from(ImmutableList.of( + assertThat(assertions.expression("CAST(a as ROW(a BIGINT, b BIGINT, c BIGINT, d BIGINT))") + .binding("a", "JSON '{\"a\":1,\"c\":3}'")) + .hasType(RowType.from(ImmutableList.of( RowType.field("a", BIGINT), RowType.field("b", BIGINT), RowType.field("c", BIGINT), - RowType.field("d", BIGINT))), - asList(1L, null, 3L, null)); - assertFunction( - "CAST(JSON '[{\"a\":1,\"c\":3}]' AS ARRAY)", - new ArrayType( + RowType.field("d", BIGINT)))) + .isEqualTo(asList(1L, null, 3L, null)); + + assertThat(assertions.expression("CAST(a as ARRAY(ROW(a BIGINT, b BIGINT, c BIGINT, d BIGINT)))") + .binding("a", "JSON '[{\"a\":1,\"c\":3}]'")) + .hasType(new ArrayType( RowType.from(ImmutableList.of( RowType.field("a", BIGINT), RowType.field("b", BIGINT), RowType.field("c", BIGINT), - RowType.field("d", BIGINT)))), - ImmutableList.of(asList(1L, null, 3L, null))); + RowType.field("d", BIGINT))))) + .isEqualTo(ImmutableList.of(asList(1L, null, 3L, null))); // fields out of order - assertFunction( - "CAST(unchecked_to_json('{\"k4\": 4, \"k2\": 2, \"k3\": 3, \"k1\": 1}') AS ROW(k1 BIGINT, k2 BIGINT, k3 BIGINT, k4 BIGINT))", - RowType.from(ImmutableList.of( + assertThat(assertions.expression("CAST(a as ROW(k1 BIGINT, k2 BIGINT, k3 BIGINT, k4 BIGINT))") + .binding("a", "unchecked_to_json('{\"k4\": 4, \"k2\": 2, \"k3\": 3, \"k1\": 1}')")) + .hasType(RowType.from(ImmutableList.of( RowType.field("k1", BIGINT), RowType.field("k2", BIGINT), RowType.field("k3", BIGINT), - RowType.field("k4", BIGINT))), - ImmutableList.of(1L, 2L, 3L, 4L)); - assertFunction( - "CAST(unchecked_to_json('[{\"k4\": 4, \"k2\": 2, \"k3\": 3, \"k1\": 1}]') AS ARRAY(ROW(k1 BIGINT, k2 BIGINT, k3 BIGINT, k4 BIGINT)))", - new ArrayType( + RowType.field("k4", BIGINT)))) + .isEqualTo(ImmutableList.of(1L, 2L, 3L, 4L)); + + assertThat(assertions.expression("CAST(a as ARRAY(ROW(k1 BIGINT, k2 BIGINT, k3 BIGINT, k4 BIGINT)))") + .binding("a", "unchecked_to_json('[{\"k4\": 4, \"k2\": 2, \"k3\": 3, \"k1\": 1}]')")) + .hasType(new ArrayType( RowType.from(ImmutableList.of( RowType.field("k1", BIGINT), RowType.field("k2", BIGINT), RowType.field("k3", BIGINT), - RowType.field("k4", BIGINT)))), - ImmutableList.of(ImmutableList.of(1L, 2L, 3L, 4L))); + RowType.field("k4", BIGINT))))) + .isEqualTo(ImmutableList.of(ImmutableList.of(1L, 2L, 3L, 4L))); // boolean - assertFunction("CAST(JSON '[true, false, 12, 0, 12.3, 0.0, \"true\", \"false\", null]' AS ROW(BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN))", - RowType.anonymous(ImmutableList.of(BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN)), - asList(true, false, true, false, true, false, true, false, null)); - - assertFunction("CAST(JSON '{\"k1\": true, \"k2\": false, \"k3\": 12, \"k4\": 0, \"k5\": 12.3, \"k6\": 0.0, \"k7\": \"true\", \"k8\": \"false\", \"k9\": null}' AS ROW(k1 BOOLEAN, k2 BOOLEAN, k3 BOOLEAN, k4 BOOLEAN, k5 BOOLEAN, k6 BOOLEAN, k7 BOOLEAN, k8 BOOLEAN, k9 BOOLEAN))", - RowType.from(ImmutableList.of( + assertThat(assertions.expression("CAST(a as ROW(BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN))") + .binding("a", "JSON '[true, false, 12, 0, 12.3, 0.0, \"true\", \"false\", null]'")) + .hasType(RowType.anonymous(ImmutableList.of(BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN, BOOLEAN))) + .isEqualTo(asList(true, false, true, false, true, false, true, false, null)); + + assertThat(assertions.expression("CAST(a as ROW(k1 BOOLEAN, k2 BOOLEAN, k3 BOOLEAN, k4 BOOLEAN, k5 BOOLEAN, k6 BOOLEAN, k7 BOOLEAN, k8 BOOLEAN, k9 BOOLEAN))") + .binding("a", "JSON '{\"k1\": true, \"k2\": false, \"k3\": 12, \"k4\": 0, \"k5\": 12.3, \"k6\": 0.0, \"k7\": \"true\", \"k8\": \"false\", \"k9\": null}'")) + .hasType(RowType.from(ImmutableList.of( RowType.field("k1", BOOLEAN), RowType.field("k2", BOOLEAN), RowType.field("k3", BOOLEAN), @@ -233,23 +321,21 @@ public void testJsonToRow() RowType.field("k6", BOOLEAN), RowType.field("k7", BOOLEAN), RowType.field("k8", BOOLEAN), - RowType.field("k9", BOOLEAN))), - asList(true, false, true, false, true, false, true, false, null)); + RowType.field("k9", BOOLEAN)))) + .isEqualTo(asList(true, false, true, false, true, false, true, false, null)); // tinyint, smallint, integer, bigint - assertFunction( - "CAST(JSON '[12,12345,123456789,1234567890123456789,null,null,null,null]' AS ROW(TINYINT, SMALLINT, INTEGER, BIGINT, TINYINT, SMALLINT, INTEGER, BIGINT))", - RowType.anonymous(ImmutableList.of(TINYINT, SMALLINT, INTEGER, BIGINT, TINYINT, SMALLINT, INTEGER, BIGINT)), - asList((byte) 12, (short) 12345, 123456789, 1234567890123456789L, null, null, null, null)); + assertThat(assertions.expression("CAST(a as ROW(TINYINT, SMALLINT, INTEGER, BIGINT, TINYINT, SMALLINT, INTEGER, BIGINT))") + .binding("a", "JSON '[12,12345,123456789,1234567890123456789,null,null,null,null]'")) + .hasType(RowType.anonymous(ImmutableList.of(TINYINT, SMALLINT, INTEGER, BIGINT, TINYINT, SMALLINT, INTEGER, BIGINT))) + .isEqualTo(asList((byte) 12, (short) 12345, 123456789, 1234567890123456789L, null, null, null, null)); - assertFunction( - "CAST(JSON '{\"tinyint_value\": 12, \"tinyint_null\":null, " + + assertThat(assertions.expression("CAST(a AS ROW(tinyint_value TINYINT, smallint_value SMALLINT, integer_value INTEGER, bigint_value BIGINT, tinyint_null TINYINT, smallint_null SMALLINT, integer_null INTEGER, bigint_null BIGINT))") + .binding("a", "JSON '{\"tinyint_value\": 12, \"tinyint_null\":null, " + "\"smallint_value\":12345, \"smallint_null\":null, " + " \"integer_value\":123456789, \"integer_null\": null, " + - "\"bigint_value\":1234567890123456789, \"bigint_null\": null}' " + - "AS ROW(tinyint_value TINYINT, smallint_value SMALLINT, integer_value INTEGER, bigint_value BIGINT, " + - "tinyint_null TINYINT, smallint_null SMALLINT, integer_null INTEGER, bigint_null BIGINT))", - RowType.from(ImmutableList.of( + "\"bigint_value\":1234567890123456789, \"bigint_null\": null}'")) + .hasType(RowType.from(ImmutableList.of( RowType.field("tinyint_value", TINYINT), RowType.field("smallint_value", SMALLINT), RowType.field("integer_value", INTEGER), @@ -257,14 +343,14 @@ public void testJsonToRow() RowType.field("tinyint_null", TINYINT), RowType.field("smallint_null", SMALLINT), RowType.field("integer_null", INTEGER), - RowType.field("bigint_null", BIGINT))), - asList((byte) 12, (short) 12345, 123456789, 1234567890123456789L, null, null, null, null)); + RowType.field("bigint_null", BIGINT)))) + .isEqualTo(asList((byte) 12, (short) 12345, 123456789, 1234567890123456789L, null, null, null, null)); // real, double, decimal - assertFunction( - "CAST(JSON '[12345.67,1234567890.1,123.456,12345678.12345678,null,null,null]' AS ROW(REAL, DOUBLE, DECIMAL(10, 5), DECIMAL(38, 8), REAL, DOUBLE, DECIMAL(7, 7)))", - RowType.anonymous(ImmutableList.of(REAL, DOUBLE, createDecimalType(10, 5), createDecimalType(38, 8), REAL, DOUBLE, createDecimalType(7, 7))), - asList( + assertThat(assertions.expression("CAST(a as ROW(REAL, DOUBLE, DECIMAL(10, 5), DECIMAL(38, 8), REAL, DOUBLE, DECIMAL(7, 7)))") + .binding("a", "JSON '[12345.67,1234567890.1,123.456,12345678.12345678,null,null,null]'")) + .hasType(RowType.anonymous(ImmutableList.of(REAL, DOUBLE, createDecimalType(10, 5), createDecimalType(38, 8), REAL, DOUBLE, createDecimalType(7, 7)))) + .isEqualTo(asList( 12345.67f, 1234567890.1, decimal("123.45600", createDecimalType(10, 5)), @@ -273,22 +359,20 @@ public void testJsonToRow() null, null)); - assertFunction( - "CAST(JSON '{" + + assertThat(assertions.expression("CAST(a AS ROW(real_value REAL, double_value DOUBLE, decimal_value1 DECIMAL(10, 5), decimal_value2 DECIMAL(38, 8), real_null REAL, double_null DOUBLE, decimal_null DECIMAL(7, 7)))") + .binding("a", "JSON '{" + "\"real_value\": 12345.67, \"real_null\": null, " + "\"double_value\": 1234567890.1, \"double_null\": null, " + - "\"decimal_value1\": 123.456, \"decimal_value2\": 12345678.12345678, \"decimal_null\": null}' " + - "AS ROW(real_value REAL, double_value DOUBLE, decimal_value1 DECIMAL(10, 5), decimal_value2 DECIMAL(38, 8), " + - "real_null REAL, double_null DOUBLE, decimal_null DECIMAL(7, 7)))", - RowType.from(ImmutableList.of( + "\"decimal_value1\": 123.456, \"decimal_value2\": 12345678.12345678, \"decimal_null\": null}'")) + .hasType(RowType.from(ImmutableList.of( RowType.field("real_value", REAL), RowType.field("double_value", DOUBLE), RowType.field("decimal_value1", createDecimalType(10, 5)), RowType.field("decimal_value2", createDecimalType(38, 8)), RowType.field("real_null", REAL), RowType.field("double_null", DOUBLE), - RowType.field("decimal_null", createDecimalType(7, 7)))), - asList( + RowType.field("decimal_null", createDecimalType(7, 7))))) + .isEqualTo(asList( 12345.67f, 1234567890.1, decimal("123.45600", createDecimalType(10, 5)), @@ -298,32 +382,28 @@ public void testJsonToRow() null)); // varchar, json - assertFunction( - "CAST(JSON '[\"puppies\", [1, 2, 3], null, null]' AS ROW(VARCHAR, JSON, VARCHAR, JSON))", - RowType.anonymous(ImmutableList.of(VARCHAR, JSON, VARCHAR, JSON)), - asList("puppies", "[1,2,3]", null, "null")); - - assertFunction( - "CAST(JSON '{\"varchar_value\": \"puppies\", \"json_value_field\": [1, 2, 3], \"varchar_null\": null, \"json_null\": null}' " + - "AS ROW(varchar_value VARCHAR, json_value_field JSON, varchar_null VARCHAR, json_null JSON))", - RowType.from(ImmutableList.of( + assertThat(assertions.expression("CAST(a as ROW(VARCHAR, JSON, VARCHAR, JSON))") + .binding("a", "JSON '[\"puppies\", [1, 2, 3], null, null]'")) + .hasType(RowType.anonymous(ImmutableList.of(VARCHAR, JSON, VARCHAR, JSON))) + .isEqualTo(asList("puppies", "[1,2,3]", null, "null")); + + assertThat(assertions.expression("CAST(a AS ROW(varchar_value VARCHAR, json_value_field JSON, varchar_null VARCHAR, json_null JSON))") + .binding("a", "JSON '{\"varchar_value\": \"puppies\", \"json_value_field\": [1, 2, 3], \"varchar_null\": null, \"json_null\": null}'")) + .hasType(RowType.from(ImmutableList.of( RowType.field("varchar_value", VARCHAR), RowType.field("json_value_field", JSON), RowType.field("varchar_null", VARCHAR), - RowType.field("json_null", JSON))), - asList("puppies", "[1,2,3]", null, "null")); + RowType.field("json_null", JSON)))) + .isEqualTo(asList("puppies", "[1,2,3]", null, "null")); // nested array/map/row - assertFunction("CAST(JSON '[" + + assertThat(assertions.expression("CAST(a AS ROW(ARRAY(BIGINT), ARRAY(BIGINT), ARRAY(BIGINT), MAP(VARCHAR, BIGINT), MAP(VARCHAR, BIGINT), MAP(VARCHAR, BIGINT), ROW(BIGINT, BIGINT, BIGINT, BIGINT), ROW(BIGINT),ROW(a BIGINT, b BIGINT, three BIGINT, none BIGINT), ROW(nothing BIGINT)))") + .binding("a", "JSON '[" + "[1, 2, null, 3], [], null, " + "{\"a\": 1, \"b\": 2, \"none\": null, \"three\": 3}, {}, null, " + "[1, 2, null, 3], null, " + - "{\"a\": 1, \"b\": 2, \"none\": null, \"three\": 3}, null]' " + - "AS ROW(ARRAY(BIGINT), ARRAY(BIGINT), ARRAY(BIGINT), " + - "MAP(VARCHAR, BIGINT), MAP(VARCHAR, BIGINT), MAP(VARCHAR, BIGINT), " + - "ROW(BIGINT, BIGINT, BIGINT, BIGINT), ROW(BIGINT)," + - "ROW(a BIGINT, b BIGINT, three BIGINT, none BIGINT), ROW(nothing BIGINT)))", - RowType.anonymous( + "{\"a\": 1, \"b\": 2, \"none\": null, \"three\": 3}, null]' ")) + .hasType(RowType.anonymous( ImmutableList.of( new ArrayType(BIGINT), new ArrayType(BIGINT), new ArrayType(BIGINT), mapType(VARCHAR, BIGINT), mapType(VARCHAR, BIGINT), mapType(VARCHAR, BIGINT), @@ -333,14 +413,18 @@ public void testJsonToRow() RowType.field("b", BIGINT), RowType.field("three", BIGINT), RowType.field("none", BIGINT))), - RowType.from(ImmutableList.of(RowType.field("nothing", BIGINT))))), - asList( + RowType.from(ImmutableList.of(RowType.field("nothing", BIGINT)))))) + .isEqualTo(asList( asList(1L, 2L, null, 3L), emptyList(), null, asMap(ImmutableList.of("a", "b", "none", "three"), asList(1L, 2L, null, 3L)), ImmutableMap.of(), null, asList(1L, 2L, null, 3L), null, asList(1L, 2L, 3L, null), null)); - assertFunction("CAST(JSON '{" + + assertThat(assertions.expression("CAST(a AS ROW(array1 ARRAY(BIGINT), array2 ARRAY(BIGINT), array3 ARRAY(BIGINT), " + + "map1 MAP(VARCHAR, BIGINT), map2 MAP(VARCHAR, BIGINT), map3 MAP(VARCHAR, BIGINT), " + + "rowAsJsonArray1 ROW(BIGINT, BIGINT, BIGINT, BIGINT), rowAsJsonArray2 ROW(BIGINT)," + + "rowAsJsonObject1 ROW(nothing BIGINT), rowAsJsonObject2 ROW(a BIGINT, b BIGINT, three BIGINT, none BIGINT)))") + .binding("a", "JSON '{" + "\"array2\": [1, 2, null, 3], " + "\"array1\": [], " + "\"array3\": null, " + @@ -350,12 +434,8 @@ public void testJsonToRow() "\"rowAsJsonArray1\": [1, 2, null, 3], " + "\"rowAsJsonArray2\": null, " + "\"rowAsJsonObject2\": {\"a\": 1, \"b\": 2, \"none\": null, \"three\": 3}, " + - "\"rowAsJsonObject1\": null}' " + - "AS ROW(array1 ARRAY(BIGINT), array2 ARRAY(BIGINT), array3 ARRAY(BIGINT), " + - "map1 MAP(VARCHAR, BIGINT), map2 MAP(VARCHAR, BIGINT), map3 MAP(VARCHAR, BIGINT), " + - "rowAsJsonArray1 ROW(BIGINT, BIGINT, BIGINT, BIGINT), rowAsJsonArray2 ROW(BIGINT)," + - "rowAsJsonObject1 ROW(nothing BIGINT), rowAsJsonObject2 ROW(a BIGINT, b BIGINT, three BIGINT, none BIGINT)))", - RowType.from(ImmutableList.of( + "\"rowAsJsonObject1\": null}' ")) + .hasType(RowType.from(ImmutableList.of( RowType.field("array1", new ArrayType(BIGINT)), RowType.field("array2", new ArrayType(BIGINT)), RowType.field("array3", new ArrayType(BIGINT)), @@ -369,60 +449,145 @@ public void testJsonToRow() RowType.field("a", BIGINT), RowType.field("b", BIGINT), RowType.field("three", BIGINT), - RowType.field("none", BIGINT)))))), - asList( + RowType.field("none", BIGINT))))))) + .isEqualTo(asList( emptyList(), asList(1L, 2L, null, 3L), null, ImmutableMap.of(), null, asMap(ImmutableList.of("a", "b", "none", "three"), asList(1L, 2L, null, 3L)), asList(1L, 2L, null, 3L), null, null, asList(1L, 2L, 3L, null))); // invalid cast - assertInvalidCast("CAST(unchecked_to_json('{\"a\":1,\"b\":2,\"a\":3}') AS ROW(a BIGINT, b BIGINT))", "Cannot cast to row(a bigint, b bigint). Duplicate field: a\n{\"a\":1,\"b\":2,\"a\":3}"); - assertInvalidCast("CAST(unchecked_to_json('[{\"a\":1,\"b\":2,\"a\":3}]') AS ARRAY(ROW(a BIGINT, b BIGINT)))", "Cannot cast to array(row(a bigint, b bigint)). Duplicate field: a\n[{\"a\":1,\"b\":2,\"a\":3}]"); + assertTrinoExceptionThrownBy(() -> assertions.expression("CAST(a as ROW(a BIGINT, b BIGINT))") + .binding("a", "unchecked_to_json('{\"a\":1,\"b\":2,\"a\":3}')") + .evaluate()) + .hasMessage("Cannot cast to row(a bigint, b bigint). Duplicate field: a\n{\"a\":1,\"b\":2,\"a\":3}") + .hasErrorCode(INVALID_CAST_ARGUMENT); + + assertTrinoExceptionThrownBy(() -> assertions.expression("CAST(a as ARRAY(ROW(a BIGINT, b BIGINT)))") + .binding("a", "unchecked_to_json('[{\"a\":1,\"b\":2,\"a\":3}]')") + .evaluate()) + .hasMessage("Cannot cast to array(row(a bigint, b bigint)). Duplicate field: a\n[{\"a\":1,\"b\":2,\"a\":3}]") + .hasErrorCode(INVALID_CAST_ARGUMENT); } @Test public void testFieldAccessor() { - assertFunction("row(1, CAST(NULL AS DOUBLE))[2]", DOUBLE, null); - assertFunction("row(TRUE, CAST(NULL AS BOOLEAN))[2]", BOOLEAN, null); - assertFunction("row(TRUE, CAST(NULL AS ARRAY(INTEGER)))[2]", new ArrayType(INTEGER), null); - assertFunction("row(1.0E0, CAST(NULL AS VARCHAR))[2]", createUnboundedVarcharType(), null); - assertFunction("row(1, 2)[1]", INTEGER, 1); - assertFunction("row(1, 'kittens')[2]", createVarcharType(7), "kittens"); - assertFunction("row(1, 2)[2]", INTEGER, 2); - assertFunction("array[row(1, 2)][1][2]", INTEGER, 2); - assertFunction("row(FALSE, ARRAY [1, 2], MAP(ARRAY[1, 3], ARRAY[2.0E0, 4.0E0]))[2]", new ArrayType(INTEGER), ImmutableList.of(1, 2)); - assertFunction("row(FALSE, ARRAY [1, 2], MAP(ARRAY[1, 3], ARRAY[2.0E0, 4.0E0]))[3]", mapType(INTEGER, DOUBLE), ImmutableMap.of(1, 2.0, 3, 4.0)); - assertFunction("row(1.0E0, ARRAY[row(31, 4.1E0), row(32, 4.2E0)], row(3, 4.0E0))[2][2][1]", INTEGER, 32); + assertThat(assertions.expression("a[2]") + .binding("a", "row(1, CAST(NULL AS DOUBLE))")) + .isNull(DOUBLE); + + assertThat(assertions.expression("a[2]") + .binding("a", "row(TRUE, CAST(NULL AS BOOLEAN))")) + .isNull(BOOLEAN); + + assertThat(assertions.expression("a[2]") + .binding("a", "row(TRUE, CAST(NULL AS ARRAY(INTEGER)))")) + .isNull(new ArrayType(INTEGER)); + + assertThat(assertions.expression("a[2]") + .binding("a", "row(1.0E0, CAST(NULL AS VARCHAR))")) + .isNull(createUnboundedVarcharType()); + + assertThat(assertions.expression("a[1]") + .binding("a", "row(1, 2)")) + .isEqualTo(1); + + assertThat(assertions.expression("a[2]") + .binding("a", "row(1, 'kittens')")) + .hasType(createVarcharType(7)) + .isEqualTo("kittens"); + + assertThat(assertions.expression("a[2]") + .binding("a", "row(1, 2)")) + .isEqualTo(2); + + assertThat(assertions.expression("a[2]") + .binding("a", "row(FALSE, ARRAY [1, 2], MAP(ARRAY[1, 3], ARRAY[2.0E0, 4.0E0]))")) + .hasType(new ArrayType(INTEGER)) + .isEqualTo(ImmutableList.of(1, 2)); + + assertThat(assertions.expression("a[3]") + .binding("a", "row(FALSE, ARRAY [1, 2], MAP(ARRAY[1, 3], ARRAY[2.0E0, 4.0E0]))")) + .hasType(mapType(INTEGER, DOUBLE)) + .isEqualTo(ImmutableMap.of(1, 2.0, 3, 4.0)); + + assertThat(assertions.expression("a[2][2][1]") + .binding("a", "row(1.0E0, ARRAY[row(31, 4.1E0), row(32, 4.2E0)], row(3, 4.0E0))")) + .isEqualTo(32); // Using ROW constructor - assertFunction("CAST(ROW(1, 2) AS ROW(a BIGINT, b DOUBLE))[1]", BIGINT, 1L); - assertFunction("CAST(ROW(1, 2) AS ROW(a BIGINT, b DOUBLE))[2]", DOUBLE, 2.0); - assertFunction("ROW(ROW('aa'))[1][1]", createVarcharType(2), "aa"); - assertFunction("ROW(ROW('ab'))[1][1]", createVarcharType(2), "ab"); - assertFunction("CAST(ROW(ARRAY[NULL]) AS ROW(a ARRAY(BIGINT)))[1]", new ArrayType(BIGINT), asList((Integer) null)); - - assertDecimalFunction("CAST(row(1.0, 123123123456.6549876543) AS ROW(col0 decimal(2,1), col1 decimal(22,10)))[1]", decimal("1.0", createDecimalType(2, 1))); - assertDecimalFunction("CAST(row(1.0, 123123123456.6549876543) AS ROW(col0 decimal(2,1), col1 decimal(22,10)))[2]", decimal("123123123456.6549876543", createDecimalType(22, 10))); + assertThat(assertions.expression("a[1]") + .binding("a", "cast(ROW(1, 2) AS ROW(a BIGINT, b DOUBLE))")) + .isEqualTo(1L); + + assertThat(assertions.expression("a[2]") + .binding("a", "cast(ROW(1, 2) AS ROW(a BIGINT, b DOUBLE))")) + .isEqualTo(2.0); + + assertThat(assertions.expression("a[1][1]") + .binding("a", "row(row('aa'))")) + .hasType(createVarcharType(2)) + .isEqualTo("aa"); + + assertThat(assertions.expression("a[1][1]") + .binding("a", "row(row('ab'))")) + .hasType(createVarcharType(2)) + .isEqualTo("ab"); + + assertThat(assertions.expression("a[1]") + .binding("a", "cast(ROW(ARRAY[NULL]) AS ROW(a ARRAY(BIGINT)))")) + .hasType(new ArrayType(BIGINT)) + .isEqualTo(asList((Integer) null)); + + assertThat(assertions.expression("a[1]") + .binding("a", "cast(row(1.0, 123123123456.6549876543) AS ROW(col0 decimal(2,1), col1 decimal(22,10)))")) + .isEqualTo(decimal("1.0", createDecimalType(2, 1))); + + assertThat(assertions.expression("a[2]") + .binding("a", "cast(row(1.0, 123123123456.6549876543) AS ROW(col0 decimal(2,1), col1 decimal(22,10)))")) + .isEqualTo(decimal("123123123456.6549876543", createDecimalType(22, 10))); } @Test public void testRowCast() { - assertFunction("cast(row(2, 3) as row(aa bigint, bb bigint))[1]", BIGINT, 2L); - assertFunction("cast(row(2, 3) as row(aa bigint, bb bigint))[2]", BIGINT, 3L); - assertFunction("cast(row(2, 3) as row(aa bigint, bb boolean))[2]", BOOLEAN, true); - assertFunction("cast(row(2, cast(null as double)) as row(aa bigint, bb double))[2]", DOUBLE, null); - assertFunction("cast(row(2, 'test_str') as row(aa bigint, bb varchar))[2]", VARCHAR, "test_str"); + assertThat(assertions.expression("cast(a AS row(aa bigint, bb bigint))[1]") + .binding("a", "row(2, 3)")) + .isEqualTo(2L); + + assertThat(assertions.expression("cast(a AS row(aa bigint, bb bigint))[2]") + .binding("a", "row(2, 3)")) + .isEqualTo(3L); + + assertThat(assertions.expression("cast(a AS row(aa bigint, bb boolean))[2]") + .binding("a", "row(2, 3)")) + .isEqualTo(true); + + assertThat(assertions.expression("cast(a AS row(aa bigint, bb double))[2]") + .binding("a", "row(2, CAST(null as double))")) + .isNull(DOUBLE); + + assertThat(assertions.expression("cast(a AS row(aa bigint, bb varchar))[2]") + .binding("a", "row(2, 'test_str')")) + .hasType(VARCHAR) + .isEqualTo("test_str"); // ROW casting with NULLs - assertFunction("cast(row(1,null,3) as row(aa bigint, bb boolean, cc boolean))[2]", BOOLEAN, null); - assertFunction("cast(row(1,null,3) as row(aa bigint, bb boolean, cc boolean))[1]", BIGINT, 1L); - assertFunction("cast(row(null,null,null) as row(aa bigint, bb boolean, cc boolean))[1]", BIGINT, null); + assertThat(assertions.expression("cast(a AS row(aa bigint, bb boolean, cc boolean))[2]") + .binding("a", "row(1,null,3)")) + .isNull(BOOLEAN); + + assertThat(assertions.expression("cast(a AS row(aa bigint, bb boolean, cc boolean))[1]") + .binding("a", "row(1,null,3)")) + .isEqualTo(1L); + + assertThat(assertions.expression("cast(a AS row(aa bigint, bb boolean, cc boolean))[1]") + .binding("a", "row(null,null,null)")) + .isNull(BIGINT); // there are totally 7 field names - String longFieldNameCast = "CAST(row(1.2E0, ARRAY[row(233, 6.9E0)], row(1000, 6.3E0)) AS ROW(%s VARCHAR, %s ARRAY(ROW(%s VARCHAR, %s VARCHAR)), %s ROW(%s VARCHAR, %s VARCHAR)))[2][1][1]"; + String longFieldNameCast = "CAST(a AS ROW(%s VARCHAR, %s ARRAY(ROW(%s VARCHAR, %s VARCHAR)), %s ROW(%s VARCHAR, %s VARCHAR)))[2][1][1]"; int fieldCount = 7; char[] chars = new char[9333]; String[] fields = new String[fieldCount]; @@ -430,47 +595,77 @@ public void testRowCast() Arrays.fill(chars, (char) ('a' + i)); fields[i] = new String(chars); } - assertFunction(format(longFieldNameCast, fields[0], fields[1], fields[2], fields[3], fields[4], fields[5], fields[6]), VARCHAR, "233"); - - assertFunction( - "cast(row(json '2', json '1.5', json 'true', json '\"abc\"', json '[1, 2]') as row(a BIGINT, b DOUBLE, c BOOLEAN, d VARCHAR, e ARRAY(BIGINT)))", - RowType.from(ImmutableList.of( + assertThat(assertions.expression(format(longFieldNameCast, fields[0], fields[1], fields[2], fields[3], fields[4], fields[5], fields[6])) + .binding("a", "row(1.2E0, ARRAY[row(233, 6.9E0)], row(1000, 6.3E0))")) + .hasType(VARCHAR) + .isEqualTo("233"); + + assertThat(assertions.expression("CAST(a as row(a BIGINT, b DOUBLE, c BOOLEAN, d VARCHAR, e ARRAY(BIGINT)))") + .binding("a", "row(json '2', json '1.5', json 'true', json '\"abc\"', json '[1, 2]')")) + .hasType(RowType.from(ImmutableList.of( RowType.field("a", BIGINT), RowType.field("b", DOUBLE), RowType.field("c", BOOLEAN), RowType.field("d", VARCHAR), - RowType.field("e", new ArrayType(BIGINT)))), - asList(2L, 1.5, true, "abc", ImmutableList.of(1L, 2L))); + RowType.field("e", new ArrayType(BIGINT))))) + .isEqualTo(asList(2L, 1.5, true, "abc", ImmutableList.of(1L, 2L))); } @Test public void testIsDistinctFrom() { - assertFunction("CAST(NULL AS ROW(UNKNOWN)) IS DISTINCT FROM CAST(NULL AS ROW(UNKNOWN))", BOOLEAN, false); - assertFunction("row(NULL) IS DISTINCT FROM row(NULL)", BOOLEAN, false); - assertFunction("row(1, 'cat') IS DISTINCT FROM row(1, 'cat')", BOOLEAN, false); - assertFunction("row(1, ARRAY [1]) IS DISTINCT FROM row(1, ARRAY [1])", BOOLEAN, false); - assertFunction("row(1, ARRAY [1, 2]) IS DISTINCT FROM row(1, ARRAY [1, NULL])", BOOLEAN, true); - assertFunction("row(1, 2.0E0, TRUE, 'cat', from_unixtime(1)) IS DISTINCT FROM row(1, 2.0E0, TRUE, 'cat', from_unixtime(1))", BOOLEAN, false); - assertFunction("row(1, 2.0E0, TRUE, 'cat', from_unixtime(1)) IS DISTINCT FROM row(1, 2.0E0, TRUE, 'cat', from_unixtime(2))", BOOLEAN, true); - assertFunction("row(1, 2.0E0, TRUE, 'cat', CAST(NULL AS INTEGER)) IS DISTINCT FROM row(1, 2.0E0, TRUE, 'cat', 2)", BOOLEAN, true); - assertFunction("row(1, 2.0E0, TRUE, 'cat', CAST(NULL AS INTEGER)) IS DISTINCT FROM row(1, 2.0E0, TRUE, 'cat', CAST(NULL AS INTEGER))", BOOLEAN, false); - assertFunction("row(1, 2.0E0, TRUE, 'cat') IS DISTINCT FROM row(1, 2.0E0, TRUE, CAST(NULL AS VARCHAR(3)))", BOOLEAN, true); - assertFunction("row(1, 2.0E0, TRUE, CAST(NULL AS VARCHAR(3))) IS DISTINCT FROM row(1, 2.0E0, TRUE, CAST(NULL AS VARCHAR(3)))", BOOLEAN, false); - assertFunction("ARRAY[ROW(1)] IS DISTINCT FROM ARRAY[ROW(1)]", BOOLEAN, false); + assertThat(assertions.operator(IS_DISTINCT_FROM, "CAST(NULL AS ROW(UNKNOWN))", "CAST(NULL AS ROW(UNKNOWN))")) + .isEqualTo(false); + + assertThat(assertions.operator(IS_DISTINCT_FROM, "row(NULL)", "row(NULL)")) + .isEqualTo(false); + + assertThat(assertions.operator(IS_DISTINCT_FROM, "row(1, 'cat')", "row(1, 'cat')")) + .isEqualTo(false); + + assertThat(assertions.operator(IS_DISTINCT_FROM, "row(1, ARRAY [1])", "row(1, ARRAY [1])")) + .isEqualTo(false); + + assertThat(assertions.operator(IS_DISTINCT_FROM, "row(1, ARRAY [1, 2])", "row(1, ARRAY [1, NULL])")) + .isEqualTo(true); + + assertThat(assertions.operator(IS_DISTINCT_FROM, "row(1, 2.0E0, TRUE, 'cat', from_unixtime(1))", "row(1, 2.0E0, TRUE, 'cat', from_unixtime(1))")) + .isEqualTo(false); + + assertThat(assertions.operator(IS_DISTINCT_FROM, "row(1, 2.0E0, TRUE, 'cat', from_unixtime(1))", "row(1, 2.0E0, TRUE, 'cat', from_unixtime(2))")) + .isEqualTo(true); + + assertThat(assertions.operator(IS_DISTINCT_FROM, "row(1, 2.0E0, TRUE, 'cat', CAST(NULL AS INTEGER))", "row(1, 2.0E0, TRUE, 'cat', 2)")) + .isEqualTo(true); + + assertThat(assertions.operator(IS_DISTINCT_FROM, "row(1, 2.0E0, TRUE, 'cat', CAST(NULL AS INTEGER))", "row(1, 2.0E0, TRUE, 'cat', CAST(NULL AS INTEGER))")) + .isEqualTo(false); + + assertThat(assertions.operator(IS_DISTINCT_FROM, "row(1, 2.0E0, TRUE, 'cat')", "row(1, 2.0E0, TRUE, CAST(NULL AS VARCHAR(3)))")) + .isEqualTo(true); + + assertThat(assertions.operator(IS_DISTINCT_FROM, "row(1, 2.0E0, TRUE, CAST(NULL AS VARCHAR(3)))", "row(1, 2.0E0, TRUE, CAST(NULL AS VARCHAR(3)))")) + .isEqualTo(false); + + assertThat(assertions.operator(IS_DISTINCT_FROM, "ARRAY[ROW(1)]", "ARRAY[ROW(1)]")) + .isEqualTo(false); } @Test public void testRowComparison() { - assertFunction("row(TIMESTAMP '2002-01-02 03:04:05.321 +08:10', TIMESTAMP '2002-01-02 03:04:05.321 +08:10') = " + - "row(TIMESTAMP '2002-01-02 02:04:05.321 +07:10', TIMESTAMP '2002-01-02 03:05:05.321 +08:11')", BOOLEAN, true); - assertFunction("row(1.0E0, row(TIMESTAMP '2001-01-02 03:04:05.321 +07:09', TIMESTAMP '2001-01-02 03:04:05.321 +07:10')) = " + - "row(1.0E0, row(TIMESTAMP '2001-01-02 03:04:05.321 +07:09', TIMESTAMP '2001-01-02 03:04:05.321 +07:11'))", BOOLEAN, false); + assertThat(assertions.operator(EQUAL, "row(TIMESTAMP '2002-01-02 03:04:05.321 +08:10', TIMESTAMP '2002-01-02 03:04:05.321 +08:10')", "row(TIMESTAMP '2002-01-02 02:04:05.321 +07:10', TIMESTAMP '2002-01-02 03:05:05.321 +08:11')")) + .isEqualTo(true); + + assertThat(assertions.operator(EQUAL, "row(1.0E0, row(TIMESTAMP '2001-01-02 03:04:05.321 +07:09', TIMESTAMP '2001-01-02 03:04:05.321 +07:10'))", "row(1.0E0, row(TIMESTAMP '2001-01-02 03:04:05.321 +07:09', TIMESTAMP '2001-01-02 03:04:05.321 +07:11'))")) + .isEqualTo(false); - assertComparisonCombination("row(TIMESTAMP '2001-01-02 03:04:05.321 +07:09', TIMESTAMP '2001-01-02 03:04:05.321 +07:10')", + assertComparisonCombination( + "row(TIMESTAMP '2001-01-02 03:04:05.321 +07:09', TIMESTAMP '2001-01-02 03:04:05.321 +07:10')", "row(TIMESTAMP '2002-01-02 03:04:05.321 +07:09', TIMESTAMP '2002-01-02 03:04:05.321 +07:09')"); - assertComparisonCombination("row(1.0E0, row(TIMESTAMP '2001-01-02 03:04:05.321 +07:09', TIMESTAMP '2001-01-02 03:04:05.321 +07:10'))", + + assertComparisonCombination( + "row(1.0E0, row(TIMESTAMP '2001-01-02 03:04:05.321 +07:09', TIMESTAMP '2001-01-02 03:04:05.321 +07:10'))", "row(2.0E0, row(TIMESTAMP '2001-01-02 03:04:05.321 +07:09', TIMESTAMP '2001-01-02 03:04:05.321 +07:10'))"); assertComparisonCombination("row(1.0E0, 'kittens')", "row(1.0E0, 'puppies')"); @@ -478,38 +673,78 @@ public void testRowComparison() assertComparisonCombination("row(TRUE, FALSE, TRUE, FALSE)", "row(TRUE, TRUE, TRUE, FALSE)"); assertComparisonCombination("row(1, 2.0E0, TRUE, 'kittens', from_unixtime(1))", "row(1, 3.0E0, TRUE, 'kittens', from_unixtime(1))"); - assertInvalidFunction("cast(row(cast(cast ('' as varbinary) as hyperloglog)) as row(col0 hyperloglog)) = cast(row(cast(cast ('' as varbinary) as hyperloglog)) as row(col0 hyperloglog))", - TYPE_MISMATCH, "line 1:81: Cannot apply operator: row(col0 HyperLogLog) = row(col0 HyperLogLog)"); - assertInvalidFunction("cast(row(cast(cast ('' as varbinary) as hyperloglog)) as row(col0 hyperloglog)) > cast(row(cast(cast ('' as varbinary) as hyperloglog)) as row(col0 hyperloglog))", - TYPE_MISMATCH, "line 1:81: Cannot apply operator: row(col0 HyperLogLog) < row(col0 HyperLogLog)"); + assertTrinoExceptionThrownBy(() -> assertions.expression("CAST(row(CAST(CAST('' as varbinary) as hyperloglog)) as row(col0 hyperloglog)) = CAST(row(CAST(CAST('' as varbinary) as hyperloglog)) as row(col0 hyperloglog))").evaluate()) + .hasErrorCode(TYPE_MISMATCH) + .hasMessage("line 1:91: Cannot apply operator: row(col0 HyperLogLog) = row(col0 HyperLogLog)"); + + assertTrinoExceptionThrownBy(() -> assertions.expression("CAST(row(CAST(CAST('' as varbinary) as hyperloglog)) as row(col0 hyperloglog)) > CAST(row(CAST(CAST('' as varbinary) as hyperloglog)) as row(col0 hyperloglog))").evaluate()) + .hasErrorCode(TYPE_MISMATCH) + .hasMessage("line 1:91: Cannot apply operator: row(col0 HyperLogLog) < row(col0 HyperLogLog)"); + + assertTrinoExceptionThrownBy(() -> assertions.expression("CAST(row(CAST(CAST('' as varbinary) as qdigest(double))) as row(col0 qdigest(double))) = CAST(row(CAST(CAST('' as varbinary) as qdigest(double))) as row(col0 qdigest(double)))").evaluate()) + .hasErrorCode(TYPE_MISMATCH) + .hasMessage("line 1:99: Cannot apply operator: row(col0 qdigest(double)) = row(col0 qdigest(double))"); - assertInvalidFunction("cast(row(cast(cast ('' as varbinary) as qdigest(double))) as row(col0 qdigest(double))) = cast(row(cast(cast ('' as varbinary) as qdigest(double))) as row(col0 qdigest(double)))", - TYPE_MISMATCH, "line 1:89: Cannot apply operator: row(col0 qdigest(double)) = row(col0 qdigest(double))"); - assertInvalidFunction("cast(row(cast(cast ('' as varbinary) as qdigest(double))) as row(col0 qdigest(double))) > cast(row(cast(cast ('' as varbinary) as qdigest(double))) as row(col0 qdigest(double)))", - TYPE_MISMATCH, "line 1:89: Cannot apply operator: row(col0 qdigest(double)) < row(col0 qdigest(double))"); + assertTrinoExceptionThrownBy(() -> assertions.expression("CAST(row(CAST(CAST('' as varbinary) as qdigest(double))) as row(col0 qdigest(double))) > CAST(row(CAST(CAST('' as varbinary) as qdigest(double))) as row(col0 qdigest(double)))").evaluate()) + .hasErrorCode(TYPE_MISMATCH) + .hasMessage("line 1:99: Cannot apply operator: row(col0 qdigest(double)) < row(col0 qdigest(double))"); - assertFunction("row(TRUE, ARRAY [1], MAP(ARRAY[1, 3], ARRAY[2.0E0, 4.0E0])) = row(TRUE, ARRAY [1, 2], MAP(ARRAY[1, 3], ARRAY[2.0E0, 4.0E0]))", BOOLEAN, false); - assertFunction("row(TRUE, ARRAY [1, 2], MAP(ARRAY[1, 3], ARRAY[2.0E0, 4.0E0])) = row(TRUE, ARRAY [1, 2], MAP(ARRAY[1, 3], ARRAY[2.0E0, 4.0E0]))", BOOLEAN, true); + assertThat(assertions.operator(EQUAL, "row(TRUE, ARRAY [1], MAP(ARRAY[1, 3], ARRAY[2.0E0, 4.0E0]))", "row(TRUE, ARRAY [1, 2], MAP(ARRAY[1, 3], ARRAY[2.0E0, 4.0E0]))")) + .isEqualTo(false); - assertFunction("row(1, CAST(NULL AS INTEGER)) = row(1, 2)", BOOLEAN, null); - assertFunction("row(1, CAST(NULL AS INTEGER)) != row(1, 2)", BOOLEAN, null); - assertFunction("row(2, CAST(NULL AS INTEGER)) = row(1, 2)", BOOLEAN, false); - assertFunction("row(2, CAST(NULL AS INTEGER)) != row(1, 2)", BOOLEAN, true); - assertInvalidFunction("row(TRUE, ARRAY [1, 2], MAP(ARRAY[1, 3], ARRAY[2.0E0, 4.0E0])) > row(TRUE, ARRAY [1, 2], MAP(ARRAY[1, 3], ARRAY[2.0E0, 4.0E0]))", - TYPE_MISMATCH, "line 1:64: Cannot apply operator: row(boolean, array(integer), map(integer, double)) < row(boolean, array(integer), map(integer, double))"); + assertThat(assertions.operator(EQUAL, "row(TRUE, ARRAY [1, 2], MAP(ARRAY[1, 3], ARRAY[2.0E0, 4.0E0]))", "row(TRUE, ARRAY [1, 2], MAP(ARRAY[1, 3], ARRAY[2.0E0, 4.0E0]))")) + .isEqualTo(true); - assertInvalidFunction("row(1, CAST(NULL AS INTEGER)) < row(1, 2)", StandardErrorCode.NOT_SUPPORTED); + assertThat(assertions.operator(EQUAL, "row(1, CAST(NULL AS INTEGER))", "row(1, 2)")) + .isNull(BOOLEAN); + + assertThat(assertions.expression("a != b") + .binding("a", "row(1, CAST(NULL AS INTEGER))") + .binding("b", "row(1, 2)")) + .isNull(BOOLEAN); + + assertThat(assertions.operator(EQUAL, "row(2, CAST(NULL AS INTEGER))", "row(1, 2)")) + .isEqualTo(false); + + assertThat(assertions.expression("a != b") + .binding("a", "row(2, CAST(NULL AS INTEGER))") + .binding("b", "row(1, 2)")) + .isEqualTo(true); + + assertTrinoExceptionThrownBy(() -> assertions.expression("row(TRUE, ARRAY [1, 2], MAP(ARRAY[1, 3], ARRAY[2.0E0, 4.0E0])) > row(TRUE, ARRAY [1, 2], MAP(ARRAY[1, 3], ARRAY[2.0E0, 4.0E0]))").evaluate()) + .hasErrorCode(TYPE_MISMATCH) + .hasMessage("line 1:75: Cannot apply operator: row(boolean, array(integer), map(integer, double)) < row(boolean, array(integer), map(integer, double))"); + + assertTrinoExceptionThrownBy(() -> assertions.expression("row(1, CAST(NULL AS INTEGER)) < row(1, 2)").evaluate()) + .hasErrorCode(StandardErrorCode.NOT_SUPPORTED); assertComparisonCombination("row(1.0E0, ARRAY [1,2,3], row(2, 2.0E0))", "row(1.0E0, ARRAY [1,3,3], row(2, 2.0E0))"); assertComparisonCombination("row(TRUE, ARRAY [1])", "row(TRUE, ARRAY [1, 2])"); assertComparisonCombination("ROW(1, 2)", "ROW(2, 1)"); - assertFunction("ROW(1, 2) = ROW(1, 2)", BOOLEAN, true); - assertFunction("ROW(2, 1) != ROW(1, 2)", BOOLEAN, true); - assertFunction("ROW(1.0, 123123123456.6549876543) = ROW(1.0, 123123123456.6549876543)", BOOLEAN, true); - assertFunction("ROW(1.0, 123123123456.6549876543) = ROW(1.0, 123123123456.6549876542)", BOOLEAN, false); - assertFunction("ROW(1.0, 123123123456.6549876543) != ROW(1.0, 123123123456.6549876543)", BOOLEAN, false); - assertFunction("ROW(1.0, 123123123456.6549876543) != ROW(1.0, 123123123456.6549876542)", BOOLEAN, true); + assertThat(assertions.operator(EQUAL, "ROW(1, 2)", "ROW(1, 2)")) + .isEqualTo(true); + + assertThat(assertions.expression("a != b") + .binding("a", "ROW(2, 1)") + .binding("b", "ROW(1, 2)")) + .isEqualTo(true); + + assertThat(assertions.operator(EQUAL, "ROW(1.0, 123123123456.6549876543)", "ROW(1.0, 123123123456.6549876543)")) + .isEqualTo(true); + + assertThat(assertions.operator(EQUAL, "ROW(1.0, 123123123456.6549876543)", "ROW(1.0, 123123123456.6549876542)")) + .isEqualTo(false); + + assertThat(assertions.expression("a != b") + .binding("a", "ROW(1.0, 123123123456.6549876543)") + .binding("b", "ROW(1.0, 123123123456.6549876543)")) + .isEqualTo(false); + + assertThat(assertions.expression("a != b") + .binding("a", "ROW(1.0, 123123123456.6549876543)") + .binding("b", "ROW(1.0, 123123123456.6549876542)")) + .isEqualTo(true); for (int i = 1; i < 128; i += 10) { assertEqualOperator(i); @@ -531,54 +766,137 @@ public void testRowHashOperator() @Test public void testIndeterminate() { - assertOperator(INDETERMINATE, "cast(null as row(col0 bigint))", BOOLEAN, true); - assertOperator(INDETERMINATE, "row(1)", BOOLEAN, false); - assertOperator(INDETERMINATE, "row(null)", BOOLEAN, true); - assertOperator(INDETERMINATE, "row(1,2)", BOOLEAN, false); - assertOperator(INDETERMINATE, "row(1,null)", BOOLEAN, true); - assertOperator(INDETERMINATE, "row(null,2)", BOOLEAN, true); - assertOperator(INDETERMINATE, "row(null,null)", BOOLEAN, true); - assertOperator(INDETERMINATE, "row('111',null)", BOOLEAN, true); - assertOperator(INDETERMINATE, "row(null,'222')", BOOLEAN, true); - assertOperator(INDETERMINATE, "row('111','222')", BOOLEAN, false); - assertOperator(INDETERMINATE, "row(row(1), row(2), row(3))", BOOLEAN, false); - assertOperator(INDETERMINATE, "row(row(1), row(null), row(3))", BOOLEAN, true); - assertOperator(INDETERMINATE, "row(row(1), row(cast(null as bigint)), row(3))", BOOLEAN, true); - assertOperator(INDETERMINATE, "row(row(row(1)), row(2), row(3))", BOOLEAN, false); - assertOperator(INDETERMINATE, "row(row(row(null)), row(2), row(3))", BOOLEAN, true); - assertOperator(INDETERMINATE, "row(row(row(cast(null as boolean))), row(2), row(3))", BOOLEAN, true); - assertOperator(INDETERMINATE, "row(row(1,2),row(array[3,4,5]))", BOOLEAN, false); - assertOperator(INDETERMINATE, "row(row(1,2),row(array[row(3,4)]))", BOOLEAN, false); - assertOperator(INDETERMINATE, "row(row(null,2),row(array[row(3,4)]))", BOOLEAN, true); - assertOperator(INDETERMINATE, "row(row(1,null),row(array[row(3,4)]))", BOOLEAN, true); - assertOperator(INDETERMINATE, "row(row(1,2),row(array[cast(row(3,4) as row(a integer, b integer)), cast(null as row(a integer, b integer))]))", BOOLEAN, true); - assertOperator(INDETERMINATE, "row(row(1,2),row(array[row(null,4)]))", BOOLEAN, true); - assertOperator(INDETERMINATE, "row(row(1,2),row(array[row(map(array[8], array[9]),4)]))", BOOLEAN, false); - assertOperator(INDETERMINATE, "row(row(1,2),row(array[row(map(array[8], array[null]),4)]))", BOOLEAN, true); - assertOperator(INDETERMINATE, "row(1E0,2E0)", BOOLEAN, false); - assertOperator(INDETERMINATE, "row(1E0,null)", BOOLEAN, true); - assertOperator(INDETERMINATE, "row(true,false)", BOOLEAN, false); - assertOperator(INDETERMINATE, "row(true,null)", BOOLEAN, true); + assertThat(assertions.operator(INDETERMINATE, "CAST(null as row(col0 bigint))")) + .isEqualTo(true); + + assertThat(assertions.operator(INDETERMINATE, "row(1)")) + .isEqualTo(false); + + assertThat(assertions.operator(INDETERMINATE, "row(null)")) + .isEqualTo(true); + + assertThat(assertions.operator(INDETERMINATE, "row(1,2)")) + .isEqualTo(false); + + assertThat(assertions.operator(INDETERMINATE, "row(1,null)")) + .isEqualTo(true); + + assertThat(assertions.operator(INDETERMINATE, "row(null,2)")) + .isEqualTo(true); + + assertThat(assertions.operator(INDETERMINATE, "row(null,null)")) + .isEqualTo(true); + + assertThat(assertions.operator(INDETERMINATE, "row('111',null)")) + .isEqualTo(true); + + assertThat(assertions.operator(INDETERMINATE, "row(null,'222')")) + .isEqualTo(true); + + assertThat(assertions.operator(INDETERMINATE, "row('111','222')")) + .isEqualTo(false); + + assertThat(assertions.operator(INDETERMINATE, "row(row(1), row(2), row(3))")) + .isEqualTo(false); + + assertThat(assertions.operator(INDETERMINATE, "row(row(1), row(null), row(3))")) + .isEqualTo(true); + + assertThat(assertions.operator(INDETERMINATE, "row(row(1), row(CAST(null as bigint)), row(3))")) + .isEqualTo(true); + + assertThat(assertions.operator(INDETERMINATE, "row(row(row(1)), row(2), row(3))")) + .isEqualTo(false); + + assertThat(assertions.operator(INDETERMINATE, "row(row(row(null)), row(2), row(3))")) + .isEqualTo(true); + + assertThat(assertions.operator(INDETERMINATE, "row(row(row(CAST(null as boolean))), row(2), row(3))")) + .isEqualTo(true); + + assertThat(assertions.operator(INDETERMINATE, "row(row(1,2),row(array[3,4,5]))")) + .isEqualTo(false); + + assertThat(assertions.operator(INDETERMINATE, "row(row(1,2),row(array[row(3,4)]))")) + .isEqualTo(false); + + assertThat(assertions.operator(INDETERMINATE, "row(row(null,2),row(array[row(3,4)]))")) + .isEqualTo(true); + + assertThat(assertions.operator(INDETERMINATE, "row(row(1,null),row(array[row(3,4)]))")) + .isEqualTo(true); + + assertThat(assertions.operator(INDETERMINATE, "row(row(1,2),row(array[CAST(row(3,4) as row(a integer, b integer)), CAST(null as row(a integer, b integer))]))")) + .isEqualTo(true); + + assertThat(assertions.operator(INDETERMINATE, "row(row(1,2),row(array[row(null,4)]))")) + .isEqualTo(true); + + assertThat(assertions.operator(INDETERMINATE, "row(row(1,2),row(array[row(map(array[8], array[9]),4)]))")) + .isEqualTo(false); + + assertThat(assertions.operator(INDETERMINATE, "row(row(1,2),row(array[row(map(array[8], array[null]),4)]))")) + .isEqualTo(true); + + assertThat(assertions.operator(INDETERMINATE, "row(1E0,2E0)")) + .isEqualTo(false); + + assertThat(assertions.operator(INDETERMINATE, "row(1E0,null)")) + .isEqualTo(true); + + assertThat(assertions.operator(INDETERMINATE, "row(true,false)")) + .isEqualTo(false); + + assertThat(assertions.operator(INDETERMINATE, "row(true,null)")) + .isEqualTo(true); } private void assertEqualOperator(int fieldCount) { String rowLiteral = toRowLiteral(largeRow(fieldCount, false)); - assertFunction(rowLiteral + " = " + rowLiteral, BOOLEAN, true); - assertFunction(rowLiteral + " != " + rowLiteral, BOOLEAN, false); + assertThat(assertions.expression("a = b") + .binding("a", rowLiteral) + .binding("b", rowLiteral)) + .isEqualTo(true); + + assertThat(assertions.expression("a != b") + .binding("a", rowLiteral) + .binding("b", rowLiteral)) + .isEqualTo(false); String alternateRowLiteral = toRowLiteral(largeRow(fieldCount, false, true)); - assertFunction(rowLiteral + " = " + alternateRowLiteral, BOOLEAN, false); - assertFunction(rowLiteral + " != " + alternateRowLiteral, BOOLEAN, true); + assertThat(assertions.expression("a = b") + .binding("a", rowLiteral) + .binding("b", alternateRowLiteral)) + .isEqualTo(false); + + assertThat(assertions.expression("a != b") + .binding("a", rowLiteral) + .binding("b", alternateRowLiteral)) + .isEqualTo(true); if (fieldCount > 1) { String rowLiteralWithNulls = toRowLiteral(largeRow(fieldCount, true)); - assertFunction(rowLiteralWithNulls + " = " + rowLiteralWithNulls, BOOLEAN, null); - assertFunction(rowLiteralWithNulls + " != " + rowLiteralWithNulls, BOOLEAN, null); + assertThat(assertions.expression("a = b") + .binding("a", rowLiteralWithNulls) + .binding("b", rowLiteralWithNulls)) + .isNull(BOOLEAN); + + assertThat(assertions.expression("a != b") + .binding("a", rowLiteralWithNulls) + .binding("b", rowLiteralWithNulls)) + .isNull(BOOLEAN); String alternateRowLiteralWithNulls = toRowLiteral(largeRow(fieldCount, true, true)); - assertFunction(rowLiteralWithNulls + " = " + alternateRowLiteralWithNulls, BOOLEAN, false); - assertFunction(rowLiteralWithNulls + " != " + alternateRowLiteralWithNulls, BOOLEAN, true); + assertThat(assertions.expression("a = b") + .binding("a", rowLiteralWithNulls) + .binding("b", alternateRowLiteralWithNulls)) + .isEqualTo(false); + + assertThat(assertions.expression("a != b") + .binding("a", rowLiteralWithNulls) + .binding("b", alternateRowLiteralWithNulls)) + .isEqualTo(true); } } @@ -596,8 +914,11 @@ private static String toRowLiteral(List data) private void assertRowHashOperator(String inputString, List types, List elements) { checkArgument(types.size() == elements.size(), "types and elements must have the same size"); - assertOperator(HASH_CODE, inputString, BIGINT, hashFields(types, elements)); - assertOperator(XX_HASH_64, inputString, BIGINT, hashFields(types, elements)); + assertThat(assertions.operator(HASH_CODE, inputString)) + .isEqualTo(hashFields(types, elements)); + + assertThat(assertions.operator(XX_HASH_64, inputString)) + .isEqualTo(hashFields(types, elements)); } private long hashFields(List types, List elements) @@ -612,7 +933,7 @@ private long hashFields(List types, List elements) if (fieldValue != null) { Type fieldType = types.get(i); try { - fieldHashCode = (long) functionAssertions.getTypeOperators().getHashCodeOperator(fieldType, simpleConvention(FAIL_ON_NULL, NEVER_NULL)) + fieldHashCode = (long) ((LocalQueryRunner) assertions.getQueryRunner()).getTypeOperators().getHashCodeOperator(fieldType, simpleConvention(FAIL_ON_NULL, NEVER_NULL)) .invoke(fieldValue); } catch (Throwable throwable) { @@ -679,9 +1000,20 @@ private void assertComparisonCombination(String base, String greater) Set greaterOrInequalityOperators = new HashSet<>(ImmutableSet.of(">=", ">", "!=")); Set lessOrInequalityOperators = new HashSet<>(ImmutableSet.of("<=", "<", "!=")); for (String operator : ImmutableList.of(">", "=", "<", ">=", "<=", "!=")) { - assertFunction(base + operator + base, BOOLEAN, equalOperators.contains(operator)); - assertFunction(base + operator + greater, BOOLEAN, lessOrInequalityOperators.contains(operator)); - assertFunction(greater + operator + base, BOOLEAN, greaterOrInequalityOperators.contains(operator)); + assertThat(assertions.expression("a %s b".formatted(operator)) + .binding("a", base) + .binding("b", base)) + .isEqualTo(equalOperators.contains(operator)); + + assertThat(assertions.expression("a %s b".formatted(operator)) + .binding("a", base) + .binding("b", greater)) + .isEqualTo(lessOrInequalityOperators.contains(operator)); + + assertThat(assertions.expression("a %s b".formatted(operator)) + .binding("a", greater) + .binding("b", base)) + .isEqualTo(greaterOrInequalityOperators.contains(operator)); } } } diff --git a/plugin/trino-geospatial/pom.xml b/plugin/trino-geospatial/pom.xml index ddb3eb1563b6..473278e56dd5 100644 --- a/plugin/trino-geospatial/pom.xml +++ b/plugin/trino-geospatial/pom.xml @@ -180,6 +180,18 @@ test + + org.junit.jupiter + junit-jupiter-api + test + + + + org.junit.jupiter + junit-jupiter-engine + test + + org.openjdk.jmh jmh-core diff --git a/plugin/trino-geospatial/src/test/java/io/trino/plugin/geospatial/TestBingTileFunctions.java b/plugin/trino-geospatial/src/test/java/io/trino/plugin/geospatial/TestBingTileFunctions.java index ca120a1de6ae..db2160bc7bb4 100644 --- a/plugin/trino-geospatial/src/test/java/io/trino/plugin/geospatial/TestBingTileFunctions.java +++ b/plugin/trino-geospatial/src/test/java/io/trino/plugin/geospatial/TestBingTileFunctions.java @@ -16,10 +16,12 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.ImmutableList; import io.trino.metadata.InternalFunctionBundle; -import io.trino.operator.scalar.AbstractTestFunctions; import io.trino.spi.type.ArrayType; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; +import io.trino.sql.query.QueryAssertions; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; import java.io.File; import java.nio.file.Files; @@ -32,23 +34,34 @@ import static io.trino.operator.scalar.ApplyFunction.APPLY_FUNCTION; import static io.trino.plugin.geospatial.BingTile.fromCoordinates; import static io.trino.plugin.geospatial.BingTileType.BING_TILE; -import static io.trino.spi.type.BigintType.BIGINT; -import static io.trino.spi.type.BooleanType.BOOLEAN; -import static io.trino.spi.type.IntegerType.INTEGER; +import static io.trino.spi.function.OperatorType.EQUAL; +import static io.trino.spi.function.OperatorType.IS_DISTINCT_FROM; import static io.trino.spi.type.TinyintType.TINYINT; import static io.trino.spi.type.VarcharType.VARCHAR; -import static java.lang.String.format; +import static io.trino.testing.assertions.TrinoExceptionAssert.assertTrinoExceptionThrownBy; import static java.util.Collections.emptyList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS; import static org.testng.Assert.assertEquals; +@TestInstance(PER_CLASS) public class TestBingTileFunctions - extends AbstractTestFunctions { - @BeforeClass - public void registerFunctions() + private QueryAssertions assertions; + + @BeforeAll + public void init() + { + assertions = new QueryAssertions(); + assertions.addPlugin(new GeoPlugin()); + assertions.addFunctions(new InternalFunctionBundle(APPLY_FUNCTION)); + } + + @AfterAll + public void teardown() { - functionAssertions.installPlugin(new GeoPlugin()); - functionAssertions.addFunctions(new InternalFunctionBundle(APPLY_FUNCTION)); + assertions.close(); + assertions = null; } @Test @@ -65,61 +78,111 @@ public void testSerialization() @Test public void testArrayOfBingTiles() { - assertFunction("array [bing_tile(1, 2, 10), bing_tile(3, 4, 11)]", - new ArrayType(BING_TILE), - ImmutableList.of(fromCoordinates(1, 2, 10), fromCoordinates(3, 4, 11))); + assertThat(assertions.expression("ARRAY[a, b]") + .binding("a", "bing_tile(1, 2, 10)") + .binding("b", "bing_tile(3, 4, 11)")) + .hasType(new ArrayType(BING_TILE)) + .isEqualTo(ImmutableList.of(fromCoordinates(1, 2, 10), fromCoordinates(3, 4, 11))); } @Test public void testBingTile() { - assertFunction("bing_tile_quadkey(bing_tile('213'))", VARCHAR, "213"); - assertFunction("bing_tile_quadkey(bing_tile('123030123010121'))", VARCHAR, "123030123010121"); + assertThat(assertions.function("bing_tile_quadkey", "bing_tile('213')")) + .hasType(VARCHAR) + .isEqualTo("213"); - assertFunction("bing_tile_quadkey(bing_tile(3, 5, 3))", VARCHAR, "213"); - assertFunction("bing_tile_quadkey(bing_tile(21845, 13506, 15))", VARCHAR, "123030123010121"); + assertThat(assertions.function("bing_tile_quadkey", "bing_tile('123030123010121')")) + .hasType(VARCHAR) + .isEqualTo("123030123010121"); + + assertThat(assertions.function("bing_tile_quadkey", "bing_tile(3, 5, 3)")) + .hasType(VARCHAR) + .isEqualTo("213"); + + assertThat(assertions.function("bing_tile_quadkey", "bing_tile(21845, 13506, 15)")) + .hasType(VARCHAR) + .isEqualTo("123030123010121"); // Invalid calls: corrupt quadkeys - assertInvalidFunction("bing_tile('')", "QuadKey must not be empty string"); - assertInvalidFunction("bing_tile('test')", "Invalid QuadKey digit sequence: test"); - assertInvalidFunction("bing_tile('12345')", "Invalid QuadKey digit sequence: 12345"); - assertInvalidFunction("bing_tile('101010101010101010101010101010100101010101001010')", "QuadKey must be 23 characters or less"); + assertTrinoExceptionThrownBy(() -> assertions.function("bing_tile", "''").evaluate()) + .hasMessage("QuadKey must not be empty string"); + + assertTrinoExceptionThrownBy(() -> assertions.function("bing_tile", "'test'").evaluate()) + .hasMessage("Invalid QuadKey digit sequence: test"); + + assertTrinoExceptionThrownBy(() -> assertions.function("bing_tile", "'12345'").evaluate()) + .hasMessage("Invalid QuadKey digit sequence: 12345"); + + assertTrinoExceptionThrownBy(() -> assertions.function("bing_tile", "'101010101010101010101010101010100101010101001010'").evaluate()) + .hasMessage("QuadKey must be 23 characters or less"); // Invalid calls: XY out of range - assertInvalidFunction("bing_tile(10, 2, 3)", "XY coordinates for a Bing tile at zoom level 3 must be within [0, 8) range"); - assertInvalidFunction("bing_tile(2, 10, 3)", "XY coordinates for a Bing tile at zoom level 3 must be within [0, 8) range"); + assertTrinoExceptionThrownBy(() -> assertions.function("bing_tile", "10", "2", "3").evaluate()) + .hasMessage("XY coordinates for a Bing tile at zoom level 3 must be within [0, 8) range"); + + assertTrinoExceptionThrownBy(() -> assertions.function("bing_tile", "2", "10", "3").evaluate()) + .hasMessage("XY coordinates for a Bing tile at zoom level 3 must be within [0, 8) range"); // Invalid calls: zoom level out of range - assertInvalidFunction("bing_tile(2, 7, 37)", "Zoom level must be <= 23"); + assertTrinoExceptionThrownBy(() -> assertions.function("bing_tile", "2", "7", "37").evaluate()) + .hasMessage("Zoom level must be <= 23"); } @Test public void testPointToBingTile() { - assertFunction("bing_tile_at(30.12, 60, 15)", BING_TILE, fromCoordinates(21845, 13506, 15)); - assertFunction("bing_tile_at(0, -0.002, 1)", BING_TILE, fromCoordinates(0, 1, 1)); - assertFunction("bing_tile_at(1e0/512, 0, 1)", BING_TILE, fromCoordinates(1, 0, 1)); - assertFunction("bing_tile_at(1e0/512, 0, 9)", BING_TILE, fromCoordinates(256, 255, 9)); + assertThat(assertions.function("bing_tile_at", "30.12", "60", "15")) + .hasType(BING_TILE) + .isEqualTo(fromCoordinates(21845, 13506, 15)); + + assertThat(assertions.function("bing_tile_at", "0", "-0.002", "1")) + .hasType(BING_TILE) + .isEqualTo(fromCoordinates(0, 1, 1)); + + assertThat(assertions.function("bing_tile_at", "1e0/512", "0", "1")) + .hasType(BING_TILE) + .isEqualTo(fromCoordinates(1, 0, 1)); + + assertThat(assertions.function("bing_tile_at", "1e0/512", "0", "9")) + .hasType(BING_TILE) + .isEqualTo(fromCoordinates(256, 255, 9)); // Invalid calls // Longitude out of range - assertInvalidFunction("bing_tile_at(30.12, 600, 15)", "Longitude must be between -180.0 and 180.0"); + assertTrinoExceptionThrownBy(() -> assertions.function("bing_tile_at", "30.12", "600", "15").evaluate()) + .hasMessage("Longitude must be between -180.0 and 180.0"); + // Latitude out of range - assertInvalidFunction("bing_tile_at(300.12, 60, 15)", "Latitude must be between -85.05112878 and 85.05112878"); + assertTrinoExceptionThrownBy(() -> assertions.function("bing_tile_at", "300.12", "60", "15").evaluate()) + .hasMessage("Latitude must be between -85.05112878 and 85.05112878"); + // Invalid zoom levels - assertInvalidFunction("bing_tile_at(30.12, 60, 0)", "Zoom level must be > 0"); - assertInvalidFunction("bing_tile_at(30.12, 60, 40)", "Zoom level must be <= 23"); + assertTrinoExceptionThrownBy(() -> assertions.function("bing_tile_at", "30.12", "60", "0").evaluate()) + .hasMessage("Zoom level must be > 0"); + + assertTrinoExceptionThrownBy(() -> assertions.function("bing_tile_at", "30.12", "60", "40").evaluate()) + .hasMessage("Zoom level must be <= 23"); } @Test public void testBingTileCoordinates() { - assertFunction("bing_tile_coordinates(bing_tile('213'))[1]", INTEGER, 3); - assertFunction("bing_tile_coordinates(bing_tile('213'))[2]", INTEGER, 5); - assertFunction("bing_tile_coordinates(bing_tile('123030123010121'))[1]", INTEGER, 21845); - assertFunction("bing_tile_coordinates(bing_tile('123030123010121'))[2]", INTEGER, 13506); + assertThat(assertions.expression("bing_tile_coordinates(tile)[1]") + .binding("tile", "bing_tile('213')")) + .isEqualTo(3); + + assertThat(assertions.expression("bing_tile_coordinates(tile)[2]") + .binding("tile", "bing_tile('213')")) + .isEqualTo(5); - assertCachedInstanceHasBoundedRetainedSize("bing_tile_coordinates(bing_tile('213'))"); + assertThat(assertions.expression("bing_tile_coordinates(tile)[1]") + .binding("tile", "bing_tile('123030123010121')")) + .isEqualTo(21845); + + assertThat(assertions.expression("bing_tile_coordinates(tile)[2]") + .binding("tile", "bing_tile('123030123010121')")) + .isEqualTo(13506); } private void assertBingTilesAroundWithRadius( @@ -129,11 +192,10 @@ private void assertBingTilesAroundWithRadius( double radius, String... expectedQuadKeys) { - assertFunction( - format("transform(bing_tiles_around(%s, %s, %s, %s), x -> bing_tile_quadkey(x))", - latitude, longitude, zoomLevel, radius), - new ArrayType(VARCHAR), - ImmutableList.copyOf(expectedQuadKeys)); + assertThat(assertions.expression("transform(tiles, x -> bing_tile_quadkey(x))") + .binding("tiles", "bing_tiles_around(%s, %s, %s, %s)".formatted(latitude, longitude, zoomLevel, radius))) + .hasType(new ArrayType(VARCHAR)) + .isEqualTo(ImmutableList.copyOf(expectedQuadKeys)); } @Test @@ -230,26 +292,29 @@ public void testBingTilesAroundEdgeWithRadius() public void testBingTilesWithRadiusBadInput() { // Invalid radius - assertInvalidFunction("bing_tiles_around(30.12, 60.0, 1, -1)", "Radius must be >= 0"); - assertInvalidFunction("bing_tiles_around(30.12, 60.0, 1, 2000)", - "Radius must be <= 1,000 km"); + assertTrinoExceptionThrownBy(() -> assertions.function("bing_tiles_around", "30.12", "60.0", "1", "-1").evaluate()) + .hasMessage("Radius must be >= 0"); + + assertTrinoExceptionThrownBy(() -> assertions.function("bing_tiles_around", "30.12", "60.0", "1", "2000").evaluate()) + .hasMessage("Radius must be <= 1,000 km"); // Too many tiles - assertInvalidFunction("bing_tiles_around(30.12, 60.0, 20, 100)", - "The number of tiles covering input rectangle exceeds the limit of 1M. Number of tiles: 36699364. Radius: 100.0 km. Zoom level: 20."); + assertTrinoExceptionThrownBy(() -> assertions.function("bing_tiles_around", "30.12", "60.0", "20", "100").evaluate()) + .hasMessage("The number of tiles covering input rectangle exceeds the limit of 1M. Number of tiles: 36699364. Radius: 100.0 km. Zoom level: 20."); } @Test public void testBingTilesAround() { - assertFunction( - "transform(bing_tiles_around(30.12, 60, 1), x -> bing_tile_quadkey(x))", - new ArrayType(VARCHAR), - ImmutableList.of("0", "2", "1", "3")); - assertFunction( - "transform(bing_tiles_around(30.12, 60, 15), x -> bing_tile_quadkey(x))", - new ArrayType(VARCHAR), - ImmutableList.of( + assertThat(assertions.expression("transform(tiles, x -> bing_tile_quadkey(x))") + .binding("tiles", "bing_tiles_around(30.12, 60, 1)")) + .hasType(new ArrayType(VARCHAR)) + .isEqualTo(ImmutableList.of("0", "2", "1", "3")); + + assertThat(assertions.expression("transform(tiles, x -> bing_tile_quadkey(x))") + .binding("tiles", "bing_tiles_around(30.12, 60, 15)")) + .hasType(new ArrayType(VARCHAR)) + .isEqualTo(ImmutableList.of( "123030123010102", "123030123010120", "123030123010122", @@ -259,10 +324,11 @@ public void testBingTilesAround() "123030123010112", "123030123010130", "123030123010132")); - assertFunction( - "transform(bing_tiles_around(30.12, 60, 23), x -> bing_tile_quadkey(x))", - new ArrayType(VARCHAR), - ImmutableList.of( + + assertThat(assertions.expression("transform(tiles, x -> bing_tile_quadkey(x))") + .binding("tiles", "bing_tiles_around(30.12, 60, 23)")) + .hasType(new ArrayType(VARCHAR)) + .isEqualTo(ImmutableList.of( "12303012301012121210122", "12303012301012121210300", "12303012301012121210302", @@ -278,55 +344,62 @@ public void testBingTilesAround() public void testBingTilesAroundCorner() { // Different zoom Level - assertFunction( - "transform(bing_tiles_around(-85.05112878, -180, 1), x -> bing_tile_quadkey(x))", - new ArrayType(VARCHAR), - ImmutableList.of("0", "2", "1", "3")); - assertFunction( - "transform(bing_tiles_around(-85.05112878, -180, 3), x -> bing_tile_quadkey(x))", - new ArrayType(VARCHAR), - ImmutableList.of("220", "222", "221", "223")); - assertFunction( - "transform(bing_tiles_around(-85.05112878, -180, 15), x -> bing_tile_quadkey(x))", - new ArrayType(VARCHAR), - ImmutableList.of("222222222222220", "222222222222222", "222222222222221", "222222222222223")); + assertThat(assertions.expression("transform(tiles, x -> bing_tile_quadkey(x))") + .binding("tiles", "bing_tiles_around(-85.05112878, -180, 1)")) + .hasType(new ArrayType(VARCHAR)) + .isEqualTo(ImmutableList.of("0", "2", "1", "3")); + + assertThat(assertions.expression("transform(tiles, x -> bing_tile_quadkey(x))") + .binding("tiles", "bing_tiles_around(-85.05112878, -180, 3)")) + .hasType(new ArrayType(VARCHAR)) + .isEqualTo(ImmutableList.of("220", "222", "221", "223")); + + assertThat(assertions.expression("transform(tiles, x -> bing_tile_quadkey(x))") + .binding("tiles", "bing_tiles_around(-85.05112878, -180, 15)")) + .hasType(new ArrayType(VARCHAR)) + .isEqualTo(ImmutableList.of("222222222222220", "222222222222222", "222222222222221", "222222222222223")); // Different Corners // Starting Corner 0,3 - assertFunction( - "transform(bing_tiles_around(-85.05112878, -180, 2), x -> bing_tile_quadkey(x))", - new ArrayType(VARCHAR), - ImmutableList.of("20", "22", "21", "23")); - assertFunction( - "transform(bing_tiles_around(-85.05112878, 180, 2), x -> bing_tile_quadkey(x))", - new ArrayType(VARCHAR), - ImmutableList.of("30", "32", "31", "33")); - assertFunction( - "transform(bing_tiles_around(85.05112878, -180, 2), x -> bing_tile_quadkey(x))", - new ArrayType(VARCHAR), - ImmutableList.of("00", "02", "01", "03")); - assertFunction( - "transform(bing_tiles_around(85.05112878, 180, 2), x -> bing_tile_quadkey(x))", - new ArrayType(VARCHAR), - ImmutableList.of("10", "12", "11", "13")); + assertThat(assertions.expression("transform(tiles, x -> bing_tile_quadkey(x))") + .binding("tiles", "bing_tiles_around(-85.05112878, -180, 2)")) + .hasType(new ArrayType(VARCHAR)) + .isEqualTo(ImmutableList.of("20", "22", "21", "23")); + + assertThat(assertions.expression("transform(tiles, x -> bing_tile_quadkey(x))") + .binding("tiles", "bing_tiles_around(-85.05112878, 180, 2)")) + .hasType(new ArrayType(VARCHAR)) + .isEqualTo(ImmutableList.of("30", "32", "31", "33")); + + assertThat(assertions.expression("transform(tiles, x -> bing_tile_quadkey(x))") + .binding("tiles", "bing_tiles_around(85.05112878, -180, 2)")) + .hasType(new ArrayType(VARCHAR)) + .isEqualTo(ImmutableList.of("00", "02", "01", "03")); + + assertThat(assertions.expression("transform(tiles, x -> bing_tile_quadkey(x))") + .binding("tiles", "bing_tiles_around(85.05112878, 180, 2)")) + .hasType(new ArrayType(VARCHAR)) + .isEqualTo(ImmutableList.of("10", "12", "11", "13")); } @Test public void testBingTilesAroundEdge() { // Different zoom Level - assertFunction( - "transform(bing_tiles_around(-85.05112878, 0, 1), x -> bing_tile_quadkey(x))", - new ArrayType(VARCHAR), - ImmutableList.of("0", "2", "1", "3")); - assertFunction( - "transform(bing_tiles_around(-85.05112878, 0, 3), x -> bing_tile_quadkey(x))", - new ArrayType(VARCHAR), - ImmutableList.of("231", "233", "320", "322", "321", "323")); - assertFunction( - "transform(bing_tiles_around(-85.05112878, 0, 15), x -> bing_tile_quadkey(x))", - new ArrayType(VARCHAR), - ImmutableList.of( + assertThat(assertions.expression("transform(tiles, x -> bing_tile_quadkey(x))") + .binding("tiles", "bing_tiles_around(-85.05112878, 0, 1)")) + .hasType(new ArrayType(VARCHAR)) + .isEqualTo(ImmutableList.of("0", "2", "1", "3")); + + assertThat(assertions.expression("transform(tiles, x -> bing_tile_quadkey(x))") + .binding("tiles", "bing_tiles_around(-85.05112878, 0, 3)")) + .hasType(new ArrayType(VARCHAR)) + .isEqualTo(ImmutableList.of("231", "233", "320", "322", "321", "323")); + + assertThat(assertions.expression("transform(tiles, x -> bing_tile_quadkey(x))") + .binding("tiles", "bing_tiles_around(-85.05112878, 0, 15)")) + .hasType(new ArrayType(VARCHAR)) + .isEqualTo(ImmutableList.of( "233333333333331", "233333333333333", "322222222222220", @@ -336,62 +409,131 @@ public void testBingTilesAroundEdge() // Different Edges // Starting Edge 2,3 - assertFunction( - "transform(bing_tiles_around(-85.05112878, 0, 2), x -> bing_tile_quadkey(x))", - new ArrayType(VARCHAR), - ImmutableList.of("21", "23", "30", "32", "31", "33")); - assertFunction( - "transform(bing_tiles_around(85.05112878, 0, 2), x -> bing_tile_quadkey(x))", - new ArrayType(VARCHAR), - ImmutableList.of("01", "03", "10", "12", "11", "13")); - assertFunction( - "transform(bing_tiles_around(0, 180, 2), x -> bing_tile_quadkey(x))", - new ArrayType(VARCHAR), - ImmutableList.of("12", "30", "32", "13", "31", "33")); - assertFunction( - "transform(bing_tiles_around(0, -180, 2), x -> bing_tile_quadkey(x))", - new ArrayType(VARCHAR), - ImmutableList.of("02", "20", "22", "03", "21", "23")); + assertThat(assertions.expression("transform(tiles, x -> bing_tile_quadkey(x))") + .binding("tiles", "bing_tiles_around(-85.05112878, 0, 2)")) + .hasType(new ArrayType(VARCHAR)) + .isEqualTo(ImmutableList.of("21", "23", "30", "32", "31", "33")); + + assertThat(assertions.expression("transform(tiles, x -> bing_tile_quadkey(x))") + .binding("tiles", "bing_tiles_around(85.05112878, 0, 2)")) + .hasType(new ArrayType(VARCHAR)) + .isEqualTo(ImmutableList.of("01", "03", "10", "12", "11", "13")); + + assertThat(assertions.expression("transform(tiles, x -> bing_tile_quadkey(x))") + .binding("tiles", "bing_tiles_around(0, 180, 2)")) + .hasType(new ArrayType(VARCHAR)) + .isEqualTo(ImmutableList.of("12", "30", "32", "13", "31", "33")); + + assertThat(assertions.expression("transform(tiles, x -> bing_tile_quadkey(x))") + .binding("tiles", "bing_tiles_around(0, -180, 2)")) + .hasType(new ArrayType(VARCHAR)) + .isEqualTo(ImmutableList.of("02", "20", "22", "03", "21", "23")); } @Test public void testBingTileZoomLevel() { - assertFunction("bing_tile_zoom_level(bing_tile('213'))", TINYINT, (byte) 3); - assertFunction("bing_tile_zoom_level(bing_tile('123030123010121'))", TINYINT, (byte) 15); + assertThat(assertions.function("bing_tile_zoom_level", "bing_tile('213')")) + .hasType(TINYINT) + .isEqualTo((byte) 3); + + assertThat(assertions.function("bing_tile_zoom_level", "bing_tile('123030123010121')")) + .hasType(TINYINT) + .isEqualTo((byte) 15); } @Test public void testBingTilePolygon() { - assertFunction("ST_AsText(bing_tile_polygon(bing_tile('123030123010121')))", VARCHAR, "POLYGON ((59.996337890625 30.11662158281937, 60.00732421875 30.11662158281937, 60.00732421875 30.12612436422458, 59.996337890625 30.12612436422458, 59.996337890625 30.11662158281937))"); - assertFunction("ST_AsText(ST_Centroid(bing_tile_polygon(bing_tile('123030123010121'))))", VARCHAR, "POINT (60.0018310442288 30.121372968273892)"); + assertThat(assertions.function("ST_AsText", "bing_tile_polygon(bing_tile('123030123010121'))")) + .hasType(VARCHAR) + .isEqualTo("POLYGON ((59.996337890625 30.11662158281937, 60.00732421875 30.11662158281937, 60.00732421875 30.12612436422458, 59.996337890625 30.12612436422458, 59.996337890625 30.11662158281937))"); + + assertThat(assertions.function("ST_AsText", "ST_Centroid(bing_tile_polygon(bing_tile('123030123010121')))")) + .hasType(VARCHAR) + .isEqualTo("POINT (60.0018310442288 30.121372968273892)"); // Check bottom right corner of a stack of tiles at different zoom levels - assertFunction("ST_AsText(apply(bing_tile_polygon(bing_tile(1, 1, 1)), g -> ST_Point(ST_XMax(g), ST_YMin(g))))", VARCHAR, "POINT (180 -85.05112877980659)"); - assertFunction("ST_AsText(apply(bing_tile_polygon(bing_tile(3, 3, 2)), g -> ST_Point(ST_XMax(g), ST_YMin(g))))", VARCHAR, "POINT (180 -85.05112877980659)"); - assertFunction("ST_AsText(apply(bing_tile_polygon(bing_tile(7, 7, 3)), g -> ST_Point(ST_XMax(g), ST_YMin(g))))", VARCHAR, "POINT (180 -85.05112877980659)"); - assertFunction("ST_AsText(apply(bing_tile_polygon(bing_tile(15, 15, 4)), g -> ST_Point(ST_XMax(g), ST_YMin(g))))", VARCHAR, "POINT (180 -85.05112877980659)"); - assertFunction("ST_AsText(apply(bing_tile_polygon(bing_tile(31, 31, 5)), g -> ST_Point(ST_XMax(g), ST_YMin(g))))", VARCHAR, "POINT (180 -85.05112877980659)"); - - assertFunction("ST_AsText(apply(bing_tile_polygon(bing_tile(0, 0, 1)), g -> ST_Point(ST_XMax(g), ST_YMin(g))))", VARCHAR, "POINT (0 0)"); - assertFunction("ST_AsText(apply(bing_tile_polygon(bing_tile(1, 1, 2)), g -> ST_Point(ST_XMax(g), ST_YMin(g))))", VARCHAR, "POINT (0 0)"); - assertFunction("ST_AsText(apply(bing_tile_polygon(bing_tile(3, 3, 3)), g -> ST_Point(ST_XMax(g), ST_YMin(g))))", VARCHAR, "POINT (0 0)"); - assertFunction("ST_AsText(apply(bing_tile_polygon(bing_tile(7, 7, 4)), g -> ST_Point(ST_XMax(g), ST_YMin(g))))", VARCHAR, "POINT (0 0)"); - assertFunction("ST_AsText(apply(bing_tile_polygon(bing_tile(15, 15, 5)), g -> ST_Point(ST_XMax(g), ST_YMin(g))))", VARCHAR, "POINT (0 0)"); + assertThat(assertions.function("ST_AsText", "apply(bing_tile_polygon(bing_tile(1, 1, 1)), g -> ST_Point(ST_XMax(g), ST_YMin(g)))")) + .hasType(VARCHAR) + .isEqualTo("POINT (180 -85.05112877980659)"); + + assertThat(assertions.function("ST_AsText", "apply(bing_tile_polygon(bing_tile(3, 3, 2)), g -> ST_Point(ST_XMax(g), ST_YMin(g)))")) + .hasType(VARCHAR) + .isEqualTo("POINT (180 -85.05112877980659)"); + + assertThat(assertions.function("ST_AsText", "apply(bing_tile_polygon(bing_tile(7, 7, 3)), g -> ST_Point(ST_XMax(g), ST_YMin(g)))")) + .hasType(VARCHAR) + .isEqualTo("POINT (180 -85.05112877980659)"); + + assertThat(assertions.function("ST_AsText", "apply(bing_tile_polygon(bing_tile(15, 15, 4)), g -> ST_Point(ST_XMax(g), ST_YMin(g)))")) + .hasType(VARCHAR) + .isEqualTo("POINT (180 -85.05112877980659)"); + + assertThat(assertions.function("ST_AsText", "apply(bing_tile_polygon(bing_tile(31, 31, 5)), g -> ST_Point(ST_XMax(g), ST_YMin(g)))")) + .hasType(VARCHAR) + .isEqualTo("POINT (180 -85.05112877980659)"); + + assertThat(assertions.function("ST_AsText", "apply(bing_tile_polygon(bing_tile(0, 0, 1)), g -> ST_Point(ST_XMax(g), ST_YMin(g)))")) + .hasType(VARCHAR) + .isEqualTo("POINT (0 0)"); + + assertThat(assertions.function("ST_AsText", "apply(bing_tile_polygon(bing_tile(1, 1, 2)), g -> ST_Point(ST_XMax(g), ST_YMin(g)))")) + .hasType(VARCHAR) + .isEqualTo("POINT (0 0)"); + + assertThat(assertions.function("ST_AsText", "apply(bing_tile_polygon(bing_tile(3, 3, 3)), g -> ST_Point(ST_XMax(g), ST_YMin(g)))")) + .hasType(VARCHAR) + .isEqualTo("POINT (0 0)"); + + assertThat(assertions.function("ST_AsText", "apply(bing_tile_polygon(bing_tile(7, 7, 4)), g -> ST_Point(ST_XMax(g), ST_YMin(g)))")) + .hasType(VARCHAR) + .isEqualTo("POINT (0 0)"); + + assertThat(assertions.function("ST_AsText", "apply(bing_tile_polygon(bing_tile(15, 15, 5)), g -> ST_Point(ST_XMax(g), ST_YMin(g)))")) + .hasType(VARCHAR) + .isEqualTo("POINT (0 0)"); // Check top left corner of a stack of tiles at different zoom levels - assertFunction("ST_AsText(apply(bing_tile_polygon(bing_tile(1, 1, 1)), g -> ST_Point(ST_XMin(g), ST_YMax(g))))", VARCHAR, "POINT (0 0)"); - assertFunction("ST_AsText(apply(bing_tile_polygon(bing_tile(2, 2, 2)), g -> ST_Point(ST_XMin(g), ST_YMax(g))))", VARCHAR, "POINT (0 0)"); - assertFunction("ST_AsText(apply(bing_tile_polygon(bing_tile(4, 4, 3)), g -> ST_Point(ST_XMin(g), ST_YMax(g))))", VARCHAR, "POINT (0 0)"); - assertFunction("ST_AsText(apply(bing_tile_polygon(bing_tile(8, 8, 4)), g -> ST_Point(ST_XMin(g), ST_YMax(g))))", VARCHAR, "POINT (0 0)"); - assertFunction("ST_AsText(apply(bing_tile_polygon(bing_tile(16, 16, 5)), g -> ST_Point(ST_XMin(g), ST_YMax(g))))", VARCHAR, "POINT (0 0)"); - - assertFunction("ST_AsText(apply(bing_tile_polygon(bing_tile(0, 0, 1)), g -> ST_Point(ST_XMin(g), ST_YMax(g))))", VARCHAR, "POINT (-180 85.05112877980659)"); - assertFunction("ST_AsText(apply(bing_tile_polygon(bing_tile(0, 0, 2)), g -> ST_Point(ST_XMin(g), ST_YMax(g))))", VARCHAR, "POINT (-180 85.05112877980659)"); - assertFunction("ST_AsText(apply(bing_tile_polygon(bing_tile(0, 0, 3)), g -> ST_Point(ST_XMin(g), ST_YMax(g))))", VARCHAR, "POINT (-180 85.05112877980659)"); - assertFunction("ST_AsText(apply(bing_tile_polygon(bing_tile(0, 0, 4)), g -> ST_Point(ST_XMin(g), ST_YMax(g))))", VARCHAR, "POINT (-180 85.05112877980659)"); - assertFunction("ST_AsText(apply(bing_tile_polygon(bing_tile(0, 0, 5)), g -> ST_Point(ST_XMin(g), ST_YMax(g))))", VARCHAR, "POINT (-180 85.05112877980659)"); + assertThat(assertions.function("ST_AsText", "apply(bing_tile_polygon(bing_tile(1, 1, 1)), g -> ST_Point(ST_XMin(g), ST_YMax(g)))")) + .hasType(VARCHAR) + .isEqualTo("POINT (0 0)"); + + assertThat(assertions.function("ST_AsText", "apply(bing_tile_polygon(bing_tile(2, 2, 2)), g -> ST_Point(ST_XMin(g), ST_YMax(g)))")) + .hasType(VARCHAR) + .isEqualTo("POINT (0 0)"); + + assertThat(assertions.function("ST_AsText", "apply(bing_tile_polygon(bing_tile(4, 4, 3)), g -> ST_Point(ST_XMin(g), ST_YMax(g)))")) + .hasType(VARCHAR) + .isEqualTo("POINT (0 0)"); + + assertThat(assertions.function("ST_AsText", "apply(bing_tile_polygon(bing_tile(8, 8, 4)), g -> ST_Point(ST_XMin(g), ST_YMax(g)))")) + .hasType(VARCHAR) + .isEqualTo("POINT (0 0)"); + + assertThat(assertions.function("ST_AsText", "apply(bing_tile_polygon(bing_tile(16, 16, 5)), g -> ST_Point(ST_XMin(g), ST_YMax(g)))")) + .hasType(VARCHAR) + .isEqualTo("POINT (0 0)"); + + assertThat(assertions.function("ST_AsText", "apply(bing_tile_polygon(bing_tile(0, 0, 1)), g -> ST_Point(ST_XMin(g), ST_YMax(g)))")) + .hasType(VARCHAR) + .isEqualTo("POINT (-180 85.05112877980659)"); + + assertThat(assertions.function("ST_AsText", "apply(bing_tile_polygon(bing_tile(0, 0, 2)), g -> ST_Point(ST_XMin(g), ST_YMax(g)))")) + .hasType(VARCHAR) + .isEqualTo("POINT (-180 85.05112877980659)"); + + assertThat(assertions.function("ST_AsText", "apply(bing_tile_polygon(bing_tile(0, 0, 3)), g -> ST_Point(ST_XMin(g), ST_YMax(g)))")) + .hasType(VARCHAR) + .isEqualTo("POINT (-180 85.05112877980659)"); + + assertThat(assertions.function("ST_AsText", "apply(bing_tile_polygon(bing_tile(0, 0, 4)), g -> ST_Point(ST_XMin(g), ST_YMax(g)))")) + .hasType(VARCHAR) + .isEqualTo("POINT (-180 85.05112877980659)"); + + assertThat(assertions.function("ST_AsText", "apply(bing_tile_polygon(bing_tile(0, 0, 5)), g -> ST_Point(ST_XMin(g), ST_YMax(g)))")) + .hasType(VARCHAR) + .isEqualTo("POINT (-180 85.05112877980659)"); } @Test @@ -405,7 +547,10 @@ public void testLargeGeometryToBingTiles() String wkt = parts[0]; int zoomLevel = Integer.parseInt(parts[1]); long tileCount = Long.parseLong(parts[2]); - assertFunction("cardinality(geometry_to_bing_tiles(ST_GeometryFromText('" + wkt + "'), " + zoomLevel + "))", BIGINT, tileCount); + assertThat(assertions.expression("cardinality(geometry_to_bing_tiles(geometry, zoom))") + .binding("geometry", "ST_GeometryFromText('%s')".formatted(wkt)) + .binding("zoom", Integer.toString(zoomLevel))) + .isEqualTo(tileCount); } } @@ -429,10 +574,20 @@ public void testGeometryToBingTiles() assertGeometryToBingTiles("GEOMETRYCOLLECTION (POINT (60 30.12), POLYGON ((10 10, -10 10, -20 -15, 10 10)))", 3, ImmutableList.of("033", "211", "122", "123")); assertGeometryToBingTiles("GEOMETRYCOLLECTION (POINT (60 30.12), LINESTRING (61 31, 61.01 31.01), POLYGON EMPTY)", 15, ImmutableList.of("123030123010121", "123030112310200", "123030112310202", "123030112310201")); - assertFunction("transform(geometry_to_bing_tiles(bing_tile_polygon(bing_tile('1230301230')), 10), x -> bing_tile_quadkey(x))", new ArrayType(VARCHAR), ImmutableList.of("1230301230")); - assertFunction("transform(geometry_to_bing_tiles(bing_tile_polygon(bing_tile('1230301230')), 11), x -> bing_tile_quadkey(x))", new ArrayType(VARCHAR), ImmutableList.of("12303012300", "12303012302", "12303012301", "12303012303")); + assertThat(assertions.expression("transform(tiles, x -> bing_tile_quadkey(x))") + .binding("tiles", "geometry_to_bing_tiles(bing_tile_polygon(bing_tile('1230301230')), 10)")) + .hasType(new ArrayType(VARCHAR)) + .isEqualTo(ImmutableList.of("1230301230")); + + assertThat(assertions.expression("transform(tiles, x -> bing_tile_quadkey(x))") + .binding("tiles", "geometry_to_bing_tiles(bing_tile_polygon(bing_tile('1230301230')), 11)")) + .hasType(new ArrayType(VARCHAR)) + .isEqualTo(ImmutableList.of("12303012300", "12303012302", "12303012301", "12303012303")); - assertFunction("transform(geometry_to_bing_tiles(ST_Envelope(ST_GeometryFromText('LINESTRING (59.765625 29.84064389983442, 60.2 30.14512718337612)')), 10), x -> bing_tile_quadkey(x))", new ArrayType(VARCHAR), ImmutableList.of("1230301230", "1230301231")); + assertThat(assertions.expression("transform(tiles, x -> bing_tile_quadkey(x))") + .binding("tiles", "geometry_to_bing_tiles(ST_Envelope(ST_GeometryFromText('LINESTRING (59.765625 29.84064389983442, 60.2 30.14512718337612)')), 10)")) + .hasType(new ArrayType(VARCHAR)) + .isEqualTo(ImmutableList.of("1230301230", "1230301231")); // Empty geometries assertGeometryToBingTiles("POINT EMPTY", 10, emptyList()); @@ -441,19 +596,50 @@ public void testGeometryToBingTiles() // Invalid input // Longitude out of range - assertInvalidFunction("geometry_to_bing_tiles(ST_Point(600, 30.12), 10)", "Longitude span for the geometry must be in [-180.00, 180.00] range"); - assertInvalidFunction("geometry_to_bing_tiles(ST_GeometryFromText('POLYGON ((1000 10, -10 10, -20 -15))'), 10)", "Longitude span for the geometry must be in [-180.00, 180.00] range"); + assertTrinoExceptionThrownBy(() -> assertions.expression("geometry_to_bing_tiles(geometry, zoom)") + .binding("geometry", "ST_Point(600, 30.12)") + .binding("zoom", Integer.toString(10)) + .evaluate()) + .hasMessage("Longitude span for the geometry must be in [-180.00, 180.00] range"); + + assertTrinoExceptionThrownBy(() -> assertions.expression("geometry_to_bing_tiles(geometry, zoom)") + .binding("geometry", "ST_GeometryFromText('POLYGON ((1000 10, -10 10, -20 -15))')") + .binding("zoom", Integer.toString(10)) + .evaluate()) + .hasMessage("Longitude span for the geometry must be in [-180.00, 180.00] range"); + // Latitude out of range - assertInvalidFunction("geometry_to_bing_tiles(ST_Point(60, 300.12), 10)", "Latitude span for the geometry must be in [-85.05, 85.05] range"); - assertInvalidFunction("geometry_to_bing_tiles(ST_GeometryFromText('POLYGON ((10 1000, -10 10, -20 -15))'), 10)", "Latitude span for the geometry must be in [-85.05, 85.05] range"); + assertTrinoExceptionThrownBy(() -> assertions.expression("geometry_to_bing_tiles(geometry, zoom)") + .binding("geometry", "ST_Point(60, 300.12)") + .binding("zoom", Integer.toString(10)) + .evaluate()) + .hasMessage("Latitude span for the geometry must be in [-85.05, 85.05] range"); + + assertTrinoExceptionThrownBy(() -> assertions.expression("geometry_to_bing_tiles(geometry, zoom)") + .binding("geometry", "ST_GeometryFromText('POLYGON ((10 1000, -10 10, -20 -15))')") + .binding("zoom", Integer.toString(10)) + .evaluate()) + .hasMessage("Latitude span for the geometry must be in [-85.05, 85.05] range"); + // Invalid zoom levels - assertInvalidFunction("geometry_to_bing_tiles(ST_Point(60, 30.12), 0)", "Zoom level must be > 0"); - assertInvalidFunction("geometry_to_bing_tiles(ST_Point(60, 30.12), 40)", "Zoom level must be <= 23"); + assertTrinoExceptionThrownBy(() -> assertions.expression("geometry_to_bing_tiles(geometry, zoom)") + .binding("geometry", "ST_Point(60, 30.12)") + .binding("zoom", Integer.toString(0)) + .evaluate()) + .hasMessage("Zoom level must be > 0"); + + assertTrinoExceptionThrownBy(() -> assertions.expression("geometry_to_bing_tiles(geometry, zoom)") + .binding("geometry", "ST_Point(60, 30.12)") + .binding("zoom", Integer.toString(40)) + .evaluate()) + .hasMessage("Zoom level must be <= 23"); // Input rectangle too large - assertInvalidFunction("geometry_to_bing_tiles(ST_Envelope(ST_GeometryFromText('LINESTRING (0 0, 80 80)')), 16)", - "The number of tiles covering input rectangle exceeds the limit of 1M. Number of tiles: 370085804. Rectangle: xMin=0.00, yMin=0.00, xMax=80.00, yMax=80.00. Zoom level: 16."); - assertFunction("cardinality(geometry_to_bing_tiles(ST_Envelope(ST_GeometryFromText('LINESTRING (0 0, 80 80)')), 5))", BIGINT, 104L); + assertTrinoExceptionThrownBy(() -> assertions.function("geometry_to_bing_tiles", "ST_Envelope(ST_GeometryFromText('LINESTRING (0 0, 80 80)'))", "16").evaluate()) + .hasMessage("The number of tiles covering input rectangle exceeds the limit of 1M. Number of tiles: 370085804. Rectangle: xMin=0.00, yMin=0.00, xMax=80.00, yMax=80.00. Zoom level: 16."); + + assertThat(assertions.function("cardinality", "geometry_to_bing_tiles(ST_Envelope(ST_GeometryFromText('LINESTRING (0 0, 80 80)')), 5)")) + .isEqualTo(104L); // Input polygon too complex String filePath = new File(getResource("too_large_polygon.txt").toURI()).getPath(); @@ -461,53 +647,102 @@ public void testGeometryToBingTiles() try (Stream lines = Files.lines(Paths.get(filePath))) { largeWkt = lines.findFirst().get(); } - assertInvalidFunction("geometry_to_bing_tiles(ST_GeometryFromText('" + largeWkt + "'), 16)", "The zoom level is too high or the geometry is too complex to compute a set of covering Bing tiles. Please use a lower zoom level or convert the geometry to its bounding box using the ST_Envelope function."); - assertFunction("cardinality(geometry_to_bing_tiles(ST_Envelope(ST_GeometryFromText('" + largeWkt + "')), 16))", BIGINT, 19939L); + assertTrinoExceptionThrownBy(() -> assertions.expression("geometry_to_bing_tiles(ST_GeometryFromText('" + largeWkt + "'), 16)").evaluate()) + .hasMessage("The zoom level is too high or the geometry is too complex to compute a set of covering Bing tiles. Please use a lower zoom level or convert the geometry to its bounding box using the ST_Envelope function."); + + assertThat(assertions.expression("cardinality(geometry_to_bing_tiles(ST_Envelope(ST_GeometryFromText('" + largeWkt + "')), 16))")) + .isEqualTo(19939L); // Zoom level is too high - assertInvalidFunction("geometry_to_bing_tiles(ST_GeometryFromText('POLYGON ((0 0, 0 20, 20 20, 0 0))'), 20)", "The zoom level is too high to compute a set of covering Bing tiles."); - assertFunction("cardinality(geometry_to_bing_tiles(ST_GeometryFromText('POLYGON ((0 0, 0 20, 20 20, 0 0))'), 14))", BIGINT, 428787L); + assertTrinoExceptionThrownBy(() -> assertions.function("geometry_to_bing_tiles", "ST_GeometryFromText('POLYGON ((0 0, 0 20, 20 20, 0 0))')", "20").evaluate()) + .hasMessage("The zoom level is too high to compute a set of covering Bing tiles."); + + assertThat(assertions.function("cardinality", "geometry_to_bing_tiles(ST_GeometryFromText('POLYGON ((0 0, 0 20, 20 20, 0 0))'), 14)")) + .isEqualTo(428787L); } private void assertGeometryToBingTiles(String wkt, int zoomLevel, List expectedQuadKeys) { - assertFunction(format("transform(geometry_to_bing_tiles(ST_GeometryFromText('%s'), %s), x -> bing_tile_quadkey(x))", wkt, zoomLevel), new ArrayType(VARCHAR), expectedQuadKeys); + assertThat(assertions.expression("transform(geometry_to_bing_tiles(geometry, zoom), x -> bing_tile_quadkey(x))") + .binding("geometry", "ST_GeometryFromText('%s')".formatted(wkt)) + .binding("zoom", Integer.toString(zoomLevel))) + .hasType(new ArrayType(VARCHAR)) + .isEqualTo(expectedQuadKeys); } @Test public void testEqual() { - assertFunction("bing_tile(3, 5, 3) = bing_tile(3, 5, 3)", BOOLEAN, true); - assertFunction("bing_tile('213') = bing_tile(3, 5, 3)", BOOLEAN, true); - assertFunction("bing_tile('213') = bing_tile('213')", BOOLEAN, true); + assertThat(assertions.operator(EQUAL, "bing_tile(3, 5, 3)", "bing_tile(3, 5, 3)")) + .isEqualTo(true); + + assertThat(assertions.operator(EQUAL, "bing_tile('213')", "bing_tile(3, 5, 3)")) + .isEqualTo(true); + + assertThat(assertions.operator(EQUAL, "bing_tile('213')", "bing_tile('213')")) + .isEqualTo(true); - assertFunction("bing_tile(3, 5, 3) = bing_tile(3, 5, 4)", BOOLEAN, false); - assertFunction("bing_tile('213') = bing_tile('2131')", BOOLEAN, false); + assertThat(assertions.operator(EQUAL, "bing_tile(3, 5, 3)", "bing_tile(3, 5, 4)")) + .isEqualTo(false); + + assertThat(assertions.operator(EQUAL, "bing_tile('213')", "bing_tile('2131')")) + .isEqualTo(false); } @Test public void testNotEqual() { - assertFunction("bing_tile(3, 5, 3) <> bing_tile(3, 5, 3)", BOOLEAN, false); - assertFunction("bing_tile('213') <> bing_tile(3, 5, 3)", BOOLEAN, false); - assertFunction("bing_tile('213') <> bing_tile('213')", BOOLEAN, false); - - assertFunction("bing_tile(3, 5, 3) <> bing_tile(3, 5, 4)", BOOLEAN, true); - assertFunction("bing_tile('213') <> bing_tile('2131')", BOOLEAN, true); + assertThat(assertions.expression("a <> b") + .binding("a", "bing_tile(3, 5, 3)") + .binding("b", "bing_tile(3, 5, 3)")) + .isEqualTo(false); + + assertThat(assertions.expression("a <> b") + .binding("a", "bing_tile('213')") + .binding("b", "bing_tile(3, 5, 3)")) + .isEqualTo(false); + + assertThat(assertions.expression("a <> b") + .binding("a", "bing_tile('213')") + .binding("b", "bing_tile('213')")) + .isEqualTo(false); + + assertThat(assertions.expression("a <> b") + .binding("a", "bing_tile(3, 5, 3)") + .binding("b", "bing_tile(3, 5, 4)")) + .isEqualTo(true); + + assertThat(assertions.expression("a <> b") + .binding("a", "bing_tile('213')") + .binding("b", "bing_tile('2131')")) + .isEqualTo(true); } @Test public void testDistinctFrom() { - assertFunction("null IS DISTINCT FROM null", BOOLEAN, false); - assertFunction("bing_tile(3, 5, 3) IS DISTINCT FROM null", BOOLEAN, true); - assertFunction("null IS DISTINCT FROM bing_tile(3, 5, 3)", BOOLEAN, true); + assertThat(assertions.operator(IS_DISTINCT_FROM, "null", "null")) + .isEqualTo(false); + + assertThat(assertions.operator(IS_DISTINCT_FROM, "bing_tile(3, 5, 3)", "null")) + .isEqualTo(true); + + assertThat(assertions.operator(IS_DISTINCT_FROM, "null", "bing_tile(3, 5, 3)")) + .isEqualTo(true); + + assertThat(assertions.operator(IS_DISTINCT_FROM, "bing_tile(3, 5, 3)", "bing_tile(3, 5, 3)")) + .isEqualTo(false); + + assertThat(assertions.operator(IS_DISTINCT_FROM, "bing_tile('213')", "bing_tile(3, 5, 3)")) + .isEqualTo(false); + + assertThat(assertions.operator(IS_DISTINCT_FROM, "bing_tile('213')", "bing_tile('213')")) + .isEqualTo(false); - assertFunction("bing_tile(3, 5, 3) IS DISTINCT FROM bing_tile(3, 5, 3)", BOOLEAN, false); - assertFunction("bing_tile('213') IS DISTINCT FROM bing_tile(3, 5, 3)", BOOLEAN, false); - assertFunction("bing_tile('213') IS DISTINCT FROM bing_tile('213')", BOOLEAN, false); + assertThat(assertions.operator(IS_DISTINCT_FROM, "bing_tile(3, 5, 3)", "bing_tile(3, 5, 4)")) + .isEqualTo(true); - assertFunction("bing_tile(3, 5, 3) IS DISTINCT FROM bing_tile(3, 5, 4)", BOOLEAN, true); - assertFunction("bing_tile('213') IS DISTINCT FROM bing_tile('2131')", BOOLEAN, true); + assertThat(assertions.operator(IS_DISTINCT_FROM, "bing_tile('213')", "bing_tile('2131')")) + .isEqualTo(true); } } diff --git a/plugin/trino-geospatial/src/test/java/io/trino/plugin/geospatial/TestGeoFunctions.java b/plugin/trino-geospatial/src/test/java/io/trino/plugin/geospatial/TestGeoFunctions.java index 7d81ed85d2b2..415a610ab544 100644 --- a/plugin/trino-geospatial/src/test/java/io/trino/plugin/geospatial/TestGeoFunctions.java +++ b/plugin/trino-geospatial/src/test/java/io/trino/plugin/geospatial/TestGeoFunctions.java @@ -18,13 +18,15 @@ import com.google.common.collect.ImmutableList; import io.trino.geospatial.KdbTreeUtils; import io.trino.geospatial.Rectangle; -import io.trino.operator.scalar.AbstractTestFunctions; import io.trino.spi.block.Block; import io.trino.spi.block.BlockBuilder; import io.trino.spi.type.ArrayType; import io.trino.spi.type.RowType; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; +import io.trino.sql.query.QueryAssertions; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; import java.util.Arrays; import java.util.List; @@ -33,22 +35,32 @@ import static com.google.common.collect.ImmutableList.toImmutableList; import static io.trino.geospatial.KdbTree.buildKdbTree; import static io.trino.plugin.geospatial.GeometryType.GEOMETRY; -import static io.trino.spi.type.BigintType.BIGINT; import static io.trino.spi.type.BooleanType.BOOLEAN; import static io.trino.spi.type.DoubleType.DOUBLE; import static io.trino.spi.type.IntegerType.INTEGER; -import static io.trino.spi.type.TinyintType.TINYINT; import static io.trino.spi.type.VarcharType.VARCHAR; -import static java.lang.String.format; +import static io.trino.testing.assertions.TrinoExceptionAssert.assertTrinoExceptionThrownBy; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS; import static org.testng.Assert.assertEquals; +@TestInstance(PER_CLASS) public class TestGeoFunctions - extends AbstractTestFunctions { - @BeforeClass - public void registerFunctions() + private QueryAssertions assertions; + + @BeforeAll + public void init() { - functionAssertions.installPlugin(new GeoPlugin()); + assertions = new QueryAssertions(); + assertions.addPlugin(new GeoPlugin()); + } + + @AfterAll + public void teardown() + { + assertions.close(); + assertions = null; } @Test @@ -99,12 +111,16 @@ private static String makeKdbTreeJson() private void assertSpatialPartitions(String kdbTreeJson, String wkt, List expectedPartitions) { - assertFunction(format("spatial_partitions(cast('%s' as KdbTree), ST_GeometryFromText('%s'))", kdbTreeJson, wkt), new ArrayType(INTEGER), expectedPartitions); + assertThat(assertions.function("spatial_partitions", "cast('%s' as KdbTree)".formatted(kdbTreeJson), "ST_GeometryFromText('%s')".formatted(wkt))) + .hasType(new ArrayType(INTEGER)) + .isEqualTo(expectedPartitions); } private void assertSpatialPartitions(String kdbTreeJson, String wkt, double distance, List expectedPartitions) { - assertFunction(format("spatial_partitions(cast('%s' as KdbTree), ST_GeometryFromText('%s'), %s)", kdbTreeJson, wkt, distance), new ArrayType(INTEGER), expectedPartitions); + assertThat(assertions.function("spatial_partitions", "cast('%s' as KdbTree)".formatted(kdbTreeJson), "ST_GeometryFromText('%s')".formatted(wkt), Double.toString(distance))) + .hasType(new ArrayType(INTEGER)) + .isEqualTo(expectedPartitions); } @Test @@ -120,25 +136,46 @@ public void testGeometryGetObjectValue() @Test public void testSTPoint() { - assertFunction("ST_AsText(ST_Point(1, 4))", VARCHAR, "POINT (1 4)"); - assertFunction("ST_AsText(ST_Point(122.3, 10.55))", VARCHAR, "POINT (122.3 10.55)"); + assertThat(assertions.function("ST_AsText", "ST_Point(1, 4)")) + .hasType(VARCHAR) + .isEqualTo("POINT (1 4)"); + + assertThat(assertions.function("ST_AsText", "ST_Point(122.3, 10.55)")) + .hasType(VARCHAR) + .isEqualTo("POINT (122.3 10.55)"); } @Test public void testSTLineFromText() { - assertFunction("ST_AsText(ST_LineFromText('LINESTRING EMPTY'))", VARCHAR, "LINESTRING EMPTY"); - assertFunction("ST_AsText(ST_LineFromText('LINESTRING (1 1, 2 2, 1 3)'))", VARCHAR, "LINESTRING (1 1, 2 2, 1 3)"); - assertInvalidFunction("ST_AsText(ST_LineFromText('MULTILINESTRING EMPTY'))", "ST_LineFromText only applies to LINE_STRING. Input type is: MULTI_LINE_STRING"); - assertInvalidFunction("ST_AsText(ST_LineFromText('POLYGON ((1 1, 1 4, 4 4, 4 1))'))", "ST_LineFromText only applies to LINE_STRING. Input type is: POLYGON"); + assertThat(assertions.function("ST_AsText", "ST_LineFromText('LINESTRING EMPTY')")) + .hasType(VARCHAR) + .isEqualTo("LINESTRING EMPTY"); + + assertThat(assertions.function("ST_AsText", "ST_LineFromText('LINESTRING (1 1, 2 2, 1 3)')")) + .hasType(VARCHAR) + .isEqualTo("LINESTRING (1 1, 2 2, 1 3)"); + + assertTrinoExceptionThrownBy(() -> assertions.function("ST_AsText", "ST_LineFromText('MULTILINESTRING EMPTY')").evaluate()) + .hasMessage("ST_LineFromText only applies to LINE_STRING. Input type is: MULTI_LINE_STRING"); + + assertTrinoExceptionThrownBy(() -> assertions.function("ST_AsText", "ST_LineFromText('POLYGON ((1 1, 1 4, 4 4, 4 1))')").evaluate()) + .hasMessage("ST_LineFromText only applies to LINE_STRING. Input type is: POLYGON"); } @Test public void testSTPolygon() { - assertFunction("ST_AsText(ST_Polygon('POLYGON EMPTY'))", VARCHAR, "POLYGON EMPTY"); - assertFunction("ST_AsText(ST_Polygon('POLYGON ((1 1, 1 4, 4 4, 4 1))'))", VARCHAR, "POLYGON ((1 1, 4 1, 4 4, 1 4, 1 1))"); - assertInvalidFunction("ST_AsText(ST_Polygon('LINESTRING (1 1, 2 2, 1 3)'))", "ST_Polygon only applies to POLYGON. Input type is: LINE_STRING"); + assertThat(assertions.function("ST_AsText", "ST_Polygon('POLYGON EMPTY')")) + .hasType(VARCHAR) + .isEqualTo("POLYGON EMPTY"); + + assertThat(assertions.function("ST_AsText", "ST_Polygon('POLYGON ((1 1, 1 4, 4 4, 4 1))')")) + .hasType(VARCHAR) + .isEqualTo("POLYGON ((1 1, 4 1, 4 4, 1 4, 1 1))"); + + assertTrinoExceptionThrownBy(() -> assertions.function("ST_AsText", "ST_Polygon('LINESTRING (1 1, 2 2, 1 3)')").evaluate()) + .hasMessage("ST_Polygon only applies to POLYGON. Input type is: LINE_STRING"); } @Test @@ -164,48 +201,102 @@ public void testSTArea() private void assertArea(String wkt, double expectedArea) { - assertFunction(format("ST_Area(ST_GeometryFromText('%s'))", wkt), DOUBLE, expectedArea); + assertThat(assertions.expression("ST_Area(geometry)") + .binding("geometry", "ST_GeometryFromText('%s')".formatted(wkt))) + .isEqualTo(expectedArea); } @Test public void testSTBuffer() { - assertFunction("ST_AsText(ST_Buffer(ST_Point(0, 0), 0.5))", VARCHAR, "POLYGON ((0.5 0, 0.4989294616193014 0.03270156461507146, 0.49572243068690486 0.0652630961100257, 0.4903926402016149 0.09754516100806403, 0.4829629131445338 0.12940952255126026, 0.47346506474755257 0.16071973265158065, 0.46193976625564315 0.19134171618254472, 0.4484363707663439 0.22114434510950046, 0.43301270189221913 0.2499999999999998, 0.41573480615127245 0.2777851165098009, 0.39667667014561747 0.30438071450436016, 0.3759199037394886 0.32967290755003426, 0.3535533905932737 0.3535533905932736, 0.32967290755003437 0.3759199037394886, 0.3043807145043603 0.39667667014561747, 0.2777851165098011 0.4157348061512725, 0.24999999999999997 0.43301270189221924, 0.22114434510950062 0.4484363707663441, 0.19134171618254486 0.4619397662556433, 0.16071973265158077 0.4734650647475528, 0.12940952255126037 0.48296291314453416, 0.09754516100806412 0.4903926402016152, 0.06526309611002579 0.4957224306869052, 0.03270156461507153 0.49892946161930174, 0 0.5, -0.03270156461507146 0.4989294616193014, -0.0652630961100257 0.49572243068690486, -0.09754516100806403 0.4903926402016149, -0.12940952255126026 0.4829629131445338, -0.16071973265158065 0.47346506474755257, -0.19134171618254472 0.46193976625564315, -0.22114434510950046 0.4484363707663439, -0.2499999999999998 0.43301270189221913, -0.2777851165098009 0.41573480615127245, -0.30438071450436016 0.39667667014561747, -0.32967290755003426 0.3759199037394886, -0.3535533905932736 0.3535533905932737, -0.3759199037394886 0.32967290755003437, -0.39667667014561747 0.3043807145043603, -0.4157348061512725 0.2777851165098011, -0.43301270189221924 0.24999999999999997, -0.4484363707663441 0.22114434510950062, -0.4619397662556433 0.19134171618254486, -0.4734650647475528 0.16071973265158077, -0.48296291314453416 0.12940952255126037, -0.4903926402016152 0.09754516100806412, -0.4957224306869052 0.06526309611002579, -0.49892946161930174 0.03270156461507153, -0.5 0, -0.4989294616193014 -0.03270156461507146, -0.49572243068690486 -0.0652630961100257, -0.4903926402016149 -0.09754516100806403, -0.4829629131445338 -0.12940952255126026, -0.47346506474755257 -0.16071973265158065, -0.46193976625564315 -0.19134171618254472, -0.4484363707663439 -0.22114434510950046, -0.43301270189221913 -0.2499999999999998, -0.41573480615127245 -0.2777851165098009, -0.39667667014561747 -0.30438071450436016, -0.3759199037394886 -0.32967290755003426, -0.3535533905932737 -0.3535533905932736, -0.32967290755003437 -0.3759199037394886, -0.3043807145043603 -0.39667667014561747, -0.2777851165098011 -0.4157348061512725, -0.24999999999999997 -0.43301270189221924, -0.22114434510950062 -0.4484363707663441, -0.19134171618254486 -0.4619397662556433, -0.16071973265158077 -0.4734650647475528, -0.12940952255126037 -0.48296291314453416, -0.09754516100806412 -0.4903926402016152, -0.06526309611002579 -0.4957224306869052, -0.03270156461507153 -0.49892946161930174, 0 -0.5, 0.03270156461507146 -0.4989294616193014, 0.0652630961100257 -0.49572243068690486, 0.09754516100806403 -0.4903926402016149, 0.12940952255126026 -0.4829629131445338, 0.16071973265158065 -0.47346506474755257, 0.19134171618254472 -0.46193976625564315, 0.22114434510950046 -0.4484363707663439, 0.2499999999999998 -0.43301270189221913, 0.2777851165098009 -0.41573480615127245, 0.30438071450436016 -0.39667667014561747, 0.32967290755003426 -0.3759199037394886, 0.3535533905932736 -0.3535533905932737, 0.3759199037394886 -0.32967290755003437, 0.39667667014561747 -0.3043807145043603, 0.4157348061512725 -0.2777851165098011, 0.43301270189221924 -0.24999999999999997, 0.4484363707663441 -0.22114434510950062, 0.4619397662556433 -0.19134171618254486, 0.4734650647475528 -0.16071973265158077, 0.48296291314453416 -0.12940952255126037, 0.4903926402016152 -0.09754516100806412, 0.4957224306869052 -0.06526309611002579, 0.49892946161930174 -0.03270156461507153, 0.5 0))"); - assertFunction("ST_AsText(ST_Buffer(ST_LineFromText('LINESTRING (0 0, 1 1, 2 0.5)'), 0.2))", VARCHAR, "POLYGON ((0 -0.19999999999999996, 0.013080625846028537 -0.19957178464772052, 0.02610523844401036 -0.19828897227476194, 0.03901806440322564 -0.19615705608064593, 0.05176380902050415 -0.1931851652578136, 0.06428789306063232 -0.18938602589902098, 0.07653668647301792 -0.18477590650225728, 0.0884577380438003 -0.17937454830653754, 0.09999999999999987 -0.17320508075688767, 0.11111404660392044 -0.166293922460509, 0.12175228580174413 -0.15867066805824703, 0.13186916302001372 -0.15036796149579545, 0.14142135623730945 -0.14142135623730945, 1.0394906098164265 0.7566478973418078, 1.9105572809000084 0.32111456180001685, 1.9115422619561997 0.32062545169346235, 1.923463313526982 0.31522409349774266, 1.9357121069393677 0.3106139741009789, 1.9482361909794959 0.3068148347421863, 1.9609819355967744 0.3038429439193539, 1.9738947615559896 0.30171102772523795, 1.9869193741539715 0.30042821535227926, 2 0.3, 2.0130806258460288 0.3004282153522794, 2.02610523844401 0.30171102772523806, 2.0390180644032254 0.30384294391935407, 2.051763809020504 0.30681483474218646, 2.0642878930606323 0.31061397410097896, 2.076536686473018 0.3152240934977427, 2.0884577380438003 0.32062545169346246, 2.1 0.3267949192431123, 2.1111140466039204 0.333706077539491, 2.121752285801744 0.34132933194175297, 2.1318691630200135 0.34963203850420455, 2.1414213562373092 0.35857864376269055, 2.1503679614957956 0.3681308369799863, 2.158670668058247 0.37824771419825587, 2.166293922460509 0.38888595339607956, 2.1732050807568877 0.4, 2.1793745483065377 0.41154226195619975, 2.1847759065022574 0.4234633135269821, 2.189386025899021 0.4357121069393677, 2.193185165257814 0.44823619097949585, 2.1961570560806463 0.46098193559677436, 2.1982889722747623 0.4738947615559897, 2.1995717846477207 0.4869193741539714, 2.2 0.5, 2.1995717846477207 0.5130806258460285, 2.198288972274762 0.5261052384440102, 2.196157056080646 0.5390180644032256, 2.1931851652578134 0.5517638090205041, 2.189386025899021 0.5642878930606323, 2.1847759065022574 0.5765366864730179, 2.1793745483065377 0.5884577380438002, 2.1732050807568877 0.5999999999999999, 2.166293922460509 0.6111140466039204, 2.158670668058247 0.6217522858017441, 2.1503679614957956 0.6318691630200137, 2.1414213562373097 0.6414213562373094, 2.131869163020014 0.6503679614957955, 2.121752285801744 0.658670668058247, 2.1111140466039204 0.666293922460509, 2.1 0.6732050807568877, 2.0894427190999916 0.6788854381999831, 1.0894427190999916 1.1788854381999831, 1.0884577380438003 1.1793745483065377, 1.076536686473018 1.1847759065022574, 1.0642878930606323 1.189386025899021, 1.0517638090205041 1.1931851652578138, 1.0390180644032256 1.196157056080646, 1.0261052384440104 1.198288972274762, 1.0130806258460288 1.1995717846477207, 1 1.2, 0.9869193741539715 1.1995717846477205, 0.9738947615559896 1.1982889722747618, 0.9609819355967744 1.1961570560806458, 0.9482361909794959 1.1931851652578136, 0.9357121069393677 1.189386025899021, 0.9234633135269821 1.1847759065022574, 0.9115422619561997 1.1793745483065377, 0.9000000000000001 1.1732050807568877, 0.8888859533960796 1.166293922460509, 0.8782477141982559 1.158670668058247, 0.8681308369799863 1.1503679614957956, 0.8585786437626906 1.1414213562373094, -0.14142135623730967 0.1414213562373095, -0.15036796149579557 0.13186916302001372, -0.1586706680582468 0.12175228580174413, -0.1662939224605089 0.11111404660392044, -0.17320508075688767 0.09999999999999998, -0.17937454830653765 0.08845773804380025, -0.1847759065022574 0.07653668647301792, -0.18938602589902098 0.06428789306063232, -0.19318516525781382 0.05176380902050415, -0.19615705608064626 0.03901806440322564, -0.19828897227476228 0.026105238444010304, -0.19957178464772074 0.013080625846028593, -0.20000000000000018 0, -0.19957178464772074 -0.013080625846028537, -0.19828897227476183 -0.026105238444010248, -0.19615705608064582 -0.03901806440322564, -0.19318516525781337 -0.05176380902050415, -0.18938602589902098 -0.06428789306063232, -0.1847759065022574 -0.07653668647301792, -0.17937454830653765 -0.0884577380438002, -0.17320508075688767 -0.09999999999999987, -0.1662939224605089 -0.11111404660392044, -0.1586706680582468 -0.12175228580174413, -0.15036796149579557 -0.13186916302001372, -0.14142135623730967 -0.14142135623730945, -0.13186916302001395 -0.15036796149579545, -0.12175228580174391 -0.15867066805824703, -0.11111404660392044 -0.166293922460509, -0.10000000000000009 -0.17320508075688767, -0.0884577380438003 -0.17937454830653765, -0.07653668647301792 -0.1847759065022574, -0.06428789306063232 -0.1893860258990211, -0.05176380902050415 -0.1931851652578137, -0.03901806440322586 -0.19615705608064604, -0.026105238444010137 -0.19828897227476205, -0.01308062584602876 -0.19957178464772074, 0 -0.19999999999999996))"); - assertFunction("ST_AsText(ST_Buffer(ST_GeometryFromText('POLYGON ((0 0, 0 5, 5 5, 5 0, 0 0))'), 1.2))", VARCHAR, "POLYGON ((-1.2 0, -1.1974307078863233 -0.0784837550761715, -1.1897338336485717 -0.15663143066406168, -1.1769423364838756 -0.23410838641935366, -1.1591109915468811 -0.3105828541230246, -1.1363161553941261 -0.38572735836379357, -1.1086554390135435 -0.4592201188381073, -1.0762472898392252 -0.530746428262801, -1.0392304845413258 -0.5999999999999995, -0.9977635347630538 -0.6666842796235222, -0.9520240083494819 -0.7305137148104643, -0.9022077689747725 -0.7912149781200822, -0.8485281374238568 -0.8485281374238567, -0.7912149781200825 -0.9022077689747725, -0.7305137148104647 -0.9520240083494819, -0.6666842796235226 -0.997763534763054, -0.5999999999999999 -1.039230484541326, -0.5307464282628015 -1.0762472898392257, -0.45922011883810765 -1.108655439013544, -0.38572735836379385 -1.1363161553941266, -0.3105828541230249 -1.159110991546882, -0.2341083864193539 -1.1769423364838765, -0.15663143066406188 -1.1897338336485723, -0.07848375507617167 -1.1974307078863242, 0 -1.2, 5 -1.2, 5.078483755076172 -1.1974307078863233, 5.156631430664062 -1.1897338336485717, 5.234108386419353 -1.1769423364838756, 5.310582854123025 -1.1591109915468811, 5.385727358363794 -1.1363161553941261, 5.4592201188381075 -1.1086554390135435, 5.530746428262801 -1.0762472898392252, 5.6 -1.0392304845413258, 5.666684279623523 -0.9977635347630538, 5.730513714810464 -0.9520240083494819, 5.791214978120082 -0.9022077689747725, 5.848528137423857 -0.8485281374238568, 5.9022077689747725 -0.7912149781200825, 5.952024008349482 -0.7305137148104647, 5.997763534763054 -0.6666842796235226, 6.039230484541326 -0.5999999999999999, 6.076247289839226 -0.5307464282628015, 6.108655439013544 -0.45922011883810765, 6.136316155394127 -0.38572735836379385, 6.159110991546882 -0.3105828541230249, 6.176942336483877 -0.2341083864193539, 6.189733833648573 -0.15663143066406188, 6.197430707886324 -0.07848375507617167, 6.2 0, 6.2 5, 6.1974307078863236 5.078483755076172, 6.189733833648572 5.156631430664062, 6.176942336483876 5.234108386419353, 6.159110991546881 5.310582854123025, 6.136316155394126 5.385727358363794, 6.1086554390135435 5.4592201188381075, 6.076247289839225 5.530746428262801, 6.039230484541326 5.6, 5.997763534763054 5.666684279623523, 5.952024008349482 5.730513714810464, 5.9022077689747725 5.791214978120082, 5.848528137423857 5.848528137423857, 5.791214978120083 5.9022077689747725, 5.730513714810464 5.952024008349482, 5.666684279623523 5.997763534763054, 5.6 6.039230484541326, 5.530746428262802 6.076247289839226, 5.4592201188381075 6.108655439013544, 5.385727358363794 6.136316155394127, 5.310582854123025 6.159110991546882, 5.234108386419354 6.176942336483877, 5.156631430664062 6.189733833648573, 5.078483755076172 6.197430707886324, 5 6.2, 0 6.2, -0.0784837550761715 6.1974307078863236, -0.15663143066406168 6.189733833648572, -0.23410838641935366 6.176942336483876, -0.3105828541230246 6.159110991546881, -0.38572735836379357 6.136316155394126, -0.4592201188381073 6.1086554390135435, -0.530746428262801 6.076247289839225, -0.5999999999999995 6.039230484541326, -0.6666842796235222 5.997763534763054, -0.7305137148104643 5.952024008349482, -0.7912149781200822 5.9022077689747725, -0.8485281374238567 5.848528137423857, -0.9022077689747725 5.791214978120083, -0.9520240083494819 5.730513714810464, -0.997763534763054 5.666684279623523, -1.039230484541326 5.6, -1.0762472898392257 5.530746428262802, -1.108655439013544 5.4592201188381075, -1.1363161553941266 5.385727358363794, -1.159110991546882 5.310582854123025, -1.1769423364838765 5.234108386419354, -1.1897338336485723 5.156631430664062, -1.1974307078863242 5.078483755076172, -1.2 5, -1.2 0))"); + assertThat(assertions.function("ST_AsText", "ST_Buffer(ST_Point(0, 0), 0.5)")) + .hasType(VARCHAR) + .isEqualTo("POLYGON ((0.5 0, 0.4989294616193014 0.03270156461507146, 0.49572243068690486 0.0652630961100257, 0.4903926402016149 0.09754516100806403, 0.4829629131445338 0.12940952255126026, 0.47346506474755257 0.16071973265158065, 0.46193976625564315 0.19134171618254472, 0.4484363707663439 0.22114434510950046, 0.43301270189221913 0.2499999999999998, 0.41573480615127245 0.2777851165098009, 0.39667667014561747 0.30438071450436016, 0.3759199037394886 0.32967290755003426, 0.3535533905932737 0.3535533905932736, 0.32967290755003437 0.3759199037394886, 0.3043807145043603 0.39667667014561747, 0.2777851165098011 0.4157348061512725, 0.24999999999999997 0.43301270189221924, 0.22114434510950062 0.4484363707663441, 0.19134171618254486 0.4619397662556433, 0.16071973265158077 0.4734650647475528, 0.12940952255126037 0.48296291314453416, 0.09754516100806412 0.4903926402016152, 0.06526309611002579 0.4957224306869052, 0.03270156461507153 0.49892946161930174, 0 0.5, -0.03270156461507146 0.4989294616193014, -0.0652630961100257 0.49572243068690486, -0.09754516100806403 0.4903926402016149, -0.12940952255126026 0.4829629131445338, -0.16071973265158065 0.47346506474755257, -0.19134171618254472 0.46193976625564315, -0.22114434510950046 0.4484363707663439, -0.2499999999999998 0.43301270189221913, -0.2777851165098009 0.41573480615127245, -0.30438071450436016 0.39667667014561747, -0.32967290755003426 0.3759199037394886, -0.3535533905932736 0.3535533905932737, -0.3759199037394886 0.32967290755003437, -0.39667667014561747 0.3043807145043603, -0.4157348061512725 0.2777851165098011, -0.43301270189221924 0.24999999999999997, -0.4484363707663441 0.22114434510950062, -0.4619397662556433 0.19134171618254486, -0.4734650647475528 0.16071973265158077, -0.48296291314453416 0.12940952255126037, -0.4903926402016152 0.09754516100806412, -0.4957224306869052 0.06526309611002579, -0.49892946161930174 0.03270156461507153, -0.5 0, -0.4989294616193014 -0.03270156461507146, -0.49572243068690486 -0.0652630961100257, -0.4903926402016149 -0.09754516100806403, -0.4829629131445338 -0.12940952255126026, -0.47346506474755257 -0.16071973265158065, -0.46193976625564315 -0.19134171618254472, -0.4484363707663439 -0.22114434510950046, -0.43301270189221913 -0.2499999999999998, -0.41573480615127245 -0.2777851165098009, -0.39667667014561747 -0.30438071450436016, -0.3759199037394886 -0.32967290755003426, -0.3535533905932737 -0.3535533905932736, -0.32967290755003437 -0.3759199037394886, -0.3043807145043603 -0.39667667014561747, -0.2777851165098011 -0.4157348061512725, -0.24999999999999997 -0.43301270189221924, -0.22114434510950062 -0.4484363707663441, -0.19134171618254486 -0.4619397662556433, -0.16071973265158077 -0.4734650647475528, -0.12940952255126037 -0.48296291314453416, -0.09754516100806412 -0.4903926402016152, -0.06526309611002579 -0.4957224306869052, -0.03270156461507153 -0.49892946161930174, 0 -0.5, 0.03270156461507146 -0.4989294616193014, 0.0652630961100257 -0.49572243068690486, 0.09754516100806403 -0.4903926402016149, 0.12940952255126026 -0.4829629131445338, 0.16071973265158065 -0.47346506474755257, 0.19134171618254472 -0.46193976625564315, 0.22114434510950046 -0.4484363707663439, 0.2499999999999998 -0.43301270189221913, 0.2777851165098009 -0.41573480615127245, 0.30438071450436016 -0.39667667014561747, 0.32967290755003426 -0.3759199037394886, 0.3535533905932736 -0.3535533905932737, 0.3759199037394886 -0.32967290755003437, 0.39667667014561747 -0.3043807145043603, 0.4157348061512725 -0.2777851165098011, 0.43301270189221924 -0.24999999999999997, 0.4484363707663441 -0.22114434510950062, 0.4619397662556433 -0.19134171618254486, 0.4734650647475528 -0.16071973265158077, 0.48296291314453416 -0.12940952255126037, 0.4903926402016152 -0.09754516100806412, 0.4957224306869052 -0.06526309611002579, 0.49892946161930174 -0.03270156461507153, 0.5 0))"); + + assertThat(assertions.function("ST_AsText", "ST_Buffer(ST_LineFromText('LINESTRING (0 0, 1 1, 2 0.5)'), 0.2)")) + .hasType(VARCHAR) + .isEqualTo("POLYGON ((0 -0.19999999999999996, 0.013080625846028537 -0.19957178464772052, 0.02610523844401036 -0.19828897227476194, 0.03901806440322564 -0.19615705608064593, 0.05176380902050415 -0.1931851652578136, 0.06428789306063232 -0.18938602589902098, 0.07653668647301792 -0.18477590650225728, 0.0884577380438003 -0.17937454830653754, 0.09999999999999987 -0.17320508075688767, 0.11111404660392044 -0.166293922460509, 0.12175228580174413 -0.15867066805824703, 0.13186916302001372 -0.15036796149579545, 0.14142135623730945 -0.14142135623730945, 1.0394906098164265 0.7566478973418078, 1.9105572809000084 0.32111456180001685, 1.9115422619561997 0.32062545169346235, 1.923463313526982 0.31522409349774266, 1.9357121069393677 0.3106139741009789, 1.9482361909794959 0.3068148347421863, 1.9609819355967744 0.3038429439193539, 1.9738947615559896 0.30171102772523795, 1.9869193741539715 0.30042821535227926, 2 0.3, 2.0130806258460288 0.3004282153522794, 2.02610523844401 0.30171102772523806, 2.0390180644032254 0.30384294391935407, 2.051763809020504 0.30681483474218646, 2.0642878930606323 0.31061397410097896, 2.076536686473018 0.3152240934977427, 2.0884577380438003 0.32062545169346246, 2.1 0.3267949192431123, 2.1111140466039204 0.333706077539491, 2.121752285801744 0.34132933194175297, 2.1318691630200135 0.34963203850420455, 2.1414213562373092 0.35857864376269055, 2.1503679614957956 0.3681308369799863, 2.158670668058247 0.37824771419825587, 2.166293922460509 0.38888595339607956, 2.1732050807568877 0.4, 2.1793745483065377 0.41154226195619975, 2.1847759065022574 0.4234633135269821, 2.189386025899021 0.4357121069393677, 2.193185165257814 0.44823619097949585, 2.1961570560806463 0.46098193559677436, 2.1982889722747623 0.4738947615559897, 2.1995717846477207 0.4869193741539714, 2.2 0.5, 2.1995717846477207 0.5130806258460285, 2.198288972274762 0.5261052384440102, 2.196157056080646 0.5390180644032256, 2.1931851652578134 0.5517638090205041, 2.189386025899021 0.5642878930606323, 2.1847759065022574 0.5765366864730179, 2.1793745483065377 0.5884577380438002, 2.1732050807568877 0.5999999999999999, 2.166293922460509 0.6111140466039204, 2.158670668058247 0.6217522858017441, 2.1503679614957956 0.6318691630200137, 2.1414213562373097 0.6414213562373094, 2.131869163020014 0.6503679614957955, 2.121752285801744 0.658670668058247, 2.1111140466039204 0.666293922460509, 2.1 0.6732050807568877, 2.0894427190999916 0.6788854381999831, 1.0894427190999916 1.1788854381999831, 1.0884577380438003 1.1793745483065377, 1.076536686473018 1.1847759065022574, 1.0642878930606323 1.189386025899021, 1.0517638090205041 1.1931851652578138, 1.0390180644032256 1.196157056080646, 1.0261052384440104 1.198288972274762, 1.0130806258460288 1.1995717846477207, 1 1.2, 0.9869193741539715 1.1995717846477205, 0.9738947615559896 1.1982889722747618, 0.9609819355967744 1.1961570560806458, 0.9482361909794959 1.1931851652578136, 0.9357121069393677 1.189386025899021, 0.9234633135269821 1.1847759065022574, 0.9115422619561997 1.1793745483065377, 0.9000000000000001 1.1732050807568877, 0.8888859533960796 1.166293922460509, 0.8782477141982559 1.158670668058247, 0.8681308369799863 1.1503679614957956, 0.8585786437626906 1.1414213562373094, -0.14142135623730967 0.1414213562373095, -0.15036796149579557 0.13186916302001372, -0.1586706680582468 0.12175228580174413, -0.1662939224605089 0.11111404660392044, -0.17320508075688767 0.09999999999999998, -0.17937454830653765 0.08845773804380025, -0.1847759065022574 0.07653668647301792, -0.18938602589902098 0.06428789306063232, -0.19318516525781382 0.05176380902050415, -0.19615705608064626 0.03901806440322564, -0.19828897227476228 0.026105238444010304, -0.19957178464772074 0.013080625846028593, -0.20000000000000018 0, -0.19957178464772074 -0.013080625846028537, -0.19828897227476183 -0.026105238444010248, -0.19615705608064582 -0.03901806440322564, -0.19318516525781337 -0.05176380902050415, -0.18938602589902098 -0.06428789306063232, -0.1847759065022574 -0.07653668647301792, -0.17937454830653765 -0.0884577380438002, -0.17320508075688767 -0.09999999999999987, -0.1662939224605089 -0.11111404660392044, -0.1586706680582468 -0.12175228580174413, -0.15036796149579557 -0.13186916302001372, -0.14142135623730967 -0.14142135623730945, -0.13186916302001395 -0.15036796149579545, -0.12175228580174391 -0.15867066805824703, -0.11111404660392044 -0.166293922460509, -0.10000000000000009 -0.17320508075688767, -0.0884577380438003 -0.17937454830653765, -0.07653668647301792 -0.1847759065022574, -0.06428789306063232 -0.1893860258990211, -0.05176380902050415 -0.1931851652578137, -0.03901806440322586 -0.19615705608064604, -0.026105238444010137 -0.19828897227476205, -0.01308062584602876 -0.19957178464772074, 0 -0.19999999999999996))"); + + assertThat(assertions.function("ST_AsText", "ST_Buffer(ST_GeometryFromText('POLYGON ((0 0, 0 5, 5 5, 5 0, 0 0))'), 1.2)")) + .hasType(VARCHAR) + .isEqualTo("POLYGON ((-1.2 0, -1.1974307078863233 -0.0784837550761715, -1.1897338336485717 -0.15663143066406168, -1.1769423364838756 -0.23410838641935366, -1.1591109915468811 -0.3105828541230246, -1.1363161553941261 -0.38572735836379357, -1.1086554390135435 -0.4592201188381073, -1.0762472898392252 -0.530746428262801, -1.0392304845413258 -0.5999999999999995, -0.9977635347630538 -0.6666842796235222, -0.9520240083494819 -0.7305137148104643, -0.9022077689747725 -0.7912149781200822, -0.8485281374238568 -0.8485281374238567, -0.7912149781200825 -0.9022077689747725, -0.7305137148104647 -0.9520240083494819, -0.6666842796235226 -0.997763534763054, -0.5999999999999999 -1.039230484541326, -0.5307464282628015 -1.0762472898392257, -0.45922011883810765 -1.108655439013544, -0.38572735836379385 -1.1363161553941266, -0.3105828541230249 -1.159110991546882, -0.2341083864193539 -1.1769423364838765, -0.15663143066406188 -1.1897338336485723, -0.07848375507617167 -1.1974307078863242, 0 -1.2, 5 -1.2, 5.078483755076172 -1.1974307078863233, 5.156631430664062 -1.1897338336485717, 5.234108386419353 -1.1769423364838756, 5.310582854123025 -1.1591109915468811, 5.385727358363794 -1.1363161553941261, 5.4592201188381075 -1.1086554390135435, 5.530746428262801 -1.0762472898392252, 5.6 -1.0392304845413258, 5.666684279623523 -0.9977635347630538, 5.730513714810464 -0.9520240083494819, 5.791214978120082 -0.9022077689747725, 5.848528137423857 -0.8485281374238568, 5.9022077689747725 -0.7912149781200825, 5.952024008349482 -0.7305137148104647, 5.997763534763054 -0.6666842796235226, 6.039230484541326 -0.5999999999999999, 6.076247289839226 -0.5307464282628015, 6.108655439013544 -0.45922011883810765, 6.136316155394127 -0.38572735836379385, 6.159110991546882 -0.3105828541230249, 6.176942336483877 -0.2341083864193539, 6.189733833648573 -0.15663143066406188, 6.197430707886324 -0.07848375507617167, 6.2 0, 6.2 5, 6.1974307078863236 5.078483755076172, 6.189733833648572 5.156631430664062, 6.176942336483876 5.234108386419353, 6.159110991546881 5.310582854123025, 6.136316155394126 5.385727358363794, 6.1086554390135435 5.4592201188381075, 6.076247289839225 5.530746428262801, 6.039230484541326 5.6, 5.997763534763054 5.666684279623523, 5.952024008349482 5.730513714810464, 5.9022077689747725 5.791214978120082, 5.848528137423857 5.848528137423857, 5.791214978120083 5.9022077689747725, 5.730513714810464 5.952024008349482, 5.666684279623523 5.997763534763054, 5.6 6.039230484541326, 5.530746428262802 6.076247289839226, 5.4592201188381075 6.108655439013544, 5.385727358363794 6.136316155394127, 5.310582854123025 6.159110991546882, 5.234108386419354 6.176942336483877, 5.156631430664062 6.189733833648573, 5.078483755076172 6.197430707886324, 5 6.2, 0 6.2, -0.0784837550761715 6.1974307078863236, -0.15663143066406168 6.189733833648572, -0.23410838641935366 6.176942336483876, -0.3105828541230246 6.159110991546881, -0.38572735836379357 6.136316155394126, -0.4592201188381073 6.1086554390135435, -0.530746428262801 6.076247289839225, -0.5999999999999995 6.039230484541326, -0.6666842796235222 5.997763534763054, -0.7305137148104643 5.952024008349482, -0.7912149781200822 5.9022077689747725, -0.8485281374238567 5.848528137423857, -0.9022077689747725 5.791214978120083, -0.9520240083494819 5.730513714810464, -0.997763534763054 5.666684279623523, -1.039230484541326 5.6, -1.0762472898392257 5.530746428262802, -1.108655439013544 5.4592201188381075, -1.1363161553941266 5.385727358363794, -1.159110991546882 5.310582854123025, -1.1769423364838765 5.234108386419354, -1.1897338336485723 5.156631430664062, -1.1974307078863242 5.078483755076172, -1.2 5, -1.2 0))"); // zero distance - assertFunction("ST_AsText(ST_Buffer(ST_Point(0, 0), 0))", VARCHAR, "POINT (0 0)"); - assertFunction("ST_AsText(ST_Buffer(ST_LineFromText('LINESTRING (0 0, 1 1, 2 0.5)'), 0))", VARCHAR, "LINESTRING (0 0, 1 1, 2 0.5)"); - assertFunction("ST_AsText(ST_Buffer(ST_GeometryFromText('POLYGON ((0 0, 0 5, 5 5, 5 0, 0 0))'), 0))", VARCHAR, "POLYGON ((0 0, 5 0, 5 5, 0 5, 0 0))"); + assertThat(assertions.function("ST_AsText", "ST_Buffer(ST_Point(0, 0), 0)")) + .hasType(VARCHAR) + .isEqualTo("POINT (0 0)"); + + assertThat(assertions.function("ST_AsText", "ST_Buffer(ST_LineFromText('LINESTRING (0 0, 1 1, 2 0.5)'), 0)")) + .hasType(VARCHAR) + .isEqualTo("LINESTRING (0 0, 1 1, 2 0.5)"); + + assertThat(assertions.function("ST_AsText", "ST_Buffer(ST_GeometryFromText('POLYGON ((0 0, 0 5, 5 5, 5 0, 0 0))'), 0)")) + .hasType(VARCHAR) + .isEqualTo("POLYGON ((0 0, 5 0, 5 5, 0 5, 0 0))"); // geometry collection - assertFunction("ST_AsText(ST_Buffer(ST_Intersection(ST_GeometryFromText('MULTILINESTRING ((1 1, 5 1), (2 4, 4 4))'), ST_GeometryFromText('MULTILINESTRING ((3 4, 6 4), (5 0, 5 4))')), 0.2))", VARCHAR, "MULTIPOLYGON (((5 0.8, 5.013080625846029 0.8004282153522794, 5.026105238444011 0.801711027725238, 5.039018064403225 0.803842943919354, 5.051763809020504 0.8068148347421864, 5.064287893060633 0.8106139741009789, 5.076536686473018 0.8152240934977427, 5.0884577380438 0.8206254516934623, 5.1 0.8267949192431123, 5.11111404660392 0.833706077539491, 5.121752285801744 0.841329331941753, 5.1318691630200135 0.8496320385042045, 5.141421356237309 0.8585786437626906, 5.150367961495795 0.8681308369799863, 5.158670668058247 0.8782477141982559, 5.166293922460509 0.8888859533960796, 5.173205080756888 0.9, 5.179374548306538 0.9115422619561997, 5.184775906502257 0.9234633135269821, 5.189386025899021 0.9357121069393677, 5.193185165257813 0.9482361909794959, 5.196157056080646 0.9609819355967744, 5.198288972274762 0.9738947615559896, 5.199571784647721 0.9869193741539714, 5.2 1, 5.199571784647721 1.0130806258460288, 5.198288972274762 1.0261052384440104, 5.196157056080646 1.0390180644032256, 5.193185165257813 1.0517638090205041, 5.189386025899021 1.0642878930606323, 5.184775906502257 1.076536686473018, 5.179374548306537 1.0884577380438003, 5.173205080756888 1.1, 5.166293922460509 1.1111140466039204, 5.158670668058247 1.1217522858017441, 5.150367961495795 1.1318691630200137, 5.141421356237309 1.1414213562373094, 5.1318691630200135 1.1503679614957956, 5.121752285801744 1.158670668058247, 5.11111404660392 1.1662939224605091, 5.1 1.1732050807568877, 5.0884577380438 1.1793745483065377, 5.076536686473018 1.1847759065022574, 5.064287893060632 1.1893860258990212, 5.051763809020504 1.1931851652578138, 5.039018064403225 1.196157056080646, 5.026105238444011 1.198288972274762, 5.013080625846029 1.1995717846477207, 5 1.2, 4.986919374153971 1.1995717846477207, 4.973894761555989 1.198288972274762, 4.960981935596775 1.196157056080646, 4.948236190979496 1.1931851652578136, 4.935712106939367 1.189386025899021, 4.923463313526982 1.1847759065022574, 4.9115422619562 1.1793745483065377, 4.9 1.1732050807568877, 4.88888595339608 1.166293922460509, 4.878247714198256 1.158670668058247, 4.8681308369799865 1.1503679614957956, 4.858578643762691 1.1414213562373094, 4.849632038504205 1.1318691630200137, 4.841329331941753 1.1217522858017441, 4.833706077539491 1.1111140466039204, 4.826794919243112 1.1, 4.820625451693462 1.0884577380438003, 4.815224093497743 1.076536686473018, 4.810613974100979 1.0642878930606323, 4.806814834742187 1.0517638090205041, 4.803842943919354 1.0390180644032256, 4.801711027725238 1.0261052384440104, 4.800428215352279 1.0130806258460285, 4.8 1, 4.800428215352279 0.9869193741539714, 4.801711027725238 0.9738947615559896, 4.803842943919354 0.9609819355967743, 4.806814834742187 0.9482361909794959, 4.810613974100979 0.9357121069393677, 4.815224093497743 0.923463313526982, 4.820625451693463 0.9115422619561997, 4.826794919243112 0.8999999999999999, 4.833706077539491 0.8888859533960796, 4.841329331941753 0.8782477141982559, 4.849632038504205 0.8681308369799862, 4.858578643762691 0.8585786437626904, 4.8681308369799865 0.8496320385042044, 4.878247714198256 0.841329331941753, 4.88888595339608 0.8337060775394909, 4.9 0.8267949192431122, 4.9115422619562 0.8206254516934623, 4.923463313526982 0.8152240934977426, 4.935712106939368 0.8106139741009788, 4.948236190979496 0.8068148347421863, 4.960981935596775 0.8038429439193538, 4.973894761555989 0.801711027725238, 4.986919374153971 0.8004282153522793, 5 0.8)), ((3 3.8, 4 3.8, 4.013080625846029 3.8004282153522793, 4.026105238444011 3.801711027725238, 4.039018064403225 3.803842943919354, 4.051763809020504 3.8068148347421866, 4.064287893060632 3.810613974100979, 4.076536686473018 3.8152240934977426, 4.0884577380438 3.8206254516934623, 4.1 3.8267949192431123, 4.11111404660392 3.833706077539491, 4.121752285801744 3.841329331941753, 4.1318691630200135 3.8496320385042044, 4.141421356237309 3.8585786437626903, 4.150367961495795 3.868130836979986, 4.158670668058247 3.878247714198256, 4.166293922460509 3.8888859533960796, 4.173205080756888 3.9, 4.179374548306537 3.9115422619561997, 4.184775906502257 3.923463313526982, 4.189386025899021 3.9357121069393677, 4.193185165257813 3.948236190979496, 4.196157056080646 3.960981935596774, 4.198288972274762 3.97389476155599, 4.199571784647721 3.9869193741539712, 4.2 4, 4.199571784647721 4.013080625846029, 4.198288972274762 4.026105238444011, 4.196157056080646 4.039018064403225, 4.193185165257813 4.051763809020504, 4.189386025899021 4.064287893060632, 4.184775906502257 4.076536686473018, 4.179374548306537 4.0884577380438, 4.173205080756888 4.1, 4.166293922460509 4.11111404660392, 4.158670668058247 4.121752285801744, 4.150367961495795 4.1318691630200135, 4.141421356237309 4.141421356237309, 4.1318691630200135 4.150367961495795, 4.121752285801744 4.158670668058247, 4.11111404660392 4.166293922460509, 4.1 4.173205080756888, 4.0884577380438 4.179374548306537, 4.076536686473018 4.184775906502257, 4.064287893060632 4.189386025899021, 4.051763809020504 4.193185165257813, 4.039018064403225 4.196157056080646, 4.026105238444011 4.198288972274762, 4.013080625846029 4.199571784647721, 4 4.2, 3 4.2, 2.9869193741539712 4.199571784647721, 2.9738947615559894 4.198288972274762, 2.9609819355967746 4.196157056080646, 2.948236190979496 4.193185165257813, 2.9357121069393677 4.189386025899021, 2.923463313526982 4.184775906502257, 2.9115422619561997 4.179374548306537, 2.9000000000000004 4.173205080756888, 2.8888859533960796 4.166293922460509, 2.878247714198256 4.158670668058247, 2.8681308369799865 4.150367961495795, 2.8585786437626908 4.141421356237309, 2.8496320385042044 4.1318691630200135, 2.841329331941753 4.121752285801744, 2.833706077539491 4.11111404660392, 2.8267949192431123 4.1, 2.8206254516934623 4.0884577380438, 2.8152240934977426 4.076536686473018, 2.8106139741009786 4.064287893060632, 2.8068148347421866 4.051763809020504, 2.8038429439193537 4.039018064403225, 2.801711027725238 4.026105238444011, 2.8004282153522793 4.013080625846029, 2.8 4, 2.8004282153522793 3.9869193741539712, 2.801711027725238 3.97389476155599, 2.8038429439193537 3.9609819355967746, 2.8068148347421866 3.948236190979496, 2.810613974100979 3.9357121069393677, 2.8152240934977426 3.923463313526982, 2.8206254516934623 3.9115422619561997, 2.8267949192431123 3.9, 2.833706077539491 3.8888859533960796, 2.841329331941753 3.878247714198256, 2.8496320385042044 3.8681308369799865, 2.8585786437626908 3.8585786437626908, 2.8681308369799865 3.8496320385042044, 2.878247714198256 3.841329331941753, 2.8888859533960796 3.833706077539491, 2.9 3.8267949192431123, 2.9115422619561997 3.8206254516934623, 2.923463313526982 3.8152240934977426, 2.9357121069393677 3.810613974100979, 2.948236190979496 3.806814834742186, 2.9609819355967746 3.8038429439193537, 2.9738947615559894 3.8017110277252377, 2.9869193741539712 3.8004282153522793, 3 3.8)))"); + assertThat(assertions.function("ST_AsText", "ST_Buffer(ST_Intersection(ST_GeometryFromText('MULTILINESTRING ((1 1, 5 1), (2 4, 4 4))'), ST_GeometryFromText('MULTILINESTRING ((3 4, 6 4), (5 0, 5 4))')), 0.2)")) + .hasType(VARCHAR) + .isEqualTo("MULTIPOLYGON (((5 0.8, 5.013080625846029 0.8004282153522794, 5.026105238444011 0.801711027725238, 5.039018064403225 0.803842943919354, 5.051763809020504 0.8068148347421864, 5.064287893060633 0.8106139741009789, 5.076536686473018 0.8152240934977427, 5.0884577380438 0.8206254516934623, 5.1 0.8267949192431123, 5.11111404660392 0.833706077539491, 5.121752285801744 0.841329331941753, 5.1318691630200135 0.8496320385042045, 5.141421356237309 0.8585786437626906, 5.150367961495795 0.8681308369799863, 5.158670668058247 0.8782477141982559, 5.166293922460509 0.8888859533960796, 5.173205080756888 0.9, 5.179374548306538 0.9115422619561997, 5.184775906502257 0.9234633135269821, 5.189386025899021 0.9357121069393677, 5.193185165257813 0.9482361909794959, 5.196157056080646 0.9609819355967744, 5.198288972274762 0.9738947615559896, 5.199571784647721 0.9869193741539714, 5.2 1, 5.199571784647721 1.0130806258460288, 5.198288972274762 1.0261052384440104, 5.196157056080646 1.0390180644032256, 5.193185165257813 1.0517638090205041, 5.189386025899021 1.0642878930606323, 5.184775906502257 1.076536686473018, 5.179374548306537 1.0884577380438003, 5.173205080756888 1.1, 5.166293922460509 1.1111140466039204, 5.158670668058247 1.1217522858017441, 5.150367961495795 1.1318691630200137, 5.141421356237309 1.1414213562373094, 5.1318691630200135 1.1503679614957956, 5.121752285801744 1.158670668058247, 5.11111404660392 1.1662939224605091, 5.1 1.1732050807568877, 5.0884577380438 1.1793745483065377, 5.076536686473018 1.1847759065022574, 5.064287893060632 1.1893860258990212, 5.051763809020504 1.1931851652578138, 5.039018064403225 1.196157056080646, 5.026105238444011 1.198288972274762, 5.013080625846029 1.1995717846477207, 5 1.2, 4.986919374153971 1.1995717846477207, 4.973894761555989 1.198288972274762, 4.960981935596775 1.196157056080646, 4.948236190979496 1.1931851652578136, 4.935712106939367 1.189386025899021, 4.923463313526982 1.1847759065022574, 4.9115422619562 1.1793745483065377, 4.9 1.1732050807568877, 4.88888595339608 1.166293922460509, 4.878247714198256 1.158670668058247, 4.8681308369799865 1.1503679614957956, 4.858578643762691 1.1414213562373094, 4.849632038504205 1.1318691630200137, 4.841329331941753 1.1217522858017441, 4.833706077539491 1.1111140466039204, 4.826794919243112 1.1, 4.820625451693462 1.0884577380438003, 4.815224093497743 1.076536686473018, 4.810613974100979 1.0642878930606323, 4.806814834742187 1.0517638090205041, 4.803842943919354 1.0390180644032256, 4.801711027725238 1.0261052384440104, 4.800428215352279 1.0130806258460285, 4.8 1, 4.800428215352279 0.9869193741539714, 4.801711027725238 0.9738947615559896, 4.803842943919354 0.9609819355967743, 4.806814834742187 0.9482361909794959, 4.810613974100979 0.9357121069393677, 4.815224093497743 0.923463313526982, 4.820625451693463 0.9115422619561997, 4.826794919243112 0.8999999999999999, 4.833706077539491 0.8888859533960796, 4.841329331941753 0.8782477141982559, 4.849632038504205 0.8681308369799862, 4.858578643762691 0.8585786437626904, 4.8681308369799865 0.8496320385042044, 4.878247714198256 0.841329331941753, 4.88888595339608 0.8337060775394909, 4.9 0.8267949192431122, 4.9115422619562 0.8206254516934623, 4.923463313526982 0.8152240934977426, 4.935712106939368 0.8106139741009788, 4.948236190979496 0.8068148347421863, 4.960981935596775 0.8038429439193538, 4.973894761555989 0.801711027725238, 4.986919374153971 0.8004282153522793, 5 0.8)), ((3 3.8, 4 3.8, 4.013080625846029 3.8004282153522793, 4.026105238444011 3.801711027725238, 4.039018064403225 3.803842943919354, 4.051763809020504 3.8068148347421866, 4.064287893060632 3.810613974100979, 4.076536686473018 3.8152240934977426, 4.0884577380438 3.8206254516934623, 4.1 3.8267949192431123, 4.11111404660392 3.833706077539491, 4.121752285801744 3.841329331941753, 4.1318691630200135 3.8496320385042044, 4.141421356237309 3.8585786437626903, 4.150367961495795 3.868130836979986, 4.158670668058247 3.878247714198256, 4.166293922460509 3.8888859533960796, 4.173205080756888 3.9, 4.179374548306537 3.9115422619561997, 4.184775906502257 3.923463313526982, 4.189386025899021 3.9357121069393677, 4.193185165257813 3.948236190979496, 4.196157056080646 3.960981935596774, 4.198288972274762 3.97389476155599, 4.199571784647721 3.9869193741539712, 4.2 4, 4.199571784647721 4.013080625846029, 4.198288972274762 4.026105238444011, 4.196157056080646 4.039018064403225, 4.193185165257813 4.051763809020504, 4.189386025899021 4.064287893060632, 4.184775906502257 4.076536686473018, 4.179374548306537 4.0884577380438, 4.173205080756888 4.1, 4.166293922460509 4.11111404660392, 4.158670668058247 4.121752285801744, 4.150367961495795 4.1318691630200135, 4.141421356237309 4.141421356237309, 4.1318691630200135 4.150367961495795, 4.121752285801744 4.158670668058247, 4.11111404660392 4.166293922460509, 4.1 4.173205080756888, 4.0884577380438 4.179374548306537, 4.076536686473018 4.184775906502257, 4.064287893060632 4.189386025899021, 4.051763809020504 4.193185165257813, 4.039018064403225 4.196157056080646, 4.026105238444011 4.198288972274762, 4.013080625846029 4.199571784647721, 4 4.2, 3 4.2, 2.9869193741539712 4.199571784647721, 2.9738947615559894 4.198288972274762, 2.9609819355967746 4.196157056080646, 2.948236190979496 4.193185165257813, 2.9357121069393677 4.189386025899021, 2.923463313526982 4.184775906502257, 2.9115422619561997 4.179374548306537, 2.9000000000000004 4.173205080756888, 2.8888859533960796 4.166293922460509, 2.878247714198256 4.158670668058247, 2.8681308369799865 4.150367961495795, 2.8585786437626908 4.141421356237309, 2.8496320385042044 4.1318691630200135, 2.841329331941753 4.121752285801744, 2.833706077539491 4.11111404660392, 2.8267949192431123 4.1, 2.8206254516934623 4.0884577380438, 2.8152240934977426 4.076536686473018, 2.8106139741009786 4.064287893060632, 2.8068148347421866 4.051763809020504, 2.8038429439193537 4.039018064403225, 2.801711027725238 4.026105238444011, 2.8004282153522793 4.013080625846029, 2.8 4, 2.8004282153522793 3.9869193741539712, 2.801711027725238 3.97389476155599, 2.8038429439193537 3.9609819355967746, 2.8068148347421866 3.948236190979496, 2.810613974100979 3.9357121069393677, 2.8152240934977426 3.923463313526982, 2.8206254516934623 3.9115422619561997, 2.8267949192431123 3.9, 2.833706077539491 3.8888859533960796, 2.841329331941753 3.878247714198256, 2.8496320385042044 3.8681308369799865, 2.8585786437626908 3.8585786437626908, 2.8681308369799865 3.8496320385042044, 2.878247714198256 3.841329331941753, 2.8888859533960796 3.833706077539491, 2.9 3.8267949192431123, 2.9115422619561997 3.8206254516934623, 2.923463313526982 3.8152240934977426, 2.9357121069393677 3.810613974100979, 2.948236190979496 3.806814834742186, 2.9609819355967746 3.8038429439193537, 2.9738947615559894 3.8017110277252377, 2.9869193741539712 3.8004282153522793, 3 3.8)))"); // empty geometry - assertFunction("ST_Buffer(ST_GeometryFromText('POINT EMPTY'), 1)", GEOMETRY, null); + assertThat(assertions.function("ST_Buffer", "ST_GeometryFromText('POINT EMPTY')", "1")) + .isNull(GEOMETRY); // negative distance - assertInvalidFunction("ST_Buffer(ST_Point(0, 0), -1.2)", "distance is negative"); - assertInvalidFunction("ST_Buffer(ST_Point(0, 0), -infinity())", "distance is negative"); + assertTrinoExceptionThrownBy(() -> assertions.function("ST_Buffer", "ST_Point(0, 0)", "-1.2").evaluate()) + .hasMessage("distance is negative"); + + assertTrinoExceptionThrownBy(() -> assertions.function("ST_Buffer", "ST_Point(0, 0)", "-infinity()").evaluate()) + .hasMessage("distance is negative"); // infinity() and nan() distance - assertFunction("ST_AsText(ST_Buffer(ST_Point(0, 0), infinity()))", VARCHAR, "MULTIPOLYGON EMPTY"); - assertInvalidFunction("ST_Buffer(ST_Point(0, 0), nan())", "distance is NaN"); + assertThat(assertions.function("ST_AsText", "ST_Buffer(ST_Point(0, 0), infinity())")) + .hasType(VARCHAR) + .isEqualTo("MULTIPOLYGON EMPTY"); + + assertTrinoExceptionThrownBy(() -> assertions.function("ST_Buffer", "ST_Point(0, 0)", "nan()").evaluate()) + .hasMessage("distance is NaN"); } @Test public void testSTCentroid() { - assertFunction("ST_AsText(ST_Centroid(ST_GeometryFromText('LINESTRING EMPTY')))", VARCHAR, "POINT EMPTY"); - assertFunction("ST_AsText(ST_Centroid(ST_GeometryFromText('POINT (3 5)')))", VARCHAR, "POINT (3 5)"); - assertFunction("ST_AsText(ST_Centroid(ST_GeometryFromText('MULTIPOINT (1 2, 2 4, 3 6, 4 8)')))", VARCHAR, "POINT (2.5 5)"); - assertFunction("ST_AsText(ST_Centroid(ST_GeometryFromText('LINESTRING (1 1, 2 2, 3 3)')))", VARCHAR, "POINT (2 2)"); - assertFunction("ST_AsText(ST_Centroid(ST_GeometryFromText('MULTILINESTRING ((1 1, 5 1), (2 4, 4 4))')))", VARCHAR, "POINT (3 2)"); - assertFunction("ST_AsText(ST_Centroid(ST_GeometryFromText('POLYGON ((1 1, 1 4, 4 4, 4 1))')))", VARCHAR, "POINT (2.5 2.5)"); - assertFunction("ST_AsText(ST_Centroid(ST_GeometryFromText('POLYGON ((1 1, 5 1, 3 4))')))", VARCHAR, "POINT (3 2)"); - assertFunction("ST_AsText(ST_Centroid(ST_GeometryFromText('MULTIPOLYGON (((1 1, 1 3, 3 3, 3 1)), ((2 4, 2 6, 6 6, 6 4)))')))", VARCHAR, "POINT (3.3333333333333335 4)"); - assertFunction("ST_AsText(ST_Centroid(ST_GeometryFromText('POLYGON ((0 0, 0 5, 5 5, 5 0, 0 0), (1 1, 1 2, 2 2, 2 1, 1 1))')))", VARCHAR, "POINT (2.5416666666666665 2.5416666666666665)"); + assertThat(assertions.function("ST_AsText", "ST_Centroid(ST_GeometryFromText('LINESTRING EMPTY'))")) + .hasType(VARCHAR) + .isEqualTo("POINT EMPTY"); + + assertThat(assertions.function("ST_AsText", "ST_Centroid(ST_GeometryFromText('POINT (3 5)'))")) + .hasType(VARCHAR) + .isEqualTo("POINT (3 5)"); + + assertThat(assertions.function("ST_AsText", "ST_Centroid(ST_GeometryFromText('MULTIPOINT (1 2, 2 4, 3 6, 4 8)'))")) + .hasType(VARCHAR) + .isEqualTo("POINT (2.5 5)"); + + assertThat(assertions.function("ST_AsText", "ST_Centroid(ST_GeometryFromText('LINESTRING (1 1, 2 2, 3 3)'))")) + .hasType(VARCHAR) + .isEqualTo("POINT (2 2)"); + + assertThat(assertions.function("ST_AsText", "ST_Centroid(ST_GeometryFromText('MULTILINESTRING ((1 1, 5 1), (2 4, 4 4))'))")) + .hasType(VARCHAR) + .isEqualTo("POINT (3 2)"); + + assertThat(assertions.function("ST_AsText", "ST_Centroid(ST_GeometryFromText('POLYGON ((1 1, 1 4, 4 4, 4 1))'))")) + .hasType(VARCHAR) + .isEqualTo("POINT (2.5 2.5)"); + + assertThat(assertions.function("ST_AsText", "ST_Centroid(ST_GeometryFromText('POLYGON ((1 1, 5 1, 3 4))'))")) + .hasType(VARCHAR) + .isEqualTo("POINT (3 2)"); + + assertThat(assertions.function("ST_AsText", "ST_Centroid(ST_GeometryFromText('MULTIPOLYGON (((1 1, 1 3, 3 3, 3 1)), ((2 4, 2 6, 6 6, 6 4)))'))")) + .hasType(VARCHAR) + .isEqualTo("POINT (3.3333333333333335 4)"); + + assertThat(assertions.function("ST_AsText", "ST_Centroid(ST_GeometryFromText('POLYGON ((0 0, 0 5, 5 5, 5 0, 0 0), (1 1, 1 2, 2 2, 2 1, 1 1))'))")) + .hasType(VARCHAR) + .isEqualTo("POINT (2.5416666666666665 2.5416666666666665)"); } @Test @@ -263,50 +354,77 @@ public void testSTConvexHull() private void assertConvexHull(String inputWKT, String expectWKT) { - assertFunction(format("ST_AsText(ST_ConvexHull(ST_GeometryFromText('%s')))", inputWKT), VARCHAR, expectWKT); + assertThat(assertions.expression("ST_AsText(ST_ConvexHull(geometry))") + .binding("geometry", "ST_GeometryFromText('%s')".formatted(inputWKT))) + .hasType(VARCHAR) + .isEqualTo(expectWKT); } @Test public void testSTCoordDim() { - assertFunction("ST_CoordDim(ST_GeometryFromText('POLYGON ((1 1, 1 4, 4 4, 4 1))'))", TINYINT, (byte) 2); - assertFunction("ST_CoordDim(ST_GeometryFromText('POLYGON EMPTY'))", TINYINT, (byte) 2); - assertFunction("ST_CoordDim(ST_GeometryFromText('LINESTRING EMPTY'))", TINYINT, (byte) 2); - assertFunction("ST_CoordDim(ST_GeometryFromText('POINT (1 4)'))", TINYINT, (byte) 2); + assertThat(assertions.function("ST_CoordDim", "ST_GeometryFromText('POLYGON ((1 1, 1 4, 4 4, 4 1))')")) + .isEqualTo((byte) 2); + + assertThat(assertions.function("ST_CoordDim", "ST_GeometryFromText('POLYGON EMPTY')")) + .isEqualTo((byte) 2); + + assertThat(assertions.function("ST_CoordDim", "ST_GeometryFromText('LINESTRING EMPTY')")) + .isEqualTo((byte) 2); + + assertThat(assertions.function("ST_CoordDim", "ST_GeometryFromText('POINT (1 4)')")) + .isEqualTo((byte) 2); } @Test public void testSTDimension() { - assertFunction("ST_Dimension(ST_GeometryFromText('POLYGON EMPTY'))", TINYINT, (byte) 2); - assertFunction("ST_Dimension(ST_GeometryFromText('POLYGON ((1 1, 1 4, 4 4, 4 1))'))", TINYINT, (byte) 2); - assertFunction("ST_Dimension(ST_GeometryFromText('LINESTRING EMPTY'))", TINYINT, (byte) 1); - assertFunction("ST_Dimension(ST_GeometryFromText('POINT (1 4)'))", TINYINT, (byte) 0); + assertThat(assertions.function("ST_Dimension", "ST_GeometryFromText('POLYGON EMPTY')")) + .isEqualTo((byte) 2); + + assertThat(assertions.function("ST_Dimension", "ST_GeometryFromText('POLYGON ((1 1, 1 4, 4 4, 4 1))')")) + .isEqualTo((byte) 2); + + assertThat(assertions.function("ST_Dimension", "ST_GeometryFromText('LINESTRING EMPTY')")) + .isEqualTo((byte) 1); + + assertThat(assertions.function("ST_Dimension", "ST_GeometryFromText('POINT (1 4)')")) + .isEqualTo((byte) 0); } @Test public void testSTIsClosed() { - assertFunction("ST_IsClosed(ST_GeometryFromText('LINESTRING (1 1, 2 2, 1 3, 1 1)'))", BOOLEAN, true); - assertFunction("ST_IsClosed(ST_GeometryFromText('LINESTRING (1 1, 2 2, 1 3)'))", BOOLEAN, false); - assertInvalidFunction("ST_IsClosed(ST_GeometryFromText('POLYGON ((1 1, 1 4, 4 4, 4 1))'))", "ST_IsClosed only applies to LINE_STRING or MULTI_LINE_STRING. Input type is: POLYGON"); + assertThat(assertions.function("ST_IsClosed", "ST_GeometryFromText('LINESTRING (1 1, 2 2, 1 3, 1 1)')")) + .isEqualTo(true); + + assertThat(assertions.function("ST_IsClosed", "ST_GeometryFromText('LINESTRING (1 1, 2 2, 1 3)')")) + .isEqualTo(false); + + assertTrinoExceptionThrownBy(() -> assertions.function("ST_IsClosed", "ST_GeometryFromText('POLYGON ((1 1, 1 4, 4 4, 4 1))')").evaluate()) + .hasMessage("ST_IsClosed only applies to LINE_STRING or MULTI_LINE_STRING. Input type is: POLYGON"); } @Test public void testSTIsEmpty() { - assertFunction("ST_IsEmpty(ST_GeometryFromText('POINT (1.5 2.5)'))", BOOLEAN, false); - assertFunction("ST_IsEmpty(ST_GeometryFromText('POLYGON EMPTY'))", BOOLEAN, true); + assertThat(assertions.function("ST_IsEmpty", "ST_GeometryFromText('POINT (1.5 2.5)')")) + .isEqualTo(false); + + assertThat(assertions.function("ST_IsEmpty", "ST_GeometryFromText('POLYGON EMPTY')")) + .isEqualTo(true); } private void assertSimpleGeometry(String text) { - assertFunction("ST_IsSimple(ST_GeometryFromText('" + text + "'))", BOOLEAN, true); + assertThat(assertions.function("ST_IsSimple", "ST_GeometryFromText('%s')".formatted(text))) + .isEqualTo(true); } private void assertNotSimpleGeometry(String text) { - assertFunction("ST_IsSimple(ST_GeometryFromText('" + text + "'))", BOOLEAN, false); + assertThat(assertions.function("ST_IsSimple", "ST_GeometryFromText('%s')".formatted(text))) + .isEqualTo(false); } @Test @@ -329,14 +447,22 @@ public void testSTIsSimple() public void testSimplifyGeometry() { // Eliminate unnecessary points on the same line. - assertFunction("ST_AsText(simplify_geometry(ST_GeometryFromText('POLYGON ((1 0, 2 1, 3 1, 3 1, 4 1, 1 0))'), 1.5))", VARCHAR, "POLYGON ((1 0, 4 1, 2 1, 1 0))"); + assertThat(assertions.function("ST_AsText", "simplify_geometry(ST_GeometryFromText('POLYGON ((1 0, 2 1, 3 1, 3 1, 4 1, 1 0))'), 1.5)")) + .hasType(VARCHAR) + .isEqualTo("POLYGON ((1 0, 4 1, 2 1, 1 0))"); // Use distanceTolerance to control fidelity. - assertFunction("ST_AsText(simplify_geometry(ST_GeometryFromText('POLYGON ((1 0, 1 1, 2 1, 2 3, 3 3, 3 1, 4 1, 4 0, 1 0))'), 1.0))", VARCHAR, "POLYGON ((1 0, 4 0, 3 3, 2 3, 1 0))"); - assertFunction("ST_AsText(simplify_geometry(ST_GeometryFromText('POLYGON ((1 0, 1 1, 2 1, 2 3, 3 3, 3 1, 4 1, 4 0, 1 0))'), 0.5))", VARCHAR, "POLYGON ((1 0, 4 0, 4 1, 3 1, 3 3, 2 3, 2 1, 1 1, 1 0))"); + assertThat(assertions.function("ST_AsText", "simplify_geometry(ST_GeometryFromText('POLYGON ((1 0, 1 1, 2 1, 2 3, 3 3, 3 1, 4 1, 4 0, 1 0))'), 1.0)")) + .hasType(VARCHAR) + .isEqualTo("POLYGON ((1 0, 4 0, 3 3, 2 3, 1 0))"); + + assertThat(assertions.function("ST_AsText", "simplify_geometry(ST_GeometryFromText('POLYGON ((1 0, 1 1, 2 1, 2 3, 3 3, 3 1, 4 1, 4 0, 1 0))'), 0.5)")) + .hasType(VARCHAR) + .isEqualTo("POLYGON ((1 0, 4 0, 4 1, 3 1, 3 3, 2 3, 2 1, 1 1, 1 0))"); // Negative distance tolerance is invalid. - assertInvalidFunction("ST_AsText(simplify_geometry(ST_GeometryFromText('POLYGON ((1 0, 1 1, 2 1, 2 3, 3 3, 3 1, 4 1, 4 0, 1 0))'), -0.5))", "distanceTolerance is negative"); + assertTrinoExceptionThrownBy(() -> assertions.function("ST_AsText", "simplify_geometry(ST_GeometryFromText('POLYGON ((1 0, 1 1, 2 1, 2 3, 3 3, 3 1, 4 1, 4 0, 1 0))'), -0.5)").evaluate()) + .hasMessage("distanceTolerance is negative"); } @Test @@ -375,29 +501,46 @@ public void testSTIsValid() assertInvalidGeometry("GEOMETRYCOLLECTION (POINT (1 2), POLYGON ((0 0, 0 1, 2 1, 1 1, 1 0, 0 0)))", "Intersecting or overlapping segments at or near (0.0 1.0) and (2.0 1.0)"); // corner cases - assertFunction("ST_IsValid(ST_GeometryFromText(null))", BOOLEAN, null); - assertFunction("geometry_invalid_reason(ST_GeometryFromText(null))", VARCHAR, null); + assertThat(assertions.function("ST_IsValid", "ST_GeometryFromText(null)")) + .isNull(BOOLEAN); + + assertThat(assertions.function("geometry_invalid_reason", "ST_GeometryFromText(null)")) + .isNull(VARCHAR); } private void assertValidGeometry(String wkt) { - assertFunction("ST_IsValid(ST_GeometryFromText('" + wkt + "'))", BOOLEAN, true); - assertFunction("geometry_invalid_reason(ST_GeometryFromText('" + wkt + "'))", VARCHAR, null); + assertThat(assertions.function("ST_IsValid", "ST_GeometryFromText('%s')".formatted(wkt))) + .isEqualTo(true); + + assertThat(assertions.function("geometry_invalid_reason", "ST_GeometryFromText('%s')".formatted(wkt))) + .isNull(VARCHAR); } private void assertInvalidGeometry(String wkt, String reason) { - assertFunction("ST_IsValid(ST_GeometryFromText('" + wkt + "'))", BOOLEAN, false); - assertFunction("geometry_invalid_reason(ST_GeometryFromText('" + wkt + "'))", VARCHAR, reason); + assertThat(assertions.function("ST_IsValid", "ST_GeometryFromText('%s')".formatted(wkt))) + .isEqualTo(false); + + assertThat(assertions.function("geometry_invalid_reason", "ST_GeometryFromText('%s')".formatted(wkt))) + .hasType(VARCHAR) + .isEqualTo(reason); } @Test public void testSTLength() { - assertFunction("ST_Length(ST_GeometryFromText('LINESTRING EMPTY'))", DOUBLE, 0.0); - assertFunction("ST_Length(ST_GeometryFromText('LINESTRING (0 0, 2 2)'))", DOUBLE, 2.8284271247461903); - assertFunction("ST_Length(ST_GeometryFromText('MULTILINESTRING ((1 1, 5 1), (2 4, 4 4))'))", DOUBLE, 6.0); - assertInvalidFunction("ST_Length(ST_GeometryFromText('POLYGON ((1 1, 1 4, 4 4, 4 1))'))", "ST_Length only applies to LINE_STRING or MULTI_LINE_STRING. Input type is: POLYGON"); + assertThat(assertions.function("ST_Length", "ST_GeometryFromText('LINESTRING EMPTY')")) + .isEqualTo(0.0); + + assertThat(assertions.function("ST_Length", "ST_GeometryFromText('LINESTRING (0 0, 2 2)')")) + .isEqualTo(2.8284271247461903); + + assertThat(assertions.function("ST_Length", "ST_GeometryFromText('MULTILINESTRING ((1 1, 5 1), (2 4, 4 4))')")) + .isEqualTo(6.0); + + assertTrinoExceptionThrownBy(() -> assertions.function("ST_Length", "ST_GeometryFromText('POLYGON ((1 1, 1 4, 4 4, 4 1))')").evaluate()) + .hasMessage("ST_Length only applies to LINE_STRING or MULTI_LINE_STRING. Input type is: POLYGON"); } @Test @@ -438,42 +581,70 @@ public void testSTLengthSphericalGeography() private void assertSTLengthSphericalGeography(String lineString, Double expectedLength) { - String function = format("ST_Length(to_spherical_geography(ST_GeometryFromText('%s')))", lineString); - if (expectedLength == null || expectedLength == 0.0) { - assertFunction(function, DOUBLE, expectedLength); + assertThat(assertions.function("ST_Length", "to_spherical_geography(ST_GeometryFromText('%s'))".formatted(lineString))) + .isEqualTo(expectedLength); } else { - assertFunction(format("ROUND(ABS((%s / %f) - 1.0) / %f, 0)", function, expectedLength, 1e-4), DOUBLE, 0.0); + assertThat(assertions.expression("ROUND(ABS((ST_Length(to_spherical_geography(ST_GeometryFromText('%s'))) / %f) - 1.0) / %f, 0)".formatted(lineString, expectedLength, 1e-4))) + .isEqualTo(0.0); } } @Test public void testLineLocatePoint() { - assertFunction("line_locate_point(ST_GeometryFromText('LINESTRING (0 0, 0 1)'), ST_Point(0, 0.2))", DOUBLE, 0.2); - assertFunction("line_locate_point(ST_GeometryFromText('LINESTRING (0 0, 0 1)'), ST_Point(0, 0))", DOUBLE, 0.0); - assertFunction("line_locate_point(ST_GeometryFromText('LINESTRING (0 0, 0 1)'), ST_Point(0, -1))", DOUBLE, 0.0); - assertFunction("line_locate_point(ST_GeometryFromText('LINESTRING (0 0, 0 1)'), ST_Point(0, 1))", DOUBLE, 1.0); - assertFunction("line_locate_point(ST_GeometryFromText('LINESTRING (0 0, 0 1)'), ST_Point(0, 2))", DOUBLE, 1.0); - assertFunction("line_locate_point(ST_GeometryFromText('LINESTRING (0 0, 0 1, 2 1)'), ST_Point(0, 0.2))", DOUBLE, 0.06666666666666667); - assertFunction("line_locate_point(ST_GeometryFromText('LINESTRING (0 0, 0 1, 2 1)'), ST_Point(0.9, 1))", DOUBLE, 0.6333333333333333); - assertFunction("line_locate_point(ST_GeometryFromText('LINESTRING (1 3, 5 4)'), ST_Point(1, 3))", DOUBLE, 0.0); - assertFunction("line_locate_point(ST_GeometryFromText('LINESTRING (1 3, 5 4)'), ST_Point(2, 3))", DOUBLE, 0.23529411764705882); - assertFunction("line_locate_point(ST_GeometryFromText('LINESTRING (1 3, 5 4)'), ST_Point(5, 4))", DOUBLE, 1.0); - assertFunction("line_locate_point(ST_GeometryFromText('MULTILINESTRING ((0 0, 0 1), (2 2, 4 2))'), ST_Point(3, 1))", DOUBLE, 0.6666666666666666); + assertThat(assertions.function("line_locate_point", "ST_GeometryFromText('LINESTRING (0 0, 0 1)')", "ST_Point(0, 0.2)")) + .isEqualTo(0.2); + + assertThat(assertions.function("line_locate_point", "ST_GeometryFromText('LINESTRING (0 0, 0 1)')", "ST_Point(0, 0)")) + .isEqualTo(0.0); + + assertThat(assertions.function("line_locate_point", "ST_GeometryFromText('LINESTRING (0 0, 0 1)')", "ST_Point(0, -1)")) + .isEqualTo(0.0); + + assertThat(assertions.function("line_locate_point", "ST_GeometryFromText('LINESTRING (0 0, 0 1)')", "ST_Point(0, 1)")) + .isEqualTo(1.0); + + assertThat(assertions.function("line_locate_point", "ST_GeometryFromText('LINESTRING (0 0, 0 1)')", "ST_Point(0, 2)")) + .isEqualTo(1.0); + + assertThat(assertions.function("line_locate_point", "ST_GeometryFromText('LINESTRING (0 0, 0 1, 2 1)')", "ST_Point(0, 0.2)")) + .isEqualTo(0.06666666666666667); + + assertThat(assertions.function("line_locate_point", "ST_GeometryFromText('LINESTRING (0 0, 0 1, 2 1)')", "ST_Point(0.9, 1)")) + .isEqualTo(0.6333333333333333); + + assertThat(assertions.function("line_locate_point", "ST_GeometryFromText('LINESTRING (1 3, 5 4)')", "ST_Point(1, 3)")) + .isEqualTo(0.0); + + assertThat(assertions.function("line_locate_point", "ST_GeometryFromText('LINESTRING (1 3, 5 4)')", "ST_Point(2, 3)")) + .isEqualTo(0.23529411764705882); + + assertThat(assertions.function("line_locate_point", "ST_GeometryFromText('LINESTRING (1 3, 5 4)')", "ST_Point(5, 4)")) + .isEqualTo(1.0); + + assertThat(assertions.function("line_locate_point", "ST_GeometryFromText('MULTILINESTRING ((0 0, 0 1), (2 2, 4 2))')", "ST_Point(3, 1)")) + .isEqualTo(0.6666666666666666); - assertFunction("line_locate_point(ST_GeometryFromText('LINESTRING EMPTY'), ST_Point(0, 1))", DOUBLE, null); - assertFunction("line_locate_point(ST_GeometryFromText('LINESTRING (0 0, 0 1, 2 1)'), ST_GeometryFromText('POINT EMPTY'))", DOUBLE, null); + assertThat(assertions.function("line_locate_point", "ST_GeometryFromText('LINESTRING EMPTY')", "ST_Point(0, 1)")) + .isNull(DOUBLE); - assertInvalidFunction("line_locate_point(ST_GeometryFromText('POLYGON ((1 1, 1 4, 4 4, 4 1))'), ST_Point(0.4, 1))", "First argument to line_locate_point must be a LineString or a MultiLineString. Got: Polygon"); - assertInvalidFunction("line_locate_point(ST_GeometryFromText('LINESTRING (0 0, 0 1, 2 1)'), ST_GeometryFromText('POLYGON ((1 1, 1 4, 4 4, 4 1))'))", "Second argument to line_locate_point must be a Point. Got: Polygon"); + assertThat(assertions.function("line_locate_point", "ST_GeometryFromText('LINESTRING (0 0, 0 1, 2 1)')", "ST_GeometryFromText('POINT EMPTY')")) + .isNull(DOUBLE); + + assertTrinoExceptionThrownBy(() -> assertions.function("line_locate_point", "ST_GeometryFromText('POLYGON ((1 1, 1 4, 4 4, 4 1))')", "ST_Point(0.4, 1)").evaluate()) + .hasMessage("First argument to line_locate_point must be a LineString or a MultiLineString. Got: Polygon"); + + assertTrinoExceptionThrownBy(() -> assertions.function("line_locate_point", "ST_GeometryFromText('LINESTRING (0 0, 0 1, 2 1)')", "ST_GeometryFromText('POLYGON ((1 1, 1 4, 4 4, 4 1))')").evaluate()) + .hasMessage("Second argument to line_locate_point must be a Point. Got: Polygon"); } @Test public void testLineInterpolatePoint() { - assertFunction("ST_AsText(line_interpolate_point(ST_GeometryFromText('LINESTRING EMPTY'), 0.5))", VARCHAR, null); + assertThat(assertions.function("ST_AsText", "line_interpolate_point(ST_GeometryFromText('LINESTRING EMPTY'), 0.5)")) + .isNull(VARCHAR); assertLineInterpolatePoint("LINESTRING (0 0, 1 1, 10 10)", 0.0, "POINT (0 0)"); assertLineInterpolatePoint("LINESTRING (0 0, 1 1, 10 10)", 0.1, "POINT (1 1)"); @@ -493,15 +664,21 @@ public void testLineInterpolatePoint() assertLineInterpolatePoint("LINESTRING (0 0, 1 0, 1 9)", 0.5, "POINT (1 4)"); assertLineInterpolatePoint("LINESTRING (0 0, 1 0, 1 9)", 1.0, "POINT (1 9)"); - assertInvalidFunction("line_interpolate_point(ST_GeometryFromText('LINESTRING (0 0, 1 0, 1 9)'), -0.5)", "fraction must be between 0 and 1"); - assertInvalidFunction("line_interpolate_point(ST_GeometryFromText('LINESTRING (0 0, 1 0, 1 9)'), 2.0)", "fraction must be between 0 and 1"); - assertInvalidFunction("line_interpolate_point(ST_GeometryFromText('POLYGON ((0 0, 1 1, 0 1, 1 0, 0 0))'), 0.2)", "line_interpolate_point only applies to LINE_STRING. Input type is: POLYGON"); + assertTrinoExceptionThrownBy(() -> assertions.function("line_interpolate_point", "ST_GeometryFromText('LINESTRING (0 0, 1 0, 1 9)')", "-0.5").evaluate()) + .hasMessage("fraction must be between 0 and 1"); + + assertTrinoExceptionThrownBy(() -> assertions.function("line_interpolate_point", "ST_GeometryFromText('LINESTRING (0 0, 1 0, 1 9)')", "2.0").evaluate()) + .hasMessage("fraction must be between 0 and 1"); + + assertTrinoExceptionThrownBy(() -> assertions.function("line_interpolate_point", "ST_GeometryFromText('POLYGON ((0 0, 1 1, 0 1, 1 0, 0 0))')", "0.2").evaluate()) + .hasMessage("line_interpolate_point only applies to LINE_STRING. Input type is: POLYGON"); } @Test public void testLineInterpolatePoints() { - assertFunction("line_interpolate_points(ST_GeometryFromText('LINESTRING EMPTY'), 0.5)", new ArrayType(GEOMETRY), null); + assertThat(assertions.function("line_interpolate_points", "ST_GeometryFromText('LINESTRING EMPTY')", "0.5")) + .isNull(new ArrayType(GEOMETRY)); assertLineInterpolatePoints("LINESTRING (0 0, 1 1, 10 10)", 0.0, "0 0"); assertLineInterpolatePoints("LINESTRING (0 0, 1 1, 10 10)", 0.4, "4.000000000000001 4.000000000000001", "8 8"); @@ -509,76 +686,161 @@ public void testLineInterpolatePoints() assertLineInterpolatePoints("LINESTRING (0 0, 1 1, 10 10)", 0.5, "5.000000000000001 5.000000000000001", "10 10"); assertLineInterpolatePoints("LINESTRING (0 0, 1 1, 10 10)", 1, "10 10"); - assertInvalidFunction("line_interpolate_points(ST_GeometryFromText('LINESTRING (0 0, 1 0, 1 9)'), -0.5)", "fraction must be between 0 and 1"); - assertInvalidFunction("line_interpolate_points(ST_GeometryFromText('LINESTRING (0 0, 1 0, 1 9)'), 2.0)", "fraction must be between 0 and 1"); - assertInvalidFunction("line_interpolate_points(ST_GeometryFromText('POLYGON ((0 0, 1 1, 0 1, 1 0, 0 0))'), 0.2)", "line_interpolate_point only applies to LINE_STRING. Input type is: POLYGON"); + assertTrinoExceptionThrownBy(() -> assertions.function("line_interpolate_points", "ST_GeometryFromText('LINESTRING (0 0, 1 0, 1 9)')", "-0.5").evaluate()) + .hasMessage("fraction must be between 0 and 1"); + + assertTrinoExceptionThrownBy(() -> assertions.function("line_interpolate_points", "ST_GeometryFromText('LINESTRING (0 0, 1 0, 1 9)')", "2.0").evaluate()) + .hasMessage("fraction must be between 0 and 1"); + + assertTrinoExceptionThrownBy(() -> assertions.function("line_interpolate_points", "ST_GeometryFromText('POLYGON ((0 0, 1 1, 0 1, 1 0, 0 0))')", "0.2").evaluate()) + .hasMessage("line_interpolate_point only applies to LINE_STRING. Input type is: POLYGON"); } private void assertLineInterpolatePoint(String wkt, double fraction, String expectedPoint) { - assertFunction(format("ST_AsText(line_interpolate_point(ST_GeometryFromText('%s)'), %s))", wkt, fraction), VARCHAR, expectedPoint); + assertThat(assertions.expression("ST_AsText(line_interpolate_point(geometry, fraction))") + .binding("geometry", "ST_GeometryFromText('%s')".formatted(wkt)) + .binding("fraction", Double.toString(fraction))) + .hasType(VARCHAR) + .isEqualTo(expectedPoint); } private void assertLineInterpolatePoints(String wkt, double fraction, String... expected) { - assertFunction( - format("transform(line_interpolate_points(ST_GeometryFromText('%s'), %s), x -> ST_AsText(x))", wkt, fraction), - new ArrayType(VARCHAR), - Arrays.stream(expected).map(s -> "POINT (" + s + ")").collect(toImmutableList())); + assertThat(assertions.expression("transform(line_interpolate_points(geometry, fraction), x -> ST_AsText(x))") + .binding("geometry", "ST_GeometryFromText('%s')".formatted(wkt)) + .binding("fraction", Double.toString(fraction))) + .hasType(new ArrayType(VARCHAR)) + .isEqualTo(Arrays.stream(expected).map(s -> "POINT (" + s + ")").collect(toImmutableList())); } @Test public void testSTMax() { - assertFunction("ST_XMax(ST_GeometryFromText('POINT (1.5 2.5)'))", DOUBLE, 1.5); - assertFunction("ST_YMax(ST_GeometryFromText('POINT (1.5 2.5)'))", DOUBLE, 2.5); - assertFunction("ST_XMax(ST_GeometryFromText('MULTIPOINT (1 2, 2 4, 3 6, 4 8)'))", DOUBLE, 4.0); - assertFunction("ST_YMax(ST_GeometryFromText('MULTIPOINT (1 2, 2 4, 3 6, 4 8)'))", DOUBLE, 8.0); - assertFunction("ST_XMax(ST_GeometryFromText('LINESTRING (8 4, 5 7)'))", DOUBLE, 8.0); - assertFunction("ST_YMax(ST_GeometryFromText('LINESTRING (8 4, 5 7)'))", DOUBLE, 7.0); - assertFunction("ST_XMax(ST_GeometryFromText('MULTILINESTRING ((1 1, 5 1), (2 4, 4 4))'))", DOUBLE, 5.0); - assertFunction("ST_YMax(ST_GeometryFromText('MULTILINESTRING ((1 1, 5 1), (2 4, 4 4))'))", DOUBLE, 4.0); - assertFunction("ST_XMax(ST_GeometryFromText('POLYGON ((2 0, 2 1, 3 1))'))", DOUBLE, 3.0); - assertFunction("ST_YMax(ST_GeometryFromText('POLYGON ((2 0, 2 1, 3 1))'))", DOUBLE, 1.0); - assertFunction("ST_XMax(ST_GeometryFromText('MULTIPOLYGON (((1 1, 1 3, 3 3, 3 1)), ((2 4, 2 6, 6 6, 6 4)))'))", DOUBLE, 6.0); - assertFunction("ST_YMax(ST_GeometryFromText('MULTIPOLYGON (((1 1, 1 3, 3 3, 3 1)), ((2 4, 2 6, 6 10, 6 4)))'))", DOUBLE, 10.0); - assertFunction("ST_XMax(ST_GeometryFromText('POLYGON EMPTY'))", DOUBLE, null); - assertFunction("ST_YMax(ST_GeometryFromText('POLYGON EMPTY'))", DOUBLE, null); - assertFunction("ST_XMax(ST_GeometryFromText('GEOMETRYCOLLECTION (POINT (5 1), LINESTRING (3 4, 4 4))'))", DOUBLE, 5.0); - assertFunction("ST_YMax(ST_GeometryFromText('GEOMETRYCOLLECTION (POINT (5 1), LINESTRING (3 4, 4 4))'))", DOUBLE, 4.0); - assertFunction("ST_XMax(null)", DOUBLE, null); - assertFunction("ST_YMax(null)", DOUBLE, null); + assertThat(assertions.function("ST_XMax", "ST_GeometryFromText('POINT (1.5 2.5)')")) + .isEqualTo(1.5); + + assertThat(assertions.function("ST_YMax", "ST_GeometryFromText('POINT (1.5 2.5)')")) + .isEqualTo(2.5); + + assertThat(assertions.function("ST_XMax", "ST_GeometryFromText('MULTIPOINT (1 2, 2 4, 3 6, 4 8)')")) + .isEqualTo(4.0); + + assertThat(assertions.function("ST_YMax", "ST_GeometryFromText('MULTIPOINT (1 2, 2 4, 3 6, 4 8)')")) + .isEqualTo(8.0); + + assertThat(assertions.function("ST_XMax", "ST_GeometryFromText('LINESTRING (8 4, 5 7)')")) + .isEqualTo(8.0); + + assertThat(assertions.function("ST_YMax", "ST_GeometryFromText('LINESTRING (8 4, 5 7)')")) + .isEqualTo(7.0); + + assertThat(assertions.function("ST_XMax", "ST_GeometryFromText('MULTILINESTRING ((1 1, 5 1), (2 4, 4 4))')")) + .isEqualTo(5.0); + + assertThat(assertions.function("ST_YMax", "ST_GeometryFromText('MULTILINESTRING ((1 1, 5 1), (2 4, 4 4))')")) + .isEqualTo(4.0); + + assertThat(assertions.function("ST_XMax", "ST_GeometryFromText('POLYGON ((2 0, 2 1, 3 1))')")) + .isEqualTo(3.0); + + assertThat(assertions.function("ST_YMax", "ST_GeometryFromText('POLYGON ((2 0, 2 1, 3 1))')")) + .isEqualTo(1.0); + + assertThat(assertions.function("ST_XMax", "ST_GeometryFromText('MULTIPOLYGON (((1 1, 1 3, 3 3, 3 1)), ((2 4, 2 6, 6 6, 6 4)))')")) + .isEqualTo(6.0); + + assertThat(assertions.function("ST_YMax", "ST_GeometryFromText('MULTIPOLYGON (((1 1, 1 3, 3 3, 3 1)), ((2 4, 2 6, 6 10, 6 4)))')")) + .isEqualTo(10.0); + + assertThat(assertions.function("ST_XMax", "ST_GeometryFromText('POLYGON EMPTY')")) + .isNull(DOUBLE); + + assertThat(assertions.function("ST_YMax", "ST_GeometryFromText('POLYGON EMPTY')")) + .isNull(DOUBLE); + + assertThat(assertions.function("ST_XMax", "ST_GeometryFromText('GEOMETRYCOLLECTION (POINT (5 1), LINESTRING (3 4, 4 4))')")) + .isEqualTo(5.0); + + assertThat(assertions.function("ST_YMax", "ST_GeometryFromText('GEOMETRYCOLLECTION (POINT (5 1), LINESTRING (3 4, 4 4))')")) + .isEqualTo(4.0); + + assertThat(assertions.function("ST_XMax", "null")) + .isNull(DOUBLE); + + assertThat(assertions.function("ST_YMax", "null")) + .isNull(DOUBLE); } @Test public void testSTMin() { - assertFunction("ST_XMin(ST_GeometryFromText('POINT (1.5 2.5)'))", DOUBLE, 1.5); - assertFunction("ST_YMin(ST_GeometryFromText('POINT (1.5 2.5)'))", DOUBLE, 2.5); - assertFunction("ST_XMin(ST_GeometryFromText('MULTIPOINT (1 2, 2 4, 3 6, 4 8)'))", DOUBLE, 1.0); - assertFunction("ST_YMin(ST_GeometryFromText('MULTIPOINT (1 2, 2 4, 3 6, 4 8)'))", DOUBLE, 2.0); - assertFunction("ST_XMin(ST_GeometryFromText('LINESTRING (8 4, 5 7)'))", DOUBLE, 5.0); - assertFunction("ST_YMin(ST_GeometryFromText('LINESTRING (8 4, 5 7)'))", DOUBLE, 4.0); - assertFunction("ST_XMin(ST_GeometryFromText('MULTILINESTRING ((1 1, 5 1), (2 4, 4 4))'))", DOUBLE, 1.0); - assertFunction("ST_YMin(ST_GeometryFromText('MULTILINESTRING ((1 2, 5 3), (2 4, 4 4))'))", DOUBLE, 2.0); - assertFunction("ST_XMin(ST_GeometryFromText('POLYGON ((2 0, 2 1, 3 1))'))", DOUBLE, 2.0); - assertFunction("ST_YMin(ST_GeometryFromText('POLYGON ((2 0, 2 1, 3 1))'))", DOUBLE, 0.0); - assertFunction("ST_XMin(ST_GeometryFromText('MULTIPOLYGON (((1 10, 1 3, 3 3, 3 10)), ((2 4, 2 6, 6 6, 6 4)))'))", DOUBLE, 1.0); - assertFunction("ST_YMin(ST_GeometryFromText('MULTIPOLYGON (((1 10, 1 3, 3 3, 3 10)), ((2 4, 2 6, 6 10, 6 4)))'))", DOUBLE, 3.0); - assertFunction("ST_XMin(ST_GeometryFromText('POLYGON EMPTY'))", DOUBLE, null); - assertFunction("ST_YMin(ST_GeometryFromText('POLYGON EMPTY'))", DOUBLE, null); - assertFunction("ST_XMin(ST_GeometryFromText('GEOMETRYCOLLECTION (POINT (5 1), LINESTRING (3 4, 4 4))'))", DOUBLE, 3.0); - assertFunction("ST_YMin(ST_GeometryFromText('GEOMETRYCOLLECTION (POINT (5 1), LINESTRING (3 4, 4 4))'))", DOUBLE, 1.0); - assertFunction("ST_XMin(null)", DOUBLE, null); - assertFunction("ST_YMin(null)", DOUBLE, null); + assertThat(assertions.function("ST_XMin", "ST_GeometryFromText('POINT (1.5 2.5)')")) + .isEqualTo(1.5); + + assertThat(assertions.function("ST_YMin", "ST_GeometryFromText('POINT (1.5 2.5)')")) + .isEqualTo(2.5); + + assertThat(assertions.function("ST_XMin", "ST_GeometryFromText('MULTIPOINT (1 2, 2 4, 3 6, 4 8)')")) + .isEqualTo(1.0); + + assertThat(assertions.function("ST_YMin", "ST_GeometryFromText('MULTIPOINT (1 2, 2 4, 3 6, 4 8)')")) + .isEqualTo(2.0); + + assertThat(assertions.function("ST_XMin", "ST_GeometryFromText('LINESTRING (8 4, 5 7)')")) + .isEqualTo(5.0); + + assertThat(assertions.function("ST_YMin", "ST_GeometryFromText('LINESTRING (8 4, 5 7)')")) + .isEqualTo(4.0); + + assertThat(assertions.function("ST_XMin", "ST_GeometryFromText('MULTILINESTRING ((1 1, 5 1), (2 4, 4 4))')")) + .isEqualTo(1.0); + + assertThat(assertions.function("ST_YMin", "ST_GeometryFromText('MULTILINESTRING ((1 2, 5 3), (2 4, 4 4))')")) + .isEqualTo(2.0); + + assertThat(assertions.function("ST_XMin", "ST_GeometryFromText('POLYGON ((2 0, 2 1, 3 1))')")) + .isEqualTo(2.0); + + assertThat(assertions.function("ST_YMin", "ST_GeometryFromText('POLYGON ((2 0, 2 1, 3 1))')")) + .isEqualTo(0.0); + + assertThat(assertions.function("ST_XMin", "ST_GeometryFromText('MULTIPOLYGON (((1 10, 1 3, 3 3, 3 10)), ((2 4, 2 6, 6 6, 6 4)))')")) + .isEqualTo(1.0); + + assertThat(assertions.function("ST_YMin", "ST_GeometryFromText('MULTIPOLYGON (((1 10, 1 3, 3 3, 3 10)), ((2 4, 2 6, 6 10, 6 4)))')")) + .isEqualTo(3.0); + + assertThat(assertions.function("ST_XMin", "ST_GeometryFromText('POLYGON EMPTY')")) + .isNull(DOUBLE); + + assertThat(assertions.function("ST_YMin", "ST_GeometryFromText('POLYGON EMPTY')")) + .isNull(DOUBLE); + + assertThat(assertions.function("ST_XMin", "ST_GeometryFromText('GEOMETRYCOLLECTION (POINT (5 1), LINESTRING (3 4, 4 4))')")) + .isEqualTo(3.0); + + assertThat(assertions.function("ST_YMin", "ST_GeometryFromText('GEOMETRYCOLLECTION (POINT (5 1), LINESTRING (3 4, 4 4))')")) + .isEqualTo(1.0); + + assertThat(assertions.function("ST_XMin", "null")) + .isNull(DOUBLE); + + assertThat(assertions.function("ST_YMin", "null")) + .isNull(DOUBLE); } @Test public void testSTNumInteriorRing() { - assertFunction("ST_NumInteriorRing(ST_GeometryFromText('POLYGON ((0 0, 0 5, 5 5, 5 0, 0 0))'))", BIGINT, 0L); - assertFunction("ST_NumInteriorRing(ST_GeometryFromText('POLYGON ((0 0, 8 0, 0 8, 0 0), (1 1, 1 5, 5 1, 1 1))'))", BIGINT, 1L); - assertInvalidFunction("ST_NumInteriorRing(ST_GeometryFromText('LINESTRING (8 4, 5 7)'))", "ST_NumInteriorRing only applies to POLYGON. Input type is: LINE_STRING"); + assertThat(assertions.function("ST_NumInteriorRing", "ST_GeometryFromText('POLYGON ((0 0, 0 5, 5 5, 5 0, 0 0))')")) + .isEqualTo(0L); + + assertThat(assertions.function("ST_NumInteriorRing", "ST_GeometryFromText('POLYGON ((0 0, 8 0, 0 8, 0 0), (1 1, 1 5, 5 1, 1 1))')")) + .isEqualTo(1L); + + assertTrinoExceptionThrownBy(() -> assertions.function("ST_NumInteriorRing", "ST_GeometryFromText('LINESTRING (8 4, 5 7)')").evaluate()) + .hasMessage("ST_NumInteriorRing only applies to POLYGON. Input type is: LINE_STRING"); } @Test @@ -603,59 +865,88 @@ public void testSTNumPoints() private void assertNumPoints(String wkt, int expectedPoints) { - assertFunction(format("ST_NumPoints(ST_GeometryFromText('%s'))", wkt), BIGINT, (long) expectedPoints); + assertThat(assertions.function("ST_NumPoints", "ST_GeometryFromText('%s')".formatted(wkt))) + .isEqualTo((long) expectedPoints); } @Test public void testSTIsRing() { - assertFunction("ST_IsRing(ST_GeometryFromText('LINESTRING (8 4, 4 8)'))", BOOLEAN, false); - assertFunction("ST_IsRing(ST_GeometryFromText('LINESTRING (0 0, 1 1, 0 2, 0 0)'))", BOOLEAN, true); - assertInvalidFunction("ST_IsRing(ST_GeometryFromText('POLYGON ((2 0, 2 1, 3 1))'))", "ST_IsRing only applies to LINE_STRING. Input type is: POLYGON"); + assertThat(assertions.function("ST_IsRing", "ST_GeometryFromText('LINESTRING (8 4, 4 8)')")) + .isEqualTo(false); + + assertThat(assertions.function("ST_IsRing", "ST_GeometryFromText('LINESTRING (0 0, 1 1, 0 2, 0 0)')")) + .isEqualTo(true); + + assertTrinoExceptionThrownBy(() -> assertions.function("ST_IsRing", "ST_GeometryFromText('POLYGON ((2 0, 2 1, 3 1))')").evaluate()) + .hasMessage("ST_IsRing only applies to LINE_STRING. Input type is: POLYGON"); } @Test public void testSTStartEndPoint() { - assertFunction("ST_AsText(ST_StartPoint(ST_GeometryFromText('LINESTRING (8 4, 4 8, 5 6)')))", VARCHAR, "POINT (8 4)"); - assertFunction("ST_AsText(ST_EndPoint(ST_GeometryFromText('LINESTRING (8 4, 4 8, 5 6)')))", VARCHAR, "POINT (5 6)"); - assertInvalidFunction("ST_AsText(ST_StartPoint(ST_GeometryFromText('POLYGON ((2 0, 2 1, 3 1))')))", "ST_StartPoint only applies to LINE_STRING. Input type is: POLYGON"); - assertInvalidFunction("ST_AsText(ST_EndPoint(ST_GeometryFromText('POLYGON ((2 0, 2 1, 3 1))')))", "ST_EndPoint only applies to LINE_STRING. Input type is: POLYGON"); + assertThat(assertions.function("ST_AsText", "ST_StartPoint(ST_GeometryFromText('LINESTRING (8 4, 4 8, 5 6)'))")) + .hasType(VARCHAR) + .isEqualTo("POINT (8 4)"); + + assertThat(assertions.function("ST_AsText", "ST_EndPoint(ST_GeometryFromText('LINESTRING (8 4, 4 8, 5 6)'))")) + .hasType(VARCHAR) + .isEqualTo("POINT (5 6)"); + + assertTrinoExceptionThrownBy(() -> assertions.function("ST_AsText", "ST_StartPoint(ST_GeometryFromText('POLYGON ((2 0, 2 1, 3 1))'))").evaluate()) + .hasMessage("ST_StartPoint only applies to LINE_STRING. Input type is: POLYGON"); + + assertTrinoExceptionThrownBy(() -> assertions.function("ST_AsText", "ST_EndPoint(ST_GeometryFromText('POLYGON ((2 0, 2 1, 3 1))'))").evaluate()) + .hasMessage("ST_EndPoint only applies to LINE_STRING. Input type is: POLYGON"); } @Test public void testSTPoints() { - assertFunction("ST_Points(ST_GeometryFromText('LINESTRING EMPTY'))", new ArrayType(GEOMETRY), null); + assertThat(assertions.function("ST_Points", "ST_GeometryFromText('LINESTRING EMPTY')")) + .isNull(new ArrayType(GEOMETRY)); + assertSTPoints("LINESTRING (0 0, 0 0)", "0 0", "0 0"); assertSTPoints("LINESTRING (8 4, 3 9, 8 4)", "8 4", "3 9", "8 4"); assertSTPoints("LINESTRING (8 4, 3 9, 5 6)", "8 4", "3 9", "5 6"); assertSTPoints("LINESTRING (8 4, 3 9, 5 6, 3 9, 8 4)", "8 4", "3 9", "5 6", "3 9", "8 4"); - assertFunction("ST_Points(ST_GeometryFromText('POLYGON EMPTY'))", new ArrayType(GEOMETRY), null); + assertThat(assertions.function("ST_Points", "ST_GeometryFromText('POLYGON EMPTY')")) + .isNull(new ArrayType(GEOMETRY)); + assertSTPoints("POLYGON ((8 4, 3 9, 5 6, 8 4))", "8 4", "5 6", "3 9", "8 4"); assertSTPoints("POLYGON ((8 4, 3 9, 5 6, 7 2, 8 4))", "8 4", "7 2", "5 6", "3 9", "8 4"); - assertFunction("ST_Points(ST_GeometryFromText('POINT EMPTY'))", new ArrayType(GEOMETRY), null); + assertThat(assertions.function("ST_Points", "ST_GeometryFromText('POINT EMPTY')")) + .isNull(new ArrayType(GEOMETRY)); + assertSTPoints("POINT (0 0)", "0 0"); assertSTPoints("POINT (0 1)", "0 1"); - assertFunction("ST_Points(ST_GeometryFromText('MULTIPOINT EMPTY'))", new ArrayType(GEOMETRY), null); + assertThat(assertions.function("ST_Points", "ST_GeometryFromText('MULTIPOINT EMPTY')")) + .isNull(new ArrayType(GEOMETRY)); + assertSTPoints("MULTIPOINT (0 0)", "0 0"); assertSTPoints("MULTIPOINT (0 0, 1 2)", "0 0", "1 2"); - assertFunction("ST_Points(ST_GeometryFromText('MULTILINESTRING EMPTY'))", new ArrayType(GEOMETRY), null); + assertThat(assertions.function("ST_Points", "ST_GeometryFromText('MULTILINESTRING EMPTY')")) + .isNull(new ArrayType(GEOMETRY)); + assertSTPoints("MULTILINESTRING ((0 0, 1 1), (2 3, 3 2))", "0 0", "1 1", "2 3", "3 2"); assertSTPoints("MULTILINESTRING ((0 0, 1 1, 1 2), (2 3, 3 2, 5 4))", "0 0", "1 1", "1 2", "2 3", "3 2", "5 4"); assertSTPoints("MULTILINESTRING ((0 0, 1 1, 1 2), (1 2, 3 2, 5 4))", "0 0", "1 1", "1 2", "1 2", "3 2", "5 4"); - assertFunction("ST_Points(ST_GeometryFromText('MULTIPOLYGON EMPTY'))", new ArrayType(GEOMETRY), null); + assertThat(assertions.function("ST_Points", "ST_GeometryFromText('MULTIPOLYGON EMPTY')")) + .isNull(new ArrayType(GEOMETRY)); + assertSTPoints("MULTIPOLYGON (((0 0, 4 0, 4 4, 0 4, 0 0), (1 1, 2 1, 2 2, 1 2, 1 1)), ((-1 -1, -1 -2, -2 -2, -2 -1, -1 -1)))", "0 0", "0 4", "4 4", "4 0", "0 0", "1 1", "2 1", "2 2", "1 2", "1 1", "-1 -1", "-1 -2", "-2 -2", "-2 -1", "-1 -1"); - assertFunction("ST_Points(ST_GeometryFromText('GEOMETRYCOLLECTION EMPTY'))", new ArrayType(GEOMETRY), null); + assertThat(assertions.function("ST_Points", "ST_GeometryFromText('GEOMETRYCOLLECTION EMPTY')")) + .isNull(new ArrayType(GEOMETRY)); + String newLine = System.getProperty("line.separator"); String geometryCollection = String.join(newLine, "GEOMETRYCOLLECTION(", @@ -676,52 +967,107 @@ public void testSTPoints() private void assertSTPoints(String wkt, String... expected) { - assertFunction( - format("transform(ST_Points(ST_GeometryFromText('%s')), x -> ST_AsText(x))", wkt), - new ArrayType(VARCHAR), - Arrays.stream(expected).map(s -> "POINT (" + s + ")").collect(toImmutableList())); + assertThat(assertions.expression("transform(ST_Points(geometry), x -> ST_AsText(x))") + .binding("geometry", "ST_GeometryFromText('%s')".formatted(wkt))) + .hasType(new ArrayType(VARCHAR)) + .isEqualTo(Arrays.stream(expected).map(s -> "POINT (" + s + ")").collect(toImmutableList())); } @Test public void testSTXY() { - assertFunction("ST_Y(ST_GeometryFromText('POINT EMPTY'))", DOUBLE, null); - assertFunction("ST_X(ST_GeometryFromText('POINT (1 2)'))", DOUBLE, 1.0); - assertFunction("ST_Y(ST_GeometryFromText('POINT (1 2)'))", DOUBLE, 2.0); - assertInvalidFunction("ST_Y(ST_GeometryFromText('POLYGON ((2 0, 2 1, 3 1))'))", "ST_Y only applies to POINT. Input type is: POLYGON"); + assertThat(assertions.function("ST_Y", "ST_GeometryFromText('POINT EMPTY')")) + .isNull(DOUBLE); + + assertThat(assertions.function("ST_X", "ST_GeometryFromText('POINT (1 2)')")) + .isEqualTo(1.0); + + assertThat(assertions.function("ST_Y", "ST_GeometryFromText('POINT (1 2)')")) + .isEqualTo(2.0); + + assertTrinoExceptionThrownBy(() -> assertions.function("ST_Y", "ST_GeometryFromText('POLYGON ((2 0, 2 1, 3 1))')").evaluate()) + .hasMessage("ST_Y only applies to POINT. Input type is: POLYGON"); } @Test public void testSTBoundary() { - assertFunction("ST_AsText(ST_Boundary(ST_GeometryFromText('POINT (1 2)')))", VARCHAR, "MULTIPOINT EMPTY"); - assertFunction("ST_AsText(ST_Boundary(ST_GeometryFromText('MULTIPOINT (1 2, 2 4, 3 6, 4 8)')))", VARCHAR, "MULTIPOINT EMPTY"); - assertFunction("ST_AsText(ST_Boundary(ST_GeometryFromText('LINESTRING EMPTY')))", VARCHAR, "MULTIPOINT EMPTY"); - assertFunction("ST_AsText(ST_Boundary(ST_GeometryFromText('LINESTRING (8 4, 5 7)')))", VARCHAR, "MULTIPOINT ((8 4), (5 7))"); - assertFunction("ST_AsText(ST_Boundary(ST_GeometryFromText('LINESTRING (100 150,50 60, 70 80, 160 170)')))", VARCHAR, "MULTIPOINT ((100 150), (160 170))"); - assertFunction("ST_AsText(ST_Boundary(ST_GeometryFromText('MULTILINESTRING ((1 1, 5 1), (2 4, 4 4))')))", VARCHAR, "MULTIPOINT ((1 1), (5 1), (2 4), (4 4))"); - assertFunction("ST_AsText(ST_Boundary(ST_GeometryFromText('POLYGON ((1 1, 4 1, 1 4))')))", VARCHAR, "MULTILINESTRING ((1 1, 4 1, 1 4, 1 1))"); - assertFunction("ST_AsText(ST_Boundary(ST_GeometryFromText('MULTIPOLYGON (((1 1, 1 3, 3 3, 3 1)), ((0 0, 0 2, 2 2, 2 0)))')))", VARCHAR, "MULTILINESTRING ((1 1, 3 1, 3 3, 1 3, 1 1), (0 0, 2 0, 2 2, 0 2, 0 0))"); + assertThat(assertions.function("ST_AsText", "ST_Boundary(ST_GeometryFromText('POINT (1 2)'))")) + .hasType(VARCHAR) + .isEqualTo("MULTIPOINT EMPTY"); + + assertThat(assertions.function("ST_AsText", "ST_Boundary(ST_GeometryFromText('MULTIPOINT (1 2, 2 4, 3 6, 4 8)'))")) + .hasType(VARCHAR) + .isEqualTo("MULTIPOINT EMPTY"); + + assertThat(assertions.function("ST_AsText", "ST_Boundary(ST_GeometryFromText('LINESTRING EMPTY'))")) + .hasType(VARCHAR) + .isEqualTo("MULTIPOINT EMPTY"); + + assertThat(assertions.function("ST_AsText", "ST_Boundary(ST_GeometryFromText('LINESTRING (8 4, 5 7)'))")) + .hasType(VARCHAR) + .isEqualTo("MULTIPOINT ((8 4), (5 7))"); + + assertThat(assertions.function("ST_AsText", "ST_Boundary(ST_GeometryFromText('LINESTRING (100 150,50 60, 70 80, 160 170)'))")) + .hasType(VARCHAR) + .isEqualTo("MULTIPOINT ((100 150), (160 170))"); + + assertThat(assertions.function("ST_AsText", "ST_Boundary(ST_GeometryFromText('MULTILINESTRING ((1 1, 5 1), (2 4, 4 4))'))")) + .hasType(VARCHAR) + .isEqualTo("MULTIPOINT ((1 1), (5 1), (2 4), (4 4))"); + + assertThat(assertions.function("ST_AsText", "ST_Boundary(ST_GeometryFromText('POLYGON ((1 1, 4 1, 1 4))'))")) + .hasType(VARCHAR) + .isEqualTo("MULTILINESTRING ((1 1, 4 1, 1 4, 1 1))"); + + assertThat(assertions.function("ST_AsText", "ST_Boundary(ST_GeometryFromText('MULTIPOLYGON (((1 1, 1 3, 3 3, 3 1)), ((0 0, 0 2, 2 2, 2 0)))'))")) + .hasType(VARCHAR) + .isEqualTo("MULTILINESTRING ((1 1, 3 1, 3 3, 1 3, 1 1), (0 0, 2 0, 2 2, 0 2, 0 0))"); } @Test public void testSTEnvelope() { - assertFunction("ST_AsText(ST_Envelope(ST_GeometryFromText('MULTIPOINT (1 2, 2 4, 3 6, 4 8)')))", VARCHAR, "POLYGON ((1 2, 4 2, 4 8, 1 8, 1 2))"); - assertFunction("ST_AsText(ST_Envelope(ST_GeometryFromText('LINESTRING EMPTY')))", VARCHAR, "POLYGON EMPTY"); - assertFunction("ST_AsText(ST_Envelope(ST_GeometryFromText('LINESTRING (1 1, 2 2, 1 3)')))", VARCHAR, "POLYGON ((1 1, 2 1, 2 3, 1 3, 1 1))"); - assertFunction("ST_AsText(ST_Envelope(ST_GeometryFromText('LINESTRING (8 4, 5 7)')))", VARCHAR, "POLYGON ((5 4, 8 4, 8 7, 5 7, 5 4))"); - assertFunction("ST_AsText(ST_Envelope(ST_GeometryFromText('MULTILINESTRING ((1 1, 5 1), (2 4, 4 4))')))", VARCHAR, "POLYGON ((1 1, 5 1, 5 4, 1 4, 1 1))"); - assertFunction("ST_AsText(ST_Envelope(ST_GeometryFromText('POLYGON ((1 1, 4 1, 1 4))')))", VARCHAR, "POLYGON ((1 1, 4 1, 4 4, 1 4, 1 1))"); - assertFunction("ST_AsText(ST_Envelope(ST_GeometryFromText('MULTIPOLYGON (((1 1, 1 3, 3 3, 3 1)), ((0 0, 0 2, 2 2, 2 0)))')))", VARCHAR, "POLYGON ((0 0, 3 0, 3 3, 0 3, 0 0))"); - assertFunction("ST_AsText(ST_Envelope(ST_GeometryFromText('GEOMETRYCOLLECTION (POINT (5 1), LINESTRING (3 4, 4 4))')))", VARCHAR, "POLYGON ((3 1, 5 1, 5 4, 3 4, 3 1))"); + assertThat(assertions.function("ST_AsText", "ST_Envelope(ST_GeometryFromText('MULTIPOINT (1 2, 2 4, 3 6, 4 8)'))")) + .hasType(VARCHAR) + .isEqualTo("POLYGON ((1 2, 4 2, 4 8, 1 8, 1 2))"); + + assertThat(assertions.function("ST_AsText", "ST_Envelope(ST_GeometryFromText('LINESTRING EMPTY'))")) + .hasType(VARCHAR) + .isEqualTo("POLYGON EMPTY"); + + assertThat(assertions.function("ST_AsText", "ST_Envelope(ST_GeometryFromText('LINESTRING (1 1, 2 2, 1 3)'))")) + .hasType(VARCHAR) + .isEqualTo("POLYGON ((1 1, 2 1, 2 3, 1 3, 1 1))"); + + assertThat(assertions.function("ST_AsText", "ST_Envelope(ST_GeometryFromText('LINESTRING (8 4, 5 7)'))")) + .hasType(VARCHAR) + .isEqualTo("POLYGON ((5 4, 8 4, 8 7, 5 7, 5 4))"); + + assertThat(assertions.function("ST_AsText", "ST_Envelope(ST_GeometryFromText('MULTILINESTRING ((1 1, 5 1), (2 4, 4 4))'))")) + .hasType(VARCHAR) + .isEqualTo("POLYGON ((1 1, 5 1, 5 4, 1 4, 1 1))"); + + assertThat(assertions.function("ST_AsText", "ST_Envelope(ST_GeometryFromText('POLYGON ((1 1, 4 1, 1 4))'))")) + .hasType(VARCHAR) + .isEqualTo("POLYGON ((1 1, 4 1, 4 4, 1 4, 1 1))"); + + assertThat(assertions.function("ST_AsText", "ST_Envelope(ST_GeometryFromText('MULTIPOLYGON (((1 1, 1 3, 3 3, 3 1)), ((0 0, 0 2, 2 2, 2 0)))'))")) + .hasType(VARCHAR) + .isEqualTo("POLYGON ((0 0, 3 0, 3 3, 0 3, 0 0))"); + + assertThat(assertions.function("ST_AsText", "ST_Envelope(ST_GeometryFromText('GEOMETRYCOLLECTION (POINT (5 1), LINESTRING (3 4, 4 4))'))")) + .hasType(VARCHAR) + .isEqualTo("POLYGON ((3 1, 5 1, 5 4, 3 4, 3 1))"); } @Test public void testSTEnvelopeAsPts() { assertEnvelopeAsPts("MULTIPOINT (1 2, 2 4, 3 6, 4 8)", new Point(1, 2), new Point(4, 8)); - assertFunction("ST_EnvelopeAsPts(ST_GeometryFromText('LINESTRING EMPTY'))", new ArrayType(GEOMETRY), null); + assertThat(assertions.function("ST_EnvelopeAsPts", "ST_GeometryFromText('LINESTRING EMPTY')")) + .isNull(new ArrayType(GEOMETRY)); + assertEnvelopeAsPts("LINESTRING (1 1, 2 2, 1 3)", new Point(1, 1), new Point(2, 3)); assertEnvelopeAsPts("LINESTRING (8 4, 5 7)", new Point(5, 4), new Point(8, 7)); assertEnvelopeAsPts("MULTILINESTRING ((1 1, 5 1), (2 4, 4 4))", new Point(1, 1), new Point(5, 4)); @@ -733,40 +1079,90 @@ public void testSTEnvelopeAsPts() private void assertEnvelopeAsPts(String wkt, Point lowerLeftCorner, Point upperRightCorner) { - assertFunction(format("transform(ST_EnvelopeAsPts(ST_GeometryFromText('%s')), x -> ST_AsText(x))", wkt), new ArrayType(VARCHAR), ImmutableList.of(new OGCPoint(lowerLeftCorner, null).asText(), new OGCPoint(upperRightCorner, null).asText())); + assertThat(assertions.expression("transform(ST_EnvelopeAsPts(geometry), x -> ST_AsText(x))") + .binding("geometry", "ST_GeometryFromText('%s')".formatted(wkt))) + .hasType(new ArrayType(VARCHAR)) + .isEqualTo(ImmutableList.of(new OGCPoint(lowerLeftCorner, null).asText(), new OGCPoint(upperRightCorner, null).asText())); } @Test public void testSTDifference() { - assertFunction("ST_AsText(ST_Difference(ST_GeometryFromText('POINT (50 100)'), ST_GeometryFromText('POINT (150 150)')))", VARCHAR, "POINT (50 100)"); - assertFunction("ST_AsText(ST_Difference(ST_GeometryFromText('MULTIPOINT (50 100, 50 200)'), ST_GeometryFromText('POINT (50 100)')))", VARCHAR, "POINT (50 200)"); - assertFunction("ST_AsText(ST_Difference(ST_GeometryFromText('LINESTRING (50 100, 50 200)'), ST_GeometryFromText('LINESTRING (50 50, 50 150)')))", VARCHAR, "LINESTRING (50 150, 50 200)"); - assertFunction("ST_AsText(ST_Difference(ST_GeometryFromText('MULTILINESTRING ((1 1, 5 1), (2 4, 4 4))'), ST_GeometryFromText('MULTILINESTRING ((2 1, 4 1), (3 3, 7 3))')))", VARCHAR, "MULTILINESTRING ((1 1, 2 1), (4 1, 5 1), (2 4, 4 4))"); - assertFunction("ST_AsText(ST_Difference(ST_GeometryFromText('POLYGON ((1 1, 1 4, 4 4, 4 1))'), ST_GeometryFromText('POLYGON ((2 2, 2 5, 5 5, 5 2))')))", VARCHAR, "POLYGON ((1 1, 4 1, 4 2, 2 2, 2 4, 1 4, 1 1))"); - assertFunction("ST_AsText(ST_Difference(ST_GeometryFromText('MULTIPOLYGON (((1 1, 1 3, 3 3, 3 1)), ((0 0, 0 2, 2 2, 2 0)))'), ST_GeometryFromText('POLYGON ((0 1, 3 1, 3 3, 0 3, 0 1))')))", VARCHAR, "POLYGON ((1 1, 0 1, 0 0, 2 0, 2 1, 1 1))"); + assertThat(assertions.function("ST_AsText", "ST_Difference(ST_GeometryFromText('POINT (50 100)'), ST_GeometryFromText('POINT (150 150)'))")) + .hasType(VARCHAR) + .isEqualTo("POINT (50 100)"); + + assertThat(assertions.function("ST_AsText", "ST_Difference(ST_GeometryFromText('MULTIPOINT (50 100, 50 200)'), ST_GeometryFromText('POINT (50 100)'))")) + .hasType(VARCHAR) + .isEqualTo("POINT (50 200)"); + + assertThat(assertions.function("ST_AsText", "ST_Difference(ST_GeometryFromText('LINESTRING (50 100, 50 200)'), ST_GeometryFromText('LINESTRING (50 50, 50 150)'))")) + .hasType(VARCHAR) + .isEqualTo("LINESTRING (50 150, 50 200)"); + + assertThat(assertions.function("ST_AsText", "ST_Difference(ST_GeometryFromText('MULTILINESTRING ((1 1, 5 1), (2 4, 4 4))'), ST_GeometryFromText('MULTILINESTRING ((2 1, 4 1), (3 3, 7 3))'))")) + .hasType(VARCHAR) + .isEqualTo("MULTILINESTRING ((1 1, 2 1), (4 1, 5 1), (2 4, 4 4))"); + + assertThat(assertions.function("ST_AsText", "ST_Difference(ST_GeometryFromText('POLYGON ((1 1, 1 4, 4 4, 4 1))'), ST_GeometryFromText('POLYGON ((2 2, 2 5, 5 5, 5 2))'))")) + .hasType(VARCHAR) + .isEqualTo("POLYGON ((1 1, 4 1, 4 2, 2 2, 2 4, 1 4, 1 1))"); + + assertThat(assertions.function("ST_AsText", "ST_Difference(ST_GeometryFromText('MULTIPOLYGON (((1 1, 1 3, 3 3, 3 1)), ((0 0, 0 2, 2 2, 2 0)))'), ST_GeometryFromText('POLYGON ((0 1, 3 1, 3 3, 0 3, 0 1))'))")) + .hasType(VARCHAR) + .isEqualTo("POLYGON ((1 1, 0 1, 0 0, 2 0, 2 1, 1 1))"); } @Test public void testSTDistance() { - assertFunction("ST_Distance(ST_Point(50, 100), ST_Point(150, 150))", DOUBLE, 111.80339887498948); - assertFunction("ST_Distance(ST_Point(50, 100), ST_GeometryFromText('POINT (150 150)'))", DOUBLE, 111.80339887498948); - assertFunction("ST_Distance(ST_GeometryFromText('POINT (50 100)'), ST_GeometryFromText('POINT (150 150)'))", DOUBLE, 111.80339887498948); - assertFunction("ST_Distance(ST_GeometryFromText('MULTIPOINT (50 100, 50 200)'), ST_GeometryFromText('Point (50 100)'))", DOUBLE, 0.0); - assertFunction("ST_Distance(ST_GeometryFromText('LINESTRING (50 100, 50 200)'), ST_GeometryFromText('LINESTRING (10 10, 20 20)'))", DOUBLE, 85.44003745317531); - assertFunction("ST_Distance(ST_GeometryFromText('MULTILINESTRING ((1 1, 5 1), (2 4, 4 4))'), ST_GeometryFromText('LINESTRING (10 20, 20 50)'))", DOUBLE, 17.08800749063506); - assertFunction("ST_Distance(ST_GeometryFromText('POLYGON ((1 1, 1 3, 3 3, 3 1))'), ST_GeometryFromText('POLYGON ((4 4, 4 5, 5 5, 5 4))'))", DOUBLE, 1.4142135623730951); - assertFunction("ST_Distance(ST_GeometryFromText('MULTIPOLYGON (((1 1, 1 3, 3 3, 3 1)), ((0 0, 0 2, 2 2, 2 0)))'), ST_GeometryFromText('POLYGON ((10 100, 30 10))'))", DOUBLE, 27.892651361962706); - - assertFunction("ST_Distance(ST_GeometryFromText('POINT EMPTY'), ST_Point(150, 150))", DOUBLE, null); - assertFunction("ST_Distance(ST_Point(50, 100), ST_GeometryFromText('POINT EMPTY'))", DOUBLE, null); - assertFunction("ST_Distance(ST_GeometryFromText('POINT EMPTY'), ST_GeometryFromText('POINT EMPTY'))", DOUBLE, null); - assertFunction("ST_Distance(ST_GeometryFromText('MULTIPOINT EMPTY'), ST_GeometryFromText('Point (50 100)'))", DOUBLE, null); - assertFunction("ST_Distance(ST_GeometryFromText('LINESTRING (50 100, 50 200)'), ST_GeometryFromText('LINESTRING EMPTY'))", DOUBLE, null); - assertFunction("ST_Distance(ST_GeometryFromText('MULTILINESTRING EMPTY'), ST_GeometryFromText('LINESTRING (10 20, 20 50)'))", DOUBLE, null); - assertFunction("ST_Distance(ST_GeometryFromText('POLYGON ((1 1, 1 3, 3 3, 3 1))'), ST_GeometryFromText('POLYGON EMPTY'))", DOUBLE, null); - assertFunction("ST_Distance(ST_GeometryFromText('MULTIPOLYGON EMPTY'), ST_GeometryFromText('POLYGON ((10 100, 30 10))'))", DOUBLE, null); + assertThat(assertions.function("ST_Distance", "ST_Point(50, 100)", "ST_Point(150, 150)")) + .isEqualTo(111.80339887498948); + + assertThat(assertions.function("ST_Distance", "ST_Point(50, 100)", "ST_GeometryFromText('POINT (150 150)')")) + .isEqualTo(111.80339887498948); + + assertThat(assertions.function("ST_Distance", "ST_GeometryFromText('POINT (50 100)')", "ST_GeometryFromText('POINT (150 150)')")) + .isEqualTo(111.80339887498948); + + assertThat(assertions.function("ST_Distance", "ST_GeometryFromText('MULTIPOINT (50 100, 50 200)')", "ST_GeometryFromText('Point (50 100)')")) + .isEqualTo(0.0); + + assertThat(assertions.function("ST_Distance", "ST_GeometryFromText('LINESTRING (50 100, 50 200)')", "ST_GeometryFromText('LINESTRING (10 10, 20 20)')")) + .isEqualTo(85.44003745317531); + + assertThat(assertions.function("ST_Distance", "ST_GeometryFromText('MULTILINESTRING ((1 1, 5 1), (2 4, 4 4))')", "ST_GeometryFromText('LINESTRING (10 20, 20 50)')")) + .isEqualTo(17.08800749063506); + + assertThat(assertions.function("ST_Distance", "ST_GeometryFromText('POLYGON ((1 1, 1 3, 3 3, 3 1))')", "ST_GeometryFromText('POLYGON ((4 4, 4 5, 5 5, 5 4))')")) + .isEqualTo(1.4142135623730951); + + assertThat(assertions.function("ST_Distance", "ST_GeometryFromText('MULTIPOLYGON (((1 1, 1 3, 3 3, 3 1)), ((0 0, 0 2, 2 2, 2 0)))')", "ST_GeometryFromText('POLYGON ((10 100, 30 10))')")) + .isEqualTo(27.892651361962706); + + assertThat(assertions.function("ST_Distance", "ST_GeometryFromText('POINT EMPTY')", "ST_Point(150, 150)")) + .isNull(DOUBLE); + + assertThat(assertions.function("ST_Distance", "ST_Point(50, 100)", "ST_GeometryFromText('POINT EMPTY')")) + .isNull(DOUBLE); + + assertThat(assertions.function("ST_Distance", "ST_GeometryFromText('POINT EMPTY')", "ST_GeometryFromText('POINT EMPTY')")) + .isNull(DOUBLE); + + assertThat(assertions.function("ST_Distance", "ST_GeometryFromText('MULTIPOINT EMPTY')", "ST_GeometryFromText('Point (50 100)')")) + .isNull(DOUBLE); + + assertThat(assertions.function("ST_Distance", "ST_GeometryFromText('LINESTRING (50 100, 50 200)')", "ST_GeometryFromText('LINESTRING EMPTY')")) + .isNull(DOUBLE); + + assertThat(assertions.function("ST_Distance", "ST_GeometryFromText('MULTILINESTRING EMPTY')", "ST_GeometryFromText('LINESTRING (10 20, 20 50)')")) + .isNull(DOUBLE); + + assertThat(assertions.function("ST_Distance", "ST_GeometryFromText('POLYGON ((1 1, 1 3, 3 3, 3 1))')", "ST_GeometryFromText('POLYGON EMPTY')")) + .isNull(DOUBLE); + + assertThat(assertions.function("ST_Distance", "ST_GeometryFromText('MULTIPOLYGON EMPTY')", "ST_GeometryFromText('POLYGON ((10 100, 30 10))')")) + .isNull(DOUBLE); } @Test @@ -792,41 +1188,72 @@ public void testGeometryNearestPoints() private void assertNearestPoints(String leftInputWkt, String rightInputWkt, String leftPointWkt, String rightPointWkt) { - assertFunction( - format("geometry_nearest_points(ST_GeometryFromText('%s'), ST_GeometryFromText('%s'))", leftInputWkt, rightInputWkt), - RowType.anonymous(ImmutableList.of(GEOMETRY, GEOMETRY)), - ImmutableList.of(leftPointWkt, rightPointWkt)); + assertThat(assertions.function("geometry_nearest_points", "ST_GeometryFromText('%s')".formatted(leftInputWkt), "ST_GeometryFromText('%s')".formatted(rightInputWkt))) + .hasType(RowType.anonymous(ImmutableList.of(GEOMETRY, GEOMETRY))) + .isEqualTo(ImmutableList.of(leftPointWkt, rightPointWkt)); } private void assertNoNearestPoints(String leftInputWkt, String rightInputWkt) { - assertFunction( - format("geometry_nearest_points(ST_GeometryFromText('%s'), ST_GeometryFromText('%s'))", leftInputWkt, rightInputWkt), - RowType.anonymous(ImmutableList.of(GEOMETRY, GEOMETRY)), - null); + assertThat(assertions.function("geometry_nearest_points", "ST_GeometryFromText('%s')".formatted(leftInputWkt), "ST_GeometryFromText('%s')".formatted(rightInputWkt))) + .isNull(RowType.anonymous(ImmutableList.of(GEOMETRY, GEOMETRY))); } @Test public void testSTExteriorRing() { - assertFunction("ST_AsText(ST_ExteriorRing(ST_GeometryFromText('POLYGON EMPTY')))", VARCHAR, null); - assertFunction("ST_AsText(ST_ExteriorRing(ST_GeometryFromText('POLYGON ((1 1, 1 4, 4 1))')))", VARCHAR, "LINESTRING (1 1, 4 1, 1 4, 1 1)"); - assertFunction("ST_AsText(ST_ExteriorRing(ST_GeometryFromText('POLYGON ((0 0, 0 5, 5 5, 5 0, 0 0), (1 1, 1 2, 2 2, 2 1, 1 1))')))", VARCHAR, "LINESTRING (0 0, 5 0, 5 5, 0 5, 0 0)"); - assertInvalidFunction("ST_AsText(ST_ExteriorRing(ST_GeometryFromText('LINESTRING (1 1, 2 2, 1 3)')))", "ST_ExteriorRing only applies to POLYGON. Input type is: LINE_STRING"); - assertInvalidFunction("ST_AsText(ST_ExteriorRing(ST_GeometryFromText('MULTIPOLYGON (((1 1, 2 2, 1 3, 1 1)), ((4 4, 5 5, 4 6, 4 4)))')))", "ST_ExteriorRing only applies to POLYGON. Input type is: MULTI_POLYGON"); + assertThat(assertions.function("ST_AsText", "ST_ExteriorRing(ST_GeometryFromText('POLYGON EMPTY'))")) + .isNull(VARCHAR); + + assertThat(assertions.function("ST_AsText", "ST_ExteriorRing(ST_GeometryFromText('POLYGON ((1 1, 1 4, 4 1))'))")) + .hasType(VARCHAR) + .isEqualTo("LINESTRING (1 1, 4 1, 1 4, 1 1)"); + + assertThat(assertions.function("ST_AsText", "ST_ExteriorRing(ST_GeometryFromText('POLYGON ((0 0, 0 5, 5 5, 5 0, 0 0), (1 1, 1 2, 2 2, 2 1, 1 1))'))")) + .hasType(VARCHAR) + .isEqualTo("LINESTRING (0 0, 5 0, 5 5, 0 5, 0 0)"); + + assertTrinoExceptionThrownBy(() -> assertions.function("ST_AsText", "ST_ExteriorRing(ST_GeometryFromText('LINESTRING (1 1, 2 2, 1 3)'))").evaluate()) + .hasMessage("ST_ExteriorRing only applies to POLYGON. Input type is: LINE_STRING"); + + assertTrinoExceptionThrownBy(() -> assertions.function("ST_AsText", "ST_ExteriorRing(ST_GeometryFromText('MULTIPOLYGON (((1 1, 2 2, 1 3, 1 1)), ((4 4, 5 5, 4 6, 4 4)))'))").evaluate()) + .hasMessage("ST_ExteriorRing only applies to POLYGON. Input type is: MULTI_POLYGON"); } @Test public void testSTIntersection() { - assertFunction("ST_AsText(ST_Intersection(ST_GeometryFromText('POINT (50 100)'), ST_GeometryFromText('POINT (150 150)')))", VARCHAR, "MULTIPOLYGON EMPTY"); - assertFunction("ST_AsText(ST_Intersection(ST_GeometryFromText('MULTIPOINT (50 100, 50 200)'), ST_GeometryFromText('Point (50 100)')))", VARCHAR, "POINT (50 100)"); - assertFunction("ST_AsText(ST_Intersection(ST_GeometryFromText('LINESTRING (50 100, 50 200)'), ST_GeometryFromText('LINESTRING (20 150, 100 150)')))", VARCHAR, "POINT (50 150)"); - assertFunction("ST_AsText(ST_Intersection(ST_GeometryFromText('MULTILINESTRING ((1 1, 5 1), (2 4, 4 4))'), ST_GeometryFromText('MULTILINESTRING ((3 4, 6 4), (5 0, 5 4))')))", VARCHAR, "GEOMETRYCOLLECTION (POINT (5 1), LINESTRING (3 4, 4 4))"); - assertFunction("ST_AsText(ST_Intersection(ST_GeometryFromText('POLYGON ((1 1, 1 3, 3 3, 3 1))'), ST_GeometryFromText('POLYGON ((4 4, 4 5, 5 5, 5 4))')))", VARCHAR, "MULTIPOLYGON EMPTY"); - assertFunction("ST_AsText(ST_Intersection(ST_GeometryFromText('MULTIPOLYGON (((1 1, 1 3, 3 3, 3 1)), ((0 0, 0 2, 2 2, 2 0)))'), ST_GeometryFromText('POLYGON ((0 1, 3 1, 3 3, 0 3))')))", VARCHAR, "GEOMETRYCOLLECTION (LINESTRING (1 1, 2 1), MULTIPOLYGON (((0 1, 1 1, 1 2, 0 2, 0 1)), ((2 1, 3 1, 3 3, 1 3, 1 2, 2 2, 2 1))))"); - assertFunction("ST_AsText(ST_Intersection(ST_GeometryFromText('POLYGON ((1 1, 1 4, 4 4, 4 1))'), ST_GeometryFromText('LINESTRING (2 0, 2 3)')))", VARCHAR, "LINESTRING (2 1, 2 3)"); - assertFunction("ST_AsText(ST_Intersection(ST_GeometryFromText('POLYGON ((0 0, 0 1, 1 1, 1 0, 0 0))'), ST_GeometryFromText('LINESTRING (0 0, 1 -1, 1 2)')))", VARCHAR, "GEOMETRYCOLLECTION (POINT (0 0), LINESTRING (1 0, 1 1))"); + assertThat(assertions.function("ST_AsText", "ST_Intersection(ST_GeometryFromText('POINT (50 100)'), ST_GeometryFromText('POINT (150 150)'))")) + .hasType(VARCHAR) + .isEqualTo("MULTIPOLYGON EMPTY"); + + assertThat(assertions.function("ST_AsText", "ST_Intersection(ST_GeometryFromText('MULTIPOINT (50 100, 50 200)'), ST_GeometryFromText('Point (50 100)'))")) + .hasType(VARCHAR) + .isEqualTo("POINT (50 100)"); + + assertThat(assertions.function("ST_AsText", "ST_Intersection(ST_GeometryFromText('LINESTRING (50 100, 50 200)'), ST_GeometryFromText('LINESTRING (20 150, 100 150)'))")) + .hasType(VARCHAR) + .isEqualTo("POINT (50 150)"); + + assertThat(assertions.function("ST_AsText", "ST_Intersection(ST_GeometryFromText('MULTILINESTRING ((1 1, 5 1), (2 4, 4 4))'), ST_GeometryFromText('MULTILINESTRING ((3 4, 6 4), (5 0, 5 4))'))")) + .hasType(VARCHAR) + .isEqualTo("GEOMETRYCOLLECTION (POINT (5 1), LINESTRING (3 4, 4 4))"); + + assertThat(assertions.function("ST_AsText", "ST_Intersection(ST_GeometryFromText('POLYGON ((1 1, 1 3, 3 3, 3 1))'), ST_GeometryFromText('POLYGON ((4 4, 4 5, 5 5, 5 4))'))")) + .hasType(VARCHAR) + .isEqualTo("MULTIPOLYGON EMPTY"); + + assertThat(assertions.function("ST_AsText", "ST_Intersection(ST_GeometryFromText('MULTIPOLYGON (((1 1, 1 3, 3 3, 3 1)), ((0 0, 0 2, 2 2, 2 0)))'), ST_GeometryFromText('POLYGON ((0 1, 3 1, 3 3, 0 3))'))")) + .hasType(VARCHAR) + .isEqualTo("GEOMETRYCOLLECTION (LINESTRING (1 1, 2 1), MULTIPOLYGON (((0 1, 1 1, 1 2, 0 2, 0 1)), ((2 1, 3 1, 3 3, 1 3, 1 2, 2 2, 2 1))))"); + + assertThat(assertions.function("ST_AsText", "ST_Intersection(ST_GeometryFromText('POLYGON ((1 1, 1 4, 4 4, 4 1))'), ST_GeometryFromText('LINESTRING (2 0, 2 3)'))")) + .hasType(VARCHAR) + .isEqualTo("LINESTRING (2 1, 2 3)"); + + assertThat(assertions.function("ST_AsText", "ST_Intersection(ST_GeometryFromText('POLYGON ((0 0, 0 1, 1 1, 1 0, 0 0))'), ST_GeometryFromText('LINESTRING (0 0, 1 -1, 1 2)'))")) + .hasType(VARCHAR) + .isEqualTo("GEOMETRYCOLLECTION (POINT (0 0), LINESTRING (1 0, 1 1))"); // test intersection of envelopes assertEnvelopeIntersection("POLYGON ((0 0, 5 0, 5 5, 0 5, 0 0))", "POLYGON ((0 0, 5 0, 5 5, 0 5, 0 0))", "POLYGON ((0 0, 5 0, 5 5, 0 5, 0 0))"); @@ -841,174 +1268,380 @@ public void testSTIntersection() private void assertEnvelopeIntersection(String envelope, String otherEnvelope, String intersection) { - assertFunction("ST_AsText(ST_Intersection(ST_Envelope(ST_GeometryFromText('" + envelope + "')), ST_Envelope(ST_GeometryFromText('" + otherEnvelope + "'))))", VARCHAR, intersection); + assertThat(assertions.expression("ST_AsText(ST_Intersection(ST_Envelope(a), ST_Envelope(b)))") + .binding("a", "ST_GeometryFromText('%s')".formatted(envelope)) + .binding("b", "ST_GeometryFromText('%s')".formatted(otherEnvelope))) + .hasType(VARCHAR) + .isEqualTo(intersection); } @Test public void testSTSymmetricDifference() { - assertFunction("ST_AsText(ST_SymDifference(ST_GeometryFromText('POINT (50 100)'), ST_GeometryFromText('POINT (50 150)')))", VARCHAR, "MULTIPOINT ((50 100), (50 150))"); - assertFunction("ST_AsText(ST_SymDifference(ST_GeometryFromText('MULTIPOINT (50 100, 60 200)'), ST_GeometryFromText('MULTIPOINT (60 200, 70 150)')))", VARCHAR, "MULTIPOINT ((50 100), (70 150))"); - assertFunction("ST_AsText(ST_SymDifference(ST_GeometryFromText('LINESTRING (50 100, 50 200)'), ST_GeometryFromText('LINESTRING (50 50, 50 150)')))", VARCHAR, "MULTILINESTRING ((50 50, 50 100), (50 150, 50 200))"); - assertFunction("ST_AsText(ST_SymDifference(ST_GeometryFromText('MULTILINESTRING ((1 1, 5 1), (2 4, 4 4))'), ST_GeometryFromText('MULTILINESTRING ((3 4, 6 4), (5 0, 5 4))')))", VARCHAR, "MULTILINESTRING ((5 0, 5 1), (1 1, 5 1), (5 1, 5 4), (2 4, 3 4), (4 4, 5 4), (5 4, 6 4))"); - assertFunction("ST_AsText(ST_SymDifference(ST_GeometryFromText('POLYGON ((1 1, 1 4, 4 4, 4 1))'), ST_GeometryFromText('POLYGON ((2 2, 2 5, 5 5, 5 2))')))", VARCHAR, "MULTIPOLYGON (((1 1, 4 1, 4 2, 2 2, 2 4, 1 4, 1 1)), ((4 2, 5 2, 5 5, 2 5, 2 4, 4 4, 4 2)))"); - assertFunction("ST_AsText(ST_SymDifference(ST_GeometryFromText('MULTIPOLYGON (((0 0 , 0 2, 2 2, 2 0)), ((2 2, 2 4, 4 4, 4 2)))'), ST_GeometryFromText('POLYGON ((0 0, 0 3, 3 3, 3 0))')))", VARCHAR, "MULTIPOLYGON (((2 0, 3 0, 3 2, 2 2, 2 0)), ((0 2, 2 2, 2 3, 0 3, 0 2)), ((3 2, 4 2, 4 4, 2 4, 2 3, 3 3, 3 2)))"); + assertThat(assertions.function("ST_AsText", "ST_SymDifference(ST_GeometryFromText('POINT (50 100)'), ST_GeometryFromText('POINT (50 150)'))")) + .hasType(VARCHAR) + .isEqualTo("MULTIPOINT ((50 100), (50 150))"); + + assertThat(assertions.function("ST_AsText", "ST_SymDifference(ST_GeometryFromText('MULTIPOINT (50 100, 60 200)'), ST_GeometryFromText('MULTIPOINT (60 200, 70 150)'))")) + .hasType(VARCHAR) + .isEqualTo("MULTIPOINT ((50 100), (70 150))"); + + assertThat(assertions.function("ST_AsText", "ST_SymDifference(ST_GeometryFromText('LINESTRING (50 100, 50 200)'), ST_GeometryFromText('LINESTRING (50 50, 50 150)'))")) + .hasType(VARCHAR) + .isEqualTo("MULTILINESTRING ((50 50, 50 100), (50 150, 50 200))"); + + assertThat(assertions.function("ST_AsText", "ST_SymDifference(ST_GeometryFromText('MULTILINESTRING ((1 1, 5 1), (2 4, 4 4))'), ST_GeometryFromText('MULTILINESTRING ((3 4, 6 4), (5 0, 5 4))'))")) + .hasType(VARCHAR) + .isEqualTo("MULTILINESTRING ((5 0, 5 1), (1 1, 5 1), (5 1, 5 4), (2 4, 3 4), (4 4, 5 4), (5 4, 6 4))"); + + assertThat(assertions.function("ST_AsText", "ST_SymDifference(ST_GeometryFromText('POLYGON ((1 1, 1 4, 4 4, 4 1))'), ST_GeometryFromText('POLYGON ((2 2, 2 5, 5 5, 5 2))'))")) + .hasType(VARCHAR) + .isEqualTo("MULTIPOLYGON (((1 1, 4 1, 4 2, 2 2, 2 4, 1 4, 1 1)), ((4 2, 5 2, 5 5, 2 5, 2 4, 4 4, 4 2)))"); + + assertThat(assertions.function("ST_AsText", "ST_SymDifference(ST_GeometryFromText('MULTIPOLYGON (((0 0 , 0 2, 2 2, 2 0)), ((2 2, 2 4, 4 4, 4 2)))'), ST_GeometryFromText('POLYGON ((0 0, 0 3, 3 3, 3 0))'))")) + .hasType(VARCHAR) + .isEqualTo("MULTIPOLYGON (((2 0, 3 0, 3 2, 2 2, 2 0)), ((0 2, 2 2, 2 3, 0 3, 0 2)), ((3 2, 4 2, 4 4, 2 4, 2 3, 3 3, 3 2)))"); } @Test public void testStContains() { - assertFunction("ST_Contains(ST_GeometryFromText(null), ST_GeometryFromText('POINT (25 25)'))", BOOLEAN, null); - assertFunction("ST_Contains(ST_GeometryFromText('POINT (20 20)'), ST_GeometryFromText('POINT (25 25)'))", BOOLEAN, false); - assertFunction("ST_Contains(ST_GeometryFromText('MULTIPOINT (20 20, 25 25)'), ST_GeometryFromText('POINT (25 25)'))", BOOLEAN, true); - assertFunction("ST_Contains(ST_GeometryFromText('LINESTRING (20 20, 30 30)'), ST_GeometryFromText('POINT (25 25)'))", BOOLEAN, true); - assertFunction("ST_Contains(ST_GeometryFromText('LINESTRING (20 20, 30 30)'), ST_GeometryFromText('MULTIPOINT (25 25, 31 31)'))", BOOLEAN, false); - assertFunction("ST_Contains(ST_GeometryFromText('LINESTRING (20 20, 30 30)'), ST_GeometryFromText('LINESTRING (25 25, 27 27)'))", BOOLEAN, true); - assertFunction("ST_Contains(ST_GeometryFromText('MULTILINESTRING ((1 1, 5 1), (2 4, 4 4))'), ST_GeometryFromText('MULTILINESTRING ((3 4, 4 4), (2 1, 6 1))'))", BOOLEAN, false); - assertFunction("ST_Contains(ST_GeometryFromText('POLYGON ((0 0, 0 4, 4 4, 4 0))'), ST_GeometryFromText('POLYGON ((1 1, 1 2, 2 2, 2 1))'))", BOOLEAN, true); - assertFunction("ST_Contains(ST_GeometryFromText('POLYGON ((0 0, 0 4, 4 4, 4 0))'), ST_GeometryFromText('POLYGON ((-1 -1, -1 2, 2 2, 2 -1))'))", BOOLEAN, false); - assertFunction("ST_Contains(ST_GeometryFromText('MULTIPOLYGON (((0 0 , 0 2, 2 2, 2 0)), ((2 2, 2 4, 4 4, 4 2)))'), ST_GeometryFromText('POLYGON ((2 2, 2 3, 3 3, 3 2))'))", BOOLEAN, true); - assertFunction("ST_Contains(ST_GeometryFromText('LINESTRING (20 20, 30 30)'), ST_GeometryFromText('POLYGON ((0 0, 0 4, 4 4, 4 0))'))", BOOLEAN, false); - assertFunction("ST_Contains(ST_GeometryFromText('LINESTRING EMPTY'), ST_GeometryFromText('POLYGON ((0 0, 0 4, 4 4, 4 0))'))", BOOLEAN, false); - assertFunction("ST_Contains(ST_GeometryFromText('LINESTRING (20 20, 30 30)'), ST_GeometryFromText('POLYGON EMPTY'))", BOOLEAN, false); + assertThat(assertions.function("ST_Contains", "ST_GeometryFromText(null)", "ST_GeometryFromText('POINT (25 25)')")) + .isNull(BOOLEAN); + + assertThat(assertions.function("ST_Contains", "ST_GeometryFromText('POINT (20 20)')", "ST_GeometryFromText('POINT (25 25)')")) + .isEqualTo(false); + + assertThat(assertions.function("ST_Contains", "ST_GeometryFromText('MULTIPOINT (20 20, 25 25)')", "ST_GeometryFromText('POINT (25 25)')")) + .isEqualTo(true); + + assertThat(assertions.function("ST_Contains", "ST_GeometryFromText('LINESTRING (20 20, 30 30)')", "ST_GeometryFromText('POINT (25 25)')")) + .isEqualTo(true); + + assertThat(assertions.function("ST_Contains", "ST_GeometryFromText('LINESTRING (20 20, 30 30)')", "ST_GeometryFromText('MULTIPOINT (25 25, 31 31)')")) + .isEqualTo(false); + + assertThat(assertions.function("ST_Contains", "ST_GeometryFromText('LINESTRING (20 20, 30 30)')", "ST_GeometryFromText('LINESTRING (25 25, 27 27)')")) + .isEqualTo(true); + + assertThat(assertions.function("ST_Contains", "ST_GeometryFromText('MULTILINESTRING ((1 1, 5 1), (2 4, 4 4))')", "ST_GeometryFromText('MULTILINESTRING ((3 4, 4 4), (2 1, 6 1))')")) + .isEqualTo(false); + + assertThat(assertions.function("ST_Contains", "ST_GeometryFromText('POLYGON ((0 0, 0 4, 4 4, 4 0))')", "ST_GeometryFromText('POLYGON ((1 1, 1 2, 2 2, 2 1))')")) + .isEqualTo(true); + + assertThat(assertions.function("ST_Contains", "ST_GeometryFromText('POLYGON ((0 0, 0 4, 4 4, 4 0))')", "ST_GeometryFromText('POLYGON ((-1 -1, -1 2, 2 2, 2 -1))')")) + .isEqualTo(false); + + assertThat(assertions.function("ST_Contains", "ST_GeometryFromText('MULTIPOLYGON (((0 0 , 0 2, 2 2, 2 0)), ((2 2, 2 4, 4 4, 4 2)))')", "ST_GeometryFromText('POLYGON ((2 2, 2 3, 3 3, 3 2))')")) + .isEqualTo(true); + + assertThat(assertions.function("ST_Contains", "ST_GeometryFromText('LINESTRING (20 20, 30 30)')", "ST_GeometryFromText('POLYGON ((0 0, 0 4, 4 4, 4 0))')")) + .isEqualTo(false); + + assertThat(assertions.function("ST_Contains", "ST_GeometryFromText('LINESTRING EMPTY')", "ST_GeometryFromText('POLYGON ((0 0, 0 4, 4 4, 4 0))')")) + .isEqualTo(false); + + assertThat(assertions.function("ST_Contains", "ST_GeometryFromText('LINESTRING (20 20, 30 30)')", "ST_GeometryFromText('POLYGON EMPTY')")) + .isEqualTo(false); } @Test public void testSTCrosses() { - assertFunction("ST_Crosses(ST_GeometryFromText('POINT (20 20)'), ST_GeometryFromText('POINT (25 25)'))", BOOLEAN, false); - assertFunction("ST_Crosses(ST_GeometryFromText('LINESTRING (20 20, 30 30)'), ST_GeometryFromText('POINT (25 25)'))", BOOLEAN, false); - assertFunction("ST_Crosses(ST_GeometryFromText('LINESTRING (20 20, 30 30)'), ST_GeometryFromText('MULTIPOINT (25 25, 31 31)'))", BOOLEAN, true); - assertFunction("ST_Crosses(ST_GeometryFromText('LINESTRING(0 0, 1 1)'), ST_GeometryFromText('LINESTRING (1 0, 0 1)'))", BOOLEAN, true); - assertFunction("ST_Crosses(ST_GeometryFromText('POLYGON ((1 1, 1 4, 4 4, 4 1))'), ST_GeometryFromText('POLYGON ((2 2, 2 5, 5 5, 5 2))'))", BOOLEAN, false); - assertFunction("ST_Crosses(ST_GeometryFromText('MULTIPOLYGON (((0 0 , 0 2, 2 2, 2 0)), ((2 2, 2 4, 4 4, 4 2)))'), ST_GeometryFromText('POLYGON ((2 2, 2 3, 3 3, 3 2))'))", BOOLEAN, false); - assertFunction("ST_Crosses(ST_GeometryFromText('LINESTRING (-2 -2, 6 6)'), ST_GeometryFromText('POLYGON ((0 0, 0 4, 4 4, 4 0))'))", BOOLEAN, true); - assertFunction("ST_Crosses(ST_GeometryFromText('POINT (20 20)'), ST_GeometryFromText('POINT (20 20)'))", BOOLEAN, false); - assertFunction("ST_Crosses(ST_GeometryFromText('POLYGON ((0 0, 0 4, 4 4, 4 0))'), ST_GeometryFromText('POLYGON ((0 0, 0 4, 4 4, 4 0))'))", BOOLEAN, false); - assertFunction("ST_Crosses(ST_GeometryFromText('POLYGON ((0 0, 0 4, 4 4, 4 0))'), ST_GeometryFromText('LINESTRING (0 0, 0 4, 4 4, 4 0)'))", BOOLEAN, false); + assertThat(assertions.function("ST_Crosses", "ST_GeometryFromText('POINT (20 20)')", "ST_GeometryFromText('POINT (25 25)')")) + .isEqualTo(false); + + assertThat(assertions.function("ST_Crosses", "ST_GeometryFromText('LINESTRING (20 20, 30 30)')", "ST_GeometryFromText('POINT (25 25)')")) + .isEqualTo(false); + + assertThat(assertions.function("ST_Crosses", "ST_GeometryFromText('LINESTRING (20 20, 30 30)')", "ST_GeometryFromText('MULTIPOINT (25 25, 31 31)')")) + .isEqualTo(true); + + assertThat(assertions.function("ST_Crosses", "ST_GeometryFromText('LINESTRING(0 0, 1 1)')", "ST_GeometryFromText('LINESTRING (1 0, 0 1)')")) + .isEqualTo(true); + + assertThat(assertions.function("ST_Crosses", "ST_GeometryFromText('POLYGON ((1 1, 1 4, 4 4, 4 1))')", "ST_GeometryFromText('POLYGON ((2 2, 2 5, 5 5, 5 2))')")) + .isEqualTo(false); + + assertThat(assertions.function("ST_Crosses", "ST_GeometryFromText('MULTIPOLYGON (((0 0 , 0 2, 2 2, 2 0)), ((2 2, 2 4, 4 4, 4 2)))')", "ST_GeometryFromText('POLYGON ((2 2, 2 3, 3 3, 3 2))')")) + .isEqualTo(false); + + assertThat(assertions.function("ST_Crosses", "ST_GeometryFromText('LINESTRING (-2 -2, 6 6)')", "ST_GeometryFromText('POLYGON ((0 0, 0 4, 4 4, 4 0))')")) + .isEqualTo(true); + + assertThat(assertions.function("ST_Crosses", "ST_GeometryFromText('POINT (20 20)')", "ST_GeometryFromText('POINT (20 20)')")) + .isEqualTo(false); + + assertThat(assertions.function("ST_Crosses", "ST_GeometryFromText('POLYGON ((0 0, 0 4, 4 4, 4 0))')", "ST_GeometryFromText('POLYGON ((0 0, 0 4, 4 4, 4 0))')")) + .isEqualTo(false); + + assertThat(assertions.function("ST_Crosses", "ST_GeometryFromText('POLYGON ((0 0, 0 4, 4 4, 4 0))')", "ST_GeometryFromText('LINESTRING (0 0, 0 4, 4 4, 4 0)')")) + .isEqualTo(false); } @Test public void testSTDisjoint() { - assertFunction("ST_Disjoint(ST_GeometryFromText('POINT (50 100)'), ST_GeometryFromText('POINT (150 150)'))", BOOLEAN, true); - assertFunction("ST_Disjoint(ST_GeometryFromText('MULTIPOINT (50 100, 50 200)'), ST_GeometryFromText('POINT (50 100)'))", BOOLEAN, false); - assertFunction("ST_Disjoint(ST_GeometryFromText('LINESTRING (0 0, 0 1)'), ST_GeometryFromText('LINESTRING (1 1, 1 0)'))", BOOLEAN, true); - assertFunction("ST_Disjoint(ST_GeometryFromText('LINESTRING (2 1, 1 2)'), ST_GeometryFromText('LINESTRING (3 1, 1 3)'))", BOOLEAN, true); - assertFunction("ST_Disjoint(ST_GeometryFromText('LINESTRING (1 1, 3 3)'), ST_GeometryFromText('LINESTRING (3 1, 1 3)'))", BOOLEAN, false); - assertFunction("ST_Disjoint(ST_GeometryFromText('LINESTRING (50 100, 50 200)'), ST_GeometryFromText('LINESTRING (20 150, 100 150)'))", BOOLEAN, false); - assertFunction("ST_Disjoint(ST_GeometryFromText('MULTILINESTRING ((1 1, 5 1), (2 4, 4 4))'), ST_GeometryFromText('MULTILINESTRING ((3 4, 6 4), (5 0, 5 4))'))", BOOLEAN, false); - assertFunction("ST_Disjoint(ST_GeometryFromText('POLYGON ((1 1, 1 3, 3 3, 3 1))'), ST_GeometryFromText('POLYGON ((4 4, 4 5, 5 5, 5 4))'))", BOOLEAN, true); - assertFunction("ST_Disjoint(ST_GeometryFromText('MULTIPOLYGON (((1 1, 1 3, 3 3, 3 1)), ((0 0, 0 2, 2 2, 2 0)))'), ST_GeometryFromText('POLYGON ((0 1, 3 1, 3 3, 0 3))'))", BOOLEAN, false); + assertThat(assertions.function("ST_Disjoint", "ST_GeometryFromText('POINT (50 100)')", "ST_GeometryFromText('POINT (150 150)')")) + .isEqualTo(true); + + assertThat(assertions.function("ST_Disjoint", "ST_GeometryFromText('MULTIPOINT (50 100, 50 200)')", "ST_GeometryFromText('POINT (50 100)')")) + .isEqualTo(false); + + assertThat(assertions.function("ST_Disjoint", "ST_GeometryFromText('LINESTRING (0 0, 0 1)')", "ST_GeometryFromText('LINESTRING (1 1, 1 0)')")) + .isEqualTo(true); + + assertThat(assertions.function("ST_Disjoint", "ST_GeometryFromText('LINESTRING (2 1, 1 2)')", "ST_GeometryFromText('LINESTRING (3 1, 1 3)')")) + .isEqualTo(true); + + assertThat(assertions.function("ST_Disjoint", "ST_GeometryFromText('LINESTRING (1 1, 3 3)')", "ST_GeometryFromText('LINESTRING (3 1, 1 3)')")) + .isEqualTo(false); + + assertThat(assertions.function("ST_Disjoint", "ST_GeometryFromText('LINESTRING (50 100, 50 200)')", "ST_GeometryFromText('LINESTRING (20 150, 100 150)')")) + .isEqualTo(false); + + assertThat(assertions.function("ST_Disjoint", "ST_GeometryFromText('MULTILINESTRING ((1 1, 5 1), (2 4, 4 4))')", "ST_GeometryFromText('MULTILINESTRING ((3 4, 6 4), (5 0, 5 4))')")) + .isEqualTo(false); + + assertThat(assertions.function("ST_Disjoint", "ST_GeometryFromText('POLYGON ((1 1, 1 3, 3 3, 3 1))')", "ST_GeometryFromText('POLYGON ((4 4, 4 5, 5 5, 5 4))')")) + .isEqualTo(true); + + assertThat(assertions.function("ST_Disjoint", "ST_GeometryFromText('MULTIPOLYGON (((1 1, 1 3, 3 3, 3 1)), ((0 0, 0 2, 2 2, 2 0)))')", "ST_GeometryFromText('POLYGON ((0 1, 3 1, 3 3, 0 3))')")) + .isEqualTo(false); } @Test public void testSTEquals() { - assertFunction("ST_Equals(ST_GeometryFromText('POINT (50 100)'), ST_GeometryFromText('POINT (150 150)'))", BOOLEAN, false); - assertFunction("ST_Equals(ST_GeometryFromText('MULTIPOINT (50 100, 50 200)'), ST_GeometryFromText('POINT (50 100)'))", BOOLEAN, false); - assertFunction("ST_Equals(ST_GeometryFromText('LINESTRING (0 0, 0 1)'), ST_GeometryFromText('LINESTRING (1 1, 1 0)'))", BOOLEAN, false); - assertFunction("ST_Equals(ST_GeometryFromText('LINESTRING (0 0, 2 2)'), ST_GeometryFromText('LINESTRING (0 0, 2 2)'))", BOOLEAN, true); - assertFunction("ST_Equals(ST_GeometryFromText('MULTILINESTRING ((1 1, 5 1), (2 4, 4 4))'), ST_GeometryFromText('MULTILINESTRING ((3 4, 6 4), (5 0, 5 4))'))", BOOLEAN, false); - assertFunction("ST_Equals(ST_GeometryFromText('POLYGON ((1 1, 1 3, 3 3, 3 1))'), ST_GeometryFromText('POLYGON ((3 3, 3 1, 1 1, 1 3))'))", BOOLEAN, true); - assertFunction("ST_Equals(ST_GeometryFromText('MULTIPOLYGON (((1 1, 1 3, 3 3, 3 1)), ((0 0, 0 2, 2 2, 2 0)))'), ST_GeometryFromText('POLYGON ((0 1, 3 1, 3 3, 0 3))'))", BOOLEAN, false); + assertThat(assertions.function("ST_Equals", "ST_GeometryFromText('POINT (50 100)')", "ST_GeometryFromText('POINT (150 150)')")) + .isEqualTo(false); + + assertThat(assertions.function("ST_Equals", "ST_GeometryFromText('MULTIPOINT (50 100, 50 200)')", "ST_GeometryFromText('POINT (50 100)')")) + .isEqualTo(false); + + assertThat(assertions.function("ST_Equals", "ST_GeometryFromText('LINESTRING (0 0, 0 1)')", "ST_GeometryFromText('LINESTRING (1 1, 1 0)')")) + .isEqualTo(false); + + assertThat(assertions.function("ST_Equals", "ST_GeometryFromText('LINESTRING (0 0, 2 2)')", "ST_GeometryFromText('LINESTRING (0 0, 2 2)')")) + .isEqualTo(true); + + assertThat(assertions.function("ST_Equals", "ST_GeometryFromText('MULTILINESTRING ((1 1, 5 1), (2 4, 4 4))')", "ST_GeometryFromText('MULTILINESTRING ((3 4, 6 4), (5 0, 5 4))')")) + .isEqualTo(false); + + assertThat(assertions.function("ST_Equals", "ST_GeometryFromText('POLYGON ((1 1, 1 3, 3 3, 3 1))')", "ST_GeometryFromText('POLYGON ((3 3, 3 1, 1 1, 1 3))')")) + .isEqualTo(true); + + assertThat(assertions.function("ST_Equals", "ST_GeometryFromText('MULTIPOLYGON (((1 1, 1 3, 3 3, 3 1)), ((0 0, 0 2, 2 2, 2 0)))')", "ST_GeometryFromText('POLYGON ((0 1, 3 1, 3 3, 0 3))')")) + .isEqualTo(false); } @Test public void testSTIntersects() { - assertFunction("ST_Intersects(ST_GeometryFromText('POINT (50 100)'), ST_GeometryFromText('POINT (150 150)'))", BOOLEAN, false); - assertFunction("ST_Intersects(ST_GeometryFromText('MULTIPOINT (50 100, 50 200)'), ST_GeometryFromText('POINT (50 100)'))", BOOLEAN, true); - assertFunction("ST_Intersects(ST_GeometryFromText('LINESTRING (0 0, 0 1)'), ST_GeometryFromText('LINESTRING (1 1, 1 0)'))", BOOLEAN, false); - assertFunction("ST_Intersects(ST_GeometryFromText('LINESTRING (50 100, 50 200)'), ST_GeometryFromText('LINESTRING (20 150, 100 150)'))", BOOLEAN, true); - assertFunction("ST_Intersects(ST_GeometryFromText('MULTILINESTRING ((1 1, 5 1), (2 4, 4 4))'), ST_GeometryFromText('MULTILINESTRING ((3 4, 6 4), (5 0, 5 4))'))", BOOLEAN, true); - assertFunction("ST_Intersects(ST_GeometryFromText('POLYGON ((1 1, 1 3, 3 3, 3 1))'), ST_GeometryFromText('POLYGON ((4 4, 4 5, 5 5, 5 4))'))", BOOLEAN, false); - assertFunction("ST_Intersects(ST_GeometryFromText('MULTIPOLYGON (((1 1, 1 3, 3 3, 3 1)), ((0 0, 0 2, 2 2, 2 0)))'), ST_GeometryFromText('POLYGON ((0 1, 3 1, 3 3, 0 3))'))", BOOLEAN, true); - assertFunction("ST_Intersects(ST_GeometryFromText('POLYGON ((16.5 54, 16.5 54.1, 16.51 54.1, 16.8 54))'), ST_GeometryFromText('LINESTRING (16.6 53, 16.6 56)'))", BOOLEAN, true); - assertFunction("ST_Intersects(ST_GeometryFromText('POLYGON ((16.5 54, 16.5 54.1, 16.51 54.1, 16.8 54))'), ST_GeometryFromText('LINESTRING (16.6667 54.05, 16.8667 54.05)'))", BOOLEAN, false); - assertFunction("ST_Intersects(ST_GeometryFromText('POLYGON ((16.5 54, 16.5 54.1, 16.51 54.1, 16.8 54))'), ST_GeometryFromText('LINESTRING (16.6667 54.25, 16.8667 54.25)'))", BOOLEAN, false); + assertThat(assertions.function("ST_Intersects", "ST_GeometryFromText('POINT (50 100)')", "ST_GeometryFromText('POINT (150 150)')")) + .isEqualTo(false); + + assertThat(assertions.function("ST_Intersects", "ST_GeometryFromText('MULTIPOINT (50 100, 50 200)')", "ST_GeometryFromText('POINT (50 100)')")) + .isEqualTo(true); + + assertThat(assertions.function("ST_Intersects", "ST_GeometryFromText('LINESTRING (0 0, 0 1)')", "ST_GeometryFromText('LINESTRING (1 1, 1 0)')")) + .isEqualTo(false); + + assertThat(assertions.function("ST_Intersects", "ST_GeometryFromText('LINESTRING (50 100, 50 200)')", "ST_GeometryFromText('LINESTRING (20 150, 100 150)')")) + .isEqualTo(true); + + assertThat(assertions.function("ST_Intersects", "ST_GeometryFromText('MULTILINESTRING ((1 1, 5 1), (2 4, 4 4))')", "ST_GeometryFromText('MULTILINESTRING ((3 4, 6 4), (5 0, 5 4))')")) + .isEqualTo(true); + + assertThat(assertions.function("ST_Intersects", "ST_GeometryFromText('POLYGON ((1 1, 1 3, 3 3, 3 1))')", "ST_GeometryFromText('POLYGON ((4 4, 4 5, 5 5, 5 4))')")) + .isEqualTo(false); + + assertThat(assertions.function("ST_Intersects", "ST_GeometryFromText('MULTIPOLYGON (((1 1, 1 3, 3 3, 3 1)), ((0 0, 0 2, 2 2, 2 0)))')", "ST_GeometryFromText('POLYGON ((0 1, 3 1, 3 3, 0 3))')")) + .isEqualTo(true); + + assertThat(assertions.function("ST_Intersects", "ST_GeometryFromText('POLYGON ((16.5 54, 16.5 54.1, 16.51 54.1, 16.8 54))')", "ST_GeometryFromText('LINESTRING (16.6 53, 16.6 56)')")) + .isEqualTo(true); + + assertThat(assertions.function("ST_Intersects", "ST_GeometryFromText('POLYGON ((16.5 54, 16.5 54.1, 16.51 54.1, 16.8 54))')", "ST_GeometryFromText('LINESTRING (16.6667 54.05, 16.8667 54.05)')")) + .isEqualTo(false); + + assertThat(assertions.function("ST_Intersects", "ST_GeometryFromText('POLYGON ((16.5 54, 16.5 54.1, 16.51 54.1, 16.8 54))')", "ST_GeometryFromText('LINESTRING (16.6667 54.25, 16.8667 54.25)')")) + .isEqualTo(false); } @Test public void testSTOverlaps() { - assertFunction("ST_Overlaps(ST_GeometryFromText('POINT (50 100)'), ST_GeometryFromText('POINT (150 150)'))", BOOLEAN, false); - assertFunction("ST_Overlaps(ST_GeometryFromText('POINT (50 100)'), ST_GeometryFromText('POINT (50 100)'))", BOOLEAN, false); - assertFunction("ST_Overlaps(ST_GeometryFromText('MULTIPOINT (50 100, 50 200)'), ST_GeometryFromText('POINT (50 100)'))", BOOLEAN, false); - assertFunction("ST_Overlaps(ST_GeometryFromText('LINESTRING (0 0, 0 1)'), ST_GeometryFromText('LINESTRING (1 1, 1 0)'))", BOOLEAN, false); - assertFunction("ST_Overlaps(ST_GeometryFromText('MULTILINESTRING ((1 1, 5 1), (2 4, 4 4))'), ST_GeometryFromText('MULTILINESTRING ((3 4, 6 4), (5 0, 5 4))'))", BOOLEAN, true); - assertFunction("ST_Overlaps(ST_GeometryFromText('POLYGON ((1 1, 1 4, 4 4, 4 1))'), ST_GeometryFromText('POLYGON ((3 3, 3 5, 5 5, 5 3))'))", BOOLEAN, true); - assertFunction("ST_Overlaps(ST_GeometryFromText('POLYGON ((1 1, 1 4, 4 4, 4 1))'), ST_GeometryFromText('POLYGON ((1 1, 1 4, 4 4, 4 1))'))", BOOLEAN, false); - assertFunction("ST_Overlaps(ST_GeometryFromText('POLYGON ((1 1, 1 4, 4 4, 4 1))'), ST_GeometryFromText('LINESTRING (1 1, 4 4)'))", BOOLEAN, false); - assertFunction("ST_Overlaps(ST_GeometryFromText('POLYGON ((1 1, 1 3, 3 3, 3 1))'), ST_GeometryFromText('POLYGON ((4 4, 4 5, 5 5, 5 4))'))", BOOLEAN, false); - assertFunction("ST_Overlaps(ST_GeometryFromText('MULTIPOLYGON (((1 1, 1 3, 3 3, 3 1)), ((0 0, 0 2, 2 2, 2 0)))'), ST_GeometryFromText('POLYGON ((0 1, 3 1, 3 3, 0 3))'))", BOOLEAN, true); + assertThat(assertions.function("ST_Overlaps", "ST_GeometryFromText('POINT (50 100)')", "ST_GeometryFromText('POINT (150 150)')")) + .isEqualTo(false); + + assertThat(assertions.function("ST_Overlaps", "ST_GeometryFromText('POINT (50 100)')", "ST_GeometryFromText('POINT (50 100)')")) + .isEqualTo(false); + + assertThat(assertions.function("ST_Overlaps", "ST_GeometryFromText('MULTIPOINT (50 100, 50 200)')", "ST_GeometryFromText('POINT (50 100)')")) + .isEqualTo(false); + + assertThat(assertions.function("ST_Overlaps", "ST_GeometryFromText('LINESTRING (0 0, 0 1)')", "ST_GeometryFromText('LINESTRING (1 1, 1 0)')")) + .isEqualTo(false); + + assertThat(assertions.function("ST_Overlaps", "ST_GeometryFromText('MULTILINESTRING ((1 1, 5 1), (2 4, 4 4))')", "ST_GeometryFromText('MULTILINESTRING ((3 4, 6 4), (5 0, 5 4))')")) + .isEqualTo(true); + + assertThat(assertions.function("ST_Overlaps", "ST_GeometryFromText('POLYGON ((1 1, 1 4, 4 4, 4 1))')", "ST_GeometryFromText('POLYGON ((3 3, 3 5, 5 5, 5 3))')")) + .isEqualTo(true); + + assertThat(assertions.function("ST_Overlaps", "ST_GeometryFromText('POLYGON ((1 1, 1 4, 4 4, 4 1))')", "ST_GeometryFromText('POLYGON ((1 1, 1 4, 4 4, 4 1))')")) + .isEqualTo(false); + + assertThat(assertions.function("ST_Overlaps", "ST_GeometryFromText('POLYGON ((1 1, 1 4, 4 4, 4 1))')", "ST_GeometryFromText('LINESTRING (1 1, 4 4)')")) + .isEqualTo(false); + + assertThat(assertions.function("ST_Overlaps", "ST_GeometryFromText('POLYGON ((1 1, 1 3, 3 3, 3 1))')", "ST_GeometryFromText('POLYGON ((4 4, 4 5, 5 5, 5 4))')")) + .isEqualTo(false); + + assertThat(assertions.function("ST_Overlaps", "ST_GeometryFromText('MULTIPOLYGON (((1 1, 1 3, 3 3, 3 1)), ((0 0, 0 2, 2 2, 2 0)))')", "ST_GeometryFromText('POLYGON ((0 1, 3 1, 3 3, 0 3))')")) + .isEqualTo(true); } @Test public void testSTRelate() { - assertFunction("ST_Relate(ST_GeometryFromText('LINESTRING (0 0, 3 3)'), ST_GeometryFromText('LINESTRING (1 1, 4 1)'), '****T****')", BOOLEAN, false); - assertFunction("ST_Relate(ST_GeometryFromText('POLYGON ((2 0, 2 1, 3 1))'), ST_GeometryFromText('POLYGON ((1 1, 1 4, 4 4, 4 1))'), '****T****')", BOOLEAN, true); - assertFunction("ST_Relate(ST_GeometryFromText('POLYGON ((2 0, 2 1, 3 1))'), ST_GeometryFromText('POLYGON ((1 1, 1 4, 4 4, 4 1))'), 'T********')", BOOLEAN, false); + assertThat(assertions.function("ST_Relate", "ST_GeometryFromText('LINESTRING (0 0, 3 3)')", "ST_GeometryFromText('LINESTRING (1 1, 4 1)')", "'****T****'")) + .isEqualTo(false); + + assertThat(assertions.function("ST_Relate", "ST_GeometryFromText('POLYGON ((2 0, 2 1, 3 1))')", "ST_GeometryFromText('POLYGON ((1 1, 1 4, 4 4, 4 1))')", "'****T****'")) + .isEqualTo(true); + + assertThat(assertions.function("ST_Relate", "ST_GeometryFromText('POLYGON ((2 0, 2 1, 3 1))')", "ST_GeometryFromText('POLYGON ((1 1, 1 4, 4 4, 4 1))')", "'T********'")) + .isEqualTo(false); } @Test public void testSTTouches() { - assertFunction("ST_Touches(ST_GeometryFromText('POINT (50 100)'), ST_GeometryFromText('POINT (150 150)'))", BOOLEAN, false); - assertFunction("ST_Touches(ST_GeometryFromText('MULTIPOINT (50 100, 50 200)'), ST_GeometryFromText('POINT (50 100)'))", BOOLEAN, false); - assertFunction("ST_Touches(ST_GeometryFromText('LINESTRING (50 100, 50 200)'), ST_GeometryFromText('LINESTRING (20 150, 100 150)'))", BOOLEAN, false); - assertFunction("ST_Touches(ST_GeometryFromText('MULTILINESTRING ((1 1, 5 1), (2 4, 4 4))'), ST_GeometryFromText('MULTILINESTRING ((3 4, 6 4), (5 0, 5 4))'))", BOOLEAN, false); - assertFunction("ST_Touches(ST_GeometryFromText('POINT (1 2)'), ST_GeometryFromText('POLYGON ((1 1, 1 4, 4 4, 4 1))'))", BOOLEAN, true); - assertFunction("ST_Touches(ST_GeometryFromText('POLYGON ((1 1, 1 3, 3 3, 3 1))'), ST_GeometryFromText('POLYGON ((4 4, 4 5, 5 5, 5 4))'))", BOOLEAN, false); - assertFunction("ST_Touches(ST_GeometryFromText('POLYGON ((1 1, 1 3, 3 3, 3 1))'), ST_GeometryFromText('LINESTRING (0 0, 1 1)'))", BOOLEAN, true); - assertFunction("ST_Touches(ST_GeometryFromText('POLYGON ((1 1, 1 3, 3 3, 3 1))'), ST_GeometryFromText('POLYGON ((3 3, 3 5, 5 5, 5 3))'))", BOOLEAN, true); - assertFunction("ST_Touches(ST_GeometryFromText('MULTIPOLYGON (((1 1, 1 3, 3 3, 3 1)), ((0 0, 0 2, 2 2, 2 0)))'), ST_GeometryFromText('POLYGON ((0 1, 3 1, 3 3, 0 3))'))", BOOLEAN, false); + assertThat(assertions.function("ST_Touches", "ST_GeometryFromText('POINT (50 100)')", "ST_GeometryFromText('POINT (150 150)')")) + .isEqualTo(false); + + assertThat(assertions.function("ST_Touches", "ST_GeometryFromText('MULTIPOINT (50 100, 50 200)')", "ST_GeometryFromText('POINT (50 100)')")) + .isEqualTo(false); + + assertThat(assertions.function("ST_Touches", "ST_GeometryFromText('LINESTRING (50 100, 50 200)')", "ST_GeometryFromText('LINESTRING (20 150, 100 150)')")) + .isEqualTo(false); + + assertThat(assertions.function("ST_Touches", "ST_GeometryFromText('MULTILINESTRING ((1 1, 5 1), (2 4, 4 4))')", "ST_GeometryFromText('MULTILINESTRING ((3 4, 6 4), (5 0, 5 4))')")) + .isEqualTo(false); + + assertThat(assertions.function("ST_Touches", "ST_GeometryFromText('POINT (1 2)')", "ST_GeometryFromText('POLYGON ((1 1, 1 4, 4 4, 4 1))')")) + .isEqualTo(true); + + assertThat(assertions.function("ST_Touches", "ST_GeometryFromText('POLYGON ((1 1, 1 3, 3 3, 3 1))')", "ST_GeometryFromText('POLYGON ((4 4, 4 5, 5 5, 5 4))')")) + .isEqualTo(false); + + assertThat(assertions.function("ST_Touches", "ST_GeometryFromText('POLYGON ((1 1, 1 3, 3 3, 3 1))')", "ST_GeometryFromText('LINESTRING (0 0, 1 1)')")) + .isEqualTo(true); + + assertThat(assertions.function("ST_Touches", "ST_GeometryFromText('POLYGON ((1 1, 1 3, 3 3, 3 1))')", "ST_GeometryFromText('POLYGON ((3 3, 3 5, 5 5, 5 3))')")) + .isEqualTo(true); + + assertThat(assertions.function("ST_Touches", "ST_GeometryFromText('MULTIPOLYGON (((1 1, 1 3, 3 3, 3 1)), ((0 0, 0 2, 2 2, 2 0)))')", "ST_GeometryFromText('POLYGON ((0 1, 3 1, 3 3, 0 3))')")) + .isEqualTo(false); } @Test public void testSTWithin() { - assertFunction("ST_Within(ST_GeometryFromText('POINT (50 100)'), ST_GeometryFromText('POINT (150 150)'))", BOOLEAN, false); - assertFunction("ST_Within(ST_GeometryFromText('POINT (50 100)'), ST_GeometryFromText('MULTIPOINT (50 100, 50 200)'))", BOOLEAN, true); - assertFunction("ST_Within(ST_GeometryFromText('LINESTRING (50 100, 50 200)'), ST_GeometryFromText('LINESTRING (50 50, 50 250)'))", BOOLEAN, true); - assertFunction("ST_Within(ST_GeometryFromText('MULTILINESTRING ((1 1, 5 1), (2 4, 4 4))'), ST_GeometryFromText('MULTILINESTRING ((3 4, 6 4), (5 0, 5 4))'))", BOOLEAN, false); - assertFunction("ST_Within(ST_GeometryFromText('POINT (3 2)'), ST_GeometryFromText('POLYGON ((1 1, 1 4, 4 4, 4 1))'))", BOOLEAN, true); - assertFunction("ST_Within(ST_GeometryFromText('POLYGON ((1 1, 1 3, 3 3, 3 1))'), ST_GeometryFromText('POLYGON ((0 0, 0 4, 4 4, 4 0))'))", BOOLEAN, true); - assertFunction("ST_Within(ST_GeometryFromText('LINESTRING (1 1, 3 3)'), ST_GeometryFromText('POLYGON ((0 0, 0 4, 4 4, 4 0))'))", BOOLEAN, true); - assertFunction("ST_Within(ST_GeometryFromText('MULTIPOLYGON (((1 1, 1 3, 3 3, 3 1)), ((0 0, 0 2, 2 2, 2 0)))'), ST_GeometryFromText('POLYGON ((0 1, 3 1, 3 3, 0 3))'))", BOOLEAN, false); - assertFunction("ST_Within(ST_GeometryFromText('POLYGON ((1 1, 1 5, 5 5, 5 1))'), ST_GeometryFromText('POLYGON ((0 0, 0 4, 4 4, 4 0))'))", BOOLEAN, false); + assertThat(assertions.function("ST_Within", "ST_GeometryFromText('POINT (50 100)')", "ST_GeometryFromText('POINT (150 150)')")) + .isEqualTo(false); + + assertThat(assertions.function("ST_Within", "ST_GeometryFromText('POINT (50 100)')", "ST_GeometryFromText('MULTIPOINT (50 100, 50 200)')")) + .isEqualTo(true); + + assertThat(assertions.function("ST_Within", "ST_GeometryFromText('LINESTRING (50 100, 50 200)')", "ST_GeometryFromText('LINESTRING (50 50, 50 250)')")) + .isEqualTo(true); + + assertThat(assertions.function("ST_Within", "ST_GeometryFromText('MULTILINESTRING ((1 1, 5 1), (2 4, 4 4))')", "ST_GeometryFromText('MULTILINESTRING ((3 4, 6 4), (5 0, 5 4))')")) + .isEqualTo(false); + + assertThat(assertions.function("ST_Within", "ST_GeometryFromText('POINT (3 2)')", "ST_GeometryFromText('POLYGON ((1 1, 1 4, 4 4, 4 1))')")) + .isEqualTo(true); + + assertThat(assertions.function("ST_Within", "ST_GeometryFromText('POLYGON ((1 1, 1 3, 3 3, 3 1))')", "ST_GeometryFromText('POLYGON ((0 0, 0 4, 4 4, 4 0))')")) + .isEqualTo(true); + + assertThat(assertions.function("ST_Within", "ST_GeometryFromText('LINESTRING (1 1, 3 3)')", "ST_GeometryFromText('POLYGON ((0 0, 0 4, 4 4, 4 0))')")) + .isEqualTo(true); + + assertThat(assertions.function("ST_Within", "ST_GeometryFromText('MULTIPOLYGON (((1 1, 1 3, 3 3, 3 1)), ((0 0, 0 2, 2 2, 2 0)))')", "ST_GeometryFromText('POLYGON ((0 1, 3 1, 3 3, 0 3))')")) + .isEqualTo(false); + + assertThat(assertions.function("ST_Within", "ST_GeometryFromText('POLYGON ((1 1, 1 5, 5 5, 5 1))')", "ST_GeometryFromText('POLYGON ((0 0, 0 4, 4 4, 4 0))')")) + .isEqualTo(false); } @Test public void testInvalidWKT() { - assertInvalidFunction("ST_LineFromText('LINESTRING (0 0, 1)')", "Invalid WKT: LINESTRING (0 0, 1)"); - assertInvalidFunction("ST_GeometryFromText('POLYGON(0 0)')", "Invalid WKT: POLYGON(0 0)"); - assertInvalidFunction("ST_Polygon('POLYGON(-1 1, 1 -1)')", "Invalid WKT: POLYGON(-1 1, 1 -1)"); + assertTrinoExceptionThrownBy(() -> assertions.function("ST_LineFromText", "'LINESTRING (0 0, 1)'").evaluate()) + .hasMessage("Invalid WKT: LINESTRING (0 0, 1)"); + + assertTrinoExceptionThrownBy(() -> assertions.function("ST_GeometryFromText", "'POLYGON(0 0)'").evaluate()) + .hasMessage("Invalid WKT: POLYGON(0 0)"); + + assertTrinoExceptionThrownBy(() -> assertions.function("ST_Polygon", "'POLYGON(-1 1, 1 -1)'").evaluate()) + .hasMessage("Invalid WKT: POLYGON(-1 1, 1 -1)"); } @Test public void testGreatCircleDistance() { - assertFunction("great_circle_distance(36.12, -86.67, 33.94, -118.40)", DOUBLE, 2886.4489734367016); - assertFunction("great_circle_distance(33.94, -118.40, 36.12, -86.67)", DOUBLE, 2886.4489734367016); - assertFunction("great_circle_distance(42.3601, -71.0589, 42.4430, -71.2290)", DOUBLE, 16.73469743457383); - assertFunction("great_circle_distance(36.12, -86.67, 36.12, -86.67)", DOUBLE, 0.0); + assertThat(assertions.function("great_circle_distance", "36.12", "-86.67", "33.94", "-118.40")) + .isEqualTo(2886.4489734367016); + + assertThat(assertions.function("great_circle_distance", "33.94", "-118.40", "36.12", "-86.67")) + .isEqualTo(2886.4489734367016); + + assertThat(assertions.function("great_circle_distance", "42.3601", "-71.0589", "42.4430", "-71.2290")) + .isEqualTo(16.73469743457383); - assertInvalidFunction("great_circle_distance(100, 20, 30, 40)", "Latitude must be between -90 and 90"); - assertInvalidFunction("great_circle_distance(10, 20, 300, 40)", "Latitude must be between -90 and 90"); - assertInvalidFunction("great_circle_distance(10, 200, 30, 40)", "Longitude must be between -180 and 180"); - assertInvalidFunction("great_circle_distance(10, 20, 30, 400)", "Longitude must be between -180 and 180"); + assertThat(assertions.function("great_circle_distance", "36.12", "-86.67", "36.12", "-86.67")) + .isEqualTo(0.0); - assertInvalidFunction("great_circle_distance(nan(), -86.67, 33.94, -118.40)", "Latitude must be between -90 and 90"); - assertInvalidFunction("great_circle_distance(infinity(), -86.67, 33.94, -118.40)", "Latitude must be between -90 and 90"); - assertInvalidFunction("great_circle_distance(36.12, nan(), 33.94, -118.40)", "Longitude must be between -180 and 180"); - assertInvalidFunction("great_circle_distance(36.12, infinity(), 33.94, -118.40)", "Longitude must be between -180 and 180"); - assertInvalidFunction("great_circle_distance(36.12, -86.67, nan(), -118.40)", "Latitude must be between -90 and 90"); - assertInvalidFunction("great_circle_distance(36.12, -86.67, infinity(), -118.40)", "Latitude must be between -90 and 90"); - assertInvalidFunction("great_circle_distance(36.12, -86.67, 33.94, nan())", "Longitude must be between -180 and 180"); - assertInvalidFunction("great_circle_distance(36.12, -86.67, 33.94, infinity())", "Longitude must be between -180 and 180"); + assertTrinoExceptionThrownBy(() -> assertions.function("great_circle_distance", "100", "20", "30", "40").evaluate()) + .hasMessage("Latitude must be between -90 and 90"); + + assertTrinoExceptionThrownBy(() -> assertions.function("great_circle_distance", "10", "20", "300", "40").evaluate()) + .hasMessage("Latitude must be between -90 and 90"); + + assertTrinoExceptionThrownBy(() -> assertions.function("great_circle_distance", "10", "200", "30", "40").evaluate()) + .hasMessage("Longitude must be between -180 and 180"); + + assertTrinoExceptionThrownBy(() -> assertions.function("great_circle_distance", "10", "20", "30", "400").evaluate()) + .hasMessage("Longitude must be between -180 and 180"); + + assertTrinoExceptionThrownBy(() -> assertions.function("great_circle_distance", "nan()", "-86.67", "33.94", "-118.40").evaluate()) + .hasMessage("Latitude must be between -90 and 90"); + + assertTrinoExceptionThrownBy(() -> assertions.function("great_circle_distance", "infinity()", "-86.67", "33.94", "-118.40").evaluate()) + .hasMessage("Latitude must be between -90 and 90"); + + assertTrinoExceptionThrownBy(() -> assertions.function("great_circle_distance", "36.12", "nan()", "33.94", "-118.40").evaluate()) + .hasMessage("Longitude must be between -180 and 180"); + + assertTrinoExceptionThrownBy(() -> assertions.function("great_circle_distance", "36.12", "infinity()", "33.94", "-118.40").evaluate()) + .hasMessage("Longitude must be between -180 and 180"); + + assertTrinoExceptionThrownBy(() -> assertions.function("great_circle_distance", "36.12", "-86.67", "nan()", "-118.40").evaluate()) + .hasMessage("Latitude must be between -90 and 90"); + + assertTrinoExceptionThrownBy(() -> assertions.function("great_circle_distance", "36.12", "-86.67", "infinity()", "-118.40").evaluate()) + .hasMessage("Latitude must be between -90 and 90"); + + assertTrinoExceptionThrownBy(() -> assertions.function("great_circle_distance", "36.12", "-86.67", "33.94", "nan()").evaluate()) + .hasMessage("Longitude must be between -180 and 180"); + + assertTrinoExceptionThrownBy(() -> assertions.function("great_circle_distance", "36.12", "-86.67", "33.94", "infinity()").evaluate()) + .hasMessage("Longitude must be between -180 and 180"); } @Test @@ -1021,7 +1654,9 @@ public void testSTInteriorRings() assertInvalidInteriorRings("MULTIPOLYGON (((1 1, 1 3, 3 3, 3 1)), ((0 0, 0 2, 2 2, 2 0)))", "MULTI_POLYGON"); assertInvalidInteriorRings("GEOMETRYCOLLECTION (POINT (1 1), POINT (2 3), LINESTRING (5 8, 13 21))", "GEOMETRY_COLLECTION"); - assertFunction("ST_InteriorRings(ST_GeometryFromText('POLYGON EMPTY'))", new ArrayType(GEOMETRY), null); + assertThat(assertions.function("ST_InteriorRings", "ST_GeometryFromText('POLYGON EMPTY')")) + .isNull(new ArrayType(GEOMETRY)); + assertInteriorRings("POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))"); assertInteriorRings("POLYGON ((0 0, 0 3, 3 3, 3 0, 0 0), (1 1, 1 2, 2 2, 2 1, 1 1))", "LINESTRING (1 1, 1 2, 2 2, 2 1, 1 1)"); assertInteriorRings("POLYGON ((0 0, 0 5, 5 5, 5 0, 0 0), (1 1, 1 2, 2 2, 2 1, 1 1), (3 3, 3 4, 4 4, 4 3, 3 3))", @@ -1030,12 +1665,18 @@ public void testSTInteriorRings() private void assertInteriorRings(String wkt, String... expected) { - assertFunction(format("transform(ST_InteriorRings(ST_GeometryFromText('%s')), x -> ST_ASText(x))", wkt), new ArrayType(VARCHAR), ImmutableList.copyOf(expected)); + assertThat(assertions.expression("transform(ST_InteriorRings(geometry), x -> ST_AsText(x))") + .binding("geometry", "ST_GeometryFromText('%s')".formatted(wkt))) + .hasType(new ArrayType(VARCHAR)) + .isEqualTo(ImmutableList.copyOf(expected)); } private void assertInvalidInteriorRings(String wkt, String geometryType) { - assertInvalidFunction(format("ST_InteriorRings(ST_GeometryFromText('%s'))", wkt), format("ST_InteriorRings only applies to POLYGON. Input type is: %s", geometryType)); + assertTrinoExceptionThrownBy(() -> assertions.expression("transform(ST_InteriorRings(geometry), x -> ST_AsText(x))") + .binding("geometry", "ST_GeometryFromText('%s')".formatted(wkt)) + .evaluate()) + .hasMessage("ST_InteriorRings only applies to POLYGON. Input type is: %s".formatted(geometryType)); } @Test @@ -1059,7 +1700,8 @@ public void testSTNumGeometries() private void assertSTNumGeometries(String wkt, int expected) { - assertFunction("ST_NumGeometries(ST_GeometryFromText('" + wkt + "'))", INTEGER, expected); + assertThat(assertions.function("ST_NumGeometries", "ST_GeometryFromText('%s')".formatted(wkt))) + .isEqualTo(expected); } @Test @@ -1123,14 +1765,17 @@ public void testSTUnion() private void assertUnion(String leftWkt, String rightWkt, String expectWkt) { - assertFunction(format("ST_ASText(ST_Union(ST_GeometryFromText('%s'), ST_GeometryFromText('%s')))", leftWkt, rightWkt), VARCHAR, expectWkt); - assertFunction(format("ST_ASText(ST_Union(ST_GeometryFromText('%s'), ST_GeometryFromText('%s')))", rightWkt, leftWkt), VARCHAR, expectWkt); - } + assertThat(assertions.expression("ST_AsText(ST_Union(a, b))") + .binding("a", "ST_GeometryFromText('%s')".formatted(leftWkt)) + .binding("b", "ST_GeometryFromText('%s')".formatted(rightWkt))) + .hasType(VARCHAR) + .isEqualTo(expectWkt); - private void assertInvalidGeometryCollectionUnion(String validWkt) - { - assertInvalidFunction(format("ST_Union(ST_GeometryFromText('%s'), ST_GeometryFromText('GEOMETRYCOLLECTION (POINT(2 3))'))", validWkt), "ST_Union only applies to POINT or MULTI_POINT or LINE_STRING or MULTI_LINE_STRING or POLYGON or MULTI_POLYGON. Input type is: GEOMETRY_COLLECTION"); - assertInvalidFunction(format("ST_Union(ST_GeometryFromText('GEOMETRYCOLLECTION (POINT(2 3))'), ST_GeometryFromText('%s'))", validWkt), "ST_Union only applies to POINT or MULTI_POINT or LINE_STRING or MULTI_LINE_STRING or POLYGON or MULTI_POLYGON. Input type is: GEOMETRY_COLLECTION"); + assertThat(assertions.expression("ST_AsText(ST_Union(a, b))") + .binding("a", "ST_GeometryFromText('%s')".formatted(rightWkt)) + .binding("b", "ST_GeometryFromText('%s')".formatted(leftWkt))) + .hasType(VARCHAR) + .isEqualTo(expectWkt); } @Test @@ -1181,44 +1826,81 @@ public void testSTGeometryN() private void assertSTGeometryN(String wkt, int index, String expected) { - assertFunction("ST_ASText(ST_GeometryN(ST_GeometryFromText('" + wkt + "')," + index + "))", VARCHAR, expected); + assertThat(assertions.expression("ST_AsText(ST_GeometryN(geometry, index))") + .binding("geometry", "ST_GeometryFromText('%s')".formatted(wkt)) + .binding("index", Integer.toString(index))) + .hasType(VARCHAR) + .isEqualTo(expected); } @Test public void testSTLineString() { // General case, 2+ points - assertFunction("ST_LineString(array[ST_Point(1,2), ST_Point(3,4)])", GEOMETRY, "LINESTRING (1 2, 3 4)"); - assertFunction("ST_LineString(array[ST_Point(1,2), ST_Point(3,4), ST_Point(5, 6)])", GEOMETRY, "LINESTRING (1 2, 3 4, 5 6)"); - assertFunction("ST_LineString(array[ST_Point(1,2), ST_Point(3,4), ST_Point(5,6), ST_Point(7,8)])", GEOMETRY, "LINESTRING (1 2, 3 4, 5 6, 7 8)"); + assertThat(assertions.function("ST_LineString", "array[ST_Point(1,2), ST_Point(3,4)]")) + .hasType(GEOMETRY) + .isEqualTo("LINESTRING (1 2, 3 4)"); + + assertThat(assertions.function("ST_LineString", "array[ST_Point(1,2), ST_Point(3,4), ST_Point(5, 6)]")) + .hasType(GEOMETRY) + .isEqualTo("LINESTRING (1 2, 3 4, 5 6)"); + + assertThat(assertions.function("ST_LineString", "array[ST_Point(1,2), ST_Point(3,4), ST_Point(5,6), ST_Point(7,8)]")) + .hasType(GEOMETRY) + .isEqualTo("LINESTRING (1 2, 3 4, 5 6, 7 8)"); // Other ways of creating points - assertFunction("ST_LineString(array[ST_GeometryFromText('POINT (1 2)'), ST_GeometryFromText('POINT (3 4)')])", GEOMETRY, "LINESTRING (1 2, 3 4)"); + assertThat(assertions.function("ST_LineString", "array[ST_GeometryFromText('POINT (1 2)'), ST_GeometryFromText('POINT (3 4)')]")) + .hasType(GEOMETRY) + .isEqualTo("LINESTRING (1 2, 3 4)"); // Duplicate consecutive points throws exception - assertInvalidFunction("ST_LineString(array[ST_Point(1, 2), ST_Point(1, 2)])", "Invalid input to ST_LineString: consecutive duplicate points at index 2"); - assertFunction("ST_LineString(array[ST_Point(1, 2), ST_Point(3, 4), ST_Point(1, 2)])", GEOMETRY, "LINESTRING (1 2, 3 4, 1 2)"); + assertTrinoExceptionThrownBy(() -> assertions.function("ST_LineString", "array[ST_Point(1, 2), ST_Point(1, 2)]").evaluate()) + .hasMessage("Invalid input to ST_LineString: consecutive duplicate points at index 2"); + + assertThat(assertions.function("ST_LineString", "array[ST_Point(1, 2), ST_Point(3, 4), ST_Point(1, 2)]")) + .hasType(GEOMETRY) + .isEqualTo("LINESTRING (1 2, 3 4, 1 2)"); // Single point - assertFunction("ST_LineString(array[ST_Point(9,10)])", GEOMETRY, "LINESTRING EMPTY"); + assertThat(assertions.function("ST_LineString", "array[ST_Point(9,10)]")) + .hasType(GEOMETRY) + .isEqualTo("LINESTRING EMPTY"); // Zero points - assertFunction("ST_LineString(array[])", GEOMETRY, "LINESTRING EMPTY"); + assertThat(assertions.function("ST_LineString", "array[]")) + .hasType(GEOMETRY) + .isEqualTo("LINESTRING EMPTY"); // Only points can be passed - assertInvalidFunction("ST_LineString(array[ST_Point(7,8), ST_GeometryFromText('LINESTRING (1 2, 3 4)')])", "ST_LineString takes only an array of valid points, LineString was passed"); + assertTrinoExceptionThrownBy(() -> assertions.function("ST_LineString", "array[ST_Point(7,8), ST_GeometryFromText('LINESTRING (1 2, 3 4)')]").evaluate()) + .hasMessage("ST_LineString takes only an array of valid points, LineString was passed"); // Nulls points are invalid - assertInvalidFunction("ST_LineString(array[NULL])", "Invalid input to ST_LineString: null point at index 1"); - assertInvalidFunction("ST_LineString(array[ST_Point(1,2), NULL])", "Invalid input to ST_LineString: null point at index 2"); - assertInvalidFunction("ST_LineString(array[ST_Point(1, 2), NULL, ST_Point(3, 4)])", "Invalid input to ST_LineString: null point at index 2"); - assertInvalidFunction("ST_LineString(array[ST_Point(1, 2), NULL, ST_Point(3, 4), NULL])", "Invalid input to ST_LineString: null point at index 2"); + assertTrinoExceptionThrownBy(() -> assertions.function("ST_LineString", "array[NULL]").evaluate()) + .hasMessage("Invalid input to ST_LineString: null point at index 1"); + + assertTrinoExceptionThrownBy(() -> assertions.function("ST_LineString", "array[ST_Point(1,2), NULL]").evaluate()) + .hasMessage("Invalid input to ST_LineString: null point at index 2"); + + assertTrinoExceptionThrownBy(() -> assertions.function("ST_LineString", "array[ST_Point(1, 2), NULL, ST_Point(3, 4)]").evaluate()) + .hasMessage("Invalid input to ST_LineString: null point at index 2"); + + assertTrinoExceptionThrownBy(() -> assertions.function("ST_LineString", "array[ST_Point(1, 2), NULL, ST_Point(3, 4), NULL]").evaluate()) + .hasMessage("Invalid input to ST_LineString: null point at index 2"); // Empty points are invalid - assertInvalidFunction("ST_LineString(array[ST_GeometryFromText('POINT EMPTY')])", "Invalid input to ST_LineString: empty point at index 1"); - assertInvalidFunction("ST_LineString(array[ST_Point(1,2), ST_GeometryFromText('POINT EMPTY')])", "Invalid input to ST_LineString: empty point at index 2"); - assertInvalidFunction("ST_LineString(array[ST_Point(1,2), ST_GeometryFromText('POINT EMPTY'), ST_Point(3,4)])", "Invalid input to ST_LineString: empty point at index 2"); - assertInvalidFunction("ST_LineString(array[ST_Point(1,2), ST_GeometryFromText('POINT EMPTY'), ST_Point(3,4), ST_GeometryFromText('POINT EMPTY')])", "Invalid input to ST_LineString: empty point at index 2"); + assertTrinoExceptionThrownBy(() -> assertions.function("ST_LineString", "array[ST_GeometryFromText('POINT EMPTY')]").evaluate()) + .hasMessage("Invalid input to ST_LineString: empty point at index 1"); + + assertTrinoExceptionThrownBy(() -> assertions.function("ST_LineString", "array[ST_Point(1,2), ST_GeometryFromText('POINT EMPTY')]").evaluate()) + .hasMessage("Invalid input to ST_LineString: empty point at index 2"); + + assertTrinoExceptionThrownBy(() -> assertions.function("ST_LineString", "array[ST_Point(1,2), ST_GeometryFromText('POINT EMPTY'), ST_Point(3,4)]").evaluate()) + .hasMessage("Invalid input to ST_LineString: empty point at index 2"); + + assertTrinoExceptionThrownBy(() -> assertions.function("ST_LineString", "array[ST_Point(1,2), ST_GeometryFromText('POINT EMPTY'), ST_Point(3,4), ST_GeometryFromText('POINT EMPTY')]").evaluate()) + .hasMessage("Invalid input to ST_LineString: empty point at index 2"); } @Test @@ -1237,13 +1919,16 @@ public void testMultiPoint() assertMultiPoint("MULTIPOINT ((1 2))", "POINT (1 2)"); // Empty array - assertFunction("ST_MultiPoint(array[])", GEOMETRY, null); + assertThat(assertions.function("ST_MultiPoint", "array[]")) + .isNull(GEOMETRY); // Only points can be passed assertInvalidMultiPoint("geometry is not a point: LineString at index 2", "POINT (7 8)", "LINESTRING (1 2, 3 4)"); // Null point raises exception - assertInvalidFunction("ST_MultiPoint(array[null])", "Invalid input to ST_MultiPoint: null at index 1"); + assertTrinoExceptionThrownBy(() -> assertions.function("ST_MultiPoint", "array[null]").evaluate()) + .hasMessage("Invalid input to ST_MultiPoint: null at index 1"); + assertInvalidMultiPoint("null at index 3", "POINT (1 2)", "POINT (1 2)", null); assertInvalidMultiPoint("null at index 2", "POINT (1 2)", null, "POINT (3 4)"); assertInvalidMultiPoint("null at index 2", "POINT (1 2)", null, "POINT (3 4)", null); @@ -1255,25 +1940,22 @@ public void testMultiPoint() private void assertMultiPoint(String expectedWkt, String... pointWkts) { - assertFunction( - format( - "ST_MultiPoint(array[%s])", - Arrays.stream(pointWkts) - .map(wkt -> wkt == null ? "null" : format("ST_GeometryFromText('%s')", wkt)) - .collect(Collectors.joining(","))), - GEOMETRY, - expectedWkt); + assertThat(assertions.expression("ST_MultiPoint(a)") + .binding("a", "array[%s]".formatted(Arrays.stream(pointWkts) + .map(wkt -> wkt == null ? "null" : "ST_GeometryFromText('%s')".formatted(wkt)) + .collect(Collectors.joining(","))))) + .hasType(GEOMETRY) + .isEqualTo(expectedWkt); } private void assertInvalidMultiPoint(String errorMessage, String... pointWkts) { - assertInvalidFunction( - format( - "ST_MultiPoint(array[%s])", - Arrays.stream(pointWkts) - .map(wkt -> wkt == null ? "null" : format("ST_GeometryFromText('%s')", wkt)) - .collect(Collectors.joining(","))), - format("Invalid input to ST_MultiPoint: %s", errorMessage)); + assertTrinoExceptionThrownBy(() -> assertions.expression("ST_MultiPoint(a)") + .binding("a", "array[%s]".formatted(Arrays.stream(pointWkts) + .map(wkt -> wkt == null ? "null" : "ST_GeometryFromText('%s')".formatted(wkt)) + .collect(Collectors.joining(",")))) + .evaluate()) + .hasMessage("Invalid input to ST_MultiPoint: %s".formatted(errorMessage)); } @Test @@ -1295,19 +1977,27 @@ public void testSTPointN() private void assertPointN(String wkt, int index, String expected) { - assertFunction(format("ST_ASText(ST_PointN(ST_GeometryFromText('%s'), %d))", wkt, index), VARCHAR, expected); + assertThat(assertions.expression("ST_AsText(ST_PointN(geometry, index))") + .binding("geometry", "ST_GeometryFromText('%s')".formatted(wkt)) + .binding("index", Integer.toString(index))) + .hasType(VARCHAR) + .isEqualTo(expected); } private void assertInvalidPointN(String wkt, String type) { - String message = format("ST_PointN only applies to LINE_STRING. Input type is: %s", type); - assertInvalidFunction(format("ST_PointN(ST_GeometryFromText('%s'), 1)", wkt), message); + assertTrinoExceptionThrownBy(() -> assertions.expression("ST_PointN(geometry, 1)") + .binding("geometry", "ST_GeometryFromText('%s')".formatted(wkt)) + .evaluate()) + .hasMessage("ST_PointN only applies to LINE_STRING. Input type is: %s".formatted(type)); } @Test public void testSTGeometries() { - assertFunction("ST_Geometries(ST_GeometryFromText('POINT EMPTY'))", new ArrayType(GEOMETRY), null); + assertThat(assertions.function("ST_Geometries", "ST_GeometryFromText('POINT EMPTY')")) + .isNull(new ArrayType(GEOMETRY)); + assertSTGeometries("POINT (1 5)", "POINT (1 5)"); assertSTGeometries("LINESTRING (77.29 29.07, 77.42 29.26, 77.27 29.31, 77.29 29.07)", "LINESTRING (77.29 29.07, 77.42 29.26, 77.27 29.31, 77.29 29.07)"); assertSTGeometries("POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))"); @@ -1320,7 +2010,10 @@ public void testSTGeometries() private void assertSTGeometries(String wkt, String... expected) { - assertFunction(format("transform(ST_Geometries(ST_GeometryFromText('%s')), x -> ST_ASText(x))", wkt), new ArrayType(VARCHAR), ImmutableList.copyOf(expected)); + assertThat(assertions.expression("transform(ST_Geometries(geometry), x -> ST_AsText(x))") + .binding("geometry", "ST_GeometryFromText('%s')".formatted(wkt))) + .hasType(new ArrayType(VARCHAR)) + .isEqualTo(ImmutableList.copyOf(expected)); } @Test @@ -1343,31 +2036,63 @@ public void testSTInteriorRingN() private void assertInteriorRingN(String wkt, int index, String expected) { - assertFunction(format("ST_ASText(ST_InteriorRingN(ST_GeometryFromText('%s'), %d))", wkt, index), VARCHAR, expected); + assertThat(assertions.expression("ST_AsText(ST_InteriorRingN(geometry, index))") + .binding("geometry", "ST_GeometryFromText('%s')".formatted(wkt)) + .binding("index", Integer.toString(index))) + .hasType(VARCHAR) + .isEqualTo(expected); } private void assertInvalidInteriorRingN(String wkt, int index, String geometryType) { - assertInvalidFunction(format("ST_InteriorRingN(ST_GeometryFromText('%s'), %d)", wkt, index), format("ST_InteriorRingN only applies to POLYGON. Input type is: %s", geometryType)); + assertTrinoExceptionThrownBy(() -> assertions.expression("ST_InteriorRingN(geometry, index)") + .binding("geometry", "ST_GeometryFromText('%s')".formatted(wkt)) + .binding("index", Integer.toString(index)) + .evaluate()) + .hasMessage("ST_InteriorRingN only applies to POLYGON. Input type is: %s".formatted(geometryType)); } @Test public void testSTGeometryType() { - assertFunction("ST_GeometryType(ST_Point(1, 4))", VARCHAR, "ST_Point"); - assertFunction("ST_GeometryType(ST_GeometryFromText('LINESTRING (1 1, 2 2)'))", VARCHAR, "ST_LineString"); - assertFunction("ST_GeometryType(ST_GeometryFromText('POLYGON ((1 1, 1 4, 4 4, 4 1))'))", VARCHAR, "ST_Polygon"); - assertFunction("ST_GeometryType(ST_GeometryFromText('MULTIPOINT (1 1, 2 2)'))", VARCHAR, "ST_MultiPoint"); - assertFunction("ST_GeometryType(ST_GeometryFromText('MULTILINESTRING ((1 1, 2 2), (3 3, 4 4))'))", VARCHAR, "ST_MultiLineString"); - assertFunction("ST_GeometryType(ST_GeometryFromText('MULTIPOLYGON (((1 1, 1 4, 4 4, 4 1)), ((1 1, 1 4, 4 4, 4 1)))'))", VARCHAR, "ST_MultiPolygon"); - assertFunction("ST_GeometryType(ST_GeometryFromText('GEOMETRYCOLLECTION(POINT(4 6),LINESTRING(4 6, 7 10))'))", VARCHAR, "ST_GeomCollection"); - assertFunction("ST_GeometryType(ST_Envelope(ST_GeometryFromText('LINESTRING (1 1, 2 2)')))", VARCHAR, "ST_Polygon"); + assertThat(assertions.function("ST_GeometryType", "ST_Point(1, 4)")) + .hasType(VARCHAR) + .isEqualTo("ST_Point"); + + assertThat(assertions.function("ST_GeometryType", "ST_GeometryFromText('LINESTRING (1 1, 2 2)')")) + .hasType(VARCHAR) + .isEqualTo("ST_LineString"); + + assertThat(assertions.function("ST_GeometryType", "ST_GeometryFromText('POLYGON ((1 1, 1 4, 4 4, 4 1))')")) + .hasType(VARCHAR) + .isEqualTo("ST_Polygon"); + + assertThat(assertions.function("ST_GeometryType", "ST_GeometryFromText('MULTIPOINT (1 1, 2 2)')")) + .hasType(VARCHAR) + .isEqualTo("ST_MultiPoint"); + + assertThat(assertions.function("ST_GeometryType", "ST_GeometryFromText('MULTILINESTRING ((1 1, 2 2), (3 3, 4 4))')")) + .hasType(VARCHAR) + .isEqualTo("ST_MultiLineString"); + + assertThat(assertions.function("ST_GeometryType", "ST_GeometryFromText('MULTIPOLYGON (((1 1, 1 4, 4 4, 4 1)), ((1 1, 1 4, 4 4, 4 1)))')")) + .hasType(VARCHAR) + .isEqualTo("ST_MultiPolygon"); + + assertThat(assertions.function("ST_GeometryType", "ST_GeometryFromText('GEOMETRYCOLLECTION(POINT(4 6),LINESTRING(4 6, 7 10))')")) + .hasType(VARCHAR) + .isEqualTo("ST_GeomCollection"); + + assertThat(assertions.function("ST_GeometryType", "ST_Envelope(ST_GeometryFromText('LINESTRING (1 1, 2 2)'))")) + .hasType(VARCHAR) + .isEqualTo("ST_Polygon"); } @Test public void testSTGeometryFromBinary() { - assertFunction("ST_GeomFromBinary(null)", GEOMETRY, null); + assertThat(assertions.function("ST_GeomFromBinary", "null")) + .isNull(GEOMETRY); // empty geometries assertGeomFromBinary("POINT EMPTY"); @@ -1389,25 +2114,33 @@ public void testSTGeometryFromBinary() assertGeomFromBinary("GEOMETRYCOLLECTION (POINT (1 2), LINESTRING (0 0, 1 2, 3 4), POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0)))"); // array of geometries - assertFunction("transform(array[ST_AsBinary(ST_Point(1, 2)), ST_AsBinary(ST_Point(3, 4))], wkb -> ST_AsText(ST_GeomFromBinary(wkb)))", new ArrayType(VARCHAR), ImmutableList.of("POINT (1 2)", "POINT (3 4)")); + assertThat(assertions.expression("transform(a, wkb -> ST_AsText(ST_GeomFromBinary(wkb)))") + .binding("a", "ARRAY[ST_AsBinary(ST_Point(1, 2)), ST_AsBinary(ST_Point(3, 4))]")) + .hasType(new ArrayType(VARCHAR)) + .isEqualTo(ImmutableList.of("POINT (1 2)", "POINT (3 4)")); // invalid geometries assertGeomFromBinary("MULTIPOINT ((0 0), (0 1), (1 1), (0 1))"); assertGeomFromBinary("LINESTRING (0 0, 0 1, 0 1, 1 1, 1 0, 0 0)"); // invalid binary - assertInvalidFunction("ST_GeomFromBinary(from_hex('deadbeef'))", "Invalid WKB"); + assertTrinoExceptionThrownBy(() -> assertions.function("ST_GeomFromBinary", "from_hex('deadbeef')").evaluate()) + .hasMessage("Invalid WKB"); } private void assertGeomFromBinary(String wkt) { - assertFunction(format("ST_AsText(ST_GeomFromBinary(ST_AsBinary(ST_GeometryFromText('%s'))))", wkt), VARCHAR, wkt); + assertThat(assertions.expression("ST_AsText(ST_GeomFromBinary(geometry))") + .binding("geometry", "ST_AsBinary(ST_GeometryFromText('%s'))".formatted(wkt))) + .hasType(VARCHAR) + .isEqualTo(wkt); } @Test public void testGeometryFromHadoopShape() { - assertFunction("geometry_from_hadoop_shape(null)", GEOMETRY, null); + assertThat(assertions.function("geometry_from_hadoop_shape", "null")) + .isNull(GEOMETRY); // empty geometries assertGeometryFromHadoopShape("000000000101000000FFFFFFFFFFFFEFFFFFFFFFFFFFFFEFFF", "POINT EMPTY"); @@ -1426,22 +2159,31 @@ public void testGeometryFromHadoopShape() assertGeometryFromHadoopShape("000000000605000000000000000000F03F000000000000F03F00000000000018400000000000001840020000000A0000000000000005000000000000000000F03F000000000000F03F000000000000F03F0000000000000840000000000000084000000000000008400000000000000840000000000000F03F000000000000F03F000000000000F03F0000000000000040000000000000104000000000000000400000000000001840000000000000184000000000000018400000000000001840000000000000104000000000000000400000000000001040", "MULTIPOLYGON (((1 1, 3 1, 3 3, 1 3, 1 1)), ((2 4, 6 4, 6 6, 2 6, 2 4)))"); // given hadoop shape is too short - assertInvalidFunction("geometry_from_hadoop_shape(from_hex('1234'))", "Hadoop shape input is too short"); + assertTrinoExceptionThrownBy(() -> assertions.function("geometry_from_hadoop_shape", "from_hex('1234')").evaluate()) + .hasMessage("Hadoop shape input is too short"); // hadoop shape type invalid - assertInvalidFunction("geometry_from_hadoop_shape(from_hex('000000000701000000FFFFFFFFFFFFEFFFFFFFFFFFFFFFEFFF'))", "Invalid Hadoop shape type: 7"); - assertInvalidFunction("geometry_from_hadoop_shape(from_hex('00000000FF01000000FFFFFFFFFFFFEFFFFFFFFFFFFFFFEFFF'))", "Invalid Hadoop shape type: -1"); + assertTrinoExceptionThrownBy(() -> assertions.function("geometry_from_hadoop_shape", "from_hex('000000000701000000FFFFFFFFFFFFEFFFFFFFFFFFFFFFEFFF')").evaluate()) + .hasMessage("Invalid Hadoop shape type: 7"); + + assertTrinoExceptionThrownBy(() -> assertions.function("geometry_from_hadoop_shape", "from_hex('00000000FF01000000FFFFFFFFFFFFEFFFFFFFFFFFFFFFEFFF')").evaluate()) + .hasMessage("Invalid Hadoop shape type: -1"); // esri shape invalid - assertInvalidFunction("geometry_from_hadoop_shape(from_hex('000000000101000000FFFFFFFFFFFFEFFFFFFFFFFFFFFFEF'))", "Invalid Hadoop shape"); + assertTrinoExceptionThrownBy(() -> assertions.function("geometry_from_hadoop_shape", "from_hex('000000000101000000FFFFFFFFFFFFEFFFFFFFFFFFFFFFEF')").evaluate()) + .hasMessage("Invalid Hadoop shape"); // shape type is invalid for given shape - assertInvalidFunction("geometry_from_hadoop_shape(from_hex('000000000501000000000000000000F03F0000000000000040'))", "Invalid Hadoop shape"); + assertTrinoExceptionThrownBy(() -> assertions.function("geometry_from_hadoop_shape", "from_hex('000000000501000000000000000000F03F0000000000000040')").evaluate()) + .hasMessage("Invalid Hadoop shape"); } private void assertGeometryFromHadoopShape(String hadoopHex, String expectedWkt) { - assertFunction(format("ST_AsText(geometry_from_hadoop_shape(from_hex('%s')))", hadoopHex), VARCHAR, expectedWkt); + assertThat(assertions.expression("ST_AsText(geometry_from_hadoop_shape(geometry))") + .binding("geometry", "from_hex('%s')".formatted(hadoopHex))) + .hasType(VARCHAR) + .isEqualTo(expectedWkt); } @Test @@ -1520,16 +2262,21 @@ public void testGeometryJsonConversion() private void assertGeoToAndFromJson(String wkt) { - assertFunction(format("ST_AsText(to_geometry(from_geojson_geometry(to_geojson_geometry(to_spherical_geography(ST_GeometryFromText('%s'))))))", wkt), VARCHAR, wkt); + assertThat(assertions.function("ST_AsText", "to_geometry(from_geojson_geometry(to_geojson_geometry(to_spherical_geography(ST_GeometryFromText('%s')))))".formatted(wkt))) + .hasType(VARCHAR) + .isEqualTo(wkt); } private void assertValidGeometryJson(String json, String wkt) { - assertFunction("ST_AsText(to_geometry(from_geojson_geometry('" + json + "')))", VARCHAR, wkt); + assertThat(assertions.function("ST_AsText", "to_geometry(from_geojson_geometry('%s'))".formatted(json))) + .hasType(VARCHAR) + .isEqualTo(wkt); } private void assertInvalidGeometryJson(String json, String message) { - assertInvalidFunction("from_geojson_geometry('" + json + "')", message); + assertTrinoExceptionThrownBy(() -> assertions.function("from_geojson_geometry", "'%s'".formatted(json)).evaluate()) + .hasMessage(message); } } diff --git a/plugin/trino-geospatial/src/test/java/io/trino/plugin/geospatial/aggregation/TestGeometryUnionGeoAggregation.java b/plugin/trino-geospatial/src/test/java/io/trino/plugin/geospatial/aggregation/TestGeometryUnionGeoAggregation.java index 03504f73eff0..1c1634b6b549 100644 --- a/plugin/trino-geospatial/src/test/java/io/trino/plugin/geospatial/aggregation/TestGeometryUnionGeoAggregation.java +++ b/plugin/trino-geospatial/src/test/java/io/trino/plugin/geospatial/aggregation/TestGeometryUnionGeoAggregation.java @@ -14,6 +14,10 @@ package io.trino.plugin.geospatial.aggregation; import com.google.common.base.Joiner; +import io.trino.plugin.geospatial.GeoPlugin; +import io.trino.sql.query.QueryAssertions; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -24,10 +28,27 @@ import static java.lang.String.format; import static java.util.Collections.reverse; import static java.util.stream.Collectors.toList; +import static org.assertj.core.api.Assertions.assertThat; public class TestGeometryUnionGeoAggregation extends AbstractTestGeoAggregationFunctions { + private QueryAssertions assertions; + + @BeforeClass + public void init() + { + assertions = new QueryAssertions(); + assertions.addPlugin(new GeoPlugin()); + } + + @AfterClass(alwaysRun = true) + public void teardown() + { + assertions.close(); + assertions = null; + } + private static final Joiner COMMA_JOINER = Joiner.on(","); @DataProvider(name = "point") @@ -361,10 +382,14 @@ private void assertArrayAggAndGeometryUnion(String expectedWkt, String[] wkts) List wktList = Arrays.stream(wkts).map(wkt -> format("ST_GeometryFromText('%s')", wkt)).collect(toList()); String wktArray = format("ARRAY[%s]", COMMA_JOINER.join(wktList)); // ST_Union(ARRAY[ST_GeometryFromText('...'), ...]) - assertFunction(format("geometry_union(%s)", wktArray), GEOMETRY, expectedWkt); + assertThat(assertions.function("geometry_union", wktArray)) + .hasType(GEOMETRY) + .isEqualTo(expectedWkt); reverse(wktList); wktArray = format("ARRAY[%s]", COMMA_JOINER.join(wktList)); - assertFunction(format("geometry_union(%s)", wktArray), GEOMETRY, expectedWkt); + assertThat(assertions.function("geometry_union", wktArray)) + .hasType(GEOMETRY) + .isEqualTo(expectedWkt); } }