voonhous opened a new pull request, #20351:
URL: https://github.com/apache/hudi/pull/20351

   ### Describe the issue this Pull Request addresses
   
   Closes #20349
   
   A COW upsert on the default AVRO record type fails with `AvroTypeException: 
Invalid default for field v: null not a {record ...}` when a base file stores a 
shredded variant column as REQUIRED and the table schema has it nullable. Spark 
4.1 produces such files with default configs: a DataFrame insert of a non-null 
`parse_json` column, or a METADATA_ONLY bootstrap of a vanilla Spark parquet 
file. Once it happens, no later upsert into that file group succeeds.
   
   ### Summary and Changelog
   
   - `VariantSchemaUtils.swapShreddedVariantsAt`: at a shredded position the 
swapped-in schema stays nullable when the base side is nullable. 
`toShreddedReadSchema` therefore keeps the requested nullability, and reading a 
REQUIRED column as nullable is a safe widening. A nullable side is never 
narrowed.
   - Tests: unit (`TestVariantSchemaUtils`), parquet read through 
`HoodieAvroParquetReader` (`TestHoodieVariantReconstructionRoundTrip`), and 
both user routes end to end (`TestVariantShreddingMixedLayouts`). Each fails 
without the fix.
   
   <details>
   <summary>Details</summary>
   
   **Cause.** `toShreddedReadSchema` swaps each requested variant for its 
on-disk shredded form. At a shredded position it took the file's schema 
wholesale, nullability included, and `swapShreddedVariantFields` rebuilt the 
requested field around it with `withSchema`, keeping the requested `null` 
default on a non-null record.
   
   **Both directions.** The rule is keyed on the base side, so it also covers 
`alignShreddedVariants` (base = file, used by `HoodieMergeHelper`). There, a 
nullable file column (null default) under a non-null requested variant hit the 
same invalid default; it now stays nullable. The opposite mismatch in 
`toShreddedReadSchema` (nullable file, non-null request) is unchanged: the 
file's nullable group is kept, since a REQUIRED projection of an OPTIONAL 
column would not read.
   
   **Depth.** The rule applies at every position the swap walks: top-level 
columns, record members, array elements and map values.
   
   **Tests.**
   - `TestVariantSchemaUtils`: requested nullability kept at top level, record 
member, array element and map value; opposite mismatch kept nullable; Avro 
round trip; `alignShreddedVariants` with a nullable file column.
   - `TestHoodieVariantReconstructionRoundTrip`: a REQUIRED shredded `v` 
written by `AvroParquetWriter`, read at a nullable unshredded request; typed, 
partly residual, scalar and variant-null rows rebuild.
   - `TestVariantShreddingMixedLayouts`: the DataFrame route on both record 
types, and a COW upsert added to the METADATA_ONLY bootstrap test.
   
   </details>
   
   ### Impact
   
   Fixes the failing upsert. No change for files whose variant nullability 
already matches the table schema.
   
   ### Risk Level
   
   low: only shredded positions whose nullability differs between the file and 
the request are affected. At record fields those failed outright; a REQUIRED 
array element or map value is now read at the requested nullable type instead 
of the file's.
   
   ### Documentation Update
   
   none
   
   ### Contributor's checklist
   
   - [x] Read through [contributor's 
guide](https://hudi.apache.org/contribute/how-to-contribute)
   - [x] Enough context is provided in the sections above
   - [x] Adequate tests were added if applicable
   


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

Reply via email to