This is an automated email from the ASF dual-hosted git repository.

yihua pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hudi.git


The following commit(s) were added to refs/heads/master by this push:
     new e19a990e3dec fix(common): resolve nested Avro records by class built 
by Hudi (#19956)
e19a990e3dec is described below

commit e19a990e3dec6820453e22914c2c5ce4bd545dab
Author: voonhous <[email protected]>
AuthorDate: Wed Sep 16 04:17:25 2026 +0800

    fix(common): resolve nested Avro records by class built by Hudi (#19956)
---
 .../org/apache/hudi/common/avro/HoodieAvroUtils.java   | 18 +++++++++++++++++-
 1 file changed, 17 insertions(+), 1 deletion(-)

diff --git 
a/hudi-common/src/main/java/org/apache/hudi/common/avro/HoodieAvroUtils.java 
b/hudi-common/src/main/java/org/apache/hudi/common/avro/HoodieAvroUtils.java
index cc98489cce52..e45d3f033542 100644
--- a/hudi-common/src/main/java/org/apache/hudi/common/avro/HoodieAvroUtils.java
+++ b/hudi-common/src/main/java/org/apache/hudi/common/avro/HoodieAvroUtils.java
@@ -1761,11 +1761,27 @@ public class HoodieAvroUtils {
     }
   }
 
+  /**
+   * Loads the generated class for a nested RECORD schema with {@code 
Class.forName}, bypassing the
+   * {@code ClassSecurityValidator} check that Avro 1.12.2+ runs in {@code 
ClassUtils.forName} (reached from
+   * {@link SpecificData#getClass(Schema)}), which rejects Hudi's generated 
classes.
+   *
+   * <p>Only pass schemas taken from a compiled SCHEMA$, never a schema read 
from storage, which is what the validation guards against.
+   */
+  private static Class<? extends SpecificRecordBase> 
getSpecificRecordClass(Schema recordSchema, SpecificData specificData) {
+    String className = SpecificData.getClassName(recordSchema);
+    try {
+      return Class.forName(className, false, 
specificData.getClassLoader()).asSubclass(SpecificRecordBase.class);
+    } catch (ClassNotFoundException e) {
+      throw new HoodieException("Failed to load SpecificRecord class " + 
className + " for Avro schema " + recordSchema.getFullName(), e);
+    }
+  }
+
   private static Object convertFieldToSpecificRecordValue(Schema fieldSchema, 
Object value, SpecificData specificData) {
     Schema resolvedFieldSchema = getActualSchemaFromUnion(fieldSchema, value);
     switch (resolvedFieldSchema.getType()) {
       case RECORD:
-        value = 
convertToSpecificRecord(specificData.getClass(resolvedFieldSchema), 
(GenericRecord) value, specificData);
+        value = 
convertToSpecificRecord(getSpecificRecordClass(resolvedFieldSchema, 
specificData), (GenericRecord) value, specificData);
         break;
       case ARRAY:
         value = ((List<?>) value).stream().map(element -> 
convertFieldToSpecificRecordValue(resolvedFieldSchema.getElementType(), 
element, specificData)).collect(Collectors.toList());

Reply via email to