This is an automated email from the ASF dual-hosted git repository.
voonhous 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 369ca5cb3596 refactor: Migrate
reconcileSchema/reconcileSchemaRequirements to HoodieSchema (#19154)
369ca5cb3596 is described below
commit 369ca5cb3596c1efe8fd8c2ea4ab922fda21bdf1
Author: voonhous <[email protected]>
AuthorDate: Fri Jul 3 18:52:22 2026 +0800
refactor: Migrate reconcileSchema/reconcileSchemaRequirements to
HoodieSchema (#19154)
The reconcile adapters in AvroSchemaEvolutionUtils took Avro Schema in/out
and
immediately wrapped inputs via HoodieSchema.fromAvroSchema, while every
caller
already held a HoodieSchema and passed .toAvroSchema(). Migrate the three
signatures
to HoodieSchema:
- reconcileSchema(Schema, InternalSchema) -> (HoodieSchema, InternalSchema)
- reconcileSchema(Schema, Schema): Schema -> (HoodieSchema, HoodieSchema):
HoodieSchema
- reconcileSchemaRequirements(Schema, Schema): Schema -> HoodieSchema in/out
Callers (BaseHoodieWriteClient, FileGroupReaderBasedMergeHandle,
HoodieMergeHelper,
HoodieSchemaUtils.scala x3, and the unit test) drop the
.toAvroSchema()/.getAvroSchema()
round-trips; the InternalSchema convert(...) boundary is unchanged. Drops
the
org.apache.avro.Schema import from AvroSchemaEvolutionUtils.
Part of the Avro Schema -> HoodieSchema migration (#14263).
---
.../apache/hudi/client/BaseHoodieWriteClient.java | 2 +-
.../hudi/io/FileGroupReaderBasedMergeHandle.java | 2 +-
.../table/action/commit/HoodieMergeHelper.java | 2 +-
.../schema/utils/AvroSchemaEvolutionUtils.java | 26 ++++++++++------------
.../schema/utils/TestAvroSchemaEvolutionUtils.java | 6 ++---
.../scala/org/apache/hudi/HoodieSchemaUtils.scala | 8 +++----
6 files changed, 21 insertions(+), 25 deletions(-)
diff --git
a/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/client/BaseHoodieWriteClient.java
b/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/client/BaseHoodieWriteClient.java
index 14789a4c667d..932e11da2733 100644
---
a/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/client/BaseHoodieWriteClient.java
+++
b/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/client/BaseHoodieWriteClient.java
@@ -369,7 +369,7 @@ public abstract class BaseHoodieWriteClient<T, I, K, O>
extends BaseHoodieClient
internalSchema =
InternalSchemaUtils.searchSchema(Long.parseLong(instantTime),
SerDeHelper.parseSchemas(historySchemaStr));
}
- InternalSchema evolvedSchema =
AvroSchemaEvolutionUtils.reconcileSchema(schema.toAvroSchema(), internalSchema,
config.getBooleanOrDefault(HoodieCommonConfig.SET_NULL_FOR_MISSING_COLUMNS));
+ InternalSchema evolvedSchema =
AvroSchemaEvolutionUtils.reconcileSchema(schema, internalSchema,
config.getBooleanOrDefault(HoodieCommonConfig.SET_NULL_FOR_MISSING_COLUMNS));
if (evolvedSchema.equals(internalSchema)) {
metadata.addMetadata(SerDeHelper.LATEST_SCHEMA,
SerDeHelper.toJson(evolvedSchema));
//TODO save history schema by metaTable
diff --git
a/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/io/FileGroupReaderBasedMergeHandle.java
b/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/io/FileGroupReaderBasedMergeHandle.java
index 530734172ba6..8de3ff23f68e 100644
---
a/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/io/FileGroupReaderBasedMergeHandle.java
+++
b/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/io/FileGroupReaderBasedMergeHandle.java
@@ -279,7 +279,7 @@ public class FileGroupReaderBasedMergeHandle<T, I, K, O>
extends HoodieWriteMerg
}
boolean usePosition =
config.getBooleanOrDefault(MERGE_USE_RECORD_POSITIONS);
Option<InternalSchema> internalSchemaOption =
SerDeHelper.fromJson(config.getInternalSchema())
- .map(internalSchema ->
AvroSchemaEvolutionUtils.reconcileSchema(writeSchemaWithMetaFields.toAvroSchema(),
internalSchema,
+ .map(internalSchema ->
AvroSchemaEvolutionUtils.reconcileSchema(writeSchemaWithMetaFields,
internalSchema,
config.getBooleanOrDefault(HoodieCommonConfig.SET_NULL_FOR_MISSING_COLUMNS)));
long maxMemoryPerCompaction = getMaxMemoryForMerge();
props.put(HoodieMemoryConfig.MAX_MEMORY_FOR_MERGE.key(),
String.valueOf(maxMemoryPerCompaction));
diff --git
a/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/table/action/commit/HoodieMergeHelper.java
b/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/table/action/commit/HoodieMergeHelper.java
index f887e4d5ac74..2f571e0d3e2b 100644
---
a/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/table/action/commit/HoodieMergeHelper.java
+++
b/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/table/action/commit/HoodieMergeHelper.java
@@ -170,7 +170,7 @@ public class HoodieMergeHelper<T> extends BaseMergeHelper {
// TODO support bootstrap
if (querySchemaOpt.isPresent() &&
!baseFile.getBootstrapBaseFile().isPresent()) {
// check implicitly add columns, and position reorder(spark sql may
change cols order)
- InternalSchema querySchema =
AvroSchemaEvolutionUtils.reconcileSchema(writerSchema.toAvroSchema(),
+ InternalSchema querySchema =
AvroSchemaEvolutionUtils.reconcileSchema(writerSchema,
querySchemaOpt.get(),
writeConfig.getBooleanOrDefault(HoodieCommonConfig.SET_NULL_FOR_MISSING_COLUMNS));
long commitInstantTime = Long.parseLong(baseFile.getCommitTime());
InternalSchema fileSchema =
InternalSchemaCache.getInternalSchemaByVersionId(commitInstantTime, metaClient);
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 5ae6f203f0f3..c31b5c057181 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
@@ -24,8 +24,6 @@ import org.apache.hudi.internal.schema.InternalSchema;
import org.apache.hudi.internal.schema.action.TableChanges;
import org.apache.hudi.internal.schema.action.TableChangesHelper;
-import org.apache.avro.Schema;
-
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -64,12 +62,12 @@ public class AvroSchemaEvolutionUtils {
* nullable in the result. Otherwise, no
updates will be made to those fields.
* @return reconcile Schema
*/
- public static InternalSchema reconcileSchema(Schema incomingSchema,
InternalSchema oldTableSchema, boolean makeMissingFieldsNullable) {
+ public static InternalSchema reconcileSchema(HoodieSchema incomingSchema,
InternalSchema oldTableSchema, boolean makeMissingFieldsNullable) {
/* If incoming schema is null, we fall back on table schema. */
- if (incomingSchema.getType() == Schema.Type.NULL) {
+ if (incomingSchema.isSchemaNull()) {
return oldTableSchema;
}
- InternalSchema inComingInternalSchema =
convert(HoodieSchema.fromAvroSchema(incomingSchema),
oldTableSchema.getNameToPosition());
+ InternalSchema inComingInternalSchema = convert(incomingSchema,
oldTableSchema.getNameToPosition());
// check column add/missing
List<String> colNamesFromIncoming =
inComingInternalSchema.getAllColsFullName();
List<String> colNamesFromOldSchema = oldTableSchema.getAllColsFullName();
@@ -149,8 +147,8 @@ public class AvroSchemaEvolutionUtils {
return evolvedSchema;
}
- public static Schema reconcileSchema(Schema incomingSchema, Schema
oldTableSchema, boolean makeMissingFieldsNullable) {
- return convert(reconcileSchema(incomingSchema,
convert(HoodieSchema.fromAvroSchema(oldTableSchema)),
makeMissingFieldsNullable), oldTableSchema.getFullName()).toAvroSchema();
+ public static HoodieSchema reconcileSchema(HoodieSchema incomingSchema,
HoodieSchema oldTableSchema, boolean makeMissingFieldsNullable) {
+ return convert(reconcileSchema(incomingSchema, convert(oldTableSchema),
makeMissingFieldsNullable), oldTableSchema.getFullName());
}
/**
@@ -169,18 +167,18 @@ public class AvroSchemaEvolutionUtils {
* @param targetSchema target schema that source schema will be reconciled
against
* @return schema (based off {@code source} one) that has nullability
constraints and datatypes reconciled
*/
- public static Schema reconcileSchemaRequirements(Schema sourceSchema, Schema
targetSchema, boolean shouldReorderColumns) {
- if (targetSchema.getType() == Schema.Type.NULL ||
targetSchema.getFields().isEmpty()) {
+ public static HoodieSchema reconcileSchemaRequirements(HoodieSchema
sourceSchema, HoodieSchema targetSchema, boolean shouldReorderColumns) {
+ if (targetSchema.isSchemaNull() || targetSchema.getFields().isEmpty()) {
return sourceSchema;
}
- if (sourceSchema == null || sourceSchema.getType() == Schema.Type.NULL ||
sourceSchema.getFields().isEmpty()) {
+ if (sourceSchema == null || sourceSchema.isSchemaNull() ||
sourceSchema.getFields().isEmpty()) {
return targetSchema;
}
- InternalSchema targetInternalSchema =
convert(HoodieSchema.fromAvroSchema(targetSchema));
+ InternalSchema targetInternalSchema = convert(targetSchema);
// Use existing fieldIds for consistent field ordering between commits
when shouldReorderColumns is true
- InternalSchema sourceInternalSchema =
convert(HoodieSchema.fromAvroSchema(sourceSchema), shouldReorderColumns ?
targetInternalSchema.getNameToPosition() : Collections.emptyMap());
+ InternalSchema sourceInternalSchema = convert(sourceSchema,
shouldReorderColumns ? targetInternalSchema.getNameToPosition() :
Collections.emptyMap());
List<String> colNamesSourceSchema =
sourceInternalSchema.getAllColsFullName();
List<String> colNamesTargetSchema =
targetInternalSchema.getAllColsFullName();
@@ -200,7 +198,7 @@ public class AvroSchemaEvolutionUtils {
if (nullableUpdateColsInSource.isEmpty() &&
typeUpdateColsInSource.isEmpty()) {
//standardize order of unions
- return convert(sourceInternalSchema,
sourceSchema.getFullName()).toAvroSchema();
+ return convert(sourceInternalSchema, sourceSchema.getFullName());
}
TableChanges.ColumnUpdateChange schemaChange =
TableChanges.ColumnUpdateChange.get(sourceInternalSchema);
@@ -218,7 +216,7 @@ public class AvroSchemaEvolutionUtils {
}
- return
convert(SchemaChangeUtils.applyTableChanges2Schema(sourceInternalSchema,
schemaChange), sourceSchema.getFullName()).toAvroSchema();
+ return
convert(SchemaChangeUtils.applyTableChanges2Schema(sourceInternalSchema,
schemaChange), sourceSchema.getFullName());
}
}
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 662436de9d75..19d4205d3556 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
@@ -486,7 +486,7 @@ public class TestAvroSchemaEvolutionUtils {
);
evolvedRecord =
(Types.RecordType)InternalSchemaBuilder.getBuilder().refreshNewId(evolvedRecord,
new AtomicInteger(0));
HoodieSchema evolvedSchema =
InternalSchemaConverter.convert(evolvedRecord, "test1");
- InternalSchema result =
AvroSchemaEvolutionUtils.reconcileSchema(evolvedSchema.getAvroSchema(),
oldSchema, false);
+ InternalSchema result =
AvroSchemaEvolutionUtils.reconcileSchema(evolvedSchema, oldSchema, false);
Types.RecordType checkedRecord = Types.RecordType.get(
Types.Field.get(0, false, "id", Types.IntType.get()),
Types.Field.get(1, true, "data", Types.StringType.get()),
@@ -541,7 +541,7 @@ public class TestAvroSchemaEvolutionUtils {
+
"{\"name\":\"d2\",\"type\":[\"null\",{\"type\":\"int\",\"logicalType\":\"date\"}],\"default\":null}]}");
HoodieSchema simpleReconcileSchema =
InternalSchemaConverter.convert(AvroSchemaEvolutionUtils
- .reconcileSchema(incomingSchema.getAvroSchema(),
InternalSchemaConverter.convert(schema), false), "schemaNameFallback");
+ .reconcileSchema(incomingSchema,
InternalSchemaConverter.convert(schema), false), "schemaNameFallback");
Assertions.assertEquals(simpleCheckSchema, simpleReconcileSchema);
}
@@ -563,7 +563,7 @@ public class TestAvroSchemaEvolutionUtils {
InternalSchema oldInternalSchema =
InternalSchemaConverter.convert(oldSchema);
// set a non-default schema id for old table schema, e.g., 2.
oldInternalSchema.setSchemaId(2);
- InternalSchema evolvedSchema =
AvroSchemaEvolutionUtils.reconcileSchema(incomingSchema.getAvroSchema(),
oldInternalSchema, false);
+ InternalSchema evolvedSchema =
AvroSchemaEvolutionUtils.reconcileSchema(incomingSchema, oldInternalSchema,
false);
// the evolved schema should be the old table schema, since there is no
type change at all.
Assertions.assertEquals(oldInternalSchema, evolvedSchema);
}
diff --git
a/hudi-spark-datasource/hudi-spark-common/src/main/scala/org/apache/hudi/HoodieSchemaUtils.scala
b/hudi-spark-datasource/hudi-spark-common/src/main/scala/org/apache/hudi/HoodieSchemaUtils.scala
index 5a40f45c1241..6c353481595e 100644
---
a/hudi-spark-datasource/hudi-spark-common/src/main/scala/org/apache/hudi/HoodieSchemaUtils.scala
+++
b/hudi-spark-datasource/hudi-spark-common/src/main/scala/org/apache/hudi/HoodieSchemaUtils.scala
@@ -173,7 +173,7 @@ object HoodieSchemaUtils {
if (!mergeIntoWrites && !shouldValidateSchemasCompatibility &&
!allowAutoEvolutionColumnDrop) {
// Default behaviour
val reconciledSchema = if (setNullForMissingColumns) {
-
HoodieSchema.fromAvroSchema(AvroSchemaEvolutionUtils.reconcileSchema(canonicalizedSourceSchema.toAvroSchema(),
latestTableSchema.toAvroSchema(), setNullForMissingColumns))
+ AvroSchemaEvolutionUtils.reconcileSchema(canonicalizedSourceSchema,
latestTableSchema, setNullForMissingColumns)
} else {
canonicalizedSourceSchema
}
@@ -205,7 +205,7 @@ object HoodieSchemaUtils {
// Apply schema evolution, by auto-merging write schema and read schema
val setNullForMissingColumns =
opts.getOrElse(HoodieCommonConfig.SET_NULL_FOR_MISSING_COLUMNS.key(),
HoodieCommonConfig.SET_NULL_FOR_MISSING_COLUMNS.defaultValue()).toBoolean
- val mergedInternalSchema =
AvroSchemaEvolutionUtils.reconcileSchema(canonicalizedSourceSchema.toAvroSchema(),
internalSchema, setNullForMissingColumns)
+ val mergedInternalSchema =
AvroSchemaEvolutionUtils.reconcileSchema(canonicalizedSourceSchema,
internalSchema, setNullForMissingColumns)
val evolvedSchema =
InternalSchemaConverter.convert(mergedInternalSchema,
latestTableSchema.getFullName)
val shouldRemoveMetaDataFromInternalSchema =
sourceSchema.getFields.asScala.filter(f =>
f.name().equalsIgnoreCase(HoodieRecord.RECORD_KEY_METADATA_FIELD)).isEmpty
if (shouldRemoveMetaDataFromInternalSchema)
HoodieCommonSchemaUtils.removeMetadataFields(evolvedSchema) else evolvedSchema
@@ -257,9 +257,7 @@ object HoodieSchemaUtils {
*/
private def canonicalizeSchema(sourceSchema: HoodieSchema,
latestTableSchema: HoodieSchema, opts : Map[String, String],
shouldReorderColumns: Boolean): HoodieSchema
= {
- HoodieSchema.fromAvroSchema(
- reconcileSchemaRequirements(sourceSchema.toAvroSchema(),
latestTableSchema.toAvroSchema(), shouldReorderColumns)
- )
+ reconcileSchemaRequirements(sourceSchema, latestTableSchema,
shouldReorderColumns)
}