SQL: Enhance timestamp escaped literal parsing (#52097)
Allow also whitespace ` ` (together with `T`) as a separator between date and time parts of the timestamp string. E.g.: ``` {ts '2020-02-08 12.10.45'} ``` or ``` {ts '2020-02-08T12.10.45'} ``` Fixes: #46069 (cherry picked from commit 07c977023fb8ceab5991c359a6cbfe07beaad9bb)
This commit is contained in:
parent
4e4815355a
commit
27265f032a
|
@ -32,11 +32,16 @@ public final class DateUtils {
|
|||
public static final LocalDate EPOCH = LocalDate.of(1970, 1, 1);
|
||||
public static final long DAY_IN_MILLIS = 60 * 60 * 24 * 1000L;
|
||||
|
||||
private static final DateTimeFormatter DATE_TIME_ESCAPED_LITERAL_FORMATTER = new DateTimeFormatterBuilder()
|
||||
.append(ISO_LOCAL_DATE)
|
||||
.appendLiteral(" ")
|
||||
.append(ISO_LOCAL_TIME)
|
||||
.toFormatter().withZone(UTC);
|
||||
private static final DateTimeFormatter DATE_TIME_ESCAPED_LITERAL_FORMATTER_WHITESPACE = new DateTimeFormatterBuilder()
|
||||
.append(ISO_LOCAL_DATE)
|
||||
.appendLiteral(' ')
|
||||
.append(ISO_LOCAL_TIME)
|
||||
.toFormatter().withZone(UTC);
|
||||
private static final DateTimeFormatter DATE_TIME_ESCAPED_LITERAL_FORMATTER_T_LITERAL = new DateTimeFormatterBuilder()
|
||||
.append(ISO_LOCAL_DATE)
|
||||
.appendLiteral('T')
|
||||
.append(ISO_LOCAL_TIME)
|
||||
.toFormatter().withZone(UTC);
|
||||
|
||||
private static final DateFormatter UTC_DATE_TIME_FORMATTER = DateFormatter.forPattern("date_optional_time").withZone(UTC);
|
||||
private static final int DEFAULT_PRECISION_FOR_CURRENT_FUNCTIONS = 3;
|
||||
|
@ -105,7 +110,13 @@ public final class DateUtils {
|
|||
}
|
||||
|
||||
public static ZonedDateTime ofEscapedLiteral(String dateFormat) {
|
||||
return ZonedDateTime.parse(dateFormat, DATE_TIME_ESCAPED_LITERAL_FORMATTER.withZone(UTC));
|
||||
int separatorIdx = dateFormat.lastIndexOf('-') + 3;
|
||||
// Avoid index out of bounds - it will lead to DateTimeParseException anyways
|
||||
if (separatorIdx >= dateFormat.length() || dateFormat.charAt(separatorIdx) == 'T') {
|
||||
return ZonedDateTime.parse(dateFormat, DATE_TIME_ESCAPED_LITERAL_FORMATTER_T_LITERAL.withZone(UTC));
|
||||
} else {
|
||||
return ZonedDateTime.parse(dateFormat, DATE_TIME_ESCAPED_LITERAL_FORMATTER_WHITESPACE.withZone(UTC));
|
||||
}
|
||||
}
|
||||
|
||||
public static String toString(ZonedDateTime dateTime) {
|
||||
|
|
|
@ -62,6 +62,33 @@ public class EscapedFunctionsTests extends ESTestCase {
|
|||
return (Literal) exp;
|
||||
}
|
||||
|
||||
private String buildDate() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int length = randomIntBetween(4, 9);
|
||||
|
||||
if (randomBoolean()) {
|
||||
sb.append('-');
|
||||
} else {
|
||||
if (length > 4) {
|
||||
sb.append('-');
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 1; i <= length; i++) {
|
||||
sb.append(i);
|
||||
}
|
||||
sb.append("-05-10");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String buildSecsAndFractional() {
|
||||
if (randomBoolean()) {
|
||||
return ":55" + randomFrom("", ".1", ".12", ".123", ".1234", ".12345", ".123456",
|
||||
".1234567", ".12345678", ".123456789");
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
private Literal guidLiteral(String guid) {
|
||||
Expression exp = parser.createExpression(buildExpression("guid", "'%s'", guid));
|
||||
assertThat(exp, instanceOf(Expression.class));
|
||||
|
@ -185,7 +212,7 @@ public class EscapedFunctionsTests extends ESTestCase {
|
|||
}
|
||||
|
||||
public void testDateLiteral() {
|
||||
Literal l = dateLiteral("2012-01-01");
|
||||
Literal l = dateLiteral(buildDate());
|
||||
assertThat(l.dataType(), is(DATE));
|
||||
}
|
||||
|
||||
|
@ -197,7 +224,7 @@ public class EscapedFunctionsTests extends ESTestCase {
|
|||
}
|
||||
|
||||
public void testTimeLiteral() {
|
||||
Literal l = timeLiteral("12:23:56");
|
||||
Literal l = timeLiteral("12:23" + buildSecsAndFractional());
|
||||
assertThat(l.dataType(), is(TIME));
|
||||
}
|
||||
|
||||
|
@ -209,14 +236,27 @@ public class EscapedFunctionsTests extends ESTestCase {
|
|||
}
|
||||
|
||||
public void testTimestampLiteral() {
|
||||
Literal l = timestampLiteral("2012-01-01 10:01:02.3456");
|
||||
Literal l = timestampLiteral(buildDate() + " 10:20" + buildSecsAndFractional());
|
||||
assertThat(l.dataType(), is(DATETIME));
|
||||
l = timestampLiteral(buildDate() + "T11:22" + buildSecsAndFractional());
|
||||
assertThat(l.dataType(), is(DATETIME));
|
||||
}
|
||||
|
||||
public void testTimestampLiteralValidation() {
|
||||
ParsingException ex = expectThrows(ParsingException.class, () -> timestampLiteral("2012-01-01T10:01:02.3456"));
|
||||
String date = buildDate();
|
||||
ParsingException ex = expectThrows(ParsingException.class, () -> timestampLiteral(date+ "_AB 10:01:02.3456"));
|
||||
assertEquals(
|
||||
"line 1:2: Invalid timestamp received; Text '2012-01-01T10:01:02.3456' could not be parsed at index 10",
|
||||
"line 1:2: Invalid timestamp received; Text '" + date + "_AB 10:01:02.3456' could not be parsed at index " +
|
||||
date.length(),
|
||||
ex.getMessage());
|
||||
ex = expectThrows(ParsingException.class, () -> timestampLiteral("20120101_AB 10:01:02.3456"));
|
||||
assertEquals(
|
||||
"line 1:2: Invalid timestamp received; Text '20120101_AB 10:01:02.3456' could not be parsed at index 0",
|
||||
ex.getMessage());
|
||||
|
||||
ex = expectThrows(ParsingException.class, () -> timestampLiteral(date));
|
||||
assertEquals(
|
||||
"line 1:2: Invalid timestamp received; Text '" + date + "' could not be parsed at index " + date.length(),
|
||||
ex.getMessage());
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue