Skip to content

Commit 078bed4

Browse files
PostgreSQL: support right-deep join chains (deferred ON clauses)
PostgreSQL accepts `t0 JOIN t1 JOIN t2 ON c1 ON c2` where ON clauses follow all JOIN keywords, associating right-to-left. Enable this by overriding supports_left_associative_joins_without_parens() to false for PostgresqlDialect, matching Snowflake's existing behavior. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent b376022 commit 078bed4

2 files changed

Lines changed: 23 additions & 0 deletions

File tree

src/dialect/postgresql.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,12 @@ impl Dialect for PostgreSqlDialect {
290290
true
291291
}
292292

293+
/// PostgreSQL supports right-deep join chains: `t0 JOIN t1 JOIN t2 ON c1 ON c2`
294+
/// See: <https://www.postgresql.org/docs/current/queries-table-expressions.html#QUERIES-JOIN>
295+
fn supports_left_associative_joins_without_parens(&self) -> bool {
296+
false
297+
}
298+
293299
/// Postgres supports `NOTNULL` as an alias for `IS NOT NULL`
294300
/// See: <https://www.postgresql.org/docs/17/functions-comparison.html>
295301
fn supports_notnull_operator(&self) -> bool {

tests/sqlparser_postgres.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9261,3 +9261,20 @@ fn parse_lock_table() {
92619261
}
92629262
}
92639263
}
9264+
9265+
#[test]
9266+
fn parse_right_deep_join_chain() {
9267+
// PostgreSQL supports right-deep join syntax where ON clauses follow all JOIN keywords:
9268+
// t0 JOIN t1 JOIN t2 ON c1 ON c2
9269+
// which is equivalent to (and serialized as) t0 JOIN (t1 JOIN t2 ON c1) ON c2.
9270+
pg().one_statement_parses_to(
9271+
"SELECT * FROM t0 INNER JOIN t1 INNER JOIN t2 ON true ON true",
9272+
"SELECT * FROM t0 INNER JOIN (t1 INNER JOIN t2 ON true) ON true",
9273+
);
9274+
pg().one_statement_parses_to(
9275+
"SELECT * FROM t0 INNER JOIN t1 INNER JOIN t2 INNER JOIN t3 ON true ON true ON true",
9276+
"SELECT * FROM t0 INNER JOIN (t1 INNER JOIN (t2 INNER JOIN t3 ON true) ON true) ON true",
9277+
);
9278+
// NATURAL JOIN followed by a constrained join must stay left-associative.
9279+
pg().verified_stmt("SELECT * FROM t0 NATURAL JOIN t1 INNER JOIN t2 ON true");
9280+
}

0 commit comments

Comments
 (0)