david-mollitor-db opened a new pull request, #58834:
URL: https://github.com/apache/spark/pull/58834

   ### What changes were proposed in this pull request?
   
   JSON, CSV, and XML schema inference probe whether a string value is a `TIME` 
by calling the
   throwing `TimeFormatter.parse` inside `allCatch opt`:
   
   ```scala
   if (isTimeTypeEnabled && (allCatch opt 
timeFormatter.parse(field)).isDefined) {
     TimeType(TimeType.DEFAULT_PRECISION)
   }
   ```
   
   For the default (pattern-less) formatter, `parse` calls 
`SparkDateTimeUtils.stringToTimeAnsi`, which
   throws a `SparkDateTimeException` (filling in a stack trace) for every value 
that is not a time.
   
   This PR adds a non-throwing `TimeFormatter.parseOptional`, mirroring 
`TimestampFormatter`:
   
   - trait default: `try Some(parse(s)) catch { case _: Exception => None }`;
   - `Iso8601TimeFormatter`: an exception-free implementation using 
`DateTimeFormatter.parseUnresolved`
     with a `ParsePosition`, requiring the whole input to be consumed (as in 
`Iso8601TimestampFormatter`);
   - `DefaultTimeFormatter`: delegates to `SparkDateTimeUtils.stringToTime` 
(already `Option[Long]`).
   
   and switches the `TimeType` inference probe in `JsonInferSchema`, 
`CSVInferSchema`, and
   `XmlInferSchema` to `timeFormatter.parseOptional(field).isDefined`.
   
   ### Why are the changes needed?
   
   Inferring a non-time column throws and catches one exception per row. This 
is exactly the cost that
   SPARK-39193 / SPARK-39280 / SPARK-39281 removed for timestamp and date 
inference by adding
   `parseOptional` (that work reported exception handling as more than 90% of 
type-inference time).
   `TimeType` was added later and never got a `parseOptional`, so it is the 
only datetime formatter
   still using the throwing probe — the timestamp probe immediately below the 
time probe already uses
   `parseOptional`.
   
   Profiling `JsonBenchmark` under JFR (`settings=profile`):
   
   - allocation samples through `SparkDateTimeUtils.stringToTimeAnsi` (the 
throwing time probe):
     ~64,400 → 0;
   - `java.lang.Throwable.fillInStackTrace`: ~1.7% of sampled allocation → 
absent;
   - total sampled allocation: about 23% lower.
   
   No new allocation is introduced: the default inference path (no custom 
`timeFormat`) uses
   `stringToTime` and allocates no `ParsePosition`; the `Iso8601` path 
allocates only a small
   `ParsePosition` in place of the former exception and stack trace.
   
   ### Does this PR introduce _any_ user-facing change?
   
   No. `parseOptional` accepts exactly what `parse` accepts (full-string 
consumption), so inference
   results are unchanged.
   
   ### How was this patch tested?
   
   `TimeFormatterSuite` gains a `parseOptional` test (valid input returns 
`Some`, invalid returns
   `None`, prefix/partial match returns `None`). Existing inference tests pass 
unchanged:
   
   ```
   build/sbt 'catalyst/testOnly *TimeFormatterSuite *CSVInferSchemaSuite'
   build/sbt 'sql/testOnly *JsonSuite *JsonV1Suite *JsonV2Suite *CSVSuite 
*CSVv1Suite *CSVv2Suite *XmlSuite'
   ```
   
   All 902 tests pass.
   
   ### Was this patch authored or co-authored using generative AI tooling?
   
   Generated-by: Isaac
   
   This pull request and its description were written by Isaac.
   


-- 
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