File tree Expand file tree Collapse file tree 5 files changed +64
-1
lines changed Expand file tree Collapse file tree 5 files changed +64
-1
lines changed Original file line number Diff line number Diff line change @@ -52,6 +52,10 @@ impl Dialect for GenericDialect {
52
52
true
53
53
}
54
54
55
+ fn supports_left_associative_joins_without_parens ( & self ) -> bool {
56
+ true
57
+ }
58
+
55
59
fn supports_connect_by ( & self ) -> bool {
56
60
true
57
61
}
Original file line number Diff line number Diff line change @@ -278,6 +278,34 @@ pub trait Dialect: Debug + Any {
278
278
false
279
279
}
280
280
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
+
281
309
/// Returns true if the dialect supports the `(+)` syntax for OUTER JOIN.
282
310
fn supports_outer_join_operator ( & self ) -> bool {
283
311
false
Original file line number Diff line number Diff line change @@ -283,6 +283,10 @@ impl Dialect for SnowflakeDialect {
283
283
true
284
284
}
285
285
286
+ fn supports_left_associative_joins_without_parens ( & self ) -> bool {
287
+ false
288
+ }
289
+
286
290
fn is_reserved_for_identifier ( & self , kw : Keyword ) -> bool {
287
291
// Unreserve some keywords that Snowflake accepts as identifiers
288
292
// See: https://docs.snowflake.com/en/sql-reference/reserved-keywords
Original file line number Diff line number Diff line change @@ -12495,7 +12495,11 @@ impl<'a> Parser<'a> {
12495
12495
};
12496
12496
let mut relation = self.parse_table_factor()?;
12497
12497
12498
- if self.peek_parens_less_nested_join() {
12498
+ if !self
12499
+ .dialect
12500
+ .supports_left_associative_joins_without_parens()
12501
+ && self.peek_parens_less_nested_join()
12502
+ {
12499
12503
let joins = self.parse_joins()?;
12500
12504
relation = TableFactor::NestedJoin {
12501
12505
table_with_joins: Box::new(TableWithJoins { relation, joins }),
Original file line number Diff line number Diff line change @@ -15359,6 +15359,29 @@ fn check_enforced() {
15359
15359
);
15360
15360
}
15361
15361
15362
+ #[test]
15363
+ fn join_precedence() {
15364
+ all_dialects_except(|d| !d.supports_left_associative_joins_without_parens())
15365
+ .verified_query_with_canonical(
15366
+ "SELECT *
15367
+ FROM t1
15368
+ NATURAL JOIN t5
15369
+ INNER JOIN t0 ON (t0.v1 + t5.v0) > 0
15370
+ WHERE t0.v1 = t1.v0",
15371
+ // canonical string without parentheses
15372
+ "SELECT * FROM t1 NATURAL JOIN t5 INNER JOIN t0 ON (t0.v1 + t5.v0) > 0 WHERE t0.v1 = t1.v0",
15373
+ );
15374
+ all_dialects_except(|d| d.supports_left_associative_joins_without_parens()).verified_query_with_canonical(
15375
+ "SELECT *
15376
+ FROM t1
15377
+ NATURAL JOIN t5
15378
+ INNER JOIN t0 ON (t0.v1 + t5.v0) > 0
15379
+ WHERE t0.v1 = t1.v0",
15380
+ // canonical string with parentheses
15381
+ "SELECT * FROM t1 NATURAL JOIN (t5 INNER JOIN t0 ON (t0.v1 + t5.v0) > 0) WHERE t0.v1 = t1.v0",
15382
+ );
15383
+ }
15384
+
15362
15385
#[test]
15363
15386
fn parse_create_procedure_with_language() {
15364
15387
let sql = r#"CREATE PROCEDURE test_proc LANGUAGE sql AS BEGIN SELECT 1; END"#;
You can’t perform that action at this time.
0 commit comments