Ruiqi Dong created CALCITE-7559:
-----------------------------------
Summary: SqlParserUtil.parseTimeTzLiteral(...) accepts unknown
time zones while sibling parseTimestampTzLiteral(...) rejects them
Key: CALCITE-7559
URL: https://issues.apache.org/jira/browse/CALCITE-7559
Project: Calcite
Issue Type: Bug
Components: core
Affects Versions: 1.41.0
Reporter: Ruiqi Dong
*Summary*
parseTimestampTzLiteral(...) already validates zone identifiers via
ZoneId.of(...), but parseTimeTzLiteral(...) still uses
TimeZone.getTimeZone(...) without validating the input.
Because TimeZone.getTimeZone(...) silently falls back to GMT for unknown IDs,
invalid "TIME WITH TIME ZONE" literals are accepted instead of rejected. The
sibling "TIMESTAMP WITH TIME ZONE" parser rejects the same bad zone string, so
the inconsistency is inside SqlParserUtil itself rather than a broader parser
policy.
*Affected code*
File: core/src/main/java/org/apache/calcite/sql/parser/SqlParserUtil.java
{code:java}
public static SqlTimeTzLiteral parseTimeTzLiteral(
String s, SqlParserPos pos) {
final int lastSpace = s.lastIndexOf(" ");
DateTimeUtils.PrecisionTime pt = null;
if (lastSpace >= 0) {
final String timeZone = s.substring(lastSpace + 1);
final String time = s.substring(0, lastSpace);
final TimeZone tz = TimeZone.getTimeZone(timeZone);
if (tz != null) {
pt =
DateTimeUtils.parsePrecisionDateTimeLiteral(time, Format.get().time,
tz, -1);
}
}
if (pt == null) {
throw SqlUtil.newContextException(pos,
RESOURCE.illegalLiteral("TIME WITH TIME ZONE", s,
RESOURCE.badFormat(DateTimeUtils.TIME_FORMAT_STRING).str()));
}
...
} {code}
{code:java}
public static SqlTimestampTzLiteral parseTimestampTzLiteral(
String s, SqlParserPos pos) {
...
try {
ZoneId zoneId = ZoneId.of(timeZone);
TimeZone tz = TimeZone.getTimeZone(zoneId);
...
} catch (DateTimeException e) {
throw SqlUtil.newContextException(pos,
RESOURCE.illegalLiteral("TIMESTAMP WITH TIME ZONE", s, message));
}
} {code}
Unlike parseTimestampTzLiteral(...), the "TIME WITH TIME ZONE" path never
checks whether the zone identifier is actually valid. The if (tz != null) guard
does not help here, because an unknown zone is converted into GMT instead of
yielding null.
*Reproducer*
Add the following test to
core/src/test/java/org/apache/calcite/sql/parser/SqlParserUtilTest.java
{code:java}
@Test void testTimeWithTimeZoneRejectsUnknownTimeZone() {
SqlParserPos pos = new SqlParserPos(2, 3);
assertThrows(CalciteContextException.class,
() -> SqlParserUtil.parseTimeTzLiteral("10:10:10 incorrect_zone", pos));
} {code}
Run:
{code:java}
./gradlew -q :core:test --tests
org.apache.calcite.sql.parser.SqlParserUtilTest.testTimeWithTimeZoneRejectsUnknownTimeZone
{code}
Observed behavior:
The test fails because no exception is thrown
{code:java}
Expected org.apache.calcite.runtime.CalciteContextException to be thrown, but
nothing was thrown. {code}
Expected behavior:
An invalid time-zone identifier should be rejected, just as invalid zones are
already rejected for "TIMESTAMP WITH TIME ZONE".
The same malformed zone identifier is rejected by the sibling "TIMESTAMP WITH
TIME ZONE" path but accepted here and reinterpreted under GMT. That silently
changes the meaning of the literal instead of reporting bad input.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)