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 394229ca5c9f feat(schema): HoodieSchema: add helper methods, fix
issues with schema subtypes not returned (#14346)
394229ca5c9f is described below
commit 394229ca5c9fb8a6f8749805b3678ddfeb4ea788
Author: Tim Brown <[email protected]>
AuthorDate: Tue Nov 25 01:29:43 2025 -0500
feat(schema): HoodieSchema: add helper methods, fix issues with schema
subtypes not returned (#14346)
---
.../apache/hudi/common/schema/HoodieSchema.java | 98 ++++++++++-------
.../hudi/common/schema/TestHoodieSchema.java | 121 ++++++++++++---------
.../schema/TestHoodieSchemaCompatibility.java | 2 +-
3 files changed, 127 insertions(+), 94 deletions(-)
diff --git
a/hudi-common/src/main/java/org/apache/hudi/common/schema/HoodieSchema.java
b/hudi-common/src/main/java/org/apache/hudi/common/schema/HoodieSchema.java
index c866cb56667d..ebdca4903d42 100644
--- a/hudi-common/src/main/java/org/apache/hudi/common/schema/HoodieSchema.java
+++ b/hudi-common/src/main/java/org/apache/hudi/common/schema/HoodieSchema.java
@@ -106,9 +106,22 @@ public class HoodieSchema implements Serializable {
*
* @param avroSchema the Avro schema to wrap
* @return new HoodieSchema instance
- * @throws IllegalArgumentException if avroSchema is null
*/
public static HoodieSchema fromAvroSchema(Schema avroSchema) {
+ if (avroSchema == null) {
+ return null;
+ }
+ LogicalType logicalType = avroSchema.getLogicalType();
+ if (logicalType != null) {
+ if (logicalType instanceof LogicalTypes.Decimal) {
+ return new HoodieSchema.Decimal(avroSchema);
+ } else if (logicalType instanceof LogicalTypes.TimeMillis || logicalType
instanceof LogicalTypes.TimeMicros) {
+ return new HoodieSchema.Time(avroSchema);
+ } else if (logicalType instanceof LogicalTypes.TimestampMillis ||
logicalType instanceof LogicalTypes.TimestampMicros
+ || logicalType instanceof LogicalTypes.LocalTimestampMillis ||
logicalType instanceof LogicalTypes.LocalTimestampMicros) {
+ return new HoodieSchema.Timestamp(avroSchema);
+ }
+ }
return new HoodieSchema(avroSchema);
}
@@ -149,6 +162,16 @@ public class HoodieSchema implements Serializable {
return new HoodieSchema(avroSchema);
}
+ /**
+ * Creates a nullable schema for the specified primitive type.
+ * @param type the primitive schema type
+ * @return new HoodieSchema representing a nullable version of the primitive
type
+ */
+ public static HoodieSchema createNullable(HoodieSchemaType type) {
+ HoodieSchema nonNullSchema = create(type);
+ return createNullable(nonNullSchema);
+ }
+
/**
* Creates a nullable schema (union of null and the specified schema).
*
@@ -171,14 +194,14 @@ public class HoodieSchema implements Serializable {
}
// Add null to existing union
- List<Schema> newUnionTypes = new ArrayList<>();
+ List<Schema> newUnionTypes = new ArrayList<>(unionTypes.size() + 1);
newUnionTypes.add(Schema.create(Schema.Type.NULL));
newUnionTypes.addAll(unionTypes);
Schema nullableSchema = Schema.createUnion(newUnionTypes);
return new HoodieSchema(nullableSchema);
} else {
// Create new union with null
- List<Schema> unionTypes = new ArrayList<>();
+ List<Schema> unionTypes = new ArrayList<>(2);
unionTypes.add(Schema.create(Schema.Type.NULL));
unionTypes.add(inputAvroSchema);
Schema nullableSchema = Schema.createUnion(unionTypes);
@@ -492,19 +515,6 @@ public class HoodieSchema implements Serializable {
return HoodieSchema.fromAvroSchema(resultAvro);
}
- /**
- * Creates a nullable union (null + specified type).
- *
- * @param type the non-null type
- * @return new HoodieSchema representing a nullable union
- */
- public static HoodieSchema createNullableSchema(HoodieSchema type) {
- ValidationUtils.checkArgument(type != null, "Type cannot be null");
-
- HoodieSchema nullSchema = HoodieSchema.create(HoodieSchemaType.NULL);
- return createUnion(nullSchema, type);
- }
-
/**
* Returns the type of this schema.
*
@@ -515,15 +525,15 @@ public class HoodieSchema implements Serializable {
}
/**
- * Returns the name of this schema, if it has one.
+ * Returns the name of the schema if a record, otherwise it returns the name
of the type.
*
- * @return Option containing the schema name, or Option.empty() if none
+ * @return the schema name
*/
- public Option<String> getName() {
+ public String getName() {
if (avroSchema.getLogicalType() != null) {
- return Option.of(type.name().toLowerCase(Locale.ENGLISH));
+ return type.name().toLowerCase(Locale.ENGLISH);
}
- return Option.ofNullable(avroSchema.getName());
+ return avroSchema.getName();
}
/**
@@ -536,12 +546,12 @@ public class HoodieSchema implements Serializable {
}
/**
- * Returns the full name of this schema (namespace + name).
+ * Returns the full name of this schema (namespace + name) if a record, or
the type name otherwise.
*
- * @return Option containing the full schema name, or Option.empty() if none
+ * @return The full schema name, or name of the type if not a record
*/
- public Option<String> getFullName() {
- return Option.ofNullable(avroSchema.getFullName());
+ public String getFullName() {
+ return avroSchema.getFullName();
}
/**
@@ -617,7 +627,7 @@ public class HoodieSchema implements Serializable {
throw new IllegalStateException("Cannot get element type from non-array
schema: " + type);
}
- return new HoodieSchema(avroSchema.getElementType());
+ return HoodieSchema.fromAvroSchema(avroSchema.getElementType());
}
/**
@@ -631,7 +641,7 @@ public class HoodieSchema implements Serializable {
throw new IllegalStateException("Cannot get value type from non-map
schema: " + type);
}
- return new HoodieSchema(avroSchema.getValueType());
+ return HoodieSchema.fromAvroSchema(avroSchema.getValueType());
}
/**
@@ -646,7 +656,7 @@ public class HoodieSchema implements Serializable {
}
return avroSchema.getTypes().stream()
- .map(HoodieSchema::new)
+ .map(HoodieSchema::fromAvroSchema)
.collect(Collectors.toList());
}
@@ -736,14 +746,14 @@ public class HoodieSchema implements Serializable {
}
/**
- * If this is a union schema, returns the non-null type.
+ * If this is a union schema, returns the non-null type. Otherwise, returns
this schema.
*
- * @return the non-null schema from a union
- * @throws IllegalStateException if this is not a nullable union
+ * @return the non-null schema from a union or the current schema
+ * @throws IllegalStateException if the union has more than two types
*/
public HoodieSchema getNonNullType() {
if (type != HoodieSchemaType.UNION) {
- throw new IllegalStateException("Cannot get non-null type from non-union
schema: " + type);
+ return this;
}
List<HoodieSchema> types = getTypes();
@@ -1121,8 +1131,12 @@ public class HoodieSchema implements Serializable {
}
@Override
- public Option<String> getName() {
- return Option.of(String.format("decimal(%d,%d)", precision, scale));
+ public String getName() {
+ return String.format("decimal(%d,%d)", precision, scale);
+ }
+
+ public boolean isFixed() {
+ return fixedSize.isPresent();
}
@Override
@@ -1193,18 +1207,18 @@ public class HoodieSchema implements Serializable {
}
@Override
- public Option<String> getName() {
+ public String getName() {
if (isUtcAdjusted) {
if (precision == TimePrecision.MILLIS) {
- return Option.of("timestamp-millis");
+ return "timestamp-millis";
} else {
- return Option.of("timestamp-micros");
+ return "timestamp-micros";
}
} else {
if (precision == TimePrecision.MILLIS) {
- return Option.of("local-timestamp-millis");
+ return "local-timestamp-millis";
} else {
- return Option.of("local-timestamp-micros");
+ return "local-timestamp-micros";
}
}
}
@@ -1255,11 +1269,11 @@ public class HoodieSchema implements Serializable {
}
@Override
- public Option<String> getName() {
+ public String getName() {
if (precision == TimePrecision.MILLIS) {
- return Option.of("time-millis");
+ return "time-millis";
} else {
- return Option.of("time-micros");
+ return "time-micros";
}
}
diff --git
a/hudi-common/src/test/java/org/apache/hudi/common/schema/TestHoodieSchema.java
b/hudi-common/src/test/java/org/apache/hudi/common/schema/TestHoodieSchema.java
index cba2af9ac901..25ea4c16f7e1 100644
---
a/hudi-common/src/test/java/org/apache/hudi/common/schema/TestHoodieSchema.java
+++
b/hudi-common/src/test/java/org/apache/hudi/common/schema/TestHoodieSchema.java
@@ -36,6 +36,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -74,13 +75,7 @@ public class TestHoodieSchema {
@Test
public void testSchemaCreationWithNullAvroSchema() {
- assertThrows(IllegalArgumentException.class, () -> {
- HoodieSchema.fromAvroSchema(null);
- }, "Should throw exception for null Avro schema");
-
- assertThrows(IllegalArgumentException.class, () -> {
- HoodieSchema.fromAvroSchema(null);
- }, "Should throw exception for null Avro schema in constructor");
+ assertNull(HoodieSchema.fromAvroSchema(null));
}
@Test
@@ -89,9 +84,9 @@ public class TestHoodieSchema {
assertNotNull(schema);
assertEquals(HoodieSchemaType.RECORD, schema.getType());
- assertEquals(Option.of("User"), schema.getName());
+ assertEquals("User", schema.getName());
assertEquals(Option.of("com.example"), schema.getNamespace());
- assertEquals(Option.of("com.example.User"), schema.getFullName());
+ assertEquals("com.example.User", schema.getFullName());
assertEquals(Option.of("User record schema"), schema.getDoc());
List<HoodieSchemaField> fields = schema.getFields();
@@ -174,14 +169,13 @@ public class TestHoodieSchema {
assertNotNull(fixedSchema);
assertEquals(HoodieSchemaType.FIXED, fixedSchema.getType());
- assertEquals("FixedField", fixedSchema.getName().get());
+ assertEquals("FixedField", fixedSchema.getName());
assertEquals(16, fixedSchema.getFixedSize());
}
@Test
public void testNullableSchemaCreation() {
- HoodieSchema stringSchema = HoodieSchema.create(HoodieSchemaType.STRING);
- HoodieSchema nullableSchema = HoodieSchema.createNullable(stringSchema);
+ HoodieSchema nullableSchema =
HoodieSchema.createNullable(HoodieSchemaType.STRING);
assertEquals(HoodieSchemaType.UNION, nullableSchema.getType());
assertTrue(nullableSchema.isNullable());
@@ -318,13 +312,9 @@ public class TestHoodieSchema {
assertEquals(HoodieSchemaType.STRING, nonNullType.getType());
// Test with non-union schema
- assertThrows(IllegalStateException.class, () -> {
- stringSchema.getTypes();
- }, "Should throw exception when getting types from non-union schema");
+ assertThrows(IllegalStateException.class, stringSchema::getTypes, "Should
throw exception when getting types from non-union schema");
- assertThrows(IllegalStateException.class, () -> {
- stringSchema.getNonNullType();
- }, "Should throw exception when getting non-null type from non-union
schema");
+ assertSame(stringSchema, stringSchema.getNonNullType());
}
@Test
@@ -337,9 +327,7 @@ public class TestHoodieSchema {
// Test with non-fixed schema
HoodieSchema stringSchema = HoodieSchema.create(HoodieSchemaType.STRING);
- assertThrows(IllegalStateException.class, () -> {
- stringSchema.getFixedSize();
- }, "Should throw exception when getting fixed size from non-fixed schema");
+ assertThrows(IllegalStateException.class, stringSchema::getFixedSize,
"Should throw exception when getting fixed size from non-fixed schema");
}
@Test
@@ -353,25 +341,23 @@ public class TestHoodieSchema {
// Test with non-enum schema
HoodieSchema stringSchema = HoodieSchema.create(HoodieSchemaType.STRING);
- assertThrows(IllegalStateException.class, () -> {
- stringSchema.getEnumSymbols();
- }, "Should throw exception when getting symbols from non-enum schema");
+ assertThrows(IllegalStateException.class, stringSchema::getEnumSymbols,
"Should throw exception when getting symbols from non-enum schema");
}
@Test
public void testSchemaProperties() {
HoodieSchema schema = HoodieSchema.parse(SAMPLE_RECORD_SCHEMA);
- assertEquals(Option.of("User"), schema.getName());
+ assertEquals("User", schema.getName());
assertEquals(Option.of("com.example"), schema.getNamespace());
- assertEquals(Option.of("com.example.User"), schema.getFullName());
+ assertEquals("com.example.User", schema.getFullName());
assertEquals(Option.of("User record schema"), schema.getDoc());
// Test schema without these properties (primitive types have their type
name as name but no namespace)
HoodieSchema stringSchema = HoodieSchema.create(HoodieSchemaType.STRING);
- assertEquals(Option.of("string"), stringSchema.getName()); // Primitive
types use type name
+ assertEquals("string", stringSchema.getName()); // Primitive types use
type name
// Note: Don't test getNamespace() on primitive types as Avro throws
exception
- assertEquals(Option.of("string"), stringSchema.getFullName()); // Same as
name for primitives
+ assertEquals("string", stringSchema.getFullName()); // Same as name for
primitives
assertEquals(Option.empty(), stringSchema.getDoc());
}
@@ -452,7 +438,7 @@ public class TestHoodieSchema {
assertNotNull(recordSchema);
assertEquals(HoodieSchemaType.RECORD, recordSchema.getType());
- assertEquals(Option.of("User"), recordSchema.getName());
+ assertEquals("User", recordSchema.getName());
assertEquals(Option.of("com.example"), recordSchema.getNamespace());
assertEquals(Option.of("User record schema"), recordSchema.getDoc());
assertEquals(2, recordSchema.getFields().size());
@@ -468,7 +454,7 @@ public class TestHoodieSchema {
HoodieSchema recordSchema = HoodieSchema.createRecord("User",
"com.example", "User record", fields);
assertEquals(HoodieSchemaType.RECORD, recordSchema.getType());
- assertEquals(Option.of("User"), recordSchema.getName());
+ assertEquals("User", recordSchema.getName());
// Test createUnion (varargs version)
HoodieSchema stringSchema = HoodieSchema.create(HoodieSchemaType.STRING);
@@ -481,13 +467,13 @@ public class TestHoodieSchema {
List<String> symbols = Arrays.asList("RED", "GREEN", "BLUE");
HoodieSchema enumSchema = HoodieSchema.createEnum("Color", "com.example",
"Color enum", symbols);
assertEquals(HoodieSchemaType.ENUM, enumSchema.getType());
- assertEquals(Option.of("Color"), enumSchema.getName());
+ assertEquals("Color", enumSchema.getName());
assertEquals(symbols, enumSchema.getEnumSymbols());
// Test createFixed
HoodieSchema fixedSchema = HoodieSchema.createFixed("MD5", "com.example",
"MD5 hash", 16);
assertEquals(HoodieSchemaType.FIXED, fixedSchema.getType());
- assertEquals(Option.of("MD5"), fixedSchema.getName());
+ assertEquals("MD5", fixedSchema.getName());
assertEquals(16, fixedSchema.getFixedSize());
}
@@ -533,7 +519,7 @@ public class TestHoodieSchema {
assertNotNull(schema);
assertEquals(HoodieSchemaType.RECORD, schema.getType());
- assertEquals("Test", schema.getName().get());
+ assertEquals("Test", schema.getName());
assertEquals(2, schema.getFields().size());
}
@@ -548,7 +534,7 @@ public class TestHoodieSchema {
assertNotNull(recordSchema);
assertEquals(HoodieSchemaType.RECORD, recordSchema.getType());
- assertEquals("ErrorRecord", recordSchema.getName().get());
+ assertEquals("ErrorRecord", recordSchema.getName());
assertEquals("com.example", recordSchema.getNamespace().get());
assertTrue(recordSchema.isError());
assertEquals(2, recordSchema.getFields().size());
@@ -600,7 +586,7 @@ public class TestHoodieSchema {
assertNotNull(recordSchema);
assertEquals(HoodieSchemaType.RECORD, recordSchema.getType());
- assertEquals(Option.of("User"), recordSchema.getName());
+ assertEquals("User", recordSchema.getName());
assertEquals(2, recordSchema.getFields().size());
// Verify fields
@@ -623,9 +609,9 @@ public class TestHoodieSchema {
assertNotNull(recordSchema);
assertEquals(HoodieSchemaType.RECORD, recordSchema.getType());
- assertEquals(Option.of("TestRecord"), recordSchema.getName());
+ assertEquals("TestRecord", recordSchema.getName());
assertEquals(Option.of("com.example"), recordSchema.getNamespace());
- assertEquals(Option.of("com.example.TestRecord"),
recordSchema.getFullName());
+ assertEquals("com.example.TestRecord", recordSchema.getFullName());
assertEquals(Option.of("Test record schema"), recordSchema.getDoc());
}
@@ -657,7 +643,7 @@ public class TestHoodieSchema {
assertNotNull(recordSchema);
assertEquals(HoodieSchemaType.RECORD, recordSchema.getType());
- assertEquals(Option.of("EmptyRecord"), recordSchema.getName());
+ assertEquals("EmptyRecord", recordSchema.getName());
assertEquals(0, recordSchema.getFields().size());
}
@@ -668,7 +654,7 @@ public class TestHoodieSchema {
assertNotNull(enumSchema);
assertEquals(HoodieSchemaType.ENUM, enumSchema.getType());
- assertEquals(Option.of("Color"), enumSchema.getName());
+ assertEquals("Color", enumSchema.getName());
assertEquals(symbols, enumSchema.getEnumSymbols());
}
@@ -703,7 +689,7 @@ public class TestHoodieSchema {
assertNotNull(fixedSchema);
assertEquals(HoodieSchemaType.FIXED, fixedSchema.getType());
- assertEquals(Option.of("MD5Hash"), fixedSchema.getName());
+ assertEquals("MD5Hash", fixedSchema.getName());
assertEquals(16, fixedSchema.getFixedSize());
}
@@ -784,7 +770,7 @@ public class TestHoodieSchema {
@Test
public void testCreateNullableSchema() {
HoodieSchema stringSchema = HoodieSchema.create(HoodieSchemaType.STRING);
- HoodieSchema nullableSchema =
HoodieSchema.createNullableSchema(stringSchema);
+ HoodieSchema nullableSchema = HoodieSchema.createNullable(stringSchema);
assertNotNull(nullableSchema);
assertEquals(HoodieSchemaType.UNION, nullableSchema.getType());
@@ -802,7 +788,7 @@ public class TestHoodieSchema {
@Test
public void testCreateNullableSchemaWithInvalidParameters() {
assertThrows(IllegalArgumentException.class, () -> {
- HoodieSchema.createNullableSchema(null);
+ HoodieSchema.createNullable((HoodieSchema) null);
}, "Should throw exception for null type");
}
@@ -812,7 +798,7 @@ public class TestHoodieSchema {
assertTrue(decimalSchema instanceof HoodieSchema.Decimal);
assertEquals(HoodieSchemaType.DECIMAL, decimalSchema.getType());
- assertEquals(Option.of("decimal(10,2)"), decimalSchema.getName());
+ assertEquals("decimal(10,2)", decimalSchema.getName());
assertEquals(10, ((HoodieSchema.Decimal) decimalSchema).getPrecision());
assertEquals(2, ((HoodieSchema.Decimal) decimalSchema).getScale());
LogicalTypes.Decimal avroLogicalType = (LogicalTypes.Decimal)
decimalSchema.toAvroSchema().getLogicalType();
@@ -836,7 +822,7 @@ public class TestHoodieSchema {
HoodieSchema timestampSchema = HoodieSchema.createTimestampMillis();
assertEquals(HoodieSchemaType.TIMESTAMP, timestampSchema.getType());
- assertEquals("timestamp-millis", timestampSchema.getName().get());
+ assertEquals("timestamp-millis", timestampSchema.getName());
assertTrue(((HoodieSchema.Timestamp) timestampSchema).isUtcAdjusted());
assertEquals(HoodieSchema.TimePrecision.MILLIS, ((HoodieSchema.Timestamp)
timestampSchema).getPrecision());
assertInstanceOf(LogicalTypes.TimestampMillis.class,
timestampSchema.toAvroSchema().getLogicalType());
@@ -847,7 +833,7 @@ public class TestHoodieSchema {
HoodieSchema timestampSchema = HoodieSchema.createTimestampMicros();
assertEquals(HoodieSchemaType.TIMESTAMP, timestampSchema.getType());
- assertEquals("timestamp-micros", timestampSchema.getName().get());
+ assertEquals("timestamp-micros", timestampSchema.getName());
assertTrue(((HoodieSchema.Timestamp) timestampSchema).isUtcAdjusted());
assertEquals(HoodieSchema.TimePrecision.MICROS, ((HoodieSchema.Timestamp)
timestampSchema).getPrecision());
assertInstanceOf(LogicalTypes.TimestampMicros.class,
timestampSchema.toAvroSchema().getLogicalType());
@@ -858,7 +844,7 @@ public class TestHoodieSchema {
HoodieSchema timestampSchema = HoodieSchema.createLocalTimestampMillis();
assertEquals(HoodieSchemaType.TIMESTAMP, timestampSchema.getType());
- assertEquals("local-timestamp-millis", timestampSchema.getName().get());
+ assertEquals("local-timestamp-millis", timestampSchema.getName());
assertFalse(((HoodieSchema.Timestamp) timestampSchema).isUtcAdjusted());
assertEquals(HoodieSchema.TimePrecision.MILLIS, ((HoodieSchema.Timestamp)
timestampSchema).getPrecision());
assertInstanceOf(LogicalTypes.LocalTimestampMillis.class,
timestampSchema.toAvroSchema().getLogicalType());
@@ -869,7 +855,7 @@ public class TestHoodieSchema {
HoodieSchema timestampSchema = HoodieSchema.createLocalTimestampMicros();
assertEquals(HoodieSchemaType.TIMESTAMP, timestampSchema.getType());
- assertEquals("local-timestamp-micros", timestampSchema.getName().get());
+ assertEquals("local-timestamp-micros", timestampSchema.getName());
assertFalse(((HoodieSchema.Timestamp) timestampSchema).isUtcAdjusted());
assertEquals(HoodieSchema.TimePrecision.MICROS, ((HoodieSchema.Timestamp)
timestampSchema).getPrecision());
assertInstanceOf(LogicalTypes.LocalTimestampMicros.class,
timestampSchema.toAvroSchema().getLogicalType());
@@ -880,7 +866,7 @@ public class TestHoodieSchema {
HoodieSchema timeSchema = HoodieSchema.createTimeMillis();
assertEquals(HoodieSchemaType.TIME, timeSchema.getType());
- assertEquals("time-millis", timeSchema.getName().get());
+ assertEquals("time-millis", timeSchema.getName());
assertInstanceOf(LogicalTypes.TimeMillis.class,
timeSchema.toAvroSchema().getLogicalType());
}
@@ -889,7 +875,7 @@ public class TestHoodieSchema {
HoodieSchema timeSchema = HoodieSchema.createTimeMicros();
assertEquals(HoodieSchemaType.TIME, timeSchema.getType());
- assertEquals("time-micros", timeSchema.getName().get());
+ assertEquals("time-micros", timeSchema.getName());
assertInstanceOf(LogicalTypes.TimeMicros.class,
timeSchema.toAvroSchema().getLogicalType());
}
@@ -898,7 +884,7 @@ public class TestHoodieSchema {
HoodieSchema dateSchema = HoodieSchema.createDate();
assertEquals(HoodieSchemaType.DATE, dateSchema.getType());
- assertEquals("date", dateSchema.getName().get());
+ assertEquals("date", dateSchema.getName());
assertInstanceOf(LogicalTypes.Date.class,
dateSchema.toAvroSchema().getLogicalType());
}
@@ -907,7 +893,40 @@ public class TestHoodieSchema {
HoodieSchema uuidSchema = HoodieSchema.createUUID();
assertEquals(HoodieSchemaType.UUID, uuidSchema.getType());
- assertEquals("uuid", uuidSchema.getName().get());
+ assertEquals("uuid", uuidSchema.getName());
assertEquals("uuid", uuidSchema.toAvroSchema().getLogicalType().getName());
}
+
+ @Test
+ void testSubclassIsPreservedInNestedSchemas() {
+ // Create a record schema with a decimal field
+ HoodieSchema decimalFieldSchema = HoodieSchema.createDecimal(10, 2);
+ HoodieSchemaField decimalField = HoodieSchemaField.of("amount",
decimalFieldSchema);
+ List<HoodieSchemaField> fields = Collections.singletonList(decimalField);
+
+ HoodieSchema recordSchema = HoodieSchema.createRecord("Transaction", null,
null, fields);
+
+ // Retrieve the field schema and verify it's still a Decimal subclass
+ Option<HoodieSchemaField> retrievedFieldOpt =
recordSchema.getField("amount");
+ assertTrue(retrievedFieldOpt.isPresent());
+ HoodieSchema retrievedFieldSchema = retrievedFieldOpt.get().schema();
+
+ assertTrue(retrievedFieldSchema instanceof HoodieSchema.Decimal);
+ assertEquals(10, ((HoodieSchema.Decimal)
retrievedFieldSchema).getPrecision());
+ assertEquals(2, ((HoodieSchema.Decimal) retrievedFieldSchema).getScale());
+
+ // Create an array schema with timestamp elements
+ HoodieSchema timestampElementSchema = HoodieSchema.createTimestampMillis();
+ HoodieSchema arraySchema =
HoodieSchema.createArray(timestampElementSchema);
+ HoodieSchema retrievedElementSchema = arraySchema.getElementType();
+ assertTrue(retrievedElementSchema instanceof HoodieSchema.Timestamp);
+ assertEquals(HoodieSchema.TimePrecision.MILLIS, ((HoodieSchema.Timestamp)
retrievedElementSchema).getPrecision());
+
+ // Create a map schema with time values
+ HoodieSchema timeSchema = HoodieSchema.createTimeMillis();
+ HoodieSchema mapSchema = HoodieSchema.createMap(timeSchema);
+ HoodieSchema retrievedValueSchema = mapSchema.getValueType();
+ assertTrue(retrievedValueSchema instanceof HoodieSchema.Time);
+ assertEquals(HoodieSchema.TimePrecision.MILLIS, ((HoodieSchema.Time)
retrievedValueSchema).getPrecision());
+ }
}
diff --git
a/hudi-common/src/test/java/org/apache/hudi/common/schema/TestHoodieSchemaCompatibility.java
b/hudi-common/src/test/java/org/apache/hudi/common/schema/TestHoodieSchemaCompatibility.java
index 192747305d8b..c7e36d52c12b 100644
---
a/hudi-common/src/test/java/org/apache/hudi/common/schema/TestHoodieSchemaCompatibility.java
+++
b/hudi-common/src/test/java/org/apache/hudi/common/schema/TestHoodieSchemaCompatibility.java
@@ -449,7 +449,7 @@ public class TestHoodieSchemaCompatibility {
@Test
void testAreSchemasProjectionEquivalentNullableSchemaComparison() {
- HoodieSchema s1 =
HoodieSchema.createNullableSchema(HoodieSchema.create(HoodieSchemaType.INT));
+ HoodieSchema s1 = HoodieSchema.createNullable(HoodieSchemaType.INT);
HoodieSchema s2 = HoodieSchema.create(HoodieSchemaType.INT);
s2.addProp("prop1", "value1"); // prevent Objects.equals from returning
true
assertTrue(HoodieSchemaCompatibility.areSchemasProjectionEquivalent(s1,
s2));