[
https://issues.apache.org/jira/browse/HIVE-30206?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Dimitri BEKALE updated HIVE-30206:
----------------------------------
Description:
{\{HiveSqlDateTimeFormatter.parseSeparator}} compares the separator run found
in the input against the separator run in the pattern when \{{FX}} is on, at
[HiveSqlDateTimeFormatter.java:1453|https://github.com/apache/hive/blob/295a0e79169c1eee7f2b2746526b7ab1f723d958/common/src/java/org/apache/hadoop/hive/common/format/datetime/HiveSqlDateTimeFormatter.java#L1453]:
{code:java}
if (formatExact && !token.string.equals(separatorsFound.toString())) {
throw new IllegalArgumentException("FX on and separator found: " +
separatorsFound
+ " does not match expected separator: " + token.string);
}
{code}
The two sides are built under different rules. Per the class javadoc (lines 339
to 342), if the
last separator character is a hyphen and is immediately followed by a \{{tzh}}
token, it is the time zone's negative sign and is *not* counted as a separator,
unless it is the only separator character.
So \{{separatorsFound}} omits that hyphen, while \{{token.string}} (the raw run
from the pattern) keeps it. When a pattern's separator run before \{{tzh}} is
longer than one character and ends in a hyphen, the two can never match and FX
always throws.
h3. Reproducer
{code:java}
// parses
new HiveSqlDateTimeFormatter("yyyy-mm-dd -tzh:tzm",
true).parseTimestamp("2026-10-20 -07:00");
// same pattern with FX: throws
new HiveSqlDateTimeFormatter("FXyyyy-mm-dd -tzh:tzm",
true).parseTimestamp("2026-10-20 -07:00");
// IllegalArgumentException: FX on and separator found: does not match
expected separator: -
{code}
Two controls isolate the cause (same separator shape without \{{tzh}}; single
hyphen, so the
javadoc's "only separator character" exemption applies):
{noformat}
OK pattern="FXyyyy-mm-dd -hh24:mi" input="2026-10-20 -10:30"
OK pattern="FXyyyy-mm-dd-tzh:tzm" input="2026-10-20-07:00"
{noformat}
The pattern is not merely awkward, it is unusable. Trying every separator
filler of length 1 to 4 built from the seven valid separator characters (2,800
inputs) against the pattern above, exactly one parses, namely space followed by
two hyphens; the natural input with space and one hyphen does not:
{noformat}
OK "2026-10-20 --07:00"
THROW "2026-10-20 -07:00"
{noformat}
h3. Impact
Reachable from SQL through \{{CAST (... AS TIMESTAMP FORMAT '...')}}. The user
gets
{\{IllegalArgumentException}} on a pattern that is accepted when FX is off, and
no input they are likely to write will satisfy it. Severity is minor: the
affected patterns are a narrow shape (a
multi-character separator run ending in a hyphen immediately before \{{tzh}}),
and everything else about FX and \{{tzh}} works.
h3. Origin
{\{git blame}} attributes line 1453 to \{{03aae630d9fd}} (2019-07-29,
HIVE-21578, "Introduce SQL:2016 formats FM, FX, and nested strings"). Present
verbatim on \{{rel/release-4.0.0}} (line 1446) and \{{rel/release-4.2.1}} (line
1452) as well as master.
{\{TestHiveSqlDateTimeFormatter}} has FX assertions in \{{testFx()}} and
\{{tzh}} assertions elsewhere, but no test combines the two, which is
presumably why this has gone unnoticed.
h3. Proposed fix
Apply the same hyphen rule to the expected string before comparing:
{code}
- if (formatExact && !token.string.equals(separatorsFound.toString())) {
+ String expectedSeparator = token.string;
+ if (expectedSeparator.length() > 1 && expectedSeparator.endsWith("-")
+ && nextTokenIs("tzh", token)) {
+ expectedSeparator = expectedSeparator.substring(0,
expectedSeparator.length() - 1);
+ }
+ if (formatExact && !expectedSeparator.equals(separatorsFound.toString())) {
throw new IllegalArgumentException("FX on and separator found: " +
separatorsFound
- + " does not match expected separator: " + token.string);
+ + " does not match expected separator: " + expectedSeparator);
}
{code}
With this, the inputs with a space and a hyphen or a plus sign parse, while the
input with two spaces is still rejected, so FX exactness is kept:
{noformat}
OK "2026-10-20 -07:00"
OK "2026-10-20 +07:00"
THROW "2026-10-20 07:00"
{noformat}
A test for \{{testFx()}}:
{code:java}
// FX with a separator ending in "-" before tzh: the "-" is the tzh sign, not a
separator
checkParseTimestamp("FXyyyy-mm-dd -tzh:tzm", "2026-10-20 -07:00", "2026-10-20
00:00:00");
checkParseTimestamp("FXyyyy-mm-dd -tzh:tzm", "2026-10-20 +07:00", "2026-10-20
00:00:00");
verifyBadParseString("FXyyyy-mm-dd -tzh:tzm", "2026-10-20 07:00");
{code}
I replayed all 154 parse assertions in \{{TestHiveSqlDateTimeFormatter}}
against the patched class with the suite's fixed clock and all of them still
pass, but I have not run the full Hive build, so please treat this as a
proposal rather than a CI-validated patch. I am happy to open a PR if that is
welcome, and equally happy to be told the intended behaviour here is different.
h3. Provenance
This was found during an academic study of LLM-assisted code review at Jeonbuk
National University, Jeonju, South Korea. An automated LLM-based reviewer
produced the initial analysis; I verified it by reading the source at
\{{295a0e7916}} and by compiling and running the formatter against the inputs
above. I believe it is a genuine defect, but a "not a bug" outcome is useful to
me as a correction.
was:
{\{HiveSqlDateTimeFormatter.parseSeparator}} compares the separator run found
in the input against the separator run in the pattern when \{{FX}} is on, at
[HiveSqlDateTimeFormatter.java:1453|https://github.com/apache/hive/blob/295a0e79169c1eee7f2b2746526b7ab1f723d958/common/src/java/org/apache/hadoop/hive/common/format/datetime/HiveSqlDateTimeFormatter.java#L1453]:
{code:java}
if (formatExact && !token.string.equals(separatorsFound.toString())) {
throw new IllegalArgumentException("FX on and separator found: " +
separatorsFound
+ " does not match expected separator: " + token.string);
}
{code}
The two sides are built under different rules. Per the class javadoc (lines
339-342), if the last
separator character is \{{-}} and is immediately followed by a \{{tzh}} token,
it is the time zone's negative sign and is *not* counted as a separator, unless
it is the only separator character. So \{{separatorsFound}} omits that hyphen,
while \{{token.string}} - the raw run from the pattern - keeps it. When a
pattern's separator run before \{{tzh}} is longer than one character and ends
in \{{-}}, the two can never match and FX always throws.
h3. Reproducer
{code:java}
// parses
new HiveSqlDateTimeFormatter("yyyy-mm-dd -tzh:tzm",
true).parseTimestamp("2026-10-20 -07:00");
// same pattern with FX: throws
new HiveSqlDateTimeFormatter("FXyyyy-mm-dd -tzh:tzm",
true).parseTimestamp("2026-10-20 -07:00");
// IllegalArgumentException: FX on and separator found: does not match
expected separator: -
{code}
Two controls isolate the cause: \{{FXyyyy-mm-dd -hh24:mi}} with \{{"2026-10-20
-10:30"}} parses (same separator shape, no \{{tzh}}), and
\{{FXyyyy-mm-dd-tzh:tzm}} with \{{"2026-10-20-07:00"}} parses (single \{{-}},
so the javadoc's "only separator character" exemption applies).
The pattern is not merely awkward, it is unusable: trying every separator
filler of length 1-4 built from the seven valid separator characters (2,800
inputs) against \{{FXyyyy-mm-dd -tzh:tzm}}, exactly one parses - \{{" --"}} -
and the natural \{{" -"}} does not.
h3. Impact
Reachable from SQL through \{{CAST (... AS TIMESTAMP FORMAT '...')}}. The user
gets
{\{IllegalArgumentException}} on a pattern that is accepted when FX is off, and
no input they are likely to write will satisfy it. Severity is minor: the
affected patterns are a narrow shape (a
multi-character separator run ending in \{{-}} immediately before \{{tzh}}),
and everything else about FX and \{{tzh}} works.
h3. Origin
{\{git blame}} attributes line 1453 to \{{03aae630d9fd}} (2019-07-29,
HIVE-21578, "Introduce SQL:2016 formats FM, FX, and nested strings"). Present
verbatim on \{{rel/release-4.0.0}} (line 1446) and \{{rel/release-4.2.1}} (line
1452) as well as master.
{\{TestHiveSqlDateTimeFormatter}} has FX assertions in \{{testFx()}} and
\{{tzh}} assertions elsewhere, but no test combines the two, which is
presumably why this has gone unnoticed.
h3. Proposed fix
Apply the same hyphen rule to the expected string before comparing:
{code}
- if (formatExact && !token.string.equals(separatorsFound.toString())) {
+ String expectedSeparator = token.string;
+ if (expectedSeparator.length() > 1 && expectedSeparator.endsWith("-")
+ && nextTokenIs("tzh", token)) {
+ expectedSeparator = expectedSeparator.substring(0,
expectedSeparator.length() - 1);
+ }
+ if (formatExact && !expectedSeparator.equals(separatorsFound.toString())) {
throw new IllegalArgumentException("FX on and separator found: " +
separatorsFound
- + " does not match expected separator: " + token.string);
+ + " does not match expected separator: " + expectedSeparator);
}
{code}
With this, \{{"2026-10-20 -07:00"}} and \{{"2026-10-20 +07:00"}} parse, while
\{{"2026-10-20 07:00"}} is still rejected, so FX exactness is kept. A test for
\{{testFx()}}:
{code:java}
// FX with a separator ending in "-" before tzh: the "-" is the tzh sign, not a
separator
checkParseTimestamp("FXyyyy-mm-dd -tzh:tzm", "2026-10-20 -07:00", "2026-10-20
00:00:00");
checkParseTimestamp("FXyyyy-mm-dd -tzh:tzm", "2026-10-20 +07:00", "2026-10-20
00:00:00");
verifyBadParseString("FXyyyy-mm-dd -tzh:tzm", "2026-10-20 07:00");
{code}
I replayed all 154 parse assertions in \{{TestHiveSqlDateTimeFormatter}}
against the patched class with the suite's fixed clock and all of them still
pass, but I have not run the full Hive build, so please treat this as a
proposal. I am happy to open a PR if that is welcome, and equally happy to be
told the intended behaviour here is different.
h3. Provenance
This was found during an academic study of LLM-assisted code review at Jeonbuk
National University, Jeonju, South Korea. An automated LLM-based reviewer
produced the initial analysis; I verified it by reading the source at
\{{295a0e7916}} and by compiling and running the formatter against the inputs
above. I believe it is a genuine defect but a "not a bug" outcome is useful to
me as a correction.
> CAST ... FORMAT: an FX pattern whose separator before TZH ends in "-" can
> never be parsed
> -----------------------------------------------------------------------------------------
>
> Key: HIVE-30206
> URL: https://issues.apache.org/jira/browse/HIVE-30206
> Project: Hive
> Issue Type: Bug
> Affects Versions: 4.0.0, 4.2.1
> Reporter: Dimitri BEKALE
> Priority: Minor
>
> {\{HiveSqlDateTimeFormatter.parseSeparator}} compares the separator run found
> in the input against the separator run in the pattern when \{{FX}} is on, at
> [HiveSqlDateTimeFormatter.java:1453|https://github.com/apache/hive/blob/295a0e79169c1eee7f2b2746526b7ab1f723d958/common/src/java/org/apache/hadoop/hive/common/format/datetime/HiveSqlDateTimeFormatter.java#L1453]:
> {code:java}
> if (formatExact && !token.string.equals(separatorsFound.toString())) {
> throw new IllegalArgumentException("FX on and separator found: " +
> separatorsFound
> + " does not match expected separator: " + token.string);
> }
> {code}
> The two sides are built under different rules. Per the class javadoc (lines
> 339 to 342), if the
> last separator character is a hyphen and is immediately followed by a
> \{{tzh}} token, it is the time zone's negative sign and is *not* counted as a
> separator, unless it is the only separator character.
> So \{{separatorsFound}} omits that hyphen, while \{{token.string}} (the raw
> run from the pattern) keeps it. When a pattern's separator run before
> \{{tzh}} is longer than one character and ends in a hyphen, the two can never
> match and FX always throws.
> h3. Reproducer
> {code:java}
> // parses
> new HiveSqlDateTimeFormatter("yyyy-mm-dd -tzh:tzm",
> true).parseTimestamp("2026-10-20 -07:00");
> // same pattern with FX: throws
> new HiveSqlDateTimeFormatter("FXyyyy-mm-dd -tzh:tzm",
> true).parseTimestamp("2026-10-20 -07:00");
> // IllegalArgumentException: FX on and separator found: does not match
> expected separator: -
> {code}
> Two controls isolate the cause (same separator shape without \{{tzh}}; single
> hyphen, so the
> javadoc's "only separator character" exemption applies):
> {noformat}
> OK pattern="FXyyyy-mm-dd -hh24:mi" input="2026-10-20 -10:30"
> OK pattern="FXyyyy-mm-dd-tzh:tzm" input="2026-10-20-07:00"
> {noformat}
> The pattern is not merely awkward, it is unusable. Trying every separator
> filler of length 1 to 4 built from the seven valid separator characters
> (2,800 inputs) against the pattern above, exactly one parses, namely space
> followed by two hyphens; the natural input with space and one hyphen does not:
> {noformat}
> OK "2026-10-20 --07:00"
> THROW "2026-10-20 -07:00"
> {noformat}
> h3. Impact
> Reachable from SQL through \{{CAST (... AS TIMESTAMP FORMAT '...')}}. The
> user gets
> {\{IllegalArgumentException}} on a pattern that is accepted when FX is off,
> and no input they are likely to write will satisfy it. Severity is minor: the
> affected patterns are a narrow shape (a
> multi-character separator run ending in a hyphen immediately before
> \{{tzh}}), and everything else about FX and \{{tzh}} works.
> h3. Origin
> {\{git blame}} attributes line 1453 to \{{03aae630d9fd}} (2019-07-29,
> HIVE-21578, "Introduce SQL:2016 formats FM, FX, and nested strings"). Present
> verbatim on \{{rel/release-4.0.0}} (line 1446) and \{{rel/release-4.2.1}}
> (line 1452) as well as master.
> {\{TestHiveSqlDateTimeFormatter}} has FX assertions in \{{testFx()}} and
> \{{tzh}} assertions elsewhere, but no test combines the two, which is
> presumably why this has gone unnoticed.
> h3. Proposed fix
> Apply the same hyphen rule to the expected string before comparing:
> {code}
> - if (formatExact && !token.string.equals(separatorsFound.toString())) {
> + String expectedSeparator = token.string;
> + if (expectedSeparator.length() > 1 && expectedSeparator.endsWith("-")
> + && nextTokenIs("tzh", token)) {
> + expectedSeparator = expectedSeparator.substring(0,
> expectedSeparator.length() - 1);
> + }
> + if (formatExact &&
> !expectedSeparator.equals(separatorsFound.toString())) {
> throw new IllegalArgumentException("FX on and separator found: " +
> separatorsFound
> - + " does not match expected separator: " + token.string);
> + + " does not match expected separator: " + expectedSeparator);
> }
> {code}
> With this, the inputs with a space and a hyphen or a plus sign parse, while
> the input with two spaces is still rejected, so FX exactness is kept:
> {noformat}
> OK "2026-10-20 -07:00"
> OK "2026-10-20 +07:00"
> THROW "2026-10-20 07:00"
> {noformat}
> A test for \{{testFx()}}:
> {code:java}
> // FX with a separator ending in "-" before tzh: the "-" is the tzh sign, not
> a separator
> checkParseTimestamp("FXyyyy-mm-dd -tzh:tzm", "2026-10-20 -07:00", "2026-10-20
> 00:00:00");
> checkParseTimestamp("FXyyyy-mm-dd -tzh:tzm", "2026-10-20 +07:00", "2026-10-20
> 00:00:00");
> verifyBadParseString("FXyyyy-mm-dd -tzh:tzm", "2026-10-20 07:00");
> {code}
> I replayed all 154 parse assertions in \{{TestHiveSqlDateTimeFormatter}}
> against the patched class with the suite's fixed clock and all of them still
> pass, but I have not run the full Hive build, so please treat this as a
> proposal rather than a CI-validated patch. I am happy to open a PR if that is
> welcome, and equally happy to be told the intended behaviour here is
> different.
> h3. Provenance
> This was found during an academic study of LLM-assisted code review at
> Jeonbuk National University, Jeonju, South Korea. An automated LLM-based
> reviewer produced the initial analysis; I verified it by reading the source
> at \{{295a0e7916}} and by compiling and running the formatter against the
> inputs above. I believe it is a genuine defect, but a "not a bug" outcome is
> useful to me as a correction.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)