Skip to content

Commit 2280580

Browse files
eyalsatorinickolay
authored andcommitted
support the single table in parnes with alias in snowflake by dialect
1 parent 54be391 commit 2280580

File tree

6 files changed

+240
-69
lines changed

6 files changed

+240
-69
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ Cargo.lock
1212

1313
# IDEs
1414
.idea
15+
.vscode

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

+35
Original file line numberDiff line numberDiff line change
@@ -143,3 +143,38 @@ pub fn expr_from_projection(item: &SelectItem) -> &Expr {
143143
pub fn number(n: &'static str) -> Value {
144144
Value::Number(n.parse().unwrap())
145145
}
146+
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+
166+
pub fn table(name: impl Into<String>) -> TableFactor {
167+
TableFactor::Table {
168+
name: ObjectName(vec![Ident::new(name.into())]),
169+
alias: None,
170+
args: vec![],
171+
with_hints: vec![],
172+
}
173+
}
174+
175+
pub fn join(relation: TableFactor) -> Join {
176+
Join {
177+
relation,
178+
join_operator: JoinOperator::Inner(JoinConstraint::Natural),
179+
}
180+
}

tests/macros/mod.rs

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Licensed under the Apache License, Version 2.0 (the "License");
2+
// you may not use this file except in compliance with the License.
3+
// You may obtain a copy of the License at
4+
//
5+
// http://www.apache.org/licenses/LICENSE-2.0
6+
//
7+
// Unless required by applicable law or agreed to in writing, software
8+
// distributed under the License is distributed on an "AS IS" BASIS,
9+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
// See the License for the specific language governing permissions and
11+
// limitations under the License.
12+
13+
macro_rules! nest {
14+
($base:expr $(, $join:expr)*) => {
15+
TableFactor::NestedJoin(Box::new(TableWithJoins {
16+
relation: $base,
17+
joins: vec![$(join($join)),*]
18+
}))
19+
};
20+
}

tests/sqlparser_common.rs

+6-61
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,16 @@
1818
//! sqlparser regardless of the chosen dialect (i.e. it doesn't conflict with
1919
//! dialect-specific parsing rules).
2020
21-
use matches::assert_matches;
21+
#[macro_use]
22+
#[path = "macros/mod.rs"]
23+
mod macros;
2224

25+
use matches::assert_matches;
2326
use sqlparser::ast::*;
27+
2428
use sqlparser::dialect::keywords::ALL_KEYWORDS;
2529
use sqlparser::parser::ParserError;
26-
use sqlparser::test_utils::{all_dialects, expr_from_projection, number, only};
30+
use sqlparser::test_utils::{all_dialects, expr_from_projection, join, number, only, table};
2731

2832
#[test]
2933
fn parse_insert_values() {
@@ -2282,31 +2286,6 @@ fn parse_complex_join() {
22822286

22832287
#[test]
22842288
fn parse_join_nesting() {
2285-
fn table(name: impl Into<String>) -> TableFactor {
2286-
TableFactor::Table {
2287-
name: ObjectName(vec![Ident::new(name.into())]),
2288-
alias: None,
2289-
args: vec![],
2290-
with_hints: vec![],
2291-
}
2292-
}
2293-
2294-
fn join(relation: TableFactor) -> Join {
2295-
Join {
2296-
relation,
2297-
join_operator: JoinOperator::Inner(JoinConstraint::Natural),
2298-
}
2299-
}
2300-
2301-
macro_rules! nest {
2302-
($base:expr $(, $join:expr)*) => {
2303-
TableFactor::NestedJoin(Box::new(TableWithJoins {
2304-
relation: $base,
2305-
joins: vec![$(join($join)),*]
2306-
}))
2307-
};
2308-
}
2309-
23102289
let sql = "SELECT * FROM a NATURAL JOIN (b NATURAL JOIN (c NATURAL JOIN d NATURAL JOIN e)) \
23112290
NATURAL JOIN (f NATURAL JOIN (g NATURAL JOIN h))";
23122291
assert_eq!(
@@ -2337,20 +2316,6 @@ fn parse_join_nesting() {
23372316
from.joins,
23382317
vec![join(nest!(nest!(nest!(table("b"), table("c")))))]
23392318
);
2340-
2341-
// Parenthesized table names are non-standard, but supported in Snowflake SQL
2342-
let sql = "SELECT * FROM (a NATURAL JOIN (b))";
2343-
let select = verified_only_select(sql);
2344-
let from = only(select.from);
2345-
2346-
assert_eq!(from.relation, nest!(table("a"), nest!(table("b"))));
2347-
2348-
// Double parentheses around table names are non-standard, but supported in Snowflake SQL
2349-
let sql = "SELECT * FROM (a NATURAL JOIN ((b)))";
2350-
let select = verified_only_select(sql);
2351-
let from = only(select.from);
2352-
2353-
assert_eq!(from.relation, nest!(table("a"), nest!(nest!(table("b")))));
23542319
}
23552320

23562321
#[test]
@@ -2490,26 +2455,6 @@ fn parse_derived_tables() {
24902455
}],
24912456
}))
24922457
);
2493-
2494-
// Nesting a subquery in parentheses is non-standard, but supported in Snowflake SQL
2495-
let sql = "SELECT * FROM ((SELECT 1) AS t)";
2496-
let select = verified_only_select(sql);
2497-
let from = only(select.from);
2498-
2499-
assert_eq!(
2500-
from.relation,
2501-
TableFactor::NestedJoin(Box::new(TableWithJoins {
2502-
relation: TableFactor::Derived {
2503-
lateral: false,
2504-
subquery: Box::new(verified_query("SELECT 1")),
2505-
alias: Some(TableAlias {
2506-
name: "t".into(),
2507-
columns: vec![],
2508-
})
2509-
},
2510-
joins: Vec::new(),
2511-
}))
2512-
);
25132458
}
25142459

25152460
#[test]

tests/sqlparser_snowflake.rs

+126
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,14 @@
99
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1010
// See the License for the specific language governing permissions and
1111
// limitations under the License.
12+
13+
#[macro_use]
14+
#[path = "macros/mod.rs"]
15+
mod macros;
16+
1217
use sqlparser::ast::*;
1318
use sqlparser::dialect::{GenericDialect, SnowflakeDialect};
19+
use sqlparser::parser::ParserError;
1420
use sqlparser::test_utils::*;
1521
use sqlparser::tokenizer::*;
1622

@@ -63,6 +69,126 @@ fn test_snowflake_single_line_tokenize() {
6369
assert_eq!(expected, tokens);
6470
}
6571

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+
88+
#[test]
89+
fn test_sf_derives_single_table_in_parenthesis() {
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+
105+
assert_eq!(
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+
}
115+
);
116+
}
117+
118+
#[test]
119+
fn test_single_table_in_parenthesis() {
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+
);
165+
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+
);
178+
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+
}
185+
186+
fn snowflake() -> TestedDialects {
187+
TestedDialects {
188+
dialects: vec![Box::new(SnowflakeDialect {})],
189+
}
190+
}
191+
66192
fn snowflake_and_generic() -> TestedDialects {
67193
TestedDialects {
68194
dialects: vec![Box::new(SnowflakeDialect {}), Box::new(GenericDialect {})],

0 commit comments

Comments
 (0)