From ef978c4dafea37e5097ed0b7123d4939b5cd18ae Mon Sep 17 00:00:00 2001 From: Y Ethan Guo Date: Thu, 18 Sep 2025 08:04:29 -0700 Subject: [PATCH 01/35] [HUDI-9666] Fix the record key encoding with a single record key field and add a guard for complex key generator (#13650) Co-authored-by: danny0405 Co-authored-by: Rahil Chertara Co-authored-by: Rahil C <32500120+rahil-c@users.noreply.github.com> --- .../hudi/client/BaseHoodieWriteClient.java | 45 ++- .../apache/hudi/config/HoodieWriteConfig.java | 40 +++ .../hudi/keygen/ComplexAvroKeyGenerator.java | 18 +- .../org/apache/hudi/keygen/KeyGenUtils.java | 31 ++ .../upgrade/EightToNineUpgradeHandler.java | 0 .../upgrade/EightToSevenDowngradeHandler.java | 0 .../upgrade/NineToEightDowngradeHandler.java | 0 .../upgrade/SevenToEightUpgradeHandler.java | 0 .../client/TestBaseHoodieWriteClient.java | 292 ++++++++++++++++++ .../keygen/TestComplexAvroKeyGenerator.java | 49 ++- .../apache/hudi/keygen/TestKeyGenUtils.java | 116 +++++++ .../org/apache/hudi/testutils/Assertions.java | 9 + .../hudi/keygen/BuiltinKeyGenerator.java | 40 ++- .../hudi/keygen/ComplexKeyGenerator.java | 8 +- .../hudi/keygen/GlobalDeleteKeyGenerator.java | 4 +- .../common/TestSparkReaderContextFactory.java | 0 .../keygen/KeyGeneratorTestUtilities.java | 200 ++++++++++++ .../hudi/keygen/TestComplexKeyGenerator.java | 223 +++++++++++++ .../hudi/keygen/TestCustomKeyGenerator.java | 171 +++++----- .../TestGlobalDeleteRecordGenerator.java | 69 +++-- .../TestNonpartitionedKeyGenerator.java | 86 +++--- .../hudi/keygen/TestSimpleKeyGenerator.java | 59 ++-- .../TestTimestampBasedKeyGenerator.java | 116 +++---- ...stCreateKeyGeneratorByTypeWithFactory.java | 24 +- .../TestHoodieSparkKeyGeneratorFactory.java | 17 +- .../hudi/testutils/SparkDatasetTestUtils.java | 2 +- .../hudi/common/config/ConfigProperty.java | 40 ++- .../hudi/common/engine/RecordContext.java | 0 .../common/table/HoodieTableMetaClient.java | 34 -- .../keygen/constant/KeyGeneratorType.java | 17 + .../common/config/TestConfigProperty.java | 12 + .../apache/hudi/keygen/TestKeyGenerator.java | 0 .../hudi/configuration/OptionsResolver.java | 8 + .../hudi/sink/bulk/AutoRowDataKeyGen.java | 10 +- .../apache/hudi/sink/bulk/RowDataKeyGen.java | 29 +- .../hudi/sink/bulk/TestRowDataKeyGen.java | 34 +- .../hudi/table/ITTestSchemaEvolution.java | 8 +- .../DataSourceInternalWriterHelper.java | 2 +- .../apache/hudi/RecordLevelIndexSupport.scala | 3 +- .../apache/hudi/SparkBaseIndexSupport.scala | 0 .../table/upgrade/TestUpgradeDowngrade.java | 0 .../upgrade-downgrade-fixtures/README.md | 0 .../generate-fixtures.sh | 0 .../hudi-v6-table-complex-keygen.zip | Bin 0 -> 67342 bytes .../hudi-v8-table-complex-keygen.zip | Bin 0 -> 136082 bytes .../hudi-v9-table-complex-keygen.zip | Bin 0 -> 137899 bytes .../generate-fixture-complex-keygen-v9.scala | 107 +++++++ .../generate-fixture-complex-keygen.scala | 106 +++++++ .../scala-templates/generate-fixture-v9.scala | 107 +++++++ .../scala-templates/generate-fixture.scala | 0 .../apache/hudi/TestDataSourceDefaults.scala | 130 ++++++-- .../hudi/functional/TestCOWDataSource.scala | 10 +- .../sql/hudi/HoodieSparkSqlTestBase.scala | 20 ++ .../spark/sql/hudi/TestCreateTable.scala | 240 ++++++++++++-- 54 files changed, 2128 insertions(+), 408 deletions(-) create mode 100644 hudi-client/hudi-client-common/src/main/java/org/apache/hudi/table/upgrade/EightToNineUpgradeHandler.java create mode 100644 hudi-client/hudi-client-common/src/main/java/org/apache/hudi/table/upgrade/EightToSevenDowngradeHandler.java create mode 100644 hudi-client/hudi-client-common/src/main/java/org/apache/hudi/table/upgrade/NineToEightDowngradeHandler.java create mode 100644 hudi-client/hudi-client-common/src/main/java/org/apache/hudi/table/upgrade/SevenToEightUpgradeHandler.java create mode 100644 hudi-client/hudi-client-common/src/test/java/org/apache/hudi/client/TestBaseHoodieWriteClient.java create mode 100644 hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/client/common/TestSparkReaderContextFactory.java create mode 100644 hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/keygen/KeyGeneratorTestUtilities.java create mode 100644 hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/keygen/TestComplexKeyGenerator.java rename {hudi-spark-datasource/hudi-spark => hudi-client/hudi-spark-client}/src/test/java/org/apache/hudi/keygen/TestCustomKeyGenerator.java (63%) rename {hudi-spark-datasource/hudi-spark => hudi-client/hudi-spark-client}/src/test/java/org/apache/hudi/keygen/TestGlobalDeleteRecordGenerator.java (60%) rename {hudi-spark-datasource/hudi-spark => hudi-client/hudi-spark-client}/src/test/java/org/apache/hudi/keygen/TestNonpartitionedKeyGenerator.java (60%) rename {hudi-spark-datasource/hudi-spark => hudi-client/hudi-spark-client}/src/test/java/org/apache/hudi/keygen/TestSimpleKeyGenerator.java (79%) rename {hudi-spark-datasource/hudi-spark => hudi-client/hudi-spark-client}/src/test/java/org/apache/hudi/keygen/TestTimestampBasedKeyGenerator.java (79%) rename {hudi-spark-datasource/hudi-spark => hudi-client/hudi-spark-client}/src/test/java/org/apache/hudi/keygen/factory/TestCreateKeyGeneratorByTypeWithFactory.java (87%) rename {hudi-spark-datasource/hudi-spark => hudi-client/hudi-spark-client}/src/test/java/org/apache/hudi/keygen/factory/TestHoodieSparkKeyGeneratorFactory.java (91%) create mode 100644 hudi-common/src/main/java/org/apache/hudi/common/engine/RecordContext.java create mode 100644 hudi-common/src/test/java/org/apache/hudi/keygen/TestKeyGenerator.java create mode 100644 hudi-spark-datasource/hudi-spark-common/src/main/scala/org/apache/hudi/SparkBaseIndexSupport.scala create mode 100644 hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/table/upgrade/TestUpgradeDowngrade.java create mode 100644 hudi-spark-datasource/hudi-spark/src/test/resources/upgrade-downgrade-fixtures/README.md create mode 100755 hudi-spark-datasource/hudi-spark/src/test/resources/upgrade-downgrade-fixtures/generate-fixtures.sh create mode 100644 hudi-spark-datasource/hudi-spark/src/test/resources/upgrade-downgrade-fixtures/mor-tables/hudi-v6-table-complex-keygen.zip create mode 100644 hudi-spark-datasource/hudi-spark/src/test/resources/upgrade-downgrade-fixtures/mor-tables/hudi-v8-table-complex-keygen.zip create mode 100644 hudi-spark-datasource/hudi-spark/src/test/resources/upgrade-downgrade-fixtures/mor-tables/hudi-v9-table-complex-keygen.zip create mode 100644 hudi-spark-datasource/hudi-spark/src/test/resources/upgrade-downgrade-fixtures/scala-templates/generate-fixture-complex-keygen-v9.scala create mode 100644 hudi-spark-datasource/hudi-spark/src/test/resources/upgrade-downgrade-fixtures/scala-templates/generate-fixture-complex-keygen.scala create mode 100644 hudi-spark-datasource/hudi-spark/src/test/resources/upgrade-downgrade-fixtures/scala-templates/generate-fixture-v9.scala create mode 100644 hudi-spark-datasource/hudi-spark/src/test/resources/upgrade-downgrade-fixtures/scala-templates/generate-fixture.scala diff --git a/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/client/BaseHoodieWriteClient.java b/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/client/BaseHoodieWriteClient.java index bfb811a159904..1b3edb9105f12 100644 --- a/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/client/BaseHoodieWriteClient.java +++ b/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/client/BaseHoodieWriteClient.java @@ -40,9 +40,11 @@ import org.apache.hudi.common.model.HoodieFailedWritesCleaningPolicy; import org.apache.hudi.common.model.HoodieKey; import org.apache.hudi.common.model.HoodieRecordLocation; +import org.apache.hudi.common.model.HoodieTableType; import org.apache.hudi.common.model.HoodieWriteStat; import org.apache.hudi.common.model.TableServiceType; import org.apache.hudi.common.model.WriteOperationType; +import org.apache.hudi.common.table.HoodieTableConfig; import org.apache.hudi.common.table.HoodieTableMetaClient; import org.apache.hudi.common.table.HoodieTableVersion; import org.apache.hudi.common.table.TableSchemaResolver; @@ -101,6 +103,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Properties; import java.util.Set; import java.util.function.BiConsumer; import java.util.stream.Collectors; @@ -108,6 +111,8 @@ import static org.apache.hudi.avro.AvroSchemaUtils.getAvroRecordQualifiedName; import static org.apache.hudi.common.model.HoodieCommitMetadata.SCHEMA_KEY; import static org.apache.hudi.common.util.StringUtils.getUTF8Bytes; +import static org.apache.hudi.keygen.KeyGenUtils.getComplexKeygenErrorMessage; +import static org.apache.hudi.keygen.KeyGenUtils.isComplexKeyGeneratorWithSingleRecordKeyField; import static org.apache.hudi.metadata.HoodieTableMetadata.getMetadataTableBasePath; /** @@ -1323,7 +1328,7 @@ public final HoodieTable initTable(WriteOperationType operationType, Option COMPLEX_KEYGEN_NEW_ENCODING = ConfigProperty + .key("hoodie.write.complex.keygen.new.encoding") + .defaultValue(false) + .markAdvanced() + .sinceVersion("1.1.0") + .supportedVersions("0.14.2", "0.15.1", "1.0.3") + .withDocumentation("This config only takes effect for writing table version 8 and below. " + + "If set to false, the record key field name is encoded and prepended " + + "in the case where a single record key field is used in the complex key generator, " + + "i.e., record keys stored in _hoodie_record_key meta field is in the format of " + + "`:`, which conforms to the behavior " + + "in 0.14.0 release and older. If set to true, the record key field name is not " + + "encoded under the same case in the complex key generator, i.e., record keys stored " + + "in _hoodie_record_key meta field is in the format of ``, " + + "which conforms to the behavior in 0.14.1, 0.15.0, 1.0.0, 1.0.1, 1.0.2 releases."); + + public static final ConfigProperty ENABLE_COMPLEX_KEYGEN_VALIDATION = ConfigProperty + .key("hoodie.write.complex.keygen.validation.enable") + .defaultValue(true) + .markAdvanced() + .sinceVersion("1.1.0") + .supportedVersions("0.14.2", "0.15.1", "1.0.3") + .withDocumentation("This config only takes effect for writing table version 8 and below, " + + "upgrade or downgrade. If set to true, the writer enables the validation on whether the " + + "table uses the complex key generator with a single record key field, which can be affected " + + "by a breaking change in 0.14.1, 0.15.0, 1.0.0, 1.0.1, 1.0.2 releases, causing key " + + "encoding change and potential duplicates in the table. The validation fails the " + + "pipeline if the table meets the condition for the user to take proper action. " + + "The user can turn this validation off by setting the config to false, after " + + "evaluating the table and situation and doing table repair if needed."); + public static final ConfigProperty ROLLBACK_USING_MARKERS_ENABLE = ConfigProperty .key("hoodie.rollback.using.markers") .defaultValue("true") @@ -1317,6 +1348,10 @@ public boolean shouldRollbackUsingMarkers() { return getBoolean(ROLLBACK_USING_MARKERS_ENABLE); } + public boolean enableComplexKeygenValidation() { + return getBoolean(ENABLE_COMPLEX_KEYGEN_VALIDATION); + } + public int getWriteBufferLimitBytes() { return Integer.parseInt(getStringOrDefault(WRITE_BUFFER_LIMIT_BYTES_VALUE)); } @@ -2745,6 +2780,11 @@ public Builder withRollbackUsingMarkers(boolean rollbackUsingMarkers) { return this; } + public Builder withComplexKeygenValidation(boolean enableComplexKeygenValidation) { + writeConfig.setValue(ENABLE_COMPLEX_KEYGEN_VALIDATION, String.valueOf(enableComplexKeygenValidation)); + return this; + } + public Builder withWriteBufferLimitBytes(int writeBufferLimit) { writeConfig.setValue(WRITE_BUFFER_LIMIT_BYTES_VALUE, String.valueOf(writeBufferLimit)); return this; diff --git a/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/keygen/ComplexAvroKeyGenerator.java b/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/keygen/ComplexAvroKeyGenerator.java index 743aef1174a73..12cfac1b83210 100644 --- a/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/keygen/ComplexAvroKeyGenerator.java +++ b/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/keygen/ComplexAvroKeyGenerator.java @@ -19,6 +19,7 @@ import org.apache.avro.generic.GenericRecord; import org.apache.hudi.common.config.TypedProperties; +import org.apache.hudi.common.function.SerializableFunctionUnchecked; import org.apache.hudi.keygen.constant.KeyGeneratorOptions; import java.util.Arrays; @@ -29,6 +30,7 @@ */ public class ComplexAvroKeyGenerator extends BaseKeyGenerator { public static final String DEFAULT_RECORD_KEY_SEPARATOR = ":"; + private final SerializableFunctionUnchecked recordKeyFunction; public ComplexAvroKeyGenerator(TypedProperties props) { super(props); @@ -37,18 +39,26 @@ public ComplexAvroKeyGenerator(TypedProperties props) { .map(String::trim) .filter(s -> !s.isEmpty()) .collect(Collectors.toList()); + this.recordKeyFunction = getRecordKeyFunc(KeyGenUtils.encodeSingleKeyFieldNameForComplexKeyGen(props)); } @Override public String getRecordKey(GenericRecord record) { - if (getRecordKeyFieldNames().size() == 1) { - return KeyGenUtils.getRecordKey(record, getRecordKeyFieldNames().get(0), isConsistentLogicalTimestampEnabled()); - } - return KeyGenUtils.getRecordKey(record, getRecordKeyFieldNames(), isConsistentLogicalTimestampEnabled()); + return recordKeyFunction.apply(record); } @Override public String getPartitionPath(GenericRecord record) { return KeyGenUtils.getRecordPartitionPath(record, getPartitionPathFields(), hiveStylePartitioning, encodePartitionPath, isConsistentLogicalTimestampEnabled()); } + + private SerializableFunctionUnchecked getRecordKeyFunc(boolean encodeSingleKeyFieldName) { + if (getRecordKeyFieldNames().size() == 1) { + if (encodeSingleKeyFieldName) { + return record -> KeyGenUtils.getRecordKey(record, getRecordKeyFieldNames(), isConsistentLogicalTimestampEnabled()); + } + return record -> KeyGenUtils.getRecordKey(record, getRecordKeyFieldNames().get(0), isConsistentLogicalTimestampEnabled()); + } + return record -> KeyGenUtils.getRecordKey(record, getRecordKeyFieldNames(), isConsistentLogicalTimestampEnabled()); + } } diff --git a/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/keygen/KeyGenUtils.java b/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/keygen/KeyGenUtils.java index 7b88a0ab979b4..a9530a6ff9cf1 100644 --- a/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/keygen/KeyGenUtils.java +++ b/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/keygen/KeyGenUtils.java @@ -21,6 +21,9 @@ import org.apache.hudi.avro.HoodieAvroUtils; import org.apache.hudi.common.config.TypedProperties; import org.apache.hudi.common.model.HoodieRecord; +import org.apache.hudi.common.table.HoodieTableConfig; +import org.apache.hudi.common.table.HoodieTableVersion; +import org.apache.hudi.common.util.ConfigUtils; import org.apache.hudi.common.util.Option; import org.apache.hudi.common.util.PartitionPathEncodeUtils; import org.apache.hudi.common.util.ReflectionUtils; @@ -39,6 +42,8 @@ import java.util.List; import java.util.stream.Collectors; +import static org.apache.hudi.config.HoodieWriteConfig.COMPLEX_KEYGEN_NEW_ENCODING; + public class KeyGenUtils { protected static final String NULL_RECORDKEY_PLACEHOLDER = "__null__"; @@ -260,4 +265,30 @@ public static List getRecordKeyFields(TypedProperties props) { public static boolean enableAutoGenerateRecordKeys(TypedProperties props) { return !props.containsKey(KeyGeneratorOptions.RECORDKEY_FIELD_NAME.key()); } + + public static boolean isComplexKeyGeneratorWithSingleRecordKeyField(HoodieTableConfig tableConfig) { + Option recordKeyFields = tableConfig.getRecordKeyFields(); + return KeyGeneratorType.isComplexKeyGenerator(tableConfig) + && recordKeyFields.isPresent() && recordKeyFields.get().length == 1; + } + + public static String getComplexKeygenErrorMessage(String operation) { + return "This table uses the complex key generator with a single record " + + "key field. If the table is written with Hudi 0.14.1, 0.15.0, 1.0.0, 1.0.1, or 1.0.2 " + + "release before, the table may potentially contain duplicates due to a breaking " + + "change in the key encoding in the _hoodie_record_key meta field (HUDI-7001) which " + + "is crucial for upserts. Please take action based on the details on the deployment " + + "guide (https://hudi.apache.org/docs/deployment#complex-key-generator) " + + "before resuming the " + operation + " to the this table. If you're certain " + + "that the table is not affected by the key encoding change, set " + + "`hoodie.write.complex.keygen.validation.enable=false` to skip this validation."; + } + + public static boolean encodeSingleKeyFieldNameForComplexKeyGen(TypedProperties props) { + return !ConfigUtils.getBooleanWithAltKeys(props, COMPLEX_KEYGEN_NEW_ENCODING); + } + + public static boolean mayUseNewEncodingForComplexKeyGen(HoodieTableConfig tableConfig) { + return isComplexKeyGeneratorWithSingleRecordKeyField(tableConfig); + } } diff --git a/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/table/upgrade/EightToNineUpgradeHandler.java b/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/table/upgrade/EightToNineUpgradeHandler.java new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/table/upgrade/EightToSevenDowngradeHandler.java b/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/table/upgrade/EightToSevenDowngradeHandler.java new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/table/upgrade/NineToEightDowngradeHandler.java b/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/table/upgrade/NineToEightDowngradeHandler.java new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/table/upgrade/SevenToEightUpgradeHandler.java b/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/table/upgrade/SevenToEightUpgradeHandler.java new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/client/TestBaseHoodieWriteClient.java b/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/client/TestBaseHoodieWriteClient.java new file mode 100644 index 0000000000000..8e2aaa71b3cde --- /dev/null +++ b/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/client/TestBaseHoodieWriteClient.java @@ -0,0 +1,292 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hudi.client; + +import org.apache.hudi.client.embedded.EmbeddedTimelineService; +import org.apache.hudi.common.engine.HoodieLocalEngineContext; +import org.apache.hudi.common.model.HoodieCommitMetadata; +import org.apache.hudi.common.table.HoodieTableConfig; +import org.apache.hudi.common.table.HoodieTableMetaClient; +import org.apache.hudi.common.table.timeline.HoodieTimeline; +import org.apache.hudi.common.table.view.FileSystemViewStorageType; +import org.apache.hudi.common.testutils.HoodieCommonTestHarness; +import org.apache.hudi.common.testutils.HoodieTestUtils; +import org.apache.hudi.common.util.Option; +import org.apache.hudi.common.model.WriteOperationType; +import org.apache.hudi.config.HoodieWriteConfig; +import org.apache.hudi.index.HoodieIndex; +import org.apache.hudi.index.simple.HoodieSimpleIndex; +import org.apache.hudi.keygen.ComplexAvroKeyGenerator; +import org.apache.hudi.keygen.constant.KeyGeneratorOptions; +import org.apache.hudi.table.BulkInsertPartitioner; +import org.apache.hudi.table.HoodieTable; + +import org.apache.hadoop.conf.Configuration; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.function.BiConsumer; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.apache.hudi.common.testutils.HoodieTestUtils.getDefaultStorageConf; +import static org.apache.hudi.testutils.Assertions.assertComplexKeyGeneratorValidationThrows; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +class TestBaseHoodieWriteClient extends HoodieCommonTestHarness { + + private static Stream testWithComplexKeyGeneratorValidation() { + List arguments = new ArrayList<>(); + + List keyAndPartitionFieldOptions = Arrays.asList( + Arguments.of("r1", "p1"), + Arguments.of("r1", "p1,p2"), + Arguments.of("r1", ""), + Arguments.of("r1,r2", "p1") + ); + + List booleanOptions = Arrays.asList( + Arguments.of(false, true), + Arguments.of(true, true), + Arguments.of(true, false) + ); + + List tableVersionOptions = Arrays.asList(8); + + arguments.addAll(Stream.of("org.apache.hudi.keygen.ComplexAvroKeyGenerator", + "org.apache.hudi.keygen.ComplexKeyGenerator") + .flatMap(keyGenClass -> keyAndPartitionFieldOptions.stream() + .flatMap(keyAndPartitionField -> booleanOptions.stream() + .flatMap(booleans -> tableVersionOptions.stream() + .map(tableVersion -> Arguments.of( + keyGenClass, + keyAndPartitionField.get()[0], + keyAndPartitionField.get()[1], + booleans.get()[0], + booleans.get()[1], + tableVersion + )) + ) + )) + .collect(Collectors.toList())); + arguments.addAll(Stream.of("org.apache.hudi.keygen.SimpleAvroKeyGenerator", + "org.apache.hudi.keygen.SimpleKeyGenerator", + "org.apache.hudi.keygen.TimestampBasedAvroKeyGenerator", + "org.apache.hudi.keygen.TimestampBasedKeyGenerator") + .flatMap(keyGenClass -> booleanOptions.stream() + .flatMap(booleans -> tableVersionOptions.stream() + .map(tableVersion -> Arguments.of( + keyGenClass, + "r1", + "p1", + booleans.get()[0], + booleans.get()[1], + tableVersion + )) + ) + ) + .collect(Collectors.toList())); + arguments.addAll(Stream.of("org.apache.hudi.keygen.NonpartitionedAvroKeyGenerator", + "org.apache.hudi.keygen.NonpartitionedKeyGenerator") + .flatMap(keyGenClass -> booleanOptions.stream() + .flatMap(booleans -> tableVersionOptions.stream() + .map(tableVersion -> Arguments.of( + keyGenClass, + "r1", + "", + booleans.get()[0], + booleans.get()[1], + tableVersion + )) + ) + ) + .collect(Collectors.toList())); + arguments.addAll(Stream.of("org.apache.hudi.keygen.CustomAvroKeyGenerator", + "org.apache.hudi.keygen.CustomKeyGenerator") + .flatMap(keyGenClass -> booleanOptions.stream() + .flatMap(booleans -> tableVersionOptions.stream() + .map(tableVersion -> Arguments.of( + keyGenClass, + "r1", + "p1:SIMPLE", + booleans.get()[0], + booleans.get()[1], + tableVersion + )) + ) + ) + .collect(Collectors.toList())); + + return arguments.stream(); + } + + @ParameterizedTest + @MethodSource + void testWithComplexKeyGeneratorValidation(String keyGeneratorClass, + String recordKeyFields, + String partitionPathFields, + boolean setComplexKeyGeneratorValidationConfig, + boolean enableComplexKeyGeneratorValidation, + int tableVersion) throws IOException { + if (basePath == null) { + initPath(); + } + Properties tableProperties = new Properties(); + tableProperties.put(HoodieTableConfig.KEY_GENERATOR_CLASS_NAME.key(), keyGeneratorClass); + tableProperties.put(HoodieTableConfig.RECORDKEY_FIELDS.key(), recordKeyFields); + tableProperties.put(HoodieTableConfig.PARTITION_FIELDS.key(), partitionPathFields); + tableProperties.put(HoodieTableConfig.VERSION.key(), String.valueOf(tableVersion)); + Properties writeProperties = new Properties(); + writeProperties.put(HoodieWriteConfig.KEYGENERATOR_CLASS_NAME.key(), keyGeneratorClass); + writeProperties.put(KeyGeneratorOptions.RECORDKEY_FIELD_NAME.key(), recordKeyFields); + writeProperties.put(KeyGeneratorOptions.PARTITIONPATH_FIELD_NAME.key(), partitionPathFields); + if (setComplexKeyGeneratorValidationConfig) { + writeProperties.put( + HoodieWriteConfig.ENABLE_COMPLEX_KEYGEN_VALIDATION.key(), enableComplexKeyGeneratorValidation); + } + metaClient = HoodieTestUtils.init( + HoodieTestUtils.getDefaultStorageConf(), basePath, getTableType(), tableProperties); + HoodieWriteConfig.Builder writeConfigBuilder = HoodieWriteConfig.newBuilder() + .withPath(basePath) + .withProperties(writeProperties); + HoodieTable table = mock(HoodieTable.class); + BaseHoodieTableServiceClient tableServiceClient = mock(BaseHoodieTableServiceClient.class); + TestWriteClient writeClient = new TestWriteClient(writeConfigBuilder.build(), table, Option.empty(), tableServiceClient); + + if (tableVersion <= 8 && enableComplexKeyGeneratorValidation + && (ComplexAvroKeyGenerator.class.getCanonicalName().equals(keyGeneratorClass) + || "org.apache.hudi.keygen.ComplexKeyGenerator".equals(keyGeneratorClass)) + && recordKeyFields.split(",").length == 1) { + assertComplexKeyGeneratorValidationThrows(() -> writeClient.initTable(WriteOperationType.INSERT, Option.empty()), "ingestion"); + } else { + writeClient.initTable(WriteOperationType.INSERT, Option.empty()); + String requestedTime = writeClient.startCommit(); + + HoodieTimeline writeTimeline = metaClient.getActiveTimeline().getWriteTimeline(); + assertTrue(writeTimeline.lastInstant().isPresent()); + assertEquals("commit", writeTimeline.lastInstant().get().getAction()); + assertEquals(requestedTime, writeTimeline.lastInstant().get().getTimestamp()); + } + } + + private static class TestWriteClient extends BaseHoodieWriteClient { + private final HoodieTable table; + + public TestWriteClient(HoodieWriteConfig writeConfig, HoodieTable table, Option timelineService, + BaseHoodieTableServiceClient tableServiceClient) { + super(new HoodieLocalEngineContext(getDefaultStorageConf()), writeConfig, timelineService, null); + this.table = table; + this.tableServiceClient = tableServiceClient; + } + + @Override + protected HoodieIndex createIndex(HoodieWriteConfig writeConfig) { + return new HoodieSimpleIndex(config, Option.empty()); + } + + @Override + public boolean commit(String instantTime, String writeStatuses, Option> extraMetadata, String commitActionType, Map> partitionToReplacedFileIds, + Option> extraPreCommitFunc) { + return false; + } + + @Override + protected HoodieTable createTable(HoodieWriteConfig config, Configuration hadoopConf) { + // table should only be made with remote view config for these tests + FileSystemViewStorageType storageType = config.getViewStorageConfig().getStorageType(); + Assertions.assertTrue(storageType == FileSystemViewStorageType.REMOTE_FIRST || storageType == FileSystemViewStorageType.REMOTE_ONLY); + return table; + } + + @Override + protected HoodieTable createTable(HoodieWriteConfig config, Configuration hadoopConf, HoodieTableMetaClient metaClient) { + // table should only be made with remote view config for these tests + FileSystemViewStorageType storageType = config.getViewStorageConfig().getStorageType(); + Assertions.assertTrue(storageType == FileSystemViewStorageType.REMOTE_FIRST || storageType == FileSystemViewStorageType.REMOTE_ONLY); + // Ensure the returned table has the correct metaClient + when(table.getMetaClient()).thenReturn(metaClient); + return table; + } + + @Override + protected void validateTimestamp(HoodieTableMetaClient metaClient, String instantTime) { + } + + @Override + public String filterExists(String hoodieRecords) { + return ""; + } + + @Override + public String upsert(String records, String instantTime) { + return ""; + } + + @Override + public String upsertPreppedRecords(String preppedRecords, String instantTime) { + return ""; + } + + @Override + public String insert(String records, String instantTime) { + return ""; + } + + @Override + public String insertPreppedRecords(String preppedRecords, String instantTime) { + return ""; + } + + @Override + public String bulkInsert(String records, String instantTime) { + return ""; + } + + @Override + public String bulkInsert(String records, String instantTime, Option userDefinedBulkInsertPartitioner) { + return ""; + } + + @Override + public String bulkInsertPreppedRecords(String preppedRecords, String instantTime, Option bulkInsertPartitioner) { + return ""; + } + + @Override + public String delete(String keys, String instantTime) { + return ""; + } + + @Override + public String deletePrepped(String preppedRecords, String instantTime) { + return ""; + } + } +} \ No newline at end of file diff --git a/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/keygen/TestComplexAvroKeyGenerator.java b/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/keygen/TestComplexAvroKeyGenerator.java index 0f6afd2ade6b2..dce53907aef7b 100644 --- a/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/keygen/TestComplexAvroKeyGenerator.java +++ b/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/keygen/TestComplexAvroKeyGenerator.java @@ -21,37 +21,57 @@ import org.apache.hudi.common.config.TypedProperties; import org.apache.hudi.common.model.HoodieKey; import org.apache.hudi.common.testutils.HoodieTestDataGenerator; +import org.apache.hudi.config.HoodieWriteConfig; import org.apache.hudi.keygen.constant.KeyGeneratorOptions; import org.apache.avro.generic.GenericRecord; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; -import static junit.framework.TestCase.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class TestComplexAvroKeyGenerator { - @Test - public void testSingleValueKeyGenerator() { + @ParameterizedTest + @CsvSource(value = {"false,true", "true,false", "true,true"}) + void testSingleValueKeyGenerator(boolean setNewEncodingConfig, + boolean encodeSingleKeyFieldValueOnly) { + String recordKeyFieldName = "_row_key"; TypedProperties properties = new TypedProperties(); - properties.setProperty(KeyGeneratorOptions.RECORDKEY_FIELD_NAME.key(), "_row_key"); + properties.setProperty(KeyGeneratorOptions.RECORDKEY_FIELD_NAME.key(), recordKeyFieldName); properties.setProperty(KeyGeneratorOptions.PARTITIONPATH_FIELD_NAME.key(), "timestamp"); + if (setNewEncodingConfig) { + properties.setProperty( + HoodieWriteConfig.COMPLEX_KEYGEN_NEW_ENCODING.key(), + String.valueOf(encodeSingleKeyFieldValueOnly)); + } ComplexAvroKeyGenerator compositeKeyGenerator = new ComplexAvroKeyGenerator(properties); assertEquals(compositeKeyGenerator.getRecordKeyFieldNames().size(), 1); assertEquals(compositeKeyGenerator.getPartitionPathFields().size(), 1); HoodieTestDataGenerator dataGenerator = new HoodieTestDataGenerator(); GenericRecord record = dataGenerator.generateGenericRecords(1).get(0); - String rowKey = record.get("_row_key").toString(); + String rowKey = record.get(recordKeyFieldName).toString(); String partitionPath = record.get("timestamp").toString(); HoodieKey hoodieKey = compositeKeyGenerator.getKey(record); - assertEquals(rowKey, hoodieKey.getRecordKey()); + // Table version 8 may use new encoding config if set + String expectedRecordKey = setNewEncodingConfig && encodeSingleKeyFieldValueOnly + ? rowKey : recordKeyFieldName + ":" + rowKey; + assertEquals(expectedRecordKey, hoodieKey.getRecordKey()); assertEquals(partitionPath, hoodieKey.getPartitionPath()); } - @Test - public void testMultipleValueKeyGenerator() { + @ParameterizedTest + @CsvSource(value = {"false,true", "true,false", "true,true"}) + void testMultipleValueKeyGenerator(boolean setNewEncodingConfig, + boolean encodeSingleKeyFieldValueOnly) { TypedProperties properties = new TypedProperties(); properties.setProperty(KeyGeneratorOptions.RECORDKEY_FIELD_NAME.key(), "_row_key,timestamp"); properties.setProperty(KeyGeneratorOptions.PARTITIONPATH_FIELD_NAME.key(), "rider,driver"); + if (setNewEncodingConfig) { + properties.setProperty( + HoodieWriteConfig.COMPLEX_KEYGEN_NEW_ENCODING.key(), + String.valueOf(encodeSingleKeyFieldValueOnly)); + } ComplexAvroKeyGenerator compositeKeyGenerator = new ComplexAvroKeyGenerator(properties); assertEquals(compositeKeyGenerator.getRecordKeyFieldNames().size(), 2); assertEquals(compositeKeyGenerator.getPartitionPathFields().size(), 2); @@ -66,11 +86,18 @@ public void testMultipleValueKeyGenerator() { assertEquals(partitionPath, hoodieKey.getPartitionPath()); } - @Test - public void testMultipleValueKeyGeneratorNonPartitioned() { + @ParameterizedTest + @CsvSource(value = {"false,true", "true,false", "true,true"}) + void testMultipleValueKeyGeneratorNonPartitioned(boolean setNewEncodingConfig, + boolean encodeSingleKeyFieldValueOnly) { TypedProperties properties = new TypedProperties(); properties.setProperty(KeyGeneratorOptions.RECORDKEY_FIELD_NAME.key(), "_row_key,timestamp"); properties.setProperty(KeyGeneratorOptions.PARTITIONPATH_FIELD_NAME.key(), ""); + if (setNewEncodingConfig) { + properties.setProperty( + HoodieWriteConfig.COMPLEX_KEYGEN_NEW_ENCODING.key(), + String.valueOf(encodeSingleKeyFieldValueOnly)); + } ComplexAvroKeyGenerator compositeKeyGenerator = new ComplexAvroKeyGenerator(properties); assertEquals(compositeKeyGenerator.getRecordKeyFieldNames().size(), 2); assertEquals(compositeKeyGenerator.getPartitionPathFields().size(), 0); diff --git a/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/keygen/TestKeyGenUtils.java b/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/keygen/TestKeyGenUtils.java index ae0b259dd73cb..9c27f0eb2ebaf 100644 --- a/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/keygen/TestKeyGenUtils.java +++ b/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/keygen/TestKeyGenUtils.java @@ -18,16 +18,27 @@ package org.apache.hudi.keygen; +import org.apache.hudi.common.table.HoodieTableConfig; import org.apache.hudi.common.util.Option; +import org.apache.hudi.exception.HoodieKeyException; import org.apache.hudi.keygen.constant.KeyGeneratorType; +import org.apache.avro.Schema; +import org.apache.avro.generic.GenericData; +import org.apache.avro.generic.GenericRecord; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import static org.apache.hudi.common.table.HoodieTableConfig.KEY_GENERATOR_CLASS_NAME; +import static org.apache.hudi.common.table.HoodieTableConfig.RECORDKEY_FIELDS; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; public class TestKeyGenUtils { @@ -87,4 +98,109 @@ public void testExtractRecordKeysWithFields() { String[] s2 = KeyGenUtils.extractRecordKeysByFields("id1:1,id2:2,2,id3:3", fields); Assertions.assertArrayEquals(new String[] {"2", "2"}, s2); } + + @Test + void testGetRecordKey() { + Schema nullableStringSchema = Schema.createUnion(Schema.create(Schema.Type.NULL), Schema.create(Schema.Type.STRING)); + Schema schema = Schema.createRecord("TestRecord", "doc", "test", false, + Arrays.asList( + new Schema.Field("key1", nullableStringSchema), + new Schema.Field("key2", nullableStringSchema), + new Schema.Field("key3", nullableStringSchema), + new Schema.Field("key4", nullableStringSchema) + )); + GenericRecord avroRecord = new GenericData.Record(schema); + avroRecord.put("key1", "value1"); + avroRecord.put("key2", "value2"); + avroRecord.put("key3", null); + avroRecord.put("key4", ""); + + assertEquals("key1:value1", + KeyGenUtils.getRecordKey(avroRecord, Arrays.asList("key1"), true)); + assertThrows(HoodieKeyException.class, + () -> KeyGenUtils.getRecordKey(avroRecord, Arrays.asList("key3"), true), + "recordKey values: \"key3:__null__\" for fields: [key3] cannot be entirely null or empty."); + assertThrows(HoodieKeyException.class, + () -> KeyGenUtils.getRecordKey(avroRecord, Arrays.asList("key4"), true), + "recordKey values: \"key4:__empty__\" for fields: [key4] cannot be entirely null or empty."); + assertEquals("key1:value1,key2:value2", + KeyGenUtils.getRecordKey(avroRecord, Arrays.asList("key1", "key2"), true)); + assertEquals("key1:value1,key3:__null__", + KeyGenUtils.getRecordKey(avroRecord, Arrays.asList("key1", "key3"), true)); + assertEquals("key1:value1,key4:__empty__", + KeyGenUtils.getRecordKey(avroRecord, Arrays.asList("key1", "key4"), true)); + + assertEquals("value1", + KeyGenUtils.getRecordKey(avroRecord, "key1", true)); + assertThrows(HoodieKeyException.class, + () -> KeyGenUtils.getRecordKey(avroRecord, "key3", true), + "recordKey value: \"null\" for field: \"key3\" cannot be null or empty."); + assertThrows(HoodieKeyException.class, + () -> KeyGenUtils.getRecordKey(avroRecord, "key4", true), + "recordKey value: \"\" for field: \"key4\" cannot be null or empty."); + } + + @Test + void testIsComplexKeyGeneratorWithSingleRecordKeyField() { + HoodieTableConfig tableConfig = new HoodieTableConfig(); + tableConfig.setValue(KEY_GENERATOR_CLASS_NAME, "org.apache.hudi.keygen.ComplexKeyGenerator"); + tableConfig.setValue(RECORDKEY_FIELDS, "id"); + assertTrue(KeyGenUtils.isComplexKeyGeneratorWithSingleRecordKeyField(tableConfig)); + + tableConfig = new HoodieTableConfig(); + tableConfig.setValue(RECORDKEY_FIELDS, "userId"); + tableConfig.setValue(KEY_GENERATOR_CLASS_NAME, "org.apache.hudi.keygen.ComplexAvroKeyGenerator"); + assertTrue(KeyGenUtils.isComplexKeyGeneratorWithSingleRecordKeyField(tableConfig)); + } + + @Test + void testIsComplexKeyGeneratorWithSingleRecordKeyFieldOnMultipleFields() { + HoodieTableConfig tableConfig = new HoodieTableConfig(); + tableConfig.setValue(KEY_GENERATOR_CLASS_NAME, "org.apache.hudi.keygen.ComplexKeyGenerator"); + tableConfig.setValue(RECORDKEY_FIELDS, "id,userId"); + assertFalse(KeyGenUtils.isComplexKeyGeneratorWithSingleRecordKeyField(tableConfig)); + + tableConfig = new HoodieTableConfig(); + tableConfig.setValue(KEY_GENERATOR_CLASS_NAME, "org.apache.hudi.keygen.ComplexAvroKeyGenerator"); + tableConfig.setValue(RECORDKEY_FIELDS, "id,userId,name"); + assertFalse(KeyGenUtils.isComplexKeyGeneratorWithSingleRecordKeyField(tableConfig)); + } + + @Test + void testIsComplexKeyGeneratorWithSingleRecordKeyFieldOnNonComplexGenerator() { + HoodieTableConfig tableConfig = new HoodieTableConfig(); + tableConfig.setValue(KEY_GENERATOR_CLASS_NAME, "org.apache.hudi.keygen.SimpleKeyGenerator"); + tableConfig.setValue(RECORDKEY_FIELDS, "id"); + assertFalse(KeyGenUtils.isComplexKeyGeneratorWithSingleRecordKeyField(tableConfig)); + + tableConfig = new HoodieTableConfig(); + tableConfig.setValue(KEY_GENERATOR_CLASS_NAME, "org.apache.hudi.keygen.SimpleAvroKeyGenerator"); + tableConfig.setValue(RECORDKEY_FIELDS, "userId"); + assertFalse(KeyGenUtils.isComplexKeyGeneratorWithSingleRecordKeyField(tableConfig)); + + tableConfig = new HoodieTableConfig(); + tableConfig.setValue(KEY_GENERATOR_CLASS_NAME, "org.apache.hudi.keygen.TimestampBasedKeyGenerator"); + tableConfig.setValue(RECORDKEY_FIELDS, "id"); + assertFalse(KeyGenUtils.isComplexKeyGeneratorWithSingleRecordKeyField(tableConfig)); + + tableConfig = new HoodieTableConfig(); + tableConfig.setValue(KEY_GENERATOR_CLASS_NAME, "org.apache.hudi.keygen.CustomKeyGenerator"); + tableConfig.setValue(RECORDKEY_FIELDS, "id"); + assertFalse(KeyGenUtils.isComplexKeyGeneratorWithSingleRecordKeyField(tableConfig)); + } + + @Test + void testIsComplexKeyGeneratorWithSingleRecordKeyFieldOnNoRecordKeyFields() { + HoodieTableConfig tableConfig = new HoodieTableConfig(); + tableConfig.setValue(KEY_GENERATOR_CLASS_NAME, "org.apache.hudi.keygen.ComplexKeyGenerator"); + assertFalse(KeyGenUtils.isComplexKeyGeneratorWithSingleRecordKeyField(tableConfig)); + } + + @Test + void testIsComplexKeyGeneratorWithSingleRecordKeyFieldEmptyRecordKeyFields() { + HoodieTableConfig tableConfig = new HoodieTableConfig(); + tableConfig.setValue(KEY_GENERATOR_CLASS_NAME, "org.apache.hudi.keygen.ComplexKeyGenerator"); + tableConfig.setValue(RECORDKEY_FIELDS, ""); + assertFalse(KeyGenUtils.isComplexKeyGeneratorWithSingleRecordKeyField(tableConfig)); + } } diff --git a/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/testutils/Assertions.java b/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/testutils/Assertions.java index bb2ba84f8c9bd..cb4ef64289deb 100644 --- a/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/testutils/Assertions.java +++ b/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/testutils/Assertions.java @@ -21,12 +21,17 @@ import org.apache.hudi.client.WriteStatus; import org.apache.hudi.common.testutils.CheckedFunction; +import org.apache.hudi.exception.HoodieException; + +import org.junit.jupiter.api.function.Executable; import java.util.List; +import static org.apache.hudi.keygen.KeyGenUtils.getComplexKeygenErrorMessage; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; /** * Commonly used assertion functions. @@ -51,4 +56,8 @@ public static void assertFileSizesEqual(List statuses, CheckedFunct assertEquals(fileSizeGetter.apply(status), status.getStat().getFileSizeInBytes()))); } + public static void assertComplexKeyGeneratorValidationThrows(Executable writeOperation, String operation) { + HoodieException exception = assertThrows(HoodieException.class, writeOperation); + assertEquals(getComplexKeygenErrorMessage(operation), exception.getMessage()); + } } diff --git a/hudi-client/hudi-spark-client/src/main/java/org/apache/hudi/keygen/BuiltinKeyGenerator.java b/hudi-client/hudi-spark-client/src/main/java/org/apache/hudi/keygen/BuiltinKeyGenerator.java index 58350b0d49460..d022817404cfd 100644 --- a/hudi-client/hudi-spark-client/src/main/java/org/apache/hudi/keygen/BuiltinKeyGenerator.java +++ b/hudi-client/hudi-spark-client/src/main/java/org/apache/hudi/keygen/BuiltinKeyGenerator.java @@ -71,8 +71,6 @@ public abstract class BuiltinKeyGenerator extends BaseKeyGenerator implements Sp private static final Logger LOG = LoggerFactory.getLogger(BuiltinKeyGenerator.class); - private static final String COMPOSITE_KEY_FIELD_VALUE_INFIX = ":"; - protected static final String FIELDS_SEP = ","; protected static final UTF8String NULL_RECORD_KEY_PLACEHOLDER_UTF8 = UTF8String.fromString(NULL_RECORDKEY_PLACEHOLDER); @@ -183,8 +181,10 @@ protected final UTF8String combineRecordKeyUnsafe(List fieldNames, List< * NOTE: This method has to stay final (so that it's easier for JIT compiler to apply certain * optimizations, like inlining) */ - protected final String combineCompositeRecordKey(Object... recordKeyParts) { + protected final String combineCompositeRecordKey(boolean encodeSingleKeyFieldName, + Object... recordKeyParts) { return combineCompositeRecordKeyInternal( + encodeSingleKeyFieldName, StringPartitionPathFormatter.JavaStringBuilder::new, BuiltinKeyGenerator::toString, BuiltinKeyGenerator::handleNullOrEmptyCompositeKeyPart, @@ -197,8 +197,10 @@ protected final String combineCompositeRecordKey(Object... recordKeyParts) { * NOTE: This method has to stay final (so that it's easier for JIT compiler to apply certain * optimizations, like inlining) */ - protected final UTF8String combineCompositeRecordKeyUnsafe(Object... recordKeyParts) { + protected final UTF8String combineCompositeRecordKeyUnsafe(boolean encodeSingleKeyFieldName, + Object... recordKeyParts) { return combineCompositeRecordKeyInternal( + encodeSingleKeyFieldName, UTF8StringPartitionPathFormatter.UTF8StringBuilder::new, BuiltinKeyGenerator::toUTF8String, BuiltinKeyGenerator::handleNullOrEmptyCompositeKeyPartUTF8, @@ -233,23 +235,43 @@ private S combineRecordKeyInternal( } private S combineCompositeRecordKeyInternal( + boolean encodeSingleKeyFieldName, Supplier> builderFactory, Function converter, Function emptyKeyPartHandler, Predicate isNullOrEmptyKeyPartPredicate, Object... recordKeyParts ) { - boolean hasNonNullNonEmptyPart = false; + if (recordKeyParts.length == 0) { + throw new HoodieKeyException(String.format("All of the values for (%s) were either null or empty", recordKeyFields)); + } PartitionPathFormatterBase.StringBuilder sb = builderFactory.get(); + + if (recordKeyParts.length == 1) { + // NOTE: If record-key part has already been a string [[toString]] will be a no-op + S convertedKeyPart = emptyKeyPartHandler.apply(converter.apply(recordKeyParts[0])); + + if (encodeSingleKeyFieldName) { + sb.appendJava(recordKeyFields.get(0)); + sb.appendJava(DEFAULT_COMPOSITE_KEY_FILED_VALUE); + } + sb.append(convertedKeyPart); + // This check is to validate that overall composite-key has at least one non-null, non-empty + // segment + if (isNullOrEmptyKeyPartPredicate.test(convertedKeyPart)) { + throw new HoodieKeyException(String.format("All of the values for (%s) were either null or empty", recordKeyFields)); + } + return sb.build(); + } + + boolean hasNonNullNonEmptyPart = false; for (int i = 0; i < recordKeyParts.length; ++i) { // NOTE: If record-key part has already been a string [[toString]] will be a no-op S convertedKeyPart = emptyKeyPartHandler.apply(converter.apply(recordKeyParts[i])); - if (recordKeyParts.length > 1) { - sb.appendJava(recordKeyFields.get(i)); - sb.appendJava(COMPOSITE_KEY_FIELD_VALUE_INFIX); - } + sb.appendJava(recordKeyFields.get(i)); + sb.appendJava(DEFAULT_COMPOSITE_KEY_FILED_VALUE); sb.append(convertedKeyPart); // This check is to validate that overall composite-key has at least one non-null, non-empty // segment diff --git a/hudi-client/hudi-spark-client/src/main/java/org/apache/hudi/keygen/ComplexKeyGenerator.java b/hudi-client/hudi-spark-client/src/main/java/org/apache/hudi/keygen/ComplexKeyGenerator.java index d00ca066cedde..67be836f79fb9 100644 --- a/hudi-client/hudi-spark-client/src/main/java/org/apache/hudi/keygen/ComplexKeyGenerator.java +++ b/hudi-client/hudi-spark-client/src/main/java/org/apache/hudi/keygen/ComplexKeyGenerator.java @@ -38,6 +38,7 @@ public class ComplexKeyGenerator extends BuiltinKeyGenerator { private final ComplexAvroKeyGenerator complexAvroKeyGenerator; + private final boolean encodeSingleKeyFieldName; public ComplexKeyGenerator(TypedProperties props) { super(props); @@ -47,6 +48,7 @@ public ComplexKeyGenerator(TypedProperties props) { .filter(s -> !s.isEmpty()) .collect(Collectors.toList()); this.complexAvroKeyGenerator = new ComplexAvroKeyGenerator(props); + this.encodeSingleKeyFieldName = KeyGenUtils.encodeSingleKeyFieldNameForComplexKeyGen(props); } @Override @@ -62,13 +64,15 @@ public String getPartitionPath(GenericRecord record) { @Override public String getRecordKey(Row row) { tryInitRowAccessor(row.schema()); - return combineCompositeRecordKey(rowAccessor.getRecordKeyParts(row)); + return combineCompositeRecordKey( + encodeSingleKeyFieldName, rowAccessor.getRecordKeyParts(row)); } @Override public UTF8String getRecordKey(InternalRow internalRow, StructType schema) { tryInitRowAccessor(schema); - return combineCompositeRecordKeyUnsafe(rowAccessor.getRecordKeyParts(internalRow)); + return combineCompositeRecordKeyUnsafe( + encodeSingleKeyFieldName, rowAccessor.getRecordKeyParts(internalRow)); } @Override diff --git a/hudi-client/hudi-spark-client/src/main/java/org/apache/hudi/keygen/GlobalDeleteKeyGenerator.java b/hudi-client/hudi-spark-client/src/main/java/org/apache/hudi/keygen/GlobalDeleteKeyGenerator.java index 8a22938430751..d4e037a07d96a 100644 --- a/hudi-client/hudi-spark-client/src/main/java/org/apache/hudi/keygen/GlobalDeleteKeyGenerator.java +++ b/hudi-client/hudi-spark-client/src/main/java/org/apache/hudi/keygen/GlobalDeleteKeyGenerator.java @@ -60,13 +60,13 @@ public String getPartitionPath(GenericRecord record) { @Override public String getRecordKey(Row row) { tryInitRowAccessor(row.schema()); - return combineCompositeRecordKey(rowAccessor.getRecordKeyParts(row)); + return combineCompositeRecordKey(true, rowAccessor.getRecordKeyParts(row)); } @Override public UTF8String getRecordKey(InternalRow internalRow, StructType schema) { tryInitRowAccessor(schema); - return combineCompositeRecordKeyUnsafe(rowAccessor.getRecordKeyParts(internalRow)); + return combineCompositeRecordKeyUnsafe(true, rowAccessor.getRecordKeyParts(internalRow)); } @Override diff --git a/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/client/common/TestSparkReaderContextFactory.java b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/client/common/TestSparkReaderContextFactory.java new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/keygen/KeyGeneratorTestUtilities.java b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/keygen/KeyGeneratorTestUtilities.java new file mode 100644 index 0000000000000..29ee6af5a7a94 --- /dev/null +++ b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/keygen/KeyGeneratorTestUtilities.java @@ -0,0 +1,200 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.hudi.keygen; + +import org.apache.hudi.util.JavaScalaConverters; + +import org.apache.avro.Schema; +import org.apache.avro.generic.GenericData; +import org.apache.avro.generic.GenericRecord; +import org.apache.avro.util.Utf8; +import org.apache.spark.sql.Row; +import org.apache.spark.sql.catalyst.InternalRow; +import org.apache.spark.sql.catalyst.expressions.GenericRowWithSchema; +import org.apache.spark.sql.types.ArrayType; +import org.apache.spark.sql.types.DataType; +import org.apache.spark.sql.types.DataTypes; +import org.apache.spark.sql.types.DecimalType; +import org.apache.spark.sql.types.MapType; +import org.apache.spark.sql.types.Metadata; +import org.apache.spark.sql.types.StringType; +import org.apache.spark.sql.types.StructField; +import org.apache.spark.sql.types.StructType; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +class KeyGeneratorTestUtilities { + + public static final String NESTED_COL_SCHEMA = "{\"type\":\"record\", \"name\":\"nested_col\",\"fields\": [" + + "{\"name\": \"prop1\",\"type\": [\"null\", \"string\"]},{\"name\": \"prop2\", \"type\": \"long\"}]}"; + public static final String EXAMPLE_SCHEMA = "{\"type\": \"record\",\"name\": \"testrec\",\"fields\": [ " + + "{\"name\": \"timestamp\",\"type\": \"long\"},{\"name\": \"_row_key\", \"type\": \"string\"}," + + "{\"name\": \"ts_ms\", \"type\": \"string\"}," + + "{\"name\": \"pii_col\", \"type\": \"string\"}," + + "{\"name\": \"nested_col\",\"type\": [\"null\", " + NESTED_COL_SCHEMA + "]}" + + "]}"; + + private static final StructType FARE_STRUCT_TYPE = new StructType(new StructField[] { + new StructField("amount", DataTypes.DoubleType, false, Metadata.empty()), + new StructField("currency", DataTypes.StringType, false, Metadata.empty()) + }); + protected static final StructType TRIP_STRUCT_TYPE = new StructType(new StructField[] { + new StructField("timestamp", DataTypes.LongType, false, Metadata.empty()), + new StructField("_row_key", DataTypes.StringType, false, Metadata.empty()), + new StructField("partition_path", DataTypes.StringType, true, Metadata.empty()), + new StructField("trip_type", DataTypes.StringType, false, Metadata.empty()), + new StructField("rider", DataTypes.StringType, false, Metadata.empty()), + new StructField("driver", DataTypes.StringType, false, Metadata.empty()), + new StructField("begin_lat", DataTypes.DoubleType, false, Metadata.empty()), + new StructField("begin_lon", DataTypes.DoubleType, false, Metadata.empty()), + new StructField("end_lat", DataTypes.DoubleType, false, Metadata.empty()), + new StructField("end_lon", DataTypes.DoubleType, false, Metadata.empty()), + new StructField("distance_in_meters", DataTypes.IntegerType, false, Metadata.empty()), + new StructField("seconds_since_epoch", DataTypes.LongType, false, Metadata.empty()), + new StructField("weight", DataTypes.FloatType, false, Metadata.empty()), + new StructField("nation", DataTypes.BinaryType, false, Metadata.empty()), + new StructField("current_date", DataTypes.DateType, false, Metadata.empty()), + new StructField("current_ts", DataTypes.LongType, false, Metadata.empty()), + new StructField("height", new DecimalType(10, 6), false, Metadata.empty()), + new StructField("city_to_state", DataTypes.createMapType( + DataTypes.StringType, DataTypes.StringType, false), false, Metadata.empty()), + new StructField("fare", FARE_STRUCT_TYPE, false, Metadata.empty()), + new StructField("tip_history", DataTypes.createArrayType(FARE_STRUCT_TYPE, false), false, Metadata.empty()), + new StructField("_hoodie_is_deleted", DataTypes.BooleanType, false, Metadata.empty()) + }); + private static final StructType NESTED_TYPE = new StructType(new StructField[] { + new StructField("prop1", DataTypes.StringType, true, Metadata.empty()), + new StructField("prop2", DataTypes.LongType, false, Metadata.empty()) + }); + private static final StructType STRUCT_TYPE = new StructType(new StructField[] { + new StructField("timestamp", DataTypes.LongType, false, Metadata.empty()), + new StructField("_row_key", DataTypes.StringType, false, Metadata.empty()), + new StructField("ts_ms", DataTypes.StringType, false, Metadata.empty()), + new StructField("pii_col", DataTypes.StringType, false, Metadata.empty()), + new StructField("nested_col", NESTED_TYPE, true, Metadata.empty()) + }); + + public static GenericRecord getRecord() { + return getRecord(getNestedColRecord("val1", 10L)); + } + + public static GenericRecord getNestedColRecord(String prop1Value, Long prop2Value) { + GenericRecord nestedColRecord = new GenericData.Record(new Schema.Parser().parse(NESTED_COL_SCHEMA)); + nestedColRecord.put("prop1", prop1Value); + nestedColRecord.put("prop2", prop2Value); + return nestedColRecord; + } + + public static GenericRecord getRecord(GenericRecord nestedColRecord) { + GenericRecord avroRecord = new GenericData.Record(new Schema.Parser().parse(EXAMPLE_SCHEMA)); + avroRecord.put("timestamp", 4357686L); + avroRecord.put("_row_key", "key1"); + avroRecord.put("ts_ms", "2020-03-21"); + avroRecord.put("pii_col", "pi"); + avroRecord.put("nested_col", nestedColRecord); + return avroRecord; + } + + public static Row getRow(GenericRecord genericRecord) { + return getRow(genericRecord, STRUCT_TYPE); + } + + public static Row getRow(GenericRecord genericRecord, StructType structType) { + Row row = genericRecordToRow(genericRecord, structType); + int fieldCount = structType.fieldNames().length; + Object[] values = new Object[fieldCount]; + for (int i = 0; i < fieldCount; i++) { + values[i] = row.get(i); + } + return new GenericRowWithSchema(values, structType); + } + + public static InternalRow getInternalRow(Row row) { + List values = IntStream.range(0, row.schema().fieldNames().length) + .mapToObj(row::get).collect(Collectors.toList()); + return InternalRow.apply(JavaScalaConverters.convertJavaListToScalaList(values)); + } + + /** + * Converts an Avro GenericRecord to a Spark SQL Row based on a given schema. + * Only used by key generator tests. + * + * @param genericRecord the input Avro GenericRecord + * @param schema the target Spark SQL StructType that defines the structure of the output Row + * @return a Spark SQL Row containing the data from the GenericRecord + */ + public static Row genericRecordToRow(GenericRecord genericRecord, StructType schema) { + StructField[] fields = schema.fields(); + Object[] values = new Object[fields.length]; + + for (int i = 0; i < fields.length; i++) { + StructField field = fields[i]; + Object avroValue = genericRecord.get(field.name()); + values[i] = convertAvroValue(avroValue, field.dataType()); + } + + return new GenericRowWithSchema(values, schema); + } + + /** + * Recursively converts an Avro value to its corresponding Spark SQL data type. + * Only used by key generator tests. + * + * @param value the value to convert + * @param dataType the target Spark SQL DataType + * @return the converted value compatible with Spark SQL + */ + private static Object convertAvroValue(Object value, DataType dataType) { + if (value == null) { + return null; + } + + if (dataType instanceof StringType + && (value instanceof Utf8 || value instanceof GenericData.EnumSymbol || value instanceof String)) { + return value.toString(); + } else if (dataType instanceof DecimalType && value instanceof GenericData.Fixed) { + DecimalType decimalType = (DecimalType) dataType; + byte[] bytes = ((GenericData.Fixed) value).bytes(); + return new BigDecimal(new BigInteger(bytes), decimalType.scale()); + } else if (dataType instanceof StructType && value instanceof GenericRecord) { + return genericRecordToRow((GenericRecord) value, (StructType) dataType); + } else if (dataType instanceof ArrayType && value instanceof List) { + ArrayType arrayType = (ArrayType) dataType; + List avroList = (List) value; + return avroList.stream() + .map(item -> convertAvroValue(item, arrayType.elementType())) + .collect(Collectors.toList()); + } else if (dataType instanceof MapType && value instanceof Map) { + MapType mapType = (MapType) dataType; + Map avroMap = (Map) value; + return avroMap.entrySet().stream() + .collect(Collectors.toMap( + e -> e.getKey().toString(), // Convert key to String + e -> convertAvroValue(e.getValue(), mapType.valueType()) + )); + } + return value; + } +} \ No newline at end of file diff --git a/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/keygen/TestComplexKeyGenerator.java b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/keygen/TestComplexKeyGenerator.java new file mode 100644 index 0000000000000..bfc745974fd83 --- /dev/null +++ b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/keygen/TestComplexKeyGenerator.java @@ -0,0 +1,223 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.hudi.keygen; + +import org.apache.hudi.common.config.TypedProperties; +import org.apache.hudi.common.model.HoodieKey; +import org.apache.hudi.common.testutils.HoodieTestDataGenerator; +import org.apache.hudi.config.HoodieWriteConfig; +import org.apache.hudi.exception.HoodieKeyException; +import org.apache.hudi.keygen.constant.KeyGeneratorOptions; + +import org.apache.avro.generic.GenericRecord; +import org.apache.spark.sql.Row; +import org.apache.spark.sql.catalyst.InternalRow; +import org.apache.spark.unsafe.types.UTF8String; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class TestComplexKeyGenerator extends KeyGeneratorTestUtilities { + + private TypedProperties getCommonProps(boolean getComplexRecordKey) { + TypedProperties properties = new TypedProperties(); + if (getComplexRecordKey) { + properties.put(KeyGeneratorOptions.RECORDKEY_FIELD_NAME.key(), "_row_key, pii_col"); + } else { + properties.put(KeyGeneratorOptions.RECORDKEY_FIELD_NAME.key(), "_row_key"); + } + properties.put(KeyGeneratorOptions.HIVE_STYLE_PARTITIONING_ENABLE.key(), "true"); + return properties; + } + + private TypedProperties getPropertiesWithoutPartitionPathProp() { + return getCommonProps(false); + } + + private TypedProperties getPropertiesWithoutRecordKeyProp() { + TypedProperties properties = new TypedProperties(); + properties.put(KeyGeneratorOptions.PARTITIONPATH_FIELD_NAME.key(), "timestamp"); + return properties; + } + + private TypedProperties getWrongRecordKeyFieldProps() { + TypedProperties properties = new TypedProperties(); + properties.put(KeyGeneratorOptions.PARTITIONPATH_FIELD_NAME.key(), "timestamp"); + properties.put(KeyGeneratorOptions.RECORDKEY_FIELD_NAME.key(), "_wrong_key"); + return properties; + } + + private TypedProperties getProps() { + TypedProperties properties = getCommonProps(true); + properties.put(KeyGeneratorOptions.PARTITIONPATH_FIELD_NAME.key(), "timestamp,ts_ms"); + return properties; + } + + @Test + void testNullPartitionPathFields() { + assertThrows(IllegalArgumentException.class, () -> new ComplexKeyGenerator(getPropertiesWithoutPartitionPathProp())); + } + + @Test + void testNullRecordKeyFields() { + GenericRecord avroRecord = getRecord(); + assertThrows(HoodieKeyException.class, () -> { + ComplexKeyGenerator keyGenerator = new ComplexKeyGenerator(getPropertiesWithoutRecordKeyProp()); + keyGenerator.getRecordKey(avroRecord); + }); + } + + @Test + void testWrongRecordKeyField() { + ComplexKeyGenerator keyGenerator = new ComplexKeyGenerator(getWrongRecordKeyFieldProps()); + assertThrows(HoodieKeyException.class, () -> keyGenerator.getRecordKey(getRecord())); + } + + @Test + void testHappyFlow() { + ComplexKeyGenerator keyGenerator = new ComplexKeyGenerator(getProps()); + GenericRecord avroRecord = getRecord(); + HoodieKey key = keyGenerator.getKey(avroRecord); + assertEquals("_row_key:key1,pii_col:pi", key.getRecordKey()); + assertEquals("timestamp=4357686/ts_ms=2020-03-21", key.getPartitionPath()); + Row row = KeyGeneratorTestUtilities.getRow(avroRecord); + assertEquals("_row_key:key1,pii_col:pi", keyGenerator.getRecordKey(row)); + assertEquals("timestamp=4357686/ts_ms=2020-03-21", keyGenerator.getPartitionPath(row)); + + InternalRow internalRow = KeyGeneratorTestUtilities.getInternalRow(row); + assertEquals(UTF8String.fromString("timestamp=4357686/ts_ms=2020-03-21"), keyGenerator.getPartitionPath(internalRow, row.schema())); + } + + @ParameterizedTest + @CsvSource(value = {"false,true,8", "true,false,8", "true,true,8", "false,true,9", "true,false,9", "true,true,9"}) + void testSingleValueKeyGenerator(boolean setNewEncodingConfig, + boolean encodeSingleKeyFieldValueOnly, + String tableVersion) { + String recordKeyFieldName = "_row_key"; + TypedProperties properties = new TypedProperties(); + properties.setProperty(KeyGeneratorOptions.RECORDKEY_FIELD_NAME.key(), recordKeyFieldName); + properties.setProperty(KeyGeneratorOptions.PARTITIONPATH_FIELD_NAME.key(), "timestamp"); + if (setNewEncodingConfig) { + properties.setProperty( + HoodieWriteConfig.COMPLEX_KEYGEN_NEW_ENCODING.key(), + String.valueOf(encodeSingleKeyFieldValueOnly)); + } + ComplexKeyGenerator compositeKeyGenerator = new ComplexKeyGenerator(properties); + assertEquals(1, compositeKeyGenerator.getRecordKeyFieldNames().size()); + assertEquals(1, compositeKeyGenerator.getPartitionPathFields().size()); + try (HoodieTestDataGenerator dataGenerator = new HoodieTestDataGenerator(System.currentTimeMillis())) { + GenericRecord avroRecord = dataGenerator.generateGenericRecords(1).get(0); + String rowKey = avroRecord.get(recordKeyFieldName).toString(); + String partitionPath = avroRecord.get("timestamp").toString(); + HoodieKey hoodieKey = compositeKeyGenerator.getKey(avroRecord); + // For table version 9, new encoding config should have no effect + String expectedRecordKey; + if ("9".equals(tableVersion)) { + // Table version 9 ignores the new encoding config and always uses the old format + expectedRecordKey = recordKeyFieldName + ":" + rowKey; + } else { + // Table version 8 may use new encoding config if set + expectedRecordKey = setNewEncodingConfig && encodeSingleKeyFieldValueOnly + ? rowKey : recordKeyFieldName + ":" + rowKey; + } + assertEquals(expectedRecordKey, hoodieKey.getRecordKey()); + assertEquals(partitionPath, hoodieKey.getPartitionPath()); + + Row row = KeyGeneratorTestUtilities.getRow(avroRecord, TRIP_STRUCT_TYPE); + assertEquals(partitionPath, compositeKeyGenerator.getPartitionPath(row)); + InternalRow internalRow = KeyGeneratorTestUtilities.getInternalRow(row); + assertEquals(UTF8String.fromString(expectedRecordKey), compositeKeyGenerator.getRecordKey(internalRow, TRIP_STRUCT_TYPE)); + assertEquals(UTF8String.fromString(partitionPath), compositeKeyGenerator.getPartitionPath(internalRow, row.schema())); + } + } + + @ParameterizedTest + @CsvSource(value = {"false,true,8", "true,false,8", "true,true,8", "false,true,9", "true,false,9", "true,true,9"}) + void testMultipleValueKeyGenerator(boolean setNewEncodingConfig, + boolean encodeSingleKeyFieldValueOnly, + String tableVersion) { + TypedProperties properties = new TypedProperties(); + properties.setProperty(KeyGeneratorOptions.RECORDKEY_FIELD_NAME.key(), "_row_key,timestamp"); + properties.setProperty(KeyGeneratorOptions.PARTITIONPATH_FIELD_NAME.key(), "rider,driver"); + if (setNewEncodingConfig) { + properties.setProperty( + HoodieWriteConfig.COMPLEX_KEYGEN_NEW_ENCODING.key(), + String.valueOf(encodeSingleKeyFieldValueOnly)); + } + ComplexKeyGenerator compositeKeyGenerator = new ComplexKeyGenerator(properties); + assertEquals(2, compositeKeyGenerator.getRecordKeyFieldNames().size()); + assertEquals(2, compositeKeyGenerator.getPartitionPathFields().size()); + try (HoodieTestDataGenerator dataGenerator = new HoodieTestDataGenerator(System.currentTimeMillis())) { + GenericRecord avroRecord = dataGenerator.generateGenericRecords(1).get(0); + String rowKey = + "_row_key" + ComplexAvroKeyGenerator.DEFAULT_RECORD_KEY_SEPARATOR + avroRecord.get("_row_key").toString() + "," + + "timestamp" + ComplexAvroKeyGenerator.DEFAULT_RECORD_KEY_SEPARATOR + avroRecord.get("timestamp").toString(); + String partitionPath = avroRecord.get("rider").toString() + "/" + avroRecord.get("driver").toString(); + HoodieKey hoodieKey = compositeKeyGenerator.getKey(avroRecord); + assertEquals(rowKey, hoodieKey.getRecordKey()); + assertEquals(partitionPath, hoodieKey.getPartitionPath()); + + Row row = KeyGeneratorTestUtilities.getRow(avroRecord, TRIP_STRUCT_TYPE); + assertEquals(partitionPath, compositeKeyGenerator.getPartitionPath(row)); + + InternalRow internalRow = KeyGeneratorTestUtilities.getInternalRow(row); + assertEquals(UTF8String.fromString(rowKey), compositeKeyGenerator.getRecordKey(internalRow, TRIP_STRUCT_TYPE)); + assertEquals(UTF8String.fromString(partitionPath), compositeKeyGenerator.getPartitionPath(internalRow, row.schema())); + } + } + + @ParameterizedTest + @CsvSource(value = {"false,true,8", "true,false,8", "true,true,8", "false,true,9", "true,false,9", "true,true,9"}) + void testMultipleValueKeyGeneratorNonPartitioned(boolean setNewEncodingConfig, + boolean encodeSingleKeyFieldValueOnly, + String tableVersion) { + TypedProperties properties = new TypedProperties(); + properties.setProperty(KeyGeneratorOptions.RECORDKEY_FIELD_NAME.key(), "_row_key,timestamp"); + properties.setProperty(KeyGeneratorOptions.PARTITIONPATH_FIELD_NAME.key(), ""); + if (setNewEncodingConfig) { + properties.setProperty( + HoodieWriteConfig.COMPLEX_KEYGEN_NEW_ENCODING.key(), + String.valueOf(encodeSingleKeyFieldValueOnly)); + } + ComplexKeyGenerator compositeKeyGenerator = new ComplexKeyGenerator(properties); + assertEquals(2, compositeKeyGenerator.getRecordKeyFieldNames().size()); + assertEquals(0, compositeKeyGenerator.getPartitionPathFields().size()); + try (HoodieTestDataGenerator dataGenerator = new HoodieTestDataGenerator(System.currentTimeMillis())) { + GenericRecord avroRecord = dataGenerator.generateGenericRecords(1).get(0); + String rowKey = + "_row_key" + ComplexAvroKeyGenerator.DEFAULT_RECORD_KEY_SEPARATOR + avroRecord.get("_row_key").toString() + "," + + "timestamp" + ComplexAvroKeyGenerator.DEFAULT_RECORD_KEY_SEPARATOR + avroRecord.get("timestamp").toString(); + String partitionPath = ""; + HoodieKey hoodieKey = compositeKeyGenerator.getKey(avroRecord); + assertEquals(rowKey, hoodieKey.getRecordKey()); + assertEquals(partitionPath, hoodieKey.getPartitionPath()); + + Row row = KeyGeneratorTestUtilities.getRow(avroRecord, TRIP_STRUCT_TYPE); + assertEquals(partitionPath, compositeKeyGenerator.getPartitionPath(row)); + + InternalRow internalRow = KeyGeneratorTestUtilities.getInternalRow(row); + assertEquals(UTF8String.fromString(rowKey), compositeKeyGenerator.getRecordKey(internalRow, TRIP_STRUCT_TYPE)); + assertEquals(UTF8String.fromString(partitionPath), compositeKeyGenerator.getPartitionPath(internalRow, row.schema())); + } + } +} diff --git a/hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/keygen/TestCustomKeyGenerator.java b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/keygen/TestCustomKeyGenerator.java similarity index 63% rename from hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/keygen/TestCustomKeyGenerator.java rename to hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/keygen/TestCustomKeyGenerator.java index 0ba8d1425e725..18a4c33c93be3 100644 --- a/hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/keygen/TestCustomKeyGenerator.java +++ b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/keygen/TestCustomKeyGenerator.java @@ -24,20 +24,21 @@ import org.apache.hudi.keygen.constant.KeyGeneratorOptions; import org.apache.hudi.keygen.constant.KeyGeneratorType; import org.apache.hudi.keygen.factory.HoodieSparkKeyGeneratorFactory; -import org.apache.hudi.testutils.KeyGeneratorTestUtilities; import org.apache.avro.generic.GenericRecord; import org.apache.spark.sql.Row; import org.apache.spark.sql.catalyst.InternalRow; import org.apache.spark.unsafe.types.UTF8String; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; -public class TestCustomKeyGenerator extends KeyGeneratorTestUtilities { +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +class TestCustomKeyGenerator extends KeyGeneratorTestUtilities { /** * Method to create props used for common cases. @@ -133,87 +134,87 @@ private String stackTraceToString(Throwable e) { } @Test - public void testSimpleKeyGeneratorWithKeyGeneratorClass() throws IOException { + void testSimpleKeyGeneratorWithKeyGeneratorClass() { testSimpleKeyGenerator(getPropertiesForSimpleKeyGen(true)); } @Test - public void testSimpleKeyGeneratorWithKeyGeneratorType() throws IOException { + void testSimpleKeyGeneratorWithKeyGeneratorType() { testSimpleKeyGenerator(getPropertiesForSimpleKeyGen(false)); } - public void testSimpleKeyGenerator(TypedProperties props) throws IOException { + public void testSimpleKeyGenerator(TypedProperties props) { BuiltinKeyGenerator keyGenerator = (BuiltinKeyGenerator) HoodieSparkKeyGeneratorFactory.createKeyGenerator(props); - GenericRecord record = getRecord(); - HoodieKey key = keyGenerator.getKey(record); - Assertions.assertEquals("key1", key.getRecordKey()); - Assertions.assertEquals("timestamp=4357686", key.getPartitionPath()); - Row row = KeyGeneratorTestUtilities.getRow(record); - Assertions.assertEquals("key1", keyGenerator.getRecordKey(row)); - Assertions.assertEquals("timestamp=4357686", keyGenerator.getPartitionPath(row)); + GenericRecord avroRecord = getRecord(); + HoodieKey key = keyGenerator.getKey(avroRecord); + assertEquals("key1", key.getRecordKey()); + assertEquals("timestamp=4357686", key.getPartitionPath()); + Row row = KeyGeneratorTestUtilities.getRow(avroRecord); + assertEquals("key1", keyGenerator.getRecordKey(row)); + assertEquals("timestamp=4357686", keyGenerator.getPartitionPath(row)); InternalRow internalRow = KeyGeneratorTestUtilities.getInternalRow(row); - Assertions.assertEquals(UTF8String.fromString("timestamp=4357686"), keyGenerator.getPartitionPath(internalRow, row.schema())); + assertEquals(UTF8String.fromString("timestamp=4357686"), keyGenerator.getPartitionPath(internalRow, row.schema())); } @Test - public void testTimestampBasedKeyGeneratorWithKeyGeneratorClass() throws IOException { + void testTimestampBasedKeyGeneratorWithKeyGeneratorClass() { testTimestampBasedKeyGenerator(getPropertiesForTimestampBasedKeyGen(true)); } @Test - public void testTimestampBasedKeyGeneratorWithKeyGeneratorType() throws IOException { + void testTimestampBasedKeyGeneratorWithKeyGeneratorType() { testTimestampBasedKeyGenerator(getPropertiesForTimestampBasedKeyGen(false)); } - public void testTimestampBasedKeyGenerator(TypedProperties props) throws IOException { + public void testTimestampBasedKeyGenerator(TypedProperties props) { BuiltinKeyGenerator keyGenerator = (BuiltinKeyGenerator) HoodieSparkKeyGeneratorFactory.createKeyGenerator(props); - GenericRecord record = getRecord(); - HoodieKey key = keyGenerator.getKey(record); - Assertions.assertEquals("key1", key.getRecordKey()); - Assertions.assertEquals("ts_ms=20200321", key.getPartitionPath()); - Row row = KeyGeneratorTestUtilities.getRow(record); - Assertions.assertEquals("key1", keyGenerator.getRecordKey(row)); - Assertions.assertEquals("ts_ms=20200321", keyGenerator.getPartitionPath(row)); + GenericRecord avroRecord = getRecord(); + HoodieKey key = keyGenerator.getKey(avroRecord); + assertEquals("key1", key.getRecordKey()); + assertEquals("ts_ms=20200321", key.getPartitionPath()); + Row row = KeyGeneratorTestUtilities.getRow(avroRecord); + assertEquals("key1", keyGenerator.getRecordKey(row)); + assertEquals("ts_ms=20200321", keyGenerator.getPartitionPath(row)); InternalRow internalRow = KeyGeneratorTestUtilities.getInternalRow(row); - Assertions.assertEquals(UTF8String.fromString("ts_ms=20200321"), keyGenerator.getPartitionPath(internalRow, row.schema())); + assertEquals(UTF8String.fromString("ts_ms=20200321"), keyGenerator.getPartitionPath(internalRow, row.schema())); } @Test - public void testNonPartitionedKeyGeneratorWithKeyGeneratorClass() throws IOException { + void testNonPartitionedKeyGeneratorWithKeyGeneratorClass() { testNonPartitionedKeyGenerator(getPropertiesForNonPartitionedKeyGen(true)); } @Test - public void testNonPartitionedKeyGeneratorWithKeyGeneratorType() throws IOException { + void testNonPartitionedKeyGeneratorWithKeyGeneratorType() { testNonPartitionedKeyGenerator(getPropertiesForNonPartitionedKeyGen(false)); } - public void testNonPartitionedKeyGenerator(TypedProperties props) throws IOException { + public void testNonPartitionedKeyGenerator(TypedProperties props) { BuiltinKeyGenerator keyGenerator = (BuiltinKeyGenerator) HoodieSparkKeyGeneratorFactory.createKeyGenerator(props); - GenericRecord record = getRecord(); - HoodieKey key = keyGenerator.getKey(record); - Assertions.assertEquals(key.getRecordKey(), "key1"); - Assertions.assertTrue(key.getPartitionPath().isEmpty()); - Row row = KeyGeneratorTestUtilities.getRow(record); - Assertions.assertEquals(keyGenerator.getRecordKey(row), "key1"); - Assertions.assertTrue(keyGenerator.getPartitionPath(row).isEmpty()); + GenericRecord avroRecord = getRecord(); + HoodieKey key = keyGenerator.getKey(avroRecord); + assertEquals("key1", key.getRecordKey()); + assertTrue(key.getPartitionPath().isEmpty()); + Row row = KeyGeneratorTestUtilities.getRow(avroRecord); + assertEquals("key1", keyGenerator.getRecordKey(row)); + assertTrue(keyGenerator.getPartitionPath(row).isEmpty()); InternalRow internalRow = KeyGeneratorTestUtilities.getInternalRow(row); - Assertions.assertEquals(0, keyGenerator.getPartitionPath(internalRow, row.schema()).numBytes()); + assertEquals(0, keyGenerator.getPartitionPath(internalRow, row.schema()).numBytes()); } @Test - public void testInvalidPartitionKeyTypeWithKeyGeneratorClass() { + void testInvalidPartitionKeyTypeWithKeyGeneratorClass() { testInvalidPartitionKeyType(getInvalidPartitionKeyTypeProps(true)); } @Test - public void testInvalidPartitionKeyTypeWithKeyGeneratorType() { + void testInvalidPartitionKeyTypeWithKeyGeneratorType() { testInvalidPartitionKeyType(getInvalidPartitionKeyTypeProps(false)); } @@ -223,31 +224,31 @@ public void testInvalidPartitionKeyType(TypedProperties props) { (BuiltinKeyGenerator) HoodieSparkKeyGeneratorFactory.createKeyGenerator(props); keyGenerator.getKey(getRecord()); - Assertions.fail("should fail when invalid PartitionKeyType is provided!"); + fail("should fail when invalid PartitionKeyType is provided!"); } catch (Exception e) { - Assertions.assertTrue(getNestedConstructorErrorCause(e).getMessage().contains("No enum constant org.apache.hudi.keygen.CustomAvroKeyGenerator.PartitionKeyType.DUMMY")); + assertTrue(getNestedConstructorErrorCause(e).getMessage().contains("No enum constant org.apache.hudi.keygen.CustomAvroKeyGenerator.PartitionKeyType.DUMMY")); } try { BuiltinKeyGenerator keyGenerator = (BuiltinKeyGenerator) HoodieSparkKeyGeneratorFactory.createKeyGenerator(props); - GenericRecord record = getRecord(); - Row row = KeyGeneratorTestUtilities.getRow(record); + GenericRecord avroRecord = getRecord(); + Row row = KeyGeneratorTestUtilities.getRow(avroRecord); keyGenerator.getPartitionPath(row); - Assertions.fail("should fail when invalid PartitionKeyType is provided!"); + fail("should fail when invalid PartitionKeyType is provided!"); } catch (Exception e) { - Assertions.assertTrue(getNestedConstructorErrorCause(e).getMessage().contains("No enum constant org.apache.hudi.keygen.CustomAvroKeyGenerator.PartitionKeyType.DUMMY")); + assertTrue(getNestedConstructorErrorCause(e).getMessage().contains("No enum constant org.apache.hudi.keygen.CustomAvroKeyGenerator.PartitionKeyType.DUMMY")); } } @Test - public void testNoRecordKeyFieldPropWithKeyGeneratorClass() { + void testNoRecordKeyFieldPropWithKeyGeneratorClass() { testNoRecordKeyFieldProp(true); } @Test - public void testNoRecordKeyFieldPropWithKeyGeneratorType() { + void testNoRecordKeyFieldPropWithKeyGeneratorType() { testNoRecordKeyFieldProp(false); } @@ -257,14 +258,14 @@ public void testNoRecordKeyFieldProp(boolean useKeyGeneratorClassName) { BuiltinKeyGenerator keyGenerator = new CustomKeyGenerator(propsWithoutRecordKeyFieldProps); keyGenerator.getKey(getRecord()); - Assertions.fail("should fail when record key field is not provided!"); + fail("should fail when record key field is not provided!"); } catch (Exception e) { if (useKeyGeneratorClassName) { // "Property hoodie.datasource.write.recordkey.field not found" exception cause CustomKeyGenerator init fail - Assertions.assertTrue(e.getMessage() + assertTrue(e.getMessage() .contains("Unable to find field names for record key in cfg")); } else { - Assertions.assertTrue(stackTraceToString(e).contains("Unable to find field names for record key in cfg")); + assertTrue(stackTraceToString(e).contains("Unable to find field names for record key in cfg")); } } @@ -272,28 +273,28 @@ public void testNoRecordKeyFieldProp(boolean useKeyGeneratorClassName) { try { BuiltinKeyGenerator keyGenerator = new CustomKeyGenerator(propsWithoutRecordKeyFieldProps); - GenericRecord record = getRecord(); - Row row = KeyGeneratorTestUtilities.getRow(record); + GenericRecord avroRecord = getRecord(); + Row row = KeyGeneratorTestUtilities.getRow(avroRecord); keyGenerator.getRecordKey(row); - Assertions.fail("should fail when record key field is not provided!"); + fail("should fail when record key field is not provided!"); } catch (Exception e) { if (useKeyGeneratorClassName) { // "Property hoodie.datasource.write.recordkey.field not found" exception cause CustomKeyGenerator init fail - Assertions.assertTrue(e.getMessage() + assertTrue(e.getMessage() .contains("All of the values for ([]) were either null or empty")); } else { - Assertions.assertTrue(stackTraceToString(e).contains("All of the values for ([]) were either null or empty")); + assertTrue(stackTraceToString(e).contains("All of the values for ([]) were either null or empty")); } } } @Test - public void testPartitionFieldsInImproperFormatWithKeyGeneratorClass() { + void testPartitionFieldsInImproperFormatWithKeyGeneratorClass() { testPartitionFieldsInImproperFormat(getImproperPartitionFieldFormatProp(true)); } @Test - public void testPartitionFieldsInImproperFormatWithKeyGeneratorType() { + void testPartitionFieldsInImproperFormatWithKeyGeneratorType() { testPartitionFieldsInImproperFormat(getImproperPartitionFieldFormatProp(false)); } @@ -303,76 +304,76 @@ public void testPartitionFieldsInImproperFormat(TypedProperties props) { (BuiltinKeyGenerator) HoodieSparkKeyGeneratorFactory.createKeyGenerator(props); keyGenerator.getKey(getRecord()); - Assertions.fail("should fail when partition key field is provided in improper format!"); + fail("should fail when partition key field is provided in improper format!"); } catch (Exception e) { - Assertions.assertTrue(getNestedConstructorErrorCause(e).getMessage().contains("Unable to find field names for partition path in proper format")); + assertTrue(getNestedConstructorErrorCause(e).getMessage().contains("Unable to find field names for partition path in proper format")); } try { BuiltinKeyGenerator keyGenerator = (BuiltinKeyGenerator) HoodieSparkKeyGeneratorFactory.createKeyGenerator(props); - GenericRecord record = getRecord(); - Row row = KeyGeneratorTestUtilities.getRow(record); + GenericRecord avroRecord = getRecord(); + Row row = KeyGeneratorTestUtilities.getRow(avroRecord); keyGenerator.getPartitionPath(row); - Assertions.fail("should fail when partition key field is provided in improper format!"); + fail("should fail when partition key field is provided in improper format!"); } catch (Exception e) { - Assertions.assertTrue(getNestedConstructorErrorCause(e).getMessage().contains("Unable to find field names for partition path in proper format")); + assertTrue(getNestedConstructorErrorCause(e).getMessage().contains("Unable to find field names for partition path in proper format")); } } @Test - public void testComplexRecordKeyWithSimplePartitionPathWithKeyGeneratorClass() throws IOException { + void testComplexRecordKeyWithSimplePartitionPathWithKeyGeneratorClass() { testComplexRecordKeyWithSimplePartitionPath(getComplexRecordKeyWithSimplePartitionProps(true)); } @Test - public void testComplexRecordKeyWithSimplePartitionPathWithKeyGeneratorType() throws IOException { + void testComplexRecordKeyWithSimplePartitionPathWithKeyGeneratorType() { testComplexRecordKeyWithSimplePartitionPath(getComplexRecordKeyWithSimplePartitionProps(false)); } - public void testComplexRecordKeyWithSimplePartitionPath(TypedProperties props) throws IOException { + public void testComplexRecordKeyWithSimplePartitionPath(TypedProperties props) { BuiltinKeyGenerator keyGenerator = (BuiltinKeyGenerator) HoodieSparkKeyGeneratorFactory.createKeyGenerator(props); - GenericRecord record = getRecord(); - HoodieKey key = keyGenerator.getKey(record); - Assertions.assertEquals("_row_key:key1,pii_col:pi", key.getRecordKey()); - Assertions.assertEquals("timestamp=4357686", key.getPartitionPath()); + GenericRecord avroRecord = getRecord(); + HoodieKey key = keyGenerator.getKey(avroRecord); + assertEquals("_row_key:key1,pii_col:pi", key.getRecordKey()); + assertEquals("timestamp=4357686", key.getPartitionPath()); - Row row = KeyGeneratorTestUtilities.getRow(record); - Assertions.assertEquals("_row_key:key1,pii_col:pi", keyGenerator.getRecordKey(row)); - Assertions.assertEquals("timestamp=4357686", keyGenerator.getPartitionPath(row)); + Row row = KeyGeneratorTestUtilities.getRow(avroRecord); + assertEquals("_row_key:key1,pii_col:pi", keyGenerator.getRecordKey(row)); + assertEquals("timestamp=4357686", keyGenerator.getPartitionPath(row)); InternalRow internalRow = KeyGeneratorTestUtilities.getInternalRow(row); - Assertions.assertEquals(UTF8String.fromString("timestamp=4357686"), keyGenerator.getPartitionPath(internalRow, row.schema())); + assertEquals(UTF8String.fromString("timestamp=4357686"), keyGenerator.getPartitionPath(internalRow, row.schema())); } @Test - public void testComplexRecordKeysWithComplexPartitionPathWithKeyGeneratorClass() throws IOException { + void testComplexRecordKeysWithComplexPartitionPathWithKeyGeneratorClass() { testComplexRecordKeysWithComplexPartitionPath(getComplexRecordKeyAndPartitionPathProps(true)); } @Test - public void testComplexRecordKeysWithComplexPartitionPathWithKeyGeneratorType() throws IOException { + void testComplexRecordKeysWithComplexPartitionPathWithKeyGeneratorType() { testComplexRecordKeysWithComplexPartitionPath(getComplexRecordKeyAndPartitionPathProps(false)); } - public void testComplexRecordKeysWithComplexPartitionPath(TypedProperties props) throws IOException { + public void testComplexRecordKeysWithComplexPartitionPath(TypedProperties props) { BuiltinKeyGenerator keyGenerator = (BuiltinKeyGenerator) HoodieSparkKeyGeneratorFactory.createKeyGenerator(props); - GenericRecord record = getRecord(); - HoodieKey key = keyGenerator.getKey(record); - Assertions.assertEquals("_row_key:key1,pii_col:pi", key.getRecordKey()); - Assertions.assertEquals("timestamp=4357686/ts_ms=20200321", key.getPartitionPath()); + GenericRecord avroRecord = getRecord(); + HoodieKey key = keyGenerator.getKey(avroRecord); + assertEquals("_row_key:key1,pii_col:pi", key.getRecordKey()); + assertEquals("timestamp=4357686/ts_ms=20200321", key.getPartitionPath()); - Row row = KeyGeneratorTestUtilities.getRow(record); - Assertions.assertEquals("_row_key:key1,pii_col:pi", keyGenerator.getRecordKey(row)); - Assertions.assertEquals("timestamp=4357686/ts_ms=20200321", keyGenerator.getPartitionPath(row)); + Row row = KeyGeneratorTestUtilities.getRow(avroRecord); + assertEquals("_row_key:key1,pii_col:pi", keyGenerator.getRecordKey(row)); + assertEquals("timestamp=4357686/ts_ms=20200321", keyGenerator.getPartitionPath(row)); InternalRow internalRow = KeyGeneratorTestUtilities.getInternalRow(row); - Assertions.assertEquals(UTF8String.fromString("timestamp=4357686/ts_ms=20200321"), keyGenerator.getPartitionPath(internalRow, row.schema())); + assertEquals(UTF8String.fromString("timestamp=4357686/ts_ms=20200321"), keyGenerator.getPartitionPath(internalRow, row.schema())); } private static Throwable getNestedConstructorErrorCause(Exception e) { diff --git a/hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/keygen/TestGlobalDeleteRecordGenerator.java b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/keygen/TestGlobalDeleteRecordGenerator.java similarity index 60% rename from hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/keygen/TestGlobalDeleteRecordGenerator.java rename to hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/keygen/TestGlobalDeleteRecordGenerator.java index df69279cc89f0..14b29760f764c 100644 --- a/hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/keygen/TestGlobalDeleteRecordGenerator.java +++ b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/keygen/TestGlobalDeleteRecordGenerator.java @@ -18,28 +18,19 @@ package org.apache.hudi.keygen; -import org.apache.avro.generic.GenericRecord; import org.apache.hudi.common.config.TypedProperties; import org.apache.hudi.common.model.HoodieKey; import org.apache.hudi.exception.HoodieKeyException; import org.apache.hudi.keygen.constant.KeyGeneratorOptions; -import org.apache.hudi.testutils.KeyGeneratorTestUtilities; + +import org.apache.avro.generic.GenericRecord; import org.apache.spark.sql.Row; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -public class TestGlobalDeleteRecordGenerator extends KeyGeneratorTestUtilities { +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; - private TypedProperties getCommonProps(boolean getComplexRecordKey) { - TypedProperties properties = new TypedProperties(); - if (getComplexRecordKey) { - properties.put(KeyGeneratorOptions.RECORDKEY_FIELD_NAME.key(), "_row_key,pii_col"); - } else { - properties.put(KeyGeneratorOptions.RECORDKEY_FIELD_NAME.key(), "_row_key"); - } - properties.put(KeyGeneratorOptions.HIVE_STYLE_PARTITIONING_ENABLE.key(), "true"); - return properties; - } +class TestGlobalDeleteRecordGenerator extends KeyGeneratorTestUtilities { private TypedProperties getPropertiesWithoutRecordKeyProp() { TypedProperties properties = new TypedProperties(); @@ -53,36 +44,54 @@ private TypedProperties getWrongRecordKeyFieldProps() { return properties; } - private TypedProperties getProps() { - TypedProperties properties = getCommonProps(true); + private TypedProperties getProps(boolean multipleRecordKeyFields) { + TypedProperties properties = new TypedProperties(); + if (multipleRecordKeyFields) { + properties.put(KeyGeneratorOptions.RECORDKEY_FIELD_NAME.key(), "_row_key,pii_col"); + } else { + properties.put(KeyGeneratorOptions.RECORDKEY_FIELD_NAME.key(), "_row_key"); + } + properties.put(KeyGeneratorOptions.HIVE_STYLE_PARTITIONING_ENABLE.key(), "true"); properties.put(KeyGeneratorOptions.PARTITIONPATH_FIELD_NAME.key(), "timestamp,ts_ms"); return properties; } @Test public void testNullRecordKeyFields() { - GenericRecord record = getRecord(); - Assertions.assertThrows(StringIndexOutOfBoundsException.class, () -> { + GenericRecord avroRecord = getRecord(); + assertThrows(StringIndexOutOfBoundsException.class, () -> { BaseKeyGenerator keyGenerator = new GlobalDeleteKeyGenerator(getPropertiesWithoutRecordKeyProp()); - keyGenerator.getRecordKey(record); + keyGenerator.getRecordKey(avroRecord); }); } @Test - public void testWrongRecordKeyField() { + void testWrongRecordKeyField() { GlobalDeleteKeyGenerator keyGenerator = new GlobalDeleteKeyGenerator(getWrongRecordKeyFieldProps()); - Assertions.assertThrows(HoodieKeyException.class, () -> keyGenerator.getRecordKey(getRecord())); + assertThrows(HoodieKeyException.class, () -> keyGenerator.getRecordKey(getRecord())); + } + + @Test + void testSingleRecordKeyField() { + GlobalDeleteKeyGenerator keyGenerator = new GlobalDeleteKeyGenerator(getProps(false)); + GenericRecord avroRecord = getRecord(); + HoodieKey key = keyGenerator.getKey(avroRecord); + assertEquals("_row_key:key1", key.getRecordKey()); + assertEquals("", key.getPartitionPath()); + Row row = KeyGeneratorTestUtilities.getRow(avroRecord); + assertEquals("_row_key:key1", keyGenerator.getRecordKey(row)); + assertEquals("", keyGenerator.getPartitionPath(row)); } @Test - public void testHappyFlow() { - GlobalDeleteKeyGenerator keyGenerator = new GlobalDeleteKeyGenerator(getProps()); - GenericRecord record = getRecord(); - HoodieKey key = keyGenerator.getKey(record); - Assertions.assertEquals(key.getRecordKey(), "_row_key:key1,pii_col:pi"); - Assertions.assertEquals(key.getPartitionPath(), ""); - Row row = KeyGeneratorTestUtilities.getRow(record); - Assertions.assertEquals(keyGenerator.getRecordKey(row), "_row_key:key1,pii_col:pi"); - Assertions.assertEquals(keyGenerator.getPartitionPath(row), ""); + void testMultipleRecordKeyFields() { + GlobalDeleteKeyGenerator keyGenerator = new GlobalDeleteKeyGenerator(getProps(true)); + GenericRecord avroRecord = getRecord(); + HoodieKey key = keyGenerator.getKey(avroRecord); + assertEquals("_row_key:key1,pii_col:pi", key.getRecordKey()); + assertEquals("", key.getPartitionPath()); + Row row = KeyGeneratorTestUtilities.getRow(avroRecord); + assertEquals("_row_key:key1,pii_col:pi", keyGenerator.getRecordKey(row)); + assertEquals("", keyGenerator.getPartitionPath(row)); } } diff --git a/hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/keygen/TestNonpartitionedKeyGenerator.java b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/keygen/TestNonpartitionedKeyGenerator.java similarity index 60% rename from hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/keygen/TestNonpartitionedKeyGenerator.java rename to hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/keygen/TestNonpartitionedKeyGenerator.java index fb740d00e2a5e..b5545f501a1d2 100644 --- a/hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/keygen/TestNonpartitionedKeyGenerator.java +++ b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/keygen/TestNonpartitionedKeyGenerator.java @@ -18,20 +18,20 @@ package org.apache.hudi.keygen; -import org.apache.avro.generic.GenericRecord; import org.apache.hudi.common.config.TypedProperties; import org.apache.hudi.common.model.HoodieKey; import org.apache.hudi.common.testutils.HoodieTestDataGenerator; import org.apache.hudi.exception.HoodieKeyException; import org.apache.hudi.keygen.constant.KeyGeneratorOptions; -import org.apache.hudi.testutils.KeyGeneratorTestUtilities; + +import org.apache.avro.generic.GenericRecord; import org.apache.spark.sql.Row; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import static junit.framework.TestCase.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; -public class TestNonpartitionedKeyGenerator extends KeyGeneratorTestUtilities { +class TestNonpartitionedKeyGenerator extends KeyGeneratorTestUtilities { private TypedProperties getCommonProps(boolean getComplexRecordKey) { TypedProperties properties = new TypedProperties(); @@ -67,72 +67,74 @@ private TypedProperties getWrongRecordKeyFieldProps() { } @Test - public void testNullRecordKeyFields() { - GenericRecord record = getRecord(); - Assertions.assertThrows(StringIndexOutOfBoundsException.class, () -> { + void testNullRecordKeyFields() { + GenericRecord avroRecord = getRecord(); + assertThrows(StringIndexOutOfBoundsException.class, () -> { BaseKeyGenerator keyGenerator = new NonpartitionedKeyGenerator(getPropertiesWithoutRecordKeyProp()); - keyGenerator.getRecordKey(record); + keyGenerator.getRecordKey(avroRecord); }); } @Test - public void testNonNullPartitionPathFields() { + void testNonNullPartitionPathFields() { TypedProperties properties = getPropertiesWithPartitionPathProp(); NonpartitionedKeyGenerator keyGenerator = new NonpartitionedKeyGenerator(properties); - GenericRecord record = getRecord(); - Row row = KeyGeneratorTestUtilities.getRow(record); - Assertions.assertEquals(properties.getString(KeyGeneratorOptions.PARTITIONPATH_FIELD_NAME.key()), "timestamp,ts_ms"); - Assertions.assertEquals(keyGenerator.getPartitionPath(row), ""); + GenericRecord avroRecord = getRecord(); + Row row = KeyGeneratorTestUtilities.getRow(avroRecord); + assertEquals("timestamp,ts_ms", properties.getString(KeyGeneratorOptions.PARTITIONPATH_FIELD_NAME.key())); + assertEquals("", keyGenerator.getPartitionPath(row)); } @Test - public void testNullPartitionPathFields() { + void testNullPartitionPathFields() { TypedProperties properties = getPropertiesWithoutPartitionPathProp(); NonpartitionedKeyGenerator keyGenerator = new NonpartitionedKeyGenerator(properties); - GenericRecord record = getRecord(); - Row row = KeyGeneratorTestUtilities.getRow(record); - Assertions.assertEquals(keyGenerator.getPartitionPath(row), ""); + GenericRecord avroRecord = getRecord(); + Row row = KeyGeneratorTestUtilities.getRow(avroRecord); + assertEquals("", keyGenerator.getPartitionPath(row)); } @Test - public void testWrongRecordKeyField() { + void testWrongRecordKeyField() { NonpartitionedKeyGenerator keyGenerator = new NonpartitionedKeyGenerator(getWrongRecordKeyFieldProps()); - Assertions.assertThrows(HoodieKeyException.class, () -> keyGenerator.getRecordKey(getRecord())); + assertThrows(HoodieKeyException.class, () -> keyGenerator.getRecordKey(getRecord())); } @Test - public void testSingleValueKeyGeneratorNonPartitioned() { + void testSingleValueKeyGeneratorNonPartitioned() { TypedProperties properties = new TypedProperties(); properties.setProperty(KeyGeneratorOptions.RECORDKEY_FIELD_NAME.key(), "timestamp"); properties.setProperty(KeyGeneratorOptions.PARTITIONPATH_FIELD_NAME.key(), ""); NonpartitionedKeyGenerator keyGenerator = new NonpartitionedKeyGenerator(properties); - assertEquals(keyGenerator.getRecordKeyFieldNames().size(), 1); - assertEquals(keyGenerator.getPartitionPathFields().size(), 0); - - HoodieTestDataGenerator dataGenerator = new HoodieTestDataGenerator(); - GenericRecord record = dataGenerator.generateGenericRecords(1).get(0); - String rowKey = record.get("timestamp").toString(); - HoodieKey hoodieKey = keyGenerator.getKey(record); - assertEquals(rowKey, hoodieKey.getRecordKey()); - assertEquals("", hoodieKey.getPartitionPath()); + assertEquals(1, keyGenerator.getRecordKeyFieldNames().size()); + assertEquals(0, keyGenerator.getPartitionPathFields().size()); + + try (HoodieTestDataGenerator dataGenerator = new HoodieTestDataGenerator(System.currentTimeMillis())) { + GenericRecord avroRecord = dataGenerator.generateGenericRecords(1).get(0); + String rowKey = avroRecord.get("timestamp").toString(); + HoodieKey hoodieKey = keyGenerator.getKey(avroRecord); + assertEquals(rowKey, hoodieKey.getRecordKey()); + assertEquals("", hoodieKey.getPartitionPath()); + } } @Test - public void testMultipleValueKeyGeneratorNonPartitioned1() { + void testMultipleValueKeyGeneratorNonPartitioned1() { TypedProperties properties = new TypedProperties(); properties.setProperty(KeyGeneratorOptions.RECORDKEY_FIELD_NAME.key(), "timestamp,driver"); properties.setProperty(KeyGeneratorOptions.PARTITIONPATH_FIELD_NAME.key(), ""); NonpartitionedKeyGenerator keyGenerator = new NonpartitionedKeyGenerator(properties); - assertEquals(keyGenerator.getRecordKeyFieldNames().size(), 2); - assertEquals(keyGenerator.getPartitionPathFields().size(), 0); - HoodieTestDataGenerator dataGenerator = new HoodieTestDataGenerator(); - GenericRecord record = dataGenerator.generateGenericRecords(1).get(0); - String rowKey = - "timestamp" + ComplexAvroKeyGenerator.DEFAULT_RECORD_KEY_SEPARATOR + record.get("timestamp").toString() + "," - + "driver" + ComplexAvroKeyGenerator.DEFAULT_RECORD_KEY_SEPARATOR + record.get("driver").toString(); - String partitionPath = ""; - HoodieKey hoodieKey = keyGenerator.getKey(record); - assertEquals(rowKey, hoodieKey.getRecordKey()); - assertEquals(partitionPath, hoodieKey.getPartitionPath()); + assertEquals(2, keyGenerator.getRecordKeyFieldNames().size()); + assertEquals(0, keyGenerator.getPartitionPathFields().size()); + try (HoodieTestDataGenerator dataGenerator = new HoodieTestDataGenerator(System.currentTimeMillis())) { + GenericRecord avroRecord = dataGenerator.generateGenericRecords(1).get(0); + String rowKey = + "timestamp" + ComplexAvroKeyGenerator.DEFAULT_RECORD_KEY_SEPARATOR + avroRecord.get("timestamp").toString() + "," + + "driver" + ComplexAvroKeyGenerator.DEFAULT_RECORD_KEY_SEPARATOR + avroRecord.get("driver").toString(); + String partitionPath = ""; + HoodieKey hoodieKey = keyGenerator.getKey(avroRecord); + assertEquals(rowKey, hoodieKey.getRecordKey()); + assertEquals(partitionPath, hoodieKey.getPartitionPath()); + } } } diff --git a/hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/keygen/TestSimpleKeyGenerator.java b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/keygen/TestSimpleKeyGenerator.java similarity index 79% rename from hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/keygen/TestSimpleKeyGenerator.java rename to hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/keygen/TestSimpleKeyGenerator.java index adf522f8354b2..5c277d2e7b5da 100644 --- a/hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/keygen/TestSimpleKeyGenerator.java +++ b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/keygen/TestSimpleKeyGenerator.java @@ -7,24 +7,25 @@ * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. */ package org.apache.hudi.keygen; -import org.apache.avro.generic.GenericRecord; import org.apache.hudi.common.config.TypedProperties; import org.apache.hudi.common.model.HoodieKey; import org.apache.hudi.exception.HoodieException; import org.apache.hudi.exception.HoodieKeyException; import org.apache.hudi.keygen.constant.KeyGeneratorOptions; -import org.apache.hudi.testutils.KeyGeneratorTestUtilities; + +import org.apache.avro.generic.GenericRecord; import org.apache.spark.sql.Row; import org.apache.spark.sql.catalyst.InternalRow; import org.apache.spark.unsafe.types.UTF8String; @@ -38,7 +39,7 @@ import static org.apache.hudi.keygen.KeyGenUtils.HUDI_DEFAULT_PARTITION_PATH; import static org.junit.jupiter.api.Assertions.assertThrows; -public class TestSimpleKeyGenerator extends KeyGeneratorTestUtilities { +class TestSimpleKeyGenerator extends KeyGeneratorTestUtilities { private TypedProperties getCommonProps() { TypedProperties properties = new TypedProperties(); properties.put(KeyGeneratorOptions.RECORDKEY_FIELD_NAME.key(), "_row_key"); @@ -90,54 +91,52 @@ private TypedProperties getPropsWithNestedPartitionPathField() { } @Test - public void testNullPartitionPathFields() { + void testNullPartitionPathFields() { assertThrows(IllegalArgumentException.class, () -> new SimpleKeyGenerator(getPropertiesWithoutPartitionPathProp())); } @Test - public void testNullRecordKeyFields() { - GenericRecord record = getRecord(); + void testNullRecordKeyFields() { + GenericRecord avroRecord = getRecord(); Assertions.assertThrows(IndexOutOfBoundsException.class, () -> { BaseKeyGenerator keyGenerator = new SimpleKeyGenerator(getPropertiesWithoutRecordKeyProp()); - keyGenerator.getRecordKey(record); + keyGenerator.getRecordKey(avroRecord); }); } @Test - public void testWrongRecordKeyField() { + void testWrongRecordKeyField() { SimpleKeyGenerator keyGenerator = new SimpleKeyGenerator(getWrongRecordKeyFieldProps()); assertThrows(HoodieKeyException.class, () -> keyGenerator.getRecordKey(getRecord())); } @Test - public void testWrongPartitionPathField() { + void testWrongPartitionPathField() { SimpleKeyGenerator keyGenerator = new SimpleKeyGenerator(getWrongPartitionPathFieldProps()); - GenericRecord record = getRecord(); + GenericRecord avroRecord = getRecord(); // TODO this should throw as well //assertThrows(HoodieException.class, () -> { // keyGenerator.getPartitionPath(record); //}); - assertThrows(HoodieException.class, () -> { - keyGenerator.getPartitionPath(KeyGeneratorTestUtilities.getRow(record)); - }); + assertThrows(HoodieException.class, + () -> keyGenerator.getPartitionPath(KeyGeneratorTestUtilities.getRow(avroRecord))); } @Test - public void testComplexRecordKeyField() { - assertThrows(IllegalArgumentException.class, () -> { - new SimpleKeyGenerator(getComplexRecordKeyProp()); - }); + void testComplexRecordKeyField() { + assertThrows(IllegalArgumentException.class, + () -> new SimpleKeyGenerator(getComplexRecordKeyProp())); } @Test - public void testHappyFlow() { + void testHappyFlow() { SimpleKeyGenerator keyGenerator = new SimpleKeyGenerator(getProps()); - GenericRecord record = getRecord(); + GenericRecord avroRecord = getRecord(); HoodieKey key = keyGenerator.getKey(getRecord()); Assertions.assertEquals("key1", key.getRecordKey()); Assertions.assertEquals("timestamp=4357686", key.getPartitionPath()); - Row row = KeyGeneratorTestUtilities.getRow(record); + Row row = KeyGeneratorTestUtilities.getRow(avroRecord); Assertions.assertEquals("key1", keyGenerator.getRecordKey(row)); Assertions.assertEquals("timestamp=4357686", keyGenerator.getPartitionPath(row)); @@ -152,20 +151,20 @@ private static Stream nestedColTestRecords() { @ParameterizedTest @MethodSource("nestedColTestRecords") - public void testNestedPartitionPathField(GenericRecord nestedColRecord) { + void testNestedPartitionPathField(GenericRecord nestedColRecord) { SimpleKeyGenerator keyGenerator = new SimpleKeyGenerator(getPropsWithNestedPartitionPathField()); - GenericRecord record = getRecord(nestedColRecord); + GenericRecord avroRecord = getRecord(nestedColRecord); String partitionPathFieldValue = null; if (nestedColRecord != null) { partitionPathFieldValue = (String) nestedColRecord.get("prop1"); } String expectedPartitionPath = "nested_col.prop1=" + (partitionPathFieldValue != null && !partitionPathFieldValue.isEmpty() ? partitionPathFieldValue : HUDI_DEFAULT_PARTITION_PATH); - HoodieKey key = keyGenerator.getKey(record); + HoodieKey key = keyGenerator.getKey(avroRecord); Assertions.assertEquals("key1", key.getRecordKey()); Assertions.assertEquals(expectedPartitionPath, key.getPartitionPath()); - Row row = KeyGeneratorTestUtilities.getRow(record); + Row row = KeyGeneratorTestUtilities.getRow(avroRecord); Assertions.assertEquals("key1", keyGenerator.getRecordKey(row)); Assertions.assertEquals(expectedPartitionPath, keyGenerator.getPartitionPath(row)); } diff --git a/hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/keygen/TestTimestampBasedKeyGenerator.java b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/keygen/TestTimestampBasedKeyGenerator.java similarity index 79% rename from hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/keygen/TestTimestampBasedKeyGenerator.java rename to hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/keygen/TestTimestampBasedKeyGenerator.java index ec93ea229accb..136ed7c4b5107 100644 --- a/hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/keygen/TestTimestampBasedKeyGenerator.java +++ b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/keygen/TestTimestampBasedKeyGenerator.java @@ -18,14 +18,12 @@ package org.apache.hudi.keygen; -import org.apache.hudi.AvroConversionUtils; import org.apache.hudi.avro.AvroSchemaUtils; import org.apache.hudi.common.config.TypedProperties; import org.apache.hudi.common.model.HoodieKey; import org.apache.hudi.common.testutils.SchemaTestUtil; import org.apache.hudi.exception.HoodieKeyGeneratorException; import org.apache.hudi.keygen.constant.KeyGeneratorOptions; -import org.apache.hudi.testutils.KeyGeneratorTestUtilities; import org.apache.avro.Conversions; import org.apache.avro.LogicalTypes; @@ -34,18 +32,17 @@ import org.apache.avro.generic.GenericRecord; import org.apache.spark.sql.Row; import org.apache.spark.sql.catalyst.InternalRow; -import org.apache.spark.sql.catalyst.expressions.GenericRowWithSchema; +import org.apache.spark.sql.types.DecimalType; +import org.apache.spark.sql.types.Metadata; +import org.apache.spark.sql.types.StructField; import org.apache.spark.sql.types.StructType; import org.apache.spark.unsafe.types.UTF8String; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.io.IOException; import java.math.BigDecimal; -import scala.Function1; - import static org.apache.hudi.common.config.TimestampKeyGeneratorConfig.TIMESTAMP_INPUT_DATE_FORMAT; import static org.apache.hudi.common.config.TimestampKeyGeneratorConfig.TIMESTAMP_INPUT_DATE_FORMAT_LIST_DELIMITER_REGEX; import static org.apache.hudi.common.config.TimestampKeyGeneratorConfig.TIMESTAMP_INPUT_TIMEZONE_FORMAT; @@ -53,9 +50,13 @@ import static org.apache.hudi.common.config.TimestampKeyGeneratorConfig.TIMESTAMP_OUTPUT_TIMEZONE_FORMAT; import static org.apache.hudi.common.config.TimestampKeyGeneratorConfig.TIMESTAMP_TIMEZONE_FORMAT; import static org.apache.hudi.common.config.TimestampKeyGeneratorConfig.TIMESTAMP_TYPE_FIELD; +import static org.apache.spark.sql.types.DataTypes.LongType; +import static org.apache.spark.sql.types.DataTypes.StringType; +import static org.apache.spark.sql.types.DataTypes.TimestampType; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; -public class TestTimestampBasedKeyGenerator { +class TestTimestampBasedKeyGenerator { private GenericRecord baseRecord; private TypedProperties properties = new TypedProperties(); @@ -66,9 +67,14 @@ public class TestTimestampBasedKeyGenerator { private InternalRow internalRow; @BeforeEach - public void initialize() throws IOException { + void initialize() throws IOException { schema = SchemaTestUtil.getTimestampEvolvedSchema(); - structType = AvroConversionUtils.convertAvroSchemaToStructType(schema); + structType = new StructType(new StructField[] { + new StructField("field1", StringType, true, Metadata.empty()), + new StructField("createTime", LongType, true, Metadata.empty()), + new StructField("createTimeString", StringType, true, Metadata.empty()), + new StructField("createTimeDecimal", new DecimalType(20, 4), true, Metadata.empty()) + }); baseRecord = SchemaTestUtil .generateAvroRecordFromJson(schema, 1, "001", "f1"); baseRow = genericRecordToRow(baseRecord); @@ -80,29 +86,22 @@ public void initialize() throws IOException { } private Row genericRecordToRow(GenericRecord baseRecord) { - Function1 convertor = AvroConversionUtils.createConverterToRow(baseRecord.getSchema(), structType); - Row row = convertor.apply(baseRecord); - int fieldCount = structType.fieldNames().length; - Object[] values = new Object[fieldCount]; - for (int i = 0; i < fieldCount; i++) { - values[i] = row.get(i); - } - return new GenericRowWithSchema(values, structType); + return KeyGeneratorTestUtilities.genericRecordToRow(baseRecord, structType); } private TypedProperties getBaseKeyConfig(String partitionPathField, String timestampType, String dateFormat, String timezone, String scalarType) { - TypedProperties properties = new TypedProperties(this.properties); + TypedProperties newProperties = new TypedProperties(this.properties); - properties.setProperty(KeyGeneratorOptions.PARTITIONPATH_FIELD_NAME.key(), partitionPathField); - properties.setProperty(TIMESTAMP_TYPE_FIELD.key(), timestampType); - properties.setProperty(TIMESTAMP_OUTPUT_DATE_FORMAT.key(), dateFormat); - properties.setProperty(TIMESTAMP_TIMEZONE_FORMAT.key(), timezone); + newProperties.setProperty(KeyGeneratorOptions.PARTITIONPATH_FIELD_NAME.key(), partitionPathField); + newProperties.setProperty(TIMESTAMP_TYPE_FIELD.key(), timestampType); + newProperties.setProperty(TIMESTAMP_OUTPUT_DATE_FORMAT.key(), dateFormat); + newProperties.setProperty(TIMESTAMP_TIMEZONE_FORMAT.key(), timezone); if (scalarType != null) { - properties.setProperty("hoodie.deltastreamer.keygen.timebased.timestamp.scalar.time.unit", scalarType); + newProperties.setProperty("hoodie.deltastreamer.keygen.timebased.timestamp.scalar.time.unit", scalarType); } - return properties; + return newProperties; } private TypedProperties getBaseKeyConfig(String partitionPathField, @@ -112,33 +111,33 @@ private TypedProperties getBaseKeyConfig(String partitionPathField, String inputTimezone, String outputFormat, String outputTimezone) { - TypedProperties properties = new TypedProperties(this.properties); + TypedProperties newProperties = new TypedProperties(this.properties); - properties.setProperty(KeyGeneratorOptions.PARTITIONPATH_FIELD_NAME.key(), partitionPathField); + newProperties.setProperty(KeyGeneratorOptions.PARTITIONPATH_FIELD_NAME.key(), partitionPathField); if (timestampType != null) { - properties.setProperty(TIMESTAMP_TYPE_FIELD.key(), timestampType); + newProperties.setProperty(TIMESTAMP_TYPE_FIELD.key(), timestampType); } if (inputFormatList != null) { - properties.setProperty(TIMESTAMP_INPUT_DATE_FORMAT.key(), inputFormatList); + newProperties.setProperty(TIMESTAMP_INPUT_DATE_FORMAT.key(), inputFormatList); } if (inputFormatDelimiterRegex != null) { - properties.setProperty(TIMESTAMP_INPUT_DATE_FORMAT_LIST_DELIMITER_REGEX.key(), inputFormatDelimiterRegex); + newProperties.setProperty(TIMESTAMP_INPUT_DATE_FORMAT_LIST_DELIMITER_REGEX.key(), inputFormatDelimiterRegex); } if (inputTimezone != null) { - properties.setProperty(TIMESTAMP_INPUT_TIMEZONE_FORMAT.key(), inputTimezone); + newProperties.setProperty(TIMESTAMP_INPUT_TIMEZONE_FORMAT.key(), inputTimezone); } if (outputFormat != null) { - properties.setProperty(TIMESTAMP_OUTPUT_DATE_FORMAT.key(), outputFormat); + newProperties.setProperty(TIMESTAMP_OUTPUT_DATE_FORMAT.key(), outputFormat); } if (outputTimezone != null) { - properties.setProperty(TIMESTAMP_OUTPUT_TIMEZONE_FORMAT.key(), outputTimezone); + newProperties.setProperty(TIMESTAMP_OUTPUT_TIMEZONE_FORMAT.key(), outputTimezone); } - return properties; + return newProperties; } @Test - public void testTimestampBasedKeyGenerator() throws IOException { + void testTimestampBasedKeyGenerator() throws IOException { // timezone is GMT+8:00 baseRecord.put("createTime", 1578283932000L); properties = getBaseKeyConfig("createTime", "EPOCHMILLISECONDS", "yyyy-MM-dd hh", "GMT+8:00", null); @@ -212,7 +211,7 @@ public void testTimestampBasedKeyGenerator() throws IOException { } @Test - public void testScalar() throws IOException { + void testScalar() throws IOException { // timezone is GMT+8:00 baseRecord.put("createTime", 20000L); @@ -220,7 +219,7 @@ public void testScalar() throws IOException { properties = getBaseKeyConfig("createTime", "SCALAR", "yyyy-MM-dd hh", "GMT", "days"); TimestampBasedKeyGenerator keyGen = new TimestampBasedKeyGenerator(properties); HoodieKey hk1 = keyGen.getKey(baseRecord); - assertEquals(hk1.getPartitionPath(), "2024-10-04 12"); + assertEquals("2024-10-04 12", hk1.getPartitionPath()); // test w/ Row baseRow = genericRecordToRow(baseRecord); @@ -254,9 +253,12 @@ public void testScalar() throws IOException { } @Test - public void testScalarWithLogicalType() throws IOException { + void testScalarWithLogicalType() throws IOException { schema = SchemaTestUtil.getTimestampWithLogicalTypeSchema(); - structType = AvroConversionUtils.convertAvroSchemaToStructType(schema); + structType = new StructType(new StructField[] { + new StructField("field1", StringType, true, Metadata.empty()), + new StructField("createTime", TimestampType, true, Metadata.empty()) + }); baseRecord = SchemaTestUtil.generateAvroRecordFromJson(schema, 1, "001", "f1"); baseRecord.put("createTime", 1638513806000000L); @@ -288,7 +290,7 @@ public void testScalarWithLogicalType() throws IOException { } @Test - public void test_ExpectsMatch_SingleInputFormat_ISO8601WithMsZ_OutputTimezoneAsUTC() throws IOException { + void test_ExpectsMatch_SingleInputFormat_ISO8601WithMsZ_OutputTimezoneAsUTC() throws IOException { baseRecord.put("createTimeString", "2020-04-01T13:01:33.428Z"); properties = this.getBaseKeyConfig( "createTimeString", @@ -300,14 +302,14 @@ public void test_ExpectsMatch_SingleInputFormat_ISO8601WithMsZ_OutputTimezoneAsU "GMT"); BuiltinKeyGenerator keyGen = new TimestampBasedKeyGenerator(properties); HoodieKey hk1 = keyGen.getKey(baseRecord); - Assertions.assertEquals("2020040113", hk1.getPartitionPath()); + assertEquals("2020040113", hk1.getPartitionPath()); baseRow = genericRecordToRow(baseRecord); assertEquals("2020040113", keyGen.getPartitionPath(baseRow)); } @Test - public void test_ExpectsMatch_SingleInputFormats_ISO8601WithMsZ_OutputTimezoneAsInputDateTimeZone() throws IOException { + void test_ExpectsMatch_SingleInputFormats_ISO8601WithMsZ_OutputTimezoneAsInputDateTimeZone() throws IOException { baseRecord.put("createTimeString", "2020-04-01T13:01:33.428Z"); properties = this.getBaseKeyConfig( "createTimeString", @@ -319,14 +321,14 @@ public void test_ExpectsMatch_SingleInputFormats_ISO8601WithMsZ_OutputTimezoneAs ""); BuiltinKeyGenerator keyGen = new TimestampBasedKeyGenerator(properties); HoodieKey hk1 = keyGen.getKey(baseRecord); - Assertions.assertEquals("2020040113", hk1.getPartitionPath()); + assertEquals("2020040113", hk1.getPartitionPath()); baseRow = genericRecordToRow(baseRecord); assertEquals("2020040113", keyGen.getPartitionPath(baseRow)); } @Test - public void test_ExpectsMatch_MultipleInputFormats_ISO8601WithMsZ_OutputTimezoneAsUTC() throws IOException { + void test_ExpectsMatch_MultipleInputFormats_ISO8601WithMsZ_OutputTimezoneAsUTC() throws IOException { baseRecord.put("createTimeString", "2020-04-01T13:01:33.428Z"); properties = this.getBaseKeyConfig( "createTimeString", @@ -338,14 +340,14 @@ public void test_ExpectsMatch_MultipleInputFormats_ISO8601WithMsZ_OutputTimezone "UTC"); BuiltinKeyGenerator keyGen = new TimestampBasedKeyGenerator(properties); HoodieKey hk1 = keyGen.getKey(baseRecord); - Assertions.assertEquals("2020040113", hk1.getPartitionPath()); + assertEquals("2020040113", hk1.getPartitionPath()); baseRow = genericRecordToRow(baseRecord); assertEquals("2020040113", keyGen.getPartitionPath(baseRow)); } @Test - public void test_ExpectsMatch_MultipleInputFormats_ISO8601NoMsZ_OutputTimezoneAsUTC() throws IOException { + void test_ExpectsMatch_MultipleInputFormats_ISO8601NoMsZ_OutputTimezoneAsUTC() throws IOException { baseRecord.put("createTimeString", "2020-04-01T13:01:33Z"); properties = this.getBaseKeyConfig( "createTimeString", @@ -357,14 +359,14 @@ public void test_ExpectsMatch_MultipleInputFormats_ISO8601NoMsZ_OutputTimezoneAs "UTC"); BuiltinKeyGenerator keyGen = new TimestampBasedKeyGenerator(properties); HoodieKey hk1 = keyGen.getKey(baseRecord); - Assertions.assertEquals("2020040113", hk1.getPartitionPath()); + assertEquals("2020040113", hk1.getPartitionPath()); baseRow = genericRecordToRow(baseRecord); assertEquals("2020040113", keyGen.getPartitionPath(baseRow)); } @Test - public void test_ExpectsMatch_MultipleInputFormats_ISO8601NoMsWithOffset_OutputTimezoneAsUTC() throws IOException { + void test_ExpectsMatch_MultipleInputFormats_ISO8601NoMsWithOffset_OutputTimezoneAsUTC() throws IOException { baseRecord.put("createTimeString", "2020-04-01T13:01:33-05:00"); properties = this.getBaseKeyConfig( "createTimeString", @@ -376,14 +378,14 @@ public void test_ExpectsMatch_MultipleInputFormats_ISO8601NoMsWithOffset_OutputT "UTC"); BuiltinKeyGenerator keyGen = new TimestampBasedKeyGenerator(properties); HoodieKey hk1 = keyGen.getKey(baseRecord); - Assertions.assertEquals("2020040118", hk1.getPartitionPath()); + assertEquals("2020040118", hk1.getPartitionPath()); baseRow = genericRecordToRow(baseRecord); assertEquals("2020040118", keyGen.getPartitionPath(baseRow)); } @Test - public void test_ExpectsMatch_MultipleInputFormats_ISO8601WithMsWithOffset_OutputTimezoneAsUTC() throws IOException { + void test_ExpectsMatch_MultipleInputFormats_ISO8601WithMsWithOffset_OutputTimezoneAsUTC() throws IOException { baseRecord.put("createTimeString", "2020-04-01T13:01:33.123-05:00"); properties = this.getBaseKeyConfig( "createTimeString", @@ -395,14 +397,14 @@ public void test_ExpectsMatch_MultipleInputFormats_ISO8601WithMsWithOffset_Outpu "UTC"); BuiltinKeyGenerator keyGen = new TimestampBasedKeyGenerator(properties); HoodieKey hk1 = keyGen.getKey(baseRecord); - Assertions.assertEquals("2020040118", hk1.getPartitionPath()); + assertEquals("2020040118", hk1.getPartitionPath()); baseRow = genericRecordToRow(baseRecord); assertEquals("2020040118", keyGen.getPartitionPath(baseRow)); } @Test - public void test_ExpectsMatch_MultipleInputFormats_ISO8601WithMsZ_OutputTimezoneAsEST() throws IOException { + void test_ExpectsMatch_MultipleInputFormats_ISO8601WithMsZ_OutputTimezoneAsEST() throws IOException { baseRecord.put("createTimeString", "2020-04-01T13:01:33.123Z"); properties = this.getBaseKeyConfig( "createTimeString", @@ -414,14 +416,14 @@ public void test_ExpectsMatch_MultipleInputFormats_ISO8601WithMsZ_OutputTimezone "EST"); BuiltinKeyGenerator keyGen = new TimestampBasedKeyGenerator(properties); HoodieKey hk1 = keyGen.getKey(baseRecord); - Assertions.assertEquals("2020040109", hk1.getPartitionPath()); + assertEquals("2020040109", hk1.getPartitionPath()); baseRow = genericRecordToRow(baseRecord); assertEquals("2020040109", keyGen.getPartitionPath(baseRow)); } @Test - public void test_Throws_MultipleInputFormats_InputDateNotMatchingFormats() throws IOException { + void test_Throws_MultipleInputFormats_InputDateNotMatchingFormats() throws IOException { baseRecord.put("createTimeString", "2020-04-01 13:01:33.123-05:00"); properties = this.getBaseKeyConfig( "createTimeString", @@ -432,14 +434,14 @@ public void test_Throws_MultipleInputFormats_InputDateNotMatchingFormats() throw "yyyyMMddHH", "UTC"); BuiltinKeyGenerator keyGen = new TimestampBasedKeyGenerator(properties); - Assertions.assertThrows(HoodieKeyGeneratorException.class, () -> keyGen.getKey(baseRecord)); + assertThrows(HoodieKeyGeneratorException.class, () -> keyGen.getKey(baseRecord)); baseRow = genericRecordToRow(baseRecord); - Assertions.assertThrows(HoodieKeyGeneratorException.class, () -> keyGen.getPartitionPath(baseRow)); + assertThrows(HoodieKeyGeneratorException.class, () -> keyGen.getPartitionPath(baseRow)); } @Test - public void test_ExpectsMatch_MultipleInputFormats_ShortDate_OutputCustomDate() throws IOException { + void test_ExpectsMatch_MultipleInputFormats_ShortDate_OutputCustomDate() throws IOException { baseRecord.put("createTimeString", "20200401"); properties = this.getBaseKeyConfig( "createTimeString", @@ -451,7 +453,7 @@ public void test_ExpectsMatch_MultipleInputFormats_ShortDate_OutputCustomDate() "UTC"); BuiltinKeyGenerator keyGen = new TimestampBasedKeyGenerator(properties); HoodieKey hk1 = keyGen.getKey(baseRecord); - Assertions.assertEquals("04/01/2020", hk1.getPartitionPath()); + assertEquals("04/01/2020", hk1.getPartitionPath()); baseRow = genericRecordToRow(baseRecord); assertEquals("04/01/2020", keyGen.getPartitionPath(baseRow)); diff --git a/hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/keygen/factory/TestCreateKeyGeneratorByTypeWithFactory.java b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/keygen/factory/TestCreateKeyGeneratorByTypeWithFactory.java similarity index 87% rename from hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/keygen/factory/TestCreateKeyGeneratorByTypeWithFactory.java rename to hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/keygen/factory/TestCreateKeyGeneratorByTypeWithFactory.java index dc597df2cf5c2..ec66d19778b08 100644 --- a/hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/keygen/factory/TestCreateKeyGeneratorByTypeWithFactory.java +++ b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/keygen/factory/TestCreateKeyGeneratorByTypeWithFactory.java @@ -7,13 +7,14 @@ * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. */ package org.apache.hudi.keygen.factory; @@ -40,10 +41,9 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import java.io.IOException; import java.util.stream.Stream; -public class TestCreateKeyGeneratorByTypeWithFactory { +class TestCreateKeyGeneratorByTypeWithFactory { private TypedProperties props; @@ -54,7 +54,7 @@ private static Stream configParams() { } @BeforeEach - public void init() { + void init() { props = new TypedProperties(); props.put(KeyGeneratorOptions.RECORDKEY_FIELD_NAME.key(), "_row_key"); props.put(KeyGeneratorOptions.HIVE_STYLE_PARTITIONING_ENABLE.key(), "true"); @@ -67,13 +67,13 @@ public void init() { } @AfterEach - public void teardown() { + void teardown() { props = null; } @ParameterizedTest @MethodSource("configParams") - public void testKeyGeneratorTypes(String keyGenType) throws IOException { + void testKeyGeneratorTypes(String keyGenType) { props.put(HoodieWriteConfig.KEYGENERATOR_TYPE.key(), keyGenType); KeyGeneratorType keyType = KeyGeneratorType.valueOf(keyGenType); @@ -107,7 +107,7 @@ public void testKeyGeneratorTypes(String keyGenType) throws IOException { } @Test - public void testAutoRecordKeyGenerator() throws IOException { + void testAutoRecordKeyGenerator() { props = new TypedProperties(); props.put(KeyGeneratorOptions.PARTITIONPATH_FIELD_NAME.key(), "partition"); props.put(KeyGenUtils.RECORD_KEY_GEN_INSTANT_TIME_CONFIG, "100"); diff --git a/hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/keygen/factory/TestHoodieSparkKeyGeneratorFactory.java b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/keygen/factory/TestHoodieSparkKeyGeneratorFactory.java similarity index 91% rename from hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/keygen/factory/TestHoodieSparkKeyGeneratorFactory.java rename to hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/keygen/factory/TestHoodieSparkKeyGeneratorFactory.java index 3cc30e86399f0..3c2bd1c4b68f6 100644 --- a/hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/keygen/factory/TestHoodieSparkKeyGeneratorFactory.java +++ b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/keygen/factory/TestHoodieSparkKeyGeneratorFactory.java @@ -7,13 +7,14 @@ * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. */ package org.apache.hudi.keygen.factory; @@ -45,7 +46,7 @@ */ public class TestHoodieSparkKeyGeneratorFactory { @Test - public void testInferKeyGeneratorTypeFromWriteConfig() { + void testInferKeyGeneratorTypeFromWriteConfig() { assertEquals( KeyGeneratorType.NON_PARTITION, HoodieSparkKeyGeneratorFactory.inferKeyGeneratorTypeFromWriteConfig(new TypedProperties())); @@ -67,7 +68,7 @@ public void testInferKeyGeneratorTypeFromWriteConfig() { } @Test - public void testKeyGeneratorFactory() throws IOException { + void testKeyGeneratorFactory() throws IOException { TypedProperties props = getCommonProps(); // set KeyGenerator type only diff --git a/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/testutils/SparkDatasetTestUtils.java b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/testutils/SparkDatasetTestUtils.java index 09e6bd699bce1..e20ad8f7bc942 100644 --- a/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/testutils/SparkDatasetTestUtils.java +++ b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/testutils/SparkDatasetTestUtils.java @@ -191,7 +191,7 @@ public static HoodieWriteConfig.Builder getConfigBuilder(String basePath, int ti .withBulkInsertParallelism(2); } - private static InternalRow serializeRow(ExpressionEncoder encoder, Row row) + public static InternalRow serializeRow(ExpressionEncoder encoder, Row row) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, ClassNotFoundException { // TODO remove reflection if Spark 2.x support is dropped if (package$.MODULE$.SPARK_VERSION().startsWith("2.")) { diff --git a/hudi-common/src/main/java/org/apache/hudi/common/config/ConfigProperty.java b/hudi-common/src/main/java/org/apache/hudi/common/config/ConfigProperty.java index d4ed193a0416a..f7e28655031ac 100644 --- a/hudi-common/src/main/java/org/apache/hudi/common/config/ConfigProperty.java +++ b/hudi-common/src/main/java/org/apache/hudi/common/config/ConfigProperty.java @@ -31,6 +31,7 @@ import java.util.Objects; import java.util.Set; import java.util.function.Function; +import java.util.stream.Collectors; /** * ConfigProperty describes a configuration property. It contains the configuration @@ -54,6 +55,8 @@ public class ConfigProperty implements Serializable { private final Option deprecatedVersion; + private final List supportedVersions; + private final Set validValues; private final boolean advanced; @@ -65,6 +68,7 @@ public class ConfigProperty implements Serializable { ConfigProperty(String key, T defaultValue, String docOnDefaultValue, String doc, Option sinceVersion, Option deprecatedVersion, + List supportedVersions, Option>> inferFunc, Set validValues, boolean advanced, String... alternatives) { this.key = Objects.requireNonNull(key); @@ -73,6 +77,7 @@ public class ConfigProperty implements Serializable { this.doc = doc; this.sinceVersion = sinceVersion; this.deprecatedVersion = deprecatedVersion; + this.supportedVersions = supportedVersions; this.inferFunction = inferFunc; this.validValues = validValues; this.advanced = advanced; @@ -111,6 +116,10 @@ public Option getDeprecatedVersion() { return deprecatedVersion; } + public List getSupportedVersions() { + return supportedVersions; + } + public boolean hasInferFunction() { return getInferFunction().isPresent(); } @@ -141,7 +150,7 @@ public boolean isAdvanced() { public ConfigProperty withDocumentation(String doc) { Objects.requireNonNull(doc); - return new ConfigProperty<>(key, defaultValue, docOnDefaultValue, doc, sinceVersion, deprecatedVersion, inferFunction, validValues, advanced, alternatives); + return new ConfigProperty<>(key, defaultValue, docOnDefaultValue, doc, sinceVersion, deprecatedVersion, supportedVersions, inferFunction, validValues, advanced, alternatives); } public > ConfigProperty withDocumentation(Class e) { @@ -184,39 +193,45 @@ public > ConfigProperty withDocumentation(Class e, Strin } } - return new ConfigProperty<>(key, defaultValue, docOnDefaultValue, sb.toString(), sinceVersion, deprecatedVersion, inferFunction, validValues, advanced, alternatives); + return new ConfigProperty<>(key, defaultValue, docOnDefaultValue, sb.toString(), sinceVersion, deprecatedVersion, supportedVersions, inferFunction, validValues, advanced, alternatives); } public ConfigProperty withValidValues(String... validValues) { Objects.requireNonNull(validValues); - return new ConfigProperty<>(key, defaultValue, docOnDefaultValue, doc, sinceVersion, deprecatedVersion, inferFunction, new HashSet<>(Arrays.asList(validValues)), advanced, alternatives); + return new ConfigProperty<>(key, defaultValue, docOnDefaultValue, doc, sinceVersion, deprecatedVersion, supportedVersions, inferFunction, new HashSet<>(Arrays.asList(validValues)), advanced, + alternatives); } public ConfigProperty withAlternatives(String... alternatives) { Objects.requireNonNull(alternatives); - return new ConfigProperty<>(key, defaultValue, docOnDefaultValue, doc, sinceVersion, deprecatedVersion, inferFunction, validValues, advanced, alternatives); + return new ConfigProperty<>(key, defaultValue, docOnDefaultValue, doc, sinceVersion, deprecatedVersion, supportedVersions, inferFunction, validValues, advanced, alternatives); } public ConfigProperty sinceVersion(String sinceVersion) { Objects.requireNonNull(sinceVersion); - return new ConfigProperty<>(key, defaultValue, docOnDefaultValue, doc, Option.of(sinceVersion), deprecatedVersion, inferFunction, validValues, advanced, alternatives); + return new ConfigProperty<>(key, defaultValue, docOnDefaultValue, doc, Option.of(sinceVersion), deprecatedVersion, supportedVersions, inferFunction, validValues, advanced, alternatives); } public ConfigProperty deprecatedAfter(String deprecatedVersion) { Objects.requireNonNull(deprecatedVersion); - return new ConfigProperty<>(key, defaultValue, docOnDefaultValue, doc, sinceVersion, Option.of(deprecatedVersion), inferFunction, validValues, advanced, alternatives); + return new ConfigProperty<>(key, defaultValue, docOnDefaultValue, doc, sinceVersion, Option.of(deprecatedVersion), supportedVersions, inferFunction, validValues, advanced, alternatives); + } + + public ConfigProperty supportedVersions(String... supportedVersions) { + return new ConfigProperty<>(key, defaultValue, docOnDefaultValue, doc, sinceVersion, deprecatedVersion, + Arrays.stream(supportedVersions).collect(Collectors.toList()), inferFunction, validValues, advanced, alternatives); } public ConfigProperty withInferFunction(Function> inferFunction) { Objects.requireNonNull(inferFunction); - return new ConfigProperty<>(key, defaultValue, docOnDefaultValue, doc, sinceVersion, deprecatedVersion, Option.of(inferFunction), validValues, advanced, alternatives); + return new ConfigProperty<>(key, defaultValue, docOnDefaultValue, doc, sinceVersion, deprecatedVersion, supportedVersions, Option.of(inferFunction), validValues, advanced, alternatives); } /** * Marks the config as an advanced config. */ public ConfigProperty markAdvanced() { - return new ConfigProperty<>(key, defaultValue, docOnDefaultValue, doc, sinceVersion, deprecatedVersion, inferFunction, validValues, true, alternatives); + return new ConfigProperty<>(key, defaultValue, docOnDefaultValue, doc, sinceVersion, deprecatedVersion, supportedVersions, inferFunction, validValues, true, alternatives); } /** @@ -256,8 +271,8 @@ public ConfigProperty defaultValue(T value) { public ConfigProperty defaultValue(T value, String docOnDefaultValue) { Objects.requireNonNull(value); Objects.requireNonNull(docOnDefaultValue); - ConfigProperty configProperty = new ConfigProperty<>(key, value, docOnDefaultValue, "", Option.empty(), Option.empty(), Option.empty(), Collections.emptySet(), false); - return configProperty; + return new ConfigProperty<>(key, value, docOnDefaultValue, "", Option.empty(), + Option.empty(), Collections.emptyList(), Option.empty(), Collections.emptySet(), false); } public ConfigProperty noDefaultValue() { @@ -265,9 +280,8 @@ public ConfigProperty noDefaultValue() { } public ConfigProperty noDefaultValue(String docOnDefaultValue) { - ConfigProperty configProperty = new ConfigProperty<>(key, null, docOnDefaultValue, "", Option.empty(), - Option.empty(), Option.empty(), Collections.emptySet(), false); - return configProperty; + return new ConfigProperty<>(key, null, docOnDefaultValue, "", Option.empty(), + Option.empty(), Collections.emptyList(), Option.empty(), Collections.emptySet(), false); } } } 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 new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/hudi-common/src/main/java/org/apache/hudi/common/table/HoodieTableMetaClient.java b/hudi-common/src/main/java/org/apache/hudi/common/table/HoodieTableMetaClient.java index 589f1e6cfbf77..c84ce4a6320dd 100644 --- a/hudi-common/src/main/java/org/apache/hudi/common/table/HoodieTableMetaClient.java +++ b/hudi-common/src/main/java/org/apache/hudi/common/table/HoodieTableMetaClient.java @@ -433,40 +433,6 @@ private HoodieArchivedTimeline instantiateArchivedTimeline(String startTs) { : new HoodieArchivedTimeline(this, startTs); } - /** - * Validate table properties. - * - * @param properties Properties from writeConfig. - */ - public void validateTableProperties(Properties properties) { - // Once meta fields are disabled, it cant be re-enabled for a given table. - if (!getTableConfig().populateMetaFields() - && Boolean.parseBoolean((String) properties.getOrDefault(HoodieTableConfig.POPULATE_META_FIELDS.key(), HoodieTableConfig.POPULATE_META_FIELDS.defaultValue().toString()))) { - throw new HoodieException(HoodieTableConfig.POPULATE_META_FIELDS.key() + " already disabled for the table. Can't be re-enabled back"); - } - - // Meta fields can be disabled only when either {@code SimpleKeyGenerator}, {@code ComplexKeyGenerator}, {@code NonpartitionedKeyGenerator} is used - if (!getTableConfig().populateMetaFields()) { - String keyGenClass = properties.getProperty(HoodieTableConfig.KEY_GENERATOR_CLASS_NAME.key(), "org.apache.hudi.keygen.SimpleKeyGenerator"); - if (!keyGenClass.equals("org.apache.hudi.keygen.SimpleKeyGenerator") - && !keyGenClass.equals("org.apache.hudi.keygen.NonpartitionedKeyGenerator") - && !keyGenClass.equals("org.apache.hudi.keygen.ComplexKeyGenerator")) { - throw new HoodieException("Only simple, non-partitioned or complex key generator are supported when meta-fields are disabled. Used: " + keyGenClass); - } - } - - //Check to make sure it's not a COW table with consistent hashing bucket index - if (tableType == HoodieTableType.COPY_ON_WRITE) { - String indexType = properties.getProperty("hoodie.index.type"); - if (indexType != null && indexType.equals("BUCKET")) { - String bucketEngine = properties.getProperty("hoodie.index.bucket.engine"); - if (bucketEngine != null && bucketEngine.equals("CONSISTENT_HASHING")) { - throw new HoodieException("Consistent hashing bucket index does not work with COW table. Use simple bucket index or an MOR table."); - } - } - } - } - /** * Helper method to initialize a given path as a hoodie table with configs passed in as Properties. * diff --git a/hudi-common/src/main/java/org/apache/hudi/keygen/constant/KeyGeneratorType.java b/hudi-common/src/main/java/org/apache/hudi/keygen/constant/KeyGeneratorType.java index 5434b4901c0ce..296a97139d7bb 100644 --- a/hudi-common/src/main/java/org/apache/hudi/keygen/constant/KeyGeneratorType.java +++ b/hudi-common/src/main/java/org/apache/hudi/keygen/constant/KeyGeneratorType.java @@ -20,11 +20,14 @@ import org.apache.hudi.common.config.EnumDescription; import org.apache.hudi.common.config.EnumFieldDescription; +import org.apache.hudi.common.config.HoodieConfig; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import static org.apache.hudi.common.table.HoodieTableConfig.KEY_GENERATOR_CLASS_NAME; + /** * Types of {@link org.apache.hudi.keygen.KeyGenerator}. */ @@ -60,4 +63,18 @@ public static List getNames() { .forEach(x -> names.add(x.name())); return names; } + + public static boolean isComplexKeyGenerator(HoodieConfig config) { + if (config.contains(KEY_GENERATOR_CLASS_NAME)) { + try { + String keyGeneratorClass = config.getString(KEY_GENERATOR_CLASS_NAME).trim(); + return keyGeneratorClass.equals("org.apache.hudi.keygen.ComplexAvroKeyGenerator") + || keyGeneratorClass.equals("org.apache.hudi.keygen.ComplexKeyGenerator"); + } catch (IllegalArgumentException e) { + return false; + } + } else { + return false; + } + } } diff --git a/hudi-common/src/test/java/org/apache/hudi/common/config/TestConfigProperty.java b/hudi-common/src/test/java/org/apache/hudi/common/config/TestConfigProperty.java index dbbd45a4e4a71..910bd2d2e6b5f 100644 --- a/hudi-common/src/test/java/org/apache/hudi/common/config/TestConfigProperty.java +++ b/hudi-common/src/test/java/org/apache/hudi/common/config/TestConfigProperty.java @@ -22,7 +22,10 @@ import org.junit.jupiter.api.Test; +import java.util.Arrays; +import java.util.Collections; import java.util.Properties; +import java.util.stream.Collectors; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -37,6 +40,7 @@ public class TestConfigProperty extends HoodieConfig { public static ConfigProperty FAKE_STRING_CONFIG = ConfigProperty .key("test.fake.string.config") .defaultValue("1") + .supportedVersions("a.b.c", "d.e.f") .withAlternatives("test.fake.string.alternative.config") .withDocumentation("Fake config only for testing"); @@ -195,4 +199,12 @@ void testEnumConfigs() { assertEquals(" TEST_VAL_B(default): Test val b", lines[2]); assertEquals(" OTHER_VAL: Other val", lines[3]); } + + @Test + void testGetSupportedVersions() { + assertEquals( + Arrays.stream(new String[] {"a.b.c", "d.e.f"}).collect(Collectors.toList()), + FAKE_STRING_CONFIG.getSupportedVersions()); + assertEquals(Collections.EMPTY_LIST, FAKE_INTEGER_CONFIG.getSupportedVersions()); + } } diff --git a/hudi-common/src/test/java/org/apache/hudi/keygen/TestKeyGenerator.java b/hudi-common/src/test/java/org/apache/hudi/keygen/TestKeyGenerator.java new file mode 100644 index 0000000000000..e69de29bb2d1d 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 934e22f11397f..4554d7b65c471 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 @@ -365,6 +365,14 @@ public static boolean isLazyFailedWritesCleanPolicy(Configuration conf) { .equalsIgnoreCase(HoodieFailedWritesCleaningPolicy.LAZY.name()); } + /** + * Returns whether complex keygen encodes single record key with field name. + */ + public static boolean useComplexKeygenNewEncoding(Configuration conf) { + return Boolean.parseBoolean(conf.getString(HoodieWriteConfig.COMPLEX_KEYGEN_NEW_ENCODING.key(), + HoodieWriteConfig.COMPLEX_KEYGEN_NEW_ENCODING.defaultValue().toString())); + } + // ------------------------------------------------------------------------- // Utilities // ------------------------------------------------------------------------- diff --git a/hudi-flink-datasource/hudi-flink/src/main/java/org/apache/hudi/sink/bulk/AutoRowDataKeyGen.java b/hudi-flink-datasource/hudi-flink/src/main/java/org/apache/hudi/sink/bulk/AutoRowDataKeyGen.java index bcae6fcd7b6cb..0b169425c466a 100644 --- a/hudi-flink-datasource/hudi-flink/src/main/java/org/apache/hudi/sink/bulk/AutoRowDataKeyGen.java +++ b/hudi-flink-datasource/hudi-flink/src/main/java/org/apache/hudi/sink/bulk/AutoRowDataKeyGen.java @@ -21,6 +21,7 @@ import org.apache.hudi.common.model.HoodieRecord; import org.apache.hudi.common.util.Option; import org.apache.hudi.configuration.FlinkOptions; +import org.apache.hudi.configuration.OptionsResolver; import org.apache.flink.configuration.Configuration; import org.apache.flink.table.data.RowData; @@ -40,15 +41,18 @@ public AutoRowDataKeyGen( String partitionFields, RowType rowType, boolean hiveStylePartitioning, - boolean encodePartitionPath) { - super(Option.empty(), partitionFields, rowType, hiveStylePartitioning, encodePartitionPath, false, Option.empty()); + boolean encodePartitionPath, + boolean useCompkexKeygenNewEncoding) { + super(Option.empty(), partitionFields, rowType, hiveStylePartitioning, encodePartitionPath, false, Option.empty(), + useCompkexKeygenNewEncoding); this.taskId = taskId; this.instantTime = instantTime; } public static RowDataKeyGen instance(Configuration conf, RowType rowType, int taskId, String instantTime) { return new AutoRowDataKeyGen(taskId, instantTime, conf.getString(FlinkOptions.PARTITION_PATH_FIELD), - rowType, conf.getBoolean(FlinkOptions.HIVE_STYLE_PARTITIONING), conf.getBoolean(FlinkOptions.URL_ENCODE_PARTITIONING)); + rowType, conf.getBoolean(FlinkOptions.HIVE_STYLE_PARTITIONING), conf.getBoolean(FlinkOptions.URL_ENCODE_PARTITIONING), + OptionsResolver.useComplexKeygenNewEncoding(conf)); } @Override diff --git a/hudi-flink-datasource/hudi-flink/src/main/java/org/apache/hudi/sink/bulk/RowDataKeyGen.java b/hudi-flink-datasource/hudi-flink/src/main/java/org/apache/hudi/sink/bulk/RowDataKeyGen.java index a9f34b36d2772..8fa9219e27d32 100644 --- a/hudi-flink-datasource/hudi-flink/src/main/java/org/apache/hudi/sink/bulk/RowDataKeyGen.java +++ b/hudi-flink-datasource/hudi-flink/src/main/java/org/apache/hudi/sink/bulk/RowDataKeyGen.java @@ -18,6 +18,7 @@ package org.apache.hudi.sink.bulk; +import org.apache.hudi.common.function.SerializableFunctionUnchecked; import org.apache.hudi.common.model.HoodieKey; import org.apache.hudi.common.util.Option; import org.apache.hudi.common.util.StringUtils; @@ -41,6 +42,7 @@ import static org.apache.hudi.common.util.PartitionPathEncodeUtils.DEFAULT_PARTITION_PATH; import static org.apache.hudi.common.util.PartitionPathEncodeUtils.escapePathName; +import static org.apache.hudi.keygen.KeyGenerator.DEFAULT_COLUMN_VALUE_SEPARATOR; /** * Key generator for {@link RowData}. @@ -70,7 +72,7 @@ public class RowDataKeyGen implements Serializable { private final Option keyGenOpt; // efficient code path - private boolean simpleRecordKey = false; + private SerializableFunctionUnchecked simpleRecordKeyFunc; private RowData.FieldGetter recordKeyFieldGetter; private boolean simplePartitionPath = false; @@ -85,7 +87,8 @@ protected RowDataKeyGen( boolean hiveStylePartitioning, boolean encodePartitionPath, boolean consistentLogicalTimestampEnabled, - Option keyGenOpt) { + Option keyGenOpt, + boolean useComplexKeygenNewEncoding) { this.partitionPathFields = partitionFields.split(","); this.hiveStylePartitioning = hiveStylePartitioning; this.encodePartitionPath = encodePartitionPath; @@ -94,6 +97,8 @@ protected RowDataKeyGen( List fieldNames = rowType.getFieldNames(); List fieldTypes = rowType.getChildren(); + boolean simpleRecordKey = false; + boolean multiplePartitions = false; if (!recordKeys.isPresent()) { this.recordKeyFields = null; this.recordKeyProjection = null; @@ -101,7 +106,7 @@ protected RowDataKeyGen( this.recordKeyFields = recordKeys.get().split(","); if (this.recordKeyFields.length == 1) { // efficient code path - this.simpleRecordKey = true; + simpleRecordKey = true; int recordKeyIdx = fieldNames.indexOf(this.recordKeyFields[0]); this.recordKeyFieldGetter = RowData.createFieldGetter(fieldTypes.get(recordKeyIdx), recordKeyIdx); this.recordKeyProjection = null; @@ -122,6 +127,18 @@ protected RowDataKeyGen( this.partitionPathProjection = null; } else { this.partitionPathProjection = getProjection(this.partitionPathFields, fieldNames, fieldTypes); + multiplePartitions = true; + } + if (simpleRecordKey) { + if (multiplePartitions && !useComplexKeygenNewEncoding) { + // single record key with multiple partition fields + this.simpleRecordKeyFunc = rowData -> { + String oriKey = getRecordKey(recordKeyFieldGetter.getFieldOrNull(rowData), this.recordKeyFields[0], consistentLogicalTimestampEnabled); + return new StringBuilder(this.recordKeyFields[0]).append(DEFAULT_COLUMN_VALUE_SEPARATOR).append(oriKey).toString(); + }; + } else { + this.simpleRecordKeyFunc = rowData -> getRecordKey(recordKeyFieldGetter.getFieldOrNull(rowData), this.recordKeyFields[0], consistentLogicalTimestampEnabled); + } } this.keyGenOpt = keyGenOpt; } @@ -138,7 +155,7 @@ public static RowDataKeyGen instance(Configuration conf, RowType rowType) { boolean consistentLogicalTimestampEnabled = OptionsResolver.isConsistentLogicalTimestampEnabled(conf); return new RowDataKeyGen(Option.of(conf.getString(FlinkOptions.RECORD_KEY_FIELD)), conf.getString(FlinkOptions.PARTITION_PATH_FIELD), rowType, conf.getBoolean(FlinkOptions.HIVE_STYLE_PARTITIONING), conf.getBoolean(FlinkOptions.URL_ENCODE_PARTITIONING), - consistentLogicalTimestampEnabled, keyGeneratorOpt); + consistentLogicalTimestampEnabled, keyGeneratorOpt, OptionsResolver.useComplexKeygenNewEncoding(conf)); } public HoodieKey getHoodieKey(RowData rowData) { @@ -146,8 +163,8 @@ public HoodieKey getHoodieKey(RowData rowData) { } public String getRecordKey(RowData rowData) { - if (this.simpleRecordKey) { - return getRecordKey(recordKeyFieldGetter.getFieldOrNull(rowData), this.recordKeyFields[0], consistentLogicalTimestampEnabled); + if (this.simpleRecordKeyFunc != null) { + return this.simpleRecordKeyFunc.apply(rowData); } else { Object[] keyValues = this.recordKeyProjection.projectAsValues(rowData); return getRecordKey(keyValues, this.recordKeyFields, consistentLogicalTimestampEnabled); diff --git a/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/sink/bulk/TestRowDataKeyGen.java b/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/sink/bulk/TestRowDataKeyGen.java index d222489bd80d3..5717f4aaf3206 100644 --- a/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/sink/bulk/TestRowDataKeyGen.java +++ b/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/sink/bulk/TestRowDataKeyGen.java @@ -73,6 +73,39 @@ void testSimpleKeyAndPartition() { assertThat(keyGen2.getPartitionPath(rowData3), is(String.format("partition=%s", DEFAULT_PARTITION_PATH))); } + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void testSingleKeyMultiplePartitionFields(boolean encodeSingleKeyFieldValueOnly) { + Configuration conf = TestConfigurations.getDefaultConf("path1"); + conf.set(FlinkOptions.RECORD_KEY_FIELD, "uuid"); + conf.set(FlinkOptions.PARTITION_PATH_FIELD, "partition,ts"); + conf.setString(HoodieWriteConfig.WRITE_TABLE_VERSION.key(), "8"); + conf.setString(HoodieWriteConfig.COMPLEX_KEYGEN_NEW_ENCODING.key(), String.valueOf(encodeSingleKeyFieldValueOnly)); + RowData rowData1 = insertRow(StringData.fromString("id1"), StringData.fromString("Danny"), 23, + TimestampData.fromEpochMillis(1), StringData.fromString("par1")); + RowDataKeyGen keyGen1 = RowDataKeyGen.instance(conf, TestConfigurations.ROW_TYPE); + String expectedKey = encodeSingleKeyFieldValueOnly ? "id1" : "uuid:id1"; + assertThat(keyGen1.getRecordKey(rowData1), is(expectedKey)); + assertThat(keyGen1.getPartitionPath(rowData1), is("par1/1970-01-01T00:00:00.001")); + + // null record key and partition path + final RowData rowData2 = insertRow(TestConfigurations.ROW_TYPE, null, null, 23, null, null); + assertThrows(HoodieKeyException.class, () -> keyGen1.getRecordKey(rowData2)); + assertThat(keyGen1.getPartitionPath(rowData2), is(String.format("%s/%s", DEFAULT_PARTITION_PATH, DEFAULT_PARTITION_PATH))); + // empty record key and partition path + final RowData rowData3 = insertRow(StringData.fromString(""), StringData.fromString(""), 23, + TimestampData.fromEpochMillis(1), StringData.fromString("")); + assertThrows(HoodieKeyException.class, () -> keyGen1.getRecordKey(rowData3)); + assertThat(keyGen1.getPartitionPath(rowData3), is(String.format("%s/1970-01-01T00:00:00.001", DEFAULT_PARTITION_PATH))); + + // hive style partitioning + conf.set(FlinkOptions.HIVE_STYLE_PARTITIONING, true); + final RowDataKeyGen keyGen2 = RowDataKeyGen.instance(conf, TestConfigurations.ROW_TYPE); + assertThat(keyGen2.getPartitionPath(rowData1), is(String.format("partition=%s/ts=%s", "par1", "1970-01-01T00:00:00.001"))); + assertThat(keyGen2.getPartitionPath(rowData2), is(String.format("partition=%s/ts=%s", DEFAULT_PARTITION_PATH, DEFAULT_PARTITION_PATH))); + assertThat(keyGen2.getPartitionPath(rowData3), is(String.format("partition=%s/ts=%s", DEFAULT_PARTITION_PATH, "1970-01-01T00:00:00.001"))); + } + @Test void testComplexKeyAndPartition() { Configuration conf = TestConfigurations.getDefaultConf("path1"); @@ -209,5 +242,4 @@ void testRecordKeyContainsTimestamp() { assertThat(keyGen2.getRecordKey(rowData1), is("uuid:id1,ts:1675841687000")); } - } diff --git a/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/table/ITTestSchemaEvolution.java b/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/table/ITTestSchemaEvolution.java index 0417285815a97..c73f1c83a7007 100644 --- a/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/table/ITTestSchemaEvolution.java +++ b/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/table/ITTestSchemaEvolution.java @@ -26,7 +26,6 @@ import org.apache.hudi.config.HoodieWriteConfig; import org.apache.hudi.configuration.FlinkOptions; import org.apache.hudi.internal.schema.Types; -import org.apache.hudi.keygen.ComplexAvroKeyGenerator; import org.apache.hudi.keygen.constant.KeyGeneratorOptions; import org.apache.hudi.util.AvroSchemaConverter; import org.apache.hudi.util.FlinkWriteClients; @@ -320,7 +319,6 @@ private TableOptions defaultTableOptions(String tablePath) { KeyGeneratorOptions.RECORDKEY_FIELD_NAME.key(), "uuid", KeyGeneratorOptions.PARTITIONPATH_FIELD_NAME.key(), "partition", KeyGeneratorOptions.HIVE_STYLE_PARTITIONING_ENABLE.key(), true, - HoodieWriteConfig.KEYGENERATOR_CLASS_NAME.key(), ComplexAvroKeyGenerator.class.getName(), FlinkOptions.WRITE_BATCH_SIZE.key(), 0.000001, // each record triggers flush FlinkOptions.SOURCE_AVRO_SCHEMA.key(), AvroSchemaConverter.convertToSchema(ROW_TYPE_EVOLUTION_BEFORE), FlinkOptions.READ_TASKS.key(), 1, @@ -493,7 +491,7 @@ private ExpectedResult(String[] evolvedRows, String[] rowsWithMeta, String[] row "+I[id6, Emma, null, 20, +I[null, s6, 6, null, null, 6], {Emma=2020.0}, [20.0], null, null, null]", "+I[id7, Bob, null, 44, +I[null, s7, 7, null, null, 7], {Bob=4444.0}, [44.0, 44.0], null, null, null]", "+I[id8, Han, null, 56, +I[null, s8, 8, null, null, 8], {Han=5656.0}, [56.0, 56.0, 56.0], null, null, null]", - "+I[id9, Alice, 90000.9, unknown, +I[9, s9, 99, t9, drop_add9, 9], {Alice=9999.99}, [9999.0, 9999.0], +I[9, 9], [9], {k9=v9}]", + "+I[id9, Alice, 90000.9, unknown, +I[9, s9, 99, t9, drop_add9, 9], {Alice=9999.99}, [9999.0, 9999.0], +I[9, 9], [9], {k9=v9}]" }, new String[] { "+I[1]", @@ -531,7 +529,7 @@ private ExpectedResult(String[] evolvedRows, String[] rowsWithMeta, String[] row "+I[Han, null, 56, +I[null, s8, 8, null, null, 8], {Han=5656.0}, [56.0, 56.0, 56.0], null, null, null]", "+I[Alice, 90000.9, unknown, +I[9, s9, 99, t9, drop_add9, 9], {Alice=9999.99}, [9999.0, 9999.0], +I[9, 9], [9], {k9=v9}]", "+I[Danny, 10000.1, 23, +I[1, s1, 11, t1, drop_add1, 1], {Danny=2323.23}, [23.0, 23.0, 23.0], +I[1, 1], [1], {k1=v1}]", - "+I[Julian, 30000.3, 53, +I[3, s3, 33, t3, drop_add3, 3], {Julian=5353.53}, [53.0], +I[3, 3], [3], {k3=v3}]", + "+I[Julian, 30000.3, 53, +I[3, s3, 33, t3, drop_add3, 3], {Julian=5353.53}, [53.0], +I[3, 3], [3], {k3=v3}]" }, new String[] { "+I[id0, Indica, null, 12, null, {Indica=1212.0}, [12.0], null, null, null]", @@ -545,7 +543,7 @@ private ExpectedResult(String[] evolvedRows, String[] rowsWithMeta, String[] row "+I[id8, Han, null, 56, +I[null, s8, 8, null, null, 8], {Han=5656.0}, [56.0, 56.0, 56.0], null, null, null]", "+I[id9, Alice, 90000.9, unknown, +I[9, s9, 99, t9, drop_add9, 9], {Alice=9999.99}, [9999.0, 9999.0], +I[9, 9], [9], {k9=v9}]", "+I[id1, Danny, 10000.1, 23, +I[1, s1, 11, t1, drop_add1, 1], {Danny=2323.23}, [23.0, 23.0, 23.0], +I[1, 1], [1], {k1=v1}]", - "+I[id3, Julian, 30000.3, 53, +I[3, s3, 33, t3, drop_add3, 3], {Julian=5353.53}, [53.0], +I[3, 3], [3], {k3=v3}]", + "+I[id3, Julian, 30000.3, 53, +I[3, s3, 33, t3, drop_add3, 3], {Julian=5353.53}, [53.0], +I[3, 3], [3], {k3=v3}]" }, new String[] { "+I[1]", diff --git a/hudi-spark-datasource/hudi-spark-common/src/main/java/org/apache/hudi/internal/DataSourceInternalWriterHelper.java b/hudi-spark-datasource/hudi-spark-common/src/main/java/org/apache/hudi/internal/DataSourceInternalWriterHelper.java index 4ad6c2066a3c5..00421b1b34595 100644 --- a/hudi-spark-datasource/hudi-spark-common/src/main/java/org/apache/hudi/internal/DataSourceInternalWriterHelper.java +++ b/hudi-spark-datasource/hudi-spark-common/src/main/java/org/apache/hudi/internal/DataSourceInternalWriterHelper.java @@ -70,7 +70,7 @@ public DataSourceInternalWriterHelper(String instantTime, HoodieWriteConfig writ this.writeClient.initTable(operationType, Option.of(instantTime)); this.metaClient = HoodieTableMetaClient.builder().setConf(configuration).setBasePath(writeConfig.getBasePath()).build(); - this.metaClient.validateTableProperties(writeConfig.getProps()); + this.writeClient.validateTableProperties(this.metaClient.getTableConfig(), writeConfig.getProps()); this.hoodieTable = HoodieSparkTable.create(writeConfig, new HoodieSparkEngineContext(new JavaSparkContext(sparkSession.sparkContext())), metaClient); this.writeClient.preWrite(instantTime, WriteOperationType.BULK_INSERT, metaClient); } diff --git a/hudi-spark-datasource/hudi-spark-common/src/main/scala/org/apache/hudi/RecordLevelIndexSupport.scala b/hudi-spark-datasource/hudi-spark-common/src/main/scala/org/apache/hudi/RecordLevelIndexSupport.scala index 743ce0cc6c1df..e1b093b38e0da 100644 --- a/hudi-spark-datasource/hudi-spark-common/src/main/scala/org/apache/hudi/RecordLevelIndexSupport.scala +++ b/hudi-spark-datasource/hudi-spark-common/src/main/scala/org/apache/hudi/RecordLevelIndexSupport.scala @@ -23,6 +23,7 @@ import org.apache.hudi.common.config.HoodieMetadataConfig import org.apache.hudi.common.fs.FSUtils import org.apache.hudi.common.model.HoodieRecord.HoodieMetadataField import org.apache.hudi.common.table.HoodieTableMetaClient +import org.apache.hudi.keygen.KeyGenUtils import org.apache.hudi.metadata.{HoodieTableMetadata, HoodieTableMetadataUtil} import org.apache.hudi.util.JFunction import org.apache.spark.api.java.JavaSparkContext @@ -124,7 +125,7 @@ class RecordLevelIndexSupport(spark: SparkSession, * @return Tuple of List of filtered queries and list of record key literals that need to be matched */ def filterQueriesWithRecordKey(queryFilters: Seq[Expression]): (List[Expression], List[String]) = { - if (!isIndexAvailable) { + if (!isIndexAvailable || KeyGenUtils.mayUseNewEncodingForComplexKeyGen(metaClient.getTableConfig)) { (List.empty, List.empty) } else { var recordKeyQueries: List[Expression] = List.empty diff --git a/hudi-spark-datasource/hudi-spark-common/src/main/scala/org/apache/hudi/SparkBaseIndexSupport.scala b/hudi-spark-datasource/hudi-spark-common/src/main/scala/org/apache/hudi/SparkBaseIndexSupport.scala new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/table/upgrade/TestUpgradeDowngrade.java b/hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/table/upgrade/TestUpgradeDowngrade.java new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/hudi-spark-datasource/hudi-spark/src/test/resources/upgrade-downgrade-fixtures/README.md b/hudi-spark-datasource/hudi-spark/src/test/resources/upgrade-downgrade-fixtures/README.md new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/hudi-spark-datasource/hudi-spark/src/test/resources/upgrade-downgrade-fixtures/generate-fixtures.sh b/hudi-spark-datasource/hudi-spark/src/test/resources/upgrade-downgrade-fixtures/generate-fixtures.sh new file mode 100755 index 0000000000000..e69de29bb2d1d diff --git a/hudi-spark-datasource/hudi-spark/src/test/resources/upgrade-downgrade-fixtures/mor-tables/hudi-v6-table-complex-keygen.zip b/hudi-spark-datasource/hudi-spark/src/test/resources/upgrade-downgrade-fixtures/mor-tables/hudi-v6-table-complex-keygen.zip new file mode 100644 index 0000000000000000000000000000000000000000..0d081b416c95d7d01077d2c37116387f88e399cd GIT binary patch literal 67342 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 Co{`M} literal 0 HcmV?d00001 diff --git a/hudi-spark-datasource/hudi-spark/src/test/resources/upgrade-downgrade-fixtures/mor-tables/hudi-v9-table-complex-keygen.zip b/hudi-spark-datasource/hudi-spark/src/test/resources/upgrade-downgrade-fixtures/mor-tables/hudi-v9-table-complex-keygen.zip new file mode 100644 index 0000000000000000000000000000000000000000..af68e5afc3949a7b352ac5b79faee4e16673a608 GIT binary patch literal 137899 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�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%0AbcN7rdE z2~m_UQVlov8KC=)^TZs#R`0><;-V8i|S2h-)$4<;i(V_ z>vCn>B6~Qj9P;91Qhr^bEF)5G`GL?~5zUgd_ts8IF%)($OA}iI;ik(Ao&&Gw@fC_5WJ`y`t=Y zCd~g7Wtr&yin1L4iaY#=D64^(aeM4zX#}xc4mPTX-Qp(KUfA)3CVm-2VnbkG-Qztn zwIN-qR8UG79H!`a51%cs(R2fN z$kzu9VtSGM+m!XtEAasNhbjB_1mpqeX&7IoEcTZv`@fW5{?88JU#jfNG6H%Zn;Pn@ zDpnwvTDZigHeZnxD(FdIxG-~k04WL!N(Cglnkeclm?ACZc17he@o-0te+}`^GVA`p zz!->!>SpEgSscZ|DGxOmHJ!~Nd?_8LdKu)CkqVOcNd4f=fG-$*uhWSS(CWRzsY0ndAZ%wjkwu;G*mtPjCNKmroN9|0p>LY(^ImyE#>A%&m> zXo8>w0sGlR2%HN3{$PTX#M?Vn5x%odNWjm@u-|lKC`BTGfYB0Nn!0>x6I28)gHVuF zP$zHGt|aVYK>L-x^=C>X4q-NpB8VSG)iDgjFEJ6KM!XT957h+=K%t@p$b)`H)xipZ zNdP^ZJGW8MlmW%sbbpI-@`J<$Z#0b~Z&*J;V3Oh~Q*|PzRS}`A+x>-(hVk4vhOFn22fZCrTa*25cZvYZY32BXz#ED%@+5=R`T6*9P zq3rY`Nt6;I=EyDLj|f?7X-gOig{ZMm)Fbkx%eLbVmp>p9j1&lfA_?-WtYUtq!%tOR z7toOt8>j^TEgBF9|1BC2l9LgFwK+hd-;~8T95_pA0f|GJ?%0B2l7p%dAxa*RU;Sy3 zszQxiOYkj0g(o#1i!;Ecgi}G>w)HH)3g?50xRtd{ZY5q8) z`bJI+tW*~?@Dd*W*Z_R6(>JdJ^`*^qmuedwy}MaLz?0u#zz*+1vb?O<}pTwM2Dnw8Dpdv z%tEEWBkTlOh2d#Q7obLMM_~-`qX$mWXbupdXZr$|e}e|=Enh){X&ah!Ut}TF5fYHE zph3wb>a=*fH#R}_SJ0qd!+*}sepKZ?lYtV?wvK^34-w5H;pTbo-CVeEUK==_CIn8@ z3Q11xi(6tvpX|vP5V{GG)|e1{`~Bl9Xt1;Q?Y1gl*VyrwGAOWN7CpQ*dEstwJW`vk zAQ$mZXCg&rbZ?JuV^K}QJK24zi|sD^A&+cBh4ka_2hNBF*W&36;iLd;WTj|s^jY|% zz^U(P3F5Opa732Vc+!@_n=k~98WIh)PAX+y<-$lOnDgz?F8`s+5`F2i8|}oTXq?*p zlo;r5>TqhkBkU$B9mk&GBE@G>au}QW_a8~ zAp4rrl7ez}nh9anK(Wur}|Gqann_ z|Du)}NmS0AN=_%Mk;y2bpkLIoQCG@^5JlIjVGUJSY)@3Z@S=p@Wx%daNH8RdX7QwY zF5m1#?Cz|4*mU)F*UC|}33hZUXMx(K$#r0L*o>x@1`TeBxZ^t!4KWlj5#ui!!2lvq zGzicJX_+F*Qpa+QXvtsWLF~l34T^4dx#rYqqJ4@iXm06|0iTbCU~gc3CW7qo;TPtA8SaZ5fLK(ak*|HKxjhJ_RNW)co! zdABqxVQ(HTQXwc0DJV}bLYxO75m4AkEF!HM*dMLqo!(#e8%QrY$>tWMcT%?_cm0J& z!3c#5J1O>%E`o?KEX=)kNwsSvPY6n272GeAd>@N&j3S^c?U`?0* z19a!sx0DqJv>sxJNBdGG9lr0dbi@eO6yHJOZyLZp$0rK#4Iw$PF8V@}TuR4La zcR}HT`f&|3LWCj_O{}w;W+#U$QQPhf-XJQc!TF*(D_xwOFMlb}oN%C>*i|@&XAE12 zX2Mi`RhxPkU;p{T8nW)%uaZfkVkwlckt+o6SeaVz*E3ik%pxOUmNn%=_T^X1p!`dg zl@7!V{Ff|Crj^kb3#Xa1K}+wdKbG0@iEn+|qX8R_g_CwhJ+T5xXqf-=Th2x^G?>jV zZ98SqbuOL?u25kVlhZY-6<9KcXa^5&0kf0XOQMdW%h^Mi!9$8sz37Au83D;lVi7RH zLWo4*Bo1LDDzf+0_!2O)(pgdlU4Lyu{J4&n@9dr3GRub#5`d-TwNSWrBhJ`FjiMx& zcy5w-q>xxBn_tC}dR=VEa2Cv_nQGvwzOKVN35?eB{9@q#Fq0q{I2oY&amCSqQMHGNht+7G&o<)|HP*^uJmL?$GaQ(o3rdsRl|+u9_r_vA{2e z^qgOzhFzeqmI2MFRAMkZCNL~aN+fp;b0!5OC;?{T+Q%A4hDm`0sqL35t1{=JqSX~- zO(1=VDrP9T|K-X~wKrw`%a#3JZGHAHSC$^5DdWqPg>vY{lu6iDX|bgUy^kyK!(h5k z%xMm5j+u|X!h}sJh82rjAus`vkt6eyNGQ(9rw1U3N-CX9CMiP z6cra))EMKGS905_yZ>h8AMvj8Wt{k(u(w}G*6sBe#^&zoqYfXT(N#>!%g$AOxFICw zH^Q?xa8)EvU~=hkY42yFVRUe)f5sdWLBk>ciWTm@F`1=rtd^^Xt(jui4Cqu8g*_f# z{bnUd?JGUZ-WgqSUNu^jSA~yRmD@#0v_=@C3jfrfG{Z(}e|3=5lvnJ&kZ;(zna_bu zA7i`$VMo9ut4&h1-A^DWZXJc=vb;9R6ydAZzJdOjZL|Gi3w>0}<#A$siUHq>b?An6 z=|0nGzBRu}W_t3_ONbHetXOC#k4yWSvHm<(OyA!$GjkmQ7Ge3c(J4x{`KQUydi~EN z*8vM_thucKh%G~GkJIUTakxmfV}h?^C_g(;ebiJMhrqwJ>KMrfu0+;Mrx2|2Gng_eofwtU?F zjCd!dRlbsKalm;nQ`eR_G@itY#>To$Z=}O+B~yLgHEgFWMp=J2X2dep*ny*yhjFq6 zz!}C|2av$n$wvDeAirL(TpbZ?9$y%jz8BmcZ?aCeBeFFnj&*JV@Now9Uk%TqM}Pd$ zJe<(Q>L&w3X%w$1*-$OVucxMr zcC#tX8E^2z()I-AesO*4@f7K>c0~UurV?&_sww?$%G%^+lOXIf?fK>O z^~_8g9Riz{+8u|HmTy@9vtaFJcCdqDLyZbgqbId}I^o#(@sV?WTYL*ec_J?EvQ|CW z{mE^6En2+z(HwH)IrC0b+E(|jD2=xAP^~-n+UE^yq9@F=7w6dL*W}FQ&b410-A|J!yayt*b) zXxfp?vS&^G4~yWBkVP@CDzYO_wF)|dz*zbP%Jqxp!ZP<9xy6UzJc!LP*J{R2OGEc< zK>1t$W=r54{-JLK1`O%D3iWRv_+@#q!3fbP5P&34F7Dm2>&-_fjn;>qVsw#gkh8Bu z95-@GY2h*pXVZ`Ix{x!54JhpPQ2nw~_D9!s9@@4iaHE;^lex1Ida`JcGUdQ)rtHTxw;>`wx_f#EZQ%I*dV4teHLr={p;MV8m<*X}h?E_{h zhx5lbkhMHLjk=1mpPXDuex5JVwFhU3faTaXs>ygx#XW7eaWU)VcKcfL1~}uRx9=S0 zA0+jxgQjO~Wb;?tT@kCEzo>q^^)xvK9Fi!drI*MptatIBnRyDSfRBG3o*sJ4GdFJw zHqXC^Rf(sb;+Na8xk8w4-o0Fo?YqEz&gJ(Wp0h|Yo~u@Iy&0Cp-qcK%X2<+3-OF>8*hTTa zZNk}$(Z_AmJ29DYnECML*9QoD=0&UqpEYH@llHDkl4HgzhYT}IV9j=feG9AI*ab?@ z@)5&DL`rIw8$gDZ=gFa9>zeNb!sRto9nF5mYhHbyje+AbydIl{&i7_JM0uk4ir{gp z!t$o8K9kFLFjS&MQ>w02mNKIk#C;^;ceVB zfdrH7gi_cnEbdM&0;#se6DP8MpY$18G=IE(_O5_)1ZD2*VXQ^Z`%8LP_#M>Y^kaNx zHaIhsL*X;8_I44E zWIwl4pSwKD&xryyE>Sf=_f=~gpJ?9CIH1s7aAiAA(K|fh73c15UsUDa2FME~H!sBt zTe4GPAGGp9?{Mfna&uc|9aLWikRWuMR}T4AbKBG>b98b2*l*saGaCl(U&OQV+n8{? zuCy~!>>W!UEvyc{3tK1UwW1w9^Bs^ifjX=$$lGiUysHoeMQ;`r2R?xU>qYNz!h zS-{SAMsl%+ZL7)%b7fgto;NikDcR1?Jg@EWSHcuGd)$?pv3`z6_htIQ)*@VtwO+MX z=G}z#&KA7u2g&D>3#MNYWkJ22*#4RJG|crn!t>qOatnIgQ}@#VM?uHV<;LA+5^ziU zDBs$5Cu6gW;*oY=xV?Ex64(76BwKDjiogFbVmny~S(^9YdnbR88lA2PZp*oeE zirrrhrq9@|Z!6T1^`<^@>^bP%iMnT4dM)V+u+j#MZ1QBmvuqt=%g5Ndh1M=HHUHdv zxvJ$Ajki`~O8o zM=<+LcVOD4L3ky0W|dkTr#2})Cl|BBkIb3lyx9BKGpsWdBmwdI2d4Xb2&Qo7qF`T| zA>h9o)BV3WgD=fcN9GKW*M=rVEN#k7zA|o6xF1?FJV{re3$Q#~$j;1QIn1UiuL)M5 zmNuEl%(+(PD5p=cAu@?@q1)N5P%vq)+44G^TxiEAh>p>Zn+S>m!D4Wo5ytofnSR`Va0pY_ z-w}zAaNh`5zz9y!n+O+PxJ`jm>RN$F6Uzjc)6%Ux}yGfmjfj~u|_%)uS()8q9@pD zF#K;$C%Tj24T}O)i5UKT_m|T_S7D>IlD*Taa>i$q@dsMwvnm9{wVs5S5x!EBj&qn& z+lkhMaS}RES;>atWc!2t^lL%VhD?WqDoemaofZyGPQy{ehv%6>B4Kp6<%|f2k$d9J@ggB;@Rt^#N8SqK);JfQCem+6k`>HexXgj|b}N!xC!WGZ6?5d}f4dCGTSs zb8d-`nk17C_{s1smx&4V?nUX}pbqpuKpi`#J~3hkB4fa(GAMPO6-WT?2`wnDXg+6v z(`qPzJObI+am zzL`5iRae2Erus)i)zj-;YvwoID}@geMmQ8J+@O+Co(0a(rqqK0F*I=S50@A#XO6tA zIT!vzGH+MLN8o~q)R2DlGaeFOaaekdGB{=onP3YmpaG~s4N+`;pf8n(1eV0lU><$G zl0Xo+pb`4eNVtLgW_)m9%Qlt@jOdsWVCbI9$T8GJ9|6O@kAR_Lq#aob;h%t^<43?S z2UXYqYVrCbV2Ju73J@^N{g678XpfJ8p}a64V3@;dPemNvg4(uG5uvlbTkz{V7e*jQ z>!W(q>reG))I1FR=d-`6N2`%TtTf)+^{A1S;VnM3a1P*&VC@j_iP@)%m;4ih(8e*{4^hcQ z8V{TW<&eOh0<1*r-NuWRy6YA9P(n;-=S6zkv!kpNit z_z$dub;g{jCD?tpcg7qOqgpamVZM89(=_ft2PgQrZI4=FKKp(Pkt5~N#P9|Fqk9w- z&^_9}SLNZJKsNd0X7YJ1uhdZkgsBq>0=T%~jeOQk*ILLObm&V&GUhT$R#GO^fS`dG zS4Lj)fKZ1wC>=~99|6*)WSfnh4u5vius)gcPJnN;I;L}>G-(KleMH?Kts@-7&hG!o z93@cS)~=Mq-}g3L+S%XrUfPFOsD$Y?vWIc{Jyn{Xua6c^B?tOTRimG9x=bo%MI)0d za32Dp_D8&sdK0cxRAbnqWY7#n(CKfkvuaoTC)YjTX-53~LeXrD41pHmzmf;mVBIja zhX#W_JX0@MfiA3uws~deF*1d{$}c;;amD-BZMz?VwD3_a zz5wKic&3or$8Ebx+0EYZ!igP&YVthcsj`e2=2s@fZp)Y+g4jQ|?IAe_bb8+zlXiNQ z`4=MyWO593C41%NO=!TYnP0Vaz#2MKEGi=%F((wdcz19h5oD;}NiK#esrv1#=~%8a zBfScLpwYAnapT@&+(y#k&qi?gIu#QepMnO{2mW&U+v{KeUiU@oqkEKP4$wV{O!t6> zLIH29qRS)!2ZlpOl>b_9&M*k*DEZSp+HhD<;{ZC(9jB6m=GzPZPG)W}mb*Ihr+d^; zRTm@x|I9u4q(7SIR+&d)OC(f+k=aAXhPlvp=cHYY0RPtT-;3KN=l^en{O zu_JrMf)MOSViWxukmYa-RWkC+Iq>|=HECpRgcrpr-)?MFxH_Sl?}dm_bUkAow;pj- zYn&u6)ZK{?v9={r@AE1LI1g)W#(bh-H!yq8izv0en-H-6)@;)iBGVEXMvlwL6lSQj zd^mE5#s0~zh%AA93ah~)?eoJ~)U^A{l(~=eR5T=LH?E#+FWy(-nrG+{@(B`s2L&(B zxUnWrDhZM11CcQCK3i=_ru7g8se08Xo4D+;ZKP74wOB%$l{xW^q=t|&#&gCiQtxAN z<1TR%H79}U_lg9ihWYe@rBv#gl~7Fga+{{^r3XrV*QYy2T`v_+-&)0i4H?iX0)ewZ z6@#6PmZ0s+$m*f`vmqO-p!{OO?jRLsn{~B(x2-d?aaS2{eh7l*IYrSnXz0t}3Aae? zwwP?jJSIm!naywh1h?&JBykY>{TQ*S?mP>_Md{07{%}r&bP4Jh+>e2`W8b__s?E+7 z)tc@bpED=y32`X7rqZ@c2VD_F#ur!*3>7|UsS&YC!jCC8=Dqt%)4DH3EF*;9S6KTA zrap~ggDME<4d<_xZRL}oD>^^n3JdT!^g_u!^9xYwDXk{wH(Rav^~6FKIa@n+s0}~N zvQgL}`a~D;!?q}oh>R>nDQqT|#N0S9Ay#u7fW-2bW1WUh&oQ*vJ z?X)dB!`KV<%IK+}MX>K)s*){?UR~|+VE?e=o?PR~f)*dUIMB{zxWBvJ>d?Cd|I&BBgYmlk@ZyWUQZ9=C|b% z+QG_Qz3pV?W8r96=elg{_AdoI-#Bj-q}okeAE$wzrn8fHe(+mY%x>6ebcs30}Q{n8F$q!S!d&%bnVd~7|;AR(V#yU#Z*vf*@w=~ zvbT#E^%H3B(mGfykG$8zRT;y<5)gH;E=V!Hpt4ABYLp1C|8>&I<*iogpz6vLubE6pvw4&=`E&$hwC9Hh0lkSP z^0w&T2^Ol@v`#kjm@z?+W@TaE^xzgVDO;G2nS1fYsDv}o)1lHcdCD>7+5Ogsz&W~v zDA^;u36HZ{E{hSvs81+5jDp9l0sj0|!gqqs<0_07_0uS8kaFvH>FQ`kImZj9wwr($ zs-Agi_H=77Y*2|BpY;G;YmzeViyHLh5&^v$(OrT+JqnLM$@4$;U2+`Z3;#5HahvrEhv%sqmEP2YxfpM-F2nL5dY@OP`a_ zQI%rURm`n>-96kCv+jz7T5Fg}%GVRrkP#a*jz;iQ^a9J1z8?ir!THBX-{0P zAhNIA-li$ZPbJ%>wxCH#h7n4Q755_vr~a7Gd1s1z+}5z&2Yx?@+x^M$DL{LsCi2lst+aAi;LcgO{$J= zC6z6&xH#dU7t4*|n!De-6wN$qahw`GPTf)_TahRDg%L21>^Rl%lux$5ygh51?e5>Ts|p$+~?xXasI20qFI zV$bw(s#WHPC4MC9Hw)>9>)L*;28zGxz3!uLP@7GW_R{vCi*e}Zj6zzIRFh_RaKeo2 zTk2aQQ$iwB+6y9O+8U^8e-_@?Eplwd8e^52VfmuCN27TQ}M6Hx{ z)WqHd`^MToZ63X2!isR4)KKLnIlEd=iw~);FbkZ*=4^bL21~K&IN5XLGbCdF8a`K1 zhFc_-lE~Cc=x|)i+1iaM%)Ms4gxPQg93#{AU1)o+@*oKw$KSdShB^V;9634TiP_X_ zf_T>m`NmUmaPlk5@JP6nqeNw8(P6w<2~U99$v71lYm`;OJ0~{FD?K18^=O6y|o#f5KF% zWVVBaY{=$b#v!kd57qT0*}>{-OYIf!)mdAtgWt!+tf3Ky^!G|$Vxsb+jVMI9_7UCJ z%R5d7YFP;N^1lTTzGshDpRd<!+>raAx=Iv{;$kM^m~x+k#=f;(CKyGZ@rvUH;YmL9>5T@p7p>|ijrY*AqK@)f=jvC)01bMmJfIjpgVbzmo3OD-Hg>`N z1FV1f;lZ6LY3Zs)$o3uNKZ1iFx6hlRXPEzJ*!+Kc`~2_0!GFDd{<~rFUrjQiF}8i{ z|I#GekxcL1a}*Yglk6O}k8zj>)tU#24qW@E{yIwEmAIk)*MiND*kDUF`lAi}r%!(D z|BqvX|L&3h8XKhhlbd8_`$xkj$`5FWf&g281vpfKS+AxkV!oVgK;s?5Upq)aU`Is? z-vu;yKRfr!*;IZiD?8Hy>8bVf$O<61tBW(IerI760#lWLbzfiX<^UCEIB$D4du)9^ zYmLSiU%N}ehcZ$S@NzfWC`Wua{?4OqkeryHm=y92)0n+)YNo5pq=uXeyfsh{UJAiS zw>OG_Q*=lly|Skw;N{Fm(*b7e1YEl=I(8v%1KJKxqtKi=OaCjLyU>lkQ z@WQ}w1pztPySLhbx_nl+DG^y)aC20>9~wzVg@`~!8Aa4rKS|D~BKf7!ep$lL1KD3({YMK79#Ho+~~F z#KK1sdF^+ACV>VrP-LCy)tqT@mvCVN*P*`fCZ1K?3s|1#)OQg4wGvbekAv!nf`ysi zwv<*}JzqRU@p%Lo(o#y{eC8aDCIDC@S)vcD%vc#J zae?P7(nBBtT*_<-H*W!2+)rL25MYs9iy3{s%Fm2HL_eu*KxMv2S>f?@@&Q(mxHe)Y zQwaA6BX`Ip|EwVSYGg+6#ba4pwG3KJeZJzz$nDVf2iGO2B>jx|5KkcKE5``7BlXd- z>A3|wWQ^lRbU3r;b*PX3!;q6<<2#Y@kSIJ*3~9#Yhg+HZ7Unns}e?(fXhl|=RRE_L)rLpP0d$UZ)DGLnA!z-P1feC@R%IXL?$TJox#yY=vzvqAwByrexJe!Cfj<4_1&H2<97{oLSOPoiYM@X z!MZH~Sy$_q0K1?;TX>odgA@?PO?vOsti+euL&@El`~#8Te;_gjaOvDMqr*b!m(u57 z_7l)!ap1EW;0w5PmM@%6&f$fg3iX?J&K|D0?43ObGQbMMWG@stFgWw64HzX+lPAY8 z>9Mip`~5@u9~GOxe=eO96coj`83KW$qhdi#e>CZ2pRCaRRk8Vl0sa(0pcdb^7at6> z%2()GvNM@JOG9KSu}wsrwU3dyUzBzUf$#79|is9x$OE_a#jJ zbLcFKff4p%^x=@#Bci58{(}YF(uDjn^g`r$s6Rb2x(J1Y6nrVeG)4ZNK$>Q@_fpv@ zToN^ks?hI>He{S(CZ1w?Mj+BR5ZSNho}U3WfbKlNLdOZ zm!@VA0QgI_XX<;(^chH!G}qa`zyXQX_3GoySs+swM=R?y-PS_;@naxnW#`ayQx=x+wcUA9>uoM5;BeDm}mu}fS8cigX1FzB{Jy+ zTt>3GkZ#~A#~3*AFasIC&H+^bVVHgf@szW9k1=bvddO+_M*2LkoU9EyIV#D_BxAzQ zlaYTs_%}oHBOq-~w%u54eka5j(kD{i@e+tok8PD50{7d2k;PQ31S}vWt2Y~&a6Q2lf?+=#nCOP``8x4=krRE_u}49jg^+ZWmkaeIE1eh7!!-C(UKj0dE_QR z9vuD>-v9$hvSn?pL4X0It^moJFFSE&r#M;8XKJHubS4f~SVU-xqrfpcGz79>3{H6D zbdFz;BBiIwp$(NZ>@dWFra&5y_pS?*%WbPU25e0g!fQ;#d`%Z3FabBtfdcG*Zk!V( z59oCH%Wki>#4=5OL<0&o=|2iKVbXmRtKaD9f&LV14tx}BZsS9QgNPi)xpO%nEBw}I z#!tX}nE25vivX2x7FD;MCXL&RAPwy+b5O5zHRl$96fIORjNxgCGyr?uf!?zS2l{~U zpsFYzMP$Hu41Sm7#%4nicm z4PcN1xxF)+*6M0ghN{+o?H~DOKm!c&m*+F_hd~yZF4OIlpgq~czmhC2J1j0Vv4#Vy zr!k-ziEs~rKA4HUMesd0J3v2%lR0>LlJI(Z8VduNuN7V%P4gTEI@?xRbR_M7x-#0n zo)edL+l^n@UH=3+zM0ajM`yv!oq1~1pL+`8heSW(Bbn|cRt$HhH}uW(_s=)+oeUBI zX2~3o(HogziXtX$AYu%T3Thlw$rG(sirzpOW@_x4rAz>)N%{%?b406*WS%%P;8C6> z^}%c`ZOoWPKAFdT({Rs`;}Ik*nM0l%q zBOYBe8|Ko4-gG~Tz9yFM88H56-6r$KRzyYEFPmEIC}&M$yRJ_LOn^*P!8?|GZ3}Y2} z_qTnDY0Aip8ls(WY}_(81qKI~up252N^t{zuUA!;hu5!fwORFm>vEjc-!1qYV*J#;B0vOl%Dq8E-2mgQ zt@^^NPE}3I%1Q1#S%l_^+YQ5VnB$BCQx)^YsiMts(y!G!>>AM6JZx(?HM4 zE_cfd&o)+iE3T`0cW5uCv##yJrNHIX^|;vSFc+c;qB?2pDVpjYo0g_@;UXyNUaohr zI=wZ^Se@m!lFUMrdEVPL>GFyHTjW4fo2!(j_{~r%{LLLt6Vs56P zd;<#zOUaaC=H87CcU8$8c^D%TPno-@M^jn6m&6cWq)hcK@d^$k3iOr+SW~H^0nMS`lHp1gZj#s?h>-!s4 zIxGJKTE-p{_j>poH$LS%Y%?~6E&Z!w_IbhT1llx$oz9MjbbkI4Z(cJ*T)jIbVUxn~!l=^K!+PFcVbS;|50CE3$V;lTD|n zt;6F+R??>HYYM^*`-b?EZT5LDWrjI>_OZj%Dk>CSG>+QvallDdb-Jl;Y63k|t5TrT z{hu*>&~L$UUECJch)Cm9o9=Xs@-x=Df7|b?Acvw2rd3{afMJKS@gmdfEc1 zp6-T0vhG7Jt^akCG_l-i-%Hz~f_e>m?wpPlx$V5;(|v0ct%@V~)A{9)YEVIcsTL!g zjdl0DCI3a-kYl^X?QL{g`>C^uKNGwOwHKAv44i$#?s4g~2IRo&;GFi^UQ2vk>XDA# zZEGQ+sZ(j`FitU2Wc3+-n5C?O`Q5Epv+;3GtW*91*?hiTEj=5(Qvc~0`ZuyRV^yB= zF3N1*6ZFi;O8GkBmR{$cx4CnGE0FabF8y(r3g*3by23HSVlz`Ibd$DPOWSBlWq0{- z8!oAf$}rRmxi^~7{Oq4V2RU_h(q9Y!}2vA9(O6QBn(dx@N3YKdWp{@}%@&6jPE zYt2k}KyAEEY1BDhY72iVh#Kui(p<$itfa_EwiE^&%wWZFn@T2K znzOC@tYs)IT;^wA>uDMi&#&`59RYz-g(bG^twy`OI)=Lu@cwk9gl&^fw0t!Vy%L@z zSIg<_n&lP8%x%2I%xgZg1N>66BlILjUG1r++j7lTIu+Kpn@t|k#^d^Ru9a8+$D`zvvLrs=O)~*3ZoHNn~rLu^=WO# zLIOYS9v%FoA(JIda?2S**~VosUh6JIU%?=&}H2 zWW`n&%>9&iYq)wm9h)E6k$n%(dX1vrng(0S)$~ns-dQO$(1W3AuP$&=NJOmljT`BH zw~3~rZnrqiz3Dpa57eyMo*Azf;f~p)$sMG9d$FDKux=|XjcdkZt$KT;6MAu#f>}8g?Y`Qz z5&yXD?kZzPWf)l8z)raB`u*o=AS}Oj`N0XJwJJQ6Ks`@7g59?$Ax+xd+lL zvc9-`q(vKuqaStwsjeqSi0@*gdd@BGnxj0(|#>R{f`EXrlYq&ih}JH6H=DADaSKYyeQ=2Y`~+fAbk>{yZ`DM*-#kN53QE zU&%hEf27)?!0_jDeN*y8a+8Ln2d0Fhqn3kE#zs;M!}Rt-*z}cXGWp7gn+cxnwxv~k zvet-dDOS|n!@n))qIMo%O!iV_HW(FxM$f}uuVC7_7XQxUW`Yofsl zaKZ!abpoH1vImQ3&YbLWkAu+>{O>?s5o#gS(=m!L#%+XoG|CC*&jp zA_gu%BLD`=*?@ZN!omkaxsduX(4CkKq)+)HfhI#IVXXHDK}>>p20>I<97_aw9QdHC z1z%7Qe`044_FySIHvp1Gq=!-sJ0i>LHKfnd!Tr0b9;v({}4DiO&3LsU-#Ku}J zN{r>U>FWUw39xNXpmyro;JW^UgMT02KkWN1@4|sPrQO@w?tH80{xkM6`fZxIoHB!B0#$J_wRN z$luclC0Bs`UwZ!Ck*NR8k-q$AM{2v|gL(tLzWk>nsnY#%B*%X`lKB6&BL!J$ZUPc* z&3G#R;Yb|KmOzB-L-H_|N}n!NfB=d%dt`9!`fb)P!JOcK>kNBh#z21oFhv`{^;6vEd3S~Me$;~YaTc>EI0^X*{3nz1% zMCP^z3|08hZcBdBZ{XOYsTC-di#^8_0^ZpPW#R)tfR!XLs4HPfjezgHpFK|y5GjWZ zRY)Q=q^qtnLVv}Z#H3>GB^D8bUqt>Q$x_WKV9u#vMbhLIrvt~6M3k8Ym5Y)52(*C& zfYo^reE%!ZX7NLK;;%qkkWfwmP-5wBqlFHj`mVh%QkcIxQhoGet0gV5P%M=meOAy& z$AE%_y!WOT3{JLzh#b7wqLC4MMCz4UTZCrNaGv-wjlfE#>t<4ykMukR(6Ypp#t$>EZ`(!KKWFHVz z+Hn|t{+Sbq5<>0@E_&p9E?5I^Mgp?7PZ76^LI7du{kNpK7WIYYjB@0lcb-#IhxuxQ zO3T8NXV>0250);{zv65Z_8HC~vPc=_qA>q-qyWiFH z>sSJxuCBG4zuPdzfPe&If83sO{dIeqnF&~GX{e!X&oRkTX0Y0~Y_~^>plp3YSs){T=&jJj!w9pYCoqlS}Rc`h_NNYB)riH>x z!;%O8X~+1PxG@+E*((I+7li2ACxM)p8)NkM@jLPs*%RG3N-W zm1Q(BQJB#GgxOO6gxN~SZ5Ar<%AFi9^Cass24xMC&}46Ck&^|xN)+D}lt0nX!K#?( ztbJvQvxN8Ue~@n4Rfkmri9=p61Kaz`UGp0-)Y4s;oO9ZcKO(~Rh(g)BAONwuVK?g4?_th|~Xvlon zw0VV^s^zUGM#U+)67|Z?I!~hC#IWz@6RI=EDS|`;2$#LSG?iWoC3Wk`9xzB#uBVIJnh$aO)R-T@D)`m#)VWPZ!ae`xH4ak^Rsq@RcE(@L(A8eF)bVa$`r>zw+?v zAyN<3BoTZkcxo?K%kH!%S^zfsL7PRnhQq<&YoquS0m z0k1k*qN`H5mvxikjbG^zU%$l9BnOAKf@9emwoC_19XC-TQR>@gcweW}{7Iw~7O_rzo%E7Qv^0l_#`lO&^R`boSE;0&xaUgBRAq|xn z0=?#p*)T-3v)IWFdJM)3r|6d=BfsBM+E@9PPrA%X&$WeD5z3)n1~m)9^&Gp(bn{11 zPhCfZi;*i|HwMJ*eV6>~f!^nYIjAd`DZrN577vU9`gJ*^wkjFjQ$u-<FR)>P7GiT@Z)NPeTfGiTx=kH-ZDqz*KHp%~ zrp%qPa_XJO;Eq#ELdH=A#hA<})`m3tfSn{dWwI;mYnbZ9^S~H}q#jTlaDBsCv^gAo zN?gV2;s9S2ROg*bTZT`vss&~6c2!lcKC9IY@m4FcI}J7FQrQ<1yD8XhSj9P|=j*1O z7@K|+a_RbH&8t5?8_Wo+6=mz$@Ffz|e&{TiXWsqsW$>Er`Jx23K2^oIIme^ACy4$g zD}!Y7qA2!GiS}#zG@xl>G3;I@epr*fepp|46hBq=biY{(Hk3{{s8Ys#W!K^zcMMdB zU|+C|B4jk=)i&?7Ao>y9NS<#F$F-Y54=^Cdu}{>q11lcd-Z&(hu$In|O~G6GNi0>; z#*7&&9eX&D&g$Yg!WHsdgw~%{JMmV&I3*cT&rmv2iRA3Wt?3u;I=kEV+2Vj4N)^Du z`}+*TOrayKoHAjPTkj3{_I*4Cs6MrQdzEeV_U~K#iE8=%rKJNk z&w3s``4d=Ca+u@nWbVg~Igi-$D8A=AN}l=l`SoKItbhvr$M;3|?I1;&JTY{yM$Q4M zV}{}8cx}U#+Zo^0XfHQvh$EvGk|WE$*Gtx)Z3n~-bPEhXGg{XLcZ z>l6ri1)JZ{?Tu0e&nYqUzOGOfxZWJZ&1b|y+H*UgvPkos&#G8DT`lyqCWlfBqR5|1 z?@P?vV{+PoUw35GGuPW4x~4QIRM(N!)@TX-l8rhq@BHd~e!RBj@6=-i92;CV`Yk;> zp35RV^5@=k?eQzFKKEIh_2%0SVY&1T)%=iJJ2t&2QD+?LGP*4pQ+0KD%e3><&5bw> zWKldU^Ui~jnI1&QHLX4GyF;sJh4Ild-r1z{By!h@hV^^+rDNR|x3_6?bzB;meDXx6 zcmrPZdjLPsWM;$8xw(>TjvBMoTD5E6)#pbCLxKM4_MJU$7x$H1y8H^U}%&@HJ7IBp^JymvT8RpE~n+b$v&k_MUi5wSA>1wot1_O z(ub@?P?0ya<*{A+W`EjcpVyR2o4Ln&<25$$YPTf)M8lKobWx7If%~>(?G|^j2UoqL zYS!mfkqy|&*c9<3La(zn?no?~CU)1bp9PD?&~cgqJ>5I!&j^_I)Gc~aYABL1?@vqC zZ95w6v4mgFbFckDjch`OXGKTBbnI_j=nLkk#@2Q;>O%KX+-!ZG7n&n0`iB)^W?>oh z)CXE`X-rbe+U3(It>=E0&i0e4F^e~r>)E~Rpyv&HSANaT6J}`S)B}6*Vk&vkcav-Qbs^VqmbVaM1LIN)(xNF8)|c&-r-wk+!IeM3d8KNJva zL$>(tpp)_rH;zHXlI!#~#~uinThpv*(?1PkLQVZ)oVPgbrK%V&{bYlaL!U|W$fG#m zOUgo_tie9#az+*~i4_=J6Cqq;mkrJ?0=(-0A|-jmBha~nat-}?}LrnBbU z*^uz5zGb~an@}z0O*OHlZ>VtUcDL;~oO{es&5C8zB1E&^6x`E#Z!Lhs_0n(fCPLf# z8B`3msIAF&)*;MD-Xa!|G)jq|Bu+{U&B`i4pI!S|Ciw_b(BWn#I2%kj@qsH0+tjrT&Wt#upSN*?y(f=Aa zWBfC=!2$?eDIhMQzyy#{P?SMSVtGM(lKqm>h%5r;MIxsVAQ|+5B1()%lthvGjBJbC zT(Gd*?bl!@xvpi7rv*w*kx`aqtSDu2J|>F5H!rvDyzaJg?oFHMeAbx%a=&6d)-$j= z9&6f@;YSVgF=z~ZkuT1peV_nUU4g;erRCEY8r$fbo0|&_h+BnfBUB>z7!Jn}sE7_} zCo%MX#I3?KIgu{;z!E?aE5eVcXgnKrLTSDKN2VaN;dRI$%Ox1$+{E% zL&-#yqlngO$N_OH^1e1mK-{Vj{@01UCaGuc$MGkc3jy%DFEK7MflQ(PdY70SFR{hW zu~=fvbN41$0XF8MXyVV6d-OzOR)t>FKn&!7N{YN&qJP5G(mP21O^YZIa{`?kpSn}TYDs;`{(vE zMF|#M18WNp5Vsnf?*r{;G1kk4Ri&fSuP~C$+$A?o zZ1@=qU)I+iMGLUP;rl&@I=RJhxu`;#;!OA{Q96NtEDE=iNfM&~uLtv%4;#)E838$& zz4+twvjNYd@UPQPfhhCk0P3%zJ-|6SpaPI^^ryPSL)o7Bjncqh#c?R2FEoZ0YqKnl z6vRB!6@WSuLyx+7dn=ew2*~jiD15a6;}W(B_*y_0K>A?2(6N6jS?%9S))Vo+QnEat zaL5lOLj+IgXh-&>D8b;xi%;_P$Oc0ql{DMFAQbBVh9cSyfiJur`5BZzQARHaB}3(N zX!zGDKs6k}55syEO$w(``%2*HJdhE&E6a{gk%uEqJ3L>SHE(Q|v zoiu+P88Dlm@Bk7u<-;Sn(C`Ev;zFs;)?@g&!x}sSx>qQ#uG{}lMH`TH@gDXTpy+~`Mib*P_o5;Dw#My$#VXuO7`<_ zC5!%0vSfz8mCT!|K)gxn-l^O^ajw1LNEQCJ+ftSE8{o6T)C!Wy#f}dpi~iqKvg7}v zWKf#CWB*mjn0>SUKKms5HzgYdD4FQC;~CE%C8J;eqhzT7B?CC+tYDG;zbP3RK*@pt zN_GxVGNlhCBPn8zPTCDaOhAJ9qL=L?sukeB1y5~xk7!K~-|)k7!;5xZD6i_sxc=2^ z&Z{LUXJb+nFld}li$Q{y;^~;IiHZ09(@~DiIUF)9NWMfH=ml5_nd~6HD8rXnf{m}9 zTE9c!i=Hkt@xz$b=M5CD-L%cU+kTV1x!2ck$_!@>_T^Tc-(xF(;RZ`LIrBgiF)Cj1 zJVTb0@@QjV0}Et|szB$6h||7a@pMm-%G!KYfGw$2hU|bCSVGwH_$zHy<(SjX7W8Rn zzu(UHK#vR(0)b-^_TW&-3IVWZ9Kzp?4<2M;sQ%#%NR?0)sr`(u4HPU|8pfJ`%B4i z{zb|Bqz>rxdDm*MwxBajgg??&bRTIe=5Qay>^F2hh=vZ%zfV5<1dwpHKhjpzf2FNx z5{@4%5`qee(8O1XsQbBcxr2yvFd*_L%xiZ_U+Kt6q9p-y#@ior#$Jp8)p*Po#23v~ zWkf;;9{4>KV2L@8fX;2N?_eJIjbiPt(IP0GJyUs~<^*zU1#{Op8uBWcBD=__LSOS* zGHK#TWc>RArc z&UT$FwVx7P%_=Tp%J0g=%o}#_0?zRqzDh4qa!qh;nsaQ}UY$Q~5T`a1Bw*`(e}3eo zqaJQ6(m%@0emw7FZ|M1x7ds~4~Qs#a%*k9B5qHrio45}#hpX-EA% zC-CsOhLMS1jQ!BiG-;XmQZF9-lBf5`#Yep-oDJoTj<&$cKJu5!YmmCuxkbqpze*+5 ziMCjWx_-b8~e_jXK^9iS{%_^s`#MUB59| z4ob@jCORYS9%!>|AY=z)Iun>h>>2%;&FNI4vhn_+I0;NYtq?uurpo@%ufa==^k{x+ zw0;OYC{0vJ->j<@6+=RuCkvzU>03vtgO~^^fb#6}$&*L3 zzCJ+F)j8W;si%jzEa_?NZ(S-o#&bCOE(K4I)I`&_@m^g}f0h%7*F%CyvRqi+oV&SB zNvh9f3HQIORoi`a(OZJKZN0C#!Erg;&2D6NXb?%h5L7$A3lUN8xlv4*b9K5hTRk;W z=xoVtjAjy>wZ<~e0NQKFln;zPLP7pAj3`g7hQRc4`R&2RU~J{p`&wiTx%J!{fqg>x zx(BFC!FsH*6ZW0+GQN0?%fG9$q?&kfx^G3bS~4U5xg}hAm#-XS_#Sbb6q=-jgQxiw z=_z}8Oq`h)dyRlJdg{3)q4?Fty{hAfyYxJ-XRbTMy4%`=%%-llttF4V^#`;;z&gk2 zk3oPs=f&6y?r%KC zY2W9(E-mBV?l{ElwodFk_KEE8nqSQ@a{~%p1oZGe`xvUzFxfxs*GVX8r2wwMqKbGclVPY>q5!W^n98%X6)(8R-XuzOO_McrT|O`L<6u-)=1igEbc*A~NGBn{l;!FuIdzKY(;j$GnQ5 zEE~3BU8xP%YCB`D(PGmG60J}|$0vPfM_;0?BRRv=Y@q1;*;>^7*7iCMBBZ6)N7a~^ zklH}GGIpon7PJ%_u?xCh6|yiHHFc*ijd10!L$t1@Ma4N2kEs-y_1i-)q|urgMw~l# z0G-ilAUPtn8dr!`kXi%%UXC)|jik#&Qt@YCL#yBzHkV8Hfg5spw!G@*k=$rYnQq4Q zO}4CQ;z3_*=Qvt>`_w>)%7LYBH+w$x5z0h*`S|RJODj2T@l}s>bFIt&CfDb36$jm zN{z`U8z{!espG1VX-#@#!a|k5V~$-?CzBcU3^7K9gsfuFn^m#zd!kxzb=C?a_7x8B{&@4AwVyeBn0CQCmAc+~PNcDdd<#PYk6hSt%MzFWTqs z(d4!s@37yquRn525&vnqxEHY?*VgQHYtMP)8jrh|SO70e(Zsx}lXO=2nFIP; zRXVNup?a+9tmE*>sv)Ra{&5m@e5)f%mo!l)3d#~%U8A9iw!B;Wb3>v3QsY>W3TuzY zc5!1EIo(kE%IDSxJx`6}op9b2Z`kc&pL{Q|7ar1m#b9Tc%CnKDTZ6)-1ZL(g#r&tR z1yZrv#df@ZcMC+6numZ7bYzZU}4DXm_m^FdM?ZQJxOK(;0H zUa;p*MR0Axlqyh?<8L{yR+@FsQ+-L@5;fyYnJ(8CnMWdVUFV4(PJ|!1IJd`%noX}a zB?5O->;TS&)54a75+mbikn}U*5)(R(SMdhdkjn|)xNfXXPe}>Eg7kf~7S%;aFTI!W z3og!IuugmGPldxP|DrY<5wHPYb;3c2z{l9F;`FmIzC?c1vqfMQX+OD+y!E z=HBs2YIKX5-~3{2)wwuLY|;u>osJ>nm;A8q3lgrQ2Xj{mVO$(qWN?RC9q%^HYRQ3q z(!M5VO$_kdEnA0B{B%EhsA3>HH@NuZ(vec}`STZ-peYASViF139J85FYlq-FH1#KbqpQB-|kLDo5nF>;WpCl zG3^|BhPPmM4Tt^MLYda661@$eK8h*;e&|9ouPv?hJtT6?Nds~G~H6!t%AyAp7$ zwy$rDB4jFLEJViFJZCEN7-gR4d8W*B#xx*`NJ5$C42euhh>)3x2qBcI?;NhY?;HI| z-?`7Z$NN0@?)^Kzwf5R;uf5M&<}+b)VRVFxUZf%V3~qc;;wFhRh7PXJUSQDNs!}>7 z6Oa+WrhaM4lr-~^Q$a*$8!yz4IWKne-f7IeO!g+NNDiX}PoGD^xwGDwap zVKtspv1O3G!jh-J!d?vFC_sWBX^P;7VTQ?+-db>QV3QVsI4D~kZ$&)W#~#z+dka%T zlnF{$U^(Gp)lLIC04`9k7Qx_eVZ#IufmC^n?7x+LW??b~|10QlY01;3dp|amS0>Ty z?UH*=>R3k^vfmjf$S6QMS@|%w=m->2FwDv5Z|Vys{PPBGQ>*q!pc&{h^SzlhO&>=u@Nl865bkSji_1PjOA_C({c0L4C6o-q%^D_@pE=LEw;u@34y>cEXbD?$Y zF;>WEFz?2TS)?U_M^^m?$b&r(LUIT?8>HK;PkcI}A7R8Di=gYGx-<*9B<5>xpQnLZ z;XQx4f+*zTn1->%t5wf$tZ8XEJ>Ez*T0^03U26G4nrE~sh#IMmA-!Sd`JkrJEBd)h zSxi-eCMhW=sBvxi7p^kS$ywj z?-3kZOUqRLDI+(X7gUX+Zl7=GRW0D$ICkP%XUAzheQP7BleuWEW#&KRUL~wVnzp%} zk{0l?7nPOw&&Uae-%<4=q4>iD7l ztz~YCJ8#{2))%f(TA1&8CcjVG8o!fyDAhb>=vZx#bPUJ&jn2!*3B5PQ;$&7H(E2r^-L14^0F$uFZpXxrbH{3Gr#l9sJVs3k08|#VW)gh z^mEp>7iBn~HJm>tvBqlS|F}sr-ER8gHxc9RF9ce`xLpJ;>=&f)U5?JQRUkOM#-&pV zN&6&O?WbZgeIZ(S7#spL@pj+loiEfVpM5H za%a)`U2D5^(6w&t<2>|@H*SALJVRu5Dh-WFR-M`2B;EL0w}1TQ)H&}aC6l6VzX>K1 z2Cv#!ym2%(I}?wx4{kTQx>kQID5z1lM2g|3I@mJ4uKk$N(49J@PFf}U!b!)g*7gW#BsqUIR?d80^yZt!&y z%_Zsa=3u+g>dg|JTC zMu%*2TuWN0wJ(NNh^U;AXSm6dF89^5)9K40BP!K2J-1uFDUug->#Ikx*jM)-b5!TG z9He@E)2f5^XKF7(3onbct^8s;>oFJY_ZxnlJU7g0ybD^{&5N+JJuijCuFVic;7&Ui zK9lUyoQi+Dkl)(VIM;eH$>q~02E}s9X{_RlXiD~8boH)g`X||YiLq?v#pD<)7%E6w zrihKocoTh(GV^Q9{mAlaew>sT!)jLT>~UPb840`8^1YiYQZzlv0Y6_8SlLq4 z4+dS+!DMdjo}agq-Gn_F5R5Sd(FGiD%uZGE8TU1n{tSg4uWQ7W7mHVbM3r1Ti% z!LVmn-XvY{cK?yEuK9S3v+Go&Ct6x&<-KByBvej|Cxxv_aZQ#ZdX+Pu7c*~G8J}vz z$VaY!!a1e-Vd$~jXh6!FAEd@h1ve{_>)ZMgjNYCJEf|^jdU{p(N^XWqK=kmUC1FH# zmW4-9j+SY;Wg%vhXlN6m5En+Fi-92-ea580>5^8|H*LwKwCW9W!lKBUp}EmhV?U>71iR|8ORY_pK6Py5jjEXI^K(IK|GBG zXdTr>%o~$;Sl-b{e{Ig?K!mtdtzV((2rW@hB(S|2o1|KjUKQH=;6BrX?Hm+S7S~u%gUgpgy%09`KW;csG^(d%!j6bo!ylHZVa=~Ewwi2BW2l>(I zF|iKCzL0vlBEr#m=ko+ap^u1PaU_HpK5yD!^%^XTcT()O`LRavBQ&qcz2K$v%uNR4 zw3eRgZw84eRG0ftkEx!!q7v2E#C61!gkQPr`>T*d7k0NXP5P%M*BIG4t{F=OhQ%oH zIJ!)BOqX@xJv3FGr|7kG3{w#99X;D~^~;;^h+0Kzd#?JciP|&@r=L?bC%$=Zw=N^)jJ<~l{6xsNa^A@XQ;Y)cC%i@^bz){ z>kd{y;hxu)W%QMtfYKNm66ORfmjq=Qrb_PTW?$xIe$Y(2QM6<#)Wn=pJQ9G@Tv*q8 zqJpW&Tk%J-x%%DJ`4dHu2Dh2&a6kEvF9wLeJSSU^CD#1d8(RM|%o(+2=G|p15v5U2 z&DP2Jt}CJTu7PCFTLi9GeA6ZM_{x2Gfz#8mc&a_~mEw!FPsr8jmEqG_UumrDy3b4_ zU$v^v|9(&Ft%WL^zufYTw@>3V2vBX(UNCZ58zgctagnv9E(s1_d@ObGJvH`+$cx{} zidzSru9w+((W$wiaEAuW#!8|fA)0wRUI?V%)W{4naB{6FLNU@*G&2ZlGr;o-)mgxK z1s$Q8W$Ho0Pkjz8^Mej8Ja`6)6!Ouz$2jV=k##bL9$^WZe-j|kA~_$=s*1&l)wyKw z%*x8@%d^ixtxY_bRN7%Ir-^X={Ul2;F#^#EZ@utf2@9q2L`QVcLt47x84{|81$@>+ zb&sXw(;1n{JE4%_Q%CFY^)5s@jM-ou{}!?4z)3E+D#xo%yFaO6eN4n`!^pZOI$2*7q>U_N0UZH zsfju(nKKbg9w8Q*BM;P+voprW{VCafL+_{$?l~HyD=0s2=Zq#H8PYh-8vIvNt`gPH z{--7P5#!g^JP@RbP!wvi4h9~)BlJ}GSC66;Q-obg5-`J<_UMIe zl9cf{gnmJ+ITu2Pq^MjXoNRIsq+iwa)Siv-eDq&UIc$W;(fQjV^cdkI3Bd@IIMxfA zoSQY}o^95Y;~}k0{{hsLqvZ4rrpU6i7I77MVyybT6&ddY3w<>u{siTT>a(#FvEhm%TZXf!^jo$z7gz=|K;ITL8|16XBXTU zzOrBPt*UB#l%mfh!&9b)?~DPas+OnVex&8morWI$N)${FbJlhZO(YkUO-u|yTNgX2 zPZ2^P!jC9Zok?UHV)_=>E{fa!mruQEtc?9-n7mmbE997qo{y5##AjZ#c+J3UOU`<0zFnyvZ-YNAULKX%#K$OJt^r zqWqH&zg!lyK|Jyz=!fgs592p)>i3lfC&c12bQXqbhRisiOFGPGO1aEyx<)pO-13Od z6nP;1AVi`*Hp?i*@0|(Ge9f&cIab8ClUW3m=~V+EVmvuRUU{nmC$Q-KQD-UXWWN&y zJZR_jiDD!;=Le*RDPcacnI6WjfFMm7;W$MOS$nA{l5oOUkp0>2>t7IWbt*}m_{oJI zt%AY0NF8T1fn@G8k2x>e@rcIVlXUP*)vCVk=jC1|e7Xv=jg#@U;WGF5?%WdXQcbzf zau&f2``)xtGV;9%rQ08}PC?>ypA%2BtS8|)V;ng~uc?_XXLI{Vp#+yY*=#0OdnekJ zr)QF#xUD~@a3(#fAnl~&>~+;jH?_rUdw*2zgd`5SkFfU9W0;g_+!Qyp^r^&wJk_=O zG9s#E1t&h=3eR(B;#V#`iztXI6;L2O`aTeaP@GDpfpjxZb;ioW5ZnwDA6qQ)6V-$Y z4ChcU&>VFq^^-v7?L}=xwvcAYMY($>KcD|(goRMcdOf$2o+g?UlUk+0vC~xP2stBy>45 z>d*R?oFjWruuO3R4bN8;AC1ZN=iL;kd_l(#%chJC8Is4y9uyGXaXXS65lSHPGlKNvF%*Ws-Xo!WYxUL-M&|6*%auItZ~j6i=Rq*hK)@)Br)z0Uc@nb2}loP z0HlWjvQ%$0Gw=fGVOBg7b2x?~zczfr>ds9m3L%)#MkGL;ICDFEv!q-uP*RSt3+;P7 zDRoamWM)-QIXn3q?=O~=oU!7|@}HN^hf1%+Gh$y57G{tSwhn|Ou|$!0_<8op$RUyS zdE1$?pS^ejYg*b%htRM`Nf6`1SsjMxwe*R{N^&e`t~~5%n5UK;aA+XFV;DczfZLOx zli5sZ@N=URM>qH9WrLfSt&R&DI~?O{ExYV{a}ERLWQN`a&Mt*X!TwUDQ032OSfWvK zQP8lfitmUPu=>#A*`Fy>J3U!`i^J+CPv@v--YE(;PNpuE==`G>sxOEm^OjlQ9g9^6 z269w$JCs~@zzlk3Gc6GC)+}pQ6Njb9a8H}1}zIy4cl5$zUO3D>q_^qTI zQw;A9UaqnjnXc$XiF&Z4+)>$D+tbv!60|2NTrN_aKStUqV=9$j?y7N4qt);=yA@`} z+GI#AwO(hI*E0r$ld%|BoDdsiVrib>J_CNRq}-9sl5)i;4jAO>TP5Wr-#LLLLmXzZMKI^gUWx^y%xbTx%|EsZl z@dYlILCG^OBQ3u+deD*#_1sP27PL%H{UVPqv67-~-nq_y-eSFm(4v>bTEP8=QJ$xX z!3ze}xfUUfii)wZ@~>H$pWB{Tmj((WUuL*$!BHst#r<2003@O8?OMz6F0F5;r`}l` z5~)|pIbd9U4sj2XbWeRK>@byo|0_X6v23l;d()2GP}JM{7@BA2L@ioI7YdrMgam%E z%xf(9tU0E_UGwGZ)5L51ZMK!Q-H8||`fhRyi4f}6BbpHi5JH8mZ>%6{pF+BfV{N>?X_hDbF=t8?C!Moz`u|FPB8^4lqNpB4E!2ny4S{>#x zx%H=S6QeH%$vn#1TN@2VmR(k~nKN6}qO8(yhsVoA=98RKeTGTm@e_Bg=^0)qc3iZ|iwt*x(X)=G zIT!VaFQr7;*L|D0T`eTAX!HDXEJXgw$u|#d&sY%Y=*-UHWUm-`$`)lKzom)Cb@m|B zx{kg+;3aRfnxoAeb-R&AG=Ztp&6VY@jWtW=U~p?3t#8b1(c07GZrhLd?ZWQ|+jFN_ zC$qILzal-IhI7A$jftYUb1>;m(^BcF?|~5IvpVbDsc83B`$gW( zyou#swF>Sm&$gdhc{59*xU3ZO)a1I;#D%Hk6lDSpHpk-p;p!XLxFW5C>AMS9Yzt4& zO0Hk4ed_zY@5^I9Er`6FQP05ryU$-)zArY?@NjzbcBW$mlKYx=q57+h<|Q=Kj+9vm zKD~szGN00H#fD?PAFmA%PMjJzwk~Zkc(n4aqFcvO`o)uH+(V}W?g`yrTZrk<@r{_! zQ8zZ>`|!1)ixOj{Mo^>OLqMh)QUFYz^vs6X5^?cH$=AM%`9IbEI#<|2a| zUV(+RJ}DE+Z7tmQ`n)8nEz)-_71k6)!zSf|;(JuKGALY(xU^Gf7?(%`W1F*F_!ZlaF8tLO5I;8bZ3JsL+n5^9U6D)UhD^RL9He=S31% z$bYRT%#1U3eBF0ayKQ5=KjRkjIOAkK)0eU-y`H+KA4%q!nVxD`dp1|G6v|5&w=ZyT z@@ynmd4%(|;ALAkH<&!Dz!#{icjtN{^>p5{G4B4GXLkxm=rd2XP(L5gSRgb%^(2M1 zOsct@NzPNZsmCs_!1POPjqmxBOnhYJxvx2jT-FQ|W()VWjzEVhfzL~{wc+8Ts3hKTphzK|n(s%Tj&R(u7wQqV)0km7;8*@y2}+W zxm=?vB`5Vbs-+j+j876AA2dkO{>p7&Wh*Kynm8T0WK4c7;gQKbN{_kMFI9oG6)N2% zizWKTvyS7P-i>UL-@a*HePNkG$){j&#?MAmwGU%`QJmPT{u`Ix{TdfMb5!x+PcLVc zq^HG@FZC?yF$t(X;!O&w(A8^G2tsxr_8I-coUPXJiGalIOAoKbha5=Va6tT!<#^y9sScl^o_nBewjmL@p~e&iVV{AyZH;z z3?>Gd4b_jGsD3a=+RC_zEO!}zn=7#{aN_B zz-p7QSZ9QI41;-wXjV)`$+tVS144$i-K}MpTn$9tzmQ;RYiPnY@)%wYl6<6=*B-ns z-su_dx|oZnWnKTWvL#t-YIeMZN5lT3Q^)t0FO{V#PPM72KaG&1=X_iG#N7GYcgetU zkCVvtpGT17E;vVs?bk7%wStX1ovd-gg zy-v)1eD(Z}*S*BSDcgzc)$5oKZ=HWKJEQX8JEQ@g_HPhHkbVLM%v1h+|s1tj7xdKbm`hq zf8Ue4#mhtyY+ov<1srF}fxHNBwMbdnMuOZ#Ym5UqIK!)BiET1e2*VQ>+)v@=>Qy90 zg-AUVy7z#ka=x8EwR5&)a;QHeHA9si%RDPXkX~8ExVvBjgC>O4NPv&#!N`xBq}`G) z$u^6jeKn1qw7fghCh+{y@&sA`qw9{5P(Rkl!#_Efz3!geIt8$`J7^08RPH&@h=55` zz#Aw9w!cm1|8|*~Z0&7qjerkqj2xgh{hZtDPT#zN`9kQmBsmzp+3L&==nQE8*L4o~ z>yJC!5Ecj=nv3}9#lIZn%T48^0qfdB@fgN)85dS`51cRSJ z6qa&M)>qMqx=;9>^ToWwYDh03j&oP=i}2dp3a(OG4G32kn&`C|m&)1Y^~%}%vk9{) z`LD~vPLS0ad@oG&^62V{M2$UShn_@=iby%SyjUH36Zeb~O;l9-8GEX`QjZY}&>}y| z3vM{va&Y^e_R&|PH0>5WqAh}uZKtZ~VoubSU^7fVCS zmg`6pBQ3$JB@@wl%8*F9Avfh2P#D!!{3)4wD&b9`V3(2ir3}Pa1j^J}dML6DnSGVusY2Uq9K!218rtVc39C^kdl8GDneqvYpK_Gy z!Qocs(s`wkdiu+a)y%$iWy2E3wI%blX6es4p=w_CfeIB7_Z#k&m2+^(atF&Bcx5Fg z`rGwpOvqE=@}UqDzBzom zcjf$rFLdLc3x5nS>Q{JQQ>OC%oxONQhFkt+x_6HjnDor=ernE$SjCwk z!+$lMf#>fd9r#%N;tj0id!EtA`M^Q?>^4!O+i3g>Xx!xtgf=uzZ1;7Waxn8TZ>bDX zRyZ{zBYwVH_z49?bo>e80NE+AWZn0tn^G^LkdeQ(Ak>09T}%nanZZts3|z5uaYYI6 z9ggPnMy0}efS;UX8T#mhh^CK;M+w~^H?Fl=s?EH5Hqm8x83kVp!?o=ArM`gyItcR0v;M(p#ORKjZpqnK-+NBf zMJ7$uVnx!np6kH8hbrA!f!l|c!_n+kmw4)9I@$dTZMVNRQQ?q@C9zB~a`ux!uqL0m zgu3G1ENY$a;38TaBP&M}zv8Ezr(KpO`sECU?%Vlg9A?p2PC~&Bl3t3AX0kQ>kc8=P zA{^HqlA7I;@!`N&CU*Df@|qlxDHIcvElXvi-8pY_f3ZHAYC$Pgj9!p_N$N zwC~rW=IwLQ+v8L?UQ0(I*PQ5oFNNJBb;|c?qJrkE8FpjIC;k{( z66Dwf>sN6J3k`Rud)=^P@Dd({$J>^SYu0>}cr_DvCk*1kNi0OpU*=r!c%2AXs!P51 zsVPwR9~?HR>KM{mqk-H1)KL`!4pbt*n}bohijf=&~y{juO=;c&=j_6Lcx@n-RKn(Xq- z7^OExTkJGMwYZK<9+}ize_ly(rPx>~zSrgn-6iX;!u~wc5}%JT50u{1txoilPJf_sA$Ri6)+DymK0;+yINSvt&QW{&neoB7ntB2)fgDt5L9uS=wDLN2G_2v6X) z4*N;8$4L^MjF~gCC4YJsM{d@JsQ~x6{}XldbbbEM15)Ssa1%X8RS$=+!6rZzo4?T1;eoTWwLV8VEC~S>z{?V@r|u_ax%5|5jUT`RS$=ow*XX zA$bjTle6su+jq9-tF!%`b$!q($5ap)X6_(0VfR>d)saaVBvpRgo`P}qzR9e zV1WX}{<;q01nd7JCs?2Pubkk+7(AfMJ-_Pze>Hepx(ynWA^-F$fHF)7Iqg- z*&X!&2jS&1j*Pz^04oj1-YX4gpfAct3+of9h>^T*n($jc8zRuVcwQ!e zNq^YgM>C?KA1Vn0$TI1PPtM@9qIAG%W&hy!npU<+&QxxZu3^DXunR$B_6_EuEDL_t zllnmVdc2IniB~mp2L4%+#im^yoOt!!Iz!|`{VzHUg)$s0LuT4e&Q~s1&QjB5JHAiT zi8*F)zVm%vlDjh6>*71)T{9LA&dU$Gn)nl@X@~;^43k4{oG8j5XmZY!Qt{y%#A~oS z{Qx0D%?y2vLa)`24ns^zkEqU5zP?{@%%8u^UjoP!BZAZ>O|>M_aE8S3Du2MZsoDsd zK^D%P{Psb~bJh)NVr&x}LHRR=R7pevOgBaaw77W8@#@ykDe2KDley?V#Y{)XmlVDe z;3;&gwVnFWJftE_pjMyJ_x9=(My5E^oN^=e$=sgC8TGTGSqYwD>zBgY?|!3xM&8Rb z+{j?^QX;K~xpeKu2G!~Bx17YDub4F3X*~LDTOk{yjQ#n%H^!qQa)cE6Cs3Su7P-ZT z6dqgE6m+)Lp1*rjvIcTVx^z|#5U0hiAD%>PQEo3GdaOK1ESZjJ8>rV`&TYHMr0 z$F)p*XE;l3wyHGp)hNC|sb1aiP5AWOt>qqm+4;OtC$Ceh5HTTiWMP~mgn=VLx!5FO z9_K{Go}62KhcSEV@kr0Bu=6|1^dg0#R^xdk7+f(?|ajSQ&xVl8ONR^AdcZF6@QMkQFbml|6 z#VYLYVuJOB{Kr>ua?jPKu}D6mpi;E_h0$z4iIW zjiZt=??fP^c=)W*I0lXWdB&)7A?gA}X&M(c&UzAiZG4>1WTH`x9P*#ycz7nYFYPIQObkZsIWCMFTZ+L=C&b4GfGk~~oU zqkUQUNV(H1#8url0js4fgD;HjG96&`tpFaPnba~ovEmD=cwD zDLe|RRrJ?og;Y7!s_>k=YorZwjg`1+J^%*-S(+%+Pzm4Z1ic}-sjX%u=gr3MOsqTY zqxZ@|wK><3lPc%Cf_C9I)O>aM5ZmFGUmu&A;i5z*#ZW$I-5qon?jz1zfh6h+M+k}&jFS5?vx~xOXb)V;LlPFQ&vkiHPq!9(j7DI46M35F&*U{5_=^9;&e!K!)hGYd zNUP;~L$vL#cX6-ntLfL(10uj z=wjbVXUdlmQb;YncCKBmBo}#D5C2$uf`0oKDm4X@^4^-;-Z=r$k1OTVnU{yO&Rsh* zLH)Uqj_(zsJ^CA>r42nJ^!B>yz-ta9{?7;fbVS-7V=-TTwEQ7z=_Xx{@sOb3N!Mxl z=rMky(w@*W!J5+)n(SgT&X`rVLe`v%W9=4Q--mV0qgJHMlOK`4^h}vR077KPD{ROv zf`PE$)GwB$k(vC-YH+d7ZHUiiLOhf39>4o@vI=n_wEMRwhb7!{ulk!cXAqACG(@!p z8-IFv^m3eFHWOiH!`XoQL{{}CT%)p---sttTRE0r14UN~UL4OQ>Nx|qUN+iZFMs=9 zdFd@h1O6a0$!&$mZm&GVy5Tz+SiL1?8m zU=>-Eim0{t<$+ZKbBE6d4lIl+uJf}4Q@W`}ZgDFn54gXwj=MI|s3_~HFO9tLcKu>4 z(Rhx{9Wzuvaz@)x@4U$BaN@ET@I?zE5*hNA9|opr z`sMlpe~|wAfrBtD4F34z3L3hp-D1*Pt$|Jb&u}>4MQlZ=A%Ii80}#GddKiE{{|g{g zt6;<$wq5v`^>))KK8*)z_kTF=Tixy*qX4@F95jR!AAaERN4{ZRXKx1{`b9wpR(?Q< z3fh4Mdjzi(pXGz~$iuGB!pQ;ns@!ZG+(6)*6>z|fxY-z44T0FWF^`_HKHyig=s-5z zS++mBv(U7FAZZQBpF{vYN#B1y$reNqWC7AaX!%zdp-KF+7QRE@KR$5DxL++K20rcD zuR4H>qCFRM;OYUI?%fuSAl+M97=EyB_v{tAM^M1weM+Z7p!REa1S|mj`eg3z* zj}m?0ko5lXG5srmJ7U{1*6>;gg5A163rh~01$Y)0{8Zn;`8LxK>#xS zGlV<3wx5a04EW%5zqbE)IEb&2l+$SyUnf0&`K4G0OHp>0JIy*D$ z^apyM_I{4>m_VE04IUiWzEXUyAE?~}mKZyw_DTS%)z?d*YpPx_*3!a z;uxUY%D)+T zt>G7bH4n~44u8}9R#!~s4mQ?1{A?L*u(!3ie%vge7aKsE!9^E%Z7FvD-}JWmUYngk z+iFk{bu%5pa)4*f4CplHRzLrBG0vi4A0woj3w9h`69F44O zVPs+sCvsS8*0S%0^_*P)*e=I5-c9X1$-G~{V7pBEHa3n9j`n(ghAs@#h56rh!SpM$ zGpzQ)GbISqUE^PO2j-kGGyHR3j4*vsv+S2W2Ln?hD_BnyUXuU`|963!WV_#{9nGwa zEX}Nq{uBDvkOSuux;%YdH*k%59N4|zZW?%|_>>>GZRpLEEo*IPWa+49U}I%v<_HXZ zV@op=Q%C5r2>O$gwZusS0D>wkcmz+__DS5Yw}VEo<;3od;MnHdg6k4njz`(so)I{& zZeKu%0Wc2xvZ22aFhN7u^2c_Auw(tUU|7P9Yn5#bz%#`sn(Lr2{*1!j2si=g;AmtB zZ?j_g?%m&JLLUagQSO66fMH@&9bl((E)ED66Eru!7W!M1z=!u>Dak!B9%xE_*Unuj zar~oH&hi|T5@-m&tLL5b!D&;epeeVh#)eMk;^6u$zMJjpHPcFaDp7lV4oG z;{Ywj8okZpwwA(<*O%$*BdS7)-!)_xA|)FWb07V4|Qous#0w(&uhq5UW zSi`~2!paG4IG~Lf{H(w2+#ziGF)SAk3dPjS!O_Ov4aQdjUl$kkR@GU6aqxy!LH`5y z0W&0jw@kmQ;EsLVdqk37I_RtZ4;Z_Q$Q{Q$EB<*F3UEkc;Wz0g9vH`OLjgwmY}}mO z+aQ6hQBV5j+G0ULe_}6AZ?7AobjL*x;0CX)u#~aep#knI1`Ar5=I?C}r+}TO zIl#w-9Tptd4vk_DnxKcpZbk;4skN?oeGr>Ja08%zUBEtg*r74*N9T4K7dtAqH7SFs zP=5gf{(yB6tHTe9b32R*&4m@v2XlTpT{NI3n1ELdI6<4&mevqP{B0ix3Oa&$c(*Pv zRCMCz-*)S0Q@cjcbGt@iB{=5Eni)6xg4(r9C-umm8-$!UuX!|`kiPrn;zJA5x zphqOQNu>WYU#kjOunPjqWusr6fsRGx;XC`?R6@tLwij{Gkibb=;R~`<&90CTqyLuc zU+)hMX=^gx8`92#C8QkZ>vnX-9EPP!_YMsSW|6sfpab4!!=D~8hQYObcwEp_{SNEb z+ILHQTU>#=Z_w*+Sgx+e9iA(ACxsPy-TNz^4$l>+R#5b8COF^zo#KNLe`s9LT>T!d z*psVm4B!)X^hF*X)BdP}AAJ>}Wp+Nmi7`1A1O#g+d?`M-_x~wPyTlIIcp!U5vEWt9 z0tOp->Y=eQ{h6;{GKAMHF!~CG1x~ke4i5}kw|+NSzi>g1Ll6`6ID{pO{mJ3U+9xP@ zvf!gFutux)@W9}O2!XK=yOISO(!I%&O#8QF{lSvLlLdO^(7<4=Y0p0Z2G14?3|re1 zG(0ZQn%*zzRNZ|>G*ADxg#E7ad*Om##lTur;*3M%+8bO#a2+On%(cLvsdgC^mH;Eu2!tT1i@(*wwP`BW9A9N`s@`HHl(1Am8jPUNx^7BIz_nR;HAq~cW8j{_+S%Dv9Yl8?71liA5z$kg>7xg^0yos z+irWbpt@I8Humo>v82Oy`kS5jAB^ls>=>XxfP?^m(1r}wYmII_G%LRWgc*KJzpN`f zFfdS39g4c22TQKP4Y48>do;Y}S zAJ!v#^X|~Z!QRJ6?@`rp4`-JapTLz4!N4R-4J#Thgvc((`ES{U^o8q;sq0#>^zc24aI2BybMtzYE-@*cks?(wIz*^z0q=jr1I0 z4nTnOoTA)X6b{e{c*|_}20T-Iv?l((I~yBAGb5;z<~u^znoHncl{E`7ubptY_pT%Q#JJusR0Vj!kGN6P}FuATJXRGJ2W}q5IXJAQfCLi@dj@D zZCJoF#m5NP?myT$1USuxks3U-Egxtb8Yl2YIw#N05Z@8rhR__zU0ei&Tfq3Xgu!Qw zcsP78@Ia-4wz|;F0lA(4<~X34`?ICZL{Lin%o{gJrc)J?`q_}w;n;IP=fQhb{JDLDAy z0IhF8fTi&L0lipVbc9kg#qY17v}x4#X$FN*HqzFeJo)A%UgtCd1+I{gJxe;6YOdO4T50 z8GZZE8Z31Xp~FFgr4C-{phqn5^(lDf59$QvmJJ5+86E#3jtXeq>TlF>T{s**80z)| z51Kk~VjY&5A=#e3V8?5{^5LMtQn!!!VK=2K&nNofcme?-kQZL*>MtJ--XEERRyvl= zF~P|LZHD1}XLexBZe5`7TxWbxXqzRefBVW#Y*>7r2Y7i=;N@WtrJpkSyB_wgJ`SzQ zY+I`QpR2RwJcE%Z6$MAoZK;!YodGHN08-L}0VQ_rFG1~z33`?Tvb_Ma92fL}-W1XgsXQ_O zh5~Twz`)@N`xkKFJpvZ!RSh&4aE$bc(4~L^a|do%fpz>Xn7vgPdeE|MO=M91)0Q;` z844mGu9pS?=>j)!(th*WTF$%<|4WAUwT=OOw_?>!13I8b!2Lh~j(q>RQhaoPq&-RJuzggf9!7BKpeBeIA)&)+u&;D0Oz&q^} zgW1Vd0e>)we!J1lv!wF}XchMJdl2p3srGm?{|7BlXn|Bh`=27gd_C|bVz%E1_^RkY z-Ttdw>d@K?(gI(u0K~Utd|&1th?aesTu{L9RR(q#aW6PfkNczqdi+595G-36xM931 zP-5^2?|1M;75#tU+gJK~=s^yW2%p$~2c%r_fk6JtH23fzw?TnV*rB|90FbZ=oBlkM zg-Z7S^8YYjVcx;I{U1YRYvKi^44 row.get(i)).toArray, structType) + } + + def getInternalRow(row: Row): InternalRow = SparkDatasetTestUtils.serializeRow(encoder, row) } diff --git a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/hudi/functional/TestCOWDataSource.scala b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/hudi/functional/TestCOWDataSource.scala index f500ea83120dc..0f5e31247102e 100644 --- a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/hudi/functional/TestCOWDataSource.scala +++ b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/hudi/functional/TestCOWDataSource.scala @@ -185,7 +185,7 @@ class TestCOWDataSource extends HoodieSparkClientTestBase with ScalaAssertionSup val inputDF1 = spark.read.json(spark.sparkContext.parallelize(records1, 2)) val records2 = recordsToStrings(dataGen.generateInserts("000", 200)).toList val inputDF2 = spark.read.json(spark.sparkContext.parallelize(records2, 2)) - // hard code the value for rider and fare so that we can verify the partitions paths with hudi + // hard code the value for rider and fare so that we can verify the partition paths with hudi val toInsertDf = inputDF1.withColumn("fare", lit(100)).withColumn("rider", lit("rider-123")) .union(inputDF2.withColumn("fare", lit(200)).withColumn("rider", lit("rider-456"))) @@ -1021,10 +1021,16 @@ class TestCOWDataSource extends HoodieSparkClientTestBase with ScalaAssertionSup private def getDataFrameWriter(keyGenerator: String, opts: Map[String, String]): DataFrameWriter[Row] = { val records = recordsToStrings(dataGen.generateInserts("000", 100)).toList val inputDF = spark.read.json(spark.sparkContext.parallelize(records, 2)) - inputDF.write.format("hudi") + val writer = inputDF.write.format("hudi") .options(opts) .option(DataSourceWriteOptions.KEYGENERATOR_CLASS_NAME.key, keyGenerator) .mode(SaveMode.Overwrite) + if (classOf[ComplexKeyGenerator].getCanonicalName.equals(keyGenerator)) { + // Disable complex key generator validation so that the writer can succeed + writer.option(HoodieWriteConfig.ENABLE_COMPLEX_KEYGEN_VALIDATION.key, "false") + } else { + writer + } } @ParameterizedTest diff --git a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/HoodieSparkSqlTestBase.scala b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/HoodieSparkSqlTestBase.scala index 2e06da2aee512..83f4c07f35a72 100644 --- a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/HoodieSparkSqlTestBase.scala +++ b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/HoodieSparkSqlTestBase.scala @@ -267,6 +267,26 @@ object HoodieSparkSqlTestBase { .getActiveTimeline.getInstantDetails(cleanInstant).get) } + def enableComplexKeygenValidation(spark: SparkSession, + tableName: String): Unit = { + setComplexKeygenValidation(spark, tableName, value = true) + } + + def disableComplexKeygenValidation(spark: SparkSession, + tableName: String): Unit = { + setComplexKeygenValidation(spark, tableName, value = false) + } + + private def setComplexKeygenValidation(spark: SparkSession, + tableName: String, + value: Boolean): Unit = { + spark.sql( + s""" + |ALTER TABLE $tableName + |SET TBLPROPERTIES (hoodie.write.complex.keygen.validation.enable = '$value') + |""".stripMargin) + } + private def checkMessageContains(e: Throwable, text: String): Boolean = e.getMessage.trim.contains(text.trim) diff --git a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestCreateTable.scala b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestCreateTable.scala index 937d11af6be65..08a6d600d7161 100644 --- a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestCreateTable.scala +++ b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestCreateTable.scala @@ -25,10 +25,15 @@ import org.apache.hudi.common.util.PartitionPathEncodeUtils.escapePathName import org.apache.hudi.config.HoodieWriteConfig import org.apache.hudi.hadoop.realtime.HoodieParquetRealtimeInputFormat import org.apache.hudi.keygen.SimpleKeyGenerator -import org.apache.spark.sql.SaveMode +import org.apache.hudi.testutils.Assertions +import org.apache.hudi.testutils.HoodieClientTestUtils.createMetaClient + +import org.apache.spark.sql.{SaveMode, SparkSession} import org.apache.spark.sql.catalyst.TableIdentifier import org.apache.spark.sql.catalyst.catalog.{CatalogTableType, HoodieCatalogTable} -import org.apache.spark.sql.hudi.HoodieSparkSqlTestBase.getLastCommitMetadata +import org.apache.spark.sql.hudi.HoodieSqlCommonUtils +import org.apache.spark.sql.hudi.common.HoodieSparkSqlTestBase +import org.apache.spark.sql.hudi.common.HoodieSparkSqlTestBase.{disableComplexKeygenValidation, getLastCommitMetadata} import org.apache.spark.sql.types._ import org.junit.jupiter.api.Assertions.{assertFalse, assertTrue} @@ -983,8 +988,8 @@ class TestCreateTable extends HoodieSparkSqlTestBase { |create table $tableName using hudi |location '$tablePath' |""".stripMargin) - checkAnswer(s"select id, name, value, ts, day, hh from $tableName")( - Seq(1, "a1", 10, 1000, day, 12) + checkAnswer(s"select _hoodie_record_key, id, name, value, ts, day, hh from $tableName")( + Seq("id:1", 1, "a1", 10, 1000, day, 12) ) // Check the missing properties for spark sql val metaClient = HoodieTableMetaClient.builder() @@ -998,12 +1003,13 @@ class TestCreateTable extends HoodieSparkSqlTestBase { val escapedPathPart = escapePathName(day) + val query = s"select _hoodie_record_key, _hoodie_partition_path, id, name, value, ts, day, hh from $tableName order by id" // Test insert into spark.sql(s"insert into $tableName values(2, 'a2', 10, 1000, '$day', 12)") - checkAnswer(s"select _hoodie_record_key, _hoodie_partition_path, id, name, value, ts, day, hh from $tableName order by id")( - Seq("1", s"$escapedPathPart/12", 1, "a1", 10, 1000, day, 12), - Seq("2", s"$escapedPathPart/12", 2, "a2", 10, 1000, day, 12) - ) + checkAnswer(query)( + Seq("id:1", s"$escapedPathPart/12", 1, "a1", 10, 1000, day, 12), + Seq("id:2", s"$escapedPathPart/12", 2, "a2", 10, 1000, day, 12)) + // Test merge into spark.sql( s""" @@ -1012,25 +1018,204 @@ class TestCreateTable extends HoodieSparkSqlTestBase { |on h0.id = s0.id |when matched then update set * |""".stripMargin) - checkAnswer(s"select _hoodie_record_key, _hoodie_partition_path, id, name, value, ts, day, hh from $tableName order by id")( - Seq("1", s"$escapedPathPart/12", 1, "a1", 11, 1001, day, 12), - Seq("2", s"$escapedPathPart/12", 2, "a2", 10, 1000, day, 12) - ) + checkAnswer(query)( + Seq("id:1", s"$escapedPathPart/12", 1, "a1", 11, 1001, day, 12), + Seq("id:2", s"$escapedPathPart/12", 2, "a2", 10, 1000, day, 12)) + // Test update spark.sql(s"update $tableName set value = value + 1 where id = 2") - checkAnswer(s"select _hoodie_record_key, _hoodie_partition_path, id, name, value, ts, day, hh from $tableName order by id")( - Seq("1", s"$escapedPathPart/12", 1, "a1", 11, 1001, day, 12), - Seq("2", s"$escapedPathPart/12", 2, "a2", 11, 1000, day, 12) - ) + checkAnswer(query)( + Seq("id:1", s"$escapedPathPart/12", 1, "a1", 11, 1001, day, 12), + Seq("id:2", s"$escapedPathPart/12", 2, "a2", 11, 1000, day, 12)) + // Test delete spark.sql(s"delete from $tableName where id = 1") - checkAnswer(s"select _hoodie_record_key, _hoodie_partition_path, id, name, value, ts, day, hh from $tableName order by id")( - Seq("2", s"$escapedPathPart/12", 2, "a2", 11, 1000, day, 12) + checkAnswer(query)( + Seq("id:2", s"$escapedPathPart/12", 2, "a2", 11, 1000, day, 12)) + } + } + } + + test("Test Create Table with Complex Key Generator and Key Encoding") { + withTempDir { tmp => + Seq((false, 6), (true, 6), (false, 8), (true, 8), (false, 9), (true, 9)).foreach { params => + val tableName = generateTableName + val tablePath = s"${tmp.getCanonicalPath}/$tableName" + val encodeSingleKeyFieldValue = params._1 + val tableVersion = params._2 + import spark.implicits._ + // The COMPLEX_KEYGEN_NEW_ENCODING config only works for table version 8 and below + val keyPrefix = if (encodeSingleKeyFieldValue && tableVersion < 9) "" else "id:" + val df = Seq((1, "a1", 10, 1000, "2025-07-29", 12)).toDF("id", "name", "value", "ts", "day", "hh") + // Write a table by spark dataframe. + df.write.format("hudi") + .option(HoodieWriteConfig.TBL_NAME.key, tableName) + .option(TABLE_TYPE.key, MOR_TABLE_TYPE_OPT_VAL) + .option(RECORDKEY_FIELD.key, "id") + .option(PRECOMBINE_FIELD.key, "ts") + .option(PARTITIONPATH_FIELD.key, "day,hh") + .option(URL_ENCODE_PARTITIONING.key, "true") + .option(HoodieWriteConfig.INSERT_PARALLELISM_VALUE.key, "1") + .option(HoodieWriteConfig.UPSERT_PARALLELISM_VALUE.key, "1") + .option( + HoodieWriteConfig.COMPLEX_KEYGEN_NEW_ENCODING.key, + encodeSingleKeyFieldValue.toString) + .option(HoodieWriteConfig.ENABLE_COMPLEX_KEYGEN_VALIDATION.key, (tableVersion >= 9).toString) + .mode(SaveMode.Overwrite) + .save(tablePath) + + // Create a table over the existing table. + spark.sql( + s""" + |create table $tableName using hudi + |location '$tablePath' + |""".stripMargin) + checkAnswer(s"select _hoodie_record_key, id, name, value, ts, day, hh from $tableName")( + Seq(keyPrefix + "1", 1, "a1", 10, 1000, "2025-07-29", 12) + ) + spark.sql( + s""" + |ALTER TABLE $tableName + |SET TBLPROPERTIES (hoodie.write.complex.keygen.new.encoding = '$encodeSingleKeyFieldValue', + | hoodie.write.table.version = '$tableVersion') + |""".stripMargin) + // Check the missing properties for spark sql + val metaClient = createMetaClient(spark, tablePath) + val properties = metaClient.getTableConfig.getProps.asScala.toMap + assertResult(true)(properties.contains(HoodieTableConfig.CREATE_SCHEMA.key)) + assertResult("day,hh")(properties(HoodieTableConfig.PARTITION_FIELDS.key)) + assertResult("ts")(properties(HoodieTableConfig.PRECOMBINE_FIELD.key)) + + val query = s"select _hoodie_record_key, _hoodie_partition_path, id, name, value, ts, day, hh from $tableName order by id" + + // Test insert into + writeAndValidateWithComplexKeyGenerator( + spark, tableVersion, tableName, + s"insert into $tableName values(2, 'a2', 10, 1000, '2025-07-29', 12)", query + )( + Seq(keyPrefix + "1", "2025-07-29/12", 1, "a1", 10, 1000, "2025-07-29", 12) + )( + Seq(keyPrefix + "1", "2025-07-29/12", 1, "a1", 10, 1000, "2025-07-29", 12), + Seq(keyPrefix + "2", "2025-07-29/12", 2, "a2", 10, 1000, "2025-07-29", 12) + ) + + // Test merge into + writeAndValidateWithComplexKeyGenerator( + spark, tableVersion, tableName, + s""" + |merge into $tableName h0 + |using (select 1 as id, 'a1' as name, 11 as value, 1001 as ts, '2025-07-29' as day, 12 as hh) s0 + |on h0.id = s0.id + |when matched then update set * + |""".stripMargin, + query + )( + Seq(keyPrefix + "1", "2025-07-29/12", 1, "a1", 10, 1000, "2025-07-29", 12), + Seq(keyPrefix + "2", "2025-07-29/12", 2, "a2", 10, 1000, "2025-07-29", 12) + )( + Seq(keyPrefix + "1", "2025-07-29/12", 1, "a1", 11, 1001, "2025-07-29", 12), + Seq(keyPrefix + "2", "2025-07-29/12", 2, "a2", 10, 1000, "2025-07-29", 12) + ) + + // Test update + writeAndValidateWithComplexKeyGenerator( + spark, tableVersion, tableName, + s"update $tableName set value = value + 1 where id = 2", query + )( + Seq(keyPrefix + "1", "2025-07-29/12", 1, "a1", 11, 1001, "2025-07-29", 12), + Seq(keyPrefix + "2", "2025-07-29/12", 2, "a2", 10, 1000, "2025-07-29", 12) + )( + Seq(keyPrefix + "1", "2025-07-29/12", 1, "a1", 11, 1001, "2025-07-29", 12), + Seq(keyPrefix + "2", "2025-07-29/12", 2, "a2", 11, 1000, "2025-07-29", 12) + ) + + // Test delete + writeAndValidateWithComplexKeyGenerator( + spark, tableVersion, tableName, + s"delete from $tableName where id = 1", query + )( + Seq(keyPrefix + "1", "2025-07-29/12", 1, "a1", 11, 1001, "2025-07-29", 12), + Seq(keyPrefix + "2", "2025-07-29/12", 2, "a2", 11, 1000, "2025-07-29", 12) + )( + Seq(keyPrefix + "2", "2025-07-29/12", 2, "a2", 11, 1000, "2025-07-29", 12) ) } } } + test("Test Create Table with Complex Key Generator with multiple partition fields and record key fields") { + withTempDir { tmp => + val tableName = generateTableName + val tablePath = s"${tmp.getCanonicalPath}/$tableName" + import spark.implicits._ + val df = Seq((1, "a1", 10, 1000, "2025-07-29", 12)).toDF("id", "name", "value", "ts", "day", "hh") + // Write a table by spark dataframe. + df.write.format("hudi") + .option(HoodieWriteConfig.TBL_NAME.key, tableName) + .option(TABLE_TYPE.key, MOR_TABLE_TYPE_OPT_VAL) + .option(RECORDKEY_FIELD.key, "id,name") + .option(PRECOMBINE_FIELD.key, "ts") + .option(PARTITIONPATH_FIELD.key, "day,hh") + .option(URL_ENCODE_PARTITIONING.key, "true") + .option(HoodieWriteConfig.INSERT_PARALLELISM_VALUE.key, "1") + .option(HoodieWriteConfig.UPSERT_PARALLELISM_VALUE.key, "1") + .mode(SaveMode.Overwrite) + .save(tablePath) + + // Create a table over the existing table. + spark.sql( + s""" + |create table $tableName using hudi + |location '$tablePath' + |""".stripMargin) + checkAnswer(s"select _hoodie_record_key, id, name, value, ts, day, hh from $tableName")( + Seq("id:1,name:a1", 1, "a1", 10, 1000, "2025-07-29", 12) + ) + // Check the missing properties for spark sql + val metaClient = createMetaClient(spark, tablePath) + val properties = metaClient.getTableConfig.getProps.asScala.toMap + assertResult(true)(properties.contains(HoodieTableConfig.CREATE_SCHEMA.key)) + assertResult("id,name")(properties(HoodieTableConfig.RECORDKEY_FIELDS.key)) + assertResult("day,hh")(properties(HoodieTableConfig.PARTITION_FIELDS.key)) + assertResult("ts")(properties(HoodieTableConfig.PRECOMBINE_FIELD.key)) + + val query = s"select _hoodie_record_key, _hoodie_partition_path, id, name, value, ts, day, hh from $tableName order by id" + + // Test insert into + spark.sql(s"insert into $tableName values(2, 'a2', 10, 1000, '2025-07-29', 12)") + checkAnswer(query)( + Seq("id:1,name:a1", "2025-07-29/12", 1, "a1", 10, 1000, "2025-07-29", 12), + Seq("id:2,name:a2", "2025-07-29/12", 2, "a2", 10, 1000, "2025-07-29", 12) + ) + + // Test merge into + spark.sql( + s""" + |merge into $tableName h0 + |using (select 1 as id, 'a1' as name, 11 as value, 1001 as ts, '2025-07-29' as day, 12 as hh) s0 + |on h0.id = s0.id + |when matched then update set * + |""".stripMargin) + checkAnswer(query)( + Seq("id:1,name:a1", "2025-07-29/12", 1, "a1", 11, 1001, "2025-07-29", 12), + Seq("id:2,name:a2", "2025-07-29/12", 2, "a2", 10, 1000, "2025-07-29", 12) + ) + + // Test update + spark.sql(s"update $tableName set value = value + 1 where id = 2") + checkAnswer(query)( + Seq("id:1,name:a1", "2025-07-29/12", 1, "a1", 11, 1001, "2025-07-29", 12), + Seq("id:2,name:a2", "2025-07-29/12", 2, "a2", 11, 1000, "2025-07-29", 12) + ) + + // Test delete + spark.sql(s"delete from $tableName where id = 1") + checkAnswer(query)( + Seq("id:2,name:a2", "2025-07-29/12", 2, "a2", 11, 1000, "2025-07-29", 12) + ) + } + } + test("Test Create Table From Existing Hoodie Table For None Partitioned Table") { withTempDir { tmp => // Write a table by spark dataframe. @@ -1487,4 +1672,23 @@ class TestCreateTable extends HoodieSparkSqlTestBase { } } } + + def writeAndValidateWithComplexKeyGenerator(spark: SparkSession, + tableVersion: Int, + tableName: String, + dmlToWrite: String, + query: String)( + expectedRowsBefore: Seq[Any]*)(expectedRowsAfter: Seq[Any]*): Unit = { + if (tableVersion < 9) { + // By default, the complex key generator validation is enabled and should throw exception on DML + Assertions.assertComplexKeyGeneratorValidationThrows(() => spark.sql(dmlToWrite), "ingestion") + // Query should still succeed + checkAnswer(query)(expectedRowsBefore: _*) + // Disabling the complex key generator validation should let write succeed + HoodieSparkSqlTestBase.disableComplexKeygenValidation(spark, tableName) + } + spark.sql(dmlToWrite) + HoodieSparkSqlTestBase.enableComplexKeygenValidation(spark, tableName) + checkAnswer(query)(expectedRowsAfter: _*) + } } From 05acea02ba7caadc93b1fa06830fcede44e21f94 Mon Sep 17 00:00:00 2001 From: Lokesh Jain Date: Mon, 12 Jan 2026 23:36:15 +0530 Subject: [PATCH 02/35] Other fixes --- .../upgrade/EightToNineUpgradeHandler.java | 0 .../upgrade/EightToSevenDowngradeHandler.java | 0 .../upgrade/NineToEightDowngradeHandler.java | 0 .../upgrade/SevenToEightUpgradeHandler.java | 0 .../common/TestSparkReaderContextFactory.java | 0 .../hudi/keygen/TestComplexKeyGenerator.java | 22 ++-- .../hudi/common/engine/RecordContext.java | 0 .../apache/hudi/keygen/TestKeyGenerator.java | 0 .../apache/hudi/sink/bulk/RowDataKeyGen.java | 4 +- .../hudi/sink/bulk/TestRowDataKeyGen.java | 2 +- .../apache/hudi/SparkBaseIndexSupport.scala | 0 .../table/upgrade/TestUpgradeDowngrade.java | 0 .../upgrade-downgrade-fixtures/README.md | 0 .../generate-fixtures.sh | 0 .../hudi-v6-table-complex-keygen.zip | Bin 67342 -> 0 bytes .../hudi-v8-table-complex-keygen.zip | Bin 136082 -> 0 bytes .../hudi-v9-table-complex-keygen.zip | Bin 137899 -> 0 bytes .../generate-fixture-complex-keygen-v9.scala | 107 ------------------ .../generate-fixture-complex-keygen.scala | 106 ----------------- .../scala-templates/generate-fixture-v9.scala | 107 ------------------ .../scala-templates/generate-fixture.scala | 0 .../hudi/functional/TestCOWDataSource.scala | 6 +- .../spark/sql/hudi/TestCreateTable.scala | 76 +------------ 23 files changed, 16 insertions(+), 414 deletions(-) delete mode 100644 hudi-client/hudi-client-common/src/main/java/org/apache/hudi/table/upgrade/EightToNineUpgradeHandler.java delete mode 100644 hudi-client/hudi-client-common/src/main/java/org/apache/hudi/table/upgrade/EightToSevenDowngradeHandler.java delete mode 100644 hudi-client/hudi-client-common/src/main/java/org/apache/hudi/table/upgrade/NineToEightDowngradeHandler.java delete mode 100644 hudi-client/hudi-client-common/src/main/java/org/apache/hudi/table/upgrade/SevenToEightUpgradeHandler.java delete mode 100644 hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/client/common/TestSparkReaderContextFactory.java delete mode 100644 hudi-common/src/main/java/org/apache/hudi/common/engine/RecordContext.java delete mode 100644 hudi-common/src/test/java/org/apache/hudi/keygen/TestKeyGenerator.java delete mode 100644 hudi-spark-datasource/hudi-spark-common/src/main/scala/org/apache/hudi/SparkBaseIndexSupport.scala delete mode 100644 hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/table/upgrade/TestUpgradeDowngrade.java delete mode 100644 hudi-spark-datasource/hudi-spark/src/test/resources/upgrade-downgrade-fixtures/README.md delete mode 100755 hudi-spark-datasource/hudi-spark/src/test/resources/upgrade-downgrade-fixtures/generate-fixtures.sh delete mode 100644 hudi-spark-datasource/hudi-spark/src/test/resources/upgrade-downgrade-fixtures/mor-tables/hudi-v6-table-complex-keygen.zip delete mode 100644 hudi-spark-datasource/hudi-spark/src/test/resources/upgrade-downgrade-fixtures/mor-tables/hudi-v8-table-complex-keygen.zip delete mode 100644 hudi-spark-datasource/hudi-spark/src/test/resources/upgrade-downgrade-fixtures/mor-tables/hudi-v9-table-complex-keygen.zip delete mode 100644 hudi-spark-datasource/hudi-spark/src/test/resources/upgrade-downgrade-fixtures/scala-templates/generate-fixture-complex-keygen-v9.scala delete mode 100644 hudi-spark-datasource/hudi-spark/src/test/resources/upgrade-downgrade-fixtures/scala-templates/generate-fixture-complex-keygen.scala delete mode 100644 hudi-spark-datasource/hudi-spark/src/test/resources/upgrade-downgrade-fixtures/scala-templates/generate-fixture-v9.scala delete mode 100644 hudi-spark-datasource/hudi-spark/src/test/resources/upgrade-downgrade-fixtures/scala-templates/generate-fixture.scala diff --git a/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/table/upgrade/EightToNineUpgradeHandler.java b/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/table/upgrade/EightToNineUpgradeHandler.java deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/table/upgrade/EightToSevenDowngradeHandler.java b/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/table/upgrade/EightToSevenDowngradeHandler.java deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/table/upgrade/NineToEightDowngradeHandler.java b/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/table/upgrade/NineToEightDowngradeHandler.java deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/table/upgrade/SevenToEightUpgradeHandler.java b/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/table/upgrade/SevenToEightUpgradeHandler.java deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/client/common/TestSparkReaderContextFactory.java b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/client/common/TestSparkReaderContextFactory.java deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/keygen/TestComplexKeyGenerator.java b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/keygen/TestComplexKeyGenerator.java index bfc745974fd83..1a04e8835cb8b 100644 --- a/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/keygen/TestComplexKeyGenerator.java +++ b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/keygen/TestComplexKeyGenerator.java @@ -109,10 +109,9 @@ void testHappyFlow() { } @ParameterizedTest - @CsvSource(value = {"false,true,8", "true,false,8", "true,true,8", "false,true,9", "true,false,9", "true,true,9"}) + @CsvSource(value = {"false,true", "true,false", "true,true"}) void testSingleValueKeyGenerator(boolean setNewEncodingConfig, - boolean encodeSingleKeyFieldValueOnly, - String tableVersion) { + boolean encodeSingleKeyFieldValueOnly) { String recordKeyFieldName = "_row_key"; TypedProperties properties = new TypedProperties(); properties.setProperty(KeyGeneratorOptions.RECORDKEY_FIELD_NAME.key(), recordKeyFieldName); @@ -131,15 +130,9 @@ void testSingleValueKeyGenerator(boolean setNewEncodingConfig, String partitionPath = avroRecord.get("timestamp").toString(); HoodieKey hoodieKey = compositeKeyGenerator.getKey(avroRecord); // For table version 9, new encoding config should have no effect - String expectedRecordKey; - if ("9".equals(tableVersion)) { - // Table version 9 ignores the new encoding config and always uses the old format - expectedRecordKey = recordKeyFieldName + ":" + rowKey; - } else { - // Table version 8 may use new encoding config if set - expectedRecordKey = setNewEncodingConfig && encodeSingleKeyFieldValueOnly - ? rowKey : recordKeyFieldName + ":" + rowKey; - } + // Table version 8 may use new encoding config if set + String expectedRecordKey = setNewEncodingConfig && encodeSingleKeyFieldValueOnly + ? rowKey : recordKeyFieldName + ":" + rowKey; assertEquals(expectedRecordKey, hoodieKey.getRecordKey()); assertEquals(partitionPath, hoodieKey.getPartitionPath()); @@ -187,10 +180,9 @@ void testMultipleValueKeyGenerator(boolean setNewEncodingConfig, } @ParameterizedTest - @CsvSource(value = {"false,true,8", "true,false,8", "true,true,8", "false,true,9", "true,false,9", "true,true,9"}) + @CsvSource(value = {"false,true", "true,false", "true,true"}) void testMultipleValueKeyGeneratorNonPartitioned(boolean setNewEncodingConfig, - boolean encodeSingleKeyFieldValueOnly, - String tableVersion) { + boolean encodeSingleKeyFieldValueOnly) { TypedProperties properties = new TypedProperties(); properties.setProperty(KeyGeneratorOptions.RECORDKEY_FIELD_NAME.key(), "_row_key,timestamp"); properties.setProperty(KeyGeneratorOptions.PARTITIONPATH_FIELD_NAME.key(), ""); 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 deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/hudi-common/src/test/java/org/apache/hudi/keygen/TestKeyGenerator.java b/hudi-common/src/test/java/org/apache/hudi/keygen/TestKeyGenerator.java deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/hudi-flink-datasource/hudi-flink/src/main/java/org/apache/hudi/sink/bulk/RowDataKeyGen.java b/hudi-flink-datasource/hudi-flink/src/main/java/org/apache/hudi/sink/bulk/RowDataKeyGen.java index 8fa9219e27d32..184c986688a34 100644 --- a/hudi-flink-datasource/hudi-flink/src/main/java/org/apache/hudi/sink/bulk/RowDataKeyGen.java +++ b/hudi-flink-datasource/hudi-flink/src/main/java/org/apache/hudi/sink/bulk/RowDataKeyGen.java @@ -42,7 +42,7 @@ import static org.apache.hudi.common.util.PartitionPathEncodeUtils.DEFAULT_PARTITION_PATH; import static org.apache.hudi.common.util.PartitionPathEncodeUtils.escapePathName; -import static org.apache.hudi.keygen.KeyGenerator.DEFAULT_COLUMN_VALUE_SEPARATOR; +import static org.apache.hudi.keygen.KeyGenUtils.DEFAULT_COMPOSITE_KEY_FILED_VALUE; /** * Key generator for {@link RowData}. @@ -134,7 +134,7 @@ protected RowDataKeyGen( // single record key with multiple partition fields this.simpleRecordKeyFunc = rowData -> { String oriKey = getRecordKey(recordKeyFieldGetter.getFieldOrNull(rowData), this.recordKeyFields[0], consistentLogicalTimestampEnabled); - return new StringBuilder(this.recordKeyFields[0]).append(DEFAULT_COLUMN_VALUE_SEPARATOR).append(oriKey).toString(); + return new StringBuilder(this.recordKeyFields[0]).append(DEFAULT_COMPOSITE_KEY_FILED_VALUE).append(oriKey).toString(); }; } else { this.simpleRecordKeyFunc = rowData -> getRecordKey(recordKeyFieldGetter.getFieldOrNull(rowData), this.recordKeyFields[0], consistentLogicalTimestampEnabled); diff --git a/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/sink/bulk/TestRowDataKeyGen.java b/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/sink/bulk/TestRowDataKeyGen.java index 5717f4aaf3206..4364f61cc6c34 100644 --- a/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/sink/bulk/TestRowDataKeyGen.java +++ b/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/sink/bulk/TestRowDataKeyGen.java @@ -18,6 +18,7 @@ package org.apache.hudi.sink.bulk; +import org.apache.hudi.config.HoodieWriteConfig; import org.apache.hudi.configuration.FlinkOptions; import org.apache.hudi.exception.HoodieKeyException; import org.apache.hudi.keygen.constant.KeyGeneratorOptions; @@ -79,7 +80,6 @@ void testSingleKeyMultiplePartitionFields(boolean encodeSingleKeyFieldValueOnly) Configuration conf = TestConfigurations.getDefaultConf("path1"); conf.set(FlinkOptions.RECORD_KEY_FIELD, "uuid"); conf.set(FlinkOptions.PARTITION_PATH_FIELD, "partition,ts"); - conf.setString(HoodieWriteConfig.WRITE_TABLE_VERSION.key(), "8"); conf.setString(HoodieWriteConfig.COMPLEX_KEYGEN_NEW_ENCODING.key(), String.valueOf(encodeSingleKeyFieldValueOnly)); RowData rowData1 = insertRow(StringData.fromString("id1"), StringData.fromString("Danny"), 23, TimestampData.fromEpochMillis(1), StringData.fromString("par1")); diff --git a/hudi-spark-datasource/hudi-spark-common/src/main/scala/org/apache/hudi/SparkBaseIndexSupport.scala b/hudi-spark-datasource/hudi-spark-common/src/main/scala/org/apache/hudi/SparkBaseIndexSupport.scala deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/table/upgrade/TestUpgradeDowngrade.java b/hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/table/upgrade/TestUpgradeDowngrade.java deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/hudi-spark-datasource/hudi-spark/src/test/resources/upgrade-downgrade-fixtures/README.md b/hudi-spark-datasource/hudi-spark/src/test/resources/upgrade-downgrade-fixtures/README.md deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/hudi-spark-datasource/hudi-spark/src/test/resources/upgrade-downgrade-fixtures/generate-fixtures.sh b/hudi-spark-datasource/hudi-spark/src/test/resources/upgrade-downgrade-fixtures/generate-fixtures.sh deleted file mode 100755 index e69de29bb2d1d..0000000000000 diff --git a/hudi-spark-datasource/hudi-spark/src/test/resources/upgrade-downgrade-fixtures/mor-tables/hudi-v6-table-complex-keygen.zip b/hudi-spark-datasource/hudi-spark/src/test/resources/upgrade-downgrade-fixtures/mor-tables/hudi-v6-table-complex-keygen.zip deleted file mode 100644 index 0d081b416c95d7d01077d2c37116387f88e399cd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 67342 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 Co{`M} diff --git a/hudi-spark-datasource/hudi-spark/src/test/resources/upgrade-downgrade-fixtures/mor-tables/hudi-v9-table-complex-keygen.zip b/hudi-spark-datasource/hudi-spark/src/test/resources/upgrade-downgrade-fixtures/mor-tables/hudi-v9-table-complex-keygen.zip deleted file mode 100644 index af68e5afc3949a7b352ac5b79faee4e16673a608..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 137899 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�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%0AbcN7rdE z2~m_UQVlov8KC=)^TZs#R`0><;-V8i|S2h-)$4<;i(V_ z>vCn>B6~Qj9P;91Qhr^bEF)5G`GL?~5zUgd_ts8IF%)($OA}iI;ik(Ao&&Gw@fC_5WJ`y`t=Y zCd~g7Wtr&yin1L4iaY#=D64^(aeM4zX#}xc4mPTX-Qp(KUfA)3CVm-2VnbkG-Qztn zwIN-qR8UG79H!`a51%cs(R2fN z$kzu9VtSGM+m!XtEAasNhbjB_1mpqeX&7IoEcTZv`@fW5{?88JU#jfNG6H%Zn;Pn@ zDpnwvTDZigHeZnxD(FdIxG-~k04WL!N(Cglnkeclm?ACZc17he@o-0te+}`^GVA`p zz!->!>SpEgSscZ|DGxOmHJ!~Nd?_8LdKu)CkqVOcNd4f=fG-$*uhWSS(CWRzsY0ndAZ%wjkwu;G*mtPjCNKmroN9|0p>LY(^ImyE#>A%&m> zXo8>w0sGlR2%HN3{$PTX#M?Vn5x%odNWjm@u-|lKC`BTGfYB0Nn!0>x6I28)gHVuF zP$zHGt|aVYK>L-x^=C>X4q-NpB8VSG)iDgjFEJ6KM!XT957h+=K%t@p$b)`H)xipZ zNdP^ZJGW8MlmW%sbbpI-@`J<$Z#0b~Z&*J;V3Oh~Q*|PzRS}`A+x>-(hVk4vhOFn22fZCrTa*25cZvYZY32BXz#ED%@+5=R`T6*9P zq3rY`Nt6;I=EyDLj|f?7X-gOig{ZMm)Fbkx%eLbVmp>p9j1&lfA_?-WtYUtq!%tOR z7toOt8>j^TEgBF9|1BC2l9LgFwK+hd-;~8T95_pA0f|GJ?%0B2l7p%dAxa*RU;Sy3 zszQxiOYkj0g(o#1i!;Ecgi}G>w)HH)3g?50xRtd{ZY5q8) z`bJI+tW*~?@Dd*W*Z_R6(>JdJ^`*^qmuedwy}MaLz?0u#zz*+1vb?O<}pTwM2Dnw8Dpdv z%tEEWBkTlOh2d#Q7obLMM_~-`qX$mWXbupdXZr$|e}e|=Enh){X&ah!Ut}TF5fYHE zph3wb>a=*fH#R}_SJ0qd!+*}sepKZ?lYtV?wvK^34-w5H;pTbo-CVeEUK==_CIn8@ z3Q11xi(6tvpX|vP5V{GG)|e1{`~Bl9Xt1;Q?Y1gl*VyrwGAOWN7CpQ*dEstwJW`vk zAQ$mZXCg&rbZ?JuV^K}QJK24zi|sD^A&+cBh4ka_2hNBF*W&36;iLd;WTj|s^jY|% zz^U(P3F5Opa732Vc+!@_n=k~98WIh)PAX+y<-$lOnDgz?F8`s+5`F2i8|}oTXq?*p zlo;r5>TqhkBkU$B9mk&GBE@G>au}QW_a8~ zAp4rrl7ez}nh9anK(Wur}|Gqann_ z|Du)}NmS0AN=_%Mk;y2bpkLIoQCG@^5JlIjVGUJSY)@3Z@S=p@Wx%daNH8RdX7QwY zF5m1#?Cz|4*mU)F*UC|}33hZUXMx(K$#r0L*o>x@1`TeBxZ^t!4KWlj5#ui!!2lvq zGzicJX_+F*Qpa+QXvtsWLF~l34T^4dx#rYqqJ4@iXm06|0iTbCU~gc3CW7qo;TPtA8SaZ5fLK(ak*|HKxjhJ_RNW)co! zdABqxVQ(HTQXwc0DJV}bLYxO75m4AkEF!HM*dMLqo!(#e8%QrY$>tWMcT%?_cm0J& z!3c#5J1O>%E`o?KEX=)kNwsSvPY6n272GeAd>@N&j3S^c?U`?0* z19a!sx0DqJv>sxJNBdGG9lr0dbi@eO6yHJOZyLZp$0rK#4Iw$PF8V@}TuR4La zcR}HT`f&|3LWCj_O{}w;W+#U$QQPhf-XJQc!TF*(D_xwOFMlb}oN%C>*i|@&XAE12 zX2Mi`RhxPkU;p{T8nW)%uaZfkVkwlckt+o6SeaVz*E3ik%pxOUmNn%=_T^X1p!`dg zl@7!V{Ff|Crj^kb3#Xa1K}+wdKbG0@iEn+|qX8R_g_CwhJ+T5xXqf-=Th2x^G?>jV zZ98SqbuOL?u25kVlhZY-6<9KcXa^5&0kf0XOQMdW%h^Mi!9$8sz37Au83D;lVi7RH zLWo4*Bo1LDDzf+0_!2O)(pgdlU4Lyu{J4&n@9dr3GRub#5`d-TwNSWrBhJ`FjiMx& zcy5w-q>xxBn_tC}dR=VEa2Cv_nQGvwzOKVN35?eB{9@q#Fq0q{I2oY&amCSqQMHGNht+7G&o<)|HP*^uJmL?$GaQ(o3rdsRl|+u9_r_vA{2e z^qgOzhFzeqmI2MFRAMkZCNL~aN+fp;b0!5OC;?{T+Q%A4hDm`0sqL35t1{=JqSX~- zO(1=VDrP9T|K-X~wKrw`%a#3JZGHAHSC$^5DdWqPg>vY{lu6iDX|bgUy^kyK!(h5k z%xMm5j+u|X!h}sJh82rjAus`vkt6eyNGQ(9rw1U3N-CX9CMiP z6cra))EMKGS905_yZ>h8AMvj8Wt{k(u(w}G*6sBe#^&zoqYfXT(N#>!%g$AOxFICw zH^Q?xa8)EvU~=hkY42yFVRUe)f5sdWLBk>ciWTm@F`1=rtd^^Xt(jui4Cqu8g*_f# z{bnUd?JGUZ-WgqSUNu^jSA~yRmD@#0v_=@C3jfrfG{Z(}e|3=5lvnJ&kZ;(zna_bu zA7i`$VMo9ut4&h1-A^DWZXJc=vb;9R6ydAZzJdOjZL|Gi3w>0}<#A$siUHq>b?An6 z=|0nGzBRu}W_t3_ONbHetXOC#k4yWSvHm<(OyA!$GjkmQ7Ge3c(J4x{`KQUydi~EN z*8vM_thucKh%G~GkJIUTakxmfV}h?^C_g(;ebiJMhrqwJ>KMrfu0+;Mrx2|2Gng_eofwtU?F zjCd!dRlbsKalm;nQ`eR_G@itY#>To$Z=}O+B~yLgHEgFWMp=J2X2dep*ny*yhjFq6 zz!}C|2av$n$wvDeAirL(TpbZ?9$y%jz8BmcZ?aCeBeFFnj&*JV@Now9Uk%TqM}Pd$ zJe<(Q>L&w3X%w$1*-$OVucxMr zcC#tX8E^2z()I-AesO*4@f7K>c0~UurV?&_sww?$%G%^+lOXIf?fK>O z^~_8g9Riz{+8u|HmTy@9vtaFJcCdqDLyZbgqbId}I^o#(@sV?WTYL*ec_J?EvQ|CW z{mE^6En2+z(HwH)IrC0b+E(|jD2=xAP^~-n+UE^yq9@F=7w6dL*W}FQ&b410-A|J!yayt*b) zXxfp?vS&^G4~yWBkVP@CDzYO_wF)|dz*zbP%Jqxp!ZP<9xy6UzJc!LP*J{R2OGEc< zK>1t$W=r54{-JLK1`O%D3iWRv_+@#q!3fbP5P&34F7Dm2>&-_fjn;>qVsw#gkh8Bu z95-@GY2h*pXVZ`Ix{x!54JhpPQ2nw~_D9!s9@@4iaHE;^lex1Ida`JcGUdQ)rtHTxw;>`wx_f#EZQ%I*dV4teHLr={p;MV8m<*X}h?E_{h zhx5lbkhMHLjk=1mpPXDuex5JVwFhU3faTaXs>ygx#XW7eaWU)VcKcfL1~}uRx9=S0 zA0+jxgQjO~Wb;?tT@kCEzo>q^^)xvK9Fi!drI*MptatIBnRyDSfRBG3o*sJ4GdFJw zHqXC^Rf(sb;+Na8xk8w4-o0Fo?YqEz&gJ(Wp0h|Yo~u@Iy&0Cp-qcK%X2<+3-OF>8*hTTa zZNk}$(Z_AmJ29DYnECML*9QoD=0&UqpEYH@llHDkl4HgzhYT}IV9j=feG9AI*ab?@ z@)5&DL`rIw8$gDZ=gFa9>zeNb!sRto9nF5mYhHbyje+AbydIl{&i7_JM0uk4ir{gp z!t$o8K9kFLFjS&MQ>w02mNKIk#C;^;ceVB zfdrH7gi_cnEbdM&0;#se6DP8MpY$18G=IE(_O5_)1ZD2*VXQ^Z`%8LP_#M>Y^kaNx zHaIhsL*X;8_I44E zWIwl4pSwKD&xryyE>Sf=_f=~gpJ?9CIH1s7aAiAA(K|fh73c15UsUDa2FME~H!sBt zTe4GPAGGp9?{Mfna&uc|9aLWikRWuMR}T4AbKBG>b98b2*l*saGaCl(U&OQV+n8{? zuCy~!>>W!UEvyc{3tK1UwW1w9^Bs^ifjX=$$lGiUysHoeMQ;`r2R?xU>qYNz!h zS-{SAMsl%+ZL7)%b7fgto;NikDcR1?Jg@EWSHcuGd)$?pv3`z6_htIQ)*@VtwO+MX z=G}z#&KA7u2g&D>3#MNYWkJ22*#4RJG|crn!t>qOatnIgQ}@#VM?uHV<;LA+5^ziU zDBs$5Cu6gW;*oY=xV?Ex64(76BwKDjiogFbVmny~S(^9YdnbR88lA2PZp*oeE zirrrhrq9@|Z!6T1^`<^@>^bP%iMnT4dM)V+u+j#MZ1QBmvuqt=%g5Ndh1M=HHUHdv zxvJ$Ajki`~O8o zM=<+LcVOD4L3ky0W|dkTr#2})Cl|BBkIb3lyx9BKGpsWdBmwdI2d4Xb2&Qo7qF`T| zA>h9o)BV3WgD=fcN9GKW*M=rVEN#k7zA|o6xF1?FJV{re3$Q#~$j;1QIn1UiuL)M5 zmNuEl%(+(PD5p=cAu@?@q1)N5P%vq)+44G^TxiEAh>p>Zn+S>m!D4Wo5ytofnSR`Va0pY_ z-w}zAaNh`5zz9y!n+O+PxJ`jm>RN$F6Uzjc)6%Ux}yGfmjfj~u|_%)uS()8q9@pD zF#K;$C%Tj24T}O)i5UKT_m|T_S7D>IlD*Taa>i$q@dsMwvnm9{wVs5S5x!EBj&qn& z+lkhMaS}RES;>atWc!2t^lL%VhD?WqDoemaofZyGPQy{ehv%6>B4Kp6<%|f2k$d9J@ggB;@Rt^#N8SqK);JfQCem+6k`>HexXgj|b}N!xC!WGZ6?5d}f4dCGTSs zb8d-`nk17C_{s1smx&4V?nUX}pbqpuKpi`#J~3hkB4fa(GAMPO6-WT?2`wnDXg+6v z(`qPzJObI+am zzL`5iRae2Erus)i)zj-;YvwoID}@geMmQ8J+@O+Co(0a(rqqK0F*I=S50@A#XO6tA zIT!vzGH+MLN8o~q)R2DlGaeFOaaekdGB{=onP3YmpaG~s4N+`;pf8n(1eV0lU><$G zl0Xo+pb`4eNVtLgW_)m9%Qlt@jOdsWVCbI9$T8GJ9|6O@kAR_Lq#aob;h%t^<43?S z2UXYqYVrCbV2Ju73J@^N{g678XpfJ8p}a64V3@;dPemNvg4(uG5uvlbTkz{V7e*jQ z>!W(q>reG))I1FR=d-`6N2`%TtTf)+^{A1S;VnM3a1P*&VC@j_iP@)%m;4ih(8e*{4^hcQ z8V{TW<&eOh0<1*r-NuWRy6YA9P(n;-=S6zkv!kpNit z_z$dub;g{jCD?tpcg7qOqgpamVZM89(=_ft2PgQrZI4=FKKp(Pkt5~N#P9|Fqk9w- z&^_9}SLNZJKsNd0X7YJ1uhdZkgsBq>0=T%~jeOQk*ILLObm&V&GUhT$R#GO^fS`dG zS4Lj)fKZ1wC>=~99|6*)WSfnh4u5vius)gcPJnN;I;L}>G-(KleMH?Kts@-7&hG!o z93@cS)~=Mq-}g3L+S%XrUfPFOsD$Y?vWIc{Jyn{Xua6c^B?tOTRimG9x=bo%MI)0d za32Dp_D8&sdK0cxRAbnqWY7#n(CKfkvuaoTC)YjTX-53~LeXrD41pHmzmf;mVBIja zhX#W_JX0@MfiA3uws~deF*1d{$}c;;amD-BZMz?VwD3_a zz5wKic&3or$8Ebx+0EYZ!igP&YVthcsj`e2=2s@fZp)Y+g4jQ|?IAe_bb8+zlXiNQ z`4=MyWO593C41%NO=!TYnP0Vaz#2MKEGi=%F((wdcz19h5oD;}NiK#esrv1#=~%8a zBfScLpwYAnapT@&+(y#k&qi?gIu#QepMnO{2mW&U+v{KeUiU@oqkEKP4$wV{O!t6> zLIH29qRS)!2ZlpOl>b_9&M*k*DEZSp+HhD<;{ZC(9jB6m=GzPZPG)W}mb*Ihr+d^; zRTm@x|I9u4q(7SIR+&d)OC(f+k=aAXhPlvp=cHYY0RPtT-;3KN=l^en{O zu_JrMf)MOSViWxukmYa-RWkC+Iq>|=HECpRgcrpr-)?MFxH_Sl?}dm_bUkAow;pj- zYn&u6)ZK{?v9={r@AE1LI1g)W#(bh-H!yq8izv0en-H-6)@;)iBGVEXMvlwL6lSQj zd^mE5#s0~zh%AA93ah~)?eoJ~)U^A{l(~=eR5T=LH?E#+FWy(-nrG+{@(B`s2L&(B zxUnWrDhZM11CcQCK3i=_ru7g8se08Xo4D+;ZKP74wOB%$l{xW^q=t|&#&gCiQtxAN z<1TR%H79}U_lg9ihWYe@rBv#gl~7Fga+{{^r3XrV*QYy2T`v_+-&)0i4H?iX0)ewZ z6@#6PmZ0s+$m*f`vmqO-p!{OO?jRLsn{~B(x2-d?aaS2{eh7l*IYrSnXz0t}3Aae? zwwP?jJSIm!naywh1h?&JBykY>{TQ*S?mP>_Md{07{%}r&bP4Jh+>e2`W8b__s?E+7 z)tc@bpED=y32`X7rqZ@c2VD_F#ur!*3>7|UsS&YC!jCC8=Dqt%)4DH3EF*;9S6KTA zrap~ggDME<4d<_xZRL}oD>^^n3JdT!^g_u!^9xYwDXk{wH(Rav^~6FKIa@n+s0}~N zvQgL}`a~D;!?q}oh>R>nDQqT|#N0S9Ay#u7fW-2bW1WUh&oQ*vJ z?X)dB!`KV<%IK+}MX>K)s*){?UR~|+VE?e=o?PR~f)*dUIMB{zxWBvJ>d?Cd|I&BBgYmlk@ZyWUQZ9=C|b% z+QG_Qz3pV?W8r96=elg{_AdoI-#Bj-q}okeAE$wzrn8fHe(+mY%x>6ebcs30}Q{n8F$q!S!d&%bnVd~7|;AR(V#yU#Z*vf*@w=~ zvbT#E^%H3B(mGfykG$8zRT;y<5)gH;E=V!Hpt4ABYLp1C|8>&I<*iogpz6vLubE6pvw4&=`E&$hwC9Hh0lkSP z^0w&T2^Ol@v`#kjm@z?+W@TaE^xzgVDO;G2nS1fYsDv}o)1lHcdCD>7+5Ogsz&W~v zDA^;u36HZ{E{hSvs81+5jDp9l0sj0|!gqqs<0_07_0uS8kaFvH>FQ`kImZj9wwr($ zs-Agi_H=77Y*2|BpY;G;YmzeViyHLh5&^v$(OrT+JqnLM$@4$;U2+`Z3;#5HahvrEhv%sqmEP2YxfpM-F2nL5dY@OP`a_ zQI%rURm`n>-96kCv+jz7T5Fg}%GVRrkP#a*jz;iQ^a9J1z8?ir!THBX-{0P zAhNIA-li$ZPbJ%>wxCH#h7n4Q755_vr~a7Gd1s1z+}5z&2Yx?@+x^M$DL{LsCi2lst+aAi;LcgO{$J= zC6z6&xH#dU7t4*|n!De-6wN$qahw`GPTf)_TahRDg%L21>^Rl%lux$5ygh51?e5>Ts|p$+~?xXasI20qFI zV$bw(s#WHPC4MC9Hw)>9>)L*;28zGxz3!uLP@7GW_R{vCi*e}Zj6zzIRFh_RaKeo2 zTk2aQQ$iwB+6y9O+8U^8e-_@?Eplwd8e^52VfmuCN27TQ}M6Hx{ z)WqHd`^MToZ63X2!isR4)KKLnIlEd=iw~);FbkZ*=4^bL21~K&IN5XLGbCdF8a`K1 zhFc_-lE~Cc=x|)i+1iaM%)Ms4gxPQg93#{AU1)o+@*oKw$KSdShB^V;9634TiP_X_ zf_T>m`NmUmaPlk5@JP6nqeNw8(P6w<2~U99$v71lYm`;OJ0~{FD?K18^=O6y|o#f5KF% zWVVBaY{=$b#v!kd57qT0*}>{-OYIf!)mdAtgWt!+tf3Ky^!G|$Vxsb+jVMI9_7UCJ z%R5d7YFP;N^1lTTzGshDpRd<!+>raAx=Iv{;$kM^m~x+k#=f;(CKyGZ@rvUH;YmL9>5T@p7p>|ijrY*AqK@)f=jvC)01bMmJfIjpgVbzmo3OD-Hg>`N z1FV1f;lZ6LY3Zs)$o3uNKZ1iFx6hlRXPEzJ*!+Kc`~2_0!GFDd{<~rFUrjQiF}8i{ z|I#GekxcL1a}*Yglk6O}k8zj>)tU#24qW@E{yIwEmAIk)*MiND*kDUF`lAi}r%!(D z|BqvX|L&3h8XKhhlbd8_`$xkj$`5FWf&g281vpfKS+AxkV!oVgK;s?5Upq)aU`Is? z-vu;yKRfr!*;IZiD?8Hy>8bVf$O<61tBW(IerI760#lWLbzfiX<^UCEIB$D4du)9^ zYmLSiU%N}ehcZ$S@NzfWC`Wua{?4OqkeryHm=y92)0n+)YNo5pq=uXeyfsh{UJAiS zw>OG_Q*=lly|Skw;N{Fm(*b7e1YEl=I(8v%1KJKxqtKi=OaCjLyU>lkQ z@WQ}w1pztPySLhbx_nl+DG^y)aC20>9~wzVg@`~!8Aa4rKS|D~BKf7!ep$lL1KD3({YMK79#Ho+~~F z#KK1sdF^+ACV>VrP-LCy)tqT@mvCVN*P*`fCZ1K?3s|1#)OQg4wGvbekAv!nf`ysi zwv<*}JzqRU@p%Lo(o#y{eC8aDCIDC@S)vcD%vc#J zae?P7(nBBtT*_<-H*W!2+)rL25MYs9iy3{s%Fm2HL_eu*KxMv2S>f?@@&Q(mxHe)Y zQwaA6BX`Ip|EwVSYGg+6#ba4pwG3KJeZJzz$nDVf2iGO2B>jx|5KkcKE5``7BlXd- z>A3|wWQ^lRbU3r;b*PX3!;q6<<2#Y@kSIJ*3~9#Yhg+HZ7Unns}e?(fXhl|=RRE_L)rLpP0d$UZ)DGLnA!z-P1feC@R%IXL?$TJox#yY=vzvqAwByrexJe!Cfj<4_1&H2<97{oLSOPoiYM@X z!MZH~Sy$_q0K1?;TX>odgA@?PO?vOsti+euL&@El`~#8Te;_gjaOvDMqr*b!m(u57 z_7l)!ap1EW;0w5PmM@%6&f$fg3iX?J&K|D0?43ObGQbMMWG@stFgWw64HzX+lPAY8 z>9Mip`~5@u9~GOxe=eO96coj`83KW$qhdi#e>CZ2pRCaRRk8Vl0sa(0pcdb^7at6> z%2()GvNM@JOG9KSu}wsrwU3dyUzBzUf$#79|is9x$OE_a#jJ zbLcFKff4p%^x=@#Bci58{(}YF(uDjn^g`r$s6Rb2x(J1Y6nrVeG)4ZNK$>Q@_fpv@ zToN^ks?hI>He{S(CZ1w?Mj+BR5ZSNho}U3WfbKlNLdOZ zm!@VA0QgI_XX<;(^chH!G}qa`zyXQX_3GoySs+swM=R?y-PS_;@naxnW#`ayQx=x+wcUA9>uoM5;BeDm}mu}fS8cigX1FzB{Jy+ zTt>3GkZ#~A#~3*AFasIC&H+^bVVHgf@szW9k1=bvddO+_M*2LkoU9EyIV#D_BxAzQ zlaYTs_%}oHBOq-~w%u54eka5j(kD{i@e+tok8PD50{7d2k;PQ31S}vWt2Y~&a6Q2lf?+=#nCOP``8x4=krRE_u}49jg^+ZWmkaeIE1eh7!!-C(UKj0dE_QR z9vuD>-v9$hvSn?pL4X0It^moJFFSE&r#M;8XKJHubS4f~SVU-xqrfpcGz79>3{H6D zbdFz;BBiIwp$(NZ>@dWFra&5y_pS?*%WbPU25e0g!fQ;#d`%Z3FabBtfdcG*Zk!V( z59oCH%Wki>#4=5OL<0&o=|2iKVbXmRtKaD9f&LV14tx}BZsS9QgNPi)xpO%nEBw}I z#!tX}nE25vivX2x7FD;MCXL&RAPwy+b5O5zHRl$96fIORjNxgCGyr?uf!?zS2l{~U zpsFYzMP$Hu41Sm7#%4nicm z4PcN1xxF)+*6M0ghN{+o?H~DOKm!c&m*+F_hd~yZF4OIlpgq~czmhC2J1j0Vv4#Vy zr!k-ziEs~rKA4HUMesd0J3v2%lR0>LlJI(Z8VduNuN7V%P4gTEI@?xRbR_M7x-#0n zo)edL+l^n@UH=3+zM0ajM`yv!oq1~1pL+`8heSW(Bbn|cRt$HhH}uW(_s=)+oeUBI zX2~3o(HogziXtX$AYu%T3Thlw$rG(sirzpOW@_x4rAz>)N%{%?b406*WS%%P;8C6> z^}%c`ZOoWPKAFdT({Rs`;}Ik*nM0l%q zBOYBe8|Ko4-gG~Tz9yFM88H56-6r$KRzyYEFPmEIC}&M$yRJ_LOn^*P!8?|GZ3}Y2} z_qTnDY0Aip8ls(WY}_(81qKI~up252N^t{zuUA!;hu5!fwORFm>vEjc-!1qYV*J#;B0vOl%Dq8E-2mgQ zt@^^NPE}3I%1Q1#S%l_^+YQ5VnB$BCQx)^YsiMts(y!G!>>AM6JZx(?HM4 zE_cfd&o)+iE3T`0cW5uCv##yJrNHIX^|;vSFc+c;qB?2pDVpjYo0g_@;UXyNUaohr zI=wZ^Se@m!lFUMrdEVPL>GFyHTjW4fo2!(j_{~r%{LLLt6Vs56P zd;<#zOUaaC=H87CcU8$8c^D%TPno-@M^jn6m&6cWq)hcK@d^$k3iOr+SW~H^0nMS`lHp1gZj#s?h>-!s4 zIxGJKTE-p{_j>poH$LS%Y%?~6E&Z!w_IbhT1llx$oz9MjbbkI4Z(cJ*T)jIbVUxn~!l=^K!+PFcVbS;|50CE3$V;lTD|n zt;6F+R??>HYYM^*`-b?EZT5LDWrjI>_OZj%Dk>CSG>+QvallDdb-Jl;Y63k|t5TrT z{hu*>&~L$UUECJch)Cm9o9=Xs@-x=Df7|b?Acvw2rd3{afMJKS@gmdfEc1 zp6-T0vhG7Jt^akCG_l-i-%Hz~f_e>m?wpPlx$V5;(|v0ct%@V~)A{9)YEVIcsTL!g zjdl0DCI3a-kYl^X?QL{g`>C^uKNGwOwHKAv44i$#?s4g~2IRo&;GFi^UQ2vk>XDA# zZEGQ+sZ(j`FitU2Wc3+-n5C?O`Q5Epv+;3GtW*91*?hiTEj=5(Qvc~0`ZuyRV^yB= zF3N1*6ZFi;O8GkBmR{$cx4CnGE0FabF8y(r3g*3by23HSVlz`Ibd$DPOWSBlWq0{- z8!oAf$}rRmxi^~7{Oq4V2RU_h(q9Y!}2vA9(O6QBn(dx@N3YKdWp{@}%@&6jPE zYt2k}KyAEEY1BDhY72iVh#Kui(p<$itfa_EwiE^&%wWZFn@T2K znzOC@tYs)IT;^wA>uDMi&#&`59RYz-g(bG^twy`OI)=Lu@cwk9gl&^fw0t!Vy%L@z zSIg<_n&lP8%x%2I%xgZg1N>66BlILjUG1r++j7lTIu+Kpn@t|k#^d^Ru9a8+$D`zvvLrs=O)~*3ZoHNn~rLu^=WO# zLIOYS9v%FoA(JIda?2S**~VosUh6JIU%?=&}H2 zWW`n&%>9&iYq)wm9h)E6k$n%(dX1vrng(0S)$~ns-dQO$(1W3AuP$&=NJOmljT`BH zw~3~rZnrqiz3Dpa57eyMo*Azf;f~p)$sMG9d$FDKux=|XjcdkZt$KT;6MAu#f>}8g?Y`Qz z5&yXD?kZzPWf)l8z)raB`u*o=AS}Oj`N0XJwJJQ6Ks`@7g59?$Ax+xd+lL zvc9-`q(vKuqaStwsjeqSi0@*gdd@BGnxj0(|#>R{f`EXrlYq&ih}JH6H=DADaSKYyeQ=2Y`~+fAbk>{yZ`DM*-#kN53QE zU&%hEf27)?!0_jDeN*y8a+8Ln2d0Fhqn3kE#zs;M!}Rt-*z}cXGWp7gn+cxnwxv~k zvet-dDOS|n!@n))qIMo%O!iV_HW(FxM$f}uuVC7_7XQxUW`Yofsl zaKZ!abpoH1vImQ3&YbLWkAu+>{O>?s5o#gS(=m!L#%+XoG|CC*&jp zA_gu%BLD`=*?@ZN!omkaxsduX(4CkKq)+)HfhI#IVXXHDK}>>p20>I<97_aw9QdHC z1z%7Qe`044_FySIHvp1Gq=!-sJ0i>LHKfnd!Tr0b9;v({}4DiO&3LsU-#Ku}J zN{r>U>FWUw39xNXpmyro;JW^UgMT02KkWN1@4|sPrQO@w?tH80{xkM6`fZxIoHB!B0#$J_wRN z$luclC0Bs`UwZ!Ck*NR8k-q$AM{2v|gL(tLzWk>nsnY#%B*%X`lKB6&BL!J$ZUPc* z&3G#R;Yb|KmOzB-L-H_|N}n!NfB=d%dt`9!`fb)P!JOcK>kNBh#z21oFhv`{^;6vEd3S~Me$;~YaTc>EI0^X*{3nz1% zMCP^z3|08hZcBdBZ{XOYsTC-di#^8_0^ZpPW#R)tfR!XLs4HPfjezgHpFK|y5GjWZ zRY)Q=q^qtnLVv}Z#H3>GB^D8bUqt>Q$x_WKV9u#vMbhLIrvt~6M3k8Ym5Y)52(*C& zfYo^reE%!ZX7NLK;;%qkkWfwmP-5wBqlFHj`mVh%QkcIxQhoGet0gV5P%M=meOAy& z$AE%_y!WOT3{JLzh#b7wqLC4MMCz4UTZCrNaGv-wjlfE#>t<4ykMukR(6Ypp#t$>EZ`(!KKWFHVz z+Hn|t{+Sbq5<>0@E_&p9E?5I^Mgp?7PZ76^LI7du{kNpK7WIYYjB@0lcb-#IhxuxQ zO3T8NXV>0250);{zv65Z_8HC~vPc=_qA>q-qyWiFH z>sSJxuCBG4zuPdzfPe&If83sO{dIeqnF&~GX{e!X&oRkTX0Y0~Y_~^>plp3YSs){T=&jJj!w9pYCoqlS}Rc`h_NNYB)riH>x z!;%O8X~+1PxG@+E*((I+7li2ACxM)p8)NkM@jLPs*%RG3N-W zm1Q(BQJB#GgxOO6gxN~SZ5Ar<%AFi9^Cass24xMC&}46Ck&^|xN)+D}lt0nX!K#?( ztbJvQvxN8Ue~@n4Rfkmri9=p61Kaz`UGp0-)Y4s;oO9ZcKO(~Rh(g)BAONwuVK?g4?_th|~Xvlon zw0VV^s^zUGM#U+)67|Z?I!~hC#IWz@6RI=EDS|`;2$#LSG?iWoC3Wk`9xzB#uBVIJnh$aO)R-T@D)`m#)VWPZ!ae`xH4ak^Rsq@RcE(@L(A8eF)bVa$`r>zw+?v zAyN<3BoTZkcxo?K%kH!%S^zfsL7PRnhQq<&YoquS0m z0k1k*qN`H5mvxikjbG^zU%$l9BnOAKf@9emwoC_19XC-TQR>@gcweW}{7Iw~7O_rzo%E7Qv^0l_#`lO&^R`boSE;0&xaUgBRAq|xn z0=?#p*)T-3v)IWFdJM)3r|6d=BfsBM+E@9PPrA%X&$WeD5z3)n1~m)9^&Gp(bn{11 zPhCfZi;*i|HwMJ*eV6>~f!^nYIjAd`DZrN577vU9`gJ*^wkjFjQ$u-<FR)>P7GiT@Z)NPeTfGiTx=kH-ZDqz*KHp%~ zrp%qPa_XJO;Eq#ELdH=A#hA<})`m3tfSn{dWwI;mYnbZ9^S~H}q#jTlaDBsCv^gAo zN?gV2;s9S2ROg*bTZT`vss&~6c2!lcKC9IY@m4FcI}J7FQrQ<1yD8XhSj9P|=j*1O z7@K|+a_RbH&8t5?8_Wo+6=mz$@Ffz|e&{TiXWsqsW$>Er`Jx23K2^oIIme^ACy4$g zD}!Y7qA2!GiS}#zG@xl>G3;I@epr*fepp|46hBq=biY{(Hk3{{s8Ys#W!K^zcMMdB zU|+C|B4jk=)i&?7Ao>y9NS<#F$F-Y54=^Cdu}{>q11lcd-Z&(hu$In|O~G6GNi0>; z#*7&&9eX&D&g$Yg!WHsdgw~%{JMmV&I3*cT&rmv2iRA3Wt?3u;I=kEV+2Vj4N)^Du z`}+*TOrayKoHAjPTkj3{_I*4Cs6MrQdzEeV_U~K#iE8=%rKJNk z&w3s``4d=Ca+u@nWbVg~Igi-$D8A=AN}l=l`SoKItbhvr$M;3|?I1;&JTY{yM$Q4M zV}{}8cx}U#+Zo^0XfHQvh$EvGk|WE$*Gtx)Z3n~-bPEhXGg{XLcZ z>l6ri1)JZ{?Tu0e&nYqUzOGOfxZWJZ&1b|y+H*UgvPkos&#G8DT`lyqCWlfBqR5|1 z?@P?vV{+PoUw35GGuPW4x~4QIRM(N!)@TX-l8rhq@BHd~e!RBj@6=-i92;CV`Yk;> zp35RV^5@=k?eQzFKKEIh_2%0SVY&1T)%=iJJ2t&2QD+?LGP*4pQ+0KD%e3><&5bw> zWKldU^Ui~jnI1&QHLX4GyF;sJh4Ild-r1z{By!h@hV^^+rDNR|x3_6?bzB;meDXx6 zcmrPZdjLPsWM;$8xw(>TjvBMoTD5E6)#pbCLxKM4_MJU$7x$H1y8H^U}%&@HJ7IBp^JymvT8RpE~n+b$v&k_MUi5wSA>1wot1_O z(ub@?P?0ya<*{A+W`EjcpVyR2o4Ln&<25$$YPTf)M8lKobWx7If%~>(?G|^j2UoqL zYS!mfkqy|&*c9<3La(zn?no?~CU)1bp9PD?&~cgqJ>5I!&j^_I)Gc~aYABL1?@vqC zZ95w6v4mgFbFckDjch`OXGKTBbnI_j=nLkk#@2Q;>O%KX+-!ZG7n&n0`iB)^W?>oh z)CXE`X-rbe+U3(It>=E0&i0e4F^e~r>)E~Rpyv&HSANaT6J}`S)B}6*Vk&vkcav-Qbs^VqmbVaM1LIN)(xNF8)|c&-r-wk+!IeM3d8KNJva zL$>(tpp)_rH;zHXlI!#~#~uinThpv*(?1PkLQVZ)oVPgbrK%V&{bYlaL!U|W$fG#m zOUgo_tie9#az+*~i4_=J6Cqq;mkrJ?0=(-0A|-jmBha~nat-}?}LrnBbU z*^uz5zGb~an@}z0O*OHlZ>VtUcDL;~oO{es&5C8zB1E&^6x`E#Z!Lhs_0n(fCPLf# z8B`3msIAF&)*;MD-Xa!|G)jq|Bu+{U&B`i4pI!S|Ciw_b(BWn#I2%kj@qsH0+tjrT&Wt#upSN*?y(f=Aa zWBfC=!2$?eDIhMQzyy#{P?SMSVtGM(lKqm>h%5r;MIxsVAQ|+5B1()%lthvGjBJbC zT(Gd*?bl!@xvpi7rv*w*kx`aqtSDu2J|>F5H!rvDyzaJg?oFHMeAbx%a=&6d)-$j= z9&6f@;YSVgF=z~ZkuT1peV_nUU4g;erRCEY8r$fbo0|&_h+BnfBUB>z7!Jn}sE7_} zCo%MX#I3?KIgu{;z!E?aE5eVcXgnKrLTSDKN2VaN;dRI$%Ox1$+{E% zL&-#yqlngO$N_OH^1e1mK-{Vj{@01UCaGuc$MGkc3jy%DFEK7MflQ(PdY70SFR{hW zu~=fvbN41$0XF8MXyVV6d-OzOR)t>FKn&!7N{YN&qJP5G(mP21O^YZIa{`?kpSn}TYDs;`{(vE zMF|#M18WNp5Vsnf?*r{;G1kk4Ri&fSuP~C$+$A?o zZ1@=qU)I+iMGLUP;rl&@I=RJhxu`;#;!OA{Q96NtEDE=iNfM&~uLtv%4;#)E838$& zz4+twvjNYd@UPQPfhhCk0P3%zJ-|6SpaPI^^ryPSL)o7Bjncqh#c?R2FEoZ0YqKnl z6vRB!6@WSuLyx+7dn=ew2*~jiD15a6;}W(B_*y_0K>A?2(6N6jS?%9S))Vo+QnEat zaL5lOLj+IgXh-&>D8b;xi%;_P$Oc0ql{DMFAQbBVh9cSyfiJur`5BZzQARHaB}3(N zX!zGDKs6k}55syEO$w(``%2*HJdhE&E6a{gk%uEqJ3L>SHE(Q|v zoiu+P88Dlm@Bk7u<-;Sn(C`Ev;zFs;)?@g&!x}sSx>qQ#uG{}lMH`TH@gDXTpy+~`Mib*P_o5;Dw#My$#VXuO7`<_ zC5!%0vSfz8mCT!|K)gxn-l^O^ajw1LNEQCJ+ftSE8{o6T)C!Wy#f}dpi~iqKvg7}v zWKf#CWB*mjn0>SUKKms5HzgYdD4FQC;~CE%C8J;eqhzT7B?CC+tYDG;zbP3RK*@pt zN_GxVGNlhCBPn8zPTCDaOhAJ9qL=L?sukeB1y5~xk7!K~-|)k7!;5xZD6i_sxc=2^ z&Z{LUXJb+nFld}li$Q{y;^~;IiHZ09(@~DiIUF)9NWMfH=ml5_nd~6HD8rXnf{m}9 zTE9c!i=Hkt@xz$b=M5CD-L%cU+kTV1x!2ck$_!@>_T^Tc-(xF(;RZ`LIrBgiF)Cj1 zJVTb0@@QjV0}Et|szB$6h||7a@pMm-%G!KYfGw$2hU|bCSVGwH_$zHy<(SjX7W8Rn zzu(UHK#vR(0)b-^_TW&-3IVWZ9Kzp?4<2M;sQ%#%NR?0)sr`(u4HPU|8pfJ`%B4i z{zb|Bqz>rxdDm*MwxBajgg??&bRTIe=5Qay>^F2hh=vZ%zfV5<1dwpHKhjpzf2FNx z5{@4%5`qee(8O1XsQbBcxr2yvFd*_L%xiZ_U+Kt6q9p-y#@ior#$Jp8)p*Po#23v~ zWkf;;9{4>KV2L@8fX;2N?_eJIjbiPt(IP0GJyUs~<^*zU1#{Op8uBWcBD=__LSOS* zGHK#TWc>RArc z&UT$FwVx7P%_=Tp%J0g=%o}#_0?zRqzDh4qa!qh;nsaQ}UY$Q~5T`a1Bw*`(e}3eo zqaJQ6(m%@0emw7FZ|M1x7ds~4~Qs#a%*k9B5qHrio45}#hpX-EA% zC-CsOhLMS1jQ!BiG-;XmQZF9-lBf5`#Yep-oDJoTj<&$cKJu5!YmmCuxkbqpze*+5 ziMCjWx_-b8~e_jXK^9iS{%_^s`#MUB59| z4ob@jCORYS9%!>|AY=z)Iun>h>>2%;&FNI4vhn_+I0;NYtq?uurpo@%ufa==^k{x+ zw0;OYC{0vJ->j<@6+=RuCkvzU>03vtgO~^^fb#6}$&*L3 zzCJ+F)j8W;si%jzEa_?NZ(S-o#&bCOE(K4I)I`&_@m^g}f0h%7*F%CyvRqi+oV&SB zNvh9f3HQIORoi`a(OZJKZN0C#!Erg;&2D6NXb?%h5L7$A3lUN8xlv4*b9K5hTRk;W z=xoVtjAjy>wZ<~e0NQKFln;zPLP7pAj3`g7hQRc4`R&2RU~J{p`&wiTx%J!{fqg>x zx(BFC!FsH*6ZW0+GQN0?%fG9$q?&kfx^G3bS~4U5xg}hAm#-XS_#Sbb6q=-jgQxiw z=_z}8Oq`h)dyRlJdg{3)q4?Fty{hAfyYxJ-XRbTMy4%`=%%-llttF4V^#`;;z&gk2 zk3oPs=f&6y?r%KC zY2W9(E-mBV?l{ElwodFk_KEE8nqSQ@a{~%p1oZGe`xvUzFxfxs*GVX8r2wwMqKbGclVPY>q5!W^n98%X6)(8R-XuzOO_McrT|O`L<6u-)=1igEbc*A~NGBn{l;!FuIdzKY(;j$GnQ5 zEE~3BU8xP%YCB`D(PGmG60J}|$0vPfM_;0?BRRv=Y@q1;*;>^7*7iCMBBZ6)N7a~^ zklH}GGIpon7PJ%_u?xCh6|yiHHFc*ijd10!L$t1@Ma4N2kEs-y_1i-)q|urgMw~l# z0G-ilAUPtn8dr!`kXi%%UXC)|jik#&Qt@YCL#yBzHkV8Hfg5spw!G@*k=$rYnQq4Q zO}4CQ;z3_*=Qvt>`_w>)%7LYBH+w$x5z0h*`S|RJODj2T@l}s>bFIt&CfDb36$jm zN{z`U8z{!espG1VX-#@#!a|k5V~$-?CzBcU3^7K9gsfuFn^m#zd!kxzb=C?a_7x8B{&@4AwVyeBn0CQCmAc+~PNcDdd<#PYk6hSt%MzFWTqs z(d4!s@37yquRn525&vnqxEHY?*VgQHYtMP)8jrh|SO70e(Zsx}lXO=2nFIP; zRXVNup?a+9tmE*>sv)Ra{&5m@e5)f%mo!l)3d#~%U8A9iw!B;Wb3>v3QsY>W3TuzY zc5!1EIo(kE%IDSxJx`6}op9b2Z`kc&pL{Q|7ar1m#b9Tc%CnKDTZ6)-1ZL(g#r&tR z1yZrv#df@ZcMC+6numZ7bYzZU}4DXm_m^FdM?ZQJxOK(;0H zUa;p*MR0Axlqyh?<8L{yR+@FsQ+-L@5;fyYnJ(8CnMWdVUFV4(PJ|!1IJd`%noX}a zB?5O->;TS&)54a75+mbikn}U*5)(R(SMdhdkjn|)xNfXXPe}>Eg7kf~7S%;aFTI!W z3og!IuugmGPldxP|DrY<5wHPYb;3c2z{l9F;`FmIzC?c1vqfMQX+OD+y!E z=HBs2YIKX5-~3{2)wwuLY|;u>osJ>nm;A8q3lgrQ2Xj{mVO$(qWN?RC9q%^HYRQ3q z(!M5VO$_kdEnA0B{B%EhsA3>HH@NuZ(vec}`STZ-peYASViF139J85FYlq-FH1#KbqpQB-|kLDo5nF>;WpCl zG3^|BhPPmM4Tt^MLYda661@$eK8h*;e&|9ouPv?hJtT6?Nds~G~H6!t%AyAp7$ zwy$rDB4jFLEJViFJZCEN7-gR4d8W*B#xx*`NJ5$C42euhh>)3x2qBcI?;NhY?;HI| z-?`7Z$NN0@?)^Kzwf5R;uf5M&<}+b)VRVFxUZf%V3~qc;;wFhRh7PXJUSQDNs!}>7 z6Oa+WrhaM4lr-~^Q$a*$8!yz4IWKne-f7IeO!g+NNDiX}PoGD^xwGDwap zVKtspv1O3G!jh-J!d?vFC_sWBX^P;7VTQ?+-db>QV3QVsI4D~kZ$&)W#~#z+dka%T zlnF{$U^(Gp)lLIC04`9k7Qx_eVZ#IufmC^n?7x+LW??b~|10QlY01;3dp|amS0>Ty z?UH*=>R3k^vfmjf$S6QMS@|%w=m->2FwDv5Z|Vys{PPBGQ>*q!pc&{h^SzlhO&>=u@Nl865bkSji_1PjOA_C({c0L4C6o-q%^D_@pE=LEw;u@34y>cEXbD?$Y zF;>WEFz?2TS)?U_M^^m?$b&r(LUIT?8>HK;PkcI}A7R8Di=gYGx-<*9B<5>xpQnLZ z;XQx4f+*zTn1->%t5wf$tZ8XEJ>Ez*T0^03U26G4nrE~sh#IMmA-!Sd`JkrJEBd)h zSxi-eCMhW=sBvxi7p^kS$ywj z?-3kZOUqRLDI+(X7gUX+Zl7=GRW0D$ICkP%XUAzheQP7BleuWEW#&KRUL~wVnzp%} zk{0l?7nPOw&&Uae-%<4=q4>iD7l ztz~YCJ8#{2))%f(TA1&8CcjVG8o!fyDAhb>=vZx#bPUJ&jn2!*3B5PQ;$&7H(E2r^-L14^0F$uFZpXxrbH{3Gr#l9sJVs3k08|#VW)gh z^mEp>7iBn~HJm>tvBqlS|F}sr-ER8gHxc9RF9ce`xLpJ;>=&f)U5?JQRUkOM#-&pV zN&6&O?WbZgeIZ(S7#spL@pj+loiEfVpM5H za%a)`U2D5^(6w&t<2>|@H*SALJVRu5Dh-WFR-M`2B;EL0w}1TQ)H&}aC6l6VzX>K1 z2Cv#!ym2%(I}?wx4{kTQx>kQID5z1lM2g|3I@mJ4uKk$N(49J@PFf}U!b!)g*7gW#BsqUIR?d80^yZt!&y z%_Zsa=3u+g>dg|JTC zMu%*2TuWN0wJ(NNh^U;AXSm6dF89^5)9K40BP!K2J-1uFDUug->#Ikx*jM)-b5!TG z9He@E)2f5^XKF7(3onbct^8s;>oFJY_ZxnlJU7g0ybD^{&5N+JJuijCuFVic;7&Ui zK9lUyoQi+Dkl)(VIM;eH$>q~02E}s9X{_RlXiD~8boH)g`X||YiLq?v#pD<)7%E6w zrihKocoTh(GV^Q9{mAlaew>sT!)jLT>~UPb840`8^1YiYQZzlv0Y6_8SlLq4 z4+dS+!DMdjo}agq-Gn_F5R5Sd(FGiD%uZGE8TU1n{tSg4uWQ7W7mHVbM3r1Ti% z!LVmn-XvY{cK?yEuK9S3v+Go&Ct6x&<-KByBvej|Cxxv_aZQ#ZdX+Pu7c*~G8J}vz z$VaY!!a1e-Vd$~jXh6!FAEd@h1ve{_>)ZMgjNYCJEf|^jdU{p(N^XWqK=kmUC1FH# zmW4-9j+SY;Wg%vhXlN6m5En+Fi-92-ea580>5^8|H*LwKwCW9W!lKBUp}EmhV?U>71iR|8ORY_pK6Py5jjEXI^K(IK|GBG zXdTr>%o~$;Sl-b{e{Ig?K!mtdtzV((2rW@hB(S|2o1|KjUKQH=;6BrX?Hm+S7S~u%gUgpgy%09`KW;csG^(d%!j6bo!ylHZVa=~Ewwi2BW2l>(I zF|iKCzL0vlBEr#m=ko+ap^u1PaU_HpK5yD!^%^XTcT()O`LRavBQ&qcz2K$v%uNR4 zw3eRgZw84eRG0ftkEx!!q7v2E#C61!gkQPr`>T*d7k0NXP5P%M*BIG4t{F=OhQ%oH zIJ!)BOqX@xJv3FGr|7kG3{w#99X;D~^~;;^h+0Kzd#?JciP|&@r=L?bC%$=Zw=N^)jJ<~l{6xsNa^A@XQ;Y)cC%i@^bz){ z>kd{y;hxu)W%QMtfYKNm66ORfmjq=Qrb_PTW?$xIe$Y(2QM6<#)Wn=pJQ9G@Tv*q8 zqJpW&Tk%J-x%%DJ`4dHu2Dh2&a6kEvF9wLeJSSU^CD#1d8(RM|%o(+2=G|p15v5U2 z&DP2Jt}CJTu7PCFTLi9GeA6ZM_{x2Gfz#8mc&a_~mEw!FPsr8jmEqG_UumrDy3b4_ zU$v^v|9(&Ft%WL^zufYTw@>3V2vBX(UNCZ58zgctagnv9E(s1_d@ObGJvH`+$cx{} zidzSru9w+((W$wiaEAuW#!8|fA)0wRUI?V%)W{4naB{6FLNU@*G&2ZlGr;o-)mgxK z1s$Q8W$Ho0Pkjz8^Mej8Ja`6)6!Ouz$2jV=k##bL9$^WZe-j|kA~_$=s*1&l)wyKw z%*x8@%d^ixtxY_bRN7%Ir-^X={Ul2;F#^#EZ@utf2@9q2L`QVcLt47x84{|81$@>+ zb&sXw(;1n{JE4%_Q%CFY^)5s@jM-ou{}!?4z)3E+D#xo%yFaO6eN4n`!^pZOI$2*7q>U_N0UZH zsfju(nKKbg9w8Q*BM;P+voprW{VCafL+_{$?l~HyD=0s2=Zq#H8PYh-8vIvNt`gPH z{--7P5#!g^JP@RbP!wvi4h9~)BlJ}GSC66;Q-obg5-`J<_UMIe zl9cf{gnmJ+ITu2Pq^MjXoNRIsq+iwa)Siv-eDq&UIc$W;(fQjV^cdkI3Bd@IIMxfA zoSQY}o^95Y;~}k0{{hsLqvZ4rrpU6i7I77MVyybT6&ddY3w<>u{siTT>a(#FvEhm%TZXf!^jo$z7gz=|K;ITL8|16XBXTU zzOrBPt*UB#l%mfh!&9b)?~DPas+OnVex&8morWI$N)${FbJlhZO(YkUO-u|yTNgX2 zPZ2^P!jC9Zok?UHV)_=>E{fa!mruQEtc?9-n7mmbE997qo{y5##AjZ#c+J3UOU`<0zFnyvZ-YNAULKX%#K$OJt^r zqWqH&zg!lyK|Jyz=!fgs592p)>i3lfC&c12bQXqbhRisiOFGPGO1aEyx<)pO-13Od z6nP;1AVi`*Hp?i*@0|(Ge9f&cIab8ClUW3m=~V+EVmvuRUU{nmC$Q-KQD-UXWWN&y zJZR_jiDD!;=Le*RDPcacnI6WjfFMm7;W$MOS$nA{l5oOUkp0>2>t7IWbt*}m_{oJI zt%AY0NF8T1fn@G8k2x>e@rcIVlXUP*)vCVk=jC1|e7Xv=jg#@U;WGF5?%WdXQcbzf zau&f2``)xtGV;9%rQ08}PC?>ypA%2BtS8|)V;ng~uc?_XXLI{Vp#+yY*=#0OdnekJ zr)QF#xUD~@a3(#fAnl~&>~+;jH?_rUdw*2zgd`5SkFfU9W0;g_+!Qyp^r^&wJk_=O zG9s#E1t&h=3eR(B;#V#`iztXI6;L2O`aTeaP@GDpfpjxZb;ioW5ZnwDA6qQ)6V-$Y z4ChcU&>VFq^^-v7?L}=xwvcAYMY($>KcD|(goRMcdOf$2o+g?UlUk+0vC~xP2stBy>45 z>d*R?oFjWruuO3R4bN8;AC1ZN=iL;kd_l(#%chJC8Is4y9uyGXaXXS65lSHPGlKNvF%*Ws-Xo!WYxUL-M&|6*%auItZ~j6i=Rq*hK)@)Br)z0Uc@nb2}loP z0HlWjvQ%$0Gw=fGVOBg7b2x?~zczfr>ds9m3L%)#MkGL;ICDFEv!q-uP*RSt3+;P7 zDRoamWM)-QIXn3q?=O~=oU!7|@}HN^hf1%+Gh$y57G{tSwhn|Ou|$!0_<8op$RUyS zdE1$?pS^ejYg*b%htRM`Nf6`1SsjMxwe*R{N^&e`t~~5%n5UK;aA+XFV;DczfZLOx zli5sZ@N=URM>qH9WrLfSt&R&DI~?O{ExYV{a}ERLWQN`a&Mt*X!TwUDQ032OSfWvK zQP8lfitmUPu=>#A*`Fy>J3U!`i^J+CPv@v--YE(;PNpuE==`G>sxOEm^OjlQ9g9^6 z269w$JCs~@zzlk3Gc6GC)+}pQ6Njb9a8H}1}zIy4cl5$zUO3D>q_^qTI zQw;A9UaqnjnXc$XiF&Z4+)>$D+tbv!60|2NTrN_aKStUqV=9$j?y7N4qt);=yA@`} z+GI#AwO(hI*E0r$ld%|BoDdsiVrib>J_CNRq}-9sl5)i;4jAO>TP5Wr-#LLLLmXzZMKI^gUWx^y%xbTx%|EsZl z@dYlILCG^OBQ3u+deD*#_1sP27PL%H{UVPqv67-~-nq_y-eSFm(4v>bTEP8=QJ$xX z!3ze}xfUUfii)wZ@~>H$pWB{Tmj((WUuL*$!BHst#r<2003@O8?OMz6F0F5;r`}l` z5~)|pIbd9U4sj2XbWeRK>@byo|0_X6v23l;d()2GP}JM{7@BA2L@ioI7YdrMgam%E z%xf(9tU0E_UGwGZ)5L51ZMK!Q-H8||`fhRyi4f}6BbpHi5JH8mZ>%6{pF+BfV{N>?X_hDbF=t8?C!Moz`u|FPB8^4lqNpB4E!2ny4S{>#x zx%H=S6QeH%$vn#1TN@2VmR(k~nKN6}qO8(yhsVoA=98RKeTGTm@e_Bg=^0)qc3iZ|iwt*x(X)=G zIT!VaFQr7;*L|D0T`eTAX!HDXEJXgw$u|#d&sY%Y=*-UHWUm-`$`)lKzom)Cb@m|B zx{kg+;3aRfnxoAeb-R&AG=Ztp&6VY@jWtW=U~p?3t#8b1(c07GZrhLd?ZWQ|+jFN_ zC$qILzal-IhI7A$jftYUb1>;m(^BcF?|~5IvpVbDsc83B`$gW( zyou#swF>Sm&$gdhc{59*xU3ZO)a1I;#D%Hk6lDSpHpk-p;p!XLxFW5C>AMS9Yzt4& zO0Hk4ed_zY@5^I9Er`6FQP05ryU$-)zArY?@NjzbcBW$mlKYx=q57+h<|Q=Kj+9vm zKD~szGN00H#fD?PAFmA%PMjJzwk~Zkc(n4aqFcvO`o)uH+(V}W?g`yrTZrk<@r{_! zQ8zZ>`|!1)ixOj{Mo^>OLqMh)QUFYz^vs6X5^?cH$=AM%`9IbEI#<|2a| zUV(+RJ}DE+Z7tmQ`n)8nEz)-_71k6)!zSf|;(JuKGALY(xU^Gf7?(%`W1F*F_!ZlaF8tLO5I;8bZ3JsL+n5^9U6D)UhD^RL9He=S31% z$bYRT%#1U3eBF0ayKQ5=KjRkjIOAkK)0eU-y`H+KA4%q!nVxD`dp1|G6v|5&w=ZyT z@@ynmd4%(|;ALAkH<&!Dz!#{icjtN{^>p5{G4B4GXLkxm=rd2XP(L5gSRgb%^(2M1 zOsct@NzPNZsmCs_!1POPjqmxBOnhYJxvx2jT-FQ|W()VWjzEVhfzL~{wc+8Ts3hKTphzK|n(s%Tj&R(u7wQqV)0km7;8*@y2}+W zxm=?vB`5Vbs-+j+j876AA2dkO{>p7&Wh*Kynm8T0WK4c7;gQKbN{_kMFI9oG6)N2% zizWKTvyS7P-i>UL-@a*HePNkG$){j&#?MAmwGU%`QJmPT{u`Ix{TdfMb5!x+PcLVc zq^HG@FZC?yF$t(X;!O&w(A8^G2tsxr_8I-coUPXJiGalIOAoKbha5=Va6tT!<#^y9sScl^o_nBewjmL@p~e&iVV{AyZH;z z3?>Gd4b_jGsD3a=+RC_zEO!}zn=7#{aN_B zz-p7QSZ9QI41;-wXjV)`$+tVS144$i-K}MpTn$9tzmQ;RYiPnY@)%wYl6<6=*B-ns z-su_dx|oZnWnKTWvL#t-YIeMZN5lT3Q^)t0FO{V#PPM72KaG&1=X_iG#N7GYcgetU zkCVvtpGT17E;vVs?bk7%wStX1ovd-gg zy-v)1eD(Z}*S*BSDcgzc)$5oKZ=HWKJEQX8JEQ@g_HPhHkbVLM%v1h+|s1tj7xdKbm`hq zf8Ue4#mhtyY+ov<1srF}fxHNBwMbdnMuOZ#Ym5UqIK!)BiET1e2*VQ>+)v@=>Qy90 zg-AUVy7z#ka=x8EwR5&)a;QHeHA9si%RDPXkX~8ExVvBjgC>O4NPv&#!N`xBq}`G) z$u^6jeKn1qw7fghCh+{y@&sA`qw9{5P(Rkl!#_Efz3!geIt8$`J7^08RPH&@h=55` zz#Aw9w!cm1|8|*~Z0&7qjerkqj2xgh{hZtDPT#zN`9kQmBsmzp+3L&==nQE8*L4o~ z>yJC!5Ecj=nv3}9#lIZn%T48^0qfdB@fgN)85dS`51cRSJ z6qa&M)>qMqx=;9>^ToWwYDh03j&oP=i}2dp3a(OG4G32kn&`C|m&)1Y^~%}%vk9{) z`LD~vPLS0ad@oG&^62V{M2$UShn_@=iby%SyjUH36Zeb~O;l9-8GEX`QjZY}&>}y| z3vM{va&Y^e_R&|PH0>5WqAh}uZKtZ~VoubSU^7fVCS zmg`6pBQ3$JB@@wl%8*F9Avfh2P#D!!{3)4wD&b9`V3(2ir3}Pa1j^J}dML6DnSGVusY2Uq9K!218rtVc39C^kdl8GDneqvYpK_Gy z!Qocs(s`wkdiu+a)y%$iWy2E3wI%blX6es4p=w_CfeIB7_Z#k&m2+^(atF&Bcx5Fg z`rGwpOvqE=@}UqDzBzom zcjf$rFLdLc3x5nS>Q{JQQ>OC%oxONQhFkt+x_6HjnDor=ernE$SjCwk z!+$lMf#>fd9r#%N;tj0id!EtA`M^Q?>^4!O+i3g>Xx!xtgf=uzZ1;7Waxn8TZ>bDX zRyZ{zBYwVH_z49?bo>e80NE+AWZn0tn^G^LkdeQ(Ak>09T}%nanZZts3|z5uaYYI6 z9ggPnMy0}efS;UX8T#mhh^CK;M+w~^H?Fl=s?EH5Hqm8x83kVp!?o=ArM`gyItcR0v;M(p#ORKjZpqnK-+NBf zMJ7$uVnx!np6kH8hbrA!f!l|c!_n+kmw4)9I@$dTZMVNRQQ?q@C9zB~a`ux!uqL0m zgu3G1ENY$a;38TaBP&M}zv8Ezr(KpO`sECU?%Vlg9A?p2PC~&Bl3t3AX0kQ>kc8=P zA{^HqlA7I;@!`N&CU*Df@|qlxDHIcvElXvi-8pY_f3ZHAYC$Pgj9!p_N$N zwC~rW=IwLQ+v8L?UQ0(I*PQ5oFNNJBb;|c?qJrkE8FpjIC;k{( z66Dwf>sN6J3k`Rud)=^P@Dd({$J>^SYu0>}cr_DvCk*1kNi0OpU*=r!c%2AXs!P51 zsVPwR9~?HR>KM{mqk-H1)KL`!4pbt*n}bohijf=&~y{juO=;c&=j_6Lcx@n-RKn(Xq- z7^OExTkJGMwYZK<9+}ize_ly(rPx>~zSrgn-6iX;!u~wc5}%JT50u{1txoilPJf_sA$Ri6)+DymK0;+yINSvt&QW{&neoB7ntB2)fgDt5L9uS=wDLN2G_2v6X) z4*N;8$4L^MjF~gCC4YJsM{d@JsQ~x6{}XldbbbEM15)Ssa1%X8RS$=+!6rZzo4?T1;eoTWwLV8VEC~S>z{?V@r|u_ax%5|5jUT`RS$=ow*XX zA$bjTle6su+jq9-tF!%`b$!q($5ap)X6_(0VfR>d)saaVBvpRgo`P}qzR9e zV1WX}{<;q01nd7JCs?2Pubkk+7(AfMJ-_Pze>Hepx(ynWA^-F$fHF)7Iqg- z*&X!&2jS&1j*Pz^04oj1-YX4gpfAct3+of9h>^T*n($jc8zRuVcwQ!e zNq^YgM>C?KA1Vn0$TI1PPtM@9qIAG%W&hy!npU<+&QxxZu3^DXunR$B_6_EuEDL_t zllnmVdc2IniB~mp2L4%+#im^yoOt!!Iz!|`{VzHUg)$s0LuT4e&Q~s1&QjB5JHAiT zi8*F)zVm%vlDjh6>*71)T{9LA&dU$Gn)nl@X@~;^43k4{oG8j5XmZY!Qt{y%#A~oS z{Qx0D%?y2vLa)`24ns^zkEqU5zP?{@%%8u^UjoP!BZAZ>O|>M_aE8S3Du2MZsoDsd zK^D%P{Psb~bJh)NVr&x}LHRR=R7pevOgBaaw77W8@#@ykDe2KDley?V#Y{)XmlVDe z;3;&gwVnFWJftE_pjMyJ_x9=(My5E^oN^=e$=sgC8TGTGSqYwD>zBgY?|!3xM&8Rb z+{j?^QX;K~xpeKu2G!~Bx17YDub4F3X*~LDTOk{yjQ#n%H^!qQa)cE6Cs3Su7P-ZT z6dqgE6m+)Lp1*rjvIcTVx^z|#5U0hiAD%>PQEo3GdaOK1ESZjJ8>rV`&TYHMr0 z$F)p*XE;l3wyHGp)hNC|sb1aiP5AWOt>qqm+4;OtC$Ceh5HTTiWMP~mgn=VLx!5FO z9_K{Go}62KhcSEV@kr0Bu=6|1^dg0#R^xdk7+f(?|ajSQ&xVl8ONR^AdcZF6@QMkQFbml|6 z#VYLYVuJOB{Kr>ua?jPKu}D6mpi;E_h0$z4iIW zjiZt=??fP^c=)W*I0lXWdB&)7A?gA}X&M(c&UzAiZG4>1WTH`x9P*#ycz7nYFYPIQObkZsIWCMFTZ+L=C&b4GfGk~~oU zqkUQUNV(H1#8url0js4fgD;HjG96&`tpFaPnba~ovEmD=cwD zDLe|RRrJ?og;Y7!s_>k=YorZwjg`1+J^%*-S(+%+Pzm4Z1ic}-sjX%u=gr3MOsqTY zqxZ@|wK><3lPc%Cf_C9I)O>aM5ZmFGUmu&A;i5z*#ZW$I-5qon?jz1zfh6h+M+k}&jFS5?vx~xOXb)V;LlPFQ&vkiHPq!9(j7DI46M35F&*U{5_=^9;&e!K!)hGYd zNUP;~L$vL#cX6-ntLfL(10uj z=wjbVXUdlmQb;YncCKBmBo}#D5C2$uf`0oKDm4X@^4^-;-Z=r$k1OTVnU{yO&Rsh* zLH)Uqj_(zsJ^CA>r42nJ^!B>yz-ta9{?7;fbVS-7V=-TTwEQ7z=_Xx{@sOb3N!Mxl z=rMky(w@*W!J5+)n(SgT&X`rVLe`v%W9=4Q--mV0qgJHMlOK`4^h}vR077KPD{ROv zf`PE$)GwB$k(vC-YH+d7ZHUiiLOhf39>4o@vI=n_wEMRwhb7!{ulk!cXAqACG(@!p z8-IFv^m3eFHWOiH!`XoQL{{}CT%)p---sttTRE0r14UN~UL4OQ>Nx|qUN+iZFMs=9 zdFd@h1O6a0$!&$mZm&GVy5Tz+SiL1?8m zU=>-Eim0{t<$+ZKbBE6d4lIl+uJf}4Q@W`}ZgDFn54gXwj=MI|s3_~HFO9tLcKu>4 z(Rhx{9Wzuvaz@)x@4U$BaN@ET@I?zE5*hNA9|opr z`sMlpe~|wAfrBtD4F34z3L3hp-D1*Pt$|Jb&u}>4MQlZ=A%Ii80}#GddKiE{{|g{g zt6;<$wq5v`^>))KK8*)z_kTF=Tixy*qX4@F95jR!AAaERN4{ZRXKx1{`b9wpR(?Q< z3fh4Mdjzi(pXGz~$iuGB!pQ;ns@!ZG+(6)*6>z|fxY-z44T0FWF^`_HKHyig=s-5z zS++mBv(U7FAZZQBpF{vYN#B1y$reNqWC7AaX!%zdp-KF+7QRE@KR$5DxL++K20rcD zuR4H>qCFRM;OYUI?%fuSAl+M97=EyB_v{tAM^M1weM+Z7p!REa1S|mj`eg3z* zj}m?0ko5lXG5srmJ7U{1*6>;gg5A163rh~01$Y)0{8Zn;`8LxK>#xS zGlV<3wx5a04EW%5zqbE)IEb&2l+$SyUnf0&`K4G0OHp>0JIy*D$ z^apyM_I{4>m_VE04IUiWzEXUyAE?~}mKZyw_DTS%)z?d*YpPx_*3!a z;uxUY%D)+T zt>G7bH4n~44u8}9R#!~s4mQ?1{A?L*u(!3ie%vge7aKsE!9^E%Z7FvD-}JWmUYngk z+iFk{bu%5pa)4*f4CplHRzLrBG0vi4A0woj3w9h`69F44O zVPs+sCvsS8*0S%0^_*P)*e=I5-c9X1$-G~{V7pBEHa3n9j`n(ghAs@#h56rh!SpM$ zGpzQ)GbISqUE^PO2j-kGGyHR3j4*vsv+S2W2Ln?hD_BnyUXuU`|963!WV_#{9nGwa zEX}Nq{uBDvkOSuux;%YdH*k%59N4|zZW?%|_>>>GZRpLEEo*IPWa+49U}I%v<_HXZ zV@op=Q%C5r2>O$gwZusS0D>wkcmz+__DS5Yw}VEo<;3od;MnHdg6k4njz`(so)I{& zZeKu%0Wc2xvZ22aFhN7u^2c_Auw(tUU|7P9Yn5#bz%#`sn(Lr2{*1!j2si=g;AmtB zZ?j_g?%m&JLLUagQSO66fMH@&9bl((E)ED66Eru!7W!M1z=!u>Dak!B9%xE_*Unuj zar~oH&hi|T5@-m&tLL5b!D&;epeeVh#)eMk;^6u$zMJjpHPcFaDp7lV4oG z;{Ywj8okZpwwA(<*O%$*BdS7)-!)_xA|)FWb07V4|Qous#0w(&uhq5UW zSi`~2!paG4IG~Lf{H(w2+#ziGF)SAk3dPjS!O_Ov4aQdjUl$kkR@GU6aqxy!LH`5y z0W&0jw@kmQ;EsLVdqk37I_RtZ4;Z_Q$Q{Q$EB<*F3UEkc;Wz0g9vH`OLjgwmY}}mO z+aQ6hQBV5j+G0ULe_}6AZ?7AobjL*x;0CX)u#~aep#knI1`Ar5=I?C}r+}TO zIl#w-9Tptd4vk_DnxKcpZbk;4skN?oeGr>Ja08%zUBEtg*r74*N9T4K7dtAqH7SFs zP=5gf{(yB6tHTe9b32R*&4m@v2XlTpT{NI3n1ELdI6<4&mevqP{B0ix3Oa&$c(*Pv zRCMCz-*)S0Q@cjcbGt@iB{=5Eni)6xg4(r9C-umm8-$!UuX!|`kiPrn;zJA5x zphqOQNu>WYU#kjOunPjqWusr6fsRGx;XC`?R6@tLwij{Gkibb=;R~`<&90CTqyLuc zU+)hMX=^gx8`92#C8QkZ>vnX-9EPP!_YMsSW|6sfpab4!!=D~8hQYObcwEp_{SNEb z+ILHQTU>#=Z_w*+Sgx+e9iA(ACxsPy-TNz^4$l>+R#5b8COF^zo#KNLe`s9LT>T!d z*psVm4B!)X^hF*X)BdP}AAJ>}Wp+Nmi7`1A1O#g+d?`M-_x~wPyTlIIcp!U5vEWt9 z0tOp->Y=eQ{h6;{GKAMHF!~CG1x~ke4i5}kw|+NSzi>g1Ll6`6ID{pO{mJ3U+9xP@ zvf!gFutux)@W9}O2!XK=yOISO(!I%&O#8QF{lSvLlLdO^(7<4=Y0p0Z2G14?3|re1 zG(0ZQn%*zzRNZ|>G*ADxg#E7ad*Om##lTur;*3M%+8bO#a2+On%(cLvsdgC^mH;Eu2!tT1i@(*wwP`BW9A9N`s@`HHl(1Am8jPUNx^7BIz_nR;HAq~cW8j{_+S%Dv9Yl8?71liA5z$kg>7xg^0yos z+irWbpt@I8Humo>v82Oy`kS5jAB^ls>=>XxfP?^m(1r}wYmII_G%LRWgc*KJzpN`f zFfdS39g4c22TQKP4Y48>do;Y}S zAJ!v#^X|~Z!QRJ6?@`rp4`-JapTLz4!N4R-4J#Thgvc((`ES{U^o8q;sq0#>^zc24aI2BybMtzYE-@*cks?(wIz*^z0q=jr1I0 z4nTnOoTA)X6b{e{c*|_}20T-Iv?l((I~yBAGb5;z<~u^znoHncl{E`7ubptY_pT%Q#JJusR0Vj!kGN6P}FuATJXRGJ2W}q5IXJAQfCLi@dj@D zZCJoF#m5NP?myT$1USuxks3U-Egxtb8Yl2YIw#N05Z@8rhR__zU0ei&Tfq3Xgu!Qw zcsP78@Ia-4wz|;F0lA(4<~X34`?ICZL{Lin%o{gJrc)J?`q_}w;n;IP=fQhb{JDLDAy z0IhF8fTi&L0lipVbc9kg#qY17v}x4#X$FN*HqzFeJo)A%UgtCd1+I{gJxe;6YOdO4T50 z8GZZE8Z31Xp~FFgr4C-{phqn5^(lDf59$QvmJJ5+86E#3jtXeq>TlF>T{s**80z)| z51Kk~VjY&5A=#e3V8?5{^5LMtQn!!!VK=2K&nNofcme?-kQZL*>MtJ--XEERRyvl= zF~P|LZHD1}XLexBZe5`7TxWbxXqzRefBVW#Y*>7r2Y7i=;N@WtrJpkSyB_wgJ`SzQ zY+I`QpR2RwJcE%Z6$MAoZK;!YodGHN08-L}0VQ_rFG1~z33`?Tvb_Ma92fL}-W1XgsXQ_O zh5~Twz`)@N`xkKFJpvZ!RSh&4aE$bc(4~L^a|do%fpz>Xn7vgPdeE|MO=M91)0Q;` z844mGu9pS?=>j)!(th*WTF$%<|4WAUwT=OOw_?>!13I8b!2Lh~j(q>RQhaoPq&-RJuzggf9!7BKpeBeIA)&)+u&;D0Oz&q^} zgW1Vd0e>)we!J1lv!wF}XchMJdl2p3srGm?{|7BlXn|Bh`=27gd_C|bVz%E1_^RkY z-Ttdw>d@K?(gI(u0K~Utd|&1th?aesTu{L9RR(q#aW6PfkNczqdi+595G-36xM931 zP-5^2?|1M;75#tU+gJK~=s^yW2%p$~2c%r_fk6JtH23fzw?TnV*rB|90FbZ=oBlkM zg-Z7S^8YYjVcx;I{U1YRYvKi^44 "org.apache.hudi.keygen.ComplexKeyGenerator", KeyGeneratorOptions.HIVE_STYLE_PARTITIONING_ENABLE.key() -> "true", HiveSyncConfigHolder.HIVE_SYNC_ENABLED.key() -> "false", - HoodieWriteConfig.RECORD_MERGER_IMPLS.key() -> "org.apache.hudi.HoodieSparkRecordMerger" + HoodieWriteConfig.RECORD_MERGER_IMPLS.key() -> "org.apache.hudi.HoodieSparkRecordMerger", + HoodieWriteConfig.ENABLE_COMPLEX_KEYGEN_VALIDATION.key() -> "false" ) df1.write.format("hudi").options(hudiOptions).mode(SaveMode.Append).save(basePath) diff --git a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestCreateTable.scala b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestCreateTable.scala index 08a6d600d7161..7d643999bf5e3 100644 --- a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestCreateTable.scala +++ b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestCreateTable.scala @@ -962,83 +962,9 @@ class TestCreateTable extends HoodieSparkSqlTestBase { } } - test("Test Create Table From Existing Hoodie Table For Multi-Level Partitioned Table") { - withTempDir { tmp => - Seq("2021-08-02", "2021/08/02").foreach { day => - val tableName = generateTableName - val tablePath = s"${tmp.getCanonicalPath}/$tableName" - import spark.implicits._ - val df = Seq((1, "a1", 10, 1000, day, 12)).toDF("id", "name", "value", "ts", "day", "hh") - // Write a table by spark dataframe. - df.write.format("hudi") - .option(HoodieWriteConfig.TBL_NAME.key, tableName) - .option(TABLE_TYPE.key, MOR_TABLE_TYPE_OPT_VAL) - .option(RECORDKEY_FIELD.key, "id") - .option(PRECOMBINE_FIELD.key, "ts") - .option(PARTITIONPATH_FIELD.key, "day,hh") - .option(URL_ENCODE_PARTITIONING.key, "true") - .option(HoodieWriteConfig.INSERT_PARALLELISM_VALUE.key, "1") - .option(HoodieWriteConfig.UPSERT_PARALLELISM_VALUE.key, "1") - .mode(SaveMode.Overwrite) - .save(tablePath) - - // Create a table over the existing table. - spark.sql( - s""" - |create table $tableName using hudi - |location '$tablePath' - |""".stripMargin) - checkAnswer(s"select _hoodie_record_key, id, name, value, ts, day, hh from $tableName")( - Seq("id:1", 1, "a1", 10, 1000, day, 12) - ) - // Check the missing properties for spark sql - val metaClient = HoodieTableMetaClient.builder() - .setBasePath(tablePath) - .setConf(spark.sessionState.newHadoopConf()) - .build() - val properties = metaClient.getTableConfig.getProps.asScala.toMap - assertResult(true)(properties.contains(HoodieTableConfig.CREATE_SCHEMA.key)) - assertResult("day,hh")(properties(HoodieTableConfig.PARTITION_FIELDS.key)) - assertResult("ts")(properties(HoodieTableConfig.PRECOMBINE_FIELD.key)) - - val escapedPathPart = escapePathName(day) - - val query = s"select _hoodie_record_key, _hoodie_partition_path, id, name, value, ts, day, hh from $tableName order by id" - // Test insert into - spark.sql(s"insert into $tableName values(2, 'a2', 10, 1000, '$day', 12)") - checkAnswer(query)( - Seq("id:1", s"$escapedPathPart/12", 1, "a1", 10, 1000, day, 12), - Seq("id:2", s"$escapedPathPart/12", 2, "a2", 10, 1000, day, 12)) - - // Test merge into - spark.sql( - s""" - |merge into $tableName h0 - |using (select 1 as id, 'a1' as name, 11 as value, 1001 as ts, '$day' as day, 12 as hh) s0 - |on h0.id = s0.id - |when matched then update set * - |""".stripMargin) - checkAnswer(query)( - Seq("id:1", s"$escapedPathPart/12", 1, "a1", 11, 1001, day, 12), - Seq("id:2", s"$escapedPathPart/12", 2, "a2", 10, 1000, day, 12)) - - // Test update - spark.sql(s"update $tableName set value = value + 1 where id = 2") - checkAnswer(query)( - Seq("id:1", s"$escapedPathPart/12", 1, "a1", 11, 1001, day, 12), - Seq("id:2", s"$escapedPathPart/12", 2, "a2", 11, 1000, day, 12)) - - // Test delete - spark.sql(s"delete from $tableName where id = 1") - checkAnswer(query)( - Seq("id:2", s"$escapedPathPart/12", 2, "a2", 11, 1000, day, 12)) - } - } - } - test("Test Create Table with Complex Key Generator and Key Encoding") { withTempDir { tmp => - Seq((false, 6), (true, 6), (false, 8), (true, 8), (false, 9), (true, 9)).foreach { params => + Seq((false, 6), (true, 6)).foreach { params => val tableName = generateTableName val tablePath = s"${tmp.getCanonicalPath}/$tableName" val encodeSingleKeyFieldValue = params._1 From 88393254306be47d9d1c357a7363b8f24d6f6145 Mon Sep 17 00:00:00 2001 From: Lokesh Jain Date: Tue, 13 Jan 2026 00:13:33 +0530 Subject: [PATCH 03/35] Fix checkstyle --- .../src/main/java/org/apache/hudi/keygen/KeyGenUtils.java | 1 - 1 file changed, 1 deletion(-) diff --git a/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/keygen/KeyGenUtils.java b/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/keygen/KeyGenUtils.java index a9530a6ff9cf1..d2c94580c56c5 100644 --- a/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/keygen/KeyGenUtils.java +++ b/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/keygen/KeyGenUtils.java @@ -22,7 +22,6 @@ import org.apache.hudi.common.config.TypedProperties; import org.apache.hudi.common.model.HoodieRecord; import org.apache.hudi.common.table.HoodieTableConfig; -import org.apache.hudi.common.table.HoodieTableVersion; import org.apache.hudi.common.util.ConfigUtils; import org.apache.hudi.common.util.Option; import org.apache.hudi.common.util.PartitionPathEncodeUtils; From 0dae7e2d02ebe2c3454572c8bed90fee1a9562dc Mon Sep 17 00:00:00 2001 From: Lokesh Jain Date: Wed, 14 Jan 2026 17:55:36 +0530 Subject: [PATCH 04/35] Fix compilation --- .../client/TestBaseHoodieWriteClient.java | 10 +-- .../hudi/util/JavaScalaConverters.scala | 72 +++++++++++++++++++ .../hudi/keygen/TestCustomKeyGenerator.java | 31 ++++---- ...stCreateKeyGeneratorByTypeWithFactory.java | 5 +- .../hudi/testutils/HoodieClientTestUtils.java | 6 ++ .../hudi/testutils/SparkDatasetTestUtils.java | 2 +- .../common/testutils/HoodieTestUtils.java | 4 ++ .../apache/hudi/TestDataSourceDefaults.scala | 6 +- .../spark/sql/hudi/TestCreateTable.scala | 5 +- 9 files changed, 109 insertions(+), 32 deletions(-) create mode 100644 hudi-client/hudi-spark-client/src/main/scala/org/apache/hudi/util/JavaScalaConverters.scala diff --git a/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/client/TestBaseHoodieWriteClient.java b/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/client/TestBaseHoodieWriteClient.java index 8e2aaa71b3cde..2824d28de146a 100644 --- a/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/client/TestBaseHoodieWriteClient.java +++ b/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/client/TestBaseHoodieWriteClient.java @@ -53,7 +53,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static org.apache.hudi.common.testutils.HoodieTestUtils.getDefaultStorageConf; +import static org.apache.hudi.common.testutils.HoodieTestUtils.getDefaultHadoopConf; import static org.apache.hudi.testutils.Assertions.assertComplexKeyGeneratorValidationThrows; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -172,7 +172,7 @@ void testWithComplexKeyGeneratorValidation(String keyGeneratorClass, HoodieWriteConfig.ENABLE_COMPLEX_KEYGEN_VALIDATION.key(), enableComplexKeyGeneratorValidation); } metaClient = HoodieTestUtils.init( - HoodieTestUtils.getDefaultStorageConf(), basePath, getTableType(), tableProperties); + HoodieTestUtils.getDefaultHadoopConf(), basePath, getTableType(), tableProperties); HoodieWriteConfig.Builder writeConfigBuilder = HoodieWriteConfig.newBuilder() .withPath(basePath) .withProperties(writeProperties); @@ -201,7 +201,7 @@ private static class TestWriteClient extends BaseHoodieWriteClient table, Option timelineService, BaseHoodieTableServiceClient tableServiceClient) { - super(new HoodieLocalEngineContext(getDefaultStorageConf()), writeConfig, timelineService, null); + super(new HoodieLocalEngineContext(getDefaultHadoopConf()), writeConfig, timelineService, null); this.table = table; this.tableServiceClient = tableServiceClient; } @@ -235,10 +235,6 @@ protected HoodieTable createTable(HoodieWriteCon return table; } - @Override - protected void validateTimestamp(HoodieTableMetaClient metaClient, String instantTime) { - } - @Override public String filterExists(String hoodieRecords) { return ""; diff --git a/hudi-client/hudi-spark-client/src/main/scala/org/apache/hudi/util/JavaScalaConverters.scala b/hudi-client/hudi-spark-client/src/main/scala/org/apache/hudi/util/JavaScalaConverters.scala new file mode 100644 index 0000000000000..4a55bc7f2f076 --- /dev/null +++ b/hudi-client/hudi-spark-client/src/main/scala/org/apache/hudi/util/JavaScalaConverters.scala @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.hudi.util + +import scala.collection.JavaConverters._ + +/** + * Utils that do conversion between Java and Scala collections, used by classes in Java code only. + * For classes in Scala code, import `scala.collection.JavaConverters._` directly. + */ +object JavaScalaConverters { + /** + * @param scalaList list in Scala [[Seq]]. + * @tparam A type of item. + * @return list in [[java.util.List]]. + */ + def convertScalaListToJavaList[A](scalaList: Seq[A]): java.util.List[A] = { + scalaList.asJava + } + + /** + * @param javaList list in [[java.util.List]]. + * @tparam A type of item. + * @return list in Scala immutable [[List]]. + */ + def convertJavaListToScalaList[A](javaList: java.util.List[A]): List[A] = { + javaList.asScala.toList + } + + /** + * @param javaList list in [[java.util.List]]. + * @tparam A type of item. + * @return list in Scala [[Seq]]. + */ + def convertJavaListToScalaSeq[A](javaList: java.util.List[A]): Seq[A] = { + javaList.asScala.toSeq + } + + /** + * @param javaIterator iterator in [[java.util.Iterator]] + * @tparam A type of item. + * @return iterator in Scala [[Iterator]]. + */ + def convertJavaIteratorToScalaIterator[A](javaIterator: java.util.Iterator[A]): Iterator[A] = { + javaIterator.asScala + } + + /** + * @param javaProperties properties in [[java.util.Properties]] + * @return map in Scala [[Map]]. + */ + def convertJavaPropertiesToScalaMap(javaProperties: java.util.Properties): Map[String, String] = { + javaProperties.asScala.toMap + } +} diff --git a/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/keygen/TestCustomKeyGenerator.java b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/keygen/TestCustomKeyGenerator.java index 18a4c33c93be3..171f516e52614 100644 --- a/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/keygen/TestCustomKeyGenerator.java +++ b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/keygen/TestCustomKeyGenerator.java @@ -31,6 +31,7 @@ import org.apache.spark.unsafe.types.UTF8String; import org.junit.jupiter.api.Test; +import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; @@ -134,16 +135,16 @@ private String stackTraceToString(Throwable e) { } @Test - void testSimpleKeyGeneratorWithKeyGeneratorClass() { + void testSimpleKeyGeneratorWithKeyGeneratorClass() throws IOException { testSimpleKeyGenerator(getPropertiesForSimpleKeyGen(true)); } @Test - void testSimpleKeyGeneratorWithKeyGeneratorType() { + void testSimpleKeyGeneratorWithKeyGeneratorType() throws IOException { testSimpleKeyGenerator(getPropertiesForSimpleKeyGen(false)); } - public void testSimpleKeyGenerator(TypedProperties props) { + public void testSimpleKeyGenerator(TypedProperties props) throws IOException { BuiltinKeyGenerator keyGenerator = (BuiltinKeyGenerator) HoodieSparkKeyGeneratorFactory.createKeyGenerator(props); GenericRecord avroRecord = getRecord(); @@ -158,16 +159,16 @@ public void testSimpleKeyGenerator(TypedProperties props) { } @Test - void testTimestampBasedKeyGeneratorWithKeyGeneratorClass() { + void testTimestampBasedKeyGeneratorWithKeyGeneratorClass() throws IOException { testTimestampBasedKeyGenerator(getPropertiesForTimestampBasedKeyGen(true)); } @Test - void testTimestampBasedKeyGeneratorWithKeyGeneratorType() { + void testTimestampBasedKeyGeneratorWithKeyGeneratorType() throws IOException { testTimestampBasedKeyGenerator(getPropertiesForTimestampBasedKeyGen(false)); } - public void testTimestampBasedKeyGenerator(TypedProperties props) { + public void testTimestampBasedKeyGenerator(TypedProperties props) throws IOException { BuiltinKeyGenerator keyGenerator = (BuiltinKeyGenerator) HoodieSparkKeyGeneratorFactory.createKeyGenerator(props); @@ -183,16 +184,16 @@ public void testTimestampBasedKeyGenerator(TypedProperties props) { } @Test - void testNonPartitionedKeyGeneratorWithKeyGeneratorClass() { + void testNonPartitionedKeyGeneratorWithKeyGeneratorClass() throws IOException { testNonPartitionedKeyGenerator(getPropertiesForNonPartitionedKeyGen(true)); } @Test - void testNonPartitionedKeyGeneratorWithKeyGeneratorType() { + void testNonPartitionedKeyGeneratorWithKeyGeneratorType() throws IOException { testNonPartitionedKeyGenerator(getPropertiesForNonPartitionedKeyGen(false)); } - public void testNonPartitionedKeyGenerator(TypedProperties props) { + public void testNonPartitionedKeyGenerator(TypedProperties props) throws IOException { BuiltinKeyGenerator keyGenerator = (BuiltinKeyGenerator) HoodieSparkKeyGeneratorFactory.createKeyGenerator(props); @@ -323,16 +324,16 @@ public void testPartitionFieldsInImproperFormat(TypedProperties props) { } @Test - void testComplexRecordKeyWithSimplePartitionPathWithKeyGeneratorClass() { + void testComplexRecordKeyWithSimplePartitionPathWithKeyGeneratorClass() throws IOException { testComplexRecordKeyWithSimplePartitionPath(getComplexRecordKeyWithSimplePartitionProps(true)); } @Test - void testComplexRecordKeyWithSimplePartitionPathWithKeyGeneratorType() { + void testComplexRecordKeyWithSimplePartitionPathWithKeyGeneratorType() throws IOException { testComplexRecordKeyWithSimplePartitionPath(getComplexRecordKeyWithSimplePartitionProps(false)); } - public void testComplexRecordKeyWithSimplePartitionPath(TypedProperties props) { + public void testComplexRecordKeyWithSimplePartitionPath(TypedProperties props) throws IOException { BuiltinKeyGenerator keyGenerator = (BuiltinKeyGenerator) HoodieSparkKeyGeneratorFactory.createKeyGenerator(props); @@ -350,16 +351,16 @@ public void testComplexRecordKeyWithSimplePartitionPath(TypedProperties props) { } @Test - void testComplexRecordKeysWithComplexPartitionPathWithKeyGeneratorClass() { + void testComplexRecordKeysWithComplexPartitionPathWithKeyGeneratorClass() throws IOException { testComplexRecordKeysWithComplexPartitionPath(getComplexRecordKeyAndPartitionPathProps(true)); } @Test - void testComplexRecordKeysWithComplexPartitionPathWithKeyGeneratorType() { + void testComplexRecordKeysWithComplexPartitionPathWithKeyGeneratorType() throws IOException { testComplexRecordKeysWithComplexPartitionPath(getComplexRecordKeyAndPartitionPathProps(false)); } - public void testComplexRecordKeysWithComplexPartitionPath(TypedProperties props) { + public void testComplexRecordKeysWithComplexPartitionPath(TypedProperties props) throws IOException { BuiltinKeyGenerator keyGenerator = (BuiltinKeyGenerator) HoodieSparkKeyGeneratorFactory.createKeyGenerator(props); diff --git a/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/keygen/factory/TestCreateKeyGeneratorByTypeWithFactory.java b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/keygen/factory/TestCreateKeyGeneratorByTypeWithFactory.java index ec66d19778b08..238c45c81c1ac 100644 --- a/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/keygen/factory/TestCreateKeyGeneratorByTypeWithFactory.java +++ b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/keygen/factory/TestCreateKeyGeneratorByTypeWithFactory.java @@ -41,6 +41,7 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import java.io.IOException; import java.util.stream.Stream; class TestCreateKeyGeneratorByTypeWithFactory { @@ -73,7 +74,7 @@ void teardown() { @ParameterizedTest @MethodSource("configParams") - void testKeyGeneratorTypes(String keyGenType) { + void testKeyGeneratorTypes(String keyGenType) throws IOException { props.put(HoodieWriteConfig.KEYGENERATOR_TYPE.key(), keyGenType); KeyGeneratorType keyType = KeyGeneratorType.valueOf(keyGenType); @@ -107,7 +108,7 @@ void testKeyGeneratorTypes(String keyGenType) { } @Test - void testAutoRecordKeyGenerator() { + void testAutoRecordKeyGenerator() throws IOException { props = new TypedProperties(); props.put(KeyGeneratorOptions.PARTITIONPATH_FIELD_NAME.key(), "partition"); props.put(KeyGenUtils.RECORD_KEY_GEN_INSTANT_TIME_CONFIG, "100"); diff --git a/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/testutils/HoodieClientTestUtils.java b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/testutils/HoodieClientTestUtils.java index a7808ea938248..f5f17e1d9bc55 100644 --- a/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/testutils/HoodieClientTestUtils.java +++ b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/testutils/HoodieClientTestUtils.java @@ -36,6 +36,7 @@ import org.apache.hudi.common.table.view.FileSystemViewStorageConfig; import org.apache.hudi.common.table.view.HoodieTableFileSystemView; import org.apache.hudi.common.table.view.TableFileSystemView.BaseFileOnlyView; +import org.apache.hudi.common.testutils.HoodieTestUtils; import org.apache.hudi.common.util.Option; import org.apache.hudi.common.util.ReflectionUtils; import org.apache.hudi.config.HoodieWriteConfig; @@ -57,6 +58,7 @@ import org.apache.spark.sql.Dataset; import org.apache.spark.sql.Row; import org.apache.spark.sql.SQLContext; +import org.apache.spark.sql.SparkSession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -344,4 +346,8 @@ private static boolean canLoadClass(String className) { return false; } } + + public static HoodieTableMetaClient createMetaClient(SparkSession spark, String basePath) { + return HoodieTestUtils.createMetaClient(spark.sessionState().newHadoopConf(), basePath); + } } diff --git a/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/testutils/SparkDatasetTestUtils.java b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/testutils/SparkDatasetTestUtils.java index e20ad8f7bc942..59b4417376f84 100644 --- a/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/testutils/SparkDatasetTestUtils.java +++ b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/testutils/SparkDatasetTestUtils.java @@ -94,7 +94,7 @@ public class SparkDatasetTestUtils { * @param schema instance of {@link StructType} for which encoder is requested. * @return the encoder thus generated. */ - private static ExpressionEncoder getEncoder(StructType schema) { + public static ExpressionEncoder getEncoder(StructType schema) { List attributes = JavaConversions.asJavaCollection(schema.toAttributes()).stream() .map(Attribute::toAttribute).collect(Collectors.toList()); return RowEncoder.apply(schema) diff --git a/hudi-common/src/test/java/org/apache/hudi/common/testutils/HoodieTestUtils.java b/hudi-common/src/test/java/org/apache/hudi/common/testutils/HoodieTestUtils.java index 7100ab9af3419..a11e65dc78045 100644 --- a/hudi-common/src/test/java/org/apache/hudi/common/testutils/HoodieTestUtils.java +++ b/hudi-common/src/test/java/org/apache/hudi/common/testutils/HoodieTestUtils.java @@ -255,4 +255,8 @@ public static DistributedFileSystem useExternalHdfs() throws IOException { conf.set("dfs.replication", "3"); return (DistributedFileSystem) DistributedFileSystem.get(conf); } + + public static HoodieTableMetaClient createMetaClient(Configuration conf, String basePath) { + return HoodieTableMetaClient.builder().setConf(conf).setBasePath(basePath).build(); + } } diff --git a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/hudi/TestDataSourceDefaults.scala b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/hudi/TestDataSourceDefaults.scala index d0b644778d7ec..09364d8835249 100644 --- a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/hudi/TestDataSourceDefaults.scala +++ b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/hudi/TestDataSourceDefaults.scala @@ -21,18 +21,18 @@ import org.apache.hudi.HoodieSparkUtils.sparkAdapter import org.apache.hudi.avro.HoodieAvroUtils import org.apache.hudi.common.config.TypedProperties import org.apache.hudi.common.model._ -import org.apache.hudi.common.testutils.{SchemaTestUtil, PreCombineTestUtils} +import org.apache.hudi.common.testutils.{PreCombineTestUtils, SchemaTestUtil} import org.apache.hudi.common.util.Option import org.apache.hudi.common.util.PartitionPathEncodeUtils.DEFAULT_PARTITION_PATH import org.apache.hudi.config.{HoodiePayloadConfig, HoodieWriteConfig} import org.apache.hudi.exception.{HoodieException, HoodieKeyException} import org.apache.hudi.keygen._ import org.apache.hudi.testutils.SparkDatasetTestUtils - import org.apache.avro.Schema import org.apache.avro.generic.GenericRecord import org.apache.spark.sql.Row import org.apache.spark.sql.catalyst.InternalRow +import org.apache.spark.sql.catalyst.encoders.ExpressionEncoder import org.apache.spark.sql.catalyst.expressions.GenericRowWithSchema import org.apache.spark.sql.types.StructType import org.apache.spark.unsafe.types.UTF8String @@ -53,7 +53,7 @@ class TestDataSourceDefaults extends ScalaAssertionSupport { var internalRow: InternalRow = _ val testStructName = "testStructName" val testNamespace = "testNamespace" - val encoder = sparkAdapter.getCatalystExpressionUtils.getEncoder(structType) + val encoder: ExpressionEncoder[_] = SparkDatasetTestUtils.getEncoder(structType) @BeforeEach def initialize(): Unit = { baseRecord = SchemaTestUtil diff --git a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestCreateTable.scala b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestCreateTable.scala index 7d643999bf5e3..d28a8a205e67b 100644 --- a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestCreateTable.scala +++ b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestCreateTable.scala @@ -21,7 +21,6 @@ import org.apache.hudi.DataSourceWriteOptions._ import org.apache.hudi.HoodieSparkUtils import org.apache.hudi.common.model.{HoodieRecord, WriteOperationType} import org.apache.hudi.common.table.{HoodieTableConfig, HoodieTableMetaClient} -import org.apache.hudi.common.util.PartitionPathEncodeUtils.escapePathName import org.apache.hudi.config.HoodieWriteConfig import org.apache.hudi.hadoop.realtime.HoodieParquetRealtimeInputFormat import org.apache.hudi.keygen.SimpleKeyGenerator @@ -31,9 +30,7 @@ import org.apache.hudi.testutils.HoodieClientTestUtils.createMetaClient import org.apache.spark.sql.{SaveMode, SparkSession} import org.apache.spark.sql.catalyst.TableIdentifier import org.apache.spark.sql.catalyst.catalog.{CatalogTableType, HoodieCatalogTable} -import org.apache.spark.sql.hudi.HoodieSqlCommonUtils -import org.apache.spark.sql.hudi.common.HoodieSparkSqlTestBase -import org.apache.spark.sql.hudi.common.HoodieSparkSqlTestBase.{disableComplexKeygenValidation, getLastCommitMetadata} +import org.apache.spark.sql.hudi.HoodieSparkSqlTestBase.getLastCommitMetadata import org.apache.spark.sql.types._ import org.junit.jupiter.api.Assertions.{assertFalse, assertTrue} From bb4ad98645af8c4ccd5d8c156268cba56c1fd486 Mon Sep 17 00:00:00 2001 From: Lokesh Jain Date: Thu, 15 Jan 2026 20:44:03 +0530 Subject: [PATCH 05/35] Fix tests --- .../apache/hudi/functional/TestBootstrapRead.java | 10 +++++++++- .../hudi/functional/TestBootstrapReadBase.java | 3 ++- .../functional/TestNewHoodieParquetFileFormat.java | 3 ++- .../apache/hudi/keygen/TestComplexKeyGenerator.java | 2 +- .../scala/org/apache/hudi/TestHoodieFileIndex.scala | 13 +++++++++---- .../functional/TestGetPartitionValuesFromPath.scala | 7 +++++-- .../hudi/functional/cdc/TestCDCDataFrameSuite.scala | 2 ++ .../org/apache/spark/sql/hudi/TestInsertTable.scala | 12 +++++++++--- .../sql/hudi/TestLazyPartitionPathFetching.scala | 5 ++++- .../TestPartitionPushDownWhenListingPaths.scala | 4 +++- .../org/apache/spark/sql/hudi/TestRepairTable.scala | 6 +++++- .../apache/spark/sql/hudi/TestShowPartitions.scala | 8 ++++++-- .../apache/spark/sql/hudi/TestTruncateTable.scala | 5 +++++ .../sql/hudi/procedure/TestFsViewProcedure.scala | 5 ++++- .../sql/hudi/procedure/TestStatsProcedure.scala | 5 ++++- 15 files changed, 70 insertions(+), 20 deletions(-) diff --git a/hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/functional/TestBootstrapRead.java b/hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/functional/TestBootstrapRead.java index d926a3be5a4e2..c2a4626dfc1b8 100644 --- a/hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/functional/TestBootstrapRead.java +++ b/hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/functional/TestBootstrapRead.java @@ -19,6 +19,7 @@ package org.apache.hudi.functional; import org.apache.hudi.common.model.HoodieTableType; +import org.apache.hudi.config.HoodieWriteConfig; import org.apache.spark.sql.Dataset; import org.apache.spark.sql.Row; @@ -28,6 +29,7 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import java.util.HashMap; import java.util.Map; import java.util.stream.Stream; @@ -68,13 +70,18 @@ public void testBootstrapFunctional(String bootstrapType, Boolean dashPartitions this.dashPartitions = dashPartitions; this.tableType = tableType; this.nPartitions = nPartitions; - setupDirs(); + Map overrideOpts = new HashMap<>(); + if (nPartitions > 1) { + overrideOpts.put(HoodieWriteConfig.ENABLE_COMPLEX_KEYGEN_VALIDATION.key(), "false"); + } + setupDirs(overrideOpts); // do bootstrap Map options = setBootstrapOptions(); Dataset bootstrapDf = sparkSession.emptyDataFrame(); bootstrapDf.write().format("hudi") .options(options) + .options(overrideOpts) .mode(SaveMode.Overwrite) .save(bootstrapTargetPath); compareTables(); @@ -82,6 +89,7 @@ public void testBootstrapFunctional(String bootstrapType, Boolean dashPartitions // do upserts options = basicOptions(); + options.putAll(overrideOpts); doUpdate(options, "001"); compareTables(); verifyMetaColOnlyRead(1); diff --git a/hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/functional/TestBootstrapReadBase.java b/hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/functional/TestBootstrapReadBase.java index d3246f7c4da83..d80c2a0e836ad 100644 --- a/hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/functional/TestBootstrapReadBase.java +++ b/hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/functional/TestBootstrapReadBase.java @@ -220,7 +220,7 @@ protected void compareDf(Dataset df1, Dataset df2) { assertEquals(0, df2.except(df1).count()); } - protected void setupDirs() { + protected void setupDirs(Map overrideOpts) { dataGen = new HoodieTestDataGenerator(dashPartitions ? dashPartitionPaths : slashPartitionPaths); Dataset inserts = generateTestInserts("000", nInserts); if (dashPartitions) { @@ -240,6 +240,7 @@ protected void setupDirs() { inserts.write().format("hudi") .options(basicOptions()) + .options(overrideOpts) .mode(SaveMode.Overwrite) .save(hudiBasePath); } diff --git a/hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/functional/TestNewHoodieParquetFileFormat.java b/hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/functional/TestNewHoodieParquetFileFormat.java index ec719414dc8b9..c546e8c980e6f 100644 --- a/hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/functional/TestNewHoodieParquetFileFormat.java +++ b/hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/functional/TestNewHoodieParquetFileFormat.java @@ -31,6 +31,7 @@ import org.junit.jupiter.params.provider.MethodSource; import java.util.Arrays; +import java.util.Collections; import java.util.Map; import java.util.stream.Stream; @@ -61,7 +62,7 @@ public void testNewParquetFileFormat(HoodieTableType tableType, Integer nPartiti this.dashPartitions = true; this.tableType = tableType; this.nPartitions = nPartitions; - setupDirs(); + setupDirs(Collections.emptyMap()); //do bootstrap Map options = setBootstrapOptions(); diff --git a/hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/keygen/TestComplexKeyGenerator.java b/hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/keygen/TestComplexKeyGenerator.java index 296cf3d6e0db1..d9d1e51059b7c 100644 --- a/hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/keygen/TestComplexKeyGenerator.java +++ b/hudi-spark-datasource/hudi-spark/src/test/java/org/apache/hudi/keygen/TestComplexKeyGenerator.java @@ -118,7 +118,7 @@ public void testSingleValueKeyGenerator() { String rowKey = record.get("_row_key").toString(); String partitionPath = record.get("timestamp").toString(); HoodieKey hoodieKey = compositeKeyGenerator.getKey(record); - assertEquals(rowKey, hoodieKey.getRecordKey()); + assertEquals("_row_key:" + rowKey, hoodieKey.getRecordKey()); assertEquals(partitionPath, hoodieKey.getPartitionPath()); Row row = KeyGeneratorTestUtilities.getRow(record, HoodieTestDataGenerator.AVRO_SCHEMA, diff --git a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/hudi/TestHoodieFileIndex.scala b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/hudi/TestHoodieFileIndex.scala index 803702addb489..1ba2b0e34e15f 100644 --- a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/hudi/TestHoodieFileIndex.scala +++ b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/hudi/TestHoodieFileIndex.scala @@ -122,6 +122,7 @@ class TestHoodieFileIndex extends HoodieSparkClientTestBase with ScalaAssertionS .option(TIMESTAMP_TYPE_FIELD.key, TimestampType.DATE_STRING.name()) .option(TIMESTAMP_INPUT_DATE_FORMAT.key, "yyyy/MM/dd") .option(TIMESTAMP_OUTPUT_DATE_FORMAT.key, "yyyy-MM-dd") + .option(HoodieWriteConfig.ENABLE_COMPLEX_KEYGEN_VALIDATION.key(), "false") .mode(SaveMode.Overwrite) if (isNullOrEmpty(keyGenerator)) { @@ -315,7 +316,8 @@ class TestHoodieFileIndex extends HoodieSparkClientTestBase with ScalaAssertionS RECORDKEY_FIELD.key -> "id", PRECOMBINE_FIELD.key -> "version", PARTITIONPATH_FIELD.key -> "dt,hh", - HoodieMetadataConfig.ENABLE.key -> useMetadataTable.toString + HoodieMetadataConfig.ENABLE.key -> useMetadataTable.toString, + HoodieWriteConfig.ENABLE_COMPLEX_KEYGEN_VALIDATION.key -> "false" ) val readerOpts: Map[String, String] = queryOpts ++ Map( @@ -458,7 +460,8 @@ class TestHoodieFileIndex extends HoodieSparkClientTestBase with ScalaAssertionS RECORDKEY_FIELD.key -> "id", PRECOMBINE_FIELD.key -> "version", PARTITIONPATH_FIELD.key -> partitionNames.mkString(","), - HoodieMetadataConfig.ENABLE.key -> useMetadataTable.toString + HoodieMetadataConfig.ENABLE.key -> useMetadataTable.toString, + HoodieWriteConfig.ENABLE_COMPLEX_KEYGEN_VALIDATION.key() -> "false" ) val readerOpts: Map[String, String] = queryOpts ++ Map( @@ -512,7 +515,8 @@ class TestHoodieFileIndex extends HoodieSparkClientTestBase with ScalaAssertionS DataSourceWriteOptions.OPERATION.key -> DataSourceWriteOptions.INSERT_OPERATION_OPT_VAL, RECORDKEY_FIELD.key -> "id", PRECOMBINE_FIELD.key -> "version", - PARTITIONPATH_FIELD.key -> "dt,hh" + PARTITIONPATH_FIELD.key -> "dt,hh", + HoodieWriteConfig.ENABLE_COMPLEX_KEYGEN_VALIDATION.key() -> "false" ) val readerOpts: Map[String, String] = queryOpts ++ Map( @@ -572,7 +576,8 @@ class TestHoodieFileIndex extends HoodieSparkClientTestBase with ScalaAssertionS HoodieMetadataConfig.ENABLE.key -> enableMetadataTable.toString, RECORDKEY_FIELD.key -> "id", PARTITIONPATH_FIELD.key -> "region_code,dt", - DataSourceWriteOptions.PRECOMBINE_FIELD.key -> "price" + DataSourceWriteOptions.PRECOMBINE_FIELD.key -> "price", + HoodieWriteConfig.ENABLE_COMPLEX_KEYGEN_VALIDATION.key() -> "false" ) val readerOpts: Map[String, String] = queryOpts ++ Map( diff --git a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/hudi/functional/TestGetPartitionValuesFromPath.scala b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/hudi/functional/TestGetPartitionValuesFromPath.scala index aadd9397f47d4..50d3db3a316f9 100644 --- a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/hudi/functional/TestGetPartitionValuesFromPath.scala +++ b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/hudi/functional/TestGetPartitionValuesFromPath.scala @@ -18,6 +18,7 @@ package org.apache.hudi.functional +import org.apache.hudi.config.HoodieWriteConfig import org.apache.spark.sql.hudi.HoodieSparkSqlTestBase class TestGetPartitionValuesFromPath extends HoodieSparkSqlTestBase { @@ -38,7 +39,8 @@ class TestGetPartitionValuesFromPath extends HoodieSparkSqlTestBase { |tblproperties ( | primaryKey = 'id', | type='mor', - | hoodie.datasource.write.hive_style_partitioning='$hiveStylePartitioning') + | hoodie.datasource.write.hive_style_partitioning='$hiveStylePartitioning', + | ${HoodieWriteConfig.ENABLE_COMPLEX_KEYGEN_VALIDATION.key()} = 'false') |partitioned by (region, dt)""".stripMargin) spark.sql(s"insert into $tableName partition (region='reg1', dt='2023-08-01') select 1, 'name1'") @@ -66,7 +68,8 @@ class TestGetPartitionValuesFromPath extends HoodieSparkSqlTestBase { | primaryKey = 'id', | type = 'mor', | preCombineField = 'ts', - | hoodie.datasource.write.drop.partition.columns = 'true' + | hoodie.datasource.write.drop.partition.columns = 'true', + | ${HoodieWriteConfig.ENABLE_COMPLEX_KEYGEN_VALIDATION.key()} = 'false' |) |partitioned by (region, dt)""".stripMargin) diff --git a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/hudi/functional/cdc/TestCDCDataFrameSuite.scala b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/hudi/functional/cdc/TestCDCDataFrameSuite.scala index 210ea00048ef4..1865985811949 100644 --- a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/hudi/functional/cdc/TestCDCDataFrameSuite.scala +++ b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/hudi/functional/cdc/TestCDCDataFrameSuite.scala @@ -784,6 +784,7 @@ class TestCDCDataFrameSuite extends HoodieCDCTestBase { .option(HoodieWriteConfig.TBL_NAME.key(), tableName + loggingMode.name()) .option("hoodie.datasource.write.operation", "upsert") .option("hoodie.datasource.write.keygenerator.class", "org.apache.hudi.keygen.ComplexKeyGenerator") + .option(HoodieWriteConfig.ENABLE_COMPLEX_KEYGEN_VALIDATION.key(), "false") .option("hoodie.datasource.write.payload.class", "org.apache.hudi.common.model.AWSDmsAvroPayload") .option("hoodie.table.cdc.enabled", "true") .option("hoodie.table.cdc.supplemental.logging.mode", loggingMode.name()) @@ -804,6 +805,7 @@ class TestCDCDataFrameSuite extends HoodieCDCTestBase { .option(HoodieWriteConfig.TBL_NAME.key(), tableName + loggingMode.name()) .option("hoodie.datasource.write.operation", "upsert") .option("hoodie.datasource.write.keygenerator.class", "org.apache.hudi.keygen.ComplexKeyGenerator") + .option(HoodieWriteConfig.ENABLE_COMPLEX_KEYGEN_VALIDATION.key(), "false") .option("hoodie.datasource.write.payload.class", "org.apache.hudi.common.model.AWSDmsAvroPayload") .option("hoodie.table.cdc.enabled", "true") .option("hoodie.table.cdc.supplemental.logging.mode", loggingMode.name()) diff --git a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestInsertTable.scala b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestInsertTable.scala index 9d14064f3987f..410390df4811b 100644 --- a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestInsertTable.scala +++ b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestInsertTable.scala @@ -193,7 +193,10 @@ class TestInsertTable extends HoodieSparkSqlTestBase { | ht string, | ts long |) using hudi - | tblproperties (primaryKey = 'id') + | tblproperties ( + | primaryKey = 'id', + | ${HoodieWriteConfig.ENABLE_COMPLEX_KEYGEN_VALIDATION.key()} = 'false' + |) | partitioned by (dt, ht) | location '${tmp.getCanonicalPath}' """.stripMargin) @@ -520,7 +523,8 @@ class TestInsertTable extends HoodieSparkSqlTestBase { |) using hudi | tblproperties ( | type = '$tableType', - | primaryKey = 'id' + | primaryKey = 'id', + | ${HoodieWriteConfig.ENABLE_COMPLEX_KEYGEN_VALIDATION.key()} = 'false' | ) | partitioned by (dt, hh) """.stripMargin @@ -818,7 +822,8 @@ class TestInsertTable extends HoodieSparkSqlTestBase { |) using hudi | tblproperties ( | type = '$tableType', - | primaryKey = 'id' + | primaryKey = 'id', + | ${HoodieWriteConfig.ENABLE_COMPLEX_KEYGEN_VALIDATION.key()} = 'false' | ) | partitioned by (dt, hh) | location '${tmp.getCanonicalPath}/$tableMultiPartition' @@ -1193,6 +1198,7 @@ class TestInsertTable extends HoodieSparkSqlTestBase { .option(HoodieWriteConfig.INSERT_PARALLELISM_VALUE.key, "1") .option(HoodieWriteConfig.UPSERT_PARALLELISM_VALUE.key, "1") .option(HoodieWriteConfig.ALLOW_OPERATION_METADATA_FIELD.key, "true") + .option(HoodieWriteConfig.ENABLE_COMPLEX_KEYGEN_VALIDATION.key(), "false") .mode(SaveMode.Overwrite) .save(tablePath) diff --git a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestLazyPartitionPathFetching.scala b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestLazyPartitionPathFetching.scala index e2635c0cba879..236110932d777 100644 --- a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestLazyPartitionPathFetching.scala +++ b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestLazyPartitionPathFetching.scala @@ -17,6 +17,8 @@ package org.apache.spark.sql.hudi +import org.apache.hudi.config.HoodieWriteConfig + class TestLazyPartitionPathFetching extends HoodieSparkSqlTestBase { test("Test querying with string column + partition pruning") { @@ -102,7 +104,8 @@ class TestLazyPartitionPathFetching extends HoodieSparkSqlTestBase { | tblproperties ( | primaryKey ='id', | type = 'cow', - | preCombineField = 'ts' + | preCombineField = 'ts', + | ${HoodieWriteConfig.ENABLE_COMPLEX_KEYGEN_VALIDATION.key()} = 'false' | ) | PARTITIONED BY (country, date_par) """.stripMargin) diff --git a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestPartitionPushDownWhenListingPaths.scala b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestPartitionPushDownWhenListingPaths.scala index 1b5e590913f3b..9b469bd487801 100644 --- a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestPartitionPushDownWhenListingPaths.scala +++ b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestPartitionPushDownWhenListingPaths.scala @@ -18,6 +18,7 @@ package org.apache.spark.sql.hudi import org.apache.hudi.common.config.HoodieMetadataConfig +import org.apache.hudi.config.HoodieWriteConfig class TestPartitionPushDownWhenListingPaths extends HoodieSparkSqlTestBase { @@ -45,7 +46,8 @@ class TestPartitionPushDownWhenListingPaths extends HoodieSparkSqlTestBase { | type = '$tableType', | preCombineField = 'ts', | hoodie.datasource.write.hive_style_partitioning = 'true', - | hoodie.datasource.write.partitionpath.urlencode = 'true' + | hoodie.datasource.write.partitionpath.urlencode = 'true', + | ${HoodieWriteConfig.ENABLE_COMPLEX_KEYGEN_VALIDATION.key()} = 'false' | ) | PARTITIONED BY (date_par, country, hour, longValue)""".stripMargin) spark.sql(s"insert into $tableName values(1, 'a1', 10, 1000, date '2023-02-27', 'ID', 1, 102345L)") diff --git a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestRepairTable.scala b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestRepairTable.scala index 8078ed29bd7e4..89449959b5f3f 100644 --- a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestRepairTable.scala +++ b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestRepairTable.scala @@ -21,6 +21,7 @@ package org.apache.spark.sql.hudi import org.apache.hudi.DataSourceWriteOptions.{PARTITIONPATH_FIELD, PRECOMBINE_FIELD, RECORDKEY_FIELD} import org.apache.hudi.HoodieSparkUtils import org.apache.hudi.common.table.HoodieTableConfig.HIVE_STYLE_PARTITIONING_ENABLE +import org.apache.hudi.config.HoodieWriteConfig import org.apache.hudi.config.HoodieWriteConfig.TBL_NAME import org.apache.spark.sql.SaveMode @@ -74,7 +75,8 @@ class TestRepairTable extends HoodieSparkSqlTestBase { | tblproperties ( | primaryKey = 'id', | preCombineField = 'ts', - | hoodie.datasource.write.hive_style_partitioning = '$hiveStylePartitionEnable' + | hoodie.datasource.write.hive_style_partitioning = '$hiveStylePartitionEnable', + | ${HoodieWriteConfig.ENABLE_COMPLEX_KEYGEN_VALIDATION.key()} = 'false' | ) """.stripMargin) val table = spark.sessionState.sqlParser.parseTableIdentifier(tableName) @@ -87,6 +89,7 @@ class TestRepairTable extends HoodieSparkSqlTestBase { .option(PRECOMBINE_FIELD.key, "ts") .option(PARTITIONPATH_FIELD.key, "dt,hh") .option(HIVE_STYLE_PARTITIONING_ENABLE.key, hiveStylePartitionEnable) + .option(HoodieWriteConfig.ENABLE_COMPLEX_KEYGEN_VALIDATION.key(), "false") .mode(SaveMode.Append) .save(basePath) @@ -112,6 +115,7 @@ class TestRepairTable extends HoodieSparkSqlTestBase { .option(RECORDKEY_FIELD.key, "id") .option(PRECOMBINE_FIELD.key, "ts") .option(PARTITIONPATH_FIELD.key, "dt,hh") + .option(HoodieWriteConfig.ENABLE_COMPLEX_KEYGEN_VALIDATION.key(), "false") .option(HIVE_STYLE_PARTITIONING_ENABLE.key, hiveStylePartitionEnable) .mode(SaveMode.Append) .save(basePath) diff --git a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestShowPartitions.scala b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestShowPartitions.scala index 968d7a168aa38..0d7f7a0918bd0 100644 --- a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestShowPartitions.scala +++ b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestShowPartitions.scala @@ -19,6 +19,8 @@ package org.apache.spark.sql.hudi import org.apache.hudi.HoodieSparkUtils.isSpark2 import org.apache.hudi.common.util.PartitionPathEncodeUtils.DEFAULT_PARTITION_PATH +import org.apache.hudi.config.HoodieWriteConfig +import org.apache.spark.sql.hudi.HoodieSparkSqlTestBase class TestShowPartitions extends HoodieSparkSqlTestBase { @@ -123,7 +125,8 @@ class TestShowPartitions extends HoodieSparkSqlTestBase { | partitioned by (year, month, day) | tblproperties ( | primaryKey = 'id', - | preCombineField = 'ts' + | preCombineField = 'ts', + | ${HoodieWriteConfig.ENABLE_COMPLEX_KEYGEN_VALIDATION.key()} = 'false' | ) """.stripMargin) // Empty partitions @@ -228,7 +231,8 @@ class TestShowPartitions extends HoodieSparkSqlTestBase { | partitioned by (year, month, day) | tblproperties ( | primaryKey = 'id', - | preCombineField = 'ts' + | preCombineField = 'ts', + | ${HoodieWriteConfig.ENABLE_COMPLEX_KEYGEN_VALIDATION.key()} = 'false' | ) """.stripMargin) diff --git a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestTruncateTable.scala b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestTruncateTable.scala index 808bfebb802c0..364dc0f9cf3bd 100644 --- a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestTruncateTable.scala +++ b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestTruncateTable.scala @@ -107,6 +107,8 @@ class TestTruncateTable extends HoodieSparkSqlTestBase { val df = Seq((1, "z3", "v1", "2021", "10", "01"), (2, "l4", "v1", "2021", "10","02")) .toDF("id", "name", "ts", "year", "month", "day") + // Need to set hoodie.write.complex.keygen.validation.enable to false since truncate command table fails + spark.sql(s"set ${HoodieWriteConfig.ENABLE_COMPLEX_KEYGEN_VALIDATION.key()}=false") df.write.format("hudi") .option(HoodieWriteConfig.TBL_NAME.key, tableName) .option(TABLE_TYPE.key, COW_TABLE_TYPE_OPT_VAL) @@ -114,6 +116,7 @@ class TestTruncateTable extends HoodieSparkSqlTestBase { .option(PRECOMBINE_FIELD.key, "ts") .option(PARTITIONPATH_FIELD.key, "year,month,day") .option(HIVE_STYLE_PARTITIONING.key, hiveStyle) + .option(HoodieWriteConfig.ENABLE_COMPLEX_KEYGEN_VALIDATION.key(), "false") .option(HoodieWriteConfig.INSERT_PARALLELISM_VALUE.key, "1") .option(HoodieWriteConfig.UPSERT_PARALLELISM_VALUE.key, "1") .mode(SaveMode.Overwrite) @@ -141,6 +144,8 @@ class TestTruncateTable extends HoodieSparkSqlTestBase { // Truncate table spark.sql(s"truncate table $tableName") checkAnswer(s"select count(1) from $tableName")(Seq(0)) + + spark.sql(s"set ${HoodieWriteConfig.ENABLE_COMPLEX_KEYGEN_VALIDATION.key()}=") } } } diff --git a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/procedure/TestFsViewProcedure.scala b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/procedure/TestFsViewProcedure.scala index 9de1f1b0ee855..94d94fefb47f7 100644 --- a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/procedure/TestFsViewProcedure.scala +++ b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/procedure/TestFsViewProcedure.scala @@ -17,6 +17,8 @@ package org.apache.spark.sql.hudi.procedure +import org.apache.hudi.config.HoodieWriteConfig + class TestFsViewProcedure extends HoodieSparkProcedureTestBase { test("Test Call show_fsview_all Procedure") { withTempDir { tmp => @@ -114,7 +116,8 @@ class TestFsViewProcedure extends HoodieSparkProcedureTestBase { | location '${tmp.getCanonicalPath}/$tableName' | tblproperties ( | primaryKey = 'id', - | preCombineField = 'ts' + | preCombineField = 'ts', + | ${HoodieWriteConfig.ENABLE_COMPLEX_KEYGEN_VALIDATION.key()} = 'false' | ) """.stripMargin) // insert data to table diff --git a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/procedure/TestStatsProcedure.scala b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/procedure/TestStatsProcedure.scala index c600d45e56828..8c57839e754ab 100644 --- a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/procedure/TestStatsProcedure.scala +++ b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/procedure/TestStatsProcedure.scala @@ -19,6 +19,8 @@ package org.apache.spark.sql.hudi.procedure +import org.apache.hudi.config.HoodieWriteConfig + class TestStatsProcedure extends HoodieSparkProcedureTestBase { test("Test Call stats_wa Procedure") { withTempDir { tmp => @@ -75,7 +77,8 @@ class TestStatsProcedure extends HoodieSparkProcedureTestBase { | location '$tablePath' | tblproperties ( | primaryKey = 'id', - | preCombineField = 'ts' + | preCombineField = 'ts', + | ${HoodieWriteConfig.ENABLE_COMPLEX_KEYGEN_VALIDATION.key()} = 'false' | ) """.stripMargin) // insert data to table From 6db1b5f90e6bbc02f7f02ef49737ac8ec15d67ab Mon Sep 17 00:00:00 2001 From: Lokesh Jain Date: Thu, 15 Jan 2026 22:44:55 +0530 Subject: [PATCH 06/35] Fix tests --- .../sql/hudi/TestAlterTableDropPartition.scala | 14 ++++++++++++++ .../org/apache/spark/sql/hudi/TestSpark3DDL.scala | 3 ++- .../apache/spark/sql/hudi/TestTruncateTable.scala | 2 +- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestAlterTableDropPartition.scala b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestAlterTableDropPartition.scala index 2c592f5a8159a..84cc67858fbab 100644 --- a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestAlterTableDropPartition.scala +++ b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestAlterTableDropPartition.scala @@ -281,6 +281,9 @@ class TestAlterTableDropPartition extends HoodieSparkSqlTestBase { val df = Seq((1, "z3", "v1", "2021", "10", "01"), (2, "l4", "v1", "2021", "10", "02")) .toDF("id", "name", "ts", "year", "month", "day") + // Need to set hoodie.write.complex.keygen.validation.enable to false since alter table command also writes to the table + spark.sql(s"set ${HoodieWriteConfig.ENABLE_COMPLEX_KEYGEN_VALIDATION.key()}=false") + df.write.format("hudi") .option(HoodieWriteConfig.TBL_NAME.key, tableName) .option(TABLE_TYPE.key, COW_TABLE_TYPE_OPT_VAL) @@ -288,6 +291,7 @@ class TestAlterTableDropPartition extends HoodieSparkSqlTestBase { .option(PRECOMBINE_FIELD.key, "ts") .option(PARTITIONPATH_FIELD.key, "year,month,day") .option(HIVE_STYLE_PARTITIONING.key, hiveStyle) + .option(HoodieWriteConfig.ENABLE_COMPLEX_KEYGEN_VALIDATION.key(), "false") .option(HoodieWriteConfig.INSERT_PARALLELISM_VALUE.key, "1") .option(HoodieWriteConfig.UPSERT_PARALLELISM_VALUE.key, "1") .mode(SaveMode.Overwrite) @@ -312,6 +316,7 @@ class TestAlterTableDropPartition extends HoodieSparkSqlTestBase { .option(PRECOMBINE_FIELD.key, "ts") .option(PARTITIONPATH_FIELD.key, "year,month,day") .option(HIVE_STYLE_PARTITIONING.key, hiveStyle) + .option(HoodieWriteConfig.ENABLE_COMPLEX_KEYGEN_VALIDATION.key(), "false") .option(KEYGENERATOR_CLASS_NAME.key, classOf[ComplexKeyGenerator].getName) .option(HoodieWriteConfig.INSERT_PARALLELISM_VALUE.key, "1") .option(HoodieWriteConfig.UPSERT_PARALLELISM_VALUE.key, "1") @@ -345,6 +350,8 @@ class TestAlterTableDropPartition extends HoodieSparkSqlTestBase { } else { checkAnswer(s"show partitions $tableName")(Seq("2021/10/02")) } + + spark.sql(s"set ${HoodieWriteConfig.ENABLE_COMPLEX_KEYGEN_VALIDATION.key()}=") } } } @@ -358,6 +365,9 @@ class TestAlterTableDropPartition extends HoodieSparkSqlTestBase { import spark.implicits._ val df = Seq((1, "z3", "v1", "2021", "10", "01")).toDF("id", "name", "ts", "year", "month", "day") + // Need to set hoodie.write.complex.keygen.validation.enable to false since alter table command also writes to the table + spark.sql(s"set ${HoodieWriteConfig.ENABLE_COMPLEX_KEYGEN_VALIDATION.key()}=false") + df.write.format("hudi") .option(HoodieWriteConfig.TBL_NAME.key, tableName) .option(TABLE_TYPE.key, COW_TABLE_TYPE_OPT_VAL) @@ -365,6 +375,7 @@ class TestAlterTableDropPartition extends HoodieSparkSqlTestBase { .option(PRECOMBINE_FIELD.key, "ts") .option(PARTITIONPATH_FIELD.key, "year,month,day") .option(HIVE_STYLE_PARTITIONING.key, hiveStyle) + .option(HoodieWriteConfig.ENABLE_COMPLEX_KEYGEN_VALIDATION.key(), "false") .option(HoodieWriteConfig.INSERT_PARALLELISM_VALUE.key, "1") .option(HoodieWriteConfig.UPSERT_PARALLELISM_VALUE.key, "1") .mode(SaveMode.Overwrite) @@ -389,6 +400,7 @@ class TestAlterTableDropPartition extends HoodieSparkSqlTestBase { .option(PRECOMBINE_FIELD.key, "ts") .option(PARTITIONPATH_FIELD.key, "year,month,day") .option(HIVE_STYLE_PARTITIONING.key, hiveStyle) + .option(HoodieWriteConfig.ENABLE_COMPLEX_KEYGEN_VALIDATION.key(), "false") .option(KEYGENERATOR_CLASS_NAME.key, classOf[ComplexKeyGenerator].getName) .option(HoodieWriteConfig.INSERT_PARALLELISM_VALUE.key, "1") .option(HoodieWriteConfig.UPSERT_PARALLELISM_VALUE.key, "1") @@ -429,6 +441,8 @@ class TestAlterTableDropPartition extends HoodieSparkSqlTestBase { } else { checkAnswer(s"show partitions $tableName")(Seq("2021/10/02")) } + + spark.sql(s"set ${HoodieWriteConfig.ENABLE_COMPLEX_KEYGEN_VALIDATION.key()}=") } } } diff --git a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestSpark3DDL.scala b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestSpark3DDL.scala index 6ca1a72edcdb2..6afc46a064266 100644 --- a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestSpark3DDL.scala +++ b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestSpark3DDL.scala @@ -1030,7 +1030,8 @@ class TestSpark3DDL extends HoodieSparkSqlTestBase { |tblproperties ( | primaryKey = 'id', | type = 'cow', - | preCombineField = 'ts' + | preCombineField = 'ts', + | ${HoodieWriteConfig.ENABLE_COMPLEX_KEYGEN_VALIDATION.key()} = 'false' |) |partitioned by (region, dt)""".stripMargin) diff --git a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestTruncateTable.scala b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestTruncateTable.scala index 364dc0f9cf3bd..7f980197bc61b 100644 --- a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestTruncateTable.scala +++ b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestTruncateTable.scala @@ -107,7 +107,7 @@ class TestTruncateTable extends HoodieSparkSqlTestBase { val df = Seq((1, "z3", "v1", "2021", "10", "01"), (2, "l4", "v1", "2021", "10","02")) .toDF("id", "name", "ts", "year", "month", "day") - // Need to set hoodie.write.complex.keygen.validation.enable to false since truncate command table fails + // Need to set hoodie.write.complex.keygen.validation.enable to false since truncate table command also writes to the table spark.sql(s"set ${HoodieWriteConfig.ENABLE_COMPLEX_KEYGEN_VALIDATION.key()}=false") df.write.format("hudi") .option(HoodieWriteConfig.TBL_NAME.key, tableName) From 1a5674921171c698d43a7c9b5ad4829e9361a5bc Mon Sep 17 00:00:00 2001 From: Lokesh Jain Date: Fri, 16 Jan 2026 11:49:03 +0530 Subject: [PATCH 07/35] Fix compilation with 2.4 --- .../test/java/org/apache/hudi/keygen/TestKeyGenUtils.java | 8 ++++---- .../scala/org/apache/spark/sql/hudi/TestCreateTable.scala | 6 ++++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/keygen/TestKeyGenUtils.java b/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/keygen/TestKeyGenUtils.java index 9c27f0eb2ebaf..830c3b27b0a48 100644 --- a/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/keygen/TestKeyGenUtils.java +++ b/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/keygen/TestKeyGenUtils.java @@ -104,10 +104,10 @@ void testGetRecordKey() { Schema nullableStringSchema = Schema.createUnion(Schema.create(Schema.Type.NULL), Schema.create(Schema.Type.STRING)); Schema schema = Schema.createRecord("TestRecord", "doc", "test", false, Arrays.asList( - new Schema.Field("key1", nullableStringSchema), - new Schema.Field("key2", nullableStringSchema), - new Schema.Field("key3", nullableStringSchema), - new Schema.Field("key4", nullableStringSchema) + new Schema.Field("key1", nullableStringSchema, "", null), + new Schema.Field("key2", nullableStringSchema, "", null), + new Schema.Field("key3", nullableStringSchema, "", null), + new Schema.Field("key4", nullableStringSchema, "", null) )); GenericRecord avroRecord = new GenericData.Record(schema); avroRecord.put("key1", "value1"); diff --git a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestCreateTable.scala b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestCreateTable.scala index d28a8a205e67b..893174997b500 100644 --- a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestCreateTable.scala +++ b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestCreateTable.scala @@ -26,13 +26,13 @@ import org.apache.hudi.hadoop.realtime.HoodieParquetRealtimeInputFormat import org.apache.hudi.keygen.SimpleKeyGenerator import org.apache.hudi.testutils.Assertions import org.apache.hudi.testutils.HoodieClientTestUtils.createMetaClient - import org.apache.spark.sql.{SaveMode, SparkSession} import org.apache.spark.sql.catalyst.TableIdentifier import org.apache.spark.sql.catalyst.catalog.{CatalogTableType, HoodieCatalogTable} import org.apache.spark.sql.hudi.HoodieSparkSqlTestBase.getLastCommitMetadata import org.apache.spark.sql.types._ import org.junit.jupiter.api.Assertions.{assertFalse, assertTrue} +import org.junit.jupiter.api.function.Executable import scala.collection.JavaConverters._ @@ -1604,7 +1604,9 @@ class TestCreateTable extends HoodieSparkSqlTestBase { expectedRowsBefore: Seq[Any]*)(expectedRowsAfter: Seq[Any]*): Unit = { if (tableVersion < 9) { // By default, the complex key generator validation is enabled and should throw exception on DML - Assertions.assertComplexKeyGeneratorValidationThrows(() => spark.sql(dmlToWrite), "ingestion") + Assertions.assertComplexKeyGeneratorValidationThrows(new Executable { + override def execute(): Unit = () => spark.sql(dmlToWrite) + }, "ingestion") // Query should still succeed checkAnswer(query)(expectedRowsBefore: _*) // Disabling the complex key generator validation should let write succeed From 61c237550d40e9811bd30268e13532994f67e63e Mon Sep 17 00:00:00 2001 From: Lokesh Jain Date: Fri, 16 Jan 2026 16:08:52 +0530 Subject: [PATCH 08/35] Fix test failure --- .../spark/sql/hudi/TestCreateTable.scala | 49 +++++++++---------- 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestCreateTable.scala b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestCreateTable.scala index 893174997b500..207fe8dcf4e2a 100644 --- a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestCreateTable.scala +++ b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestCreateTable.scala @@ -961,14 +961,12 @@ class TestCreateTable extends HoodieSparkSqlTestBase { test("Test Create Table with Complex Key Generator and Key Encoding") { withTempDir { tmp => - Seq((false, 6), (true, 6)).foreach { params => + Seq(false, true).foreach { encodeSingleKeyFieldValue => val tableName = generateTableName val tablePath = s"${tmp.getCanonicalPath}/$tableName" - val encodeSingleKeyFieldValue = params._1 - val tableVersion = params._2 import spark.implicits._ // The COMPLEX_KEYGEN_NEW_ENCODING config only works for table version 8 and below - val keyPrefix = if (encodeSingleKeyFieldValue && tableVersion < 9) "" else "id:" + val keyPrefix = if (encodeSingleKeyFieldValue) "" else "id:" val df = Seq((1, "a1", 10, 1000, "2025-07-29", 12)).toDF("id", "name", "value", "ts", "day", "hh") // Write a table by spark dataframe. df.write.format("hudi") @@ -983,7 +981,7 @@ class TestCreateTable extends HoodieSparkSqlTestBase { .option( HoodieWriteConfig.COMPLEX_KEYGEN_NEW_ENCODING.key, encodeSingleKeyFieldValue.toString) - .option(HoodieWriteConfig.ENABLE_COMPLEX_KEYGEN_VALIDATION.key, (tableVersion >= 9).toString) + .option(HoodieWriteConfig.ENABLE_COMPLEX_KEYGEN_VALIDATION.key, "false") .mode(SaveMode.Overwrite) .save(tablePath) @@ -999,8 +997,8 @@ class TestCreateTable extends HoodieSparkSqlTestBase { spark.sql( s""" |ALTER TABLE $tableName - |SET TBLPROPERTIES (hoodie.write.complex.keygen.new.encoding = '$encodeSingleKeyFieldValue', - | hoodie.write.table.version = '$tableVersion') + |SET TBLPROPERTIES (hoodie.write.complex.keygen.new.encoding = '$encodeSingleKeyFieldValue' + |) |""".stripMargin) // Check the missing properties for spark sql val metaClient = createMetaClient(spark, tablePath) @@ -1013,7 +1011,7 @@ class TestCreateTable extends HoodieSparkSqlTestBase { // Test insert into writeAndValidateWithComplexKeyGenerator( - spark, tableVersion, tableName, + spark, tableName, s"insert into $tableName values(2, 'a2', 10, 1000, '2025-07-29', 12)", query )( Seq(keyPrefix + "1", "2025-07-29/12", 1, "a1", 10, 1000, "2025-07-29", 12) @@ -1023,8 +1021,7 @@ class TestCreateTable extends HoodieSparkSqlTestBase { ) // Test merge into - writeAndValidateWithComplexKeyGenerator( - spark, tableVersion, tableName, + writeAndValidateWithComplexKeyGenerator(spark, tableName, s""" |merge into $tableName h0 |using (select 1 as id, 'a1' as name, 11 as value, 1001 as ts, '2025-07-29' as day, 12 as hh) s0 @@ -1042,7 +1039,7 @@ class TestCreateTable extends HoodieSparkSqlTestBase { // Test update writeAndValidateWithComplexKeyGenerator( - spark, tableVersion, tableName, + spark, tableName, s"update $tableName set value = value + 1 where id = 2", query )( Seq(keyPrefix + "1", "2025-07-29/12", 1, "a1", 11, 1001, "2025-07-29", 12), @@ -1054,7 +1051,7 @@ class TestCreateTable extends HoodieSparkSqlTestBase { // Test delete writeAndValidateWithComplexKeyGenerator( - spark, tableVersion, tableName, + spark, tableName, s"delete from $tableName where id = 1", query )( Seq(keyPrefix + "1", "2025-07-29/12", 1, "a1", 11, 1001, "2025-07-29", 12), @@ -1597,23 +1594,23 @@ class TestCreateTable extends HoodieSparkSqlTestBase { } def writeAndValidateWithComplexKeyGenerator(spark: SparkSession, - tableVersion: Int, tableName: String, dmlToWrite: String, - query: String)( - expectedRowsBefore: Seq[Any]*)(expectedRowsAfter: Seq[Any]*): Unit = { - if (tableVersion < 9) { - // By default, the complex key generator validation is enabled and should throw exception on DML - Assertions.assertComplexKeyGeneratorValidationThrows(new Executable { - override def execute(): Unit = () => spark.sql(dmlToWrite) - }, "ingestion") - // Query should still succeed - checkAnswer(query)(expectedRowsBefore: _*) - // Disabling the complex key generator validation should let write succeed - HoodieSparkSqlTestBase.disableComplexKeygenValidation(spark, tableName) - } + query: String) + (expectedRowsBefore: Seq[Any]*) + (expectedRowsAfter: Seq[Any]*): Unit = { + // By default, the complex key generator validation is enabled and should throw exception on DML + Assertions.assertComplexKeyGeneratorValidationThrows(new Executable() { + override def execute(): Unit = { + spark.sql(dmlToWrite) + } + }, "ingestion") + // Query should still succeed + checkAnswer(query)(expectedRowsBefore: _*) + // Disabling the complex key generator validation should let write succeed + spark.sql(s"set ${HoodieWriteConfig.ENABLE_COMPLEX_KEYGEN_VALIDATION.key()}=false") spark.sql(dmlToWrite) - HoodieSparkSqlTestBase.enableComplexKeygenValidation(spark, tableName) + spark.sql(s"set ${HoodieWriteConfig.ENABLE_COMPLEX_KEYGEN_VALIDATION.key()}=true") checkAnswer(query)(expectedRowsAfter: _*) } } From eaad6db8f7383334431f53b6b54b81e32fe47b9b Mon Sep 17 00:00:00 2001 From: Lokesh Jain Date: Fri, 16 Jan 2026 18:55:01 +0530 Subject: [PATCH 09/35] Fix test failure --- .../org/apache/spark/sql/hudi/TestCreateTable.scala | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestCreateTable.scala b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestCreateTable.scala index 207fe8dcf4e2a..00920c81beebd 100644 --- a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestCreateTable.scala +++ b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/spark/sql/hudi/TestCreateTable.scala @@ -994,12 +994,9 @@ class TestCreateTable extends HoodieSparkSqlTestBase { checkAnswer(s"select _hoodie_record_key, id, name, value, ts, day, hh from $tableName")( Seq(keyPrefix + "1", 1, "a1", 10, 1000, "2025-07-29", 12) ) - spark.sql( - s""" - |ALTER TABLE $tableName - |SET TBLPROPERTIES (hoodie.write.complex.keygen.new.encoding = '$encodeSingleKeyFieldValue' - |) - |""".stripMargin) + + spark.sql(s"set hoodie.write.complex.keygen.new.encoding = $encodeSingleKeyFieldValue") + // Check the missing properties for spark sql val metaClient = createMetaClient(spark, tablePath) val properties = metaClient.getTableConfig.getProps.asScala.toMap @@ -1059,6 +1056,8 @@ class TestCreateTable extends HoodieSparkSqlTestBase { )( Seq(keyPrefix + "2", "2025-07-29/12", 2, "a2", 11, 1000, "2025-07-29", 12) ) + + spark.sql(s"set hoodie.write.complex.keygen.new.encoding=") } } } From bee399db861cdcfbafdbd4726d9eb9511454be0a Mon Sep 17 00:00:00 2001 From: Lokesh Jain Date: Mon, 19 Jan 2026 14:24:49 +0530 Subject: [PATCH 10/35] Fix test failure --- .../src/main/java/org/apache/hudi/keygen/KeyGenUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/keygen/KeyGenUtils.java b/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/keygen/KeyGenUtils.java index d2c94580c56c5..f448713f2e14f 100644 --- a/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/keygen/KeyGenUtils.java +++ b/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/keygen/KeyGenUtils.java @@ -161,11 +161,11 @@ public static String getRecordKey(GenericRecord record, List recordKeyFi keyIsNullEmpty = false; } } - recordKey.deleteCharAt(recordKey.length() - 1); if (keyIsNullEmpty) { throw new HoodieKeyException("recordKey values: \"" + recordKey + "\" for fields: " + recordKeyFields.toString() + " cannot be entirely null or empty."); } + recordKey.deleteCharAt(recordKey.length() - 1); return recordKey.toString(); } From 5c650091a3d209e82abf43841fe432ef2effac50 Mon Sep 17 00:00:00 2001 From: Lokesh Jain Date: Mon, 19 Jan 2026 16:34:23 +0530 Subject: [PATCH 11/35] Fix tests --- .../org/apache/hudi/client/TestBaseHoodieWriteClient.java | 2 +- .../java/org/apache/hudi/keygen/TestComplexKeyGenerator.java | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/client/TestBaseHoodieWriteClient.java b/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/client/TestBaseHoodieWriteClient.java index 2824d28de146a..afa7df3f89fb3 100644 --- a/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/client/TestBaseHoodieWriteClient.java +++ b/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/client/TestBaseHoodieWriteClient.java @@ -78,7 +78,7 @@ private static Stream testWithComplexKeyGeneratorValidation() { Arguments.of(true, false) ); - List tableVersionOptions = Arrays.asList(8); + List tableVersionOptions = Arrays.asList(6); arguments.addAll(Stream.of("org.apache.hudi.keygen.ComplexAvroKeyGenerator", "org.apache.hudi.keygen.ComplexKeyGenerator") diff --git a/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/keygen/TestComplexKeyGenerator.java b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/keygen/TestComplexKeyGenerator.java index 1a04e8835cb8b..aafda380cbd0e 100644 --- a/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/keygen/TestComplexKeyGenerator.java +++ b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/keygen/TestComplexKeyGenerator.java @@ -145,10 +145,9 @@ void testSingleValueKeyGenerator(boolean setNewEncodingConfig, } @ParameterizedTest - @CsvSource(value = {"false,true,8", "true,false,8", "true,true,8", "false,true,9", "true,false,9", "true,true,9"}) + @CsvSource(value = {"false,true", "true,false", "true,true"}) void testMultipleValueKeyGenerator(boolean setNewEncodingConfig, - boolean encodeSingleKeyFieldValueOnly, - String tableVersion) { + boolean encodeSingleKeyFieldValueOnly) { TypedProperties properties = new TypedProperties(); properties.setProperty(KeyGeneratorOptions.RECORDKEY_FIELD_NAME.key(), "_row_key,timestamp"); properties.setProperty(KeyGeneratorOptions.PARTITIONPATH_FIELD_NAME.key(), "rider,driver"); From 7f1ccab920e2a2d8a0075a43ead52722e0bdb02d Mon Sep 17 00:00:00 2001 From: Lokesh Jain Date: Tue, 20 Jan 2026 12:24:57 +0530 Subject: [PATCH 12/35] Fix tests --- .../src/main/java/org/apache/hudi/keygen/KeyGenUtils.java | 2 +- .../java/org/apache/hudi/keygen/TestComplexKeyGenerator.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/keygen/KeyGenUtils.java b/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/keygen/KeyGenUtils.java index f448713f2e14f..d2c94580c56c5 100644 --- a/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/keygen/KeyGenUtils.java +++ b/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/keygen/KeyGenUtils.java @@ -161,11 +161,11 @@ public static String getRecordKey(GenericRecord record, List recordKeyFi keyIsNullEmpty = false; } } + recordKey.deleteCharAt(recordKey.length() - 1); if (keyIsNullEmpty) { throw new HoodieKeyException("recordKey values: \"" + recordKey + "\" for fields: " + recordKeyFields.toString() + " cannot be entirely null or empty."); } - recordKey.deleteCharAt(recordKey.length() - 1); return recordKey.toString(); } diff --git a/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/keygen/TestComplexKeyGenerator.java b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/keygen/TestComplexKeyGenerator.java index aafda380cbd0e..5ea3c28def6f9 100644 --- a/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/keygen/TestComplexKeyGenerator.java +++ b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/keygen/TestComplexKeyGenerator.java @@ -81,7 +81,7 @@ void testNullPartitionPathFields() { @Test void testNullRecordKeyFields() { GenericRecord avroRecord = getRecord(); - assertThrows(HoodieKeyException.class, () -> { + assertThrows(StringIndexOutOfBoundsException.class, () -> { ComplexKeyGenerator keyGenerator = new ComplexKeyGenerator(getPropertiesWithoutRecordKeyProp()); keyGenerator.getRecordKey(avroRecord); }); From b5eef29b81242922f9b9f9ddd85980ea90233628 Mon Sep 17 00:00:00 2001 From: Lokesh Jain Date: Thu, 5 Feb 2026 17:39:13 +0530 Subject: [PATCH 13/35] Address review comments --- .../java/org/apache/hudi/keygen/KeyGenUtils.java | 12 ++++++------ .../org/apache/hudi/keygen/BuiltinKeyGenerator.java | 8 ++++---- .../org/apache/hudi/sink/bulk/RowDataKeyGen.java | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/keygen/KeyGenUtils.java b/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/keygen/KeyGenUtils.java index d2c94580c56c5..46d719103a299 100644 --- a/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/keygen/KeyGenUtils.java +++ b/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/keygen/KeyGenUtils.java @@ -51,7 +51,7 @@ public class KeyGenUtils { protected static final String HUDI_DEFAULT_PARTITION_PATH = PartitionPathEncodeUtils.DEFAULT_PARTITION_PATH; public static final String DEFAULT_PARTITION_PATH_SEPARATOR = "/"; public static final String DEFAULT_RECORD_KEY_PARTS_SEPARATOR = ","; - public static final String DEFAULT_COMPOSITE_KEY_FILED_VALUE = ":"; + public static final String DEFAULT_COMPOSITE_KEY_FIELD_VALUE = ":"; public static final String RECORD_KEY_GEN_PARTITION_ID_CONFIG = "_hoodie.record.key.gen.partition.id"; public static final String RECORD_KEY_GEN_INSTANT_TIME_CONFIG = "_hoodie.record.key.gen.instant.time"; @@ -132,7 +132,7 @@ public static String[] extractRecordKeys(String recordKey) { public static String[] extractRecordKeysByFields(String recordKey, List fields) { String[] fieldKV = recordKey.split(DEFAULT_RECORD_KEY_PARTS_SEPARATOR); - return Arrays.stream(fieldKV).map(kv -> kv.split(DEFAULT_COMPOSITE_KEY_FILED_VALUE, 2)) + return Arrays.stream(fieldKV).map(kv -> kv.split(DEFAULT_COMPOSITE_KEY_FIELD_VALUE, 2)) .filter(kvArray -> kvArray.length == 1 || fields.isEmpty() || (fields.contains(kvArray[0]))) .map(kvArray -> { if (kvArray.length == 1) { @@ -153,11 +153,11 @@ public static String getRecordKey(GenericRecord record, List recordKeyFi for (String recordKeyField : recordKeyFields) { String recordKeyValue = HoodieAvroUtils.getNestedFieldValAsString(record, recordKeyField, true, consistentLogicalTimestampEnabled); if (recordKeyValue == null) { - recordKey.append(recordKeyField + DEFAULT_COMPOSITE_KEY_FILED_VALUE + NULL_RECORDKEY_PLACEHOLDER + DEFAULT_RECORD_KEY_PARTS_SEPARATOR); + recordKey.append(recordKeyField + DEFAULT_COMPOSITE_KEY_FIELD_VALUE + NULL_RECORDKEY_PLACEHOLDER + DEFAULT_RECORD_KEY_PARTS_SEPARATOR); } else if (recordKeyValue.isEmpty()) { - recordKey.append(recordKeyField + DEFAULT_COMPOSITE_KEY_FILED_VALUE + EMPTY_RECORDKEY_PLACEHOLDER + DEFAULT_RECORD_KEY_PARTS_SEPARATOR); + recordKey.append(recordKeyField + DEFAULT_COMPOSITE_KEY_FIELD_VALUE + EMPTY_RECORDKEY_PLACEHOLDER + DEFAULT_RECORD_KEY_PARTS_SEPARATOR); } else { - recordKey.append(recordKeyField + DEFAULT_COMPOSITE_KEY_FILED_VALUE + recordKeyValue + DEFAULT_RECORD_KEY_PARTS_SEPARATOR); + recordKey.append(recordKeyField + DEFAULT_COMPOSITE_KEY_FIELD_VALUE + recordKeyValue + DEFAULT_RECORD_KEY_PARTS_SEPARATOR); keyIsNullEmpty = false; } } @@ -278,7 +278,7 @@ public static String getComplexKeygenErrorMessage(String operation) { + "change in the key encoding in the _hoodie_record_key meta field (HUDI-7001) which " + "is crucial for upserts. Please take action based on the details on the deployment " + "guide (https://hudi.apache.org/docs/deployment#complex-key-generator) " - + "before resuming the " + operation + " to the this table. If you're certain " + + "before resuming the " + operation + " to the table. If you're certain " + "that the table is not affected by the key encoding change, set " + "`hoodie.write.complex.keygen.validation.enable=false` to skip this validation."; } diff --git a/hudi-client/hudi-spark-client/src/main/java/org/apache/hudi/keygen/BuiltinKeyGenerator.java b/hudi-client/hudi-spark-client/src/main/java/org/apache/hudi/keygen/BuiltinKeyGenerator.java index d022817404cfd..e5fca0e0162a5 100644 --- a/hudi-client/hudi-spark-client/src/main/java/org/apache/hudi/keygen/BuiltinKeyGenerator.java +++ b/hudi-client/hudi-spark-client/src/main/java/org/apache/hudi/keygen/BuiltinKeyGenerator.java @@ -51,7 +51,7 @@ import scala.Function1; import static org.apache.hudi.common.util.CollectionUtils.tail; -import static org.apache.hudi.keygen.KeyGenUtils.DEFAULT_COMPOSITE_KEY_FILED_VALUE; +import static org.apache.hudi.keygen.KeyGenUtils.DEFAULT_COMPOSITE_KEY_FIELD_VALUE; import static org.apache.hudi.keygen.KeyGenUtils.DEFAULT_RECORD_KEY_PARTS_SEPARATOR; import static org.apache.hudi.keygen.KeyGenUtils.EMPTY_RECORDKEY_PLACEHOLDER; import static org.apache.hudi.keygen.KeyGenUtils.NULL_RECORDKEY_PLACEHOLDER; @@ -222,7 +222,7 @@ private S combineRecordKeyInternal( PartitionPathFormatterBase.StringBuilder sb = builderFactory.get(); for (int i = 0; i < recordKeyParts.size(); ++i) { - sb.appendJava(fieldNames.get(i)).appendJava(DEFAULT_COMPOSITE_KEY_FILED_VALUE); + sb.appendJava(fieldNames.get(i)).appendJava(DEFAULT_COMPOSITE_KEY_FIELD_VALUE); // NOTE: If record-key part has already been a string [[toString]] will be a no-op sb.append(emptyKeyPartHandler.apply(converter.apply(recordKeyParts.get(i)))); @@ -254,7 +254,7 @@ private S combineCompositeRecordKeyInternal( if (encodeSingleKeyFieldName) { sb.appendJava(recordKeyFields.get(0)); - sb.appendJava(DEFAULT_COMPOSITE_KEY_FILED_VALUE); + sb.appendJava(DEFAULT_COMPOSITE_KEY_FIELD_VALUE); } sb.append(convertedKeyPart); // This check is to validate that overall composite-key has at least one non-null, non-empty @@ -271,7 +271,7 @@ private S combineCompositeRecordKeyInternal( S convertedKeyPart = emptyKeyPartHandler.apply(converter.apply(recordKeyParts[i])); sb.appendJava(recordKeyFields.get(i)); - sb.appendJava(DEFAULT_COMPOSITE_KEY_FILED_VALUE); + sb.appendJava(DEFAULT_COMPOSITE_KEY_FIELD_VALUE); sb.append(convertedKeyPart); // This check is to validate that overall composite-key has at least one non-null, non-empty // segment diff --git a/hudi-flink-datasource/hudi-flink/src/main/java/org/apache/hudi/sink/bulk/RowDataKeyGen.java b/hudi-flink-datasource/hudi-flink/src/main/java/org/apache/hudi/sink/bulk/RowDataKeyGen.java index 184c986688a34..cb82332e1fa41 100644 --- a/hudi-flink-datasource/hudi-flink/src/main/java/org/apache/hudi/sink/bulk/RowDataKeyGen.java +++ b/hudi-flink-datasource/hudi-flink/src/main/java/org/apache/hudi/sink/bulk/RowDataKeyGen.java @@ -42,7 +42,7 @@ import static org.apache.hudi.common.util.PartitionPathEncodeUtils.DEFAULT_PARTITION_PATH; import static org.apache.hudi.common.util.PartitionPathEncodeUtils.escapePathName; -import static org.apache.hudi.keygen.KeyGenUtils.DEFAULT_COMPOSITE_KEY_FILED_VALUE; +import static org.apache.hudi.keygen.KeyGenUtils.DEFAULT_COMPOSITE_KEY_FIELD_VALUE; /** * Key generator for {@link RowData}. @@ -134,7 +134,7 @@ protected RowDataKeyGen( // single record key with multiple partition fields this.simpleRecordKeyFunc = rowData -> { String oriKey = getRecordKey(recordKeyFieldGetter.getFieldOrNull(rowData), this.recordKeyFields[0], consistentLogicalTimestampEnabled); - return new StringBuilder(this.recordKeyFields[0]).append(DEFAULT_COMPOSITE_KEY_FILED_VALUE).append(oriKey).toString(); + return new StringBuilder(this.recordKeyFields[0]).append(DEFAULT_COMPOSITE_KEY_FIELD_VALUE).append(oriKey).toString(); }; } else { this.simpleRecordKeyFunc = rowData -> getRecordKey(recordKeyFieldGetter.getFieldOrNull(rowData), this.recordKeyFields[0], consistentLogicalTimestampEnabled); From 14f38d91c535a544ee7d07ca746a99f38eedc397 Mon Sep 17 00:00:00 2001 From: Pavithran Ravichandiran Date: Tue, 10 Feb 2026 17:37:08 -0800 Subject: [PATCH 14/35] Change DEFAULT_COMPOSITE_KEY_FIELD_VALUE to DEFAULT_COLUMN_VALUE_SEPARATOR --- .../java/org/apache/hudi/config/HoodieWriteConfig.java | 8 ++++---- .../main/java/org/apache/hudi/keygen/KeyGenUtils.java | 10 +++++----- .../org/apache/hudi/keygen/BuiltinKeyGenerator.java | 8 ++++---- .../java/org/apache/hudi/sink/bulk/RowDataKeyGen.java | 4 ++-- 4 files changed, 15 insertions(+), 15 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 3f413db93a40d..ad3d4cee2c22b 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 @@ -195,8 +195,8 @@ public class HoodieWriteConfig extends HoodieConfig { .key("hoodie.write.complex.keygen.new.encoding") .defaultValue(false) .markAdvanced() - .sinceVersion("1.1.0") - .supportedVersions("0.14.2", "0.15.1", "1.0.3") + .sinceVersion("0.14.2") + .supportedVersions("0.14.2", "0.15.1", "1.0.3", "1.1.0") .withDocumentation("This config only takes effect for writing table version 8 and below. " + "If set to false, the record key field name is encoded and prepended " + "in the case where a single record key field is used in the complex key generator, " @@ -211,8 +211,8 @@ public class HoodieWriteConfig extends HoodieConfig { .key("hoodie.write.complex.keygen.validation.enable") .defaultValue(true) .markAdvanced() - .sinceVersion("1.1.0") - .supportedVersions("0.14.2", "0.15.1", "1.0.3") + .sinceVersion("0.14.2") + .supportedVersions("0.14.2", "0.15.1", "1.0.3", "1.1.0") .withDocumentation("This config only takes effect for writing table version 8 and below, " + "upgrade or downgrade. If set to true, the writer enables the validation on whether the " + "table uses the complex key generator with a single record key field, which can be affected " diff --git a/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/keygen/KeyGenUtils.java b/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/keygen/KeyGenUtils.java index 46d719103a299..52f955e2a32ee 100644 --- a/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/keygen/KeyGenUtils.java +++ b/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/keygen/KeyGenUtils.java @@ -51,7 +51,7 @@ public class KeyGenUtils { protected static final String HUDI_DEFAULT_PARTITION_PATH = PartitionPathEncodeUtils.DEFAULT_PARTITION_PATH; public static final String DEFAULT_PARTITION_PATH_SEPARATOR = "/"; public static final String DEFAULT_RECORD_KEY_PARTS_SEPARATOR = ","; - public static final String DEFAULT_COMPOSITE_KEY_FIELD_VALUE = ":"; + public static final String DEFAULT_COLUMN_VALUE_SEPARATOR = ":"; public static final String RECORD_KEY_GEN_PARTITION_ID_CONFIG = "_hoodie.record.key.gen.partition.id"; public static final String RECORD_KEY_GEN_INSTANT_TIME_CONFIG = "_hoodie.record.key.gen.instant.time"; @@ -132,7 +132,7 @@ public static String[] extractRecordKeys(String recordKey) { public static String[] extractRecordKeysByFields(String recordKey, List fields) { String[] fieldKV = recordKey.split(DEFAULT_RECORD_KEY_PARTS_SEPARATOR); - return Arrays.stream(fieldKV).map(kv -> kv.split(DEFAULT_COMPOSITE_KEY_FIELD_VALUE, 2)) + return Arrays.stream(fieldKV).map(kv -> kv.split(DEFAULT_COLUMN_VALUE_SEPARATOR, 2)) .filter(kvArray -> kvArray.length == 1 || fields.isEmpty() || (fields.contains(kvArray[0]))) .map(kvArray -> { if (kvArray.length == 1) { @@ -153,11 +153,11 @@ public static String getRecordKey(GenericRecord record, List recordKeyFi for (String recordKeyField : recordKeyFields) { String recordKeyValue = HoodieAvroUtils.getNestedFieldValAsString(record, recordKeyField, true, consistentLogicalTimestampEnabled); if (recordKeyValue == null) { - recordKey.append(recordKeyField + DEFAULT_COMPOSITE_KEY_FIELD_VALUE + NULL_RECORDKEY_PLACEHOLDER + DEFAULT_RECORD_KEY_PARTS_SEPARATOR); + recordKey.append(recordKeyField).append(DEFAULT_COLUMN_VALUE_SEPARATOR).append(NULL_RECORDKEY_PLACEHOLDER); } else if (recordKeyValue.isEmpty()) { - recordKey.append(recordKeyField + DEFAULT_COMPOSITE_KEY_FIELD_VALUE + EMPTY_RECORDKEY_PLACEHOLDER + DEFAULT_RECORD_KEY_PARTS_SEPARATOR); + recordKey.append(recordKeyField).append(DEFAULT_COLUMN_VALUE_SEPARATOR).append(EMPTY_RECORDKEY_PLACEHOLDER); } else { - recordKey.append(recordKeyField + DEFAULT_COMPOSITE_KEY_FIELD_VALUE + recordKeyValue + DEFAULT_RECORD_KEY_PARTS_SEPARATOR); + recordKey.append(recordKeyField).append(DEFAULT_COLUMN_VALUE_SEPARATOR).append(recordKeyValue); keyIsNullEmpty = false; } } diff --git a/hudi-client/hudi-spark-client/src/main/java/org/apache/hudi/keygen/BuiltinKeyGenerator.java b/hudi-client/hudi-spark-client/src/main/java/org/apache/hudi/keygen/BuiltinKeyGenerator.java index e5fca0e0162a5..3fdb8ef065e8d 100644 --- a/hudi-client/hudi-spark-client/src/main/java/org/apache/hudi/keygen/BuiltinKeyGenerator.java +++ b/hudi-client/hudi-spark-client/src/main/java/org/apache/hudi/keygen/BuiltinKeyGenerator.java @@ -51,7 +51,7 @@ import scala.Function1; import static org.apache.hudi.common.util.CollectionUtils.tail; -import static org.apache.hudi.keygen.KeyGenUtils.DEFAULT_COMPOSITE_KEY_FIELD_VALUE; +import static org.apache.hudi.keygen.KeyGenUtils.DEFAULT_COLUMN_VALUE_SEPARATOR; import static org.apache.hudi.keygen.KeyGenUtils.DEFAULT_RECORD_KEY_PARTS_SEPARATOR; import static org.apache.hudi.keygen.KeyGenUtils.EMPTY_RECORDKEY_PLACEHOLDER; import static org.apache.hudi.keygen.KeyGenUtils.NULL_RECORDKEY_PLACEHOLDER; @@ -222,7 +222,7 @@ private S combineRecordKeyInternal( PartitionPathFormatterBase.StringBuilder sb = builderFactory.get(); for (int i = 0; i < recordKeyParts.size(); ++i) { - sb.appendJava(fieldNames.get(i)).appendJava(DEFAULT_COMPOSITE_KEY_FIELD_VALUE); + sb.appendJava(fieldNames.get(i)).appendJava(DEFAULT_COLUMN_VALUE_SEPARATOR); // NOTE: If record-key part has already been a string [[toString]] will be a no-op sb.append(emptyKeyPartHandler.apply(converter.apply(recordKeyParts.get(i)))); @@ -254,7 +254,7 @@ private S combineCompositeRecordKeyInternal( if (encodeSingleKeyFieldName) { sb.appendJava(recordKeyFields.get(0)); - sb.appendJava(DEFAULT_COMPOSITE_KEY_FIELD_VALUE); + sb.appendJava(DEFAULT_COLUMN_VALUE_SEPARATOR); } sb.append(convertedKeyPart); // This check is to validate that overall composite-key has at least one non-null, non-empty @@ -271,7 +271,7 @@ private S combineCompositeRecordKeyInternal( S convertedKeyPart = emptyKeyPartHandler.apply(converter.apply(recordKeyParts[i])); sb.appendJava(recordKeyFields.get(i)); - sb.appendJava(DEFAULT_COMPOSITE_KEY_FIELD_VALUE); + sb.appendJava(DEFAULT_COLUMN_VALUE_SEPARATOR); sb.append(convertedKeyPart); // This check is to validate that overall composite-key has at least one non-null, non-empty // segment diff --git a/hudi-flink-datasource/hudi-flink/src/main/java/org/apache/hudi/sink/bulk/RowDataKeyGen.java b/hudi-flink-datasource/hudi-flink/src/main/java/org/apache/hudi/sink/bulk/RowDataKeyGen.java index cb82332e1fa41..ab1a0d4c1b588 100644 --- a/hudi-flink-datasource/hudi-flink/src/main/java/org/apache/hudi/sink/bulk/RowDataKeyGen.java +++ b/hudi-flink-datasource/hudi-flink/src/main/java/org/apache/hudi/sink/bulk/RowDataKeyGen.java @@ -42,7 +42,7 @@ import static org.apache.hudi.common.util.PartitionPathEncodeUtils.DEFAULT_PARTITION_PATH; import static org.apache.hudi.common.util.PartitionPathEncodeUtils.escapePathName; -import static org.apache.hudi.keygen.KeyGenUtils.DEFAULT_COMPOSITE_KEY_FIELD_VALUE; +import static org.apache.hudi.keygen.KeyGenUtils.DEFAULT_COLUMN_VALUE_SEPARATOR; /** * Key generator for {@link RowData}. @@ -134,7 +134,7 @@ protected RowDataKeyGen( // single record key with multiple partition fields this.simpleRecordKeyFunc = rowData -> { String oriKey = getRecordKey(recordKeyFieldGetter.getFieldOrNull(rowData), this.recordKeyFields[0], consistentLogicalTimestampEnabled); - return new StringBuilder(this.recordKeyFields[0]).append(DEFAULT_COMPOSITE_KEY_FIELD_VALUE).append(oriKey).toString(); + return new StringBuilder(this.recordKeyFields[0]).append(DEFAULT_COLUMN_VALUE_SEPARATOR).append(oriKey).toString(); }; } else { this.simpleRecordKeyFunc = rowData -> getRecordKey(recordKeyFieldGetter.getFieldOrNull(rowData), this.recordKeyFields[0], consistentLogicalTimestampEnabled); From efcd7af06605d06d3e31bbc40018eb37ce0602d1 Mon Sep 17 00:00:00 2001 From: Lokesh Jain Date: Fri, 20 Feb 2026 16:25:11 +0530 Subject: [PATCH 15/35] Fix tests --- .../src/main/java/org/apache/hudi/keygen/KeyGenUtils.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/keygen/KeyGenUtils.java b/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/keygen/KeyGenUtils.java index 52f955e2a32ee..9dfb44c0c3af7 100644 --- a/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/keygen/KeyGenUtils.java +++ b/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/keygen/KeyGenUtils.java @@ -153,11 +153,11 @@ public static String getRecordKey(GenericRecord record, List recordKeyFi for (String recordKeyField : recordKeyFields) { String recordKeyValue = HoodieAvroUtils.getNestedFieldValAsString(record, recordKeyField, true, consistentLogicalTimestampEnabled); if (recordKeyValue == null) { - recordKey.append(recordKeyField).append(DEFAULT_COLUMN_VALUE_SEPARATOR).append(NULL_RECORDKEY_PLACEHOLDER); + recordKey.append(recordKeyField).append(DEFAULT_COLUMN_VALUE_SEPARATOR).append(NULL_RECORDKEY_PLACEHOLDER).append(DEFAULT_RECORD_KEY_PARTS_SEPARATOR); } else if (recordKeyValue.isEmpty()) { - recordKey.append(recordKeyField).append(DEFAULT_COLUMN_VALUE_SEPARATOR).append(EMPTY_RECORDKEY_PLACEHOLDER); + recordKey.append(recordKeyField).append(DEFAULT_COLUMN_VALUE_SEPARATOR).append(EMPTY_RECORDKEY_PLACEHOLDER).append(DEFAULT_RECORD_KEY_PARTS_SEPARATOR); } else { - recordKey.append(recordKeyField).append(DEFAULT_COLUMN_VALUE_SEPARATOR).append(recordKeyValue); + recordKey.append(recordKeyField).append(DEFAULT_COLUMN_VALUE_SEPARATOR).append(recordKeyValue).append(DEFAULT_RECORD_KEY_PARTS_SEPARATOR); keyIsNullEmpty = false; } } From a3d2600deeec6b50eb2c2733977328dab75d9f76 Mon Sep 17 00:00:00 2001 From: Lokesh Jain Date: Tue, 3 Mar 2026 18:51:37 +0530 Subject: [PATCH 16/35] [HUDI-9666] Ensure no RLI partition pruning for complex keygen --- .../apache/hudi/RecordLevelIndexSupport.scala | 4 +++ .../TestRecordLevelIndexWithSQL.scala | 30 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/hudi-spark-datasource/hudi-spark-common/src/main/scala/org/apache/hudi/RecordLevelIndexSupport.scala b/hudi-spark-datasource/hudi-spark-common/src/main/scala/org/apache/hudi/RecordLevelIndexSupport.scala index e1b093b38e0da..6ee3bd3c92fde 100644 --- a/hudi-spark-datasource/hudi-spark-common/src/main/scala/org/apache/hudi/RecordLevelIndexSupport.scala +++ b/hudi-spark-datasource/hudi-spark-common/src/main/scala/org/apache/hudi/RecordLevelIndexSupport.scala @@ -24,6 +24,7 @@ import org.apache.hudi.common.fs.FSUtils import org.apache.hudi.common.model.HoodieRecord.HoodieMetadataField import org.apache.hudi.common.table.HoodieTableMetaClient import org.apache.hudi.keygen.KeyGenUtils +import org.apache.hudi.keygen.constant.KeyGeneratorType import org.apache.hudi.metadata.{HoodieTableMetadata, HoodieTableMetadataUtil} import org.apache.hudi.util.JFunction import org.apache.spark.api.java.JavaSparkContext @@ -127,6 +128,9 @@ class RecordLevelIndexSupport(spark: SparkSession, def filterQueriesWithRecordKey(queryFilters: Seq[Expression]): (List[Expression], List[String]) = { if (!isIndexAvailable || KeyGenUtils.mayUseNewEncodingForComplexKeyGen(metaClient.getTableConfig)) { (List.empty, List.empty) + } else if (KeyGeneratorType.isComplexKeyGenerator(metaClient.getTableConfig)) { + // Complex record keys filtering is not yet supported. Support was added in HUDI-8432. + (List.empty, List.empty) } else { var recordKeyQueries: List[Expression] = List.empty var recordKeys: List[String] = List.empty diff --git a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/hudi/functional/TestRecordLevelIndexWithSQL.scala b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/hudi/functional/TestRecordLevelIndexWithSQL.scala index 8e235960fba33..5c12eeb99eaf5 100644 --- a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/hudi/functional/TestRecordLevelIndexWithSQL.scala +++ b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/hudi/functional/TestRecordLevelIndexWithSQL.scala @@ -17,6 +17,7 @@ package org.apache.hudi.functional +import org.apache.hudi.DataSourceWriteOptions.RECORDKEY_FIELD import org.apache.hudi.common.model.{FileSlice, HoodieTableType} import org.apache.hudi.common.table.HoodieTableMetaClient import org.apache.hudi.metadata.HoodieMetadataFileSystemView @@ -155,4 +156,33 @@ class TestRecordLevelIndexWithSQL extends RecordLevelIndexTestBase { val readDf = spark.read.format("hudi").options(hudiOpts).load(basePath) readDf.registerTempTable(sqlTempTable) } + + @ParameterizedTest + @ValueSource(strings = Array("COPY_ON_WRITE", "MERGE_ON_READ")) + def testRLINoPruningWithComplexRecordKeys(tableType: String): Unit = { + var hudiOpts = commonOpts + { + RECORDKEY_FIELD.key -> "_row_key,rider" + } + hudiOpts = hudiOpts + ( + DataSourceWriteOptions.TABLE_TYPE.key -> tableType, + DataSourceReadOptions.ENABLE_DATA_SKIPPING.key -> "true") + + doWriteAndValidateDataAndRecordIndex(hudiOpts, + operation = DataSourceWriteOptions.INSERT_OPERATION_OPT_VAL, + saveMode = SaveMode.Overwrite, + validate = false) + doWriteAndValidateDataAndRecordIndex(hudiOpts, + operation = DataSourceWriteOptions.UPSERT_OPERATION_OPT_VAL, + saveMode = SaveMode.Append, + validate = false) + + val indexOpts = hudiOpts + {"path" -> basePath} + metaClient = HoodieTableMetaClient.reload(metaClient) + val fileIndex = HoodieFileIndex(spark, metaClient, None, indexOpts, includeLogFiles = true) + // random data filter + val filteredPartitionDirectories = fileIndex.listFiles(Seq(), Seq(EqualTo(attribute("_row_key"), Literal("abc")))) + val filteredFilesCount = filteredPartitionDirectories.flatMap(s => s.files).size + // Assert no pruning with complex record keys + assertEquals(getLatestDataFilesCount(indexOpts, includeLogFiles = false), filteredFilesCount) + } } From 6547fbfca542a8f0cebacefa20c4f1c320fbdf0b Mon Sep 17 00:00:00 2001 From: Lokesh Jain Date: Wed, 4 Mar 2026 14:06:24 +0530 Subject: [PATCH 17/35] Address review comments --- .../hudi/functional/TestRecordLevelIndexWithSQL.scala | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/hudi/functional/TestRecordLevelIndexWithSQL.scala b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/hudi/functional/TestRecordLevelIndexWithSQL.scala index 5c12eeb99eaf5..41ebb01d44e1b 100644 --- a/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/hudi/functional/TestRecordLevelIndexWithSQL.scala +++ b/hudi-spark-datasource/hudi-spark/src/test/scala/org/apache/hudi/functional/TestRecordLevelIndexWithSQL.scala @@ -27,7 +27,7 @@ import org.apache.spark.sql.SaveMode import org.apache.spark.sql.catalyst.expressions.{AttributeReference, EqualTo, Expression, GreaterThan, GreaterThanOrEqual, In, Literal, Or} import org.apache.spark.sql.types.StringType import org.junit.jupiter.api.Assertions.{assertEquals, assertTrue} -import org.junit.jupiter.api.Tag +import org.junit.jupiter.api.{Tag, Test} import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ValueSource @@ -157,14 +157,13 @@ class TestRecordLevelIndexWithSQL extends RecordLevelIndexTestBase { readDf.registerTempTable(sqlTempTable) } - @ParameterizedTest - @ValueSource(strings = Array("COPY_ON_WRITE", "MERGE_ON_READ")) - def testRLINoPruningWithComplexRecordKeys(tableType: String): Unit = { + @Test + def testRLINoPruningWithComplexRecordKeys(): Unit = { var hudiOpts = commonOpts + { RECORDKEY_FIELD.key -> "_row_key,rider" } hudiOpts = hudiOpts + ( - DataSourceWriteOptions.TABLE_TYPE.key -> tableType, + DataSourceWriteOptions.TABLE_TYPE.key -> "COPY_ON_WRITE", DataSourceReadOptions.ENABLE_DATA_SKIPPING.key -> "true") doWriteAndValidateDataAndRecordIndex(hudiOpts, From 7caf9f26fe7652b2be82900ee020fd182bbbf58d Mon Sep 17 00:00:00 2001 From: Lokesh Jain Date: Thu, 5 Mar 2026 16:34:32 +0530 Subject: [PATCH 18/35] Possible fix --- .github/workflows/bot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/bot.yml b/.github/workflows/bot.yml index 432d359817282..2494d0a24f483 100644 --- a/.github/workflows/bot.yml +++ b/.github/workflows/bot.yml @@ -241,7 +241,7 @@ jobs: mvn verify -Pintegration-tests -D"$SCALA_PROFILE" -D"$FLINK_PROFILE" -pl hudi-flink-datasource/hudi-flink $MVN_ARGS docker-java17-test: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 strategy: matrix: include: From 29befde95f42466fe39c5d2ebf5aa1a51088871a Mon Sep 17 00:00:00 2001 From: Lokesh Jain Date: Thu, 5 Mar 2026 16:59:57 +0530 Subject: [PATCH 19/35] Another possible fix --- .github/workflows/bot.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/bot.yml b/.github/workflows/bot.yml index 2494d0a24f483..d8c7c74e1207b 100644 --- a/.github/workflows/bot.yml +++ b/.github/workflows/bot.yml @@ -241,7 +241,7 @@ jobs: mvn verify -Pintegration-tests -D"$SCALA_PROFILE" -D"$FLINK_PROFILE" -pl hudi-flink-datasource/hudi-flink $MVN_ARGS docker-java17-test: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest strategy: matrix: include: @@ -252,7 +252,7 @@ jobs: steps: - uses: actions/checkout@v3 - name: Set up JDK 8 - uses: actions/setup-java@v3 + uses: actions/setup-java@v5 with: java-version: '8' distribution: 'adopt' From bcec03aed6eed611024aa449d6b2f13ddc1d587f Mon Sep 17 00:00:00 2001 From: Lokesh Jain Date: Thu, 5 Mar 2026 18:03:01 +0530 Subject: [PATCH 20/35] Revert "Another possible fix" This reverts commit 29befde95f42466fe39c5d2ebf5aa1a51088871a. --- .github/workflows/bot.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/bot.yml b/.github/workflows/bot.yml index d8c7c74e1207b..2494d0a24f483 100644 --- a/.github/workflows/bot.yml +++ b/.github/workflows/bot.yml @@ -241,7 +241,7 @@ jobs: mvn verify -Pintegration-tests -D"$SCALA_PROFILE" -D"$FLINK_PROFILE" -pl hudi-flink-datasource/hudi-flink $MVN_ARGS docker-java17-test: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 strategy: matrix: include: @@ -252,7 +252,7 @@ jobs: steps: - uses: actions/checkout@v3 - name: Set up JDK 8 - uses: actions/setup-java@v5 + uses: actions/setup-java@v3 with: java-version: '8' distribution: 'adopt' From 6d748227d2865fb46ca1158df3101ab02796556d Mon Sep 17 00:00:00 2001 From: Lokesh Jain Date: Thu, 5 Mar 2026 18:37:51 +0530 Subject: [PATCH 21/35] Fix failures --- .github/workflows/bot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/bot.yml b/.github/workflows/bot.yml index 2494d0a24f483..a26d3c40fb081 100644 --- a/.github/workflows/bot.yml +++ b/.github/workflows/bot.yml @@ -269,7 +269,7 @@ jobs: ./packaging/bundle-validation/run_docker_java17.sh validate-bundles: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 strategy: matrix: include: From e2e2f236986070fbe15b2d51d57c0f7a8b9a639c Mon Sep 17 00:00:00 2001 From: Lokesh Jain Date: Mon, 9 Mar 2026 11:08:42 +0530 Subject: [PATCH 22/35] Debug --- azure-pipelines-20230430.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines-20230430.yml b/azure-pipelines-20230430.yml index 85d185fbc2c5c..5385dec4fdea3 100644 --- a/azure-pipelines-20230430.yml +++ b/azure-pipelines-20230430.yml @@ -160,7 +160,7 @@ stages: inputs: mavenPomFile: 'pom.xml' goals: 'test' - options: $(MVN_OPTS_TEST) -Pfunctional-tests -pl $(JOB2_MODULES) + options: $(MVN_OPTS_TEST) -pl $(JOB2_MODULES) -Dtest=TestSparkHoodieHBaseIndex -Dlog4j2.rootLogger.level=info -Dcheckstyle.skip publishJUnitResults: false jdkVersionOption: '1.8' mavenOptions: '-Xmx4g' From 3133e9ec445dae522d9679dedc3250066591bcf6 Mon Sep 17 00:00:00 2001 From: Lokesh Jain Date: Mon, 9 Mar 2026 12:45:07 +0530 Subject: [PATCH 23/35] Revert "Debug" This reverts commit e2e2f236986070fbe15b2d51d57c0f7a8b9a639c. --- azure-pipelines-20230430.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines-20230430.yml b/azure-pipelines-20230430.yml index 5385dec4fdea3..85d185fbc2c5c 100644 --- a/azure-pipelines-20230430.yml +++ b/azure-pipelines-20230430.yml @@ -160,7 +160,7 @@ stages: inputs: mavenPomFile: 'pom.xml' goals: 'test' - options: $(MVN_OPTS_TEST) -pl $(JOB2_MODULES) -Dtest=TestSparkHoodieHBaseIndex -Dlog4j2.rootLogger.level=info -Dcheckstyle.skip + options: $(MVN_OPTS_TEST) -Pfunctional-tests -pl $(JOB2_MODULES) publishJUnitResults: false jdkVersionOption: '1.8' mavenOptions: '-Xmx4g' From 9e8b8dcba22e441c89adfad74151473fdc3fac00 Mon Sep 17 00:00:00 2001 From: Lokesh Jain Date: Mon, 9 Mar 2026 12:45:38 +0530 Subject: [PATCH 24/35] Possible fix --- .../org/apache/hudi/index/hbase/TestSparkHoodieHBaseIndex.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/index/hbase/TestSparkHoodieHBaseIndex.java b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/index/hbase/TestSparkHoodieHBaseIndex.java index 4b0666934cf44..5cbb997239a17 100644 --- a/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/index/hbase/TestSparkHoodieHBaseIndex.java +++ b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/index/hbase/TestSparkHoodieHBaseIndex.java @@ -51,6 +51,7 @@ import org.apache.hadoop.fs.Path; import org.apache.hadoop.hbase.HBaseConfiguration; import org.apache.hadoop.hbase.HBaseTestingUtility; +import org.apache.hadoop.hbase.StartMiniClusterOption; import org.apache.hadoop.hbase.TableName; import org.apache.hadoop.hbase.client.Connection; import org.apache.hadoop.hbase.client.Get; @@ -123,7 +124,7 @@ public static void init() throws Exception { hbaseConfig.set(ZOOKEEPER_ZNODE_PARENT, "/hudi-hbase-test"); utility = new HBaseTestingUtility(hbaseConfig); - utility.startMiniCluster(); + utility.startMiniCluster(StartMiniClusterOption.builder().numDataNodes(2).build()); hbaseConfig = utility.getConnection().getConfiguration(); utility.createTable(TableName.valueOf(TABLE_NAME), Bytes.toBytes("_s"),2); } From 9a2f0ad12bb62491c2aaec4576bd0cdef6c9a232 Mon Sep 17 00:00:00 2001 From: Lokesh Jain Date: Mon, 9 Mar 2026 19:24:40 +0530 Subject: [PATCH 25/35] Possible fix --- .../org/apache/hudi/index/hbase/TestSparkHoodieHBaseIndex.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/index/hbase/TestSparkHoodieHBaseIndex.java b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/index/hbase/TestSparkHoodieHBaseIndex.java index 5cbb997239a17..0a0a5b3ea7f83 100644 --- a/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/index/hbase/TestSparkHoodieHBaseIndex.java +++ b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/index/hbase/TestSparkHoodieHBaseIndex.java @@ -59,6 +59,7 @@ import org.apache.hadoop.hbase.client.Put; import org.apache.hadoop.hbase.client.Result; import org.apache.hadoop.hbase.util.Bytes; +import org.apache.hadoop.hdfs.server.blockmanagement.AvailableSpaceBlockPlacementPolicy; import org.apache.spark.api.java.JavaRDD; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; @@ -122,6 +123,7 @@ public static void init() throws Exception { System.setProperty("zookeeper.4lw.commands.whitelist", "*"); hbaseConfig = HBaseConfiguration.create(); hbaseConfig.set(ZOOKEEPER_ZNODE_PARENT, "/hudi-hbase-test"); + hbaseConfig.set("dfs.block.replicator.classname", AvailableSpaceBlockPlacementPolicy.class.getCanonicalName()); utility = new HBaseTestingUtility(hbaseConfig); utility.startMiniCluster(StartMiniClusterOption.builder().numDataNodes(2).build()); From ffbaf50e8d66e8a01e6705a7c1b3922a9ed2f492 Mon Sep 17 00:00:00 2001 From: Lokesh Jain Date: Mon, 9 Mar 2026 22:35:36 +0530 Subject: [PATCH 26/35] Another possible fix --- .../org/apache/hudi/index/hbase/TestSparkHoodieHBaseIndex.java | 1 + 1 file changed, 1 insertion(+) diff --git a/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/index/hbase/TestSparkHoodieHBaseIndex.java b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/index/hbase/TestSparkHoodieHBaseIndex.java index 0a0a5b3ea7f83..cba2fcd2030f4 100644 --- a/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/index/hbase/TestSparkHoodieHBaseIndex.java +++ b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/index/hbase/TestSparkHoodieHBaseIndex.java @@ -127,6 +127,7 @@ public static void init() throws Exception { utility = new HBaseTestingUtility(hbaseConfig); utility.startMiniCluster(StartMiniClusterOption.builder().numDataNodes(2).build()); + utility.getDFSCluster().waitActive(); hbaseConfig = utility.getConnection().getConfiguration(); utility.createTable(TableName.valueOf(TABLE_NAME), Bytes.toBytes("_s"),2); } From 96d7a4d70b979a3196363ebcbf390f6b7e30f2cd Mon Sep 17 00:00:00 2001 From: Lokesh Jain Date: Wed, 11 Mar 2026 14:40:41 +0530 Subject: [PATCH 27/35] Revert fixes --- .../apache/hudi/index/hbase/TestSparkHoodieHBaseIndex.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/index/hbase/TestSparkHoodieHBaseIndex.java b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/index/hbase/TestSparkHoodieHBaseIndex.java index cba2fcd2030f4..4b0666934cf44 100644 --- a/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/index/hbase/TestSparkHoodieHBaseIndex.java +++ b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/index/hbase/TestSparkHoodieHBaseIndex.java @@ -51,7 +51,6 @@ import org.apache.hadoop.fs.Path; import org.apache.hadoop.hbase.HBaseConfiguration; import org.apache.hadoop.hbase.HBaseTestingUtility; -import org.apache.hadoop.hbase.StartMiniClusterOption; import org.apache.hadoop.hbase.TableName; import org.apache.hadoop.hbase.client.Connection; import org.apache.hadoop.hbase.client.Get; @@ -59,7 +58,6 @@ import org.apache.hadoop.hbase.client.Put; import org.apache.hadoop.hbase.client.Result; import org.apache.hadoop.hbase.util.Bytes; -import org.apache.hadoop.hdfs.server.blockmanagement.AvailableSpaceBlockPlacementPolicy; import org.apache.spark.api.java.JavaRDD; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; @@ -123,11 +121,9 @@ public static void init() throws Exception { System.setProperty("zookeeper.4lw.commands.whitelist", "*"); hbaseConfig = HBaseConfiguration.create(); hbaseConfig.set(ZOOKEEPER_ZNODE_PARENT, "/hudi-hbase-test"); - hbaseConfig.set("dfs.block.replicator.classname", AvailableSpaceBlockPlacementPolicy.class.getCanonicalName()); utility = new HBaseTestingUtility(hbaseConfig); - utility.startMiniCluster(StartMiniClusterOption.builder().numDataNodes(2).build()); - utility.getDFSCluster().waitActive(); + utility.startMiniCluster(); hbaseConfig = utility.getConnection().getConfiguration(); utility.createTable(TableName.valueOf(TABLE_NAME), Bytes.toBytes("_s"),2); } From fd20eea15b8d28e37d86c971999f506f5fb428b0 Mon Sep 17 00:00:00 2001 From: Lokesh Jain Date: Thu, 12 Mar 2026 17:06:31 +0530 Subject: [PATCH 28/35] Possible fix --- .../apache/hudi/index/hbase/TestSparkHoodieHBaseIndex.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/index/hbase/TestSparkHoodieHBaseIndex.java b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/index/hbase/TestSparkHoodieHBaseIndex.java index 4b0666934cf44..6b0d558dab191 100644 --- a/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/index/hbase/TestSparkHoodieHBaseIndex.java +++ b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/index/hbase/TestSparkHoodieHBaseIndex.java @@ -122,6 +122,12 @@ public static void init() throws Exception { hbaseConfig = HBaseConfiguration.create(); hbaseConfig.set(ZOOKEEPER_ZNODE_PARENT, "/hudi-hbase-test"); + // Configure HDFS to reduce disk usage + hbaseConfig.set("dfs.replication", "1"); + hbaseConfig.set("dfs.namenode.replication.min", "1"); + hbaseConfig.setBoolean("dfs.client.block.write.replace-datanode-on-failure.enable", true); + hbaseConfig.set("dfs.client.block.write.replace-datanode-on-failure.policy", "NEVER"); + utility = new HBaseTestingUtility(hbaseConfig); utility.startMiniCluster(); hbaseConfig = utility.getConnection().getConfiguration(); From 3b3d0b96faeae752bbdb92dfc99febf2d2d272fc Mon Sep 17 00:00:00 2001 From: Lokesh Jain Date: Thu, 12 Mar 2026 17:09:02 +0530 Subject: [PATCH 29/35] Fix docs --- .../java/org/apache/hudi/config/HoodieWriteConfig.java | 7 +++---- 1 file changed, 3 insertions(+), 4 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 ad3d4cee2c22b..d7a57b4732f17 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 @@ -196,16 +196,15 @@ public class HoodieWriteConfig extends HoodieConfig { .defaultValue(false) .markAdvanced() .sinceVersion("0.14.2") - .supportedVersions("0.14.2", "0.15.1", "1.0.3", "1.1.0") - .withDocumentation("This config only takes effect for writing table version 8 and below. " - + "If set to false, the record key field name is encoded and prepended " + .supportedVersions("0.14.2") + .withDocumentation("If set to false, the record key field name is encoded and prepended " + "in the case where a single record key field is used in the complex key generator, " + "i.e., record keys stored in _hoodie_record_key meta field is in the format of " + "`:`, which conforms to the behavior " + "in 0.14.0 release and older. If set to true, the record key field name is not " + "encoded under the same case in the complex key generator, i.e., record keys stored " + "in _hoodie_record_key meta field is in the format of ``, " - + "which conforms to the behavior in 0.14.1, 0.15.0, 1.0.0, 1.0.1, 1.0.2 releases."); + + "which conforms to the behavior in 0.14.1 release."); public static final ConfigProperty ENABLE_COMPLEX_KEYGEN_VALIDATION = ConfigProperty .key("hoodie.write.complex.keygen.validation.enable") From 975c689904739e5968db5096972662fe929ed57f Mon Sep 17 00:00:00 2001 From: Lokesh Jain Date: Fri, 13 Mar 2026 10:53:11 +0530 Subject: [PATCH 30/35] Split CI --- azure-pipelines-20230430.yml | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/azure-pipelines-20230430.yml b/azure-pipelines-20230430.yml index 85d185fbc2c5c..8d5b826d7df21 100644 --- a/azure-pipelines-20230430.yml +++ b/azure-pipelines-20230430.yml @@ -160,7 +160,31 @@ stages: inputs: mavenPomFile: 'pom.xml' goals: 'test' - options: $(MVN_OPTS_TEST) -Pfunctional-tests -pl $(JOB2_MODULES) + options: $(MVN_OPTS_TEST) -Pfunctional-tests -pl $(JOB2_MODULES) -Dtest=!TestSparkHoodieHBaseIndex,!TestHoodieSparkMergeOnReadTableRollback,!TestHoodieBackedMetadata,!TestHoodieSparkCopyOnWriteTableArchiveWithReplace,!TestHoodieClientOnCopyOnWriteStorage,!TestSavepointRestoreMergeOnRead,!TestHoodieIndex,!TestConsistentBucketIndex + publishJUnitResults: false + jdkVersionOption: '1.8' + mavenOptions: '-Xmx4g' + - script: | + grep "testcase" */target/surefire-reports/*.xml */*/target/surefire-reports/*.xml | awk -F'"' ' { print $6,$4,$2 } ' | sort -nr | head -n 100 + displayName: Top 100 long-running testcases + - job: UT_FT_5 + displayName: FT client/spark-client Long running + timeoutInMinutes: '150' + steps: + - task: Maven@4 + displayName: maven install + inputs: + mavenPomFile: 'pom.xml' + goals: 'clean install' + options: $(MVN_OPTS_INSTALL) + publishJUnitResults: false + jdkVersionOption: '1.8' + - task: Maven@4 + displayName: FT client/spark-client Long running + inputs: + mavenPomFile: 'pom.xml' + goals: 'test' + options: $(MVN_OPTS_TEST) -Pfunctional-tests -pl $(JOB2_MODULES) -Dtest=TestSparkHoodieHBaseIndex,TestHoodieSparkMergeOnReadTableRollback,TestHoodieBackedMetadata,TestHoodieSparkCopyOnWriteTableArchiveWithReplace,TestHoodieClientOnCopyOnWriteStorage,TestSavepointRestoreMergeOnRead,TestHoodieIndex,TestConsistentBucketIndex publishJUnitResults: false jdkVersionOption: '1.8' mavenOptions: '-Xmx4g' From 3ff33e7cd85854ae9ec715c6bcf6194bbadac75a Mon Sep 17 00:00:00 2001 From: Lokesh Jain Date: Fri, 13 Mar 2026 12:11:57 +0530 Subject: [PATCH 31/35] Fix CI --- azure-pipelines-20230430.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azure-pipelines-20230430.yml b/azure-pipelines-20230430.yml index 8d5b826d7df21..749ded55bbf15 100644 --- a/azure-pipelines-20230430.yml +++ b/azure-pipelines-20230430.yml @@ -160,7 +160,7 @@ stages: inputs: mavenPomFile: 'pom.xml' goals: 'test' - options: $(MVN_OPTS_TEST) -Pfunctional-tests -pl $(JOB2_MODULES) -Dtest=!TestSparkHoodieHBaseIndex,!TestHoodieSparkMergeOnReadTableRollback,!TestHoodieBackedMetadata,!TestHoodieSparkCopyOnWriteTableArchiveWithReplace,!TestHoodieClientOnCopyOnWriteStorage,!TestSavepointRestoreMergeOnRead,!TestHoodieIndex,!TestConsistentBucketIndex + options: $(MVN_OPTS_TEST) -pl $(JOB2_MODULES) -Dtest='!TestSparkHoodieHBaseIndex,!TestHoodieSparkMergeOnReadTableRollback,!TestHoodieBackedMetadata,!TestHoodieSparkCopyOnWriteTableArchiveWithReplace,!TestHoodieClientOnCopyOnWriteStorage,!TestSavepointRestoreMergeOnRead,!TestHoodieIndex,!TestConsistentBucketIndex' publishJUnitResults: false jdkVersionOption: '1.8' mavenOptions: '-Xmx4g' @@ -184,7 +184,7 @@ stages: inputs: mavenPomFile: 'pom.xml' goals: 'test' - options: $(MVN_OPTS_TEST) -Pfunctional-tests -pl $(JOB2_MODULES) -Dtest=TestSparkHoodieHBaseIndex,TestHoodieSparkMergeOnReadTableRollback,TestHoodieBackedMetadata,TestHoodieSparkCopyOnWriteTableArchiveWithReplace,TestHoodieClientOnCopyOnWriteStorage,TestSavepointRestoreMergeOnRead,TestHoodieIndex,TestConsistentBucketIndex + options: $(MVN_OPTS_TEST) -pl $(JOB2_MODULES) -Dtest=TestSparkHoodieHBaseIndex,TestHoodieSparkMergeOnReadTableRollback,TestHoodieBackedMetadata,TestHoodieSparkCopyOnWriteTableArchiveWithReplace,TestHoodieClientOnCopyOnWriteStorage,TestSavepointRestoreMergeOnRead,TestHoodieIndex,TestConsistentBucketIndex publishJUnitResults: false jdkVersionOption: '1.8' mavenOptions: '-Xmx4g' From 5ac26b7939ade1ff548cd18c307fa587861f600f Mon Sep 17 00:00:00 2001 From: Lokesh Jain Date: Fri, 13 Mar 2026 12:44:53 +0530 Subject: [PATCH 32/35] Revert "Possible fix" This reverts commit fd20eea15b8d28e37d86c971999f506f5fb428b0. --- .../apache/hudi/index/hbase/TestSparkHoodieHBaseIndex.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/index/hbase/TestSparkHoodieHBaseIndex.java b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/index/hbase/TestSparkHoodieHBaseIndex.java index 6b0d558dab191..4b0666934cf44 100644 --- a/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/index/hbase/TestSparkHoodieHBaseIndex.java +++ b/hudi-client/hudi-spark-client/src/test/java/org/apache/hudi/index/hbase/TestSparkHoodieHBaseIndex.java @@ -122,12 +122,6 @@ public static void init() throws Exception { hbaseConfig = HBaseConfiguration.create(); hbaseConfig.set(ZOOKEEPER_ZNODE_PARENT, "/hudi-hbase-test"); - // Configure HDFS to reduce disk usage - hbaseConfig.set("dfs.replication", "1"); - hbaseConfig.set("dfs.namenode.replication.min", "1"); - hbaseConfig.setBoolean("dfs.client.block.write.replace-datanode-on-failure.enable", true); - hbaseConfig.set("dfs.client.block.write.replace-datanode-on-failure.policy", "NEVER"); - utility = new HBaseTestingUtility(hbaseConfig); utility.startMiniCluster(); hbaseConfig = utility.getConnection().getConfiguration(); From d4dd0bcbe2b61fcc40514679d32774d45151b10a Mon Sep 17 00:00:00 2001 From: Lokesh Jain Date: Fri, 13 Mar 2026 15:47:12 +0530 Subject: [PATCH 33/35] Fix CI --- azure-pipelines-20230430.yml | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/azure-pipelines-20230430.yml b/azure-pipelines-20230430.yml index 749ded55bbf15..b6e7aa2780c82 100644 --- a/azure-pipelines-20230430.yml +++ b/azure-pipelines-20230430.yml @@ -160,7 +160,31 @@ stages: inputs: mavenPomFile: 'pom.xml' goals: 'test' - options: $(MVN_OPTS_TEST) -pl $(JOB2_MODULES) -Dtest='!TestSparkHoodieHBaseIndex,!TestHoodieSparkMergeOnReadTableRollback,!TestHoodieBackedMetadata,!TestHoodieSparkCopyOnWriteTableArchiveWithReplace,!TestHoodieClientOnCopyOnWriteStorage,!TestSavepointRestoreMergeOnRead,!TestHoodieIndex,!TestConsistentBucketIndex' + options: $(MVN_OPTS_TEST) -pl $(JOB2_MODULES) -Pfunctional-tests -Dtest='*,!TestSparkHoodieHBaseIndex,!TestHoodieSparkMergeOnReadTableRollback,!TestHoodieBackedMetadata,!TestHoodieSparkCopyOnWriteTableArchiveWithReplace,!TestHoodieClientOnCopyOnWriteStorage,!TestSavepointRestoreMergeOnRead,!TestHoodieIndex,!TestConsistentBucketIndex' + publishJUnitResults: false + jdkVersionOption: '1.8' + mavenOptions: '-Xmx4g' + - script: | + grep "testcase" */target/surefire-reports/*.xml */*/target/surefire-reports/*.xml | awk -F'"' ' { print $6,$4,$2 } ' | sort -nr | head -n 100 + displayName: Top 100 long-running testcases + - job: UT_FT_6 + displayName: FT client/spark-client dup + timeoutInMinutes: '150' + steps: + - task: Maven@4 + displayName: maven install + inputs: + mavenPomFile: 'pom.xml' + goals: 'clean install' + options: $(MVN_OPTS_INSTALL) + publishJUnitResults: false + jdkVersionOption: '1.8' + - task: Maven@4 + displayName: FT client/spark-client dup + inputs: + mavenPomFile: 'pom.xml' + goals: 'test' + options: $(MVN_OPTS_TEST) -pl $(JOB2_MODULES) -Dtest='*,!TestSparkHoodieHBaseIndex,!TestHoodieSparkMergeOnReadTableRollback,!TestHoodieBackedMetadata,!TestHoodieSparkCopyOnWriteTableArchiveWithReplace,!TestHoodieClientOnCopyOnWriteStorage,!TestSavepointRestoreMergeOnRead,!TestHoodieIndex,!TestConsistentBucketIndex' publishJUnitResults: false jdkVersionOption: '1.8' mavenOptions: '-Xmx4g' From 11a3f03c678f22d56413089d44f5ed43dc593a3a Mon Sep 17 00:00:00 2001 From: Lokesh Jain Date: Fri, 13 Mar 2026 18:36:28 +0530 Subject: [PATCH 34/35] Fix CI --- azure-pipelines-20230430.yml | 28 ++--------------- hudi-client/hudi-spark-client/pom.xml | 44 +++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 26 deletions(-) diff --git a/azure-pipelines-20230430.yml b/azure-pipelines-20230430.yml index b6e7aa2780c82..9d81e81d9a534 100644 --- a/azure-pipelines-20230430.yml +++ b/azure-pipelines-20230430.yml @@ -160,31 +160,7 @@ stages: inputs: mavenPomFile: 'pom.xml' goals: 'test' - options: $(MVN_OPTS_TEST) -pl $(JOB2_MODULES) -Pfunctional-tests -Dtest='*,!TestSparkHoodieHBaseIndex,!TestHoodieSparkMergeOnReadTableRollback,!TestHoodieBackedMetadata,!TestHoodieSparkCopyOnWriteTableArchiveWithReplace,!TestHoodieClientOnCopyOnWriteStorage,!TestSavepointRestoreMergeOnRead,!TestHoodieIndex,!TestConsistentBucketIndex' - publishJUnitResults: false - jdkVersionOption: '1.8' - mavenOptions: '-Xmx4g' - - script: | - grep "testcase" */target/surefire-reports/*.xml */*/target/surefire-reports/*.xml | awk -F'"' ' { print $6,$4,$2 } ' | sort -nr | head -n 100 - displayName: Top 100 long-running testcases - - job: UT_FT_6 - displayName: FT client/spark-client dup - timeoutInMinutes: '150' - steps: - - task: Maven@4 - displayName: maven install - inputs: - mavenPomFile: 'pom.xml' - goals: 'clean install' - options: $(MVN_OPTS_INSTALL) - publishJUnitResults: false - jdkVersionOption: '1.8' - - task: Maven@4 - displayName: FT client/spark-client dup - inputs: - mavenPomFile: 'pom.xml' - goals: 'test' - options: $(MVN_OPTS_TEST) -pl $(JOB2_MODULES) -Dtest='*,!TestSparkHoodieHBaseIndex,!TestHoodieSparkMergeOnReadTableRollback,!TestHoodieBackedMetadata,!TestHoodieSparkCopyOnWriteTableArchiveWithReplace,!TestHoodieClientOnCopyOnWriteStorage,!TestSavepointRestoreMergeOnRead,!TestHoodieIndex,!TestConsistentBucketIndex' + options: $(MVN_OPTS_TEST) -pl $(JOB2_MODULES) -Pfunctional-tests publishJUnitResults: false jdkVersionOption: '1.8' mavenOptions: '-Xmx4g' @@ -208,7 +184,7 @@ stages: inputs: mavenPomFile: 'pom.xml' goals: 'test' - options: $(MVN_OPTS_TEST) -pl $(JOB2_MODULES) -Dtest=TestSparkHoodieHBaseIndex,TestHoodieSparkMergeOnReadTableRollback,TestHoodieBackedMetadata,TestHoodieSparkCopyOnWriteTableArchiveWithReplace,TestHoodieClientOnCopyOnWriteStorage,TestSavepointRestoreMergeOnRead,TestHoodieIndex,TestConsistentBucketIndex + options: $(MVN_OPTS_TEST) -pl $(JOB2_MODULES) -Pisolated-tests publishJUnitResults: false jdkVersionOption: '1.8' mavenOptions: '-Xmx4g' diff --git a/hudi-client/hudi-spark-client/pom.xml b/hudi-client/hudi-spark-client/pom.xml index fa437494fd9f5..e2e7191038cb5 100644 --- a/hudi-client/hudi-spark-client/pom.xml +++ b/hudi-client/hudi-spark-client/pom.xml @@ -253,6 +253,23 @@ org.apache.rat apache-rat-plugin + + org.apache.maven.plugins + maven-surefire-plugin + + + + **/TestSparkHoodieHBaseIndex.java + **/TestHoodieSparkMergeOnReadTableRollback.java + **/TestHoodieBackedMetadata.java + **/TestHoodieSparkCopyOnWriteTableArchiveWithReplace.java + **/TestHoodieClientOnCopyOnWriteStorage.java + **/TestSavepointRestoreMergeOnRead.java + **/TestHoodieIndex.java + **/TestConsistentBucketIndex.java + + + @@ -264,4 +281,31 @@ + + + + + isolated-tests + + + + org.apache.maven.plugins + maven-surefire-plugin + + + **/TestSparkHoodieHBaseIndex.java + **/TestHoodieSparkMergeOnReadTableRollback.java + **/TestHoodieBackedMetadata.java + **/TestHoodieSparkCopyOnWriteTableArchiveWithReplace.java + **/TestHoodieClientOnCopyOnWriteStorage.java + **/TestSavepointRestoreMergeOnRead.java + **/TestHoodieIndex.java + **/TestConsistentBucketIndex.java + + + + + + + From a44b3fb8623cc42c1f4b78f8abba56f841754b9b Mon Sep 17 00:00:00 2001 From: Lokesh Jain Date: Fri, 13 Mar 2026 20:19:22 +0530 Subject: [PATCH 35/35] Debug --- azure-pipelines-20230430.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines-20230430.yml b/azure-pipelines-20230430.yml index 9d81e81d9a534..3fb901d27b8ce 100644 --- a/azure-pipelines-20230430.yml +++ b/azure-pipelines-20230430.yml @@ -160,7 +160,7 @@ stages: inputs: mavenPomFile: 'pom.xml' goals: 'test' - options: $(MVN_OPTS_TEST) -pl $(JOB2_MODULES) -Pfunctional-tests + options: $(MVN_OPTS_TEST) -pl $(JOB2_MODULES) -Pfunctional-tests -debug publishJUnitResults: false jdkVersionOption: '1.8' mavenOptions: '-Xmx4g'