danny0405 commented on code in PR #19665:
URL: https://github.com/apache/hudi/pull/19665#discussion_r3826906898


##########
hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/hudi/functional/TestCOWDataSource.scala:
##########
@@ -1979,35 +1980,39 @@ class TestCOWDataSource extends 
HoodieSparkClientTestBase with ScalaAssertionSup
       HoodieWriteConfig.KEYGENERATOR_CLASS_NAME.key() -> 
"org.apache.hudi.keygen.ComplexKeyGenerator",
       KeyGeneratorOptions.HIVE_STYLE_PARTITIONING_ENABLE.key() -> "true",
       HiveSyncConfigHolder.HIVE_SYNC_ENABLED.key() -> "false",
-      HoodieWriteConfig.RECORD_MERGE_IMPL_CLASSES.key() -> 
"org.apache.hudi.DefaultSparkRecordMerger"
+      HoodieWriteConfig.RECORD_MERGE_IMPL_CLASSES.key() -> 
"org.apache.hudi.DefaultSparkRecordMerger",
+      HoodieCommonConfig.RECONCILE_SCHEMA.key() -> "true",

Review Comment:
   Addressed in . The functional test now parameterizes both  and 
schema-on-read and covers all four combinations.  is not required: all four 
cases pass, confirming that new-field nullability is applied during 
canonicalization before either reconciliation path is selected.



##########
hudi-common/src/main/java/org/apache/hudi/common/schema/internal/utils/AvroSchemaEvolutionUtils.java:
##########
@@ -330,9 +332,28 @@ public static HoodieSchema 
reconcileSchemaRequirements(HoodieSchema sourceSchema
 
     List<String> nullableUpdateColsInSource = new ArrayList<>();
     List<String> typeUpdateColsInSource = new ArrayList<>();
+
+    // Only relax the topmost field in a wholly new subtree. Relaxing every 
descendant would alter the
+    // element/field constraints supplied by the writer instead of only making 
the evolved field backfillable.
+    Set<String> visitedNewColumns = new HashSet<>();
+    colNamesSourceSchema.stream()
+        .filter(field -> !colNamesTargetSchema.contains(field))
+        .filter(field -> !META_FIELD_NAMES.contains(field))
+        .sorted()
+        .forEach(field -> {
+          String parent = TableChangesHelper.getParentName(field);
+          if (!visitedNewColumns.contains(parent)) {
+            nullableUpdateColsInSource.add(field);
+          }
+          visitedNewColumns.add(field);
+        });
+
     colNamesSourceSchema.forEach(field -> {
-      // handle columns that needs to be made nullable
-      if (colNamesTargetSchema.contains(field) && 
sourceInternalSchema.findField(field).isOptional() != 
targetInternalSchema.findField(field).isOptional()) {
+      // Reconcile nullability only for existing user columns. Metadata fields 
may be present in the source even
+      // though the target schema used for canonicalization has intentionally 
stripped them.
+      if (colNamesTargetSchema.contains(field)
+          && !META_FIELD_NAMES.contains(field)

Review Comment:
   Addressed in . I now build  once by filtering , then use that list for both 
new-field detection and existing-field nullability/type reconciliation. This 
removes the redundant per-condition metadata guard and keeps metadata fields 
out of all processing in this method.



##########
hudi-common/src/main/java/org/apache/hudi/common/schema/internal/utils/AvroSchemaEvolutionUtils.java:
##########
@@ -302,17 +302,19 @@ public static SchemaCompatibilityException 
timestampPrecisionChangeError(String
    * {@code target} one. Source is considered to be new incoming schema, while 
target could refer to prev table schema.
    * For example,
    * if colA in source is non-nullable, but is nullable in target, output 
schema will have colA as nullable.
-   * if "hoodie.datasource.write.new.columns.nullable" is set to true and if 
colB is not present in source, but
-   * is present in target, output schema will have colB as nullable.
+   * if colB is present in source, but not in target, output schema will have 
colB as nullable. If colB is a complex
+   * type, its existing descendants retain their nullability constraints.

Review Comment:
   Addressed in the PR description. The Impact and Documentation Update 
sections now explicitly mark a release note as required and describe the 
default flip: newly added required fields previously failed with  and now 
succeed by being widened to nullable with a  default.



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