Support NestedJoin with an alias (#551)

* add alias for nestedjoin

* fmt

* add/modify test cases

* inline nestedjoin instead of macro
This commit is contained in:
Wei-Ting Kuo 2022-08-04 07:41:15 +08:00 committed by GitHub
parent e24951e080
commit 076b587bb2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 91 additions and 37 deletions

View file

@ -375,7 +375,10 @@ pub enum TableFactor {
/// ///
/// The parser may also accept non-standard nesting of bare tables for some /// The parser may also accept non-standard nesting of bare tables for some
/// dialects, but the information about such nesting is stripped from AST. /// dialects, but the information about such nesting is stripped from AST.
NestedJoin(Box<TableWithJoins>), NestedJoin {
table_with_joins: Box<TableWithJoins>,
alias: Option<TableAlias>,
},
} }
impl fmt::Display for TableFactor { impl fmt::Display for TableFactor {
@ -438,7 +441,16 @@ impl fmt::Display for TableFactor {
} }
Ok(()) Ok(())
} }
TableFactor::NestedJoin(table_reference) => write!(f, "({})", table_reference), TableFactor::NestedJoin {
table_with_joins,
alias,
} => {
write!(f, "({})", table_with_joins)?;
if let Some(alias) = alias {
write!(f, " AS {}", alias)?;
}
Ok(())
}
} }
} }
} }

View file

@ -3869,12 +3869,24 @@ impl<'a> Parser<'a> {
#[allow(clippy::if_same_then_else)] #[allow(clippy::if_same_then_else)]
if !table_and_joins.joins.is_empty() { if !table_and_joins.joins.is_empty() {
self.expect_token(&Token::RParen)?; self.expect_token(&Token::RParen)?;
Ok(TableFactor::NestedJoin(Box::new(table_and_joins))) // (A) let alias = self.parse_optional_table_alias(keywords::RESERVED_FOR_TABLE_ALIAS)?;
} else if let TableFactor::NestedJoin(_) = &table_and_joins.relation { Ok(TableFactor::NestedJoin {
table_with_joins: Box::new(table_and_joins),
alias,
}) // (A)
} else if let TableFactor::NestedJoin {
table_with_joins: _,
alias: _,
} = &table_and_joins.relation
{
// (B): `table_and_joins` (what we found inside the parentheses) // (B): `table_and_joins` (what we found inside the parentheses)
// is a nested join `(foo JOIN bar)`, not followed by other joins. // is a nested join `(foo JOIN bar)`, not followed by other joins.
self.expect_token(&Token::RParen)?; self.expect_token(&Token::RParen)?;
Ok(TableFactor::NestedJoin(Box::new(table_and_joins))) let alias = self.parse_optional_table_alias(keywords::RESERVED_FOR_TABLE_ALIAS)?;
Ok(TableFactor::NestedJoin {
table_with_joins: Box::new(table_and_joins),
alias,
})
} else if dialect_of!(self is SnowflakeDialect | GenericDialect) { } else if dialect_of!(self is SnowflakeDialect | GenericDialect) {
// Dialect-specific behavior: Snowflake diverges from the // Dialect-specific behavior: Snowflake diverges from the
// standard and from most of the other implementations by // standard and from most of the other implementations by
@ -3893,7 +3905,8 @@ impl<'a> Parser<'a> {
TableFactor::Derived { alias, .. } TableFactor::Derived { alias, .. }
| TableFactor::Table { alias, .. } | TableFactor::Table { alias, .. }
| TableFactor::UNNEST { alias, .. } | TableFactor::UNNEST { alias, .. }
| TableFactor::TableFunction { alias, .. } => { | TableFactor::TableFunction { alias, .. }
| TableFactor::NestedJoin { alias, .. } => {
// but not `FROM (mytable AS alias1) AS alias2`. // but not `FROM (mytable AS alias1) AS alias2`.
if let Some(inner_alias) = alias { if let Some(inner_alias) = alias {
return Err(ParserError::ParserError(format!( return Err(ParserError::ParserError(format!(
@ -3906,7 +3919,6 @@ impl<'a> Parser<'a> {
// `(mytable AS alias)` // `(mytable AS alias)`
alias.replace(outer_alias); alias.replace(outer_alias);
} }
TableFactor::NestedJoin(_) => unreachable!(),
}; };
} }
// Do not store the extra set of parens in the AST // Do not store the extra set of parens in the AST

View file

@ -3446,32 +3446,45 @@ fn parse_joins_using() {
#[test] #[test]
fn parse_natural_join() { fn parse_natural_join() {
fn natural_join(f: impl Fn(JoinConstraint) -> JoinOperator) -> Join { fn natural_join(f: impl Fn(JoinConstraint) -> JoinOperator, alias: Option<TableAlias>) -> Join {
Join { Join {
relation: TableFactor::Table { relation: TableFactor::Table {
name: ObjectName(vec![Ident::new("t2")]), name: ObjectName(vec![Ident::new("t2")]),
alias: None, alias,
args: None, args: None,
with_hints: vec![], with_hints: vec![],
}, },
join_operator: f(JoinConstraint::Natural), join_operator: f(JoinConstraint::Natural),
} }
} }
// if not specified, inner join as default
assert_eq!( assert_eq!(
only(&verified_only_select("SELECT * FROM t1 NATURAL JOIN t2").from).joins, only(&verified_only_select("SELECT * FROM t1 NATURAL JOIN t2").from).joins,
vec![natural_join(JoinOperator::Inner)] vec![natural_join(JoinOperator::Inner, None)]
); );
// left join explicitly
assert_eq!( assert_eq!(
only(&verified_only_select("SELECT * FROM t1 NATURAL LEFT JOIN t2").from).joins, only(&verified_only_select("SELECT * FROM t1 NATURAL LEFT JOIN t2").from).joins,
vec![natural_join(JoinOperator::LeftOuter)] vec![natural_join(JoinOperator::LeftOuter, None)]
); );
// right join explicitly
assert_eq!( assert_eq!(
only(&verified_only_select("SELECT * FROM t1 NATURAL RIGHT JOIN t2").from).joins, only(&verified_only_select("SELECT * FROM t1 NATURAL RIGHT JOIN t2").from).joins,
vec![natural_join(JoinOperator::RightOuter)] vec![natural_join(JoinOperator::RightOuter, None)]
); );
// full join explicitly
assert_eq!( assert_eq!(
only(&verified_only_select("SELECT * FROM t1 NATURAL FULL JOIN t2").from).joins, only(&verified_only_select("SELECT * FROM t1 NATURAL FULL JOIN t2").from).joins,
vec![natural_join(JoinOperator::FullOuter)] vec![natural_join(JoinOperator::FullOuter, None)]
);
// natural join another table with alias
assert_eq!(
only(&verified_only_select("SELECT * FROM t1 NATURAL JOIN t2 AS t3").from).joins,
vec![natural_join(JoinOperator::Inner, table_alias("t3"))]
); );
let sql = "SELECT * FROM t1 natural"; let sql = "SELECT * FROM t1 natural";
@ -3519,6 +3532,21 @@ fn parse_join_nesting() {
from.joins, from.joins,
vec![join(nest!(nest!(nest!(table("b"), table("c")))))] vec![join(nest!(nest!(nest!(table("b"), table("c")))))]
); );
let sql = "SELECT * FROM (a NATURAL JOIN b) AS c";
let select = verified_only_select(sql);
let from = only(select.from);
assert_eq!(
from.relation,
TableFactor::NestedJoin {
table_with_joins: Box::new(TableWithJoins {
relation: table("a"),
joins: vec![join(table("b"))],
}),
alias: table_alias("c")
}
);
assert_eq!(from.joins, vec![]);
} }
#[test] #[test]
@ -3676,7 +3704,8 @@ fn parse_derived_tables() {
let from = only(select.from); let from = only(select.from);
assert_eq!( assert_eq!(
from.relation, from.relation,
TableFactor::NestedJoin(Box::new(TableWithJoins { TableFactor::NestedJoin {
table_with_joins: Box::new(TableWithJoins {
relation: TableFactor::Derived { relation: TableFactor::Derived {
lateral: false, lateral: false,
subquery: Box::new(verified_query("(SELECT 1) UNION (SELECT 2)")), subquery: Box::new(verified_query("(SELECT 1) UNION (SELECT 2)")),
@ -3694,7 +3723,9 @@ fn parse_derived_tables() {
}, },
join_operator: JoinOperator::Inner(JoinConstraint::Natural), join_operator: JoinOperator::Inner(JoinConstraint::Natural),
}], }],
})) }),
alias: None
}
); );
} }

View file

@ -131,10 +131,9 @@ fn test_single_table_in_parenthesis_with_alias() {
"SELECT * FROM (a AS alias1 NATURAL JOIN b AS c)", "SELECT * FROM (a AS alias1 NATURAL JOIN b AS c)",
); );
let res = snowflake_and_generic().parse_sql_statements("SELECT * FROM (a NATURAL JOIN b) c"); snowflake_and_generic().one_statement_parses_to(
assert_eq!( "SELECT * FROM (a NATURAL JOIN b) c",
ParserError::ParserError("Expected end of statement, found: c".to_string()), "SELECT * FROM (a NATURAL JOIN b) AS c",
res.unwrap_err()
); );
let res = snowflake().parse_sql_statements("SELECT * FROM (a b) c"); let res = snowflake().parse_sql_statements("SELECT * FROM (a b) c");

View file

@ -26,9 +26,9 @@ pub use sqlparser::test_utils::*;
#[macro_export] #[macro_export]
macro_rules! nest { macro_rules! nest {
($base:expr $(, $join:expr)*) => { ($base:expr $(, $join:expr)*) => {
TableFactor::NestedJoin(Box::new(TableWithJoins { TableFactor::NestedJoin { table_with_joins: Box::new(TableWithJoins {
relation: $base, relation: $base,
joins: vec![$(join($join)),*] joins: vec![$(join($join)),*]
})) }), alias: None}
}; };
} }