From 491ed323f9f57ba70c6a1aa7635f436d2251a3b0 Mon Sep 17 00:00:00 2001 From: Michael Victor Zink Date: Thu, 13 Feb 2025 15:58:11 -0800 Subject: [PATCH] Differentiate LEFT JOIN from LEFT OUTER JOIN And same for RIGHT JOIN. Although these are generally syntactically equivalent, I believe it is preferable to preserve the original input when rendering. --- src/ast/query.rs | 20 +++++++++++-- src/ast/spans.rs | 2 ++ src/parser/mod.rs | 6 ++-- tests/sqlparser_common.rs | 60 +++++++++++++++++++++++++++------------ 4 files changed, 65 insertions(+), 23 deletions(-) diff --git a/src/ast/query.rs b/src/ast/query.rs index 0446bcb75..004b90558 100644 --- a/src/ast/query.rs +++ b/src/ast/query.rs @@ -2077,20 +2077,34 @@ impl fmt::Display for Join { self.relation, suffix(constraint) ), - JoinOperator::LeftOuter(constraint) => write!( + JoinOperator::Left(constraint) => write!( f, " {}LEFT JOIN {}{}", prefix(constraint), self.relation, suffix(constraint) ), - JoinOperator::RightOuter(constraint) => write!( + JoinOperator::LeftOuter(constraint) => write!( + f, + " {}LEFT OUTER JOIN {}{}", + prefix(constraint), + self.relation, + suffix(constraint) + ), + JoinOperator::Right(constraint) => write!( f, " {}RIGHT JOIN {}{}", prefix(constraint), self.relation, suffix(constraint) ), + JoinOperator::RightOuter(constraint) => write!( + f, + " {}RIGHT OUTER JOIN {}{}", + prefix(constraint), + self.relation, + suffix(constraint) + ), JoinOperator::FullOuter(constraint) => write!( f, " {}FULL JOIN {}{}", @@ -2162,7 +2176,9 @@ impl fmt::Display for Join { pub enum JoinOperator { Join(JoinConstraint), Inner(JoinConstraint), + Left(JoinConstraint), LeftOuter(JoinConstraint), + Right(JoinConstraint), RightOuter(JoinConstraint), FullOuter(JoinConstraint), CrossJoin, diff --git a/src/ast/spans.rs b/src/ast/spans.rs index 18f6f6e2f..574478692 100644 --- a/src/ast/spans.rs +++ b/src/ast/spans.rs @@ -2010,7 +2010,9 @@ impl Spanned for JoinOperator { match self { JoinOperator::Join(join_constraint) => join_constraint.span(), JoinOperator::Inner(join_constraint) => join_constraint.span(), + JoinOperator::Left(join_constraint) => join_constraint.span(), JoinOperator::LeftOuter(join_constraint) => join_constraint.span(), + JoinOperator::Right(join_constraint) => join_constraint.span(), JoinOperator::RightOuter(join_constraint) => join_constraint.span(), JoinOperator::FullOuter(join_constraint) => join_constraint.span(), JoinOperator::CrossJoin => Span::empty(), diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 6d3290d23..6fc64a85e 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -5740,7 +5740,7 @@ impl<'a> Parser<'a> { drop_behavior, }) } - /// ```sql + /// ```sql /// DROP CONNECTOR [IF EXISTS] name /// ``` /// @@ -11182,9 +11182,9 @@ impl<'a> Parser<'a> { } Some(Keyword::JOIN) => { if is_left { - JoinOperator::LeftOuter + JoinOperator::Left } else { - JoinOperator::RightOuter + JoinOperator::Right } } _ => { diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index aef4d0d72..32bdd40d5 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -6575,8 +6575,16 @@ fn parse_joins_on() { only(&verified_only_select("SELECT * FROM t1 JOIN t2 ON c1 = c2").from).joins, vec![join_with_constraint("t2", None, false, JoinOperator::Join)] ); + assert_eq!( + only(&verified_only_select("SELECT * FROM t1 INNER JOIN t2 ON c1 = c2").from).joins, + vec![join_with_constraint("t2", None, false, JoinOperator::Inner)] + ); assert_eq!( only(&verified_only_select("SELECT * FROM t1 LEFT JOIN t2 ON c1 = c2").from).joins, + vec![join_with_constraint("t2", None, false, JoinOperator::Left)] + ); + assert_eq!( + only(&verified_only_select("SELECT * FROM t1 LEFT OUTER JOIN t2 ON c1 = c2").from).joins, vec![join_with_constraint( "t2", None, @@ -6586,6 +6594,10 @@ fn parse_joins_on() { ); assert_eq!( only(&verified_only_select("SELECT * FROM t1 RIGHT JOIN t2 ON c1 = c2").from).joins, + vec![join_with_constraint("t2", None, false, JoinOperator::Right)] + ); + assert_eq!( + only(&verified_only_select("SELECT * FROM t1 RIGHT OUTER JOIN t2 ON c1 = c2").from).joins, vec![join_with_constraint( "t2", None, @@ -6708,10 +6720,18 @@ fn parse_joins_using() { ); assert_eq!( only(&verified_only_select("SELECT * FROM t1 LEFT JOIN t2 USING(c1)").from).joins, + vec![join_with_constraint("t2", None, JoinOperator::Left)] + ); + assert_eq!( + only(&verified_only_select("SELECT * FROM t1 LEFT OUTER JOIN t2 USING(c1)").from).joins, vec![join_with_constraint("t2", None, JoinOperator::LeftOuter)] ); assert_eq!( only(&verified_only_select("SELECT * FROM t1 RIGHT JOIN t2 USING(c1)").from).joins, + vec![join_with_constraint("t2", None, JoinOperator::Right)] + ); + assert_eq!( + only(&verified_only_select("SELECT * FROM t1 RIGHT OUTER JOIN t2 USING(c1)").from).joins, vec![join_with_constraint("t2", None, JoinOperator::RightOuter)] ); assert_eq!( @@ -6771,20 +6791,34 @@ fn parse_natural_join() { only(&verified_only_select("SELECT * FROM t1 NATURAL JOIN t2").from).joins, vec![natural_join(JoinOperator::Join, None)] ); + // inner join explicitly assert_eq!( only(&verified_only_select("SELECT * FROM t1 NATURAL INNER JOIN t2").from).joins, vec![natural_join(JoinOperator::Inner, None)] ); + // left join explicitly assert_eq!( only(&verified_only_select("SELECT * FROM t1 NATURAL LEFT JOIN t2").from).joins, + vec![natural_join(JoinOperator::Left, None)] + ); + + // left outer join explicitly + assert_eq!( + only(&verified_only_select("SELECT * FROM t1 NATURAL LEFT OUTER JOIN t2").from).joins, vec![natural_join(JoinOperator::LeftOuter, None)] ); // right join explicitly assert_eq!( only(&verified_only_select("SELECT * FROM t1 NATURAL RIGHT JOIN t2").from).joins, + vec![natural_join(JoinOperator::Right, None)] + ); + + // right outer join explicitly + assert_eq!( + only(&verified_only_select("SELECT * FROM t1 NATURAL RIGHT OUTER JOIN t2").from).joins, vec![natural_join(JoinOperator::RightOuter, None)] ); @@ -6864,22 +6898,12 @@ fn parse_join_nesting() { #[test] fn parse_join_syntax_variants() { - one_statement_parses_to( - "SELECT c1 FROM t1 JOIN t2 USING(c1)", - "SELECT c1 FROM t1 JOIN t2 USING(c1)", - ); - one_statement_parses_to( - "SELECT c1 FROM t1 INNER JOIN t2 USING(c1)", - "SELECT c1 FROM t1 INNER JOIN t2 USING(c1)", - ); - one_statement_parses_to( - "SELECT c1 FROM t1 LEFT OUTER JOIN t2 USING(c1)", - "SELECT c1 FROM t1 LEFT JOIN t2 USING(c1)", - ); - one_statement_parses_to( - "SELECT c1 FROM t1 RIGHT OUTER JOIN t2 USING(c1)", - "SELECT c1 FROM t1 RIGHT JOIN t2 USING(c1)", - ); + verified_stmt("SELECT c1 FROM t1 JOIN t2 USING(c1)"); + verified_stmt("SELECT c1 FROM t1 INNER JOIN t2 USING(c1)"); + verified_stmt("SELECT c1 FROM t1 LEFT JOIN t2 USING(c1)"); + verified_stmt("SELECT c1 FROM t1 LEFT OUTER JOIN t2 USING(c1)"); + verified_stmt("SELECT c1 FROM t1 RIGHT JOIN t2 USING(c1)"); + verified_stmt("SELECT c1 FROM t1 RIGHT OUTER JOIN t2 USING(c1)"); one_statement_parses_to( "SELECT c1 FROM t1 FULL OUTER JOIN t2 USING(c1)", "SELECT c1 FROM t1 FULL JOIN t2 USING(c1)", @@ -7941,7 +7965,7 @@ fn lateral_derived() { let join = &from.joins[0]; assert_eq!( join.join_operator, - JoinOperator::LeftOuter(JoinConstraint::On(Expr::Value(test_utils::number("1")))) + JoinOperator::Left(JoinConstraint::On(Expr::Value(test_utils::number("1")))) ); if let TableFactor::Derived { lateral, @@ -8009,7 +8033,7 @@ fn lateral_function() { alias: None, }, global: false, - join_operator: JoinOperator::LeftOuter(JoinConstraint::None), + join_operator: JoinOperator::Left(JoinConstraint::None), }], }], lateral_views: vec![],