Skip to content

Commit

Permalink
Fix incorrect parsing of timestamps in cast from varchar
Browse files Browse the repository at this point in the history
Trailing characters were incorrectly being treated as a timezone and ignored.
  • Loading branch information
martint committed Sep 17, 2022
1 parent 9f0ace6 commit cfc12aa
Show file tree
Hide file tree
Showing 2 changed files with 126 additions and 4 deletions.
Expand Up @@ -24,6 +24,7 @@
import io.trino.type.DateTimes;

import java.time.DateTimeException;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.regex.Matcher;

Expand Down Expand Up @@ -85,9 +86,14 @@ public static long castToShortTimestamp(int precision, String value)
String minute = matcher.group("minute");
String second = matcher.group("second");
String fraction = matcher.group("fraction");
String timezone = matcher.group("timezone");

ZoneId zone = UTC;
long epochSecond;
try {
if (timezone != null) {
zone = ZoneId.of(timezone);
}
epochSecond = ZonedDateTime.of(
Integer.parseInt(year),
Integer.parseInt(month),
Expand All @@ -96,8 +102,9 @@ public static long castToShortTimestamp(int precision, String value)
minute == null ? 0 : Integer.parseInt(minute),
second == null ? 0 : Integer.parseInt(second),
0,
UTC)
.toEpochSecond();
zone)
.toLocalDateTime()
.toEpochSecond(UTC);
}
catch (DateTimeException e) {
throw new TrinoException(INVALID_CAST_ARGUMENT, "Value cannot be cast to timestamp: " + value, e);
Expand Down Expand Up @@ -135,9 +142,14 @@ public static LongTimestamp castToLongTimestamp(int precision, String value)
String minute = matcher.group("minute");
String second = matcher.group("second");
String fraction = matcher.group("fraction");
String timezone = matcher.group("timezone");

ZoneId zone = UTC;
long epochSecond;
try {
if (timezone != null) {
zone = ZoneId.of(timezone);
}
epochSecond = ZonedDateTime.of(
Integer.parseInt(year),
Integer.parseInt(month),
Expand All @@ -146,8 +158,9 @@ public static LongTimestamp castToLongTimestamp(int precision, String value)
minute == null ? 0 : Integer.parseInt(minute),
second == null ? 0 : Integer.parseInt(second),
0,
UTC)
.toEpochSecond();
zone)
.toLocalDateTime()
.toEpochSecond(UTC);
}
catch (DateTimeException e) {
throw new TrinoException(INVALID_CAST_ARGUMENT, "Value cannot be cast to timestamp: " + value, e);
Expand Down
Expand Up @@ -1640,6 +1640,115 @@ public void testCastFromVarchar()
assertThat(assertions.expression("CAST('-123001-05-01 12:34:56.111111111111' AS TIMESTAMP(10))")).matches("TIMESTAMP '-123001-05-01 12:34:56.1111111111'");
assertThat(assertions.expression("CAST('-123001-05-01 12:34:56.111111111111' AS TIMESTAMP(11))")).matches("TIMESTAMP '-123001-05-01 12:34:56.11111111111'");
assertThat(assertions.expression("CAST('-123001-05-01 12:34:56.111111111111' AS TIMESTAMP(12))")).matches("TIMESTAMP '-123001-05-01 12:34:56.111111111111'");

// values w/ time zone
assertThat(assertions.expression("CAST('2020-05-10 12:34:56 +01:23' AS TIMESTAMP(0))"))
.matches("TIMESTAMP '2020-05-10 12:34:56'");
assertThat(assertions.expression("CAST('2020-05-10 12:34:56.1 +01:23' AS TIMESTAMP(1))"))
.matches("TIMESTAMP '2020-05-10 12:34:56.1'");
assertThat(assertions.expression("CAST('2020-05-10 12:34:56.11 +01:23' AS TIMESTAMP(2))"))
.matches("TIMESTAMP '2020-05-10 12:34:56.11'");
assertThat(assertions.expression("CAST('2020-05-10 12:34:56.111 +01:23' AS TIMESTAMP(3))"))
.matches("TIMESTAMP '2020-05-10 12:34:56.111'");
assertThat(assertions.expression("CAST('2020-05-10 12:34:56.1111 +01:23' AS TIMESTAMP(4))"))
.matches("TIMESTAMP '2020-05-10 12:34:56.1111'");
assertThat(assertions.expression("CAST('2020-05-10 12:34:56.11111 +01:23' AS TIMESTAMP(5))"))
.matches("TIMESTAMP '2020-05-10 12:34:56.11111'");
assertThat(assertions.expression("CAST('2020-05-10 12:34:56.111111 +01:23' AS TIMESTAMP(6))"))
.matches("TIMESTAMP '2020-05-10 12:34:56.111111'");
assertThat(assertions.expression("CAST('2020-05-10 12:34:56.1111111 +01:23' AS TIMESTAMP(7))"))
.matches("TIMESTAMP '2020-05-10 12:34:56.1111111'");
assertThat(assertions.expression("CAST('2020-05-10 12:34:56.11111111 +01:23' AS TIMESTAMP(8))"))
.matches("TIMESTAMP '2020-05-10 12:34:56.11111111'");
assertThat(assertions.expression("CAST('2020-05-10 12:34:56.111111111 +01:23' AS TIMESTAMP(9))"))
.matches("TIMESTAMP '2020-05-10 12:34:56.111111111'");
assertThat(assertions.expression("CAST('2020-05-10 12:34:56.1111111111 +01:23' AS TIMESTAMP(10))"))
.matches("TIMESTAMP '2020-05-10 12:34:56.1111111111'");
assertThat(assertions.expression("CAST('2020-05-10 12:34:56.11111111111 +01:23' AS TIMESTAMP(11))"))
.matches("TIMESTAMP '2020-05-10 12:34:56.11111111111'");
assertThat(assertions.expression("CAST('2020-05-10 12:34:56.111111111111 +01:23' AS TIMESTAMP(12))"))
.matches("TIMESTAMP '2020-05-10 12:34:56.111111111111'");

assertThatThrownBy(() -> assertions.expression("CAST('2020-05-10 12:34:56.111111111111 xxx' AS TIMESTAMP(0))").evaluate())
.hasMessage("Value cannot be cast to timestamp: 2020-05-10 12:34:56.111111111111 xxx");
assertThatThrownBy(() -> assertions.expression("CAST('2020-05-10 12:34:56.111111111111 xxx' AS TIMESTAMP(1))").evaluate())
.hasMessage("Value cannot be cast to timestamp: 2020-05-10 12:34:56.111111111111 xxx");
assertThatThrownBy(() -> assertions.expression("CAST('2020-05-10 12:34:56.111111111111 xxx' AS TIMESTAMP(2))").evaluate())
.hasMessage("Value cannot be cast to timestamp: 2020-05-10 12:34:56.111111111111 xxx");
assertThatThrownBy(() -> assertions.expression("CAST('2020-05-10 12:34:56.111111111111 xxx' AS TIMESTAMP(3))").evaluate())
.hasMessage("Value cannot be cast to timestamp: 2020-05-10 12:34:56.111111111111 xxx");
assertThatThrownBy(() -> assertions.expression("CAST('2020-05-10 12:34:56.111111111111 xxx' AS TIMESTAMP(4))").evaluate())
.hasMessage("Value cannot be cast to timestamp: 2020-05-10 12:34:56.111111111111 xxx");
assertThatThrownBy(() -> assertions.expression("CAST('2020-05-10 12:34:56.111111111111 xxx' AS TIMESTAMP(5))").evaluate())
.hasMessage("Value cannot be cast to timestamp: 2020-05-10 12:34:56.111111111111 xxx");
assertThatThrownBy(() -> assertions.expression("CAST('2020-05-10 12:34:56.111111111111 xxx' AS TIMESTAMP(6))").evaluate())
.hasMessage("Value cannot be cast to timestamp: 2020-05-10 12:34:56.111111111111 xxx");
assertThatThrownBy(() -> assertions.expression("CAST('2020-05-10 12:34:56.111111111111 xxx' AS TIMESTAMP(7))").evaluate())
.hasMessage("Value cannot be cast to timestamp: 2020-05-10 12:34:56.111111111111 xxx");
assertThatThrownBy(() -> assertions.expression("CAST('2020-05-10 12:34:56.111111111111 xxx' AS TIMESTAMP(8))").evaluate())
.hasMessage("Value cannot be cast to timestamp: 2020-05-10 12:34:56.111111111111 xxx");
assertThatThrownBy(() -> assertions.expression("CAST('2020-05-10 12:34:56.111111111111 xxx' AS TIMESTAMP(9))").evaluate())
.hasMessage("Value cannot be cast to timestamp: 2020-05-10 12:34:56.111111111111 xxx");
assertThatThrownBy(() -> assertions.expression("CAST('2020-05-10 12:34:56.111111111111 xxx' AS TIMESTAMP(10))").evaluate())
.hasMessage("Value cannot be cast to timestamp: 2020-05-10 12:34:56.111111111111 xxx");
assertThatThrownBy(() -> assertions.expression("CAST('2020-05-10 12:34:56.111111111111 xxx' AS TIMESTAMP(11))").evaluate())
.hasMessage("Value cannot be cast to timestamp: 2020-05-10 12:34:56.111111111111 xxx");
assertThatThrownBy(() -> assertions.expression("CAST('2020-05-10 12:34:56.111111111111 xxx' AS TIMESTAMP(12))").evaluate())
.hasMessage("Value cannot be cast to timestamp: 2020-05-10 12:34:56.111111111111 xxx");

assertThatThrownBy(() -> assertions.expression("CAST('2020-05-10 xxx' AS TIMESTAMP(0))").evaluate())
.hasMessage("Value cannot be cast to timestamp: 2020-05-10 xxx");
assertThatThrownBy(() -> assertions.expression("CAST('2020-05-10 xxx' AS TIMESTAMP(1))").evaluate())
.hasMessage("Value cannot be cast to timestamp: 2020-05-10 xxx");
assertThatThrownBy(() -> assertions.expression("CAST('2020-05-10 xxx' AS TIMESTAMP(2))").evaluate())
.hasMessage("Value cannot be cast to timestamp: 2020-05-10 xxx");
assertThatThrownBy(() -> assertions.expression("CAST('2020-05-10 xxx' AS TIMESTAMP(3))").evaluate())
.hasMessage("Value cannot be cast to timestamp: 2020-05-10 xxx");
assertThatThrownBy(() -> assertions.expression("CAST('2020-05-10 xxx' AS TIMESTAMP(4))").evaluate())
.hasMessage("Value cannot be cast to timestamp: 2020-05-10 xxx");
assertThatThrownBy(() -> assertions.expression("CAST('2020-05-10 xxx' AS TIMESTAMP(5))").evaluate())
.hasMessage("Value cannot be cast to timestamp: 2020-05-10 xxx");
assertThatThrownBy(() -> assertions.expression("CAST('2020-05-10 xxx' AS TIMESTAMP(6))").evaluate())
.hasMessage("Value cannot be cast to timestamp: 2020-05-10 xxx");
assertThatThrownBy(() -> assertions.expression("CAST('2020-05-10 xxx' AS TIMESTAMP(7))").evaluate())
.hasMessage("Value cannot be cast to timestamp: 2020-05-10 xxx");
assertThatThrownBy(() -> assertions.expression("CAST('2020-05-10 xxx' AS TIMESTAMP(8))").evaluate())
.hasMessage("Value cannot be cast to timestamp: 2020-05-10 xxx");
assertThatThrownBy(() -> assertions.expression("CAST('2020-05-10 xxx' AS TIMESTAMP(9))").evaluate())
.hasMessage("Value cannot be cast to timestamp: 2020-05-10 xxx");
assertThatThrownBy(() -> assertions.expression("CAST('2020-05-10 xxx' AS TIMESTAMP(10))").evaluate())
.hasMessage("Value cannot be cast to timestamp: 2020-05-10 xxx");
assertThatThrownBy(() -> assertions.expression("CAST('2020-05-10 xxx' AS TIMESTAMP(11))").evaluate())
.hasMessage("Value cannot be cast to timestamp: 2020-05-10 xxx");
assertThatThrownBy(() -> assertions.expression("CAST('2020-05-10 xxx' AS TIMESTAMP(12))").evaluate())
.hasMessage("Value cannot be cast to timestamp: 2020-05-10 xxx");

assertThatThrownBy(() -> assertions.expression("CAST('2020-05-10T12:34:56' AS TIMESTAMP(0))").evaluate())
.hasMessage("Value cannot be cast to timestamp: 2020-05-10T12:34:56");
assertThatThrownBy(() -> assertions.expression("CAST('2020-05-10T12:34:56' AS TIMESTAMP(1))").evaluate())
.hasMessage("Value cannot be cast to timestamp: 2020-05-10T12:34:56");
assertThatThrownBy(() -> assertions.expression("CAST('2020-05-10T12:34:56' AS TIMESTAMP(2))").evaluate())
.hasMessage("Value cannot be cast to timestamp: 2020-05-10T12:34:56");
assertThatThrownBy(() -> assertions.expression("CAST('2020-05-10T12:34:56' AS TIMESTAMP(3))").evaluate())
.hasMessage("Value cannot be cast to timestamp: 2020-05-10T12:34:56");
assertThatThrownBy(() -> assertions.expression("CAST('2020-05-10T12:34:56' AS TIMESTAMP(4))").evaluate())
.hasMessage("Value cannot be cast to timestamp: 2020-05-10T12:34:56");
assertThatThrownBy(() -> assertions.expression("CAST('2020-05-10T12:34:56' AS TIMESTAMP(5))").evaluate())
.hasMessage("Value cannot be cast to timestamp: 2020-05-10T12:34:56");
assertThatThrownBy(() -> assertions.expression("CAST('2020-05-10T12:34:56' AS TIMESTAMP(6))").evaluate())
.hasMessage("Value cannot be cast to timestamp: 2020-05-10T12:34:56");
assertThatThrownBy(() -> assertions.expression("CAST('2020-05-10T12:34:56' AS TIMESTAMP(7))").evaluate())
.hasMessage("Value cannot be cast to timestamp: 2020-05-10T12:34:56");
assertThatThrownBy(() -> assertions.expression("CAST('2020-05-10T12:34:56' AS TIMESTAMP(8))").evaluate())
.hasMessage("Value cannot be cast to timestamp: 2020-05-10T12:34:56");
assertThatThrownBy(() -> assertions.expression("CAST('2020-05-10T12:34:56' AS TIMESTAMP(9))").evaluate())
.hasMessage("Value cannot be cast to timestamp: 2020-05-10T12:34:56");
assertThatThrownBy(() -> assertions.expression("CAST('2020-05-10T12:34:56' AS TIMESTAMP(10))").evaluate())
.hasMessage("Value cannot be cast to timestamp: 2020-05-10T12:34:56");
assertThatThrownBy(() -> assertions.expression("CAST('2020-05-10T12:34:56' AS TIMESTAMP(11))").evaluate())
.hasMessage("Value cannot be cast to timestamp: 2020-05-10T12:34:56");
assertThatThrownBy(() -> assertions.expression("CAST('2020-05-10T12:34:56' AS TIMESTAMP(12))").evaluate())
.hasMessage("Value cannot be cast to timestamp: 2020-05-10T12:34:56");
}

@Test
Expand Down

0 comments on commit cfc12aa

Please sign in to comment.