voonhous opened a new issue, #19775:
URL: https://github.com/apache/hudi/issues/19775

   ### Context
   
   Part of #18937. Follow-up to #19689 (nested shredding parity on the Avro 
write path), which lets both record types write a variant shredded below the 
top level (a struct member at any depth, under the forced shredding DDL). Split 
out of #19689 by design: the write parity and the footer-strip recursion land 
there; the Spark-native INTERNAL read path is pinned and, if needed, extended 
here.
   
   ### What is deferred today
   
   Four sites rewrite only TOP-LEVEL variant columns into the full-variant 
projection shape (`struct<"0": variant>`, `VariantMetadata("$")`) for internal 
reads of parquet base files (compaction, clustering, small-file merge on the 
SPARK record type, CDC `BASE_FILE_INSERT`, legacy RDD paths with the file-group 
reader disabled), and say the nested leg is deferred because no production path 
wrote a nested-shredded file:
   
   - `SparkFileFormatInternalRowReaderContext` 
(`fullVariantReadSchemaWithOrdinals` + the restore projection, and the comment 
block above `readStructTypeForScan`)
   - `BaseSpark4Adapter.rewriteTopLevelVariantsForFullRead` (behind 
`SparkAdapter.buildFullVariantReadSchema`, Spark 4.1 / 4.2 adapters)
   - `HoodieMergeOnReadRDDV2.shouldRerouteVariantSplit`
   - `CDCFileGroupIterator` (`BASE_FILE_INSERT`)
   
   ### Finding to verify first
   
   By code, Spark 4.1's row reader does not clip a `VariantType` leaf at a 
struct depth: `ParquetReadSupport.clipParquetType` returns the file's shredded 
group unchanged for `VariantType` (`case _ => parquetType`, at any struct 
depth, and via the list/map converters for array elements and map values), and 
`ParquetRowConverter` reconstructs it with `ParquetVariantConverter` 
(`spark.sql.variant.allowReadingShredded`, default true). On the Hudi side 
`SparkSchemaTransformUtils.isDataTypeEqual` and 
`HoodieParquetReadSupport.trimParquetSchema` keep the group. The existing 
section F leg of `TestVariantShreddingMixedLayouts` already reads a row-writer 
nested-shredded COW base correctly with the vectorized reader on and off. So a 
MOR compaction / clustering / CDC read over a struct-nested shredded base is 
most likely correct today, and the deferral comments describe the wrong 
mechanism: the projection shape is an explicit, Spark-4.0-safe contract, not 
what prevents clipping. None of that is p
 inned: there is no MOR or CDC leg over a nested-shredded base anywhere.
   
   ### What needs to be done
   
   1. Add the legs to `TestVariantShreddingMixedLayouts` (section F scaffolds: 
`withVariantTable(..., extraCols = "s struct<inner: variant>")`, 
`withWriteLayout(Forced("k string"))`, `runCompaction`, `runClustering`, 
`assertVariantLayout(..., column = "s.inner")`), Spark 4.1 gate, both record 
types where the scaffold allows:
      - MOR: nested-shredded base + log update -> snapshot read (vectorized 
reader swept true/false) -> read-optimized read -> compaction under `Forced` 
and under `Unshredded` (layout re-derived per run, values intact) -> clustering 
for both `row.writer.enable` values.
      - CDC (SPARK record type, both 
`hoodie.table.cdc.supplemental.logging.mode` values) over a nested-shredded COW 
base: before/after images of `s.inner` non-null through `hudi_table_changes`.
      - `variant_get(s.inner, '$.k')` projection and filter over the 
nested-shredded table (nested `PushVariantIntoScan` shape; the overlay in the 
reader context is top-level only).
      - One `array<variant>` column shredded through a declared write schema: 
snapshot read and one compaction (arrays/maps are read natively; Spark's 
`VariantInRelation` rewrites struct paths only).
   2. Outcome A (all green): rewrite the four comment blocks to state the real 
mechanism and keep the rewrite top-level.
   3. Outcome B (a leg red): extend `rewriteTopLevelVariantsForFullRead` to 
recurse into `StructType` only (matching Spark's 
`VariantInRelation.StructPathToVariant`; arrays/maps stay native), make 
`fullVariantReadSchemaWithOrdinals` return ordinal paths (`Set[Seq[Int]]`), 
build the restore projection recursively (`GetStructField(_, 0, "0")` at a 
variant path, a null-preserving `CreateNamedStruct` for each enclosing struct), 
and keep `HoodieMergeOnReadRDDV2` and `CDCFileGroupIterator` on the shared 
helpers.
   
   ### Out of scope
   
   Inference below the top level; Hive / Trino / Flink reconstruction (they 
keep failing fast); Spark 4.0 (its reader cannot rebuild shredded values, 
adapter returns `None`).
   


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