This is an automated email from the ASF dual-hosted git repository.
gortiz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pinot.git
The following commit(s) were added to refs/heads/master by this push:
new b5c9d2c4d7a Reject duplicate FieldConfig column names during table
config validation (#18211)
b5c9d2c4d7a is described below
commit b5c9d2c4d7ac07e07332abbbc3922d7fb5501b05
Author: Anshul Singh <[email protected]>
AuthorDate: Mon Apr 20 21:04:58 2026 +0530
Reject duplicate FieldConfig column names during table config validation
(#18211)
TableConfigUtils.validateIndexingConfigAndFieldConfigList did not check for
duplicate column names in fieldConfigList. A config with two FieldConfig
entries for the same column would pass validation but crash downstream in
AbstractIndexType.convertToNewFormat (Collectors.toMap throws
IllegalStateException: Duplicate key).
Fix: add a duplicate column name check in the validation loop.
Made-with: Cursor
---
.../segment/local/utils/TableConfigUtils.java | 2 +
.../segment/local/utils/TableConfigUtilsTest.java | 106 +++++++++++++++++++++
2 files changed, 108 insertions(+)
diff --git
a/pinot-segment-local/src/main/java/org/apache/pinot/segment/local/utils/TableConfigUtils.java
b/pinot-segment-local/src/main/java/org/apache/pinot/segment/local/utils/TableConfigUtils.java
index 0a874cba3a8..9396658bbc9 100644
---
a/pinot-segment-local/src/main/java/org/apache/pinot/segment/local/utils/TableConfigUtils.java
+++
b/pinot-segment-local/src/main/java/org/apache/pinot/segment/local/utils/TableConfigUtils.java
@@ -1480,8 +1480,10 @@ public final class TableConfigUtils {
IndexingConfig indexingConfig = tableConfig.getIndexingConfig();
List<FieldConfig> fieldConfigs = tableConfig.getFieldConfigList();
if (CollectionUtils.isNotEmpty(fieldConfigs)) {
+ Set<String> seenColumns = new HashSet<>();
for (FieldConfig fieldConfig : fieldConfigs) {
String column = fieldConfig.getName();
+ Preconditions.checkState(seenColumns.add(column), "Duplicate
FieldConfig for column: %s", column);
Preconditions.checkState(schema.hasColumn(column), "Failed to find
column: %s in schema", column);
// Validate DELTA / DELTADELTA compression codecs compatibility
diff --git
a/pinot-segment-local/src/test/java/org/apache/pinot/segment/local/utils/TableConfigUtilsTest.java
b/pinot-segment-local/src/test/java/org/apache/pinot/segment/local/utils/TableConfigUtilsTest.java
index 22a4fd04be9..822fca56db5 100644
---
a/pinot-segment-local/src/test/java/org/apache/pinot/segment/local/utils/TableConfigUtilsTest.java
+++
b/pinot-segment-local/src/test/java/org/apache/pinot/segment/local/utils/TableConfigUtilsTest.java
@@ -1466,6 +1466,112 @@ public class TableConfigUtilsTest {
}
}
+ @Test
+ public void testValidateFieldConfigDuplicateColumnName() {
+ final Schema schema = new Schema.SchemaBuilder().setSchemaName(TABLE_NAME)
+ .addSingleValueDimension("myCol1", FieldSpec.DataType.STRING)
+ .build();
+ final TableConfig tableConfig = new TableConfigBuilder(TableType.OFFLINE)
+ .setTableName(TABLE_NAME).build();
+
+ final FieldConfig fc1 = new FieldConfig("myCol1",
FieldConfig.EncodingType.RAW, null, null, null, null, null);
+ final FieldConfig fc2 = new FieldConfig("myCol1",
FieldConfig.EncodingType.RAW, null, null, null, null, null);
+ tableConfig.setFieldConfigList(Arrays.asList(fc1, fc2));
+
+ try {
+ TableConfigUtils.validate(tableConfig, schema);
+ fail("Should fail for duplicate FieldConfig column name");
+ } catch (Exception e) {
+ assertEquals(e.getMessage(), "Duplicate FieldConfig for column: myCol1");
+ }
+ }
+
+ @Test
+ public void testValidateFieldConfigDuplicateColumnNameDifferentEncoding() {
+ final Schema schema = new Schema.SchemaBuilder().setSchemaName(TABLE_NAME)
+ .addSingleValueDimension("myCol1", FieldSpec.DataType.STRING)
+ .build();
+ final TableConfig tableConfig = new TableConfigBuilder(TableType.OFFLINE)
+ .setTableName(TABLE_NAME).build();
+
+ final FieldConfig fc1 = new FieldConfig("myCol1",
FieldConfig.EncodingType.RAW, null, null, null, null, null);
+ final FieldConfig fc2 =
+ new FieldConfig("myCol1", FieldConfig.EncodingType.DICTIONARY, null,
null, null, null, null);
+ tableConfig.setFieldConfigList(Arrays.asList(fc1, fc2));
+
+ try {
+ TableConfigUtils.validate(tableConfig, schema);
+ fail("Should fail for duplicate FieldConfig column name even with
different encoding");
+ } catch (Exception e) {
+ assertEquals(e.getMessage(), "Duplicate FieldConfig for column: myCol1");
+ }
+ }
+
+ @Test
+ public void testValidateFieldConfigDuplicateColumnNameWithIndexes()
+ throws Exception {
+ final Schema schema = new Schema.SchemaBuilder().setSchemaName(TABLE_NAME)
+ .addSingleValueDimension("myCol1", FieldSpec.DataType.STRING)
+ .build();
+ final TableConfig tableConfig = new TableConfigBuilder(TableType.OFFLINE)
+ .setTableName(TABLE_NAME).build();
+
+ final ObjectNode indexes = JsonNodeFactory.instance.objectNode();
+ indexes.set("forward",
JsonNodeFactory.instance.objectNode().put("compressionCodec", "ZSTANDARD"));
+ final FieldConfig fc1 = new FieldConfig.Builder("myCol1")
+ .withEncodingType(FieldConfig.EncodingType.RAW)
+ .withIndexes(indexes)
+ .build();
+ final FieldConfig fc2 = new FieldConfig("myCol1",
FieldConfig.EncodingType.RAW, null, null, null, null, null);
+ tableConfig.setFieldConfigList(Arrays.asList(fc1, fc2));
+
+ try {
+ TableConfigUtils.validate(tableConfig, schema);
+ fail("Should fail for duplicate FieldConfig column name");
+ } catch (Exception e) {
+ assertEquals(e.getMessage(), "Duplicate FieldConfig for column: myCol1");
+ }
+ }
+
+ @Test
+ public void testValidateFieldConfigNoDuplicates() {
+ final Schema schema = new Schema.SchemaBuilder().setSchemaName(TABLE_NAME)
+ .addSingleValueDimension("myCol1", FieldSpec.DataType.STRING)
+ .addSingleValueDimension("myCol2", FieldSpec.DataType.INT)
+ .build();
+ final TableConfig tableConfig = new TableConfigBuilder(TableType.OFFLINE)
+ .setTableName(TABLE_NAME).build();
+
+ final FieldConfig fc1 = new FieldConfig("myCol1",
FieldConfig.EncodingType.RAW, null, null, null, null, null);
+ final FieldConfig fc2 =
+ new FieldConfig("myCol2", FieldConfig.EncodingType.DICTIONARY, null,
null, null, null, null);
+ tableConfig.setFieldConfigList(Arrays.asList(fc1, fc2));
+
+ try {
+ TableConfigUtils.validate(tableConfig, schema);
+ } catch (Exception e) {
+ fail("Should not fail for distinct column names", e);
+ }
+ }
+
+ @Test
+ public void testValidateFieldConfigSingleEntry() {
+ final Schema schema = new Schema.SchemaBuilder().setSchemaName(TABLE_NAME)
+ .addSingleValueDimension("myCol1", FieldSpec.DataType.STRING)
+ .build();
+ final TableConfig tableConfig = new TableConfigBuilder(TableType.OFFLINE)
+ .setTableName(TABLE_NAME).build();
+
+ final FieldConfig fc1 = new FieldConfig("myCol1",
FieldConfig.EncodingType.RAW, null, null, null, null, null);
+ tableConfig.setFieldConfigList(Arrays.asList(fc1));
+
+ try {
+ TableConfigUtils.validate(tableConfig, schema);
+ } catch (Exception e) {
+ fail("Should not fail for single FieldConfig entry", e);
+ }
+ }
+
@Test
public void testValidateBFOnBoolean() {
Schema schema = new Schema.SchemaBuilder().setSchemaName(TABLE_NAME)
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]