[snowflake] Support specifying an alias after FROM (table_factor)

Snowflake diverges from the standard and from most of the other
implementations by allowing extra parentheses not only around a join,
but around lone table names (e.g. `FROM (mytable [AS alias])`) and
around derived tables (e.g. `FROM ((SELECT ...)  [AS alias])`) as well.

Initially this was implemented in https://github.com/ballista-compute/sqlparser-rs/issues/154
by (ab)using `TableFactor::NestedJoin` to represent anything nested in
extra set of parens.

Afterwards we learned in https://github.com/ballista-compute/sqlparser-rs/issues/223
that in cases of such extraneous nesting Snowflake allows specifying the
alias both inside and outside parens, but not both - consider:

    FROM (table_factor AS inner_alias) AS outer_alias

We've considered implementing this by changing `TableFactor::NestedJoin`
to a `TableFactor::Nested { inner: TableWithJoins, alias:
Option<TableAlias> }`, but that seemed too generic, as no known dialect
supports duplicate aliases, as shown above, nor naming nested joins
`(foo NATURAL JOIN bar) alias`. So we decided on making a smaller change
(with no modifications to the AST), that is also more appropriate to the
contributors to the Snowflake dialect:


1) Revert #154 by rejecting `FROM (table or derived table)` in most dialects.

2) For `dialect_of!(self is SnowflakeDialect | GenericDialect)` parse
and strip the extraneous parentheses, e.g.

   `(mytable) AS alias` -> `(mytable AS alias)`


Co-authored-by: Eyal Leshem <eyal@satoricyber.com>
This commit is contained in:
Nickolay Ponomarev 2020-10-13 09:29:16 +03:00
parent d9e044aabb
commit ad72cda6b0
5 changed files with 131 additions and 46 deletions

View file

@ -2156,14 +2156,58 @@ impl<'a> Parser<'a> {
// recently consumed does not start a derived table (cases 1, 2, or 4).
// `maybe_parse` will ignore such an error and rewind to be after the opening '('.
// Inside the parentheses we expect to find a table factor
// followed by some joins or another level of nesting.
let table_and_joins = self.parse_table_and_joins()?;
self.expect_token(&Token::RParen)?;
// The SQL spec prohibits derived and bare tables from appearing
// alone in parentheses. We don't enforce this as some databases
// (e.g. Snowflake) allow such syntax.
Ok(TableFactor::NestedJoin(Box::new(table_and_joins)))
// Inside the parentheses we expect to find an (A) table factor
// followed by some joins or (B) another level of nesting.
let mut table_and_joins = self.parse_table_and_joins()?;
if !table_and_joins.joins.is_empty() {
self.expect_token(&Token::RParen)?;
Ok(TableFactor::NestedJoin(Box::new(table_and_joins))) // (A)
} else if let TableFactor::NestedJoin(_) = &table_and_joins.relation {
// (B): `table_and_joins` (what we found inside the parentheses)
// is a nested join `(foo JOIN bar)`, not followed by other joins.
self.expect_token(&Token::RParen)?;
Ok(TableFactor::NestedJoin(Box::new(table_and_joins)))
} else if dialect_of!(self is SnowflakeDialect | GenericDialect) {
// Dialect-specific behavior: Snowflake diverges from the
// standard and from most of the other implementations by
// allowing extra parentheses not only around a join (B), but
// around lone table names (e.g. `FROM (mytable [AS alias])`)
// and around derived tables (e.g. `FROM ((SELECT ...)
// [AS alias])`) as well.
self.expect_token(&Token::RParen)?;
if let Some(outer_alias) =
self.parse_optional_table_alias(keywords::RESERVED_FOR_TABLE_ALIAS)?
{
// Snowflake also allows specifying an alias *after* parens
// e.g. `FROM (mytable) AS alias`
match &mut table_and_joins.relation {
TableFactor::Derived { alias, .. }
| TableFactor::Table { alias, .. }
| TableFactor::TableFunction { alias, .. } => {
// but not `FROM (mytable AS alias1) AS alias2`.
if let Some(inner_alias) = alias {
return Err(ParserError::ParserError(format!(
"duplicate alias {}",
inner_alias
)));
}
// Act as if the alias was specified normally next
// to the table name: `(mytable) AS alias` ->
// `(mytable AS alias)`
alias.replace(outer_alias);
}
TableFactor::NestedJoin(_) => unreachable!(),
};
}
// Do not store the extra set of parens in the AST
Ok(table_and_joins.relation)
} else {
// The SQL spec prohibits derived tables and bare tables from
// appearing alone in parentheses (e.g. `FROM (mytable)`)
self.expected("joined table", self.peek_token())
}
} else {
let name = self.parse_object_name()?;
// Postgres, MSSQL: table-valued functions: