Skip to content

SQL Parser panics on nested JOIN with multiple conditions #19571

@TCeason

Description

@TCeason

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent-issueAgent-created issue marker

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions