diff --git a/src/dialect/postgresql.rs b/src/dialect/postgresql.rs index 5dc2dbcf4..91a2bd15c 100644 --- a/src/dialect/postgresql.rs +++ b/src/dialect/postgresql.rs @@ -295,6 +295,12 @@ impl Dialect for PostgreSqlDialect { true } + /// PostgreSQL supports right-deep join chains: `t0 JOIN t1 JOIN t2 ON c1 ON c2` + /// See: + fn supports_left_associative_joins_without_parens(&self) -> bool { + false + } + /// Postgres supports `NOTNULL` as an alias for `IS NOT NULL` /// See: fn supports_notnull_operator(&self) -> bool { diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index aa8e37c04..7233a973d 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -9436,3 +9436,20 @@ fn exclude_as_column_name() { } } } + +#[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"); +}