Summary
- Location:
src/query/ast/src/parser/parser.rs:218 (inside assert_reparse)
- Panic message:
error: join condition already set
- Occurrences: 2
Likely trigger files
The panic SQL is captured verbatim in the log. These come from original DuckDB source tests
that the converter validated against the running server:
Case A — DuckDB source: test/sql/join/inner/test_using_join.test (or similar)
SELECT * FROM (VALUES(NULL)) AS tbl(i)
INNER JOIN (VALUES(NULL)) AS tbl2(i)
INNER JOIN (VALUES(NULL)) AS tbl3(i) ON tbl2.i = tbl3.i USING(i);
Case B — DuckDB source: test/sql/join/left_outer/left_join_issue_15316.test
Converted file: sql/join/left_outer/left_join_issue_15316.test (query already commented out as TODO: unsupported feature)
SELECT i1.i AS i1_i, i2.s, i3.i AS i3_i
FROM integers1 AS i1
LEFT OUTER JOIN integers2 AS i2
LEFT OUTER JOIN integers3 AS i3 ON i2.i = i3.i ON NULL;
Related abort files (server crashed before these could be processed):
test/sql/join/left_outer/test_left_outer.test (abort) — contains ON NULL<>NULL pattern
test/sql/join/right_outer/test_right_outer.test (abort) — contains ON NULL<>NULL pattern
test/sql/join/inner/test_join.test (pass) — contains ON NULL = 2 but parsed successfully
Reproduction
-- Case A: INNER JOIN chain with ON + USING
SELECT * FROM (VALUES(NULL)) AS tbl(i)
INNER JOIN (VALUES(NULL)) AS tbl2(i)
INNER JOIN (VALUES(NULL)) AS tbl3(i) ON tbl2.i = tbl3.i USING(i);
-- Case B: LEFT OUTER JOIN chain with multiple ON clauses
SELECT i1.i AS i1_i, i2.s, i3.i AS i3_i
FROM integers1 AS i1
LEFT OUTER JOIN integers2 AS i2
LEFT OUTER JOIN integers3 AS i3 ON i2.i = i3.i ON NULL;
Call chain
Planner::parse_sql (planner.rs:173)
→ parse_sql (parser.rs:65)
→ assert_reparse (parser.rs:218) ← PANIC
Key stack frames
| # |
Function |
File |
| 11 |
assert_reparse |
parser.rs:218 |
| 14 |
parse_sql |
parser.rs:65 |
| 15 |
Planner::parse_sql |
planner.rs:173 |
Root cause
Databend's parser does not support DuckDB-style unparenthesized nested joins with multiple ON/USING clauses. The first parse succeeds, but assert_reparse (which re-parses the pretty-printed AST) fails because the re-serialized SQL loses the nesting structure. The assert_reparse function panics on parse failure instead of returning an error.
Fix direction
assert_reparse should return an error (or log a warning), never panic. A parse round-trip failure is a quality issue, not a crash-worthy event.
- Separately, consider whether to support this nested join syntax.
Summary
src/query/ast/src/parser/parser.rs:218(insideassert_reparse)error: join condition already setLikely trigger files
The panic SQL is captured verbatim in the log. These come from original DuckDB source tests
that the converter validated against the running server:
Case A — DuckDB source:
test/sql/join/inner/test_using_join.test(or similar)Case B — DuckDB source:
test/sql/join/left_outer/left_join_issue_15316.testConverted file:
sql/join/left_outer/left_join_issue_15316.test(query already commented out asTODO: unsupported feature)Related abort files (server crashed before these could be processed):
test/sql/join/left_outer/test_left_outer.test(abort) — containsON NULL<>NULLpatterntest/sql/join/right_outer/test_right_outer.test(abort) — containsON NULL<>NULLpatterntest/sql/join/inner/test_join.test(pass) — containsON NULL = 2but parsed successfullyReproduction
Call chain
Key stack frames
assert_reparseparser.rs:218parse_sqlparser.rs:65Planner::parse_sqlplanner.rs:173Root cause
Databend's parser does not support DuckDB-style unparenthesized nested joins with multiple ON/USING clauses. The first parse succeeds, but
assert_reparse(which re-parses the pretty-printed AST) fails because the re-serialized SQL loses the nesting structure. Theassert_reparsefunction panics on parse failure instead of returning an error.Fix direction
assert_reparseshould return an error (or log a warning), never panic. A parse round-trip failure is a quality issue, not a crash-worthy event.