Compare commits

...

3 Commits

Author SHA1 Message Date
Cursor Agent 1c5347773f Refactor AnalyzeJoinTest to align USING semantics with SQL standard
Co-authored-by: 936797922 <936797922@qq.com>
2025-09-18 12:01:49 +00:00
Cursor Agent e9be84c804 Checkpoint before follow-up message
Co-authored-by: 936797922 <936797922@qq.com>
2025-09-18 12:00:09 +00:00
Cursor Agent e226d543d4 Fix: Deduplicate USING clause columns in join scope
Co-authored-by: 936797922 <936797922@qq.com>
2025-09-18 11:49:04 +00:00
2 changed files with 31 additions and 1 deletions

View File

@ -906,6 +906,31 @@ public class QueryAnalyzer {
scope = new Scope(RelationId.of(join),
leftScope.getRelationFields().joinWith(rightScope.getRelationFields()));
}
// When USING clause is present, SQL standard/MySQL expose only one column for each
// using-name in the output schema. Deduplicate here by keeping the first occurrence
// (left-first ordering in our construction) and dropping subsequent duplicates.
if (join.getUsingColNames() != null && !join.getUsingColNames().isEmpty()) {
Set<String> usingNamesLower = join.getUsingColNames().stream()
.map(String::toLowerCase)
.collect(Collectors.toSet());
Set<String> seen = new HashSet<>();
List<Field> deduplicated = new ArrayList<>();
for (Field field : scope.getRelationFields().getAllFields()) {
String fieldName = field.getName();
if (fieldName != null) {
String lower = fieldName.toLowerCase();
if (usingNamesLower.contains(lower)) {
if (seen.contains(lower)) {
// skip duplicate USING column from the right side
continue;
}
seen.add(lower);
}
}
deduplicated.add(field);
}
scope = new Scope(scope.getRelationId(), new RelationFields(deduplicated));
}
join.setScope(scope);
GeneratedColumnExprMappingCollector collector = new GeneratedColumnExprMappingCollector();

View File

@ -91,7 +91,12 @@ public class AnalyzeJoinTest {
analyzeFail("select * from (t0 join tnotnull using(v1)) t , t1",
"Getting syntax error at line 1, column 43. Detail message: Unexpected input 't', " +
"the most similar input is {<EOF>, ';'}.");
analyzeFail("select v1 from (t0 join tnotnull using(v1)), t1", "Column 'v1' is ambiguous");
// Using columns should be coalesced and appear only once in output names
QueryRelation query1 = ((QueryStatement) analyzeSuccess(
"select * from t0 a join t0 b using(v1)")).getQueryRelation();
Assertions.assertEquals("v1,v2,v3,v2,v3", String.join(",", query1.getColumnOutputNames()));
// Unqualified reference of using column should be unambiguous
analyzeSuccess("select v1 from (t0 join tnotnull using(v1)), t1");
analyzeSuccess("select a.v1 from (t0 a join tnotnull b using(v1)), t1");
}