dongjoon-hyun opened a new pull request, #59571:
URL: https://github.com/apache/spark/pull/59571

   ### What changes were proposed in this pull request?
   
   This PR makes `Literal.sql` pad the fractional seconds of a `TIME(p)` 
literal with `p` in `[7, 9]` back to exactly `p` digits. It reuses the existing 
private `padToNanosPrecision` helper, which already does the same for the 
nanosecond-precision timestamp literals (SPARK-57339, #56494).
   
   `TIME(0)`-`TIME(6)` literals and `Literal.toString` (display-only, trailing 
zeros stripped) are unchanged.
   
   ### Why are the changes needed?
   
   `Literal.sql` renders a `TIME` literal through `FractionTimeFormatter`, 
which strips trailing fractional zeros. Since SPARK-57551 (#56622), the parser 
derives `TIME(7)`-`TIME(9)` from the number of fractional digits of a `TIME` 
literal and gives `TIME(6)` to a literal with 6 or fewer digits. So a `TIME(9)` 
literal whose value ends with zeros, e.g. `12:00:00.100000000`, is rendered as 
`TIME '12:00:00.1'` and parses back as `TIME(6)`.
   
   This causes wrong query results. By default 
(`spark.sql.legacy.parameterSubstitution.constantsOnly=false`), 
`spark.sql(sqlText, args)` and `EXECUTE IMMEDIATE ... USING` substitute each 
argument into the SQL text as `Literal.sql` and parse the result. So a 
`TIME(9)` argument becomes `TIME(6)`, and a string compared or combined with it 
is cast to `TIME(6)`, i.e. truncated to microseconds. For example, with `val t 
= expr("TIME '12:00:00.100000000'")`:
   
   | Query | Before | After |
   | --- | --- | --- |
   | `spark.sql("SELECT :t = '12:00:00.100000001'", Map("t" -> t))` | `true` | 
`false` |
   | `spark.sql("SELECT ? < '12:00:00.100000001'", Array(t))` | `false` | 
`true` |
   | `spark.sql("SELECT s FROM VALUES ('12:00:00.100000001'), ('12:00:00.1') AS 
t(s) WHERE s = :t", Map("t" -> t))` | `12:00:00.100000001`, `12:00:00.1` | 
`12:00:00.1` |
   | `EXECUTE IMMEDIATE 'SELECT ? = \'12:00:00.100000001\'' USING TIME 
'12:00:00.100000000'` | `true` | `false` |
   | `spark.sql("SELECT CASE WHEN id = 0 THEN :t ELSE '12:00:00.123456789' END 
FROM range(1, 2)", Map("t" -> t))` | `12:00:00.123456` (`TIME(6)`) | 
`12:00:00.123456789` (`TIME(9)`) |
   
   In addition, `Literal.sql` is used for the `EXISTS_DEFAULT` column metadata, 
the auto-generated column names, and the values in error messages. 
`EXISTS_DEFAULT` keeps the value because it is cast back to the column type, 
but the column names and the error messages drop the digits written in the 
literal.
   
   ### Does this PR introduce _any_ user-facing change?
   
   Yes. This fixes the wrong results above. In addition, the auto-generated 
column names and the values in error messages keep all `p` fractional digits of 
a `TIME(7)`-`TIME(9)` literal.
   
   ```scala
   spark.sql("SELECT TIME '12:00:00.100000000'").schema.simpleString
   spark.sql("SELECT CAST(TIME '23:59:59.000000000' AS TINYINT)").collect()
   ```
   
   **BEFORE**
   
   ```
   struct<TIME '12:00:00.1':time(9)>
   [CAST_OVERFLOW] The value TIME '23:59:59' of the type "TIME(9)" cannot be 
cast to "TINYINT" due to an overflow. ...
   ```
   
   **AFTER**
   
   ```
   struct<TIME '12:00:00.100000000':time(9)>
   [CAST_OVERFLOW] The value TIME '23:59:59.000000000' of the type "TIME(9)" 
cannot be cast to "TINYINT" due to an overflow. ...
   ```
   
   The behavior changes only for `TIME(7)`-`TIME(9)` literals whose values end 
with fractional zeros. This is not a user-facing change compared to the 
released Apache Spark versions because `TIME(7)`-`TIME(9)` was added by 
SPARK-57551, which exists only in the unreleased `master`, `branch-4.x`, and 
`branch-4.3` (Apache Spark 4.3.0 release candidates, 
[v4.3.0-rc1](https://github.com/apache/spark/releases/tag/v4.3.0-rc1) 
(2026-08-31) and 
[v4.3.0-rc2](https://github.com/apache/spark/releases/tag/v4.3.0-rc2) 
(2026-10-12)).
   
   ### How was this patch tested?
   
   Pass the CIs with the newly added test cases.
   
   - `LiteralExpressionSuite` checks the `Literal.sql` round trip of `TIME(7)`, 
`TIME(8)`, and `TIME(9)` literals with trailing fractional zeros, a whole 
second, and no trailing zeros.
   - `ParametersSuite` checks the comparison and filter results above with 
named and positional arguments and `EXECUTE IMMEDIATE`.
   
   Without this fix, they fail like the following.
   
   ```
   [info] - SPARK-60314: TimeType(7-9) sql keeps the precision on a round trip 
*** FAILED ***
   [info]   TimeType(6) did not equal TimeType(7) 
(LiteralExpressionSuite.scala:729)
   ```
   
   ```
   [info] - SPARK-60314: TIME(7)-TIME(9) literal argument keeps its precision 
*** FAILED ***
   [info]   TimeType(6) did not equal TimeType(9) (ParametersSuite.scala:533)
   ```
   
   The existing golden files are unchanged because all `TIME(7)`-`TIME(9)` 
literals there have no trailing fractional zeros.
   
   ### Was this patch authored or co-authored using generative AI tooling?
   
   Generated-by: Claude Opus 5.5


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to