From be73377a645da261579c2b4d15d3df200c154f96 Mon Sep 17 00:00:00 2001 From: Piotr Findeisen Date: Fri, 10 Jun 2022 13:40:44 +0200 Subject: [PATCH] Add value traversal methods to Type --- .../java/io/trino/type/AbstractTestType.java | 34 ++++ .../java/io/trino/type/TestBigintType.java | 43 +++++ .../test/java/io/trino/type/TestDateType.java | 43 +++++ .../java/io/trino/type/TestIntegerType.java | 43 +++++ .../TestLongTimestampWithTimeZoneType.java | 108 +++++++++++ .../io/trino/type/TestShortTimestampType.java | 100 ++++++++++ .../java/io/trino/type/TestSmallintType.java | 43 +++++ .../java/io/trino/type/TestTinyintType.java | 43 +++++ .../java/io/trino/type/TestUnknownType.java | 12 ++ .../io/trino/spi/type/AbstractIntType.java | 8 +- .../java/io/trino/spi/type/BigintType.java | 20 ++ .../main/java/io/trino/spi/type/DateType.java | 22 +++ .../java/io/trino/spi/type/IntegerType.java | 22 +++ .../type/LongTimestampWithTimeZoneType.java | 41 +++++ .../io/trino/spi/type/ShortTimestampType.java | 18 ++ .../java/io/trino/spi/type/SmallintType.java | 30 ++- .../java/io/trino/spi/type/TinyintType.java | 30 ++- .../src/main/java/io/trino/spi/type/Type.java | 32 ++++ .../io/trino/plugin/iceberg/IcebergUtil.java | 6 +- .../io/trino/plugin/iceberg/TrinoTypes.java | 171 ------------------ 20 files changed, 688 insertions(+), 181 deletions(-) delete mode 100644 plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/TrinoTypes.java diff --git a/core/trino-main/src/test/java/io/trino/type/AbstractTestType.java b/core/trino-main/src/test/java/io/trino/type/AbstractTestType.java index 5b3a6f6f115e..3f32cd154d02 100644 --- a/core/trino-main/src/test/java/io/trino/type/AbstractTestType.java +++ b/core/trino-main/src/test/java/io/trino/type/AbstractTestType.java @@ -14,6 +14,7 @@ package io.trino.type; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Iterables; import io.airlift.slice.DynamicSliceOutput; import io.airlift.slice.Slice; import io.airlift.slice.SliceOutput; @@ -197,6 +198,39 @@ public void testRange() .isEmpty(); } + @Test + public void testPreviousValue() + { + Object sampleValue = getSampleValue(); + if (!type.isOrderable()) { + assertThatThrownBy(() -> type.getPreviousValue(sampleValue)) + .isInstanceOf(IllegalStateException.class) + .hasMessage("Type is not orderable: " + type); + return; + } + assertThat(type.getPreviousValue(sampleValue)) + .isEmpty(); + } + + @Test + public void testNextValue() + { + Object sampleValue = getSampleValue(); + if (!type.isOrderable()) { + assertThatThrownBy(() -> type.getNextValue(sampleValue)) + .isInstanceOf(IllegalStateException.class) + .hasMessage("Type is not orderable: " + type); + return; + } + assertThat(type.getNextValue(sampleValue)) + .isEmpty(); + } + + protected Object getSampleValue() + { + return requireNonNull(Iterables.get(expectedStackValues.values(), 0), "sample value is null"); + } + protected void assertPositionEquals(Block block, int position, Object expectedStackValue, Object expectedObjectValue) { long hash = 0; diff --git a/core/trino-main/src/test/java/io/trino/type/TestBigintType.java b/core/trino-main/src/test/java/io/trino/type/TestBigintType.java index 74ce723eb49a..33481299229e 100644 --- a/core/trino-main/src/test/java/io/trino/type/TestBigintType.java +++ b/core/trino-main/src/test/java/io/trino/type/TestBigintType.java @@ -17,7 +17,10 @@ import io.trino.spi.block.BlockBuilder; import io.trino.spi.type.Type.Range; +import java.util.Optional; + import static io.trino.spi.type.BigintType.BIGINT; +import static org.assertj.core.api.Assertions.assertThat; import static org.testng.Assert.assertEquals; public class TestBigintType @@ -58,4 +61,44 @@ public void testRange() assertEquals(range.getMin(), Long.MIN_VALUE); assertEquals(range.getMax(), Long.MAX_VALUE); } + + @Override + public void testPreviousValue() + { + long minValue = Long.MIN_VALUE; + long maxValue = Long.MAX_VALUE; + + assertThat(type.getPreviousValue(minValue)) + .isEqualTo(Optional.empty()); + assertThat(type.getPreviousValue(minValue + 1)) + .isEqualTo(Optional.of(minValue)); + + assertThat(type.getPreviousValue(getSampleValue())) + .isEqualTo(Optional.of(1110L)); + + assertThat(type.getPreviousValue(maxValue - 1)) + .isEqualTo(Optional.of(maxValue - 2)); + assertThat(type.getPreviousValue(maxValue)) + .isEqualTo(Optional.of(maxValue - 1)); + } + + @Override + public void testNextValue() + { + long minValue = Long.MIN_VALUE; + long maxValue = Long.MAX_VALUE; + + assertThat(type.getNextValue(minValue)) + .isEqualTo(Optional.of(minValue + 1)); + assertThat(type.getNextValue(minValue + 1)) + .isEqualTo(Optional.of(minValue + 2)); + + assertThat(type.getNextValue(getSampleValue())) + .isEqualTo(Optional.of(1112L)); + + assertThat(type.getNextValue(maxValue - 1)) + .isEqualTo(Optional.of(maxValue)); + assertThat(type.getNextValue(maxValue)) + .isEqualTo(Optional.empty()); + } } diff --git a/core/trino-main/src/test/java/io/trino/type/TestDateType.java b/core/trino-main/src/test/java/io/trino/type/TestDateType.java index 016d5321ca5d..1889ba942ea5 100644 --- a/core/trino-main/src/test/java/io/trino/type/TestDateType.java +++ b/core/trino-main/src/test/java/io/trino/type/TestDateType.java @@ -18,7 +18,10 @@ import io.trino.spi.type.SqlDate; import io.trino.spi.type.Type.Range; +import java.util.Optional; + import static io.trino.spi.type.DateType.DATE; +import static org.assertj.core.api.Assertions.assertThat; import static org.testng.Assert.assertEquals; public class TestDateType @@ -59,4 +62,44 @@ public void testRange() assertEquals(range.getMin(), (long) Integer.MIN_VALUE); assertEquals(range.getMax(), (long) Integer.MAX_VALUE); } + + @Override + public void testPreviousValue() + { + long minValue = Integer.MIN_VALUE; + long maxValue = Integer.MAX_VALUE; + + assertThat(type.getPreviousValue(minValue)) + .isEqualTo(Optional.empty()); + assertThat(type.getPreviousValue(minValue + 1)) + .isEqualTo(Optional.of(minValue)); + + assertThat(type.getPreviousValue(getSampleValue())) + .isEqualTo(Optional.of(1110L)); + + assertThat(type.getPreviousValue(maxValue - 1)) + .isEqualTo(Optional.of(maxValue - 2)); + assertThat(type.getPreviousValue(maxValue)) + .isEqualTo(Optional.of(maxValue - 1)); + } + + @Override + public void testNextValue() + { + long minValue = Integer.MIN_VALUE; + long maxValue = Integer.MAX_VALUE; + + assertThat(type.getNextValue(minValue)) + .isEqualTo(Optional.of(minValue + 1)); + assertThat(type.getNextValue(minValue + 1)) + .isEqualTo(Optional.of(minValue + 2)); + + assertThat(type.getNextValue(getSampleValue())) + .isEqualTo(Optional.of(1112L)); + + assertThat(type.getNextValue(maxValue - 1)) + .isEqualTo(Optional.of(maxValue)); + assertThat(type.getNextValue(maxValue)) + .isEqualTo(Optional.empty()); + } } diff --git a/core/trino-main/src/test/java/io/trino/type/TestIntegerType.java b/core/trino-main/src/test/java/io/trino/type/TestIntegerType.java index e6cb9e21e0df..7c8fa786cb24 100644 --- a/core/trino-main/src/test/java/io/trino/type/TestIntegerType.java +++ b/core/trino-main/src/test/java/io/trino/type/TestIntegerType.java @@ -17,7 +17,10 @@ import io.trino.spi.block.BlockBuilder; import io.trino.spi.type.Type.Range; +import java.util.Optional; + import static io.trino.spi.type.IntegerType.INTEGER; +import static org.assertj.core.api.Assertions.assertThat; import static org.testng.Assert.assertEquals; public class TestIntegerType @@ -58,4 +61,44 @@ public void testRange() assertEquals(range.getMin(), (long) Integer.MIN_VALUE); assertEquals(range.getMax(), (long) Integer.MAX_VALUE); } + + @Override + public void testPreviousValue() + { + long minValue = Integer.MIN_VALUE; + long maxValue = Integer.MAX_VALUE; + + assertThat(type.getPreviousValue(minValue)) + .isEqualTo(Optional.empty()); + assertThat(type.getPreviousValue(minValue + 1)) + .isEqualTo(Optional.of(minValue)); + + assertThat(type.getPreviousValue(getSampleValue())) + .isEqualTo(Optional.of(1110L)); + + assertThat(type.getPreviousValue(maxValue - 1)) + .isEqualTo(Optional.of(maxValue - 2)); + assertThat(type.getPreviousValue(maxValue)) + .isEqualTo(Optional.of(maxValue - 1)); + } + + @Override + public void testNextValue() + { + long minValue = Integer.MIN_VALUE; + long maxValue = Integer.MAX_VALUE; + + assertThat(type.getNextValue(minValue)) + .isEqualTo(Optional.of(minValue + 1)); + assertThat(type.getNextValue(minValue + 1)) + .isEqualTo(Optional.of(minValue + 2)); + + assertThat(type.getNextValue(getSampleValue())) + .isEqualTo(Optional.of(1112L)); + + assertThat(type.getNextValue(maxValue - 1)) + .isEqualTo(Optional.of(maxValue)); + assertThat(type.getNextValue(maxValue)) + .isEqualTo(Optional.empty()); + } } diff --git a/core/trino-main/src/test/java/io/trino/type/TestLongTimestampWithTimeZoneType.java b/core/trino-main/src/test/java/io/trino/type/TestLongTimestampWithTimeZoneType.java index a893ae3347d9..f26e25bc15ea 100644 --- a/core/trino-main/src/test/java/io/trino/type/TestLongTimestampWithTimeZoneType.java +++ b/core/trino-main/src/test/java/io/trino/type/TestLongTimestampWithTimeZoneType.java @@ -17,9 +17,17 @@ import io.trino.spi.block.BlockBuilder; import io.trino.spi.type.LongTimestampWithTimeZone; import io.trino.spi.type.SqlTimestampWithTimeZone; +import io.trino.spi.type.Type; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import java.util.Optional; + +import static io.trino.spi.type.TimeZoneKey.UTC_KEY; import static io.trino.spi.type.TimeZoneKey.getTimeZoneKeyForOffset; +import static io.trino.spi.type.TimestampType.createTimestampType; import static io.trino.spi.type.TimestampWithTimeZoneType.TIMESTAMP_TZ_MICROS; +import static org.assertj.core.api.Assertions.assertThat; public class TestLongTimestampWithTimeZoneType extends AbstractTestType @@ -52,4 +60,104 @@ protected Object getGreaterValue(Object value) // time zone doesn't matter for ordering return LongTimestampWithTimeZone.fromEpochMillisAndFraction(((LongTimestampWithTimeZone) value).getEpochMillis() + 1, 0, getTimeZoneKeyForOffset(33)); } + + @Override + public void testPreviousValue() + { + LongTimestampWithTimeZone minValue = LongTimestampWithTimeZone.fromEpochMillisAndFraction(Long.MIN_VALUE, 0, UTC_KEY); + LongTimestampWithTimeZone nextToMinValue = LongTimestampWithTimeZone.fromEpochMillisAndFraction(Long.MIN_VALUE, 1_000_000, UTC_KEY); + LongTimestampWithTimeZone previousToMaxValue = LongTimestampWithTimeZone.fromEpochMillisAndFraction(Long.MAX_VALUE, 998_000_000, UTC_KEY); + LongTimestampWithTimeZone maxValue = LongTimestampWithTimeZone.fromEpochMillisAndFraction(Long.MAX_VALUE, 999_000_000, UTC_KEY); + + assertThat(type.getPreviousValue(minValue)) + .isEqualTo(Optional.empty()); + assertThat(type.getPreviousValue(nextToMinValue)) + .isEqualTo(Optional.of(minValue)); + + assertThat(type.getPreviousValue(getSampleValue())) + .isEqualTo(Optional.of(LongTimestampWithTimeZone.fromEpochMillisAndFraction(1110, 999_000_000, getTimeZoneKeyForOffset(0)))); + + assertThat(type.getPreviousValue(previousToMaxValue)) + .isEqualTo(Optional.of(LongTimestampWithTimeZone.fromEpochMillisAndFraction(Long.MAX_VALUE, 997_000_000, UTC_KEY))); + assertThat(type.getPreviousValue(maxValue)) + .isEqualTo(Optional.of(previousToMaxValue)); + } + + @Override + public void testNextValue() + { + LongTimestampWithTimeZone minValue = LongTimestampWithTimeZone.fromEpochMillisAndFraction(Long.MIN_VALUE, 0, UTC_KEY); + LongTimestampWithTimeZone nextToMinValue = LongTimestampWithTimeZone.fromEpochMillisAndFraction(Long.MIN_VALUE, 1_000_000, UTC_KEY); + LongTimestampWithTimeZone previousToMaxValue = LongTimestampWithTimeZone.fromEpochMillisAndFraction(Long.MAX_VALUE, 998_000_000, UTC_KEY); + LongTimestampWithTimeZone maxValue = LongTimestampWithTimeZone.fromEpochMillisAndFraction(Long.MAX_VALUE, 999_000_000, UTC_KEY); + + assertThat(type.getNextValue(minValue)) + .isEqualTo(Optional.of(nextToMinValue)); + assertThat(type.getNextValue(nextToMinValue)) + .isEqualTo(Optional.of(LongTimestampWithTimeZone.fromEpochMillisAndFraction(Long.MIN_VALUE, 2_000_000, UTC_KEY))); + + assertThat(type.getNextValue(getSampleValue())) + .isEqualTo(Optional.of(LongTimestampWithTimeZone.fromEpochMillisAndFraction(1111, 1_000_000, getTimeZoneKeyForOffset(0)))); + + assertThat(type.getNextValue(previousToMaxValue)) + .isEqualTo(Optional.of(maxValue)); + assertThat(type.getNextValue(maxValue)) + .isEqualTo(Optional.empty()); + } + + @Test(dataProvider = "testPreviousNextValueEveryPrecisionDatProvider") + public void testPreviousValueEveryPrecision(int precision, long minValue, long maxValue, long step) + { + Type type = createTimestampType(precision); + + assertThat(type.getPreviousValue(minValue)) + .isEqualTo(Optional.empty()); + assertThat(type.getPreviousValue(minValue + step)) + .isEqualTo(Optional.of(minValue)); + + assertThat(type.getPreviousValue(0L)) + .isEqualTo(Optional.of(-step)); + assertThat(type.getPreviousValue(123_456_789_000_000L)) + .isEqualTo(Optional.of(123_456_789_000_000L - step)); + + assertThat(type.getPreviousValue(maxValue - step)) + .isEqualTo(Optional.of(maxValue - 2 * step)); + assertThat(type.getPreviousValue(maxValue)) + .isEqualTo(Optional.of(maxValue - step)); + } + + @Test(dataProvider = "testPreviousNextValueEveryPrecisionDatProvider") + public void testNextValueEveryPrecision(int precision, long minValue, long maxValue, long step) + { + Type type = createTimestampType(precision); + + assertThat(type.getNextValue(minValue)) + .isEqualTo(Optional.of(minValue + step)); + assertThat(type.getNextValue(minValue + step)) + .isEqualTo(Optional.of(minValue + 2 * step)); + + assertThat(type.getNextValue(0L)) + .isEqualTo(Optional.of(step)); + assertThat(type.getNextValue(123_456_789_000_000L)) + .isEqualTo(Optional.of(123_456_789_000_000L + step)); + + assertThat(type.getNextValue(maxValue - step)) + .isEqualTo(Optional.of(maxValue)); + assertThat(type.getNextValue(maxValue)) + .isEqualTo(Optional.empty()); + } + + @DataProvider + public Object[][] testPreviousNextValueEveryPrecisionDatProvider() + { + return new Object[][] { + {0, Long.MIN_VALUE + 775808, Long.MAX_VALUE - 775807, 1_000_000L}, + {1, Long.MIN_VALUE + 75808, Long.MAX_VALUE - 75807, 100_000L}, + {2, Long.MIN_VALUE + 5808, Long.MAX_VALUE - 5807, 10_000L}, + {3, Long.MIN_VALUE + 808, Long.MAX_VALUE - 807, 1_000L}, + {4, Long.MIN_VALUE + 8, Long.MAX_VALUE - 7, 100L}, + {5, Long.MIN_VALUE + 8, Long.MAX_VALUE - 7, 10L}, + {6, Long.MIN_VALUE, Long.MAX_VALUE, 1L}, + }; + } } diff --git a/core/trino-main/src/test/java/io/trino/type/TestShortTimestampType.java b/core/trino-main/src/test/java/io/trino/type/TestShortTimestampType.java index b4bb8e656e96..1dc091588d0e 100644 --- a/core/trino-main/src/test/java/io/trino/type/TestShortTimestampType.java +++ b/core/trino-main/src/test/java/io/trino/type/TestShortTimestampType.java @@ -16,12 +16,16 @@ import io.trino.spi.block.Block; import io.trino.spi.block.BlockBuilder; import io.trino.spi.type.SqlTimestamp; +import io.trino.spi.type.Type; import io.trino.spi.type.Type.Range; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; +import java.util.Optional; + import static io.trino.spi.type.TimestampType.TIMESTAMP_MILLIS; import static io.trino.spi.type.TimestampType.createTimestampType; +import static org.assertj.core.api.Assertions.assertThat; import static org.testng.Assert.assertEquals; public class TestShortTimestampType @@ -84,4 +88,100 @@ public static Object[][] testRangeEveryPrecisionDataProvider() {6, Long.MIN_VALUE, Long.MAX_VALUE}, }; } + + @Override + public void testPreviousValue() + { + long minValue = Long.MIN_VALUE + 808; + long maxValue = Long.MAX_VALUE - 807; + + assertThat(type.getPreviousValue(minValue)) + .isEqualTo(Optional.empty()); + assertThat(type.getPreviousValue(minValue + 1_000)) + .isEqualTo(Optional.of(minValue)); + + assertThat(type.getPreviousValue(getSampleValue())) + .isEqualTo(Optional.of(1110_000L)); + + assertThat(type.getPreviousValue(maxValue - 1_000)) + .isEqualTo(Optional.of(maxValue - 2_000)); + assertThat(type.getPreviousValue(maxValue)) + .isEqualTo(Optional.of(maxValue - 1_000)); + } + + @Override + public void testNextValue() + { + long minValue = Long.MIN_VALUE + 808; + long maxValue = Long.MAX_VALUE - 807; + + assertThat(type.getNextValue(minValue)) + .isEqualTo(Optional.of(minValue + 1_000)); + assertThat(type.getNextValue(minValue + 1_000)) + .isEqualTo(Optional.of(minValue + 2_000)); + + assertThat(type.getNextValue(getSampleValue())) + .isEqualTo(Optional.of(1112_000L)); + + assertThat(type.getNextValue(maxValue - 1_000)) + .isEqualTo(Optional.of(maxValue)); + assertThat(type.getNextValue(maxValue)) + .isEqualTo(Optional.empty()); + } + + @Test(dataProvider = "testPreviousNextValueEveryPrecisionDatProvider") + public void testPreviousValueEveryPrecision(int precision, long minValue, long maxValue, long step) + { + Type type = createTimestampType(precision); + + assertThat(type.getPreviousValue(minValue)) + .isEqualTo(Optional.empty()); + assertThat(type.getPreviousValue(minValue + step)) + .isEqualTo(Optional.of(minValue)); + + assertThat(type.getPreviousValue(0L)) + .isEqualTo(Optional.of(-step)); + assertThat(type.getPreviousValue(123_456_789_000_000L)) + .isEqualTo(Optional.of(123_456_789_000_000L - step)); + + assertThat(type.getPreviousValue(maxValue - step)) + .isEqualTo(Optional.of(maxValue - 2 * step)); + assertThat(type.getPreviousValue(maxValue)) + .isEqualTo(Optional.of(maxValue - step)); + } + + @Test(dataProvider = "testPreviousNextValueEveryPrecisionDatProvider") + public void testNextValueEveryPrecision(int precision, long minValue, long maxValue, long step) + { + Type type = createTimestampType(precision); + + assertThat(type.getNextValue(minValue)) + .isEqualTo(Optional.of(minValue + step)); + assertThat(type.getNextValue(minValue + step)) + .isEqualTo(Optional.of(minValue + 2 * step)); + + assertThat(type.getNextValue(0L)) + .isEqualTo(Optional.of(step)); + assertThat(type.getNextValue(123_456_789_000_000L)) + .isEqualTo(Optional.of(123_456_789_000_000L + step)); + + assertThat(type.getNextValue(maxValue - step)) + .isEqualTo(Optional.of(maxValue)); + assertThat(type.getNextValue(maxValue)) + .isEqualTo(Optional.empty()); + } + + @DataProvider + public Object[][] testPreviousNextValueEveryPrecisionDatProvider() + { + return new Object[][] { + {0, Long.MIN_VALUE + 775808, Long.MAX_VALUE - 775807, 1_000_000L}, + {1, Long.MIN_VALUE + 75808, Long.MAX_VALUE - 75807, 100_000L}, + {2, Long.MIN_VALUE + 5808, Long.MAX_VALUE - 5807, 10_000L}, + {3, Long.MIN_VALUE + 808, Long.MAX_VALUE - 807, 1_000L}, + {4, Long.MIN_VALUE + 8, Long.MAX_VALUE - 7, 100L}, + {5, Long.MIN_VALUE + 8, Long.MAX_VALUE - 7, 10L}, + {6, Long.MIN_VALUE, Long.MAX_VALUE, 1L}, + }; + } } diff --git a/core/trino-main/src/test/java/io/trino/type/TestSmallintType.java b/core/trino-main/src/test/java/io/trino/type/TestSmallintType.java index 2a12ffb07dbe..a889ebfab2bd 100644 --- a/core/trino-main/src/test/java/io/trino/type/TestSmallintType.java +++ b/core/trino-main/src/test/java/io/trino/type/TestSmallintType.java @@ -17,7 +17,10 @@ import io.trino.spi.block.BlockBuilder; import io.trino.spi.type.Type.Range; +import java.util.Optional; + import static io.trino.spi.type.SmallintType.SMALLINT; +import static org.assertj.core.api.Assertions.assertThat; import static org.testng.Assert.assertEquals; public class TestSmallintType @@ -58,4 +61,44 @@ public void testRange() assertEquals(range.getMin(), (long) Short.MIN_VALUE); assertEquals(range.getMax(), (long) Short.MAX_VALUE); } + + @Override + public void testPreviousValue() + { + long minValue = Short.MIN_VALUE; + long maxValue = Short.MAX_VALUE; + + assertThat(type.getPreviousValue(minValue)) + .isEqualTo(Optional.empty()); + assertThat(type.getPreviousValue(minValue + 1)) + .isEqualTo(Optional.of(minValue)); + + assertThat(type.getPreviousValue(getSampleValue())) + .isEqualTo(Optional.of(1110L)); + + assertThat(type.getPreviousValue(maxValue - 1)) + .isEqualTo(Optional.of(maxValue - 2)); + assertThat(type.getPreviousValue(maxValue)) + .isEqualTo(Optional.of(maxValue - 1)); + } + + @Override + public void testNextValue() + { + long minValue = Short.MIN_VALUE; + long maxValue = Short.MAX_VALUE; + + assertThat(type.getNextValue(minValue)) + .isEqualTo(Optional.of(minValue + 1)); + assertThat(type.getNextValue(minValue + 1)) + .isEqualTo(Optional.of(minValue + 2)); + + assertThat(type.getNextValue(getSampleValue())) + .isEqualTo(Optional.of(1112L)); + + assertThat(type.getNextValue(maxValue - 1)) + .isEqualTo(Optional.of(maxValue)); + assertThat(type.getNextValue(maxValue)) + .isEqualTo(Optional.empty()); + } } diff --git a/core/trino-main/src/test/java/io/trino/type/TestTinyintType.java b/core/trino-main/src/test/java/io/trino/type/TestTinyintType.java index 33d5e5e749fb..85157ed64e18 100644 --- a/core/trino-main/src/test/java/io/trino/type/TestTinyintType.java +++ b/core/trino-main/src/test/java/io/trino/type/TestTinyintType.java @@ -17,7 +17,10 @@ import io.trino.spi.block.BlockBuilder; import io.trino.spi.type.Type.Range; +import java.util.Optional; + import static io.trino.spi.type.TinyintType.TINYINT; +import static org.assertj.core.api.Assertions.assertThat; import static org.testng.Assert.assertEquals; public class TestTinyintType @@ -58,4 +61,44 @@ public void testRange() assertEquals(range.getMin(), (long) Byte.MIN_VALUE); assertEquals(range.getMax(), (long) Byte.MAX_VALUE); } + + @Override + public void testPreviousValue() + { + long minValue = Byte.MIN_VALUE; + long maxValue = Byte.MAX_VALUE; + + assertThat(type.getPreviousValue(minValue)) + .isEqualTo(Optional.empty()); + assertThat(type.getPreviousValue(minValue + 1)) + .isEqualTo(Optional.of(minValue)); + + assertThat(type.getPreviousValue(getSampleValue())) + .isEqualTo(Optional.of(110L)); + + assertThat(type.getPreviousValue(maxValue - 1)) + .isEqualTo(Optional.of(maxValue - 2)); + assertThat(type.getPreviousValue(maxValue)) + .isEqualTo(Optional.of(maxValue - 1)); + } + + @Override + public void testNextValue() + { + long minValue = Byte.MIN_VALUE; + long maxValue = Byte.MAX_VALUE; + + assertThat(type.getNextValue(minValue)) + .isEqualTo(Optional.of(minValue + 1)); + assertThat(type.getNextValue(minValue + 1)) + .isEqualTo(Optional.of(minValue + 2)); + + assertThat(type.getNextValue(getSampleValue())) + .isEqualTo(Optional.of(112L)); + + assertThat(type.getNextValue(maxValue - 1)) + .isEqualTo(Optional.of(maxValue)); + assertThat(type.getNextValue(maxValue)) + .isEqualTo(Optional.empty()); + } } diff --git a/core/trino-main/src/test/java/io/trino/type/TestUnknownType.java b/core/trino-main/src/test/java/io/trino/type/TestUnknownType.java index 2b0c23437538..8747be3d7ac0 100644 --- a/core/trino-main/src/test/java/io/trino/type/TestUnknownType.java +++ b/core/trino-main/src/test/java/io/trino/type/TestUnknownType.java @@ -34,4 +34,16 @@ protected Object getGreaterValue(Object value) { throw new UnsupportedOperationException(); } + + @Override + public void testPreviousValue() + { + // There is no value of this type, so getPreviousValue() cannot be invoked + } + + @Override + public void testNextValue() + { + // There is no value of this type, so getNextValue() cannot be invoked + } } diff --git a/core/trino-spi/src/main/java/io/trino/spi/type/AbstractIntType.java b/core/trino-spi/src/main/java/io/trino/spi/type/AbstractIntType.java index c5b8d7b55d18..8e2513d563a6 100644 --- a/core/trino-spi/src/main/java/io/trino/spi/type/AbstractIntType.java +++ b/core/trino-spi/src/main/java/io/trino/spi/type/AbstractIntType.java @@ -83,6 +83,12 @@ public final Slice getSlice(Block block, int position) @Override public void writeLong(BlockBuilder blockBuilder, long value) + { + checkValueValid(value); + blockBuilder.writeInt((int) value).closeEntry(); + } + + protected void checkValueValid(long value) { if (value > Integer.MAX_VALUE) { throw new TrinoException(GENERIC_INTERNAL_ERROR, format("Value %d exceeds MAX_INT", value)); @@ -90,8 +96,6 @@ public void writeLong(BlockBuilder blockBuilder, long value) if (value < Integer.MIN_VALUE) { throw new TrinoException(GENERIC_INTERNAL_ERROR, format("Value %d is less than MIN_INT", value)); } - - blockBuilder.writeInt((int) value).closeEntry(); } @Override diff --git a/core/trino-spi/src/main/java/io/trino/spi/type/BigintType.java b/core/trino-spi/src/main/java/io/trino/spi/type/BigintType.java index a5c31dc7916f..8c6bd5dc61ac 100644 --- a/core/trino-spi/src/main/java/io/trino/spi/type/BigintType.java +++ b/core/trino-spi/src/main/java/io/trino/spi/type/BigintType.java @@ -59,6 +59,26 @@ public Optional getRange() return Optional.of(new Range(Long.MIN_VALUE, Long.MAX_VALUE)); } + @Override + public Optional getPreviousValue(Object object) + { + long value = (long) object; + if (value == Long.MIN_VALUE) { + return Optional.empty(); + } + return Optional.of(value - 1); + } + + @Override + public Optional getNextValue(Object object) + { + long value = (long) object; + if (value == Long.MAX_VALUE) { + return Optional.empty(); + } + return Optional.of(value + 1); + } + @Override public Optional> getDiscreteValues(Range range) { diff --git a/core/trino-spi/src/main/java/io/trino/spi/type/DateType.java b/core/trino-spi/src/main/java/io/trino/spi/type/DateType.java index 583e76a1b5c9..be74bb309a49 100644 --- a/core/trino-spi/src/main/java/io/trino/spi/type/DateType.java +++ b/core/trino-spi/src/main/java/io/trino/spi/type/DateType.java @@ -53,6 +53,28 @@ public Optional getRange() return Optional.of(new Range((long) Integer.MIN_VALUE, (long) Integer.MAX_VALUE)); } + @Override + public Optional getPreviousValue(Object object) + { + long value = (long) object; + checkValueValid(value); + if (value == Integer.MIN_VALUE) { + return Optional.empty(); + } + return Optional.of(value - 1); + } + + @Override + public Optional getNextValue(Object object) + { + long value = (long) object; + checkValueValid(value); + if (value == Integer.MAX_VALUE) { + return Optional.empty(); + } + return Optional.of(value + 1); + } + @Override @SuppressWarnings("EqualsWhichDoesntCheckParameterClass") public boolean equals(Object other) diff --git a/core/trino-spi/src/main/java/io/trino/spi/type/IntegerType.java b/core/trino-spi/src/main/java/io/trino/spi/type/IntegerType.java index 7eade34f237d..0726dabb941d 100644 --- a/core/trino-spi/src/main/java/io/trino/spi/type/IntegerType.java +++ b/core/trino-spi/src/main/java/io/trino/spi/type/IntegerType.java @@ -46,6 +46,28 @@ public Optional getRange() return Optional.of(new Range((long) Integer.MIN_VALUE, (long) Integer.MAX_VALUE)); } + @Override + public Optional getPreviousValue(Object object) + { + long value = (long) object; + checkValueValid(value); + if (value == Integer.MIN_VALUE) { + return Optional.empty(); + } + return Optional.of(value - 1); + } + + @Override + public Optional getNextValue(Object object) + { + long value = (long) object; + checkValueValid(value); + if (value == Integer.MAX_VALUE) { + return Optional.empty(); + } + return Optional.of(value + 1); + } + @Override public Optional> getDiscreteValues(Range range) { diff --git a/core/trino-spi/src/main/java/io/trino/spi/type/LongTimestampWithTimeZoneType.java b/core/trino-spi/src/main/java/io/trino/spi/type/LongTimestampWithTimeZoneType.java index c730d90a1497..9f275d02abb6 100644 --- a/core/trino-spi/src/main/java/io/trino/spi/type/LongTimestampWithTimeZoneType.java +++ b/core/trino-spi/src/main/java/io/trino/spi/type/LongTimestampWithTimeZoneType.java @@ -24,6 +24,8 @@ import io.trino.spi.function.BlockPosition; import io.trino.spi.function.ScalarOperator; +import java.util.Optional; + import static io.airlift.slice.SizeOf.SIZE_OF_LONG; import static io.trino.spi.function.OperatorType.COMPARISON_UNORDERED_LAST; import static io.trino.spi.function.OperatorType.EQUAL; @@ -33,6 +35,9 @@ import static io.trino.spi.type.DateTimeEncoding.packDateTimeWithZone; import static io.trino.spi.type.DateTimeEncoding.unpackMillisUtc; import static io.trino.spi.type.DateTimeEncoding.unpackZoneKey; +import static io.trino.spi.type.TimeZoneKey.UTC_KEY; +import static io.trino.spi.type.Timestamps.PICOSECONDS_PER_MILLISECOND; +import static io.trino.spi.type.Timestamps.rescale; import static io.trino.spi.type.TypeOperatorDeclaration.extractOperatorDeclaration; import static java.lang.String.format; import static java.lang.invoke.MethodHandles.lookup; @@ -140,6 +145,42 @@ public Object getObjectValue(ConnectorSession session, Block block, int position return SqlTimestampWithTimeZone.newInstance(getPrecision(), unpackMillisUtc(packedEpochMillis), picosOfMilli, unpackZoneKey(packedEpochMillis)); } + @Override + public Optional getPreviousValue(Object value) + { + LongTimestampWithTimeZone timestampWithTimeZone = (LongTimestampWithTimeZone) value; + long epochMillis = timestampWithTimeZone.getEpochMillis(); + int picosOfMilli = timestampWithTimeZone.getPicosOfMilli(); + picosOfMilli -= rescale(1, 0, 12 - getPrecision()); + if (picosOfMilli < 0) { + if (epochMillis == Long.MIN_VALUE) { + return Optional.empty(); + } + epochMillis--; + picosOfMilli += PICOSECONDS_PER_MILLISECOND; + } + // time zone doesn't matter for ordering + return Optional.of(LongTimestampWithTimeZone.fromEpochMillisAndFraction(epochMillis, picosOfMilli, UTC_KEY)); + } + + @Override + public Optional getNextValue(Object value) + { + LongTimestampWithTimeZone timestampWithTimeZone = (LongTimestampWithTimeZone) value; + long epochMillis = timestampWithTimeZone.getEpochMillis(); + int picosOfMilli = timestampWithTimeZone.getPicosOfMilli(); + picosOfMilli += rescale(1, 0, 12 - getPrecision()); + if (picosOfMilli >= PICOSECONDS_PER_MILLISECOND) { + if (epochMillis == Long.MAX_VALUE) { + return Optional.empty(); + } + epochMillis--; + picosOfMilli -= PICOSECONDS_PER_MILLISECOND; + } + // time zone doesn't matter for ordering + return Optional.of(LongTimestampWithTimeZone.fromEpochMillisAndFraction(epochMillis, picosOfMilli, UTC_KEY)); + } + private static long getPackedEpochMillis(Block block, int position) { return block.getLong(position, 0); diff --git a/core/trino-spi/src/main/java/io/trino/spi/type/ShortTimestampType.java b/core/trino-spi/src/main/java/io/trino/spi/type/ShortTimestampType.java index a0adf94dd762..6703b15a55ab 100644 --- a/core/trino-spi/src/main/java/io/trino/spi/type/ShortTimestampType.java +++ b/core/trino-spi/src/main/java/io/trino/spi/type/ShortTimestampType.java @@ -144,6 +144,24 @@ public Optional getRange() return Optional.of(range); } + @Override + public Optional getPreviousValue(Object value) + { + if ((long) range.getMin() == (long) value) { + return Optional.empty(); + } + return Optional.of((long) value - rescale(1_000_000, getPrecision(), 0)); + } + + @Override + public Optional getNextValue(Object value) + { + if ((long) range.getMax() == (long) value) { + return Optional.empty(); + } + return Optional.of((long) value + rescale(1_000_000, getPrecision(), 0)); + } + @ScalarOperator(EQUAL) private static boolean equalOperator(long left, long right) { diff --git a/core/trino-spi/src/main/java/io/trino/spi/type/SmallintType.java b/core/trino-spi/src/main/java/io/trino/spi/type/SmallintType.java index d116f82081f0..c81ec1a251de 100644 --- a/core/trino-spi/src/main/java/io/trino/spi/type/SmallintType.java +++ b/core/trino-spi/src/main/java/io/trino/spi/type/SmallintType.java @@ -118,6 +118,28 @@ public Optional getRange() return Optional.of(new Range((long) Short.MIN_VALUE, (long) Short.MAX_VALUE)); } + @Override + public Optional getPreviousValue(Object object) + { + long value = (long) object; + checkValueValid(value); + if (value == Short.MIN_VALUE) { + return Optional.empty(); + } + return Optional.of(value - 1); + } + + @Override + public Optional getNextValue(Object object) + { + long value = (long) object; + checkValueValid(value); + if (value == Short.MAX_VALUE) { + return Optional.empty(); + } + return Optional.of(value + 1); + } + @Override public Optional> getDiscreteValues(Range range) { @@ -143,6 +165,12 @@ public long getLong(Block block, int position) @Override public void writeLong(BlockBuilder blockBuilder, long value) + { + checkValueValid(value); + blockBuilder.writeShort((int) value).closeEntry(); + } + + private void checkValueValid(long value) { if (value > Short.MAX_VALUE) { throw new TrinoException(GENERIC_INTERNAL_ERROR, format("Value %d exceeds MAX_SHORT", value)); @@ -150,8 +178,6 @@ public void writeLong(BlockBuilder blockBuilder, long value) if (value < Short.MIN_VALUE) { throw new TrinoException(GENERIC_INTERNAL_ERROR, format("Value %d is less than MIN_SHORT", value)); } - - blockBuilder.writeShort((int) value).closeEntry(); } @Override diff --git a/core/trino-spi/src/main/java/io/trino/spi/type/TinyintType.java b/core/trino-spi/src/main/java/io/trino/spi/type/TinyintType.java index 8f4fe2cdb631..37db7bcfcac0 100644 --- a/core/trino-spi/src/main/java/io/trino/spi/type/TinyintType.java +++ b/core/trino-spi/src/main/java/io/trino/spi/type/TinyintType.java @@ -118,6 +118,28 @@ public Optional getRange() return Optional.of(new Range((long) Byte.MIN_VALUE, (long) Byte.MAX_VALUE)); } + @Override + public Optional getPreviousValue(Object object) + { + long value = (long) object; + checkValueValid(value); + if (value == Byte.MIN_VALUE) { + return Optional.empty(); + } + return Optional.of(value - 1); + } + + @Override + public Optional getNextValue(Object object) + { + long value = (long) object; + checkValueValid(value); + if (value == Byte.MAX_VALUE) { + return Optional.empty(); + } + return Optional.of(value + 1); + } + @Override public Optional> getDiscreteValues(Range range) { @@ -143,6 +165,12 @@ public long getLong(Block block, int position) @Override public void writeLong(BlockBuilder blockBuilder, long value) + { + checkValueValid(value); + blockBuilder.writeByte((int) value).closeEntry(); + } + + private void checkValueValid(long value) { if (value > Byte.MAX_VALUE) { throw new TrinoException(GENERIC_INTERNAL_ERROR, format("Value %d exceeds MAX_BYTE", value)); @@ -150,8 +178,6 @@ public void writeLong(BlockBuilder blockBuilder, long value) if (value < Byte.MIN_VALUE) { throw new TrinoException(GENERIC_INTERNAL_ERROR, format("Value %d is less than MIN_BYTE", value)); } - - blockBuilder.writeByte((int) value).closeEntry(); } @Override diff --git a/core/trino-spi/src/main/java/io/trino/spi/type/Type.java b/core/trino-spi/src/main/java/io/trino/spi/type/Type.java index 61863bf424c0..dab302cc06be 100644 --- a/core/trino-spi/src/main/java/io/trino/spi/type/Type.java +++ b/core/trino-spi/src/main/java/io/trino/spi/type/Type.java @@ -175,6 +175,38 @@ default Optional getRange() return Optional.empty(); } + /** + * Returns the maximum value that compares less than {@code value}. + *

+ * The type of the value must match {@link #getJavaType}. + * + * @throws IllegalStateException if this type is not {@link #isOrderable() orderable} + */ + default Optional getPreviousValue(Object value) + { + if (!isOrderable()) { + throw new IllegalStateException("Type is not orderable: " + this); + } + requireNonNull(value, "value is null"); + return Optional.empty(); + } + + /** + * Returns the minimum value that compares greater than {@code value}. + *

+ * The type of the value must match {@link #getJavaType}. + * + * @throws IllegalStateException if this type is not {@link #isOrderable() orderable} + */ + default Optional getNextValue(Object value) + { + if (!isOrderable()) { + throw new IllegalStateException("Type is not orderable: " + this); + } + requireNonNull(value, "value is null"); + return Optional.empty(); + } + /** * Returns a stream of discrete values inside the specified range (if supported by this type). */ diff --git a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergUtil.java b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergUtil.java index 7e65b86e56e6..7487d8cc1d0d 100644 --- a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergUtil.java +++ b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergUtil.java @@ -105,8 +105,6 @@ import static io.trino.plugin.iceberg.IcebergTableProperties.getTableLocation; import static io.trino.plugin.iceberg.PartitionFields.parsePartitionFields; import static io.trino.plugin.iceberg.PartitionFields.toPartitionFields; -import static io.trino.plugin.iceberg.TrinoTypes.getNextValue; -import static io.trino.plugin.iceberg.TrinoTypes.getPreviousValue; import static io.trino.plugin.iceberg.TypeConverter.toIcebergType; import static io.trino.plugin.iceberg.TypeConverter.toTrinoType; import static io.trino.plugin.iceberg.util.Timestamps.timestampTzFromMicros; @@ -389,14 +387,14 @@ private static boolean canEnforceRangeWithPartitioningField(PartitionField field } if (!range.isLowUnbounded()) { Object boundedValue = range.getLowBoundedValue(); - Optional adjacentValue = range.isLowInclusive() ? getPreviousValue(type, boundedValue) : getNextValue(type, boundedValue); + Optional adjacentValue = range.isLowInclusive() ? type.getPreviousValue(boundedValue) : type.getNextValue(boundedValue); if (adjacentValue.isEmpty() || yieldSamePartitioningValue(field, transform, type, boundedValue, adjacentValue.get(), targetTypeEqualOperator)) { return false; } } if (!range.isHighUnbounded()) { Object boundedValue = range.getHighBoundedValue(); - Optional adjacentValue = range.isHighInclusive() ? getNextValue(type, boundedValue) : getPreviousValue(type, boundedValue); + Optional adjacentValue = range.isHighInclusive() ? type.getNextValue(boundedValue) : type.getPreviousValue(boundedValue); if (adjacentValue.isEmpty() || yieldSamePartitioningValue(field, transform, type, boundedValue, adjacentValue.get(), targetTypeEqualOperator)) { return false; } diff --git a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/TrinoTypes.java b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/TrinoTypes.java deleted file mode 100644 index 27bf8472a80c..000000000000 --- a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/TrinoTypes.java +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.trino.plugin.iceberg; - -import io.trino.spi.type.LongTimestampWithTimeZone; -import io.trino.spi.type.TimestampType; -import io.trino.spi.type.TimestampWithTimeZoneType; -import io.trino.spi.type.Type; -import io.trino.spi.type.Type.Range; - -import java.util.Optional; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Verify.verify; -import static io.trino.spi.type.BigintType.BIGINT; -import static io.trino.spi.type.DateType.DATE; -import static io.trino.spi.type.IntegerType.INTEGER; -import static io.trino.spi.type.SmallintType.SMALLINT; -import static io.trino.spi.type.TimeZoneKey.UTC_KEY; -import static io.trino.spi.type.Timestamps.PICOSECONDS_PER_MICROSECOND; -import static io.trino.spi.type.Timestamps.PICOSECONDS_PER_MILLISECOND; -import static io.trino.spi.type.TinyintType.TINYINT; -import static java.util.Objects.requireNonNull; - -public final class TrinoTypes -{ - private TrinoTypes() {} - - /** - * Returns the maximum value that compares less than {@code value}. - *

- * The type of the value must match {@link Type#getJavaType}. - * - * @throws IllegalStateException if this type is not {@link Type#isOrderable() orderable} - */ - public static Optional getPreviousValue(Type type, Object value) - { - if (!type.isOrderable()) { - throw new IllegalArgumentException("Type is not orderable: " + type); - } - requireNonNull(value, "value is null"); - - if (type == TINYINT || type == SMALLINT || type == INTEGER || type == BIGINT) { - Range typeRange = type.getRange().orElseThrow(); - return getAdjacentValue((long) typeRange.getMin(), (long) typeRange.getMax(), (long) value, Direction.PREV); - } - - if (type == DATE) { - Range typeRange = type.getRange().orElseThrow(); - return getAdjacentValue((long) typeRange.getMin(), (long) typeRange.getMax(), (long) value, Direction.PREV); - } - - if (type instanceof TimestampType) { - // Iceberg supports timestamp only with microsecond precision - checkArgument(((TimestampType) type).getPrecision() == 6, "Unexpected type: %s", type); - Range typeRange = type.getRange().orElseThrow(); - return getAdjacentValue((long) typeRange.getMin(), (long) typeRange.getMax(), (long) value, Direction.PREV); - } - - if (type instanceof TimestampWithTimeZoneType) { - // Iceberg supports timestamp with time zone only with microsecond precision - checkArgument(((TimestampWithTimeZoneType) type).getPrecision() == 6, "Unexpected type: %s", type); - verify(type.getRange().isEmpty(), "Type %s unexpectedly returned a range", type); - LongTimestampWithTimeZone timestampTzValue = (LongTimestampWithTimeZone) value; - long epochMillis = timestampTzValue.getEpochMillis(); - int picosOfMilli = timestampTzValue.getPicosOfMilli(); - // Calculate value 1 microsecond earlier - picosOfMilli -= PICOSECONDS_PER_MICROSECOND; - if (picosOfMilli < 0) { - if (epochMillis == Long.MIN_VALUE) { - return Optional.empty(); - } - epochMillis--; - picosOfMilli += PICOSECONDS_PER_MILLISECOND; - } - // The zone doesn't matter for timestamp with time zone comparisons. Use UTC to avoid confusion. - return Optional.of(LongTimestampWithTimeZone.fromEpochMillisAndFraction(epochMillis, picosOfMilli, UTC_KEY)); - } - - return Optional.empty(); - } - - /** - * Returns the minimum value that compares greater than {@code value}. - *

- * The type of the value must match {@link Type#getJavaType}. - * - * @throws IllegalStateException if this type is not {@link Type#isOrderable() orderable} - */ - public static Optional getNextValue(Type type, Object value) - { - if (!type.isOrderable()) { - throw new IllegalArgumentException("Type is not orderable: " + type); - } - requireNonNull(value, "value is null"); - - if (type == TINYINT || type == SMALLINT || type == INTEGER || type == BIGINT) { - Range typeRange = type.getRange().orElseThrow(); - return getAdjacentValue((long) typeRange.getMin(), (long) typeRange.getMax(), (long) value, Direction.NEXT); - } - - if (type == DATE) { - Range typeRange = type.getRange().orElseThrow(); - return getAdjacentValue((long) typeRange.getMin(), (long) typeRange.getMax(), (long) value, Direction.NEXT); - } - - if (type instanceof TimestampType) { - // Iceberg supports timestamp only with microsecond precision - checkArgument(((TimestampType) type).getPrecision() == 6, "Unexpected type: %s", type); - Range typeRange = type.getRange().orElseThrow(); - return getAdjacentValue((long) typeRange.getMin(), (long) typeRange.getMax(), (long) value, Direction.NEXT); - } - - if (type instanceof TimestampWithTimeZoneType) { - // Iceberg supports timestamp with time zone only with microsecond precision - checkArgument(((TimestampWithTimeZoneType) type).getPrecision() == 6, "Unexpected type: %s", type); - verify(type.getRange().isEmpty(), "Type %s unexpectedly returned a range", type); - LongTimestampWithTimeZone timestampTzValue = (LongTimestampWithTimeZone) value; - long epochMillis = timestampTzValue.getEpochMillis(); - int picosOfMilli = timestampTzValue.getPicosOfMilli(); - // Calculate value 1 microsecond later - picosOfMilli += PICOSECONDS_PER_MICROSECOND; - if (picosOfMilli >= PICOSECONDS_PER_MILLISECOND) { - if (epochMillis == Long.MAX_VALUE) { - return Optional.empty(); - } - epochMillis++; - picosOfMilli -= PICOSECONDS_PER_MILLISECOND; - } - // The zone doesn't matter for timestamp with time zone comparisons. Use UTC to avoid confusion. - return Optional.of(LongTimestampWithTimeZone.fromEpochMillisAndFraction(epochMillis, picosOfMilli, UTC_KEY)); - } - - return Optional.empty(); - } - - private static Optional getAdjacentValue(long min, long max, long value, Direction direction) - { - return switch (direction) { - case PREV -> { - if (value == min) { - yield Optional.empty(); - } - yield Optional.of(value - 1); - } - case NEXT -> { - if (value == max) { - yield Optional.empty(); - } - yield Optional.of(value + 1); - } - }; - } - - private enum Direction - { - PREV, - NEXT - } -}