Skip to content

Commit 7ecf657

Browse files
authored
Harden schema case-collision validation with Locale.ROOT (#19089)
1 parent 6d5cdfb commit 7ecf657

2 files changed

Lines changed: 34 additions & 3 deletions

File tree

pinot-core/src/test/java/org/apache/pinot/core/util/SchemaUtilsTest.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,35 @@ public void testValidateCaseInsensitive() {
331331
checkValidationFails(pinotSchema, true);
332332
}
333333

334+
/**
335+
* Case-only column collisions (e.g. memberId / MemberID) are rejected when case-insensitive mode is on.
336+
* Cluster default is enable.case.insensitive=true, so new schemas on default clusters are already protected.
337+
* When case-insensitive mode is off, collisions are allowed (always-on rejection needs validation levels #6645).
338+
*/
339+
@Test
340+
public void testValidateCaseOnlyColumnCollision() {
341+
Schema collidingSchema = new Schema.SchemaBuilder().setSchemaName(TABLE_NAME)
342+
.addSingleValueDimension("memberId", DataType.STRING)
343+
.addSingleValueDimension("MemberID", DataType.STRING)
344+
.addDateTime(TIME_COLUMN, DataType.LONG, "1:MILLISECONDS:EPOCH", "1:MILLISECONDS")
345+
.build();
346+
347+
// Default cluster path: enable.case.insensitive=true rejects case-only collisions
348+
checkValidationFails(collidingSchema, true);
349+
350+
// Case-sensitive mode still allows collisions (compat; do not force always-on without #6645)
351+
SchemaUtils.validate(collidingSchema, false);
352+
353+
// Distinct after lowercasing is fine even when case-insensitive
354+
Schema distinctSchema = new Schema.SchemaBuilder().setSchemaName(TABLE_NAME)
355+
.addSingleValueDimension("memberId", DataType.STRING)
356+
.addSingleValueDimension("memberName", DataType.STRING)
357+
.addDateTime(TIME_COLUMN, DataType.LONG, "1:MILLISECONDS:EPOCH", "1:MILLISECONDS")
358+
.build();
359+
SchemaUtils.validate(distinctSchema, true);
360+
SchemaUtils.validate(distinctSchema, false);
361+
}
362+
334363
@Test
335364
public void testValidatePrimaryKeyColumns() {
336365
Schema pinotSchema;

pinot-segment-local/src/main/java/org/apache/pinot/segment/local/utils/SchemaUtils.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.util.HashMap;
2424
import java.util.HashSet;
2525
import java.util.List;
26+
import java.util.Locale;
2627
import java.util.Map;
2728
import java.util.Set;
2829
import org.apache.commons.lang3.StringUtils;
@@ -121,9 +122,10 @@ public static void validate(Schema schema, boolean isIgnoreCase) {
121122
if (isIgnoreCase) {
122123
Set<String> lowerCaseColumnNames = new HashSet<>();
123124
for (String column : schema.getColumnNames()) {
124-
Preconditions.checkState(lowerCaseColumnNames.add(column.toLowerCase()),
125-
"When enable case insensitive, you can't use the same lowercase column name: %s",
126-
column.toLowerCase());
125+
// Locale.ROOT avoids locale-dependent lowercasing (e.g. Turkish dotted/dotless I)
126+
String lowerCaseColumn = column.toLowerCase(Locale.ROOT);
127+
Preconditions.checkState(lowerCaseColumnNames.add(lowerCaseColumn),
128+
"When enable case insensitive, you can't use the same lowercase column name: %s", lowerCaseColumn);
127129
}
128130
}
129131
Set<String> transformedColumns = new HashSet<>();

0 commit comments

Comments
 (0)