wombatu-kun commented on code in PR #19620:
URL: https://github.com/apache/hudi/pull/19620#discussion_r3791657901


##########
hudi-hadoop-common/src/test/java/org/apache/hudi/avro/TestHoodieAvroWriteSupportShredding.java:
##########
@@ -68,4 +72,43 @@ void forcedShreddingDdlTreatsDecimalParensAsOneField() {
         .collect(Collectors.toList());
     assertEquals(Arrays.asList("a", "b", "c"), shreddedFieldNames);
   }
+
+  /**
+   * Disabling shredding over an already-shredded schema - the 
clustering/compaction case
+   * {@link HoodieAvroWriteSupport#generateEffectiveSchema} calls out - has to 
strip typed_value
+   * without tripping Avro's "Field already used". Rebuilding a record while 
reusing a
+   * {@code Schema.Field} still bound to the source record throws, so any 
table with a shredded
+   * variant AND at least one other column failed here. #18938 fixed exactly 
that defect in the
+   * sibling HoodieVariantReconstruction and left this twin behind. Nested 
variants must be
+   * stripped too, since the row writer shreds at any depth.

Review Comment:
   `generateEffectiveSchema` is the AVRO write path, where 
`applyForcedShreddingSchema` walks top-level fields only, so the row writer is 
not what puts a nested shredded variant in front of it. Worth the same scoping 
the `VariantSchemaUtils` and `HoodieVariantReconstruction` comments now carry.



##########
hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/dml/schema/TestVariantDataType.scala:
##########
@@ -1250,6 +1184,34 @@ class TestVariantDataType extends HoodieSparkSqlTestBase 
{
     })
   }
 
+  /**
+   * Pins the on-disk layout of the `v` column across every base file. Without 
it a leg meant to
+   * exercise the shredded path can silently degenerate into the unshredded 
one, or the reverse,
+   * and the branch it was written for goes uncovered.
+   */
+  private def assertVariantLayout(tablePath: String, shredded: Boolean, leg: 
String): Unit = {

Review Comment:
   The CDC test's layout block is this helper's body verbatim, with 
`tablePath`, `shredded` and `leg` all already in scope there. Worth folding 
that third site in too, since it is a drop-in call.



##########
hudi-hadoop-common/src/test/java/org/apache/hudi/io/storage/hadoop/TestHoodieVariantReconstruction.java:
##########
@@ -186,6 +186,53 @@ void 
returnsNullForFooterDerivedPlainUnshreddedShape(@TempDir Path tmp) {
         storageWithReadingShredded(tmp, false)));
   }
 
+  @Test
+  void engagesOnTwoFieldShreddedShapeWithNoValueColumn(@TempDir Path tmp) {
+    // The shredding spec lets a writer omit `value` when every row is typed, 
and
+    // HoodieSchema.Variant.determineIfShredded calls anything with a 
typed_value shredded. Shape
+    // detection is the only detector that runs on real files, since the 
footer strips the logical
+    // type, so requiring `value` here dropped such a column's payload 
silently - #19567 again by
+    // another shape. Hudi's own writer always emits three fields, so this is 
about files written
+    // elsewhere.
+    HoodieSchema twoFieldShredded = HoodieSchema.createRecord("v", 
"org.apache.hudi.test", null, Arrays.asList(
+        HoodieSchemaField.of("metadata", 
HoodieSchema.create(HoodieSchemaType.BYTES)),
+        HoodieSchemaField.of("typed_value", 
HoodieSchema.createNullable(HoodieSchemaType.INT))));
+    HoodieSchema fileSchema = recordWithIdAndVariant(twoFieldShredded);
+    HoodieSchema requestedSchema = 
recordWithIdAndVariant(HoodieSchema.createVariant());
+    HoodieStorage storage = storageWithReadingShredded(tmp, true);
+    
storage.getConf().set(HoodieStorageConfig.PARQUET_VARIANT_SHREDDING_PROVIDER_CLASS.key(),
+        TestVariantShreddingProvider.class.getName());
+
+    HoodieVariantReconstruction reconstruction = 
HoodieVariantReconstruction.create(
+        fileSchema, requestedSchema, storage);
+    assertNotNull(reconstruction, "A shredded group with no value column must 
still engage");
+
+    GenericRecord shredded = new GenericData.Record(
+        
reconstruction.intermediateSchema().getField("v").get().schema().getNonNullType().toAvroSchema());
+    shredded.put("metadata", ByteBuffer.wrap(new byte[] {1}));
+    shredded.put("typed_value", 42);
+    GenericRecord input = new 
GenericData.Record(reconstruction.intermediateSchema().toAvroSchema());
+    input.put("id", "record-1");
+    input.put("v", shredded);
+
+    GenericRecord variant = (GenericRecord) 
reconstruction.reconstruct(input).get(1);
+    assertEquals(ByteBuffer.wrap(new byte[] {42}), variant.get("value"));
+  }
+
+  @Test
+  void ignoresFourFieldStructThatMerelyCarriesTypedValue(@TempDir Path tmp) {

Review Comment:
   This four-field struct is killed by the field-count guard before the new 
no-`value` arm of `isShreddedVariantShape`, which nothing else reaches either - 
mutating its `fieldCount == 2` to `true` leaves every test green. Worth a 
sibling case with `{metadata, typed_value, extra}`.



##########
hudi-spark-datasource/hudi-spark4-common/src/test/java/org/apache/hudi/io/storage/hadoop/TestHoodieVariantReconstructionRoundTrip.java:
##########
@@ -104,6 +105,70 @@ void 
createThenReconstructRebuildsVariantAndPassesThroughNonVariant(@TempDir Pat
     assertEquals(original.toJson(ZoneOffset.UTC), 
rebuilt.toJson(ZoneOffset.UTC));
   }
 
+  @Test
+  void createThenReconstructRebuildsAValueLessShreddedGroup(@TempDir Path tmp) 
throws Exception {

Review Comment:
   No `mvn test -pl` list in bot.yml or the Azure pipeline names 
`hudi-spark-datasource/hudi-spark4-common`, so this class is compiled but never 
executed - the `variantIdx = -1` path it pins cannot go red in CI. Worth adding 
the module to the spark4.2 java-test lanes, or saying in the class javadoc that 
it is a local-only guard.



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