This is an automated email from the ASF dual-hosted git repository.
danny0405 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 eccbef97f339 feat(flink): support metadata field modes for COW tables
(#19858)
eccbef97f339 is described below
commit eccbef97f33941f7c2826a3f59f59bc235a82ad5
Author: Danny Chan <[email protected]>
AuthorDate: Wed Sep 9 12:06:49 2026 +0800
feat(flink): support metadata field modes for COW tables (#19858)
* feat(flink)!: support metadata field modes for COW tables
---
.../org/apache/hudi/config/HoodieWriteConfig.java | 7 +-
.../apache/hudi/io/HoodieAbstractMergeHandle.java | 7 --
.../org/apache/hudi/io/HoodieConcatHandle.java | 5 +-
.../org/apache/hudi/io/HoodieWriteMergeHandle.java | 8 +-
.../TestHoodieWriteConfigMetaFieldsMode.java | 17 ++++
.../hudi/client/model/HoodieFlinkRecord.java | 12 +++
.../java/org/apache/hudi/io/FlinkConcatHandle.java | 5 +-
.../hudi/io/FlinkIncrementalConcatHandle.java | 7 +-
.../java/org/apache/hudi/io/FlinkMergeHandle.java | 10 ++
.../io/storage/row/HoodieRowDataCreateHandle.java | 31 +++++--
.../row/HoodieRowDataFileWriterFactory.java | 9 +-
.../io/storage/row/HoodieRowDataLanceWriter.java | 28 +++---
.../io/storage/row/HoodieRowDataParquetWriter.java | 28 +++---
.../hudi/client/model/TestHoodieFlinkRecord.java | 37 ++++++++
.../storage/row/TestHoodieRowDataCreateHandle.java | 61 ++++++++++++
.../storage/row/TestHoodieRowDataLanceWriter.java | 41 +++++++-
.../TestBaseSparkInternalRowReaderContext.java | 2 +-
.../apache/hudi/common/engine/RecordContext.java | 12 +--
.../hudi/common/table/HoodieTableConfig.java | 3 +-
.../hudi/common/avro/TestAvroRecordContext.java | 28 ++++++
.../common/avro/TestHoodieAvroReaderContext.java | 10 +-
.../common/table/read/SchemaHandlerTestBase.java | 1 +
.../read/TestFileGroupReaderSchemaHandler.java | 5 +
.../TestParquetRowIndexBasedSchemaHandler.java | 1 +
.../buffer/TestFileGroupRecordBufferLoader.java | 1 +
.../buffer/TestKeyBasedFileGroupRecordBuffer.java | 9 ++
.../TestSortedKeyBasedFileGroupRecordBuffer.java | 1 +
...TestStreamingKeyBasedFileGroupRecordBuffer.java | 4 +
.../TestHoodieBackedTableMetadataDataCleanup.java | 2 +-
.../apache/hudi/configuration/OptionsResolver.java | 13 ++-
.../hudi/sink/bulk/BulkInsertWriterHelper.java | 12 ++-
.../java/org/apache/hudi/util/StreamerUtil.java | 2 +-
.../client/TestFlinkWriteClientFunctional.java | 103 ++++++++++++++++++++-
.../hudi/configuration/TestOptionsResolver.java | 12 +++
.../org/apache/hudi/sink/TestWriteCopyOnWrite.java | 92 +++++++++++++++++-
.../org/apache/hudi/sink/TestWriteMergeOnRead.java | 14 ++-
.../hudi/sink/TestWriteMergeOnReadWithCompact.java | 14 ++-
.../hudi/sink/bulk/TestBulkInsertWriteHelper.java | 78 ++++++++++++++++
.../org/apache/hudi/sink/utils/TestWriteBase.java | 10 ++
.../table/TestHoodieFileGroupReaderOnFlink.java | 10 +-
.../format/TestFlinkRowDataReaderContext.java | 2 +-
.../org/apache/hudi/utils/TestStreamerUtil.java | 17 ++++
.../hudi/hadoop/TestHiveHoodieReaderContext.java | 16 ++--
.../hudi/functional/TestBufferedRecordMerger.java | 3 +-
.../hudi/functional/TestMetaFieldsModeE2E.java | 68 ++++++++++++++
.../read/TestHoodieFileGroupReaderOnSpark.scala | 12 ++-
46 files changed, 761 insertions(+), 109 deletions(-)
diff --git
a/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/config/HoodieWriteConfig.java
b/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/config/HoodieWriteConfig.java
index dd93b0bd7faf..ffe2c0deb233 100644
---
a/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/config/HoodieWriteConfig.java
+++
b/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/config/HoodieWriteConfig.java
@@ -4045,10 +4045,9 @@ public class HoodieWriteConfig extends HoodieConfig {
+ "For MoR use %s=ALL or %s=NONE.",
HoodieTableConfig.META_FIELDS_MODE.key(), metaFieldsMode,
HoodieTableConfig.META_FIELDS_MODE.key(),
HoodieTableConfig.META_FIELDS_MODE.key()));
- // Selective meta-field modes are wired only for the Spark writer path
in this release. Flink
- // RowData / Java-client writers ignore the mode and would silently
produce NONE-mode output.
- checkArgument(!(engineType != EngineType.SPARK && isSelective),
- String.format("%s=%s is currently supported for the Spark writer
only. Support for engine=%s is a follow-up. "
+ // Java-client writers do not yet support selective meta-field
population.
+ checkArgument(!(engineType == EngineType.JAVA && isSelective),
+ String.format("%s=%s is currently supported for Spark and Flink
writers only. Support for engine=%s is a follow-up. "
+ "Use %s=ALL or %s=NONE.",
HoodieTableConfig.META_FIELDS_MODE.key(), metaFieldsMode,
engineType,
HoodieTableConfig.META_FIELDS_MODE.key(),
HoodieTableConfig.META_FIELDS_MODE.key()));
diff --git
a/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/io/HoodieAbstractMergeHandle.java
b/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/io/HoodieAbstractMergeHandle.java
index 54db1467a49f..36c4525a7cf2 100644
---
a/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/io/HoodieAbstractMergeHandle.java
+++
b/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/io/HoodieAbstractMergeHandle.java
@@ -26,7 +26,6 @@ import org.apache.hudi.common.model.HoodieRecord;
import org.apache.hudi.common.model.HoodieWriteStat;
import org.apache.hudi.common.model.IOType;
import org.apache.hudi.common.util.Option;
-import org.apache.hudi.common.util.ValidationUtils;
import org.apache.hudi.config.HoodieWriteConfig;
import org.apache.hudi.keygen.BaseKeyGenerator;
import org.apache.hudi.storage.StoragePath;
@@ -86,7 +85,6 @@ public abstract class HoodieAbstractMergeHandle<T, I, K, O>
extends HoodieWriteH
this.keyGeneratorOpt = keyGeneratorOpt;
initPartitionMetadataAndFilePaths(partitionPath);
initWriteStatus(fileId, partitionPath);
- validateAndSetAndKeyGenProps(keyGeneratorOpt, config.populateMetaFields());
}
/**
@@ -151,11 +149,6 @@ public abstract class HoodieAbstractMergeHandle<T, I, K,
O> extends HoodieWriteH
setWriteStatusPath();
}
- private void validateAndSetAndKeyGenProps(Option<BaseKeyGenerator>
keyGeneratorOpt, boolean populateMetaFields) {
- ValidationUtils.checkArgument(populateMetaFields ==
!keyGeneratorOpt.isPresent());
- this.keyGeneratorOpt = keyGeneratorOpt;
- }
-
protected String createNewFileName(String oldFileName) {
return FSUtils.makeBaseFileName(instantTime, writeToken, fileId,
hoodieTable.getBaseFileExtension());
}
diff --git
a/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/io/HoodieConcatHandle.java
b/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/io/HoodieConcatHandle.java
index a7a14cc4936e..1d8823c67183 100644
---
a/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/io/HoodieConcatHandle.java
+++
b/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/io/HoodieConcatHandle.java
@@ -23,6 +23,7 @@ import org.apache.hudi.common.model.HoodieBaseFile;
import org.apache.hudi.common.model.HoodieKey;
import org.apache.hudi.common.model.HoodieRecord;
import org.apache.hudi.common.model.HoodieRecordLocation;
+import org.apache.hudi.common.model.MetaFieldsMode;
import org.apache.hudi.common.schema.HoodieSchema;
import org.apache.hudi.common.util.Option;
import org.apache.hudi.config.HoodieWriteConfig;
@@ -93,8 +94,8 @@ public class HoodieConcatHandle<T, I, K, O> extends
HoodieWriteMergeHandle<T, I,
*/
@Override
public void write(HoodieRecord oldRecord) {
- HoodieSchema oldSchema = config.populateMetaFields() ?
writeSchemaWithMetaFields : writeSchema;
- String key = oldRecord.getRecordKey(oldSchema, keyGeneratorOpt);
+ HoodieSchema oldSchema = metaFieldsMode == MetaFieldsMode.NONE ?
writeSchema : writeSchemaWithMetaFields;
+ String key = getRecordKey(oldRecord, oldSchema);
try {
// NOTE: We're enforcing preservation of the record metadata to keep
existing semantic
writeToFile(new HoodieKey(key, partitionPath), oldRecord, oldSchema,
config.getPayloadConfig().getProps(), true);
diff --git
a/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/io/HoodieWriteMergeHandle.java
b/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/io/HoodieWriteMergeHandle.java
index fbd7b3d37872..b29746ec583e 100644
---
a/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/io/HoodieWriteMergeHandle.java
+++
b/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/io/HoodieWriteMergeHandle.java
@@ -112,7 +112,7 @@ public class HoodieWriteMergeHandle<T, I, K, O> extends
HoodieAbstractMergeHandl
// Read from the TABLE config, not the write config -- see the note on
BaseCreateHandle. Resolved
// once rather than per record: writeToFile consults it on the
preserve-metadata path, which runs
// for every record copied forward during a merge.
- private final MetaFieldsMode metaFieldsMode =
+ protected final MetaFieldsMode metaFieldsMode =
hoodieTable.getMetaClient().getTableConfig().getMetaFieldsMode();
protected long recordsWritten = 0;
@@ -374,7 +374,7 @@ public class HoodieWriteMergeHandle<T, I, K, O> extends
HoodieAbstractMergeHandl
HoodieSchema oldSchema = writeSchemaWithMetaFields;
HoodieSchema newSchema = getNewSchema();
boolean copyOldRecord = true;
- String key = oldRecord.getRecordKey(oldSchema, keyGeneratorOpt);
+ String key = getRecordKey(oldRecord, oldSchema);
TypedProperties props = config.getPayloadConfig().getProps();
if (keyToNewRecords.containsKey(key)) {
// If we have duplicate records that we are updating, then the hoodie
record will be deflated after
@@ -420,6 +420,10 @@ public class HoodieWriteMergeHandle<T, I, K, O> extends
HoodieAbstractMergeHandl
}
}
+ protected String getRecordKey(HoodieRecord<T> record, HoodieSchema schema) {
+ return record.getRecordKey(schema, keyGeneratorOpt);
+ }
+
protected void writeToFile(HoodieKey key, HoodieRecord<T> record,
HoodieSchema schema, Properties props, boolean shouldPreserveRecordMetadata)
throws IOException {
if (shouldPreserveRecordMetadata) {
// NOTE: `FILENAME_METADATA_FIELD` has to be rewritten to correctly
point to the
diff --git
a/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/config/TestHoodieWriteConfigMetaFieldsMode.java
b/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/config/TestHoodieWriteConfigMetaFieldsMode.java
index f8becb176f83..b918acfaab7c 100644
---
a/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/config/TestHoodieWriteConfigMetaFieldsMode.java
+++
b/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/config/TestHoodieWriteConfigMetaFieldsMode.java
@@ -19,11 +19,14 @@
package org.apache.hudi.config;
import org.apache.hudi.common.config.TypedProperties;
+import org.apache.hudi.common.engine.EngineType;
import org.apache.hudi.common.model.HoodieTableType;
import org.apache.hudi.common.model.MetaFieldsMode;
import org.apache.hudi.common.table.HoodieTableConfig;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
import java.util.Properties;
@@ -41,6 +44,20 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
*/
class TestHoodieWriteConfigMetaFieldsMode {
+ @ParameterizedTest
+ @EnumSource(MetaFieldsMode.class)
+ void flinkSupportsCopyOnWriteModes(MetaFieldsMode mode) {
+ assertEquals(mode, baseBuilder().withEngineType(EngineType.FLINK)
+ .withMetaFieldsMode(mode).build().getMetaFieldsMode());
+ if (mode.isSelective()) {
+ assertThrows(IllegalArgumentException.class, () -> baseBuilder()
+ .withEngineType(EngineType.FLINK).withMetaFieldsMode(mode)
+ .withProps(mergeOnReadProps()).build());
+ assertThrows(IllegalArgumentException.class, () -> baseBuilder()
+ .withEngineType(EngineType.JAVA).withMetaFieldsMode(mode).build());
+ }
+ }
+
private static HoodieWriteConfig.Builder baseBuilder() {
return
HoodieWriteConfig.newBuilder().withPath("file:///tmp/test_hudi_meta_fields_mode");
}
diff --git
a/hudi-client/hudi-flink-client/src/main/java/org/apache/hudi/client/model/HoodieFlinkRecord.java
b/hudi-client/hudi-flink-client/src/main/java/org/apache/hudi/client/model/HoodieFlinkRecord.java
index 913cd5e68350..c39f1fb08a46 100644
---
a/hudi-client/hudi-flink-client/src/main/java/org/apache/hudi/client/model/HoodieFlinkRecord.java
+++
b/hudi-client/hudi-flink-client/src/main/java/org/apache/hudi/client/model/HoodieFlinkRecord.java
@@ -20,6 +20,7 @@ package org.apache.hudi.client.model;
import org.apache.hudi.common.avro.HoodieAvroUtils;
import org.apache.hudi.common.config.HoodieStorageConfig;
+import org.apache.hudi.common.engine.RecordContext;
import org.apache.hudi.common.model.HoodieAvroIndexedRecord;
import org.apache.hudi.common.model.HoodieKey;
import org.apache.hudi.common.model.HoodieOperation;
@@ -125,6 +126,17 @@ public class HoodieFlinkRecord extends
HoodieRecord<RowData> {
return HoodieRecordType.FLINK;
}
+ /**
+ * Returns the cached record key, computing and caching it on the first
lookup if absent.
+ * The record context is not consulted when this record already carries a
key.
+ */
+ public String getRecordKey(HoodieSchema recordSchema, RecordContext<RowData>
recordContext) {
+ if (key == null) {
+ key = new HoodieKey(recordContext.getRecordKey(data, recordSchema),
null);
+ }
+ return getRecordKey();
+ }
+
@Override
public String getRecordKey(HoodieSchema recordSchema,
Option<BaseKeyGenerator> keyGeneratorOpt) {
if (key == null) {
diff --git
a/hudi-client/hudi-flink-client/src/main/java/org/apache/hudi/io/FlinkConcatHandle.java
b/hudi-client/hudi-flink-client/src/main/java/org/apache/hudi/io/FlinkConcatHandle.java
index 4aa8bcc19492..d3392e199b69 100644
---
a/hudi-client/hudi-flink-client/src/main/java/org/apache/hudi/io/FlinkConcatHandle.java
+++
b/hudi-client/hudi-flink-client/src/main/java/org/apache/hudi/io/FlinkConcatHandle.java
@@ -56,8 +56,9 @@ public class FlinkConcatHandle<T, I, K, O>
*/
@Override
public void write(HoodieRecord oldRecord) {
- HoodieSchema oldSchema = config.populateMetaFields() ?
writeSchemaWithMetaFields : writeSchema;
- String key = oldRecord.getRecordKey(oldSchema, keyGeneratorOpt);
+ // Match the schema supplied by HoodieMergeHelper: metadata columns are
present even in NONE mode.
+ HoodieSchema oldSchema = writeSchemaWithMetaFields;
+ String key = getRecordKey(oldRecord, oldSchema);
try {
fileWriter.write(key, oldRecord, oldSchema);
} catch (IOException | RuntimeException e) {
diff --git
a/hudi-client/hudi-flink-client/src/main/java/org/apache/hudi/io/FlinkIncrementalConcatHandle.java
b/hudi-client/hudi-flink-client/src/main/java/org/apache/hudi/io/FlinkIncrementalConcatHandle.java
index 1e3602b196a0..cc8679b8d885 100644
---
a/hudi-client/hudi-flink-client/src/main/java/org/apache/hudi/io/FlinkIncrementalConcatHandle.java
+++
b/hudi-client/hudi-flink-client/src/main/java/org/apache/hudi/io/FlinkIncrementalConcatHandle.java
@@ -57,10 +57,11 @@ public class FlinkIncrementalConcatHandle<T, I, K, O>
*/
@Override
public void write(HoodieRecord oldRecord) {
- HoodieSchema oldSchema = config.populateMetaFields() ?
writeSchemaWithMetaFields : writeSchema;
- String key = oldRecord.getRecordKey(oldSchema, keyGeneratorOpt);
+ // Match the schema supplied by HoodieMergeHelper: metadata columns are
present even in NONE mode.
+ HoodieSchema oldSchema = writeSchemaWithMetaFields;
+ String key = getRecordKey(oldRecord, oldSchema);
try {
- fileWriter.write(key, oldRecord, writeSchema);
+ fileWriter.write(key, oldRecord, oldSchema);
} catch (IOException | RuntimeException e) {
String errMsg = String.format(
"Failed to write old record into new file for key %s from old file
%s to new file %s with writerSchema %s",
diff --git
a/hudi-client/hudi-flink-client/src/main/java/org/apache/hudi/io/FlinkMergeHandle.java
b/hudi-client/hudi-flink-client/src/main/java/org/apache/hudi/io/FlinkMergeHandle.java
index b8f8c90310f6..5d5d496667ad 100644
---
a/hudi-client/hudi-flink-client/src/main/java/org/apache/hudi/io/FlinkMergeHandle.java
+++
b/hudi-client/hudi-flink-client/src/main/java/org/apache/hudi/io/FlinkMergeHandle.java
@@ -18,10 +18,13 @@
package org.apache.hudi.io;
+import org.apache.hudi.client.model.HoodieFlinkRecord;
+import org.apache.hudi.common.engine.RecordContext;
import org.apache.hudi.common.engine.TaskContextSupplier;
import org.apache.hudi.common.fs.FSUtils;
import org.apache.hudi.common.model.HoodieBaseFile;
import org.apache.hudi.common.model.HoodieRecord;
+import org.apache.hudi.common.schema.HoodieSchema;
import org.apache.hudi.common.util.Option;
import org.apache.hudi.config.HoodieWriteConfig;
import org.apache.hudi.exception.HoodieException;
@@ -31,6 +34,7 @@ import org.apache.hudi.table.marker.WriteMarkers;
import org.apache.hudi.table.marker.WriteMarkersFactory;
import lombok.extern.slf4j.Slf4j;
+import org.apache.flink.table.data.RowData;
import java.io.IOException;
import java.util.HashMap;
@@ -110,6 +114,12 @@ public class FlinkMergeHandle<T, I, K, O>
writeMarkers.createIfNotExists(partitionPath, dataFileName, getIOType());
}
+ @Override
+ @SuppressWarnings("unchecked")
+ protected String getRecordKey(HoodieRecord<T> record, HoodieSchema schema) {
+ return ((HoodieFlinkRecord) record).getRecordKey(schema,
(RecordContext<RowData>) readerContext.getRecordContext());
+ }
+
@Override
protected void initIncomingRecordsMap() {
log.info("Initialize on-heap keyToNewRecords for incoming records.");
diff --git
a/hudi-client/hudi-flink-client/src/main/java/org/apache/hudi/io/storage/row/HoodieRowDataCreateHandle.java
b/hudi-client/hudi-flink-client/src/main/java/org/apache/hudi/io/storage/row/HoodieRowDataCreateHandle.java
index 60ab015ef017..f5cd0c739930 100644
---
a/hudi-client/hudi-flink-client/src/main/java/org/apache/hudi/io/storage/row/HoodieRowDataCreateHandle.java
+++
b/hudi-client/hudi-flink-client/src/main/java/org/apache/hudi/io/storage/row/HoodieRowDataCreateHandle.java
@@ -29,6 +29,7 @@ import org.apache.hudi.common.model.HoodieRecordDelegate;
import org.apache.hudi.common.model.HoodieRecordLocation;
import org.apache.hudi.common.model.HoodieWriteStat;
import org.apache.hudi.common.model.IOType;
+import org.apache.hudi.common.model.MetaFieldsMode;
import org.apache.hudi.common.schema.HoodieSchema;
import org.apache.hudi.common.util.HoodieTimer;
import org.apache.hudi.common.util.Option;
@@ -80,6 +81,7 @@ public class HoodieRowDataCreateHandle implements
Serializable {
private final String fileId;
private final boolean preserveHoodieMetadata;
private final boolean skipMetadataWrite;
+ private final MetaFieldsMode metaFieldsMode;
// The schema (with meta fields appended when required) used to create the
file writer.
private final HoodieSchema writerSchema;
private final HoodieStorage storage;
@@ -105,6 +107,7 @@ public class HoodieRowDataCreateHandle implements
Serializable {
this.newRecordLocation = new HoodieRecordLocation(instantTime, fileId);
this.preserveHoodieMetadata = preserveHoodieMetadata;
this.skipMetadataWrite = skipMetadataWrite;
+ this.metaFieldsMode = writeConfig.getMetaFieldsMode();
this.writerSchema = schema;
this.currTimer = HoodieTimer.start();
this.storage = table.getStorage();
@@ -149,14 +152,26 @@ public class HoodieRowDataCreateHandle implements
Serializable {
String commitInstant;
RowData rowData;
if (!skipMetadataWrite) {
- seqId = preserveHoodieMetadata
- ?
record.getString(HoodieRecord.COMMIT_SEQNO_METADATA_FIELD_ORD).toString()
- : HoodieRecord.generateSequenceId(instantTime, taskPartitionId,
SEQGEN.getAndIncrement());
- commitInstant = preserveHoodieMetadata
- ?
record.getString(HoodieRecord.COMMIT_TIME_METADATA_FIELD_ORD).toString()
- : instantTime;
- rowData = HoodieRowDataCreation.create(commitInstant, seqId,
recordKey, partitionPath, path.getName(),
- record, writeConfig.allowOperationMetadataField(),
preserveHoodieMetadata);
+ if (metaFieldsMode == MetaFieldsMode.ALL) {
+ seqId = preserveHoodieMetadata
+ ?
record.getString(HoodieRecord.COMMIT_SEQNO_METADATA_FIELD_ORD).toString()
+ : HoodieRecord.generateSequenceId(instantTime, taskPartitionId,
SEQGEN.getAndIncrement());
+ commitInstant = preserveHoodieMetadata
+ ?
record.getString(HoodieRecord.COMMIT_TIME_METADATA_FIELD_ORD).toString()
+ : instantTime;
+ rowData = HoodieRowDataCreation.create(commitInstant, seqId,
recordKey, partitionPath, path.getName(),
+ record, writeConfig.allowOperationMetadataField(),
preserveHoodieMetadata);
+ } else if (metaFieldsMode == MetaFieldsMode.NONE) {
+ rowData = HoodieRowDataCreation.create(null, null, null, null, null,
+ record, writeConfig.allowOperationMetadataField(),
preserveHoodieMetadata);
+ } else {
+ commitInstant = !metaFieldsMode.isCommitTimePopulated() ? null :
preserveHoodieMetadata
+ ?
record.getString(HoodieRecord.COMMIT_TIME_METADATA_FIELD_ORD).toString()
+ : instantTime;
+ rowData = HoodieRowDataCreation.create(commitInstant, null, null,
null,
+ metaFieldsMode.isFileNamePopulated() ? path.getName() : null,
+ record, writeConfig.allowOperationMetadataField(),
preserveHoodieMetadata);
+ }
} else {
rowData = record;
}
diff --git
a/hudi-client/hudi-flink-client/src/main/java/org/apache/hudi/io/storage/row/HoodieRowDataFileWriterFactory.java
b/hudi-client/hudi-flink-client/src/main/java/org/apache/hudi/io/storage/row/HoodieRowDataFileWriterFactory.java
index 7b15cfaca90d..1f7323604d1c 100644
---
a/hudi-client/hudi-flink-client/src/main/java/org/apache/hudi/io/storage/row/HoodieRowDataFileWriterFactory.java
+++
b/hudi-client/hudi-flink-client/src/main/java/org/apache/hudi/io/storage/row/HoodieRowDataFileWriterFactory.java
@@ -93,7 +93,7 @@ public class HoodieRowDataFileWriterFactory extends
HoodieFileWriterFactory {
HoodieConfig config,
HoodieSchema schema,
TaskContextSupplier taskContextSupplier) throws IOException {
- boolean populateMetaFields =
MetaFieldsMode.resolve(config).toLegacyPopulateMetaFields();
+ MetaFieldsMode metaFieldsMode = MetaFieldsMode.resolve(config);
boolean withOperation =
config.getBooleanOrDefault(HoodieWriteConfig.ALLOW_OPERATION_METADATA_FIELD);
Pair<StorageConfiguration, HoodieConfig> injectedConfigs =
@@ -102,14 +102,14 @@ public class HoodieRowDataFileWriterFactory extends
HoodieFileWriterFactory {
HoodieConfig hoodieConfig = injectedConfigs.getRight();
Configuration conf = (Configuration)
storageConfiguration.unwrapAs(Configuration.class);
- BloomFilter filter = createBloomFilter(hoodieConfig);
+ BloomFilter filter = enableBloomFilter(metaFieldsMode, hoodieConfig) ?
createBloomFilter(hoodieConfig) : null;
HoodieRowDataParquetWriteSupport writeSupport =
(HoodieRowDataParquetWriteSupport) ReflectionUtils.loadClass(
hoodieConfig.getStringOrDefault(HoodieStorageConfig.HOODIE_PARQUET_FLINK_ROW_DATA_WRITE_SUPPORT_CLASS),
new Class<?>[] {Configuration.class, HoodieSchema.class,
BloomFilter.class},
conf, schema, filter);
return new HoodieRowDataParquetWriter(storagePath,
getParquetConfig(hoodieConfig, writeSupport),
- instantTime, taskContextSupplier, populateMetaFields, withOperation);
+ instantTime, taskContextSupplier, metaFieldsMode, withOperation);
}
@Override
@@ -120,7 +120,6 @@ public class HoodieRowDataFileWriterFactory extends
HoodieFileWriterFactory {
HoodieSchema schema,
TaskContextSupplier taskContextSupplier) {
MetaFieldsMode metaFieldsMode = MetaFieldsMode.resolve(config);
- boolean populateMetaFields = metaFieldsMode.toLegacyPopulateMetaFields();
boolean withOperation =
config.getBooleanOrDefault(HoodieWriteConfig.ALLOW_OPERATION_METADATA_FIELD);
Option<org.apache.hudi.common.bloom.BloomFilter> bloomFilter =
enableBloomFilter(metaFieldsMode, config)
? Option.of(createBloomFilter(config)) : Option.empty();
@@ -134,7 +133,7 @@ public class HoodieRowDataFileWriterFactory extends
HoodieFileWriterFactory {
config.getLongOrDefault(HoodieStorageConfig.LANCE_WRITE_ALLOCATOR_SIZE_BYTES),
config.getLongOrDefault(HoodieStorageConfig.LANCE_WRITE_FLUSH_BYTE_WATERMARK),
config.getBooleanOrDefault(HoodieStorageConfig.WRITE_UTC_TIMEZONE),
- populateMetaFields,
+ metaFieldsMode,
withOperation);
}
diff --git
a/hudi-client/hudi-flink-client/src/main/java/org/apache/hudi/io/storage/row/HoodieRowDataLanceWriter.java
b/hudi-client/hudi-flink-client/src/main/java/org/apache/hudi/io/storage/row/HoodieRowDataLanceWriter.java
index 238974cf3221..e77d8ab624a4 100644
---
a/hudi-client/hudi-flink-client/src/main/java/org/apache/hudi/io/storage/row/HoodieRowDataLanceWriter.java
+++
b/hudi-client/hudi-flink-client/src/main/java/org/apache/hudi/io/storage/row/HoodieRowDataLanceWriter.java
@@ -23,6 +23,7 @@ import org.apache.hudi.common.bloom.BloomFilter;
import org.apache.hudi.common.engine.TaskContextSupplier;
import org.apache.hudi.common.model.HoodieKey;
import org.apache.hudi.common.model.HoodieRecord;
+import org.apache.hudi.common.model.MetaFieldsMode;
import org.apache.hudi.common.schema.HoodieSchema;
import org.apache.hudi.common.util.Option;
import org.apache.hudi.common.util.ValidationUtils;
@@ -55,7 +56,7 @@ public class HoodieRowDataLanceWriter extends
HoodieBaseLanceWriter<RowData, Str
private final String instantTime;
private final long maxFileSize;
private final boolean utcTimestamp;
- private final boolean populateMetaFields;
+ private final MetaFieldsMode metaFieldsMode;
private final boolean withOperation;
private final Function<Long, String> seqIdGenerator;
private long recordCountForNextSizeCheck = MIN_RECORDS_FOR_SIZE_CHECK;
@@ -70,7 +71,7 @@ public class HoodieRowDataLanceWriter extends
HoodieBaseLanceWriter<RowData, Str
long allocatorSize,
long flushByteWatermark,
boolean utcTimestamp,
- boolean populateMetaFields,
+ MetaFieldsMode metaFieldsMode,
boolean withOperation) {
super(file, DEFAULT_BATCH_SIZE, allocatorSize, flushByteWatermark,
bloomFilterOpt.map(HoodieBloomFilterRowDataWriteSupport::new));
@@ -86,7 +87,7 @@ public class HoodieRowDataLanceWriter extends
HoodieBaseLanceWriter<RowData, Str
this.instantTime = instantTime;
this.maxFileSize = maxFileSize;
this.utcTimestamp = utcTimestamp;
- this.populateMetaFields = populateMetaFields;
+ this.metaFieldsMode = metaFieldsMode;
this.withOperation = withOperation;
this.seqIdGenerator = recordIndex -> {
Integer partitionId = taskContextSupplier.getPartitionIdSupplier().get();
@@ -118,12 +119,20 @@ public class HoodieRowDataLanceWriter extends
HoodieBaseLanceWriter<RowData, Str
@Override
public void writeRowWithMetaData(HoodieKey key, RowData row) throws
IOException {
- if (populateMetaFields) {
- RowData rowWithMeta = updateRecordMetadata(row, key,
getWrittenRecordCount());
- writeRow(key.getRecordKey(), rowWithMeta);
+ RowData rowWithMeta;
+ if (metaFieldsMode == MetaFieldsMode.ALL) {
+ rowWithMeta = HoodieRowDataCreation.create(instantTime,
seqIdGenerator.apply(getWrittenRecordCount()),
+ key.getRecordKey(), key.getPartitionPath(), fileName, row,
withOperation, true);
+ } else if (metaFieldsMode == MetaFieldsMode.NONE) {
+ rowWithMeta = row;
} else {
- writeRow(key.getRecordKey(), row);
+ rowWithMeta = HoodieRowDataCreation.create(
+ metaFieldsMode.isCommitTimePopulated() ? instantTime : null,
+ null, null, null,
+ metaFieldsMode.isFileNamePopulated() ? fileName : null,
+ row, withOperation, true);
}
+ writeRow(key.getRecordKey(), rowWithMeta);
}
@Override
@@ -170,9 +179,4 @@ public class HoodieRowDataLanceWriter extends
HoodieBaseLanceWriter<RowData, Str
rowId = 0;
}
}
-
- private RowData updateRecordMetadata(RowData row, HoodieKey key, long
recordCount) {
- return HoodieRowDataCreation.create(instantTime,
seqIdGenerator.apply(recordCount),
- key.getRecordKey(), key.getPartitionPath(), fileName, row,
withOperation, true);
- }
}
diff --git
a/hudi-client/hudi-flink-client/src/main/java/org/apache/hudi/io/storage/row/HoodieRowDataParquetWriter.java
b/hudi-client/hudi-flink-client/src/main/java/org/apache/hudi/io/storage/row/HoodieRowDataParquetWriter.java
index 46e119610e7e..f9dd49d3c674 100644
---
a/hudi-client/hudi-flink-client/src/main/java/org/apache/hudi/io/storage/row/HoodieRowDataParquetWriter.java
+++
b/hudi-client/hudi-flink-client/src/main/java/org/apache/hudi/io/storage/row/HoodieRowDataParquetWriter.java
@@ -23,6 +23,7 @@ import org.apache.hudi.common.config.HoodieParquetConfig;
import org.apache.hudi.common.engine.TaskContextSupplier;
import org.apache.hudi.common.model.HoodieKey;
import org.apache.hudi.common.model.HoodieRecord;
+import org.apache.hudi.common.model.MetaFieldsMode;
import org.apache.hudi.io.hadoop.HoodieBaseParquetWriter;
import org.apache.hudi.storage.StoragePath;
@@ -41,7 +42,7 @@ public class HoodieRowDataParquetWriter extends
HoodieBaseParquetWriter<RowData>
private final String fileName;
private final String instantTime;
- private final boolean populateMetaFields;
+ private final MetaFieldsMode metaFieldsMode;
private final boolean withOperation;
private final Function<Long, String> seqIdGenerator;
@@ -50,13 +51,13 @@ public class HoodieRowDataParquetWriter extends
HoodieBaseParquetWriter<RowData>
HoodieParquetConfig<HoodieRowDataParquetWriteSupport> parquetConfig,
String instantTime,
TaskContextSupplier taskContextSupplier,
- boolean populateMetaFields,
+ MetaFieldsMode metaFieldsMode,
boolean withOperation) throws IOException {
super(file, parquetConfig);
this.fileName = file.getName();
this.writeSupport = parquetConfig.getWriteSupport();
this.instantTime = instantTime;
- this.populateMetaFields = populateMetaFields;
+ this.metaFieldsMode = metaFieldsMode;
this.withOperation = withOperation;
this.seqIdGenerator = recordIndex -> {
Integer partitionId = taskContextSupplier.getPartitionIdSupplier().get();
@@ -77,16 +78,19 @@ public class HoodieRowDataParquetWriter extends
HoodieBaseParquetWriter<RowData>
@Override
public void writeRowWithMetaData(HoodieKey key, RowData row) throws
IOException {
- if (populateMetaFields) {
- RowData rowWithMeta = updateRecordMetadata(row, key,
getWrittenRecordCount());
- writeRow(key.getRecordKey(), rowWithMeta);
+ RowData rowWithMeta;
+ if (metaFieldsMode == MetaFieldsMode.ALL) {
+ rowWithMeta = HoodieRowDataCreation.create(instantTime,
seqIdGenerator.apply(getWrittenRecordCount()),
+ key.getRecordKey(), key.getPartitionPath(), fileName, row,
withOperation, true);
+ } else if (metaFieldsMode == MetaFieldsMode.NONE) {
+ rowWithMeta = row;
} else {
- writeRow(key.getRecordKey(), row);
+ rowWithMeta = HoodieRowDataCreation.create(
+ metaFieldsMode.isCommitTimePopulated() ? instantTime : null,
+ null, null, null,
+ metaFieldsMode.isFileNamePopulated() ? fileName : null,
+ row, withOperation, true);
}
- }
-
- private RowData updateRecordMetadata(RowData row, HoodieKey key, long
recordCount) {
- return HoodieRowDataCreation.create(instantTime,
seqIdGenerator.apply(recordCount),
- key.getRecordKey(), key.getPartitionPath(), fileName, row,
withOperation, true);
+ writeRow(key.getRecordKey(), rowWithMeta);
}
}
diff --git
a/hudi-client/hudi-flink-client/src/test/java/org/apache/hudi/client/model/TestHoodieFlinkRecord.java
b/hudi-client/hudi-flink-client/src/test/java/org/apache/hudi/client/model/TestHoodieFlinkRecord.java
index 9753628da410..969bca214400 100644
---
a/hudi-client/hudi-flink-client/src/test/java/org/apache/hudi/client/model/TestHoodieFlinkRecord.java
+++
b/hudi-client/hudi-flink-client/src/test/java/org/apache/hudi/client/model/TestHoodieFlinkRecord.java
@@ -26,6 +26,7 @@ import org.apache.hudi.common.schema.HoodieSchemaField;
import org.apache.hudi.common.schema.HoodieSchemaType;
import org.apache.hudi.common.util.Option;
import org.apache.hudi.common.util.OrderingValues;
+import org.apache.hudi.table.format.FlinkRecordContext;
import org.apache.flink.table.data.DecimalData;
import org.apache.flink.table.data.GenericRowData;
@@ -38,6 +39,7 @@ import java.math.BigDecimal;
import java.time.Instant;
import java.time.LocalDate;
import java.util.Arrays;
+import java.util.Collections;
import java.util.Properties;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -47,6 +49,11 @@ 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;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
/**
* Unit tests for {@link HoodieFlinkRecord}.
@@ -192,6 +199,36 @@ public class TestHoodieFlinkRecord {
assertNotEquals(OrderingValues.getDefault(), orderingValue);
}
+ @Test
+ public void testGetRecordKeyCachesGeneratedKey() {
+ HoodieFlinkRecord record = new
HoodieFlinkRecord(GenericRowData.of(StringData.fromString("id1")));
+ HoodieSchema schema = HoodieSchema.createRecord("test", null, null,
Collections.singletonList(
+ HoodieSchemaField.of("id",
HoodieSchema.create(HoodieSchemaType.STRING), null, null)));
+ FlinkRecordContext recordContext = mock(FlinkRecordContext.class);
+ when(recordContext.getRecordKey(record.getData(),
schema)).thenReturn("id1");
+
+ assertNull(record.getKey());
+ assertEquals("id1", record.getRecordKey(schema, recordContext));
+ assertEquals("id1", record.getRecordKey(schema, recordContext));
+ verify(recordContext).getRecordKey(record.getData(), schema);
+ verifyNoMoreInteractions(recordContext);
+ assertEquals("id1", record.getRecordKey());
+ assertSame(record.getKey(), record.newInstance().getKey());
+ }
+
+ @Test
+ public void testGetRecordKeyPreservesExistingKey() {
+ HoodieKey key = new HoodieKey("id1", "partition1");
+ HoodieFlinkRecord record = new HoodieFlinkRecord(key,
HoodieOperation.INSERT, new GenericRowData(0));
+
+ HoodieSchema schema = HoodieSchema.createRecord("test", null, null,
Collections.emptyList());
+ FlinkRecordContext recordContext = mock(FlinkRecordContext.class);
+ assertEquals("id1", record.getRecordKey(schema, recordContext));
+ verifyNoInteractions(recordContext);
+ assertSame(key, record.getKey());
+ assertEquals("partition1", record.getPartitionPath());
+ }
+
@Test
public void testGetRecordKeyWithMetadataField() {
HoodieSchema schema = HoodieSchema.createRecord("test", null, null,
Arrays.asList(
diff --git
a/hudi-client/hudi-flink-client/src/test/java/org/apache/hudi/io/storage/row/TestHoodieRowDataCreateHandle.java
b/hudi-client/hudi-flink-client/src/test/java/org/apache/hudi/io/storage/row/TestHoodieRowDataCreateHandle.java
index b8d29dfb2c34..48a7b4b66a62 100644
---
a/hudi-client/hudi-flink-client/src/test/java/org/apache/hudi/io/storage/row/TestHoodieRowDataCreateHandle.java
+++
b/hudi-client/hudi-flink-client/src/test/java/org/apache/hudi/io/storage/row/TestHoodieRowDataCreateHandle.java
@@ -19,23 +19,35 @@
package org.apache.hudi.io.storage.row;
import org.apache.hudi.client.WriteStatus;
+import org.apache.hudi.client.model.HoodieRowDataCreation;
+import org.apache.hudi.common.engine.EngineType;
import org.apache.hudi.common.model.HoodiePayloadProps;
+import org.apache.hudi.common.model.HoodieRecord;
+import org.apache.hudi.common.model.MetaFieldsMode;
+import org.apache.hudi.common.schema.HoodieSchema;
import org.apache.hudi.common.schema.HoodieSchemaUtils;
+import org.apache.hudi.common.util.ParquetUtils;
import org.apache.hudi.config.HoodieWriteConfig;
+import org.apache.hudi.storage.StoragePath;
import org.apache.hudi.table.HoodieFlinkTable;
import org.apache.hudi.testutils.HoodieFlinkClientTestHarness;
import org.apache.hudi.util.HoodieSchemaConverter;
+import org.apache.avro.generic.GenericRecord;
import org.apache.flink.table.api.DataTypes;
import org.apache.flink.table.data.GenericRowData;
+import org.apache.flink.table.data.RowData;
import org.apache.flink.table.data.StringData;
import org.apache.flink.table.types.DataType;
import org.apache.flink.table.types.logical.RowType;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
import java.io.IOException;
+import java.util.Objects;
import java.util.Properties;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -67,6 +79,55 @@ public class TestHoodieRowDataCreateHandle extends
HoodieFlinkClientTestHarness
cleanupResources();
}
+ @ParameterizedTest
+ @EnumSource(MetaFieldsMode.class)
+ void testMetaFieldsMode(MetaFieldsMode mode) throws Exception {
+ RowType rowType = (RowType) DataTypes.ROW(DataTypes.FIELD("id",
DataTypes.STRING())).notNull().getLogicalType();
+ HoodieSchema schema = HoodieSchemaConverter.convertToSchema(rowType);
+ HoodieWriteConfig config = HoodieWriteConfig.newBuilder()
+ .withPath(basePath)
+ .withEngineType(EngineType.FLINK)
+ .withMetaFieldsMode(mode)
+ .withEmbeddedTimelineServerEnabled(false)
+ .withSchema(schema.toString())
+ .build();
+ HoodieFlinkTable<?> table = HoodieFlinkTable.create(config, context,
metaClient);
+ for (boolean preserveMetadata : new boolean[] {false, true}) {
+ HoodieRowDataCreateHandle handle = new HoodieRowDataCreateHandle(
+ table, config, PARTITION_PATH, FILE_ID + preserveMetadata,
INSTANT_TIME,
+ TASK_PARTITION_ID, TASK_ID, TASK_EPOCH_ID,
+ HoodieSchemaUtils.addMetadataFields(schema, false),
preserveMetadata, false);
+ RowData row = GenericRowData.of(StringData.fromString("id1"));
+ if (preserveMetadata) {
+ row = HoodieRowDataCreation.create(
+ mode.isCommitTimePopulated() ? "old-instant" : null,
+ mode == MetaFieldsMode.ALL ? "old-sequence" : null,
+ mode.isRecordKeyPopulated() ? "id1" : null,
+ mode == MetaFieldsMode.ALL ? PARTITION_PATH : null,
+ mode.isFileNamePopulated() ? "old-file" : null,
+ row, false, false);
+ }
+ handle.write("id1", PARTITION_PATH, row);
+ WriteStatus status = handle.close();
+ assertEquals(0, status.getTotalErrorRecords());
+ StoragePath file = new StoragePath(basePath, status.getStat().getPath());
+ GenericRecord stored = new
ParquetUtils().readAvroRecords(metaClient.getStorage(), file).get(0);
+ assertEquals("id1", stored.get("id").toString());
+ assertEquals(mode.isCommitTimePopulated() ? (preserveMetadata ?
"old-instant" : INSTANT_TIME) : null,
+
Objects.toString(stored.get(HoodieRecord.COMMIT_TIME_METADATA_FIELD), null));
+ assertEquals(mode != MetaFieldsMode.ALL,
stored.get(HoodieRecord.COMMIT_SEQNO_METADATA_FIELD) == null);
+ if (preserveMetadata && mode == MetaFieldsMode.ALL) {
+ assertEquals("old-sequence",
stored.get(HoodieRecord.COMMIT_SEQNO_METADATA_FIELD).toString());
+ }
+ assertEquals(mode.isRecordKeyPopulated() ? "id1" : null,
+ Objects.toString(stored.get(HoodieRecord.RECORD_KEY_METADATA_FIELD),
null));
+ assertEquals(mode == MetaFieldsMode.ALL ? PARTITION_PATH : null,
+
Objects.toString(stored.get(HoodieRecord.PARTITION_PATH_METADATA_FIELD), null));
+ assertEquals(mode.isFileNamePopulated() ? file.getName() : null,
+ Objects.toString(stored.get(HoodieRecord.FILENAME_METADATA_FIELD),
null));
+ }
+ }
+
@Test
public void testEventTimeFieldIndexWithDoubleType() throws Exception {
// Schema with DOUBLE event_time field
diff --git
a/hudi-client/hudi-flink-client/src/test/java/org/apache/hudi/io/storage/row/TestHoodieRowDataLanceWriter.java
b/hudi-client/hudi-flink-client/src/test/java/org/apache/hudi/io/storage/row/TestHoodieRowDataLanceWriter.java
index b7303a61e93f..f66f3aaf66bd 100644
---
a/hudi-client/hudi-flink-client/src/test/java/org/apache/hudi/io/storage/row/TestHoodieRowDataLanceWriter.java
+++
b/hudi-client/hudi-flink-client/src/test/java/org/apache/hudi/io/storage/row/TestHoodieRowDataLanceWriter.java
@@ -18,8 +18,13 @@
package org.apache.hudi.io.storage.row;
+import org.apache.hudi.client.model.HoodieRowDataCreation;
+import org.apache.hudi.common.engine.LocalTaskContextSupplier;
import org.apache.hudi.common.engine.TaskContextSupplier;
+import org.apache.hudi.common.model.HoodieKey;
+import org.apache.hudi.common.model.MetaFieldsMode;
import org.apache.hudi.common.schema.HoodieSchema;
+import org.apache.hudi.common.schema.HoodieSchemaUtils;
import org.apache.hudi.common.util.Option;
import org.apache.hudi.storage.StoragePath;
import org.apache.hudi.util.HoodieSchemaConverter;
@@ -33,6 +38,7 @@ import org.apache.arrow.vector.ipc.ArrowReader;
import org.apache.arrow.vector.types.pojo.ArrowType;
import org.apache.flink.table.data.GenericArrayData;
import org.apache.flink.table.data.GenericRowData;
+import org.apache.flink.table.data.RowData;
import org.apache.flink.table.types.logical.ArrayType;
import org.apache.flink.table.types.logical.FloatType;
import org.apache.flink.table.types.logical.IntType;
@@ -40,6 +46,8 @@ import org.apache.flink.table.types.logical.LogicalType;
import org.apache.flink.table.types.logical.RowType;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
import org.lance.file.LanceFileReader;
import java.nio.file.Path;
@@ -56,6 +64,37 @@ public class TestHoodieRowDataLanceWriter {
@TempDir
Path tempDir;
+ @ParameterizedTest
+ @EnumSource(MetaFieldsMode.class)
+ void testMetaFieldsMode(MetaFieldsMode mode) throws Exception {
+ HoodieSchema schema =
HoodieSchemaUtils.addMetadataFields(HoodieSchemaConverter.convertToSchema(
+ RowType.of(new LogicalType[] {new IntType(false)}, new String[]
{"id"}).copy(false)), false);
+ StoragePath path = new
StoragePath(tempDir.resolve("metadata.lance").toUri());
+ try (HoodieRowDataLanceWriter writer = new HoodieRowDataLanceWriter(path,
schema, "001",
+ new LocalTaskContextSupplier(), Option.empty(), 128 * 1024 * 1024L,
+ 64 * 1024 * 1024L, 16 * 1024 * 1024L, true, mode, false)) {
+ RowData row = HoodieRowDataCreation.create(null, null, null, null, null,
+ GenericRowData.of(1), false, false);
+ writer.writeRowWithMetaData(new HoodieKey("key1", "partition"), row);
+ }
+ try (BufferAllocator allocator = new RootAllocator();
+ LanceFileReader reader = LanceFileReader.open(path.toString(),
allocator);
+ ArrowReader arrowReader = reader.readAll(null, null,
Integer.MAX_VALUE)) {
+ assertTrue(arrowReader.loadNextBatch());
+ VectorSchemaRoot root = arrowReader.getVectorSchemaRoot();
+ boolean[] populated = {mode.isCommitTimePopulated(), mode ==
MetaFieldsMode.ALL,
+ mode.isRecordKeyPopulated(), mode == MetaFieldsMode.ALL,
mode.isFileNamePopulated()};
+ String[] expected = {"001", null, "key1", "partition", path.getName()};
+ for (int i = 0; i < populated.length; i++) {
+ assertEquals(!populated[i], root.getVector(i).isNull(0));
+ if (populated[i] && expected[i] != null) {
+ assertEquals(expected[i], root.getVector(i).getObject(0).toString());
+ }
+ }
+ assertEquals(1, root.getVector("id").getObject(0));
+ }
+ }
+
@Test
public void testWritesVectorDataAndFooterMetadata() throws Exception {
RowType rowType = RowType.of(
@@ -75,7 +114,7 @@ public class TestHoodieRowDataLanceWriter {
64 * 1024 * 1024L,
16 * 1024 * 1024L,
true,
- false,
+ MetaFieldsMode.NONE,
false)) {
writer.writeRow("key1", GenericRowData.of(
1, new GenericArrayData(new Object[] {1.25F, 2.5F})));
diff --git
a/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/TestBaseSparkInternalRowReaderContext.java
b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/TestBaseSparkInternalRowReaderContext.java
index 7edbdc3bf054..7792beafeaa7 100644
---
a/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/TestBaseSparkInternalRowReaderContext.java
+++
b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/TestBaseSparkInternalRowReaderContext.java
@@ -70,7 +70,7 @@ class TestBaseSparkInternalRowReaderContext {
void setUp() {
storageconfig = mock(StorageConfiguration.class);
tableConfig = mock(HoodieTableConfig.class);
- when(tableConfig.populateMetaFields()).thenReturn(true);
+ when(tableConfig.isRecordKeyPopulated()).thenReturn(true);
when(tableConfig.getBaseFileFormat()).thenReturn(HoodieFileFormat.PARQUET);
when(tableConfig.getRecordKeyFields()).thenReturn(Option.of(new
String[]{"id"}));
diff --git
a/hudi-common/src/main/java/org/apache/hudi/common/engine/RecordContext.java
b/hudi-common/src/main/java/org/apache/hudi/common/engine/RecordContext.java
index 01f01a861c18..6a95e70a7b0e 100644
--- a/hudi-common/src/main/java/org/apache/hudi/common/engine/RecordContext.java
+++ b/hudi-common/src/main/java/org/apache/hudi/common/engine/RecordContext.java
@@ -74,8 +74,9 @@ public abstract class RecordContext<T> implements
Serializable {
protected RecordContext(HoodieTableConfig tableConfig, JavaTypeConverter
typeConverter) {
this.typeConverter = typeConverter;
- this.recordKeyExtractor = tableConfig.populateMetaFields() ?
metadataKeyExtractor() : virtualKeyExtractor(tableConfig.getRecordKeyFields()
- .orElseThrow(() -> new IllegalArgumentException("No record keys
specified and meta fields are not populated")));
+ this.recordKeyExtractor = tableConfig.isRecordKeyPopulated() ?
metadataKeyExtractor() : virtualKeyExtractor(tableConfig.getRecordKeyFields()
+ .orElseThrow(() -> new IllegalArgumentException("No record keys
specified and meta fields are not populated")),
+ tableConfig.getPartitionFields().map(fields ->
fields.length).orElse(0));
}
/**
@@ -440,11 +441,8 @@ public abstract class RecordContext<T> implements
Serializable {
return (record, schema) -> getValue(record, schema,
RECORD_KEY_METADATA_FIELD).toString();
}
- private SerializableBiFunction<T, HoodieSchema, String>
virtualKeyExtractor(String[] recordKeyFields) {
- if (recordKeyFields.length == 1) {
- // there might be consistency for record key encoding when partition
fields are multiple for cow merging,
- // currently the incoming records are using the keys from HoodieRecord
which utilities the write config and by default encodes the field name with the
value
- // while here the field names are ignored, this function would be used
to extract record keys from old base file.
+ private SerializableBiFunction<T, HoodieSchema, String>
virtualKeyExtractor(String[] recordKeyFields, int numPartitionFields) {
+ if (recordKeyFields.length == 1 && numPartitionFields <= 1) {
return (record, schema) -> {
Object result = getValue(record, schema, recordKeyFields[0]);
if (result == null) {
diff --git
a/hudi-common/src/main/java/org/apache/hudi/common/table/HoodieTableConfig.java
b/hudi-common/src/main/java/org/apache/hudi/common/table/HoodieTableConfig.java
index 63ead8bcc087..f77960cd96a6 100644
---
a/hudi-common/src/main/java/org/apache/hudi/common/table/HoodieTableConfig.java
+++
b/hudi-common/src/main/java/org/apache/hudi/common/table/HoodieTableConfig.java
@@ -356,7 +356,8 @@ public class HoodieTableConfig extends HoodieConfig {
.key("hoodie.meta.fields.mode")
.noDefaultValue()
.withDocumentation("Which Hudi meta columns are physically populated on
disk. Allowed values are "
- + "ALL, NONE, COMMIT_TIME_ONLY, FILE_NAME_ONLY and
COMMIT_TIME_AND_FILE_NAME. This supersedes the "
+ + "ALL, NONE, COMMIT_TIME_ONLY, FILE_NAME_ONLY and
COMMIT_TIME_AND_FILE_NAME. "
+ + "Selective modes are supported by Spark and Flink COPY_ON_WRITE
writers. This supersedes the "
+ "deprecated hoodie.populate.meta.fields boolean, which is
consulted only when this property is unset "
+ "(true maps to ALL, false maps to NONE). Supported on any table
version 1.x can write, not just the "
+ "latest. Set only at table creation, via the hudi-cli, or during
table upgrade — the property is "
diff --git
a/hudi-common/src/test/java/org/apache/hudi/common/avro/TestAvroRecordContext.java
b/hudi-common/src/test/java/org/apache/hudi/common/avro/TestAvroRecordContext.java
index d1835dfbf7ae..6972b93fc735 100644
---
a/hudi-common/src/test/java/org/apache/hudi/common/avro/TestAvroRecordContext.java
+++
b/hudi-common/src/test/java/org/apache/hudi/common/avro/TestAvroRecordContext.java
@@ -19,6 +19,10 @@
package org.apache.hudi.common.avro;
+import org.apache.hudi.common.model.MetaFieldsMode;
+import org.apache.hudi.common.schema.HoodieSchema;
+import org.apache.hudi.common.table.HoodieTableConfig;
+
import org.apache.avro.Schema;
import org.apache.avro.generic.GenericData;
import org.apache.avro.generic.GenericRecord;
@@ -83,6 +87,30 @@ class TestAvroRecordContext {
return record;
}
+ @ParameterizedTest
+ @MethodSource("virtualRecordKeyParams")
+ void testVirtualRecordKey(String recordKeyFields, String partitionFields,
String expectedKey) {
+ HoodieTableConfig tableConfig = new HoodieTableConfig();
+ tableConfig.setValue(HoodieTableConfig.META_FIELDS_MODE,
MetaFieldsMode.NONE.name());
+ tableConfig.setValue(HoodieTableConfig.RECORDKEY_FIELDS, recordKeyFields);
+ if (partitionFields != null) {
+ tableConfig.setValue(HoodieTableConfig.PARTITION_FIELDS,
partitionFields);
+ }
+ AvroRecordContext context = new AvroRecordContext(tableConfig, null);
+ assertEquals(expectedKey, context.getRecordKey(buildRecord(),
HoodieSchema.fromAvroSchema(RECORD_SCHEMA)));
+ }
+
+ private static Stream<Arguments> virtualRecordKeyParams() {
+ return Stream.of(
+ Arguments.of("id", null, "1"),
+ Arguments.of("id", "", "1"),
+ Arguments.of("id", "name", "1"),
+ Arguments.of("id", "name,address.city", "id:1"),
+ Arguments.of("id,name", null, "id:1,name:alice"),
+ Arguments.of("id,name", "name", "id:1,name:alice"),
+ Arguments.of("id,name", "name,address.city", "id:1,name:alice"));
+ }
+
@Test
void testGetFieldValueTopLevel() {
GenericRecord record = buildRecord();
diff --git
a/hudi-common/src/test/java/org/apache/hudi/common/avro/TestHoodieAvroReaderContext.java
b/hudi-common/src/test/java/org/apache/hudi/common/avro/TestHoodieAvroReaderContext.java
index 8b37dcd5465d..927158d890dd 100644
---
a/hudi-common/src/test/java/org/apache/hudi/common/avro/TestHoodieAvroReaderContext.java
+++
b/hudi-common/src/test/java/org/apache/hudi/common/avro/TestHoodieAvroReaderContext.java
@@ -163,7 +163,7 @@ class TestHoodieAvroReaderContext {
@Test
void getNestedField() {
- when(tableConfig.populateMetaFields()).thenReturn(true);
+ when(tableConfig.isRecordKeyPopulated()).thenReturn(true);
HoodieAvroReaderContext avroReaderContext =
new HoodieAvroReaderContext(storageConfig, tableConfig,
Option.empty(), Option.empty());
IndexedRecord indexedRecord = createBaseRecord("compound", "field2", 3.2);
@@ -172,7 +172,8 @@ class TestHoodieAvroReaderContext {
@Test
void getRecordKeyWithSingleKey() {
- when(tableConfig.populateMetaFields()).thenReturn(false);
+ when(tableConfig.isRecordKeyPopulated()).thenReturn(false);
+ when(tableConfig.getPartitionFields()).thenReturn(Option.empty());
when(tableConfig.getRecordKeyFields()).thenReturn(Option.of(new
String[]{"skeleton_field_1"}));
HoodieAvroReaderContext avroReaderContext =
new HoodieAvroReaderContext(storageConfig, tableConfig,
Option.empty(), Option.empty());
@@ -183,7 +184,8 @@ class TestHoodieAvroReaderContext {
@Test
void getRecordKeyWithMultipleKeys() {
- when(tableConfig.populateMetaFields()).thenReturn(false);
+ when(tableConfig.isRecordKeyPopulated()).thenReturn(false);
+ when(tableConfig.getPartitionFields()).thenReturn(Option.empty());
when(tableConfig.getRecordKeyFields()).thenReturn(Option.of(new
String[]{"base_field_1", "base_field_3.nested_field"}));
HoodieAvroReaderContext avroReaderContext =
new HoodieAvroReaderContext(storageConfig, tableConfig,
Option.empty(), Option.empty());
@@ -249,7 +251,7 @@ class TestHoodieAvroReaderContext {
}
private HoodieAvroReaderContext getReaderContextWithMetaFields() {
- when(tableConfig.populateMetaFields()).thenReturn(true);
+ when(tableConfig.isRecordKeyPopulated()).thenReturn(true);
return new HoodieAvroReaderContext(storageConfig, tableConfig,
Option.empty(), Option.empty());
}
diff --git
a/hudi-common/src/test/java/org/apache/hudi/common/table/read/SchemaHandlerTestBase.java
b/hudi-common/src/test/java/org/apache/hudi/common/table/read/SchemaHandlerTestBase.java
index 047c08cdd873..9f6754d688c6 100644
---
a/hudi-common/src/test/java/org/apache/hudi/common/table/read/SchemaHandlerTestBase.java
+++
b/hudi-common/src/test/java/org/apache/hudi/common/table/read/SchemaHandlerTestBase.java
@@ -278,6 +278,7 @@ public abstract class SchemaHandlerTestBase {
static void setupMORTable(RecordMergeMode mergeMode, boolean hasPrecombine,
HoodieTableConfig hoodieTableConfig) {
when(hoodieTableConfig.populateMetaFields()).thenReturn(true);
+ when(hoodieTableConfig.isRecordKeyPopulated()).thenReturn(true);
when(hoodieTableConfig.getRecordMergeMode()).thenReturn(mergeMode);
when(hoodieTableConfig.getTableVersion()).thenReturn(HoodieTableVersion.current());
if (hasPrecombine) {
diff --git
a/hudi-common/src/test/java/org/apache/hudi/common/table/read/TestFileGroupReaderSchemaHandler.java
b/hudi-common/src/test/java/org/apache/hudi/common/table/read/TestFileGroupReaderSchemaHandler.java
index 8e9026cd10f7..1f997412d734 100644
---
a/hudi-common/src/test/java/org/apache/hudi/common/table/read/TestFileGroupReaderSchemaHandler.java
+++
b/hudi-common/src/test/java/org/apache/hudi/common/table/read/TestFileGroupReaderSchemaHandler.java
@@ -74,6 +74,7 @@ public class TestFileGroupReaderSchemaHandler extends
SchemaHandlerTestBase {
@Test
public void testCow() {
when(hoodieTableConfig.populateMetaFields()).thenReturn(true);
+ when(hoodieTableConfig.isRecordKeyPopulated()).thenReturn(true);
HoodieReaderContext<String> readerContext =
createReaderContext(hoodieTableConfig, false, false, false, false, null);
HoodieSchema requestedSchema = DATA_SCHEMA;
FileGroupReaderSchemaHandler schemaHandler =
createSchemaHandler(readerContext, DATA_SCHEMA, requestedSchema, false);
@@ -89,6 +90,7 @@ public class TestFileGroupReaderSchemaHandler extends
SchemaHandlerTestBase {
@Test
public void testCowBootstrap() {
when(hoodieTableConfig.populateMetaFields()).thenReturn(true);
+ when(hoodieTableConfig.isRecordKeyPopulated()).thenReturn(true);
HoodieReaderContext<String> readerContext =
createReaderContext(hoodieTableConfig, false, false, true, false, null);
HoodieSchema requestedSchema = generateProjectionSchema("begin_lat",
"tip_history", "_hoodie_record_key", "rider");
@@ -105,6 +107,7 @@ public class TestFileGroupReaderSchemaHandler extends
SchemaHandlerTestBase {
@Test
void testGetRequiredSchemaForFileAndRenameColumns() {
when(hoodieTableConfig.populateMetaFields()).thenReturn(true);
+ when(hoodieTableConfig.isRecordKeyPopulated()).thenReturn(true);
HoodieReaderContext<String> readerContext =
createReaderContext(hoodieTableConfig, false, false, true, false, null);
HoodieSchema requestedSchema =
generateProjectionSchema("_hoodie_record_key", "timestamp", "rider");
@@ -244,6 +247,7 @@ public class TestFileGroupReaderSchemaHandler extends
SchemaHandlerTestBase {
when(hoodieTableConfig.getRecordMergeMode()).thenReturn(mergeMode);
when(hoodieTableConfig.populateMetaFields()).thenReturn(true);
+ when(hoodieTableConfig.isRecordKeyPopulated()).thenReturn(true);
when(hoodieTableConfig.getOrderingFieldsStr()).thenReturn(Option.of(setPrecombine
? preCombineField : StringUtils.EMPTY_STRING));
when(hoodieTableConfig.getOrderingFields()).thenReturn(setPrecombine ?
Collections.singletonList(preCombineField) : Collections.emptyList());
when(hoodieTableConfig.getTableVersion()).thenReturn(tableVersion);
@@ -343,6 +347,7 @@ public class TestFileGroupReaderSchemaHandler extends
SchemaHandlerTestBase {
when(hoodieTableConfig.getPayloadClass()).thenReturn(payloadClass);
when(hoodieTableConfig.getRecordMergeStrategyId()).thenReturn(null);
when(hoodieTableConfig.populateMetaFields()).thenReturn(true);
+ when(hoodieTableConfig.isRecordKeyPopulated()).thenReturn(true);
if (orderingField != null) {
when(hoodieTableConfig.getOrderingFieldsStr()).thenReturn(Option.of(orderingField));
when(hoodieTableConfig.getOrderingFields()).thenReturn(Collections.singletonList(orderingField));
diff --git
a/hudi-common/src/test/java/org/apache/hudi/common/table/read/TestParquetRowIndexBasedSchemaHandler.java
b/hudi-common/src/test/java/org/apache/hudi/common/table/read/TestParquetRowIndexBasedSchemaHandler.java
index f6cd3426290e..49537dc0e919 100644
---
a/hudi-common/src/test/java/org/apache/hudi/common/table/read/TestParquetRowIndexBasedSchemaHandler.java
+++
b/hudi-common/src/test/java/org/apache/hudi/common/table/read/TestParquetRowIndexBasedSchemaHandler.java
@@ -48,6 +48,7 @@ public class TestParquetRowIndexBasedSchemaHandler extends
SchemaHandlerTestBase
@Test
public void testCowBootstrapWithPositionMerge() {
when(hoodieTableConfig.populateMetaFields()).thenReturn(true);
+ when(hoodieTableConfig.isRecordKeyPopulated()).thenReturn(true);
HoodieReaderContext<String> readerContext =
createReaderContext(hoodieTableConfig, true, false, true, false, null);
HoodieSchema requestedSchema = generateProjectionSchema("begin_lat",
"tip_history", "_hoodie_record_key", "rider");
FileGroupReaderSchemaHandler schemaHandler =
createSchemaHandler(readerContext, DATA_SCHEMA, requestedSchema, true);
diff --git
a/hudi-common/src/test/java/org/apache/hudi/common/table/read/buffer/TestFileGroupRecordBufferLoader.java
b/hudi-common/src/test/java/org/apache/hudi/common/table/read/buffer/TestFileGroupRecordBufferLoader.java
index 1ed45d7e7bda..f57fe11adca5 100644
---
a/hudi-common/src/test/java/org/apache/hudi/common/table/read/buffer/TestFileGroupRecordBufferLoader.java
+++
b/hudi-common/src/test/java/org/apache/hudi/common/table/read/buffer/TestFileGroupRecordBufferLoader.java
@@ -67,6 +67,7 @@ public class TestFileGroupRecordBufferLoader extends
BaseTestFileGroupRecordBuff
when(tableConfig.getPartialUpdateMode()).thenReturn(Option.empty());
when(tableConfig.getOrderingFieldsStr()).thenReturn(Option.empty());
when(tableConfig.getRecordKeyFields()).thenReturn(Option.of(new String[]
{"record_key"}));
+ when(tableConfig.getPartitionFields()).thenReturn(Option.empty());
StorageConfiguration<?> storageConfiguration =
mock(StorageConfiguration.class);
HoodieReaderContext<IndexedRecord> readerContext = new
HoodieAvroReaderContext(storageConfiguration, tableConfig, Option.empty(),
Option.empty());
readerContext.initRecordMerger(new TypedProperties());
diff --git
a/hudi-common/src/test/java/org/apache/hudi/common/table/read/buffer/TestKeyBasedFileGroupRecordBuffer.java
b/hudi-common/src/test/java/org/apache/hudi/common/table/read/buffer/TestKeyBasedFileGroupRecordBuffer.java
index b2138b9a495c..a5dbe4c9d86f 100644
---
a/hudi-common/src/test/java/org/apache/hudi/common/table/read/buffer/TestKeyBasedFileGroupRecordBuffer.java
+++
b/hudi-common/src/test/java/org/apache/hudi/common/table/read/buffer/TestKeyBasedFileGroupRecordBuffer.java
@@ -110,6 +110,7 @@ class TestKeyBasedFileGroupRecordBuffer extends
BaseTestFileGroupRecordBuffer {
HoodieReadStats readStats = new HoodieReadStats();
HoodieTableConfig tableConfig = mock(HoodieTableConfig.class);
when(tableConfig.getRecordKeyFields()).thenReturn(Option.of(new String[]
{"record_key"}));
+ when(tableConfig.getPartitionFields()).thenReturn(Option.empty());
StorageConfiguration<?> storageConfiguration =
mock(StorageConfiguration.class);
HoodieReaderContext<IndexedRecord> readerContext = new
HoodieAvroReaderContext(storageConfiguration, tableConfig, Option.empty(),
Option.empty());
KeyBasedFileGroupRecordBuffer<IndexedRecord> fileGroupRecordBuffer =
buildKeyBasedFileGroupRecordBuffer(readerContext, tableConfig, readStats, null,
@@ -137,6 +138,7 @@ class TestKeyBasedFileGroupRecordBuffer extends
BaseTestFileGroupRecordBuffer {
HoodieReadStats readStats = new HoodieReadStats();
HoodieTableConfig tableConfig = mock(HoodieTableConfig.class);
when(tableConfig.getRecordKeyFields()).thenReturn(Option.of(new String[]
{"record_key"}));
+ when(tableConfig.getPartitionFields()).thenReturn(Option.empty());
StorageConfiguration<?> storageConfiguration =
mock(StorageConfiguration.class);
HoodieReaderContext<IndexedRecord> readerContext = new
HoodieAvroReaderContext(storageConfiguration, tableConfig, Option.empty(),
Option.empty());
KeyBasedFileGroupRecordBuffer<IndexedRecord> fileGroupRecordBuffer =
buildKeyBasedFileGroupRecordBuffer(readerContext, tableConfig, readStats, null,
@@ -178,6 +180,7 @@ class TestKeyBasedFileGroupRecordBuffer extends
BaseTestFileGroupRecordBuffer {
when(tableConfig.getPartialUpdateMode()).thenReturn(Option.empty());
when(tableConfig.getTableVersion()).thenReturn(HoodieTableVersion.current());
when(tableConfig.getRecordKeyFields()).thenReturn(Option.of(new String[]
{"record_key"}));
+ when(tableConfig.getPartitionFields()).thenReturn(Option.empty());
StorageConfiguration<?> storageConfiguration =
mock(StorageConfiguration.class);
HoodieReaderContext<IndexedRecord> readerContext = new
HoodieAvroReaderContext(storageConfiguration, tableConfig, Option.empty(),
Option.empty());
readerContext.setHasLogFiles(false);
@@ -210,6 +213,7 @@ class TestKeyBasedFileGroupRecordBuffer extends
BaseTestFileGroupRecordBuffer {
HoodieReadStats readStats = new HoodieReadStats();
HoodieTableConfig tableConfig = mock(HoodieTableConfig.class);
when(tableConfig.getRecordKeyFields()).thenReturn(Option.of(new String[]
{"record_key"}));
+ when(tableConfig.getPartitionFields()).thenReturn(Option.empty());
StorageConfiguration<?> storageConfiguration =
mock(StorageConfiguration.class);
HoodieReaderContext<IndexedRecord> readerContext = new
HoodieAvroReaderContext(storageConfiguration, tableConfig, Option.empty(),
Option.empty());
KeyBasedFileGroupRecordBuffer<IndexedRecord> fileGroupRecordBuffer =
buildKeyBasedFileGroupRecordBuffer(readerContext, tableConfig, readStats, null,
@@ -246,6 +250,7 @@ class TestKeyBasedFileGroupRecordBuffer extends
BaseTestFileGroupRecordBuffer {
when(tableConfig.getPartialUpdateMode()).thenReturn(Option.empty());
when(tableConfig.getTableVersion()).thenReturn(HoodieTableVersion.current());
when(tableConfig.getRecordKeyFields()).thenReturn(Option.of(new String[]
{"record_key"}));
+ when(tableConfig.getPartitionFields()).thenReturn(Option.empty());
StorageConfiguration<?> storageConfiguration =
mock(StorageConfiguration.class);
HoodieReaderContext<IndexedRecord> readerContext = new
HoodieAvroReaderContext(storageConfiguration, tableConfig, Option.empty(),
Option.empty());
readerContext.setHasLogFiles(false);
@@ -277,6 +282,7 @@ class TestKeyBasedFileGroupRecordBuffer extends
BaseTestFileGroupRecordBuffer {
HoodieTableConfig tableConfig = mock(HoodieTableConfig.class);
when(tableConfig.getPayloadClass()).thenReturn(CustomPayload.class.getName());
when(tableConfig.getRecordKeyFields()).thenReturn(Option.of(new String[]
{"record_key"}));
+ when(tableConfig.getPartitionFields()).thenReturn(Option.empty());
when(tableConfig.getRecordMergeMode()).thenReturn(RecordMergeMode.CUSTOM);
when(tableConfig.getPartialUpdateMode()).thenReturn(Option.empty());
StorageConfiguration<?> storageConfiguration =
mock(StorageConfiguration.class);
@@ -322,6 +328,7 @@ class TestKeyBasedFileGroupRecordBuffer extends
BaseTestFileGroupRecordBuffer {
HoodieTableConfig tableConfig = mock(HoodieTableConfig.class);
when(tableConfig.getPayloadClass()).thenReturn(TestKeyBasedFileGroupRecordBuffer.CustomPayload.class.getName());
when(tableConfig.getRecordKeyFields()).thenReturn(Option.of(new String[]
{"record_key"}));
+ when(tableConfig.getPartitionFields()).thenReturn(Option.empty());
when(tableConfig.getRecordMergeMode()).thenReturn(RecordMergeMode.CUSTOM);
when(tableConfig.getPartialUpdateMode()).thenReturn(Option.empty());
when(tableConfig.getRecordMergeStrategyId()).thenReturn(HoodieRecordMerger.PAYLOAD_BASED_MERGE_STRATEGY_UUID);
@@ -355,6 +362,7 @@ class TestKeyBasedFileGroupRecordBuffer extends
BaseTestFileGroupRecordBuffer {
HoodieTableConfig tableConfig = mock(HoodieTableConfig.class);
when(tableConfig.getPayloadClass()).thenReturn(CustomPayload.class.getName());
when(tableConfig.getRecordKeyFields()).thenReturn(Option.of(new String[]
{"record_key"}));
+ when(tableConfig.getPartitionFields()).thenReturn(Option.empty());
StorageConfiguration<?> storageConfiguration =
mock(StorageConfiguration.class);
HoodieReaderContext<IndexedRecord> readerContext = new
HoodieAvroReaderContext(storageConfiguration, tableConfig, Option.empty(),
Option.empty());
KeyBasedFileGroupRecordBuffer<IndexedRecord> fileGroupRecordBuffer =
buildKeyBasedFileGroupRecordBuffer(readerContext, tableConfig, readStats, new
CustomMerger(),
@@ -396,6 +404,7 @@ class TestKeyBasedFileGroupRecordBuffer extends
BaseTestFileGroupRecordBuffer {
HoodieTableConfig tableConfig = mock(HoodieTableConfig.class);
when(tableConfig.getPayloadClass()).thenReturn(CustomPayload.class.getName());
when(tableConfig.getRecordKeyFields()).thenReturn(Option.of(new String[]
{"record_key"}));
+ when(tableConfig.getPartitionFields()).thenReturn(Option.empty());
when(tableConfig.getRecordMergeMode()).thenReturn(RecordMergeMode.CUSTOM);
when(tableConfig.getPartialUpdateMode()).thenReturn(Option.empty());
when(tableConfig.getRecordMergeStrategyId()).thenReturn(HoodieRecordMerger.PAYLOAD_BASED_MERGE_STRATEGY_UUID);
diff --git
a/hudi-common/src/test/java/org/apache/hudi/common/table/read/buffer/TestSortedKeyBasedFileGroupRecordBuffer.java
b/hudi-common/src/test/java/org/apache/hudi/common/table/read/buffer/TestSortedKeyBasedFileGroupRecordBuffer.java
index 4dde1b53c37c..fbde79157951 100644
---
a/hudi-common/src/test/java/org/apache/hudi/common/table/read/buffer/TestSortedKeyBasedFileGroupRecordBuffer.java
+++
b/hudi-common/src/test/java/org/apache/hudi/common/table/read/buffer/TestSortedKeyBasedFileGroupRecordBuffer.java
@@ -119,6 +119,7 @@ class TestSortedKeyBasedFileGroupRecordBuffer extends
BaseTestFileGroupRecordBuf
properties.setProperty(DELETE_MARKER, "3");
HoodieTableConfig tableConfig = mock(HoodieTableConfig.class);
when(tableConfig.getRecordKeyFields()).thenReturn(Option.of(new String[]
{"record_key"}));
+ when(tableConfig.getPartitionFields()).thenReturn(Option.empty());
when(tableConfig.getTableVersion()).thenReturn(HoodieTableVersion.current());
when(tableConfig.getRecordMergeMode()).thenReturn(RecordMergeMode.EVENT_TIME_ORDERING);
StorageConfiguration<?> storageConfiguration =
mock(StorageConfiguration.class);
diff --git
a/hudi-common/src/test/java/org/apache/hudi/common/table/read/buffer/TestStreamingKeyBasedFileGroupRecordBuffer.java
b/hudi-common/src/test/java/org/apache/hudi/common/table/read/buffer/TestStreamingKeyBasedFileGroupRecordBuffer.java
index 1a650771e46a..d03be553194a 100644
---
a/hudi-common/src/test/java/org/apache/hudi/common/table/read/buffer/TestStreamingKeyBasedFileGroupRecordBuffer.java
+++
b/hudi-common/src/test/java/org/apache/hudi/common/table/read/buffer/TestStreamingKeyBasedFileGroupRecordBuffer.java
@@ -78,6 +78,7 @@ class TestStreamingKeyBasedFileGroupRecordBuffer extends
BaseTestFileGroupRecord
when(tableConfig.getPartialUpdateMode()).thenReturn(Option.empty());
when(tableConfig.getTableVersion()).thenReturn(HoodieTableVersion.current());
when(tableConfig.getRecordKeyFields()).thenReturn(Option.of(new String[]
{"record_key"}));
+ when(tableConfig.getPartitionFields()).thenReturn(Option.empty());
StorageConfiguration<?> storageConfiguration =
mock(StorageConfiguration.class);
HoodieReaderContext<IndexedRecord> readerContext = new
HoodieAvroReaderContext(storageConfiguration, tableConfig, Option.empty(),
Option.empty());
readerContext.setHasLogFiles(false);
@@ -116,6 +117,7 @@ class TestStreamingKeyBasedFileGroupRecordBuffer extends
BaseTestFileGroupRecord
when(tableConfig.getPartialUpdateMode()).thenReturn(Option.empty());
when(tableConfig.getTableVersion()).thenReturn(HoodieTableVersion.current());
when(tableConfig.getRecordKeyFields()).thenReturn(Option.of(new String[]
{"record_key"}));
+ when(tableConfig.getPartitionFields()).thenReturn(Option.empty());
StorageConfiguration<?> storageConfiguration =
mock(StorageConfiguration.class);
HoodieReaderContext<IndexedRecord> readerContext = new
HoodieAvroReaderContext(storageConfiguration, tableConfig, Option.empty(),
Option.empty());
readerContext.setHasLogFiles(false);
@@ -152,6 +154,7 @@ class TestStreamingKeyBasedFileGroupRecordBuffer extends
BaseTestFileGroupRecord
HoodieTableConfig tableConfig = mock(HoodieTableConfig.class);
when(tableConfig.getPayloadClass()).thenReturn(CustomPayload.class.getName());
when(tableConfig.getRecordKeyFields()).thenReturn(Option.of(new String[]
{"record_key"}));
+ when(tableConfig.getPartitionFields()).thenReturn(Option.empty());
when(tableConfig.getRecordMergeMode()).thenReturn(RecordMergeMode.CUSTOM);
when(tableConfig.getPartialUpdateMode()).thenReturn(Option.empty());
when(tableConfig.getRecordMergeStrategyId()).thenReturn(HoodieRecordMerger.PAYLOAD_BASED_MERGE_STRATEGY_UUID);
@@ -189,6 +192,7 @@ class TestStreamingKeyBasedFileGroupRecordBuffer extends
BaseTestFileGroupRecord
HoodieTableConfig tableConfig = mock(HoodieTableConfig.class);
when(tableConfig.getPayloadClass()).thenReturn(CustomPayload.class.getName());
when(tableConfig.getRecordKeyFields()).thenReturn(Option.of(new String[]
{"record_key"}));
+ when(tableConfig.getPartitionFields()).thenReturn(Option.empty());
when(tableConfig.getRecordMergeMode()).thenReturn(RecordMergeMode.CUSTOM);
when(tableConfig.getPartialUpdateMode()).thenReturn(Option.empty());
when(tableConfig.getRecordMergeStrategyId()).thenReturn(HoodieRecordMerger.PAYLOAD_BASED_MERGE_STRATEGY_UUID);
diff --git
a/hudi-common/src/test/java/org/apache/hudi/metadata/TestHoodieBackedTableMetadataDataCleanup.java
b/hudi-common/src/test/java/org/apache/hudi/metadata/TestHoodieBackedTableMetadataDataCleanup.java
index c6c97c66bb7e..27a49bf897f7 100644
---
a/hudi-common/src/test/java/org/apache/hudi/metadata/TestHoodieBackedTableMetadataDataCleanup.java
+++
b/hudi-common/src/test/java/org/apache/hudi/metadata/TestHoodieBackedTableMetadataDataCleanup.java
@@ -602,7 +602,7 @@ public class TestHoodieBackedTableMetadataDataCleanup {
when(timeline.filterCompletedInstants()).thenReturn(timeline);
when(timeline.lastInstant()).thenReturn(Option.empty());
HoodieTableConfig tableConfig = mock(HoodieTableConfig.class);
- when(tableConfig.populateMetaFields()).thenReturn(true);
+ when(tableConfig.isRecordKeyPopulated()).thenReturn(true);
when(tableConfig.getBaseFileFormat()).thenReturn(HoodieFileFormat.PARQUET);
when(metadataMetaClient.getTableConfig()).thenReturn(tableConfig);
diff --git
a/hudi-flink-datasource/hudi-flink/src/main/java/org/apache/hudi/configuration/OptionsResolver.java
b/hudi-flink-datasource/hudi-flink/src/main/java/org/apache/hudi/configuration/OptionsResolver.java
index f848b3ae4343..a0e1c0db4e3a 100644
---
a/hudi-flink-datasource/hudi-flink/src/main/java/org/apache/hudi/configuration/OptionsResolver.java
+++
b/hudi-flink-datasource/hudi-flink/src/main/java/org/apache/hudi/configuration/OptionsResolver.java
@@ -26,6 +26,7 @@ import org.apache.hudi.common.config.HoodieCommonConfig;
import org.apache.hudi.common.config.HoodieStorageConfig;
import org.apache.hudi.common.model.DefaultHoodieRecordPayload;
import org.apache.hudi.common.model.HoodieFailedWritesCleaningPolicy;
+import org.apache.hudi.common.model.MetaFieldsMode;
import org.apache.hudi.common.model.WriteConcurrencyMode;
import org.apache.hudi.common.model.WriteOperationType;
import org.apache.hudi.common.table.HoodieTableConfig;
@@ -565,10 +566,14 @@ public class OptionsResolver {
* Returns whether to populate meta fields or not
*/
public static boolean isPopulateMetaFields(Configuration conf) {
- return Boolean.parseBoolean(
- conf.getString(
- HoodieTableConfig.POPULATE_META_FIELDS.key(),
- HoodieTableConfig.POPULATE_META_FIELDS.defaultValue().toString()));
+ return getMetaFieldsMode(conf).toLegacyPopulateMetaFields();
+ }
+
+ /**
+ * Resolves meta-field population, including the legacy boolean fallback.
+ */
+ public static MetaFieldsMode getMetaFieldsMode(Configuration conf) {
+ return MetaFieldsMode.resolve(conf.toMap());
}
/**
diff --git
a/hudi-flink-datasource/hudi-flink/src/main/java/org/apache/hudi/sink/bulk/BulkInsertWriterHelper.java
b/hudi-flink-datasource/hudi-flink/src/main/java/org/apache/hudi/sink/bulk/BulkInsertWriterHelper.java
index 367f86085613..4fa933d75db6 100644
---
a/hudi-flink-datasource/hudi-flink/src/main/java/org/apache/hudi/sink/bulk/BulkInsertWriterHelper.java
+++
b/hudi-flink-datasource/hudi-flink/src/main/java/org/apache/hudi/sink/bulk/BulkInsertWriterHelper.java
@@ -20,6 +20,7 @@ package org.apache.hudi.sink.bulk;
import org.apache.hudi.client.WriteStatus;
import org.apache.hudi.common.model.HoodieRecord;
+import org.apache.hudi.common.model.MetaFieldsMode;
import org.apache.hudi.common.schema.HoodieSchema;
import org.apache.hudi.common.schema.HoodieSchemaUtils;
import org.apache.hudi.common.util.Option;
@@ -71,6 +72,7 @@ public class BulkInsertWriterHelper implements AutoCloseable {
// table schema handed to the write handles.
protected final HoodieSchema writerSchema;
protected final boolean preserveHoodieMetadata;
+ private final boolean preserveRecordKey;
protected final boolean isAppendMode;
// used for Append mode only, if true then only initial row data without
metacolumns is written
protected final boolean populateMetaFields;
@@ -106,7 +108,8 @@ public class BulkInsertWriterHelper implements
AutoCloseable {
this.totalSubtaskNum = totalSubtaskNum;
this.taskEpochId = taskEpochId;
this.isAppendMode = OptionsResolver.isAppendMode(conf);
- this.populateMetaFields = writeConfig.populateMetaFields();
+ MetaFieldsMode metaFieldsMode = writeConfig.getMetaFieldsMode();
+ this.populateMetaFields = metaFieldsMode != MetaFieldsMode.NONE;
HoodieSchema schema = HoodieSchemaConverter.convertToSchema(
rowType,
HoodieSchemaUtils.getRecordQualifiedName(conf.get(FlinkOptions.TABLE_NAME)),
@@ -115,20 +118,21 @@ public class BulkInsertWriterHelper implements
AutoCloseable {
? schema
: HoodieSchemaUtils.addMetadataFields(schema,
writeConfig.allowOperationMetadataField());
this.preserveHoodieMetadata = preserveHoodieMetadata;
+ this.preserveRecordKey = preserveHoodieMetadata &&
metaFieldsMode.isRecordKeyPopulated();
this.isInputSorted = OptionsResolver.isBulkInsertOperation(conf)
&& (conf.get(FlinkOptions.WRITE_BULK_INSERT_SORT_INPUT)
|| OptionsResolver.isLsmTreeStorageLayout(conf));
this.fileIdPrefix = UUID.randomUUID().toString();
- this.keyGen = preserveHoodieMetadata ? null :
RowDataKeyGens.instance(conf, rowType, taskPartitionId, instantTime);
+ this.keyGen = preserveRecordKey ? null : RowDataKeyGens.instance(conf,
rowType, taskPartitionId, instantTime);
this.writeMetrics = writeMetrics;
}
public void write(RowData record) throws IOException {
try {
- String recordKey = preserveHoodieMetadata
+ String recordKey = preserveRecordKey
? record.getString(HoodieRecord.RECORD_KEY_META_FIELD_ORD).toString()
: keyGen.getRecordKey(record);
- String partitionPath = preserveHoodieMetadata
+ String partitionPath = preserveRecordKey // only ALL mode populates the
partition path meta field
?
record.getString(HoodieRecord.PARTITION_PATH_META_FIELD_ORD).toString()
: keyGen.getPartitionPath(record);
writeRecord(recordKey, partitionPath, record);
diff --git
a/hudi-flink-datasource/hudi-flink/src/main/java/org/apache/hudi/util/StreamerUtil.java
b/hudi-flink-datasource/hudi-flink/src/main/java/org/apache/hudi/util/StreamerUtil.java
index 4da31daddece..470099723090 100644
---
a/hudi-flink-datasource/hudi-flink/src/main/java/org/apache/hudi/util/StreamerUtil.java
+++
b/hudi-flink-datasource/hudi-flink/src/main/java/org/apache/hudi/util/StreamerUtil.java
@@ -361,7 +361,7 @@ public class StreamerUtil {
.setUrlEncodePartitioning(conf.get(FlinkOptions.URL_ENCODE_PARTITIONING))
.setCDCEnabled(conf.get(FlinkOptions.CDC_ENABLED))
.setCDCSupplementalLoggingMode(conf.get(FlinkOptions.SUPPLEMENTAL_LOGGING_MODE))
- .setPopulateMetaFields(OptionsResolver.isPopulateMetaFields(conf))
+ .setMetaFieldsMode(OptionsResolver.getMetaFieldsMode(conf))
.initTable(HadoopFSUtils.getStorageConfWithCopy(hadoopConf),
basePath);
log.info("Table initialized under base path {}", basePath);
} else {
diff --git
a/hudi-client/hudi-flink-client/src/test/java/org/apache/hudi/client/TestFlinkWriteClientFunctional.java
b/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/client/TestFlinkWriteClientFunctional.java
similarity index 79%
rename from
hudi-client/hudi-flink-client/src/test/java/org/apache/hudi/client/TestFlinkWriteClientFunctional.java
rename to
hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/client/TestFlinkWriteClientFunctional.java
index 4d5e2670db14..9a127f96e5f8 100644
---
a/hudi-client/hudi-flink-client/src/test/java/org/apache/hudi/client/TestFlinkWriteClientFunctional.java
+++
b/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/client/TestFlinkWriteClientFunctional.java
@@ -22,8 +22,10 @@ package org.apache.hudi.client;
import org.apache.hudi.avro.model.HoodieClusteringPlan;
import
org.apache.hudi.client.clustering.plan.strategy.FlinkSizeBasedClusteringPlanStrategyRecently;
import org.apache.hudi.client.common.HoodieFlinkEngineContext;
+import org.apache.hudi.client.model.EventTimeFlinkRecordMerger;
import org.apache.hudi.client.model.HoodieFlinkRecord;
import org.apache.hudi.common.config.HoodieMetadataConfig;
+import org.apache.hudi.common.config.HoodieStorageConfig;
import org.apache.hudi.common.engine.EngineType;
import org.apache.hudi.common.fs.FSUtils;
import org.apache.hudi.common.model.HoodieKey;
@@ -31,6 +33,7 @@ import org.apache.hudi.common.model.HoodieOperation;
import org.apache.hudi.common.model.HoodieRecord;
import org.apache.hudi.common.model.HoodieRecordLocation;
import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.model.MetaFieldsMode;
import org.apache.hudi.common.model.WriteOperationType;
import org.apache.hudi.common.table.HoodieTableConfig;
import org.apache.hudi.common.table.HoodieTableMetaClient;
@@ -38,6 +41,7 @@ import
org.apache.hudi.common.table.cdc.HoodieCDCSupplementalLoggingMode;
import org.apache.hudi.common.table.timeline.HoodieInstant;
import org.apache.hudi.common.util.ClusteringUtils;
import org.apache.hudi.common.util.Option;
+import org.apache.hudi.common.util.ParquetUtils;
import org.apache.hudi.config.HoodieClusteringConfig;
import org.apache.hudi.config.HoodieWriteConfig;
import org.apache.hudi.io.FlinkCreateHandle;
@@ -49,8 +53,10 @@ import org.apache.hudi.table.HoodieFlinkTable;
import org.apache.hudi.table.action.HoodieWriteMetadata;
import org.apache.hudi.table.action.commit.BucketInfo;
import org.apache.hudi.table.action.commit.BucketType;
+import org.apache.hudi.table.format.HoodieFlinkIOFactory;
import org.apache.hudi.testutils.HoodieFlinkClientTestHarness;
+import org.apache.avro.generic.GenericRecord;
import org.apache.flink.table.data.GenericRowData;
import org.apache.flink.table.data.StringData;
import org.junit.jupiter.api.AfterEach;
@@ -58,6 +64,7 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.EnumSource;
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
@@ -69,6 +76,7 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Properties;
import java.util.function.Supplier;
import java.util.stream.Stream;
@@ -90,8 +98,7 @@ import static org.mockito.Mockito.when;
* Functional coverage for the Flink client write boundary.
*
* <p>The datasource bucket assigner hands this client records that already
carry a target file group.
- * These tests construct that same input directly so client and handle
behavior can be exercised without
- * depending on the datasource module.
+ * These tests construct that same input directly and use the datasource's
Flink readers for base files.
*/
class TestFlinkWriteClientFunctional extends HoodieFlinkClientTestHarness {
@@ -112,6 +119,8 @@ class TestFlinkWriteClientFunctional extends
HoodieFlinkClientTestHarness {
void setUp() {
initPath();
initFileSystem();
+ storageConf.set(HoodieStorageConfig.HOODIE_IO_FACTORY_CLASS.key(),
HoodieFlinkIOFactory.class.getName());
+ context = new HoodieFlinkEngineContext(storageConf.unwrap());
}
@AfterEach
@@ -178,9 +187,89 @@ class TestFlinkWriteClientFunctional extends
HoodieFlinkClientTestHarness {
}
}
+ @ParameterizedTest
+ @EnumSource(MetaFieldsMode.class)
+ void testCopyOnWriteMetaFieldsMode(MetaFieldsMode mode) throws IOException {
+ initWriteClient(HoodieTableType.COPY_ON_WRITE, false, false, mode);
+ String insertInstant = writeClient.startCommit();
+ transitionToInflight(insertInstant);
+ List<WriteStatus> statuses = writeClient.insert(Arrays.asList(
+ insertRecord("id1", "one", 1L), insertRecord("id2", "two", 2L)),
insertInstant);
+ assertWriteStatuses(statuses, 2);
+ statuses =
writeClient.insert(Collections.singletonList(insertRecord("id3", "three", 3L)),
insertInstant);
+ assertWriteStatuses(statuses, 3);
+ assertTrue(writeClient.commit(insertInstant, statuses));
+ assertMetaFields(statuses, mode, insertInstant, insertInstant);
+
+ writeClient.cleanHandles();
+ String updateInstant = writeClient.startCommit();
+ transitionToInflight(updateInstant);
+ statuses = writeClient.upsert(Arrays.asList(
+ updateRecord("id1", "updated", 4L, insertInstant),
+ deleteRecord("id2", 5L, insertInstant)), updateInstant);
+ assertWriteStatuses(statuses, 2);
+ assertTrue(writeClient.commit(updateInstant, statuses));
+ assertMetaFields(statuses, mode, insertInstant, updateInstant);
+ }
+
+ @ParameterizedTest
+ @EnumSource(MetaFieldsMode.class)
+ void testCopyOnWriteConcatMetaFieldsMode(MetaFieldsMode mode) throws
IOException {
+ initWriteClient(HoodieTableType.COPY_ON_WRITE, false, false, mode);
+
writeConfig.setValue(HoodieWriteConfig.MERGE_ALLOW_DUPLICATE_ON_INSERTS_ENABLE,
"true");
+ String insertInstant = writeClient.startCommit();
+ transitionToInflight(insertInstant);
+ writeClient.insert(Collections.singletonList(insertRecord("id1", "one",
1L)), insertInstant);
+ List<WriteStatus> statuses = writeClient.insert(
+ Collections.singletonList(insertRecord("id1", "one", 1L)),
insertInstant);
+ assertWriteStatuses(statuses, 2);
+ assertTrue(writeClient.commit(insertInstant, statuses));
+ assertMetaFields(statuses, mode, insertInstant, insertInstant);
+
+ writeClient.cleanHandles();
+ String nextInstant = writeClient.startCommit();
+ transitionToInflight(nextInstant);
+ // Target the existing file group while keeping insert semantics.
+ statuses = writeClient.insert(Collections.singletonList(
+ updateRecord("id3", "three", 3L, insertInstant)), nextInstant);
+ assertWriteStatuses(statuses, 3);
+ assertTrue(writeClient.commit(nextInstant, statuses));
+ for (WriteStatus status : statuses) {
+ StoragePath path = new StoragePath(basePath, status.getStat().getPath());
+ for (GenericRecord row : new
ParquetUtils().readAvroRecords(metaClient.getStorage(), path)) {
+ assertEquals(mode.isFileNamePopulated(),
row.get(HoodieRecord.FILENAME_METADATA_FIELD) != null);
+ String expectedInstant = row.get("id").toString().equals("id3") ?
nextInstant : insertInstant;
+ assertEquals(mode.isCommitTimePopulated() ? expectedInstant : null,
+ Objects.toString(row.get(HoodieRecord.COMMIT_TIME_METADATA_FIELD),
null));
+ }
+ }
+ }
+
+ private void assertMetaFields(List<WriteStatus> statuses, MetaFieldsMode
mode,
+ String insertInstant, String updateInstant) {
+ for (WriteStatus status : statuses) {
+ StoragePath path = new StoragePath(basePath, status.getStat().getPath());
+ for (GenericRecord row : new
ParquetUtils().readAvroRecords(metaClient.getStorage(), path)) {
+ String id = row.get("id").toString();
+ String expectedInstant = id.equals("id1") ? updateInstant :
insertInstant;
+ assertEquals(mode.isCommitTimePopulated() ? expectedInstant : null,
+ Objects.toString(row.get(HoodieRecord.COMMIT_TIME_METADATA_FIELD),
null));
+ assertEquals(mode != MetaFieldsMode.ALL,
row.get(HoodieRecord.COMMIT_SEQNO_METADATA_FIELD) == null);
+ assertEquals(mode.isRecordKeyPopulated() ? id : null,
+ Objects.toString(row.get(HoodieRecord.RECORD_KEY_METADATA_FIELD),
null));
+ assertEquals(mode == MetaFieldsMode.ALL ? PARTITION_PATH : null,
+
Objects.toString(row.get(HoodieRecord.PARTITION_PATH_METADATA_FIELD), null));
+ assertEquals(mode.isFileNamePopulated(),
row.get(HoodieRecord.FILENAME_METADATA_FIELD) != null);
+ if (id.equals("id1")) {
+ assertEquals(insertInstant.equals(updateInstant) ? "one" :
"updated", row.get("name").toString());
+ }
+ }
+ }
+ }
+
@Test
void testCopyOnWriteCleansRetryFiles() throws IOException {
- context = new HoodieFlinkEngineContext(
+ context = new HoodieFlinkEngineContext(storageConf,
new HoodieFlinkEngineContext.DefaultTaskContextSupplier() {
@Override
public Supplier<Long> getAttemptIdSupplier() {
@@ -408,7 +497,13 @@ class TestFlinkWriteClientFunctional extends
HoodieFlinkClientTestHarness {
private void initWriteClient(
HoodieTableType tableType, boolean cdcEnabled, boolean
useRecentClusteringStrategy)
throws IOException {
+ initWriteClient(tableType, cdcEnabled, useRecentClusteringStrategy,
MetaFieldsMode.ALL);
+ }
+
+ private void initWriteClient(HoodieTableType tableType, boolean cdcEnabled,
+ boolean useRecentClusteringStrategy,
MetaFieldsMode mode) throws IOException {
Properties tableProperties = new Properties();
+ tableProperties.setProperty(HoodieTableConfig.META_FIELDS_MODE.key(),
mode.name());
tableProperties.setProperty(HoodieTableConfig.CDC_ENABLED.key(),
Boolean.toString(cdcEnabled));
tableProperties.setProperty(
HoodieTableConfig.CDC_SUPPLEMENTAL_LOGGING_MODE.key(),
@@ -427,6 +522,8 @@ class TestFlinkWriteClientFunctional extends
HoodieFlinkClientTestHarness {
HoodieWriteConfig.Builder builder = HoodieWriteConfig.newBuilder()
.withPath(basePath)
.withEngineType(EngineType.FLINK)
+ .withRecordMergeImplClasses(EventTimeFlinkRecordMerger.class.getName())
+
.withRecordMergeStrategyId(EventTimeFlinkRecordMerger.EVENT_TIME_BASED_MERGE_STRATEGY_UUID)
.withSchema(SCHEMA)
.withProperties(tableProperties)
.withMetadataConfig(HoodieMetadataConfig.newBuilder().enable(false).build())
diff --git
a/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/configuration/TestOptionsResolver.java
b/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/configuration/TestOptionsResolver.java
index 15ff8110dcbb..965e947ab73c 100644
---
a/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/configuration/TestOptionsResolver.java
+++
b/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/configuration/TestOptionsResolver.java
@@ -25,6 +25,7 @@ import org.apache.hudi.common.config.HoodieMetadataConfig;
import org.apache.hudi.common.model.DefaultHoodieRecordPayload;
import org.apache.hudi.common.model.HoodieFailedWritesCleaningPolicy;
import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.model.MetaFieldsMode;
import org.apache.hudi.common.model.WriteConcurrencyMode;
import org.apache.hudi.common.model.WriteOperationType;
import org.apache.hudi.common.table.HoodieTableConfig;
@@ -60,6 +61,17 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
*/
public class TestOptionsResolver {
+ @Test
+ void testMetaFieldsModeLegacyFallback() {
+ Configuration conf = new Configuration();
+ assertEquals(MetaFieldsMode.ALL, OptionsResolver.getMetaFieldsMode(conf));
+ conf.setString(HoodieTableConfig.POPULATE_META_FIELDS.key(), "false");
+ assertEquals(MetaFieldsMode.NONE, OptionsResolver.getMetaFieldsMode(conf));
+ conf.setString(HoodieTableConfig.META_FIELDS_MODE.key(),
"commit_time_only");
+ assertEquals(MetaFieldsMode.COMMIT_TIME_ONLY,
OptionsResolver.getMetaFieldsMode(conf));
+ assertFalse(OptionsResolver.isPopulateMetaFields(conf));
+ }
+
@Test
void testTableStorageLayoutDefaultsByOperation() {
Configuration conf = new Configuration();
diff --git
a/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/sink/TestWriteCopyOnWrite.java
b/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/sink/TestWriteCopyOnWrite.java
index 008494fade54..a9e38d0d8e48 100644
---
a/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/sink/TestWriteCopyOnWrite.java
+++
b/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/sink/TestWriteCopyOnWrite.java
@@ -24,9 +24,12 @@ import org.apache.hudi.common.config.HoodieMetadataConfig;
import org.apache.hudi.common.data.HoodieListData;
import org.apache.hudi.common.model.HoodieFailedWritesCleaningPolicy;
import org.apache.hudi.common.model.HoodieKey;
+import org.apache.hudi.common.model.HoodieRecord;
import org.apache.hudi.common.model.HoodieRecordGlobalLocation;
import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.model.MetaFieldsMode;
import org.apache.hudi.common.model.WriteConcurrencyMode;
+import org.apache.hudi.common.table.HoodieTableConfig;
import org.apache.hudi.common.table.HoodieTableMetaClient;
import org.apache.hudi.common.table.marker.MarkerType;
import org.apache.hudi.common.table.timeline.HoodieTimeline;
@@ -66,10 +69,12 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertEquals;
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;
@@ -340,6 +345,73 @@ public class TestWriteCopyOnWrite extends TestWriteBase {
.end();
}
+ @ParameterizedTest
+ @MethodSource("mergeWithMetaFieldsModeParams")
+ public void testMergeWithMetaFieldsMode(MetaFieldsMode mode, String
storageLayout, String mergeHandleClass) throws Exception {
+ conf.setString(HoodieTableConfig.TABLE_STORAGE_LAYOUT.key(),
storageLayout);
+ conf.setString(HoodieTableConfig.META_FIELDS_MODE.key(), mode.name());
+ conf.setString(HoodieWriteConfig.MERGE_HANDLE_CLASS_NAME.key(),
mergeHandleClass);
+ preparePipeline(conf)
+ .consume(TestData.DATA_SET_INSERT.subList(0, 2))
+ .checkpoint(1)
+ .assertNextEvent()
+ .checkpointComplete(1)
+ .consume(TestData.DATA_SET_UPDATE_INSERT.subList(0, 1))
+ .checkpoint(2)
+ .assertNextEvent()
+ .checkpointComplete(2)
+ .end();
+ TestData.checkWrittenData(tempFile, Collections.singletonMap("par1",
"[id1:24, id2:33]"), 1,
+ record -> record.get("uuid") + ":" + record.get("age"));
+ }
+
+ private static Stream<Arguments> mergeWithMetaFieldsModeParams() {
+ return Arrays.stream(MetaFieldsMode.values()).flatMap(mode -> Stream.of(
+ Arguments.of(mode, "DEFAULT", HoodieWriteMergeHandle.class.getName()),
+ Arguments.of(mode, "DEFAULT",
FileGroupReaderBasedMergeHandle.class.getName()),
+ Arguments.of(mode, "LSM_TREE",
FileGroupReaderBasedMergeHandle.class.getName())));
+ }
+
+ @ParameterizedTest
+ @MethodSource("mergeWithMultiplePartitionFieldsParams")
+ public void testMergeWithMultiplePartitionFieldsAndMetaFieldsMode(
+ MetaFieldsMode mode, String storageLayout, String mergeHandleClass)
throws Exception {
+ conf.set(FlinkOptions.OPERATION, "upsert");
+ conf.set(FlinkOptions.RECORD_KEY_FIELD, "uuid");
+ conf.set(FlinkOptions.PARTITION_PATH_FIELD, "partition,name");
+ conf.setString(HoodieTableConfig.TABLE_STORAGE_LAYOUT.key(),
storageLayout);
+ conf.setString(HoodieTableConfig.META_FIELDS_MODE.key(), mode.name());
+ conf.setString(HoodieWriteConfig.MERGE_HANDLE_CLASS_NAME.key(),
mergeHandleClass);
+ conf.setString(HoodieWriteConfig.COMPLEX_KEYGEN_NEW_ENCODING.key(),
"false");
+ preparePipeline(conf)
+ .consume(TestData.DATA_SET_INSERT.subList(0, 2))
+ .checkpoint(1)
+ .assertNextEvent()
+ .checkpointComplete(1)
+ .consume(TestData.DATA_SET_UPDATE_INSERT.subList(0, 1))
+ .checkpoint(2)
+ .assertNextEvent()
+ .checkpointComplete(2)
+ .end();
+
+ Map<String, String> expected = new HashMap<>();
+ expected.put("par1/Danny", "[id1:24]");
+ expected.put("par1/Stephen", "[id2:33]");
+ // Both leaf partitions share the same top-level directory. The updated
key must occur only once.
+ TestData.checkWrittenData(tempFile, expected, 1, record -> {
+ if (mode == MetaFieldsMode.NONE) {
+
assertNull(record.getSchema().getField(HoodieRecord.RECORD_KEY_METADATA_FIELD));
+ } else {
+ assertNull(record.get(HoodieRecord.RECORD_KEY_METADATA_FIELD));
+ }
+ return record.get("uuid") + ":" + record.get("age");
+ });
+ }
+
+ private static Stream<Arguments> mergeWithMultiplePartitionFieldsParams() {
+ return mergeWithMetaFieldsModeParams().filter(args -> args.get()[0] !=
MetaFieldsMode.ALL);
+ }
+
@Test
public void testInsertDuplicates() throws Exception {
// reset the config option
@@ -503,9 +575,19 @@ public class TestWriteCopyOnWrite extends TestWriteBase {
* The test is almost same with {@link #testInsertWithSmallBufferSize}
except that
* it is with insert clustering mode.
*/
- @Test
- public void testInsertClustering() throws Exception {
+ @ParameterizedTest
+ @EnumSource(MetaFieldsMode.class)
+ public void testInsertClustering(MetaFieldsMode mode) throws Exception {
+ List<String> expected = Arrays.asList(
+ "id1,Danny,23,1970-01-01 00:00:00.0,par1",
+ "id1,Danny,23,1970-01-01 00:00:00.001,par1",
+ "id1,Danny,23,1970-01-01 00:00:00.002,par1",
+ "id1,Danny,23,1970-01-01 00:00:00.003,par1",
+ "id1,Danny,23,1970-01-01 00:00:00.004,par1");
+ List<String> expectedDuplicates = Stream.concat(expected.stream(),
expected.stream()).sorted().collect(Collectors.toList());
+
// reset the config option
+ conf.setString(HoodieTableConfig.META_FIELDS_MODE.key(), mode.name());
conf.set(FlinkOptions.OPERATION, "insert");
conf.set(FlinkOptions.INSERT_CLUSTER, true);
conf.set(FlinkOptions.WRITE_MEMORY_SEGMENT_PAGE_SIZE, 64);
@@ -521,13 +603,13 @@ public class TestWriteCopyOnWrite extends TestWriteBase {
.allDataFlushed()
.handleEvents(2)
.checkpointComplete(1)
- .checkWrittenData(EXPECTED4, 1)
- // insert duplicates again
+ .checkWrittenDataNoMeta(Collections.singletonMap("par1",
expected.toString()), 1)
+ // Insert duplicates again, exercising concat across checkpoints as
well as mini-batches.
.consume(TestData.DATA_SET_INSERT_SAME_KEY)
.checkpoint(2)
.handleEvents(2)
.checkpointComplete(2)
- .checkWrittenDataCOW(EXPECTED5)
+ .checkWrittenDataNoMeta(Collections.singletonMap("par1",
expectedDuplicates.toString()), 1)
.end();
}
diff --git
a/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/sink/TestWriteMergeOnRead.java
b/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/sink/TestWriteMergeOnRead.java
index d7572b4b4209..64cdd0405518 100644
---
a/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/sink/TestWriteMergeOnRead.java
+++
b/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/sink/TestWriteMergeOnRead.java
@@ -25,6 +25,7 @@ import org.apache.hudi.common.model.HoodieCommitMetadata;
import org.apache.hudi.common.model.HoodieKey;
import org.apache.hudi.common.model.HoodieTableType;
import org.apache.hudi.common.model.HoodieWriteStat;
+import org.apache.hudi.common.model.MetaFieldsMode;
import org.apache.hudi.common.table.HoodieTableMetaClient;
import org.apache.hudi.common.table.timeline.HoodieInstant;
import org.apache.hudi.config.HoodieClusteringConfig;
@@ -171,10 +172,21 @@ public class TestWriteMergeOnRead extends
TestWriteCopyOnWrite {
}
@Override
- public void testInsertClustering() {
+ public void testInsertClustering(MetaFieldsMode mode) {
// insert clustering is only valid for cow table.
}
+ @Override
+ public void testMergeWithMetaFieldsMode(MetaFieldsMode mode, String
storageLayout, String mergeHandleClass) {
+ // These tests exercise COW merge handles; MOR does not support selective
metadata modes.
+ }
+
+ @Override
+ public void testMergeWithMultiplePartitionFieldsAndMetaFieldsMode(
+ MetaFieldsMode mode, String storageLayout, String mergeHandleClass) {
+ // These tests exercise COW merge handles; MOR does not support selective
metadata modes.
+ }
+
@Test
public void testInsertAsyncClustering() {
// insert async clustering is only valid for cow table.
diff --git
a/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/sink/TestWriteMergeOnReadWithCompact.java
b/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/sink/TestWriteMergeOnReadWithCompact.java
index b71a4611da93..d1daaa312457 100644
---
a/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/sink/TestWriteMergeOnReadWithCompact.java
+++
b/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/sink/TestWriteMergeOnReadWithCompact.java
@@ -20,6 +20,7 @@ package org.apache.hudi.sink;
import org.apache.hudi.client.HoodieFlinkWriteClient;
import org.apache.hudi.common.model.HoodieTableType;
+import org.apache.hudi.common.model.MetaFieldsMode;
import org.apache.hudi.common.model.PartialUpdateAvroPayload;
import org.apache.hudi.common.model.WriteConcurrencyMode;
import org.apache.hudi.common.table.HoodieTableConfig;
@@ -99,10 +100,21 @@ public class TestWriteMergeOnReadWithCompact extends
TestWriteCopyOnWrite {
}
@Override
- public void testInsertClustering() {
+ public void testInsertClustering(MetaFieldsMode mode) {
// insert clustering is only valid for cow table.
}
+ @Override
+ public void testMergeWithMetaFieldsMode(MetaFieldsMode mode, String
storageLayout, String mergeHandleClass) {
+ // These tests exercise COW merge handles; MOR does not support selective
metadata modes.
+ }
+
+ @Override
+ public void testMergeWithMultiplePartitionFieldsAndMetaFieldsMode(
+ MetaFieldsMode mode, String storageLayout, String mergeHandleClass) {
+ // These tests exercise COW merge handles; MOR does not support selective
metadata modes.
+ }
+
@Test
public void testInsertAsyncClustering() {
// insert async clustering is only valid for cow table.
diff --git
a/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/sink/bulk/TestBulkInsertWriteHelper.java
b/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/sink/bulk/TestBulkInsertWriteHelper.java
index c79f33123769..da26b9d88b4c 100644
---
a/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/sink/bulk/TestBulkInsertWriteHelper.java
+++
b/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/sink/bulk/TestBulkInsertWriteHelper.java
@@ -20,7 +20,14 @@ package org.apache.hudi.sink.bulk;
import org.apache.hudi.client.WriteClientTestUtils;
import org.apache.hudi.client.WriteStatus;
+import org.apache.hudi.client.model.HoodieRowDataCreation;
+import org.apache.hudi.common.model.HoodieRecord;
+import org.apache.hudi.common.model.MetaFieldsMode;
+import org.apache.hudi.common.table.HoodieTableConfig;
+import org.apache.hudi.common.util.ParquetUtils;
import org.apache.hudi.common.util.StringUtils;
+import org.apache.hudi.configuration.FlinkOptions;
+import org.apache.hudi.storage.StoragePath;
import org.apache.hudi.table.HoodieFlinkTable;
import org.apache.hudi.util.DataTypeUtils;
import org.apache.hudi.util.FlinkTables;
@@ -37,17 +44,25 @@ import org.apache.flink.table.types.logical.RowType;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
/**
@@ -65,6 +80,69 @@ public class TestBulkInsertWriteHelper {
StreamerUtil.initTableIfNotExists(conf);
}
+ @ParameterizedTest
+ @MethodSource("metaFieldsModeParams")
+ void testMetaFieldsMode(MetaFieldsMode mode, boolean preserveMetadata)
throws Exception {
+ for (String operation : new String[] {"insert", "bulk_insert"}) {
+ File path = new File(tempFile, operation);
+ Configuration modeConf =
TestConfigurations.getDefaultConf(path.getAbsolutePath(),
TestConfigurations.ROW_DATA_TYPE);
+ modeConf.set(FlinkOptions.TABLE_TYPE, "COPY_ON_WRITE");
+ modeConf.set(FlinkOptions.OPERATION, operation);
+ modeConf.set(FlinkOptions.INSERT_CLUSTER, false);
+ modeConf.setString(HoodieTableConfig.TABLE_STORAGE_LAYOUT.key(),
"DEFAULT");
+ modeConf.setString(HoodieTableConfig.META_FIELDS_MODE.key(),
mode.name());
+ StreamerUtil.initTableIfNotExists(modeConf);
+ HoodieFlinkTable<?> table = FlinkTables.createTable(modeConf);
+ String instant = WriteClientTestUtils.createNewInstantTime();
+ String expectedCommitTime = preserveMetadata ? "old-instant" : instant;
+ RowType rowType = preserveMetadata ?
DataTypeUtils.addMetadataFields(TestConfigurations.ROW_TYPE, false) :
TestConfigurations.ROW_TYPE;
+ BulkInsertWriterHelper helper = new BulkInsertWriterHelper(modeConf,
table, table.getConfig(),
+ instant, 1, 1, 0, rowType, preserveMetadata);
+ for (RowData row : TestData.DATA_SET_INSERT) {
+ if (preserveMetadata) {
+ // Clustering reads rows with metadata columns, but selective modes
leave keys null.
+ row = HoodieRowDataCreation.create(mode.isCommitTimePopulated() ?
expectedCommitTime : null,
+ mode == MetaFieldsMode.ALL ? "old-sequence" : null,
+ mode.isRecordKeyPopulated() ? row.getString(0).toString() : null,
+ mode == MetaFieldsMode.ALL ? row.getString(4).toString() : null,
+ mode.isFileNamePopulated() ? "old.parquet" : null, row, false,
false);
+ }
+ helper.write(row);
+ }
+ List<WriteStatus> statuses = helper.getWriteStatuses(1);
+ assertWriteStatus(statuses);
+ assertEquals(TestData.DATA_SET_INSERT.size(),
statuses.stream().mapToLong(status -> status.getStat().getNumWrites()).sum());
+ for (WriteStatus status : statuses) {
+ assertFalse(status.hasErrors());
+ StoragePath file = new StoragePath(path.getAbsolutePath(),
status.getStat().getPath());
+ for (GenericRecord row : new
ParquetUtils().readAvroRecords(table.getStorage(), file)) {
+ if (mode == MetaFieldsMode.NONE && operation.equals("insert") &&
!preserveMetadata) {
+ assertEquals(TestConfigurations.ROW_TYPE.getFieldCount(),
row.getSchema().getFields().size());
+ for (String field :
HoodieRecord.HOODIE_META_COLUMNS_WITH_OPERATION) {
+ assertEquals(null, row.getSchema().getField(field), field);
+ }
+ continue;
+ }
+ assertEquals(row.get("partition").toString(),
status.getStat().getPartitionPath());
+ assertEquals(mode.isCommitTimePopulated() ? expectedCommitTime :
null,
+
Objects.toString(row.get(HoodieRecord.COMMIT_TIME_METADATA_FIELD), null));
+ assertEquals(mode != MetaFieldsMode.ALL,
row.get(HoodieRecord.COMMIT_SEQNO_METADATA_FIELD) == null);
+ assertEquals(mode.isRecordKeyPopulated() ?
row.get("uuid").toString() : null,
+
Objects.toString(row.get(HoodieRecord.RECORD_KEY_METADATA_FIELD), null));
+ assertEquals(mode == MetaFieldsMode.ALL ?
status.getStat().getPartitionPath() : null,
+
Objects.toString(row.get(HoodieRecord.PARTITION_PATH_METADATA_FIELD), null));
+ assertEquals(mode.isFileNamePopulated() ? file.getName() : null,
+ Objects.toString(row.get(HoodieRecord.FILENAME_METADATA_FIELD),
null));
+ }
+ }
+ }
+ }
+
+ private static Stream<Arguments> metaFieldsModeParams() {
+ return Arrays.stream(MetaFieldsMode.values()).flatMap(mode -> Stream.of(
+ Arguments.of(mode, false), Arguments.of(mode, true)));
+ }
+
@Test
void testWrite() throws Exception {
HoodieFlinkTable<?> table = FlinkTables.createTable(conf);
diff --git
a/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/sink/utils/TestWriteBase.java
b/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/sink/utils/TestWriteBase.java
index b051cad77a6e..623a99704951 100644
---
a/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/sink/utils/TestWriteBase.java
+++
b/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/sink/utils/TestWriteBase.java
@@ -620,6 +620,16 @@ public class TestWriteBase {
return this;
}
+ /**
+ * Checks base-file records without Hudi metadata fields.
+ */
+ public TestHarness checkWrittenDataNoMeta(
+ Map<String, String> expected,
+ int partitions) throws IOException {
+ TestData.checkWrittenData(this.baseFile, expected, partitions,
TestData::filterOutVariablesWithoutHudiMetadata);
+ return this;
+ }
+
public TestHarness checkWrittenAllData(Map<String, String> expected, int
partitions) throws IOException {
TestData.checkWrittenAllData(baseFile, expected, partitions);
return this;
diff --git
a/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/table/TestHoodieFileGroupReaderOnFlink.java
b/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/table/TestHoodieFileGroupReaderOnFlink.java
index 84e1136bc296..2d0258c17a16 100644
---
a/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/table/TestHoodieFileGroupReaderOnFlink.java
+++
b/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/table/TestHoodieFileGroupReaderOnFlink.java
@@ -211,7 +211,7 @@ public class TestHoodieFileGroupReaderOnFlink extends
TestHoodieFileGroupReaderB
@Test
public void testGetOrderingValue() {
HoodieTableConfig tableConfig = Mockito.mock(HoodieTableConfig.class);
- when(tableConfig.populateMetaFields()).thenReturn(true);
+ when(tableConfig.isRecordKeyPopulated()).thenReturn(true);
FlinkRowDataReaderContext readerContext =
new FlinkRowDataReaderContext(getStorageConf(), () ->
InternalSchemaManager.DISABLED, Collections.emptyList(), tableConfig,
Option.empty());
HoodieSchema schema = HoodieSchema.createRecord("test", null, null,
@@ -228,7 +228,7 @@ public class TestHoodieFileGroupReaderOnFlink extends
TestHoodieFileGroupReaderB
@Test
public void getRecordKeyFromMetadataFields() {
HoodieTableConfig tableConfig = Mockito.mock(HoodieTableConfig.class);
- when(tableConfig.populateMetaFields()).thenReturn(true);
+ when(tableConfig.isRecordKeyPopulated()).thenReturn(true);
FlinkRowDataReaderContext readerContext =
new FlinkRowDataReaderContext(getStorageConf(), () ->
InternalSchemaManager.DISABLED, Collections.emptyList(), tableConfig,
Option.empty());
HoodieSchema schema = HoodieSchema.createRecord("test", null, null,
@@ -244,7 +244,8 @@ public class TestHoodieFileGroupReaderOnFlink extends
TestHoodieFileGroupReaderB
@Test
public void getRecordKeySingleKey() {
HoodieTableConfig tableConfig = Mockito.mock(HoodieTableConfig.class);
- when(tableConfig.populateMetaFields()).thenReturn(false);
+ when(tableConfig.isRecordKeyPopulated()).thenReturn(false);
+ when(tableConfig.getPartitionFields()).thenReturn(Option.empty());
when(tableConfig.getRecordKeyFields()).thenReturn(Option.of(new String[]
{"field1"}));
FlinkRowDataReaderContext readerContext =
new FlinkRowDataReaderContext(getStorageConf(), () ->
InternalSchemaManager.DISABLED, Collections.emptyList(), tableConfig,
Option.empty());
@@ -261,7 +262,8 @@ public class TestHoodieFileGroupReaderOnFlink extends
TestHoodieFileGroupReaderB
@Test
public void getRecordKeyWithMultipleKeys() {
HoodieTableConfig tableConfig = Mockito.mock(HoodieTableConfig.class);
- when(tableConfig.populateMetaFields()).thenReturn(false);
+ when(tableConfig.isRecordKeyPopulated()).thenReturn(false);
+ when(tableConfig.getPartitionFields()).thenReturn(Option.empty());
when(tableConfig.getRecordKeyFields()).thenReturn(Option.of(new String[]
{"field1", "field2"}));
FlinkRowDataReaderContext readerContext =
new FlinkRowDataReaderContext(getStorageConf(), () ->
InternalSchemaManager.DISABLED, Collections.emptyList(), tableConfig,
Option.empty());
diff --git
a/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/table/format/TestFlinkRowDataReaderContext.java
b/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/table/format/TestFlinkRowDataReaderContext.java
index 751f63d6de75..f89b367441a4 100644
---
a/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/table/format/TestFlinkRowDataReaderContext.java
+++
b/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/table/format/TestFlinkRowDataReaderContext.java
@@ -62,7 +62,7 @@ class TestFlinkRowDataReaderContext {
@BeforeEach
void setUp() {
List<ExpressionPredicates.Predicate> predicates = new ArrayList<>();
- when(tableConfig.populateMetaFields()).thenReturn(true);
+ when(tableConfig.isRecordKeyPopulated()).thenReturn(true);
when(tableConfig.getBaseFileFormat()).thenReturn(HoodieFileFormat.PARQUET);
when(tableConfig.getRecordKeyFields()).thenReturn(Option.of(new
String[]{"id"}));
readerContext = new FlinkRowDataReaderContext(
diff --git
a/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/utils/TestStreamerUtil.java
b/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/utils/TestStreamerUtil.java
index becc48b64786..f6fd78802e0d 100644
---
a/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/utils/TestStreamerUtil.java
+++
b/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/utils/TestStreamerUtil.java
@@ -28,6 +28,7 @@ import org.apache.hudi.common.config.HoodieStorageConfig;
import org.apache.hudi.common.config.RecordMergeMode;
import org.apache.hudi.common.config.TypedProperties;
import org.apache.hudi.common.model.EventTimeAvroPayload;
+import org.apache.hudi.common.model.MetaFieldsMode;
import org.apache.hudi.common.model.OverwriteWithLatestAvroPayload;
import org.apache.hudi.common.model.PartialUpdateAvroPayload;
import org.apache.hudi.common.model.WriteOperationType;
@@ -62,6 +63,8 @@ import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
import org.mockito.Mockito;
import java.io.File;
@@ -92,6 +95,20 @@ class TestStreamerUtil {
@TempDir
File tempFile;
+ @ParameterizedTest
+ @EnumSource(MetaFieldsMode.class)
+ void testInitTableMetaFieldsMode(MetaFieldsMode mode) throws IOException {
+ Configuration conf =
TestConfigurations.getDefaultConf(tempFile.getAbsolutePath());
+ conf.set(FlinkOptions.TABLE_TYPE, "COPY_ON_WRITE");
+ conf.set(FlinkOptions.WRITE_TABLE_VERSION,
HoodieTableVersion.TEN.versionCode());
+ conf.setString(HoodieTableConfig.META_FIELDS_MODE.key(), mode.name());
+ // The explicit mode must win over the legacy default.
+ conf.setString(HoodieTableConfig.POPULATE_META_FIELDS.key(), "true");
+ HoodieTableMetaClient metaClient = StreamerUtil.initTableIfNotExists(conf);
+ assertEquals(mode, metaClient.getTableConfig().getMetaFieldsMode());
+ assertEquals(mode.toLegacyPopulateMetaFields(),
metaClient.getTableConfig().populateMetaFields());
+ }
+
@Test
void testMetadataConfigIncludesMetadataTableBloomFilterSettings() {
Configuration conf =
TestConfigurations.getDefaultConf(tempFile.getAbsolutePath());
diff --git
a/hudi-hadoop-mr/src/test/java/org/apache/hudi/hadoop/TestHiveHoodieReaderContext.java
b/hudi-hadoop-mr/src/test/java/org/apache/hudi/hadoop/TestHiveHoodieReaderContext.java
index 9b01becfd81c..9a484c6edf10 100644
---
a/hudi-hadoop-mr/src/test/java/org/apache/hudi/hadoop/TestHiveHoodieReaderContext.java
+++
b/hudi-hadoop-mr/src/test/java/org/apache/hudi/hadoop/TestHiveHoodieReaderContext.java
@@ -81,7 +81,8 @@ class TestHiveHoodieReaderContext {
@Test
void getRecordKeyWithSingleKey() {
- when(tableConfig.populateMetaFields()).thenReturn(false);
+ when(tableConfig.isRecordKeyPopulated()).thenReturn(false);
+ when(tableConfig.getPartitionFields()).thenReturn(Option.empty());
when(tableConfig.getRecordKeyFields()).thenReturn(Option.of(new
String[]{"field_1"}));
HiveHoodieReaderContext avroReaderContext = new
HiveHoodieReaderContext(readerCreator, Collections.emptyList(),
storageConfiguration, tableConfig);
ArrayWritable row = new ArrayWritable(Writable.class, new Writable[]{new
Text("value1"), new Text("value2"), new ArrayWritable(new String[]{"value3"})});
@@ -91,7 +92,8 @@ class TestHiveHoodieReaderContext {
@Test
void getRecordKeyWithMultipleKeys() {
- when(tableConfig.populateMetaFields()).thenReturn(false);
+ when(tableConfig.isRecordKeyPopulated()).thenReturn(false);
+ when(tableConfig.getPartitionFields()).thenReturn(Option.empty());
when(tableConfig.getRecordKeyFields()).thenReturn(Option.of(new
String[]{"field_1", "field_3.nested_field"}));
HiveHoodieReaderContext avroReaderContext = new
HiveHoodieReaderContext(readerCreator, Collections.emptyList(),
storageConfiguration, tableConfig);
ArrayWritable row = new ArrayWritable(Writable.class, new Writable[]{new
Text("value1"), new Text("value2"), new ArrayWritable(new String[]{"value3"})});
@@ -101,7 +103,7 @@ class TestHiveHoodieReaderContext {
@Test
void getNestedField() {
- when(tableConfig.populateMetaFields()).thenReturn(true);
+ when(tableConfig.isRecordKeyPopulated()).thenReturn(true);
HiveHoodieReaderContext avroReaderContext = new
HiveHoodieReaderContext(readerCreator, Collections.emptyList(),
storageConfiguration, tableConfig);
ArrayWritable row = new ArrayWritable(Writable.class, new Writable[]{new
Text("value1"), new Text("value2"), new ArrayWritable(new String[]{"value3"})});
@@ -110,7 +112,7 @@ class TestHiveHoodieReaderContext {
@Test
void testConstructEngineRecordWithFieldValues() {
- when(tableConfig.populateMetaFields()).thenReturn(true);
+ when(tableConfig.isRecordKeyPopulated()).thenReturn(true);
HiveHoodieReaderContext avroReaderContext = new HiveHoodieReaderContext(
readerCreator, Collections.emptyList(), storageConfiguration,
tableConfig);
Object[] fieldVals = new Writable[]{
@@ -126,7 +128,7 @@ class TestHiveHoodieReaderContext {
@Test
void testConstructEngineRecordWithNoUpdates() {
- when(tableConfig.populateMetaFields()).thenReturn(true);
+ when(tableConfig.isRecordKeyPopulated()).thenReturn(true);
HiveHoodieReaderContext avroReaderContext = new HiveHoodieReaderContext(
readerCreator, Collections.emptyList(), storageConfiguration,
tableConfig);
@@ -147,7 +149,7 @@ class TestHiveHoodieReaderContext {
@Test
void testConstructEngineRecordWithUpdates() {
- when(tableConfig.populateMetaFields()).thenReturn(true);
+ when(tableConfig.isRecordKeyPopulated()).thenReturn(true);
HiveHoodieReaderContext avroReaderContext = new HiveHoodieReaderContext(
readerCreator, Collections.emptyList(), storageConfiguration,
tableConfig);
@@ -389,7 +391,7 @@ class TestHiveHoodieReaderContext {
}
private HiveHoodieReaderContext newReaderContext() {
- when(tableConfig.populateMetaFields()).thenReturn(true);
+ when(tableConfig.isRecordKeyPopulated()).thenReturn(true);
HiveHoodieReaderContext readerContext =
new HiveHoodieReaderContext(readerCreator, Collections.emptyList(),
storageConfiguration, tableConfig);
readerContext.setNeedsBootstrapMerge(false);
diff --git
a/hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/functional/TestBufferedRecordMerger.java
b/hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/functional/TestBufferedRecordMerger.java
index cb6999cb62b6..d074cff217ff 100644
---
a/hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/functional/TestBufferedRecordMerger.java
+++
b/hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/functional/TestBufferedRecordMerger.java
@@ -119,7 +119,8 @@ class TestBufferedRecordMerger extends
SparkClientFunctionalTestHarness {
storageConfig = mock(StorageConfiguration.class);
when(tableConfig.getPayloadClass()).thenReturn(
"org.apache.hudi.common.model.DefaultHoodieRecordPayload");
- when(tableConfig.populateMetaFields()).thenReturn(false);
+ when(tableConfig.isRecordKeyPopulated()).thenReturn(false);
+ when(tableConfig.getPartitionFields()).thenReturn(Option.empty());
when(tableConfig.getRecordKeyFields()).thenReturn(Option.of(new
String[]{"id"}));
// Create reader context.
props = new TypedProperties();
diff --git
a/hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/functional/TestMetaFieldsModeE2E.java
b/hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/functional/TestMetaFieldsModeE2E.java
index 76cfbe687ed2..cb5f185b784c 100644
---
a/hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/functional/TestMetaFieldsModeE2E.java
+++
b/hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/functional/TestMetaFieldsModeE2E.java
@@ -30,6 +30,7 @@ import org.apache.hudi.common.table.HoodieTableMetaClient;
import org.apache.hudi.common.table.HoodieTableVersion;
import org.apache.hudi.common.table.timeline.HoodieInstant;
import org.apache.hudi.config.HoodieWriteConfig;
+import org.apache.hudi.io.FileGroupReaderBasedMergeHandle;
import org.apache.hudi.testutils.SparkClientFunctionalTestHarness;
import org.apache.spark.api.java.function.VoidFunction2;
@@ -157,6 +158,39 @@ class TestMetaFieldsModeE2E extends
SparkClientFunctionalTestHarness {
}
}
+ @ParameterizedTest
+ @EnumSource(value = MetaFieldsMode.class, names = "NONE", mode =
EnumSource.Mode.EXCLUDE)
+ void concatPreservesRecordsWithTheSparkRecordType(MetaFieldsMode mode) {
+ Map<String, String> options = baseOptions();
+ options.put(HoodieTableConfig.META_FIELDS_MODE.key(), mode.name());
+ options.put(DataSourceWriteOptions.OPERATION().key(),
DataSourceWriteOptions.INSERT_OPERATION_OPT_VAL());
+ options.put("hoodie.write.record.merge.custom.implementation.classes",
"org.apache.hudi.DefaultSparkRecordMerger");
+
options.put(HoodieWriteConfig.MERGE_ALLOW_DUPLICATE_ON_INSERTS_ENABLE.key(),
"true");
+ options.put("hoodie.parquet.small.file.limit", "10485760");
+
+ writeSampleAndGetTableConfig(options, basePath());
+ // Route a duplicate insert into the existing small file, exercising
HoodieConcatHandle.
+ writeRows(Collections.singletonList(RowFactory.create("k1", "p1", "v3")),
+ simpleSchema(), options, basePath(), SaveMode.Append);
+
+ Dataset<Row> latest = spark().read().format("hudi").load(basePath())
+ .withColumn("actual_file", functions.input_file_name());
+ assertEquals(1, latest.select("actual_file").distinct().count(), "the
inserts must share one file group");
+ assertEquals(Arrays.asList("v1", "v2", "v3"),
latest.select("column3").collectAsList().stream()
+ .map(row -> row.getString(0)).sorted().collect(Collectors.toList()),
+ "concat must preserve both existing records and the duplicate insert");
+ assertMetaColumn(latest, 3, HoodieRecord.COMMIT_TIME_METADATA_FIELD,
mode.isCommitTimePopulated(), mode);
+ assertMetaColumn(latest, 3, HoodieRecord.COMMIT_SEQNO_METADATA_FIELD, mode
== MetaFieldsMode.ALL, mode);
+ assertMetaColumn(latest, 3, HoodieRecord.RECORD_KEY_METADATA_FIELD,
mode.isRecordKeyPopulated(), mode);
+ assertMetaColumn(latest, 3, HoodieRecord.PARTITION_PATH_METADATA_FIELD,
mode == MetaFieldsMode.ALL, mode);
+ assertMetaColumn(latest, 3, HoodieRecord.FILENAME_METADATA_FIELD,
mode.isFileNamePopulated(), mode);
+ if (mode.isFileNamePopulated()) {
+ for (Row row : latest.collectAsList()) {
+ assertTrue(row.<String>getAs("actual_file").endsWith("/" +
row.getAs(HoodieRecord.FILENAME_METADATA_FIELD)));
+ }
+ }
+ }
+
@Test
void allModePersistsAndPopulatesAllColumns() {
Map<String, String> options = baseOptions();
@@ -676,6 +710,40 @@ class TestMetaFieldsModeE2E extends
SparkClientFunctionalTestHarness {
// the one write path that stamps a meta column outside the mode-aware
writers.
//
---------------------------------------------------------------------------------------------
+ @ParameterizedTest
+ @EnumSource(value = MetaFieldsMode.class, names = "ALL", mode =
EnumSource.Mode.EXCLUDE)
+ void upsertWithSingleRecordKeyAndMultiplePartitionFields(MetaFieldsMode
mode) {
+ Map<String, String> options = baseOptions();
+ options.put(HoodieTableConfig.META_FIELDS_MODE.key(), mode.name());
+ options.put(DataSourceWriteOptions.PARTITIONPATH_FIELD().key(),
"column2,column4");
+ options.put(DataSourceWriteOptions.OPERATION().key(),
DataSourceWriteOptions.UPSERT_OPERATION_OPT_VAL());
+ options.put("hoodie.write.record.merge.custom.implementation.classes",
"org.apache.hudi.DefaultSparkRecordMerger");
+ options.put(HoodieWriteConfig.MERGE_HANDLE_CLASS_NAME.key(),
FileGroupReaderBasedMergeHandle.class.getName());
+ options.put(HoodieWriteConfig.COMPLEX_KEYGEN_NEW_ENCODING.key(), "false");
+ StructType schema = simpleSchema().add("column4", DataTypes.StringType,
true);
+
+ writeRows(Arrays.asList(
+ RowFactory.create("k1", "p1", "v1", "a"),
+ RowFactory.create("k2", "p1", "v2", "a"),
+ RowFactory.create("k3", "p1", "v3", "b")),
+ schema, options, basePath(), SaveMode.Overwrite);
+ writeRows(Collections.singletonList(RowFactory.create("k1", "p1", "v4",
"a")),
+ schema, options, basePath(), SaveMode.Append);
+
+ Dataset<Row> latest = spark().read().format("hudi").load(basePath());
+ assertEquals(Arrays.asList(
+ RowFactory.create("k1", "p1", "v4", "a"),
+ RowFactory.create("k2", "p1", "v2", "a"),
+ RowFactory.create("k3", "p1", "v3", "b")),
+ latest.select("column1", "column2", "column3",
"column4").orderBy("column1").collectAsList(),
+ "upsert must replace the existing key exactly once and preserve
untouched records in both partitions");
+ if (mode == MetaFieldsMode.NONE) {
+
assertFalse(Arrays.asList(latest.columns()).contains(HoodieRecord.RECORD_KEY_METADATA_FIELD));
+ } else {
+ assertMetaColumn(latest, 3, HoodieRecord.RECORD_KEY_METADATA_FIELD,
false, mode);
+ }
+ }
+
/**
* An upsert rewrites the whole file group: the updated record goes through
the normal write path,
* while untouched records are copied forward through
diff --git
a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/hudi/common/table/read/TestHoodieFileGroupReaderOnSpark.scala
b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/hudi/common/table/read/TestHoodieFileGroupReaderOnSpark.scala
index db2488c7c8e9..550c4c456531 100644
---
a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/hudi/common/table/read/TestHoodieFileGroupReaderOnSpark.scala
+++
b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/hudi/common/table/read/TestHoodieFileGroupReaderOnSpark.scala
@@ -202,7 +202,7 @@ class TestHoodieFileGroupReaderOnSpark extends
TestHoodieFileGroupReaderBase[Int
def testGetOrderingValue(): Unit = {
val reader = Mockito.mock(classOf[SparkColumnarFileReader])
val tableConfig = Mockito.mock(classOf[HoodieTableConfig])
- Mockito.when(tableConfig.populateMetaFields()).thenReturn(true)
+ Mockito.when(tableConfig.isRecordKeyPopulated()).thenReturn(true)
val sparkReaderContext = new
SparkFileFormatInternalRowReaderContext(reader, Seq.empty, Seq.empty,
getStorageConf, tableConfig)
val orderingFieldName = "col2"
val avroSchema = new Schema.Parser().parse(
@@ -346,7 +346,7 @@ class TestHoodieFileGroupReaderOnSpark extends
TestHoodieFileGroupReaderBase[Int
val reader = Mockito.mock(classOf[SparkColumnarFileReader])
val tableConfig = Mockito.mock(classOf[HoodieTableConfig])
val storageConf = Mockito.mock(classOf[StorageConfiguration[_]])
- when(tableConfig.populateMetaFields()).thenReturn(true)
+ when(tableConfig.isRecordKeyPopulated()).thenReturn(true)
val sparkReaderContext = new
SparkFileFormatInternalRowReaderContext(reader, Seq.empty, Seq.empty,
storageConf, tableConfig)
val schema = SchemaBuilder.builder()
.record("test")
@@ -363,7 +363,8 @@ class TestHoodieFileGroupReaderOnSpark extends
TestHoodieFileGroupReaderBase[Int
def getRecordKeySingleKey(): Unit = {
val reader = Mockito.mock(classOf[SparkColumnarFileReader])
val tableConfig = Mockito.mock(classOf[HoodieTableConfig])
- when(tableConfig.populateMetaFields()).thenReturn(false)
+ when(tableConfig.isRecordKeyPopulated()).thenReturn(false)
+
when(tableConfig.getPartitionFields()).thenReturn(HOption.empty[Array[String]]())
when(tableConfig.getRecordKeyFields).thenReturn(HOption.of(Array("field1")))
val storageConf = Mockito.mock(classOf[StorageConfiguration[_]])
val props = new TypedProperties
@@ -385,7 +386,8 @@ class TestHoodieFileGroupReaderOnSpark extends
TestHoodieFileGroupReaderBase[Int
def getRecordKeyWithMultipleKeys(): Unit = {
val reader = Mockito.mock(classOf[SparkColumnarFileReader])
val tableConfig = Mockito.mock(classOf[HoodieTableConfig])
- when(tableConfig.populateMetaFields()).thenReturn(false)
+ when(tableConfig.isRecordKeyPopulated()).thenReturn(false)
+
when(tableConfig.getPartitionFields()).thenReturn(HOption.empty[Array[String]]())
when(tableConfig.getRecordKeyFields).thenReturn(HOption.of(Array("outer1.field1",
"outer1.field2", "outer1.field3")))
val storageConf = Mockito.mock(classOf[StorageConfiguration[_]])
val sparkReaderContext = new
SparkFileFormatInternalRowReaderContext(reader, Seq.empty, Seq.empty,
storageConf, tableConfig)
@@ -400,7 +402,7 @@ class TestHoodieFileGroupReaderOnSpark extends
TestHoodieFileGroupReaderBase[Int
def getNestedValue(): Unit = {
val reader = Mockito.mock(classOf[SparkColumnarFileReader])
val tableConfig = Mockito.mock(classOf[HoodieTableConfig])
- when(tableConfig.populateMetaFields()).thenReturn(true)
+ when(tableConfig.isRecordKeyPopulated()).thenReturn(true)
val storageConf = Mockito.mock(classOf[StorageConfiguration[_]])
val sparkReaderContext = new
SparkFileFormatInternalRowReaderContext(reader, Seq.empty, Seq.empty,
storageConf, tableConfig)
val schema: Schema = buildMultiLevelSchema