Skip to content

Commit 4d15d72

Browse files
author
krooswu
committed
Update comment
1 parent eb97c36 commit 4d15d72

File tree

1 file changed

+21
-15
lines changed

1 file changed

+21
-15
lines changed

core/src/main/java/org/apache/calcite/sql/dialect/ClickHouseSqlDialect.java

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -418,35 +418,41 @@ private static void unparseFloor(SqlWriter writer, SqlCall call) {
418418
}
419419
Join join = (Join) rel;
420420

421-
// ClickHouse primarily requires wrapping the right side of a JOIN
422-
// when it contains a nested JOIN
423-
return containsJoinRecursive(join.getRight());
421+
// ClickHouse requires wrapping the right-side input if it's a JOIN
422+
// to ensure that internal table qualifiers are flattened into explicit aliases.
423+
// This solves the Code 47 UNKNOWN_IDENTIFIER error.
424+
RelNode right = join.getRight();
425+
426+
// If the right side is a Join or a Project containing a Join, it needs aliasing protection
427+
return right instanceof Join || containsJoinRecursive(right);
424428
}
425429

426430
/**
427431
* Checks whether the given RelNode contains a JOIN that is directly exposed
428-
* at the JOIN boundary, possibly wrapped by a small number of transparent
429-
* single-input operators (e.g. Project, Filter, Sort).
432+
* to the outer scope, which could lead to "Unknown Identifier" errors in ClickHouse.
430433
*
431-
* <p>This method intentionally does NOT perform a full tree traversal.
432-
* ClickHouse only requires wrapping when a JOIN appears directly as a JOIN input;
433-
* JOINs deeper in the subtree do not trigger the restriction.
434+
* <p>ClickHouse (v25.x+) has strict scoping rules: when a JOIN appears on the
435+
* right side of another JOIN, internal table qualifiers (e.g., 'd2.loc') are
436+
* stripped and become invisible to the outer query unless they are explicitly
437+
* aliased within a subquery.
434438
*
435-
* <p>Therefore, a full RelVisitor is avoided here to prevent over-detection
436-
* and unnecessary wrapping.
439+
* <p>We only check for JOINs wrapped by transparent single-input operators
440+
* (Project, Filter, Sort) because these operators are typically collapsed
441+
* into the same SELECT block, exposing the problematic JOIN structure to
442+
* the outer boundary.
437443
*/
438444
private static boolean containsJoinRecursive(RelNode rel) {
439445
if (rel instanceof Join || rel instanceof Correlate) {
440446
return true;
441447
}
442448

443-
// Look through transparent single-input operators
444-
// These don't create subquery boundaries in the SQL
449+
// Look through transparent single-input operators.
450+
// We exclude Aggregate here because it naturally triggers a subquery
451+
// boundary in RelToSqlConverter, which already provides the necessary isolation.
445452
if (rel instanceof Project
446453
|| rel instanceof Filter
447-
|| rel instanceof Sort
448-
|| rel instanceof Aggregate) {
449-
return containsJoinRecursive(rel.getInput(0));
454+
|| rel instanceof Sort) {
455+
return rel.getInputs().size() == 1 && containsJoinRecursive(rel.getInput(0));
450456
}
451457

452458
return false;

0 commit comments

Comments
 (0)