Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/dialect/postgresql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,12 @@ impl Dialect for PostgreSqlDialect {
true
}

/// PostgreSQL supports right-deep join chains: `t0 JOIN t1 JOIN t2 ON c1 ON c2`
/// See: <https://www.postgresql.org/docs/current/queries-table-expressions.html#QUERIES-JOIN>
fn supports_left_associative_joins_without_parens(&self) -> bool {
false
}

/// Postgres supports `NOTNULL` as an alias for `IS NOT NULL`
/// See: <https://www.postgresql.org/docs/17/functions-comparison.html>
fn supports_notnull_operator(&self) -> bool {
Expand Down
17 changes: 17 additions & 0 deletions tests/sqlparser_postgres.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9261,3 +9261,20 @@ fn parse_lock_table() {
}
}
}

#[test]
fn parse_right_deep_join_chain() {
// PostgreSQL supports right-deep join syntax where ON clauses follow all JOIN keywords:
// t0 JOIN t1 JOIN t2 ON c1 ON c2
// which is equivalent to (and serialized as) t0 JOIN (t1 JOIN t2 ON c1) ON c2.
pg().one_statement_parses_to(
"SELECT * FROM t0 INNER JOIN t1 INNER JOIN t2 ON true ON true",
"SELECT * FROM t0 INNER JOIN (t1 INNER JOIN t2 ON true) ON true",
);
pg().one_statement_parses_to(
"SELECT * FROM t0 INNER JOIN t1 INNER JOIN t2 INNER JOIN t3 ON true ON true ON true",
"SELECT * FROM t0 INNER JOIN (t1 INNER JOIN (t2 INNER JOIN t3 ON true) ON true) ON true",
);
// NATURAL JOIN followed by a constrained join must stay left-associative.
pg().verified_stmt("SELECT * FROM t0 NATURAL JOIN t1 INNER JOIN t2 ON true");
}
Loading