dongjoon-hyun commented on PR #58603:
URL: https://github.com/apache/spark/pull/58603#issuecomment-5584124344

   Thanks for reworking this -- moving to the inference path is the right call, 
and reverting the
   parser-side option is the right thing to do given that parser recursion is 
schema-bounded.
   
   That said, I don't think the new code is reachable, and I believe the new 
test passes on `master`
   unchanged. Details below.
   
   ### 1. The new `StackOverflowError` handlers look like dead code, and the 
test doesn't exercise them
   
   `XMLInputFactory.newInstance()` in Spark resolves to **Woodstox**, shaded 
into `hadoop-client`
   (`StaxXMLRecordReader.scala:26` already imports 
`org.apache.hadoop.shaded.com.ctc.wstx...`).
   Woodstox enforces `maxElementDepth = 1000` by default, and 
`StaxXmlParserUtils.factory` (line 38)
   does not override it.
   
   I reproduced this against the exact `hadoop-client-runtime` 3.5.0 jar that 
`hadoop.version` in
   `pom.xml` selects, with the same four factory properties Spark sets and the 
exact document shape
   from the new test (`<ROWS><ROW>` + `<a>` x 60000 + ...):
   
   ```
   impl = org.apache.hadoop.shaded.com.ctc.wstx.stax.WstxInputFactory
   events read = 1001
   THROWN: javax.xml.stream.XMLStreamException : Maximum Element Depth limit 
(1000) Exceeded
   ```
   
   So the reader fails at depth 1001 with an `XMLStreamException`, long before 
the JVM stack is
   anywhere near exhausted -- `inferObject`/`inferField` never recurse deeply 
enough to overflow.
   
   And both catch sites already routed `XMLStreamException` through 
`handleXmlErrorsByParseMode`
   before this PR (`XmlInferSchema.scala:189` for the string path, `:303` for 
the record-reader path).
   That produces exactly the same error as the new `StackOverflowError` branch, 
since
   `malformedRecordsDetectedInSchemaInferenceError` 
(`QueryExecutionErrors.scala:1568`) doesn't vary
   with the cause:
   
   ```
   MALFORMED_RECORD_IN_PARSING.WITHOUT_SUGGESTION
     badRecord    -> _corrupt_record
     failFastMode -> FAILFAST
   ```
   
   which is precisely what the new test asserts. The test therefore can't 
distinguish the two paths.
   Could you run the two new tests against `master` without the 
`XmlInferSchema` change? If they pass
   there, this PR isn't doing what the description says.
   
   (I verified the Woodstox behavior and the code paths directly; I did not run 
`XmlSuite` itself.)
   
   ### 2. Catching `StackOverflowError` is unsafe regardless
   
   Spark uses `NonFatal` consistently for a reason. A `StackOverflowError` 
unwinds from an arbitrary
   frame, so the Woodstox reader's internal state may be left mid-mutation; 
continuing to touch it --
   which `handleXmlErrorsByParseMode` does under `FAILFAST` via 
`parser.close()` -- is not well
   defined.
   
   If we do want a bound on inference depth, I'd make it deterministic instead:
   
   - set `WstxInputProperties.P_MAX_ELEMENT_DEPTH` explicitly on the factory, 
which makes the limit
     visible and adjustable and keeps it an ordinary `XMLStreamException` the 
existing handlers
     already cover; or
   - add an explicit depth counter to `inferObject` / `inferField`.
   
   Either is testable without depending on stack size.
   
   ### 3. The record-reader branch silently drops the rest of the file
   
   `XmlInferSchema.scala:298` calls `parser.close()` and then returns a 
corrupt-record schema. Closing
   the streaming reader means **every remaining record in that file is excluded 
from inference**. The
   `XMLStreamException` branch above also closes, but that's a file that 
genuinely can't be parsed any
   further; here it's one deep record. Compare the `SAXException` branch right 
below, which
   deliberately does *not* close so the remaining records can still be inferred.
   
   ### 4. The test is environment-dependent even if the handler were reachable
   
   `depth = 60000` depends on `-Xss4m` (the `sql/core` default -- note 
`sql/hive` overrides it to
   `-Xss64m`) and on the frame sizes of `inferObject`/`inferField`. Tests that 
rely on triggering a
   real stack overflow tend to be fragile across JDKs and JIT states. A 
deterministic bound avoids
   this entirely.
   
   ### 5. The PERMISSIVE assertion only checks the inferred schema
   
   ```scala
   assert(df.columns.contains("_corrupt_record"))
   ```
   
   This stops at inference. Adding a `df.collect()` / `checkAnswer` would 
confirm the read actually
   completes and show what the parse side produces. The description also 
mentions `DROPMALFORMED`,
   but there's no test for it.
   
   ### 6. Document-driven recursion on the parse side is still unbounded
   
   `StaxXmlParser.convertVariantInternal` (line 1134) recurses per document 
element rather than per
   schema level, so `singleVariantColumn` or a `VariantType` field is 
data-driven at parse time too.
   Woodstox's 1000-element cap happens to shield it today, which is another 
reason to make that cap
   explicit rather than incidental.
   
   ---
   
   Item (1) is the one to settle first: if the new tests pass on `master`, the 
fix and the test both
   need rethinking, and the real question becomes whether Woodstox's default 
limit of 1000 should be
   made explicit and configurable.
   


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