This is an automated email from the ASF dual-hosted git repository.
lzljs3620320 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-paimon-presto.git
The following commit(s) were added to refs/heads/main by this push:
new 0127a21 fix-predicate-timestamp-and-decimal (#31)
0127a21 is described below
commit 0127a211e7898e83002332ce57072a6a37fec182
Author: chendapao <[email protected]>
AuthorDate: Fri Jan 19 10:45:10 2024 +0800
fix-predicate-timestamp-and-decimal (#31)
---
.../paimon/presto/PrestoFilterConverter.java | 55 +++--
.../apache/paimon/presto/PrestoPageSourceBase.java | 9 +-
.../org/apache/paimon/presto/TestPrestoITCase.java | 225 ++++++++++++++++++++-
3 files changed, 261 insertions(+), 28 deletions(-)
diff --git
a/paimon-presto-common/src/main/java/org/apache/paimon/presto/PrestoFilterConverter.java
b/paimon-presto-common/src/main/java/org/apache/paimon/presto/PrestoFilterConverter.java
index a2064d8..9515d62 100644
---
a/paimon-presto-common/src/main/java/org/apache/paimon/presto/PrestoFilterConverter.java
+++
b/paimon-presto-common/src/main/java/org/apache/paimon/presto/PrestoFilterConverter.java
@@ -19,9 +19,10 @@
package org.apache.paimon.presto;
import org.apache.paimon.data.BinaryString;
+import org.apache.paimon.data.Decimal;
+import org.apache.paimon.data.Timestamp;
import org.apache.paimon.predicate.Predicate;
import org.apache.paimon.predicate.PredicateBuilder;
-import org.apache.paimon.shade.guava30.com.google.common.base.Preconditions;
import org.apache.paimon.types.RowType;
import com.facebook.presto.common.predicate.Domain;
@@ -38,20 +39,23 @@ import com.facebook.presto.common.type.DoubleType;
import com.facebook.presto.common.type.IntegerType;
import com.facebook.presto.common.type.MapType;
import com.facebook.presto.common.type.RealType;
+import com.facebook.presto.common.type.SqlTimestampWithTimeZone;
import com.facebook.presto.common.type.TimeType;
import com.facebook.presto.common.type.TimestampType;
+import com.facebook.presto.common.type.TimestampWithTimeZoneType;
import com.facebook.presto.common.type.Type;
import com.facebook.presto.common.type.VarbinaryType;
import com.facebook.presto.common.type.VarcharType;
import io.airlift.slice.Slice;
import java.math.BigDecimal;
+import java.time.Instant;
+import java.time.ZoneId;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
-import java.util.concurrent.TimeUnit;
/** Presto filter to Paimon predicate. */
public class PrestoFilterConverter {
@@ -208,8 +212,23 @@ public class PrestoFilterConverter {
return Math.toIntExact(((Long) prestoNativeValue));
}
- if (type instanceof TimestampType || type instanceof TimeType) {
- return TimeUnit.MILLISECONDS.toMicros((Long) prestoNativeValue);
+ if (type instanceof TimeType) {
+ return (int) ((long) prestoNativeValue / 1_000);
+ }
+
+ if (type instanceof TimestampType) {
+ return Timestamp.fromLocalDateTime(
+ Instant.ofEpochMilli((Long) prestoNativeValue)
+ .atZone(ZoneId.systemDefault())
+ .toLocalDateTime());
+ }
+
+ if (type instanceof TimestampWithTimeZoneType) {
+ if (prestoNativeValue instanceof Long) {
+ return prestoNativeValue;
+ }
+ return Timestamp.fromEpochMillis(
+ ((SqlTimestampWithTimeZone)
prestoNativeValue).getMillisUtc());
}
if (type instanceof VarcharType || type instanceof CharType) {
@@ -221,23 +240,21 @@ public class PrestoFilterConverter {
}
if (type instanceof DecimalType) {
+ // Refer to trino.
DecimalType decimalType = (DecimalType) type;
- Object value =
- Objects.requireNonNull(
- prestoNativeValue, "The prestoNativeValue must be
non-null");
- if (Decimals.isShortDecimal(decimalType)) {
- Preconditions.checkArgument(
- value instanceof Long,
- "A short decimal should be represented by a Long value
but was %s",
- value.getClass().getName());
- return BigDecimal.valueOf((long)
value).movePointLeft(decimalType.getScale());
+ BigDecimal bigDecimal;
+ if (prestoNativeValue instanceof Long) {
+ bigDecimal =
+ BigDecimal.valueOf((long) prestoNativeValue)
+ .movePointLeft(decimalType.getScale());
+ } else {
+ bigDecimal =
+ new BigDecimal(
+ Decimals.decodeUnscaledValue((Slice)
prestoNativeValue),
+ decimalType.getScale());
}
- Preconditions.checkArgument(
- value instanceof Slice,
- "A long decimal should be represented by a Slice value but
was %s",
- value.getClass().getName());
- return new BigDecimal(
- Decimals.decodeUnscaledValue((Slice) value),
decimalType.getScale());
+ return Decimal.fromBigDecimal(
+ bigDecimal, decimalType.getPrecision(),
decimalType.getScale());
}
throw new UnsupportedOperationException("Unsupported type: " + type);
diff --git
a/paimon-presto-common/src/main/java/org/apache/paimon/presto/PrestoPageSourceBase.java
b/paimon-presto-common/src/main/java/org/apache/paimon/presto/PrestoPageSourceBase.java
index 8df72fb..a56de27 100644
---
a/paimon-presto-common/src/main/java/org/apache/paimon/presto/PrestoPageSourceBase.java
+++
b/paimon-presto-common/src/main/java/org/apache/paimon/presto/PrestoPageSourceBase.java
@@ -54,6 +54,7 @@ import javax.annotation.Nullable;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.math.BigDecimal;
+import java.time.ZoneId;
import java.util.ArrayList;
import java.util.List;
@@ -206,7 +207,13 @@ public abstract class PrestoPageSourceBase implements
ConnectorPageSource {
prestoType.writeLong(
output, encodeShortScaledValue(decimal,
decimalType.getScale()));
} else if (prestoType.equals(TIMESTAMP)) {
- prestoType.writeLong(output, ((Timestamp)
value).toSQLTimestamp().getTime());
+ prestoType.writeLong(
+ output,
+ ((Timestamp) value)
+ .toLocalDateTime()
+ .atZone(ZoneId.systemDefault())
+ .toInstant()
+ .toEpochMilli());
} else if (prestoType.equals(TIME)) {
prestoType.writeLong(output, (int) value * 1_000);
} else {
diff --git
a/paimon-presto-common/src/test/java/org/apache/paimon/presto/TestPrestoITCase.java
b/paimon-presto-common/src/test/java/org/apache/paimon/presto/TestPrestoITCase.java
index b740e6c..0a0f0cb 100644
---
a/paimon-presto-common/src/test/java/org/apache/paimon/presto/TestPrestoITCase.java
+++
b/paimon-presto-common/src/test/java/org/apache/paimon/presto/TestPrestoITCase.java
@@ -41,19 +41,26 @@ import org.apache.paimon.types.RowType;
import org.apache.paimon.types.TimestampType;
import org.apache.paimon.types.VarCharType;
+import com.facebook.presto.common.type.TimeZoneKey;
import com.facebook.presto.testing.MaterializedResult;
import com.facebook.presto.testing.QueryRunner;
import com.facebook.presto.tests.DistributedQueryRunner;
+import org.testng.annotations.AfterTest;
+import org.testng.annotations.BeforeSuite;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
+import java.io.IOException;
import java.math.BigDecimal;
import java.nio.file.Files;
+import java.time.Instant;
import java.time.LocalDateTime;
+import java.time.ZoneId;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
+import java.util.TimeZone;
import java.util.UUID;
import static com.facebook.airlift.testing.Closeables.closeAllSuppress;
@@ -158,7 +165,9 @@ public class TestPrestoITCase {
Path tablePath5 = new Path(warehouse, "default.db/test_timestamp");
RowType rowType =
new RowType(
- Collections.singletonList(new DataField(0, "ts",
new TimestampType())));
+ Arrays.asList(
+ new DataField(0, "ts", new
TimestampType()),
+ new DataField(1, "ts_long_0", new
TimestampType())));
new SchemaManager(LocalFileIO.create(), tablePath5)
.createTable(
new Schema(
@@ -173,7 +182,11 @@ public class TestPrestoITCase {
writer.write(
GenericRow.of(
Timestamp.fromLocalDateTime(
-
LocalDateTime.parse("2023-01-01T01:01:01.123"))));
+
LocalDateTime.parse("2023-01-01T01:01:01.123")),
+ Timestamp.fromLocalDateTime(
+ Instant.ofEpochMilli(1672534861123L)
+ .atZone(ZoneId.systemDefault())
+ .toLocalDateTime()))); //
2023-01-01T01:01:01.123 UTC
commit.commit(0, writer.prepareCommit(true, 0));
}
@@ -207,7 +220,13 @@ public class TestPrestoITCase {
try {
queryRunner =
DistributedQueryRunner.builder(
-
testSessionBuilder().setCatalog(CATALOG).setSchema(DB).build())
+ testSessionBuilder()
+ .setTimeZoneKey(
+ TimeZoneKey.getTimeZoneKey(
+
ZoneId.systemDefault().getId()))
+ .setCatalog(CATALOG)
+ .setSchema(DB)
+ .build())
.build();
queryRunner.installPlugin(new PrestoPlugin());
Map<String, String> options = new HashMap<>();
@@ -232,11 +251,23 @@ public class TestPrestoITCase {
return new SimpleTableTestHelper(tablePath, rowType);
}
+ @BeforeSuite
+ public void setup() throws Exception {
+ // Change the default time zone for presto-tests, like Trino.
+ TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
+ }
+
@BeforeTest
public void init() throws Exception {
queryRunner = createQueryRunner();
}
+ @AfterTest
+ public void clear() throws IOException {
+ // TODO Delete default.db
+ queryRunner.close();
+ }
+
@Test
public void testComplexTypes() throws Exception {
assertThat(sql("SELECT * FROM paimon.default.t4")).isEqualTo("[[1,
{1=2}]]");
@@ -279,12 +310,12 @@ public class TestPrestoITCase {
.isEqualTo("[[1, 1, 3, 3], [2, 3, 3, 3]]");
}
- // Due to the inconsistency between the testing behavior and the real
production environment,
- // we are temporarily disabling timestamp testing here.
- @Test(enabled = false)
+ @Test
public void testTimestampFormat() throws Exception {
- assertThat(sql("SELECT ts FROM paimon.default.test_timestamp"))
- .isEqualTo("[[2023-01-01T01:01:01.123]]");
+ assertThat(
+ sql(
+ "SELECT ts, format_datetime(ts, 'yyyy-MM-dd
HH:mm:ss') FROM paimon.default.test_timestamp"))
+ .isEqualTo("[[2023-01-01T01:01:01.123, 2023-01-01 01:01:01]]");
}
@Test
@@ -293,6 +324,184 @@ public class TestPrestoITCase {
.isEqualTo("[[10000000000, 123.456]]");
}
+ @Test
+ public void testTimestampPredicateWithTimezone() throws Exception {
+ // Pacific/Apia
+ assertThat(
+ sql(
+ "SELECT ts, ts_long_0 FROM
paimon.default.test_timestamp "
+ + "where ts = TIMESTAMP '2023-01-01
01:01:01.123 Pacific/Apia'"))
+ .isEqualTo("[]");
+
+ // UTC
+ assertThat(
+ sql(
+ "SELECT ts, ts_long_0 FROM
paimon.default.test_timestamp "
+ + "where ts = TIMESTAMP '2023-01-01
01:01:01.123 UTC'"))
+ .isEqualTo("[[2023-01-01T01:01:01.123,
2023-01-01T01:01:01.123]]");
+ }
+
+ @Test
+ public void testTimestampPredicateEq() throws Exception {
+ // In UT 1672534861123 is 2023-01-01T01:01:01.123 UTC.
+
+ assertThat(
+ sql(
+ "SELECT ts, ts_long_0 FROM
paimon.default.test_timestamp "
+ + "where ts = TIMESTAMP '2023-01-01
01:01:01.123'"))
+ .isEqualTo("[[2023-01-01T01:01:01.123,
2023-01-01T01:01:01.123]]");
+
+ assertThat(
+ sql(
+ "SELECT ts, ts_long_0 FROM
paimon.default.test_timestamp "
+ + "where ts = TIMESTAMP '2023-01-01
01:01:01.123'"))
+ .isEqualTo("[[2023-01-01T01:01:01.123,
2023-01-01T01:01:01.123]]");
+
+ assertThat(
+ sql(
+ "SELECT ts, ts_long_0 FROM
paimon.default.test_timestamp "
+ + "WHERE ts_long_0 = date_add("
+ + "'millisecond', "
+ + "CAST(1672534861123 % 1000 AS
INTEGER), "
+ + "from_unixtime(CAST(1672534861123 /
1000 AS BIGINT))"
+ + ")"))
+ .isEqualTo("[[2023-01-01T01:01:01.123,
2023-01-01T01:01:01.123]]");
+
+ assertThat(
+ sql(
+ "SELECT ts, ts_long_0 FROM
paimon.default.test_timestamp "
+ + "WHERE ts = TIMESTAMP '2023-01-01
01:01:01.123' "
+ + "AND ts_long_0 = date_add("
+ + "'millisecond', "
+ + "CAST(1672534861123 % 1000 AS
INTEGER), "
+ + "from_unixtime(CAST(1672534861123 /
1000 AS BIGINT)))"))
+ .isEqualTo("[[2023-01-01T01:01:01.123,
2023-01-01T01:01:01.123]]");
+ }
+
+ @Test
+ public void testTimestampPredicate() throws Exception {
+ // Test gt and gte.
+ assertThat(
+ sql(
+ "SELECT ts FROM paimon.default.test_timestamp "
+ + "where ts > TIMESTAMP '2023-01-01
01:01:01'"))
+ .isEqualTo("[[2023-01-01T01:01:01.123]]");
+
+ assertThat(
+ sql(
+ "SELECT ts FROM paimon.default.test_timestamp "
+ + "where ts >= TIMESTAMP '2023-01-01
01:01:01.123'"))
+ .isEqualTo("[[2023-01-01T01:01:01.123]]");
+
+ // Test lt and lte.
+ assertThat(
+ sql(
+ "SELECT ts FROM paimon.default.test_timestamp "
+ + "where ts < TIMESTAMP '2023-01-01
01:01:02'"))
+ .isEqualTo("[[2023-01-01T01:01:01.123]]");
+
+ assertThat(
+ sql(
+ "SELECT ts FROM paimon.default.test_timestamp "
+ + "where ts <= TIMESTAMP '2023-01-01
01:01:01.123'"))
+ .isEqualTo("[[2023-01-01T01:01:01.123]]");
+
+ // Test gt and lt.
+ assertThat(
+ sql(
+ "SELECT ts FROM paimon.default.test_timestamp "
+ + "where ts > TIMESTAMP '2023-01-01
01:01:00' "
+ + "and ts < TIMESTAMP '2023-01-01
01:01:02'"))
+ .isEqualTo("[[2023-01-01T01:01:01.123]]");
+
+ // Test gt and lte.
+ assertThat(
+ sql(
+ "SELECT ts FROM paimon.default.test_timestamp "
+ + "where ts > TIMESTAMP '2023-01-01
01:01:00' "
+ + "and ts <= TIMESTAMP '2023-01-01
01:01:01.123'"))
+ .isEqualTo("[[2023-01-01T01:01:01.123]]");
+
+ // Test gte and lte.
+ assertThat(
+ sql(
+ "SELECT ts FROM paimon.default.test_timestamp "
+ + "where ts >= TIMESTAMP '2023-01-01
01:01:01.123' "
+ + "and ts <= TIMESTAMP '2023-01-01
01:01:01.123'"))
+ .isEqualTo("[[2023-01-01T01:01:01.123]]");
+
+ // Test gte and lt.
+ assertThat(
+ sql(
+ "SELECT ts FROM paimon.default.test_timestamp "
+ + "where ts >= TIMESTAMP '2023-01-01
01:01:01' "
+ + "and ts < TIMESTAMP '2023-01-01
01:01:02'"))
+ .isEqualTo("[[2023-01-01T01:01:01.123]]");
+ }
+
+ @Test
+ public void testDecimalPredicate() throws Exception {
+ // Test eq.
+ assertThat(sql("SELECT c2 FROM paimon.default.test_decimal where c2 =
123.456"))
+ .isEqualTo("[[123.456]]");
+
+ assertThat(sql("SELECT c1 FROM paimon.default.test_decimal where c1 =
10000000000"))
+ .isEqualTo("[[10000000000]]");
+
+ // Test gt and gte.
+ assertThat(sql("SELECT c2 FROM paimon.default.test_decimal where c2 >
123"))
+ .isEqualTo("[[123.456]]");
+
+ assertThat(sql("SELECT c2 FROM paimon.default.test_decimal where c2 >
123.455"))
+ .isEqualTo("[[123.456]]");
+
+ assertThat(sql("SELECT c2 FROM paimon.default.test_decimal where c2 >=
123"))
+ .isEqualTo("[[123.456]]");
+
+ assertThat(sql("SELECT c2 FROM paimon.default.test_decimal where c2 >=
123.456"))
+ .isEqualTo("[[123.456]]");
+
+ assertThat(sql("SELECT c1 FROM paimon.default.test_decimal where c1 >=
10000000000"))
+ .isEqualTo("[[10000000000]]");
+
+ // Test lt and lte.
+ assertThat(sql("SELECT c2 FROM paimon.default.test_decimal where c2 <
124"))
+ .isEqualTo("[[123.456]]");
+
+ assertThat(sql("SELECT c2 FROM paimon.default.test_decimal where c2 <
123.457"))
+ .isEqualTo("[[123.456]]");
+
+ assertThat(sql("SELECT c2 FROM paimon.default.test_decimal where c2 <=
124"))
+ .isEqualTo("[[123.456]]");
+
+ assertThat(sql("SELECT c2 FROM paimon.default.test_decimal where c2 <=
123.457"))
+ .isEqualTo("[[123.456]]");
+
+ assertThat(sql("SELECT c1 FROM paimon.default.test_decimal where c1 <=
10000000000"))
+ .isEqualTo("[[10000000000]]");
+
+ // Test gt and lt.
+ assertThat(sql("SELECT c2 FROM paimon.default.test_decimal where c2 >
123 and c2 < 666"))
+ .isEqualTo("[[123.456]]");
+
+ // Test gt and lte.
+ assertThat(sql("SELECT c2 FROM paimon.default.test_decimal where c2 >
123 and c2 <= 666"))
+ .isEqualTo("[[123.456]]");
+
+ // Test gte and lte.
+ assertThat(sql("SELECT c2 FROM paimon.default.test_decimal where c2 >=
123 and c2 <= 666"))
+ .isEqualTo("[[123.456]]");
+
+ // Test gte and lt.
+ assertThat(sql("SELECT c2 FROM paimon.default.test_decimal where c2 >=
123 and c2 < 666"))
+ .isEqualTo("[[123.456]]");
+
+ assertThat(
+ sql(
+ "SELECT c1 FROM paimon.default.test_decimal
where c1 >= 10000000000 and c1 < 10000000001"))
+ .isEqualTo("[[10000000000]]");
+ }
+
private String sql(String sql) throws Exception {
MaterializedResult result = queryRunner.execute(sql);
return result.getMaterializedRows().toString();