Skip to content

Commit dae0a12

Browse files
committed
introduce dialect nested joins flag
1 parent b42f58d commit dae0a12

File tree

5 files changed

+44
-3
lines changed

5 files changed

+44
-3
lines changed

src/dialect/generic.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ impl Dialect for GenericDialect {
5252
true
5353
}
5454

55+
fn supports_left_associative_joins_without_parens(&self) -> bool {
56+
true
57+
}
58+
5559
fn supports_connect_by(&self) -> bool {
5660
true
5761
}

src/dialect/mod.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,34 @@ pub trait Dialect: Debug + Any {
278278
false
279279
}
280280

281+
/// Indicates whether the dialect supports left-associative join parsing
282+
/// by default when parentheses are omitted in nested joins.
283+
///
284+
/// Most dialects (like MySQL or Postgres) assume **left-associative** precedence,
285+
/// so a query like:
286+
///
287+
/// ```sql
288+
/// SELECT * FROM t1 NATURAL JOIN t5 INNER JOIN t0 ON ...
289+
/// ```
290+
/// is interpreted as:
291+
/// ```sql
292+
/// ((t1 NATURAL JOIN t5) INNER JOIN t0 ON ...)
293+
/// ```
294+
/// and internally represented as a **flat list** of joins.
295+
///
296+
/// In contrast, some dialects (e.g. **Snowflake**) assume **right-associative**
297+
/// precedence and interpret the same query as:
298+
/// ```sql
299+
/// (t1 NATURAL JOIN (t5 INNER JOIN t0 ON ...))
300+
/// ```
301+
/// which results in a **nested join** structure in the AST.
302+
///
303+
/// If this method returns `false`, the parser must build nested join trees
304+
/// even in the absence of parentheses to reflect the correct associativity
305+
fn supports_left_associative_joins_without_parens(&self) -> bool {
306+
true
307+
}
308+
281309
/// Returns true if the dialect supports the `(+)` syntax for OUTER JOIN.
282310
fn supports_outer_join_operator(&self) -> bool {
283311
false

src/dialect/snowflake.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,10 @@ impl Dialect for SnowflakeDialect {
283283
true
284284
}
285285

286+
fn supports_left_associative_joins_without_parens(&self) -> bool {
287+
false
288+
}
289+
286290
fn is_reserved_for_identifier(&self, kw: Keyword) -> bool {
287291
// Unreserve some keywords that Snowflake accepts as identifiers
288292
// See: https://docs.snowflake.com/en/sql-reference/reserved-keywords

src/parser/mod.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12486,7 +12486,11 @@ impl<'a> Parser<'a> {
1248612486
};
1248712487
let mut relation = self.parse_table_factor()?;
1248812488

12489-
if dialect_of!(self is SnowflakeDialect) && self.peek_parens_less_nested_join() {
12489+
if !self
12490+
.dialect
12491+
.supports_left_associative_joins_without_parens()
12492+
&& self.peek_parens_less_nested_join()
12493+
{
1249012494
let joins = self.parse_joins()?;
1249112495
relation = TableFactor::NestedJoin {
1249212496
table_with_joins: Box::new(TableWithJoins { relation, joins }),

tests/sqlparser_common.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15359,7 +15359,8 @@ fn check_enforced() {
1535915359

1536015360
#[test]
1536115361
fn join_precedence() {
15362-
all_dialects_except(|d| d.is::<SnowflakeDialect>()).verified_query_with_canonical(
15362+
all_dialects_except(|d| !d.supports_left_associative_joins_without_parens())
15363+
.verified_query_with_canonical(
1536315364
"SELECT *
1536415365
FROM t1
1536515366
NATURAL JOIN t5
@@ -15368,7 +15369,7 @@ fn join_precedence() {
1536815369
// canonical string without parentheses
1536915370
"SELECT * FROM t1 NATURAL JOIN t5 INNER JOIN t0 ON (t0.v1 + t5.v0) > 0 WHERE t0.v1 = t1.v0",
1537015371
);
15371-
TestedDialects::new(vec![Box::new(SnowflakeDialect {})]).verified_query_with_canonical(
15372+
all_dialects_except(|d| d.supports_left_associative_joins_without_parens()).verified_query_with_canonical(
1537215373
"SELECT *
1537315374
FROM t1
1537415375
NATURAL JOIN t5

0 commit comments

Comments
 (0)