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());