diff --git a/src/sqlast/query.rs b/src/sqlast/query.rs index d6c112bc..967f23b6 100644 --- a/src/sqlast/query.rs +++ b/src/sqlast/query.rs @@ -238,6 +238,10 @@ pub enum TableFactor { subquery: Box, alias: Option, }, + /// Represents a parenthesized join expression, such as + /// `(foo bar [ baz ... ])`. + /// The inner `TableWithJoins` can have no joins only if its + /// `relation` is itself a `TableFactor::NestedJoin`. NestedJoin(Box), } diff --git a/src/sqlparser.rs b/src/sqlparser.rs index b637f8be..dc1cc658 100644 --- a/src/sqlparser.rs +++ b/src/sqlparser.rs @@ -1719,6 +1719,16 @@ impl Parser { // we won't, and we'll return that error instead. self.index = index; let table_and_joins = self.parse_table_and_joins()?; + match table_and_joins.relation { + TableFactor::NestedJoin { .. } => (), + _ => { + if table_and_joins.joins.is_empty() { + // The SQL spec prohibits derived tables and bare + // tables from appearing alone in parentheses. + self.expected("joined table", self.peek_token())? + } + } + } self.expect_token(&Token::RParen)?; Ok(TableFactor::NestedJoin(Box::new(table_and_joins))) } diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index a539cfcd..83322350 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -1742,6 +1742,12 @@ fn parse_join_nesting() { from.joins, vec![join(nest!(nest!(nest!(table("b"), table("c")))))] ); + + let res = parse_sql_statements("SELECT * FROM (a NATURAL JOIN (b))"); + assert_eq!( + ParserError::ParserError("Expected joined table, found: )".to_string()), + res.unwrap_err() + ); } #[test] @@ -1873,7 +1879,13 @@ fn parse_derived_tables() { join_operator: JoinOperator::Inner(JoinConstraint::Natural), }], })) - ) + ); + + let res = parse_sql_statements("SELECT * FROM ((SELECT 1) AS t)"); + assert_eq!( + ParserError::ParserError("Expected joined table, found: )".to_string()), + res.unwrap_err() + ); } #[test]