voonhous commented on code in PR #19582:
URL: https://github.com/apache/hudi/pull/19582#discussion_r3763774931


##########
hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/dml/schema/TestVariantDataType.scala:
##########
@@ -448,6 +449,74 @@ class TestVariantDataType extends HoodieSparkSqlTestBase {
     })
   }
 
+  test("Test COW small-file merge preserves shredded VARIANT values") {
+    // The #19567 repro: the second insert bin-packs into the existing small 
file group, and the
+    // small-file MERGE rewrites the old base file through the AVRO read path
+    // (HoodieAvroParquetReader + HoodieVariantReconstruction), not the Spark 
reader context the
+    // clustering tests above exercise. Before the fix, the footer-derived 
file schema lost the
+    // variant logical type, reconstruction never engaged, and rows 1-2 came 
back null after the
+    // second commit. Unlike those tests, small.file.limit stays at its 
default on purpose: the
+    // bin-pack is the trigger.
+    assume(HoodieSparkUtils.gteqSpark4_1, "Shredded variant base-file read 
requires Spark 4.1 or higher")
+
+    withRecordType()(withTempDir { tmp =>
+      val tableName = generateTableName
+      val tablePath = tmp.getCanonicalPath
+      spark.sql(
+        s"""
+           |create table $tableName (
+           |  id int,
+           |  v variant,
+           |  ts long
+           |) using hudi
+           | location '$tablePath'
+           | tblproperties (
+           |  primaryKey = 'id',
+           |  type = 'cow',
+           |  preCombineField = 'ts',
+           |  hoodie.parquet.variant.write.shredding.enabled = 'true',
+           |  hoodie.parquet.variant.force.shredding.schema.for.test = 'key 
string',
+           |  hoodie.index.type = 'INMEMORY'
+           | )
+       """.stripMargin)
+
+      spark.sql(s"insert into $tableName values " +
+        "(1, parse_json('{\"key\":\"value1\"}'), 1000), " +
+        "(2, parse_json('{\"key\":\"value2\"}'), 1000)")
+
+      // Pin the trigger: the first base file must be shredded, or this 
degenerates into a
+      // plain unshredded merge.
+      val firstFiles = listDataParquetFiles(tablePath)
+      assert(firstFiles.nonEmpty, "Should have at least one data parquet file 
after the first insert")
+      firstFiles.foreach { filePath =>
+        val parquetSchema = readParquetSchema(filePath)
+        val variantGroup = getFieldAsGroup(parquetSchema, "v")
+        assert(variantGroup.containsField("typed_value"),
+          s"First base file should carry typed_value. Schema:\n$variantGroup")
+      }
+
+      spark.sql(s"insert into $tableName values " +

Review Comment:
   Addressed. It does go through `HoodieConcatHandle` -> `HoodieMergeHelper`, 
and as you traced, `recordSchema` stayed footer-derived there, so 
reconstruction never engaged on that path. Pushed a follow-up commit: 
`HoodieMergeHelper` now aligns shredded variant columns to their requested form 
(`VariantSchemaUtils.alignShreddedVariants`) before the strict-projection 
check, so the merge reads the base file at the writer schema and reconstruction 
engages; the lossy rewrite no longer runs. Also pinned the test to the AVRO 
record type since the record type picks the reader the merge uses. Verified the 
test is red without the `HoodieMergeHelper` change and green with it under the 
spark4.1 profile.



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