Skip to content

Commit

Permalink
TRY should handle invalid value error in cast VACHAR as TIMESTAMP
Browse files Browse the repository at this point in the history
  • Loading branch information
takezoe committed Mar 2, 2022
1 parent 33277e7 commit 6d5d136
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import io.trino.spi.type.LongTimestamp;
import io.trino.type.DateTimes;

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

Expand Down Expand Up @@ -74,7 +75,7 @@ public static long castToShortTimestamp(int precision, String value)

Matcher matcher = DateTimes.DATETIME_PATTERN.matcher(value);
if (!matcher.matches()) {
throw new IllegalArgumentException("Invalid timestamp: " + value);
throw new TrinoException(INVALID_CAST_ARGUMENT, "Value cannot be cast to timestamp: " + value);
}

String year = matcher.group("year");
Expand All @@ -85,16 +86,22 @@ public static long castToShortTimestamp(int precision, String value)
String second = matcher.group("second");
String fraction = matcher.group("fraction");

long epochSecond = ZonedDateTime.of(
Integer.parseInt(year),
Integer.parseInt(month),
Integer.parseInt(day),
hour == null ? 0 : Integer.parseInt(hour),
minute == null ? 0 : Integer.parseInt(minute),
second == null ? 0 : Integer.parseInt(second),
0,
UTC)
.toEpochSecond();
long epochSecond;
try {
epochSecond = ZonedDateTime.of(
Integer.parseInt(year),
Integer.parseInt(month),
Integer.parseInt(day),
hour == null ? 0 : Integer.parseInt(hour),
minute == null ? 0 : Integer.parseInt(minute),
second == null ? 0 : Integer.parseInt(second),
0,
UTC)
.toEpochSecond();
}
catch (DateTimeException e) {
throw new TrinoException(INVALID_CAST_ARGUMENT, "Value cannot be cast to timestamp: " + value, e);
}

int actualPrecision = 0;
long fractionValue = 0;
Expand All @@ -118,7 +125,7 @@ public static LongTimestamp castToLongTimestamp(int precision, String value)

Matcher matcher = DateTimes.DATETIME_PATTERN.matcher(value);
if (!matcher.matches()) {
throw new IllegalArgumentException("Invalid timestamp: " + value);
throw new TrinoException(INVALID_CAST_ARGUMENT, "Value cannot be cast to timestamp: " + value);
}

String year = matcher.group("year");
Expand All @@ -129,16 +136,22 @@ public static LongTimestamp castToLongTimestamp(int precision, String value)
String second = matcher.group("second");
String fraction = matcher.group("fraction");

long epochSecond = ZonedDateTime.of(
Integer.parseInt(year),
Integer.parseInt(month),
Integer.parseInt(day),
hour == null ? 0 : Integer.parseInt(hour),
minute == null ? 0 : Integer.parseInt(minute),
second == null ? 0 : Integer.parseInt(second),
0,
UTC)
.toEpochSecond();
long epochSecond;
try {
epochSecond = ZonedDateTime.of(
Integer.parseInt(year),
Integer.parseInt(month),
Integer.parseInt(day),
hour == null ? 0 : Integer.parseInt(hour),
minute == null ? 0 : Integer.parseInt(minute),
second == null ? 0 : Integer.parseInt(second),
0,
UTC)
.toEpochSecond();
}
catch (DateTimeException e) {
throw new TrinoException(INVALID_CAST_ARGUMENT, "Value cannot be cast to timestamp: " + value, e);
}

int actualPrecision = 0;
long fractionValue = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@
import io.trino.spi.type.LongTimestampWithTimeZone;
import io.trino.type.DateTimes;

import java.time.DateTimeException;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.zone.ZoneRulesException;
import java.util.function.Function;
import java.util.regex.Matcher;

Expand Down Expand Up @@ -86,7 +88,7 @@ private static long toShort(int precision, String value, Function<String, ZoneId

Matcher matcher = DateTimes.DATETIME_PATTERN.matcher(value);
if (!matcher.matches()) {
throw new IllegalArgumentException("Invalid timestamp: " + value);
throw new TrinoException(INVALID_CAST_ARGUMENT, "Value cannot be cast to timestamp: " + value);
}

String year = matcher.group("year");
Expand All @@ -98,17 +100,25 @@ private static long toShort(int precision, String value, Function<String, ZoneId
String fraction = matcher.group("fraction");
String timezone = matcher.group("timezone");

ZoneId zone = zoneId.apply(timezone);
long epochSecond = ZonedDateTime.of(
Integer.parseInt(year),
Integer.parseInt(month),
Integer.parseInt(day),
hour == null ? 0 : Integer.parseInt(hour),
minute == null ? 0 : Integer.parseInt(minute),
second == null ? 0 : Integer.parseInt(second),
0,
zone)
.toEpochSecond();
ZoneId zone;
long epochSecond;

try {
zone = zoneId.apply(timezone);
epochSecond = ZonedDateTime.of(
Integer.parseInt(year),
Integer.parseInt(month),
Integer.parseInt(day),
hour == null ? 0 : Integer.parseInt(hour),
minute == null ? 0 : Integer.parseInt(minute),
second == null ? 0 : Integer.parseInt(second),
0,
zone)
.toEpochSecond();
}
catch (DateTimeException e) {
throw new TrinoException(INVALID_CAST_ARGUMENT, "Value cannot be cast to timestamp: " + value, e);
}

int actualPrecision = 0;
long fractionValue = 0;
Expand All @@ -131,7 +141,7 @@ private static LongTimestampWithTimeZone toLong(int precision, String value, Fun

Matcher matcher = DateTimes.DATETIME_PATTERN.matcher(value);
if (!matcher.matches()) {
throw new IllegalArgumentException("Invalid timestamp: " + value);
throw new TrinoException(INVALID_CAST_ARGUMENT, "Value cannot be cast to timestamp: " + value);
}

String year = matcher.group("year");
Expand All @@ -143,17 +153,25 @@ private static LongTimestampWithTimeZone toLong(int precision, String value, Fun
String fraction = matcher.group("fraction");
String timezone = matcher.group("timezone");

ZoneId zone = zoneId.apply(timezone);
long epochSecond = ZonedDateTime.of(
Integer.parseInt(year),
Integer.parseInt(month),
Integer.parseInt(day),
hour == null ? 0 : Integer.parseInt(hour),
minute == null ? 0 : Integer.parseInt(minute),
second == null ? 0 : Integer.parseInt(second),
0,
zone)
.toEpochSecond();
ZoneId zone;
long epochSecond;

try {
zone = zoneId.apply(timezone);
epochSecond = ZonedDateTime.of(
Integer.parseInt(year),
Integer.parseInt(month),
Integer.parseInt(day),
hour == null ? 0 : Integer.parseInt(hour),
minute == null ? 0 : Integer.parseInt(minute),
second == null ? 0 : Integer.parseInt(second),
0,
zone)
.toEpochSecond();
}
catch (DateTimeException e) {
throw new TrinoException(INVALID_CAST_ARGUMENT, "Value cannot be cast to timestamp: " + value, e);
}

int actualPrecision = 0;
long fractionValue = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@
import static io.trino.spi.type.DoubleType.DOUBLE;
import static io.trino.spi.type.IntegerType.INTEGER;
import static io.trino.spi.type.SqlDecimal.decimal;
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.VarcharType.createVarcharType;
import static io.trino.type.JsonType.JSON;
import static io.trino.type.UnknownType.UNKNOWN;
Expand Down Expand Up @@ -70,6 +72,9 @@ public void testExceptions()
assertFunction(createTryExpression("1/0"), INTEGER, null);
assertFunction(createTryExpression("JSON_PARSE('INVALID')"), JSON, null);
assertFunction(createTryExpression("CAST(NULL AS INTEGER)"), INTEGER, null);
assertFunction(createTryExpression("CAST('0000-00-01' AS TIMESTAMP)"), TIMESTAMP_MILLIS, null);
assertFunction(createTryExpression("CAST('0000-01-00' AS TIMESTAMP)"), TIMESTAMP_MILLIS, null);
assertFunction(createTryExpression("CAST('0000-01-01 ABC' AS TIMESTAMP WITH TIME ZONE)"), TIMESTAMP_TZ_MILLIS, null);
assertFunction(createTryExpression("ABS(-9223372036854775807 - 1)"), BIGINT, null);

// Exceptions that should not be suppressed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2671,6 +2671,36 @@ public void testAtTimeZone()
assertThat(assertions.expression("TIMESTAMP '2020-05-01 12:34:56.123456789123' AT TIME ZONE INTERVAL '10' HOUR", session)).matches("TIMESTAMP '2020-05-01 09:34:56.123456789123 +10:00'");
}

@Test
public void testCastInvalidTimestamp()
{
assertThatThrownBy(() -> assertions.expression("CAST('ABC' AS TIMESTAMP)"))
.hasMessage("Value cannot be cast to timestamp: ABC");
assertThatThrownBy(() -> assertions.expression("CAST('2022-01-00 00:00:00' AS TIMESTAMP)"))
.hasMessage("Value cannot be cast to timestamp: 2022-01-00 00:00:00");
assertThatThrownBy(() -> assertions.expression("CAST('2022-00-01 00:00:00' AS TIMESTAMP)"))
.hasMessage("Value cannot be cast to timestamp: 2022-00-01 00:00:00");
assertThatThrownBy(() -> assertions.expression("CAST('2022-01-01 25:00:00' AS TIMESTAMP)"))
.hasMessage("Value cannot be cast to timestamp: 2022-01-01 25:00:00");
assertThatThrownBy(() -> assertions.expression("CAST('2022-01-01 00:61:00' AS TIMESTAMP)"))
.hasMessage("Value cannot be cast to timestamp: 2022-01-01 00:61:00");
assertThatThrownBy(() -> assertions.expression("CAST('2022-01-01 00:00:61' AS TIMESTAMP)"))
.hasMessage("Value cannot be cast to timestamp: 2022-01-01 00:00:61");

assertThatThrownBy(() -> assertions.expression("CAST('ABC' AS TIMESTAMP(12))"))
.hasMessage("Value cannot be cast to timestamp: ABC");
assertThatThrownBy(() -> assertions.expression("CAST('2022-01-00 00:00:00' AS TIMESTAMP(12))"))
.hasMessage("Value cannot be cast to timestamp: 2022-01-00 00:00:00");
assertThatThrownBy(() -> assertions.expression("CAST('2022-00-01 00:00:00' AS TIMESTAMP(12))"))
.hasMessage("Value cannot be cast to timestamp: 2022-00-01 00:00:00");
assertThatThrownBy(() -> assertions.expression("CAST('2022-01-01 25:00:00' AS TIMESTAMP(12))"))
.hasMessage("Value cannot be cast to timestamp: 2022-01-01 25:00:00");
assertThatThrownBy(() -> assertions.expression("CAST('2022-01-01 00:61:00' AS TIMESTAMP(12))"))
.hasMessage("Value cannot be cast to timestamp: 2022-01-01 00:61:00");
assertThatThrownBy(() -> assertions.expression("CAST('2022-01-01 00:00:61' AS TIMESTAMP(12))"))
.hasMessage("Value cannot be cast to timestamp: 2022-01-01 00:00:61");
}

private static BiFunction<Session, QueryRunner, Object> timestamp(int precision, int year, int month, int day, int hour, int minute, int second, long picoOfSecond)
{
return (session, queryRunner) -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2424,6 +2424,40 @@ public void testJoin()
.matches("VALUES BIGINT '1'");
}

@Test
public void testCastInvalidTimestamp()
{
assertThatThrownBy(() -> assertions.expression("CAST('ABC' AS TIMESTAMP WITH TIME ZONE)"))
.hasMessage("Value cannot be cast to timestamp: ABC");
assertThatThrownBy(() -> assertions.expression("CAST('2022-01-00 00:00:00 UTC' AS TIMESTAMP WITH TIME ZONE)"))
.hasMessage("Value cannot be cast to timestamp: 2022-01-00 00:00:00 UTC");
assertThatThrownBy(() -> assertions.expression("CAST('2022-00-01 00:00:00 UTC' AS TIMESTAMP WITH TIME ZONE)"))
.hasMessage("Value cannot be cast to timestamp: 2022-00-01 00:00:00 UTC");
assertThatThrownBy(() -> assertions.expression("CAST('2022-01-01 25:00:00 UTC' AS TIMESTAMP WITH TIME ZONE)"))
.hasMessage("Value cannot be cast to timestamp: 2022-01-01 25:00:00 UTC");
assertThatThrownBy(() -> assertions.expression("CAST('2022-01-01 00:61:00 UTC' AS TIMESTAMP WITH TIME ZONE)"))
.hasMessage("Value cannot be cast to timestamp: 2022-01-01 00:61:00 UTC");
assertThatThrownBy(() -> assertions.expression("CAST('2022-01-01 00:00:61 UTC' AS TIMESTAMP WITH TIME ZONE)"))
.hasMessage("Value cannot be cast to timestamp: 2022-01-01 00:00:61 UTC");
assertThatThrownBy(() -> assertions.expression("CAST('2022-01-01 00:00:00 ABC' AS TIMESTAMP WITH TIME ZONE)"))
.hasMessage("Value cannot be cast to timestamp: 2022-01-01 00:00:00 ABC");

assertThatThrownBy(() -> assertions.expression("CAST('ABC' AS TIMESTAMP(12))"))
.hasMessage("Value cannot be cast to timestamp: ABC");
assertThatThrownBy(() -> assertions.expression("CAST('2022-01-00 00:00:00 UTC' AS TIMESTAMP(12) WITH TIME ZONE)"))
.hasMessage("Value cannot be cast to timestamp: 2022-01-00 00:00:00 UTC");
assertThatThrownBy(() -> assertions.expression("CAST('2022-00-01 00:00:00 UTC' AS TIMESTAMP(12) WITH TIME ZONE)"))
.hasMessage("Value cannot be cast to timestamp: 2022-00-01 00:00:00 UTC");
assertThatThrownBy(() -> assertions.expression("CAST('2022-01-01 25:00:00 UTC' AS TIMESTAMP(12) WITH TIME ZONE)"))
.hasMessage("Value cannot be cast to timestamp: 2022-01-01 25:00:00 UTC");
assertThatThrownBy(() -> assertions.expression("CAST('2022-01-01 00:61:00 UTC' AS TIMESTAMP(12) WITH TIME ZONE)"))
.hasMessage("Value cannot be cast to timestamp: 2022-01-01 00:61:00 UTC");
assertThatThrownBy(() -> assertions.expression("CAST('2022-01-01 00:00:61 UTC' AS TIMESTAMP(12) WITH TIME ZONE)"))
.hasMessage("Value cannot be cast to timestamp: 2022-01-01 00:00:61 UTC");
assertThatThrownBy(() -> assertions.expression("CAST('2022-01-01 00:00:00 ABC' AS TIMESTAMP(12) WITH TIME ZONE)"))
.hasMessage("Value cannot be cast to timestamp: 2022-01-01 00:00:00 ABC");
}

private BiFunction<Session, QueryRunner, Object> timestampWithTimeZone(int precision, int year, int month, int day, int hour, int minute, int second, long picoOfSecond, TimeZoneKey timeZoneKey)
{
return (session, queryRunner) -> {
Expand Down

0 comments on commit 6d5d136

Please sign in to comment.