Skip to content

Commit 6d806a7

Browse files
nickolayeyalsatori
andcommitted
support the single table in parnes with alias in snowflake by dialect
Co-authored-by: Eyal Leshem <[email protected]>
1 parent 99fb633 commit 6d806a7

File tree

3 files changed

+179
-34
lines changed

3 files changed

+179
-34
lines changed

src/parser.rs

+52-8
Original file line numberDiff line numberDiff line change
@@ -2156,14 +2156,58 @@ impl<'a> Parser<'a> {
21562156
// recently consumed does not start a derived table (cases 1, 2, or 4).
21572157
// `maybe_parse` will ignore such an error and rewind to be after the opening '('.
21582158

2159-
// Inside the parentheses we expect to find a table factor
2160-
// followed by some joins or another level of nesting.
2161-
let table_and_joins = self.parse_table_and_joins()?;
2162-
self.expect_token(&Token::RParen)?;
2163-
// The SQL spec prohibits derived and bare tables from appearing
2164-
// alone in parentheses. We don't enforce this as some databases
2165-
// (e.g. Snowflake) allow such syntax.
2166-
Ok(TableFactor::NestedJoin(Box::new(table_and_joins)))
2159+
// Inside the parentheses we expect to find an (A) table factor
2160+
// followed by some joins or (B) another level of nesting.
2161+
let mut table_and_joins = self.parse_table_and_joins()?;
2162+
2163+
if !table_and_joins.joins.is_empty() {
2164+
self.expect_token(&Token::RParen)?;
2165+
Ok(TableFactor::NestedJoin(Box::new(table_and_joins))) // (A)
2166+
} else if let TableFactor::NestedJoin(_) = &table_and_joins.relation {
2167+
// (B): `table_and_joins` (what we found inside the parentheses)
2168+
// is a nested join `(foo JOIN bar)`, not followed by other joins.
2169+
self.expect_token(&Token::RParen)?;
2170+
Ok(TableFactor::NestedJoin(Box::new(table_and_joins)))
2171+
} else if dialect_of!(self is SnowflakeDialect) {
2172+
// Dialect-specific behavior: Snowflake diverges from the
2173+
// standard and most of other implementations by allowing
2174+
// extra parentheses not only around a join (B), but around
2175+
// lone table names (e.g. `FROM (mytable [AS alias])`) and
2176+
// around derived tables (e.g. `FROM ((SELECT ...) [AS alias])`
2177+
// as well.
2178+
self.expect_token(&Token::RParen)?;
2179+
2180+
if let Some(outer_alias) =
2181+
self.parse_optional_table_alias(keywords::RESERVED_FOR_TABLE_ALIAS)?
2182+
{
2183+
// Snowflake also allows specifying an alias *after* parens
2184+
// e.g. `FROM (mytable) AS alias`
2185+
match &mut table_and_joins.relation {
2186+
TableFactor::Derived { alias, .. }
2187+
| TableFactor::Table { alias, .. }
2188+
| TableFactor::TableFunction { alias, .. } => {
2189+
// but not `FROM (mytable AS alias1) AS alias2`.
2190+
if let Some(inner_alias) = alias {
2191+
return Err(ParserError::ParserError(format!(
2192+
"duplicate alias {}",
2193+
inner_alias
2194+
)));
2195+
}
2196+
// Act as if the alias was specified normally next
2197+
// to the table name: `(mytable) AS alias` ->
2198+
// `(mytable AS alias)`
2199+
alias.replace(outer_alias);
2200+
}
2201+
TableFactor::NestedJoin(_) => unreachable!(),
2202+
};
2203+
}
2204+
// Do not store the extra set of parens in the AST
2205+
Ok(table_and_joins.relation)
2206+
} else {
2207+
// The SQL spec prohibits derived tables and bare tables from
2208+
// appearing alone in parentheses (e.g. `FROM (mytable)`)
2209+
self.expected("joined table", self.peek_token())
2210+
}
21672211
} else {
21682212
let name = self.parse_object_name()?;
21692213
// Postgres, MSSQL: table-valued functions:

src/test_utils.rs

+19
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,25 @@ pub fn number(n: &'static str) -> Value {
144144
Value::Number(n.parse().unwrap())
145145
}
146146

147+
fn table_alias(alias: &str) -> TableAlias {
148+
TableAlias {
149+
name: Ident {
150+
value: alias.to_owned(),
151+
quote_style: None,
152+
},
153+
columns: Vec::new(),
154+
}
155+
}
156+
157+
pub fn table_with_alias(name: impl Into<String>, alias: impl Into<String>) -> TableFactor {
158+
TableFactor::Table {
159+
name: ObjectName(vec![Ident::new(name.into())]),
160+
alias: Some(table_alias(&alias.into())),
161+
args: vec![],
162+
with_hints: vec![],
163+
}
164+
}
165+
147166
pub fn table(name: impl Into<String>) -> TableFactor {
148167
TableFactor::Table {
149168
name: ObjectName(vec![Ident::new(name.into())]),

tests/sqlparser_snowflake.rs

+108-26
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ mod macros;
1717
use sqlparser::test_utils::*;
1818
use sqlparser::ast::*;
1919
use sqlparser::dialect::{GenericDialect, SnowflakeDialect};
20+
use sqlparser::parser::ParserError;
2021
use sqlparser::tokenizer::*;
2122

2223
#[test]
@@ -68,43 +69,124 @@ fn test_snowflake_single_line_tokenize() {
6869
assert_eq!(expected, tokens);
6970
}
7071

72+
fn get_from_section_from_select_query(query: &str) -> Vec<TableWithJoins> {
73+
let statement = snowflake().parse_sql_statements(query).unwrap()[0].clone();
74+
75+
let query = match statement {
76+
Statement::Query(query) => query,
77+
_ => panic!("Not a query"),
78+
};
79+
80+
let select = match query.body {
81+
SetExpr::Select(select) => select,
82+
_ => panic!("not a select query"),
83+
};
84+
85+
select.from.clone()
86+
}
87+
7188
#[test]
7289
fn test_sf_derives_single_table_in_parenthesis() {
73-
// Nesting a subquery in parentheses is non-standard, but supported in Snowflake SQL
74-
let sql = "SELECT * FROM ((SELECT 1) AS t)";
75-
let select = snowflake_and_generic().verified_only_select(sql);
76-
let from = only(select.from);
90+
let from = get_from_section_from_select_query("SELECT * FROM ((SELECT 1) AS t)");
91+
assert_eq!(
92+
only(from).relation,
93+
TableFactor::Derived {
94+
lateral: false,
95+
subquery: Box::new(snowflake().verified_query("SELECT 1")),
96+
alias: Some(TableAlias {
97+
name: "t".into(),
98+
columns: vec![],
99+
})
100+
}
101+
);
102+
103+
let from = get_from_section_from_select_query("SELECT * FROM (((SELECT 1) AS t))");
104+
77105
assert_eq!(
78-
from.relation,
79-
TableFactor::NestedJoin(Box::new(TableWithJoins {
80-
relation: TableFactor::Derived {
81-
lateral: false,
82-
subquery: Box::new(snowflake_and_generic().verified_query("SELECT 1")),
83-
alias: Some(TableAlias {
84-
name: "t".into(),
85-
columns: vec![],
86-
})
87-
},
88-
joins: Vec::new(),
89-
}))
106+
only(from).relation,
107+
TableFactor::Derived {
108+
lateral: false,
109+
subquery: Box::new(snowflake().verified_query("SELECT 1")),
110+
alias: Some(TableAlias {
111+
name: "t".into(),
112+
columns: vec![],
113+
})
114+
}
90115
);
91116
}
92117

93118
#[test]
94119
fn test_single_table_in_parenthesis() {
95-
// Parenthesized table names are non-standard, but supported in Snowflake SQL
96-
let sql = "SELECT * FROM (a NATURAL JOIN (b))";
97-
let select = snowflake_and_generic().verified_only_select(sql);
98-
let from = only(select.from);
120+
//Parenthesized table names are non-standard, but supported in Snowflake SQL
121+
122+
let from = get_from_section_from_select_query("SELECT * FROM (a NATURAL JOIN (b))");
123+
assert_eq!(only(from).relation, nest!(table("a"), table("b")));
124+
125+
let from = get_from_section_from_select_query("SELECT * FROM (a NATURAL JOIN ((b)))");
126+
assert_eq!(only(from).relation, nest!(table("a"), table("b")));
127+
}
128+
129+
#[test]
130+
fn test_single_table_in_parenthesis_with_alias() {
131+
let sql = "SELECT * FROM (a NATURAL JOIN (b) c )";
132+
let table_with_joins = only(get_from_section_from_select_query(sql));
133+
assert_eq!(
134+
table_with_joins.relation,
135+
nest!(table("a"), table_with_alias("b", "c"))
136+
);
137+
138+
let sql = "SELECT * FROM (a NATURAL JOIN ((b)) c )";
139+
let table_with_joins = only(get_from_section_from_select_query(sql));
140+
assert_eq!(
141+
table_with_joins.relation,
142+
nest!(table("a"), table_with_alias("b", "c"))
143+
);
144+
145+
let sql = "SELECT * FROM (a NATURAL JOIN ( (b) c ) )";
146+
let table_with_joins = only(get_from_section_from_select_query(sql));
147+
assert_eq!(
148+
table_with_joins.relation,
149+
nest!(table("a"), table_with_alias("b", "c"))
150+
);
151+
152+
let sql = "SELECT * FROM (a NATURAL JOIN ( (b) as c ) )";
153+
let table_with_joins = only(get_from_section_from_select_query(sql));
154+
assert_eq!(
155+
table_with_joins.relation,
156+
nest!(table("a"), table_with_alias("b", "c"))
157+
);
158+
159+
let sql = "SELECT * FROM (a alias1 NATURAL JOIN ( (b) c ) )";
160+
let table_with_joins = only(get_from_section_from_select_query(sql));
161+
assert_eq!(
162+
table_with_joins.relation,
163+
nest!(table_with_alias("a", "alias1"), table_with_alias("b", "c"))
164+
);
99165

100-
assert_eq!(from.relation, nest!(table("a"), nest!(table("b"))));
166+
let sql = "SELECT * FROM (a as alias1 NATURAL JOIN ( (b) as c ) )";
167+
let table_with_joins = only(get_from_section_from_select_query(sql));
168+
assert_eq!(
169+
table_with_joins.relation,
170+
nest!(table_with_alias("a", "alias1"), table_with_alias("b", "c"))
171+
);
172+
173+
let res = snowflake().parse_sql_statements("SELECT * FROM (a NATURAL JOIN b) c");
174+
assert_eq!(
175+
ParserError::ParserError("Expected end of statement, found: c".to_string()),
176+
res.unwrap_err()
177+
);
101178

102-
// Double parentheses around table names are non-standard, but supported in Snowflake SQL
103-
let sql = "SELECT * FROM (a NATURAL JOIN ((b)))";
104-
let select = snowflake_and_generic().verified_only_select(sql);
105-
let from = only(select.from);
179+
let res = snowflake().parse_sql_statements("SELECT * FROM (a b) c");
180+
assert_eq!(
181+
ParserError::ParserError("duplicate alias b".to_string()),
182+
res.unwrap_err()
183+
);
184+
}
106185

107-
assert_eq!(from.relation, nest!(table("a"), nest!(nest!(table("b")))));
186+
fn snowflake() -> TestedDialects {
187+
TestedDialects {
188+
dialects: vec![Box::new(SnowflakeDialect {})],
189+
}
108190
}
109191

110192
fn snowflake_and_generic() -> TestedDialects {

0 commit comments

Comments
 (0)