This is an automated email from the ASF dual-hosted git repository.
danny0405 pushed a commit to branch branch-0.x
in repository https://gitbox.apache.org/repos/asf/hudi.git
The following commit(s) were added to refs/heads/branch-0.x by this push:
new b171eb653819 [HUDI-9597] Do not evolve schema if reconciled schema
keeps unchanged except version id (#13731) (#17849)
b171eb653819 is described below
commit b171eb65381912d45e29ad45e7cd0c673a880237
Author: Lin Liu <[email protected]>
AuthorDate: Thu Jan 22 22:32:47 2026 -0800
[HUDI-9597] Do not evolve schema if reconciled schema keeps unchanged
except version id (#13731) (#17849)
Co-authored-by: Shuo Cheng <[email protected]>
---
.../hudi/internal/schema/InternalSchema.java | 10 ++++++++++
.../schema/utils/AvroSchemaEvolutionUtils.java | 7 ++++++-
.../schema/utils/TestAvroSchemaEvolutionUtils.java | 23 ++++++++++++++++++++++
3 files changed, 39 insertions(+), 1 deletion(-)
diff --git
a/hudi-common/src/main/java/org/apache/hudi/internal/schema/InternalSchema.java
b/hudi-common/src/main/java/org/apache/hudi/internal/schema/InternalSchema.java
index ce5f8f259da2..99403f8b6b83 100644
---
a/hudi-common/src/main/java/org/apache/hudi/internal/schema/InternalSchema.java
+++
b/hudi-common/src/main/java/org/apache/hudi/internal/schema/InternalSchema.java
@@ -289,6 +289,16 @@ public class InternalSchema implements Serializable {
return record.equals(that.record);
}
+ public boolean equalsIgnoringVersion(Object o) {
+ if (this == o) {
+ return true;
+ } else if (!(o instanceof InternalSchema)) {
+ return false;
+ }
+ InternalSchema that = (InternalSchema) o;
+ return record.equals(that.record);
+ }
+
@Override
public int hashCode() {
return record.hashCode();
diff --git
a/hudi-common/src/main/java/org/apache/hudi/internal/schema/utils/AvroSchemaEvolutionUtils.java
b/hudi-common/src/main/java/org/apache/hudi/internal/schema/utils/AvroSchemaEvolutionUtils.java
index 7ca0cb7f81e4..8f0298d533ad 100644
---
a/hudi-common/src/main/java/org/apache/hudi/internal/schema/utils/AvroSchemaEvolutionUtils.java
+++
b/hudi-common/src/main/java/org/apache/hudi/internal/schema/utils/AvroSchemaEvolutionUtils.java
@@ -113,7 +113,12 @@ public class AvroSchemaEvolutionUtils {
typeChange.updateColumnType(col, inComingInternalSchema.findType(col));
});
- return
SchemaChangeUtils.applyTableChanges2Schema(internalSchemaAfterAddColumns,
typeChange);
+ InternalSchema evolvedSchema =
SchemaChangeUtils.applyTableChanges2Schema(internalSchemaAfterAddColumns,
typeChange);
+ // If evolvedSchema is exactly the same as the oldSchema, except the
version number, return the old schema
+ if (evolvedSchema.equalsIgnoringVersion(oldTableSchema)) {
+ return oldTableSchema;
+ }
+ return evolvedSchema;
}
public static Schema reconcileSchema(Schema incomingSchema, Schema
oldTableSchema) {
diff --git
a/hudi-common/src/test/java/org/apache/hudi/internal/schema/utils/TestAvroSchemaEvolutionUtils.java
b/hudi-common/src/test/java/org/apache/hudi/internal/schema/utils/TestAvroSchemaEvolutionUtils.java
index 4027bd28178f..6394e87b9b8d 100644
---
a/hudi-common/src/test/java/org/apache/hudi/internal/schema/utils/TestAvroSchemaEvolutionUtils.java
+++
b/hudi-common/src/test/java/org/apache/hudi/internal/schema/utils/TestAvroSchemaEvolutionUtils.java
@@ -537,4 +537,27 @@ public class TestAvroSchemaEvolutionUtils {
.reconcileSchema(incomingSchema,
AvroInternalSchemaConverter.convert(schema)), "schemaNameFallback");
Assertions.assertEquals(simpleReconcileSchema, simpleCheckSchema);
}
+
+ @Test
+ public void testNotEvolveSchemaIfReconciledSchemaUnchanged() {
+ // a: boolean, c: long, c_1: long, d: date
+ Schema oldSchema = create("simple",
+ new Schema.Field("a",
AvroInternalSchemaConverter.nullableSchema(Schema.create(Schema.Type.BOOLEAN)),
null, JsonProperties.NULL_VALUE),
+ new Schema.Field("b",
AvroInternalSchemaConverter.nullableSchema(Schema.create(Schema.Type.INT)),
null, JsonProperties.NULL_VALUE),
+ new Schema.Field("c",
AvroInternalSchemaConverter.nullableSchema(Schema.create(Schema.Type.LONG)),
null, JsonProperties.NULL_VALUE),
+ new Schema.Field("d",
AvroInternalSchemaConverter.nullableSchema(LogicalTypes.date().addToSchema(Schema.create(Schema.Type.INT))),
null, JsonProperties.NULL_VALUE));
+ // incoming schema is part of old schema
+ // a: boolean, b: int, c: long
+ Schema incomingSchema = create("simple",
+ new Schema.Field("a",
AvroInternalSchemaConverter.nullableSchema(Schema.create(Schema.Type.BOOLEAN)),
null, JsonProperties.NULL_VALUE),
+ new Schema.Field("b",
AvroInternalSchemaConverter.nullableSchema(Schema.create(Schema.Type.INT)),
null, JsonProperties.NULL_VALUE),
+ new Schema.Field("c",
AvroInternalSchemaConverter.nullableSchema(Schema.create(Schema.Type.LONG)),
null, JsonProperties.NULL_VALUE));
+
+ InternalSchema oldInternalSchema =
AvroInternalSchemaConverter.convert(oldSchema);
+ // set a non-default schema id for old table schema, e.g., 2.
+ oldInternalSchema.setSchemaId(2);
+ InternalSchema evolvedSchema =
AvroSchemaEvolutionUtils.reconcileSchema(incomingSchema, oldInternalSchema);
+ // the evolved schema should be the old table schema, since there is no
type change at all.
+ Assertions.assertEquals(oldInternalSchema, evolvedSchema);
+ }
}