Skip to content

Commit 5724cf3

Browse files
committed
Handle derived tables with set operations
This commit adds support for derived tables (i.e., subqueries) that incorporate set operations, like: SELECT * FROM (((SELECT 1) UNION (SELECT 2)) t1 AS NATURAL JOIN t2) This introduces a bit of complexity around determining whether a left paren starts a subquery, starts a nested join, or belongs to an already-started subquery. The details are explained in a comment within the patch.
1 parent 5896f01 commit 5724cf3

File tree

2 files changed

+86
-23
lines changed

2 files changed

+86
-23
lines changed

src/sqlparser.rs

Lines changed: 57 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ pub enum IsOptional {
4242
}
4343
use IsOptional::*;
4444

45+
pub enum IsLateral {
46+
Lateral,
47+
NotLateral,
48+
}
49+
use IsLateral::*;
50+
4551
impl From<TokenizerError> for ParserError {
4652
fn from(e: TokenizerError) -> Self {
4753
ParserError::TokenizerError(format!("{:?}", e))
@@ -1670,30 +1676,42 @@ impl Parser {
16701676

16711677
/// A table name or a parenthesized subquery, followed by optional `[AS] alias`
16721678
pub fn parse_table_factor(&mut self) -> Result<TableFactor, ParserError> {
1673-
let lateral = self.parse_keyword("LATERAL");
1679+
if self.parse_keyword("LATERAL") {
1680+
// LATERAL must always be followed by a subquery.
1681+
if !self.consume_token(&Token::LParen) {
1682+
self.expected("subquery after LATERAL", self.peek_token())?;
1683+
}
1684+
return self.parse_derived_table_factor(Lateral);
1685+
}
1686+
16741687
if self.consume_token(&Token::LParen) {
1675-
if self.parse_keyword("SELECT")
1676-
|| self.parse_keyword("WITH")
1677-
|| self.parse_keyword("VALUES")
1678-
{
1679-
self.prev_token();
1680-
let subquery = Box::new(self.parse_query()?);
1681-
self.expect_token(&Token::RParen)?;
1682-
let alias = self.parse_optional_table_alias(keywords::RESERVED_FOR_TABLE_ALIAS)?;
1683-
Ok(TableFactor::Derived {
1684-
lateral,
1685-
subquery,
1686-
alias,
1687-
})
1688-
} else if lateral {
1689-
parser_err!("Expected subquery after LATERAL, found nested join".to_string())
1690-
} else {
1691-
let table_reference = self.parse_table_and_joins()?;
1692-
self.expect_token(&Token::RParen)?;
1693-
Ok(TableFactor::NestedJoin(Box::new(table_reference)))
1688+
let index = self.index;
1689+
// A left paren introduces either a derived table (i.e., a subquery)
1690+
// or a nested join. It's nearly impossible to determine ahead of
1691+
// time which it is... so we just try to parse both.
1692+
//
1693+
// Here's an example that demonstrates the complexity:
1694+
//
1695+
// SELECT * FROM ( ( (SELECT 1) UNION (SELECT 2) ) AS t1 NATURAL JOIN t2 )
1696+
// ^ ^ ^
1697+
// | | |
1698+
// | | belongs to the subquery
1699+
// | starts a derived table (subquery)
1700+
// starts a nested join
1701+
//
1702+
match self.parse_derived_table_factor(NotLateral) {
1703+
Ok(table_factor) => Ok(table_factor),
1704+
Err(_) => {
1705+
// It wasn't a derived table. Ignore the error and back up
1706+
// to where we were before. Either we'll be able to parse a
1707+
// valid nested join, or we won't, and we'll return that
1708+
// error instead.
1709+
self.index = index;
1710+
let table_and_joins = self.parse_table_and_joins()?;
1711+
self.expect_token(&Token::RParen)?;
1712+
Ok(TableFactor::NestedJoin(Box::new(table_and_joins)))
1713+
}
16941714
}
1695-
} else if lateral {
1696-
self.expected("subquery after LATERAL", self.peek_token())
16971715
} else {
16981716
let name = self.parse_object_name()?;
16991717
// Postgres, MSSQL: table-valued functions:
@@ -1723,6 +1741,23 @@ impl Parser {
17231741
}
17241742
}
17251743

1744+
pub fn parse_derived_table_factor(
1745+
&mut self,
1746+
lateral: IsLateral,
1747+
) -> Result<TableFactor, ParserError> {
1748+
let subquery = Box::new(self.parse_query()?);
1749+
self.expect_token(&Token::RParen)?;
1750+
let alias = self.parse_optional_table_alias(keywords::RESERVED_FOR_TABLE_ALIAS)?;
1751+
Ok(TableFactor::Derived {
1752+
lateral: match lateral {
1753+
Lateral => true,
1754+
NotLateral => false,
1755+
},
1756+
subquery,
1757+
alias,
1758+
})
1759+
}
1760+
17261761
fn parse_join_constraint(&mut self, natural: bool) -> Result<JoinConstraint, ParserError> {
17271762
if natural {
17281763
Ok(JoinConstraint::Natural)

tests/sqlparser_common.rs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1837,6 +1837,32 @@ fn parse_derived_tables() {
18371837
let sql = "SELECT * FROM t NATURAL JOIN (((SELECT 1)))";
18381838
let _ = verified_only_select(sql);
18391839
// TODO: add assertions
1840+
1841+
let sql = "SELECT * FROM (((SELECT 1) UNION (SELECT 2)) AS t1 NATURAL JOIN t2)";
1842+
let select = verified_only_select(sql);
1843+
let from = only(select.from);
1844+
assert_eq!(
1845+
from.relation,
1846+
TableFactor::NestedJoin(Box::new(TableWithJoins {
1847+
relation: TableFactor::Derived {
1848+
lateral: false,
1849+
subquery: Box::new(verified_query("(SELECT 1) UNION (SELECT 2)")),
1850+
alias: Some(TableAlias {
1851+
name: "t1".into(),
1852+
columns: vec![],
1853+
})
1854+
},
1855+
joins: vec![Join {
1856+
relation: TableFactor::Table {
1857+
name: SQLObjectName(vec!["t2".into()]),
1858+
alias: None,
1859+
args: vec![],
1860+
with_hints: vec![],
1861+
},
1862+
join_operator: JoinOperator::Inner(JoinConstraint::Natural),
1863+
}],
1864+
}))
1865+
)
18401866
}
18411867

18421868
#[test]
@@ -2349,7 +2375,9 @@ fn lateral_derived() {
23492375
let sql = "SELECT * FROM a LEFT JOIN LATERAL (b CROSS JOIN c)";
23502376
let res = parse_sql_statements(sql);
23512377
assert_eq!(
2352-
ParserError::ParserError("Expected subquery after LATERAL, found nested join".to_string()),
2378+
ParserError::ParserError(
2379+
"Expected SELECT or a subquery in the query body, found: b".to_string()
2380+
),
23532381
res.unwrap_err()
23542382
);
23552383
}

0 commit comments

Comments
 (0)