[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

@ -20,7 +20,7 @@
#[macro_use]
mod test_utils;
use test_utils::{all_dialects, expr_from_projection, join, number, only, table};
use test_utils::{all_dialects, expr_from_projection, join, number, only, table, table_alias};
use matches::assert_matches;
use sqlparser::ast::*;
@ -2130,13 +2130,6 @@ fn parse_cross_join() {
);
}
fn table_alias(name: impl Into<String>) -> Option<TableAlias> {
Some(TableAlias {
name: Ident::new(name),
columns: vec![],
})
}
#[test]
fn parse_joins_on() {
fn join_with_constraint(

View file

@ -20,6 +20,7 @@ use test_utils::*;
use sqlparser::ast::*;
use sqlparser::dialect::{GenericDialect, SnowflakeDialect};
use sqlparser::parser::ParserError;
use sqlparser::tokenizer::*;
#[test]
@ -72,42 +73,81 @@ fn test_snowflake_single_line_tokenize() {
}
#[test]
fn test_sf_derives_single_table_in_parenthesis() {
// Nesting a subquery in parentheses is non-standard, but supported in Snowflake SQL
let sql = "SELECT * FROM ((SELECT 1) AS t)";
let select = snowflake_and_generic().verified_only_select(sql);
let from = only(select.from);
assert_eq!(
from.relation,
TableFactor::NestedJoin(Box::new(TableWithJoins {
relation: TableFactor::Derived {
lateral: false,
subquery: Box::new(snowflake_and_generic().verified_query("SELECT 1")),
alias: Some(TableAlias {
name: "t".into(),
columns: vec![],
})
},
joins: Vec::new(),
}))
fn test_sf_derived_table_in_parenthesis() {
// Nesting a subquery in an extra set of parentheses is non-standard,
// but supported in Snowflake SQL
snowflake_and_generic().one_statement_parses_to(
"SELECT * FROM ((SELECT 1) AS t)",
"SELECT * FROM (SELECT 1) AS t",
);
snowflake_and_generic().one_statement_parses_to(
"SELECT * FROM (((SELECT 1) AS t))",
"SELECT * FROM (SELECT 1) AS t",
);
}
#[test]
fn test_single_table_in_parenthesis() {
// Parenthesized table names are non-standard, but supported in Snowflake SQL
let sql = "SELECT * FROM (a NATURAL JOIN (b))";
let select = snowflake_and_generic().verified_only_select(sql);
let from = only(select.from);
snowflake_and_generic().one_statement_parses_to(
"SELECT * FROM (a NATURAL JOIN (b))",
"SELECT * FROM (a NATURAL JOIN b)",
);
snowflake_and_generic().one_statement_parses_to(
"SELECT * FROM (a NATURAL JOIN ((b)))",
"SELECT * FROM (a NATURAL JOIN b)",
);
}
assert_eq!(from.relation, nest!(table("a"), nest!(table("b"))));
#[test]
fn test_single_table_in_parenthesis_with_alias() {
snowflake_and_generic().one_statement_parses_to(
"SELECT * FROM (a NATURAL JOIN (b) c )",
"SELECT * FROM (a NATURAL JOIN b AS c)",
);
// Double parentheses around table names are non-standard, but supported in Snowflake SQL
let sql = "SELECT * FROM (a NATURAL JOIN ((b)))";
let select = snowflake_and_generic().verified_only_select(sql);
let from = only(select.from);
snowflake_and_generic().one_statement_parses_to(
"SELECT * FROM (a NATURAL JOIN ((b)) c )",
"SELECT * FROM (a NATURAL JOIN b AS c)",
);
assert_eq!(from.relation, nest!(table("a"), nest!(nest!(table("b")))));
snowflake_and_generic().one_statement_parses_to(
"SELECT * FROM (a NATURAL JOIN ( (b) c ) )",
"SELECT * FROM (a NATURAL JOIN b AS c)",
);
snowflake_and_generic().one_statement_parses_to(
"SELECT * FROM (a NATURAL JOIN ( (b) as c ) )",
"SELECT * FROM (a NATURAL JOIN b AS c)",
);
snowflake_and_generic().one_statement_parses_to(
"SELECT * FROM (a alias1 NATURAL JOIN ( (b) c ) )",
"SELECT * FROM (a AS alias1 NATURAL JOIN b AS c)",
);
snowflake_and_generic().one_statement_parses_to(
"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");
assert_eq!(
ParserError::ParserError("Expected end of statement, found: c".to_string()),
res.unwrap_err()
);
let res = snowflake().parse_sql_statements("SELECT * FROM (a b) c");
assert_eq!(
ParserError::ParserError("duplicate alias b".to_string()),
res.unwrap_err()
);
}
fn snowflake() -> TestedDialects {
TestedDialects {
dialects: vec![Box::new(SnowflakeDialect {})],
}
}
fn snowflake_and_generic() -> TestedDialects {