mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-08-27 09:24:04 +00:00

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>
157 lines
5 KiB
Rust
157 lines
5 KiB
Rust
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#![warn(clippy::all)]
|
|
//! Test SQL syntax specific to Snowflake. The parser based on the
|
|
//! generic dialect is also tested (on the inputs it can handle).
|
|
|
|
#[macro_use]
|
|
mod test_utils;
|
|
use test_utils::*;
|
|
|
|
use sqlparser::ast::*;
|
|
use sqlparser::dialect::{GenericDialect, SnowflakeDialect};
|
|
use sqlparser::parser::ParserError;
|
|
use sqlparser::tokenizer::*;
|
|
|
|
#[test]
|
|
fn test_snowflake_create_table() {
|
|
let sql = "CREATE TABLE _my_$table (am00unt number)";
|
|
match snowflake_and_generic().verified_stmt(sql) {
|
|
Statement::CreateTable { name, .. } => {
|
|
assert_eq!("_my_$table", name.to_string());
|
|
}
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_snowflake_single_line_tokenize() {
|
|
let sql = "CREATE TABLE# this is a comment \ntable_1";
|
|
let dialect = SnowflakeDialect {};
|
|
let mut tokenizer = Tokenizer::new(&dialect, &sql);
|
|
let tokens = tokenizer.tokenize().unwrap();
|
|
|
|
let expected = vec![
|
|
Token::make_keyword("CREATE"),
|
|
Token::Whitespace(Whitespace::Space),
|
|
Token::make_keyword("TABLE"),
|
|
Token::Whitespace(Whitespace::SingleLineComment {
|
|
prefix: "#".to_string(),
|
|
comment: " this is a comment \n".to_string(),
|
|
}),
|
|
Token::make_word("table_1", None),
|
|
];
|
|
|
|
assert_eq!(expected, tokens);
|
|
|
|
let sql = "CREATE TABLE// this is a comment \ntable_1";
|
|
let mut tokenizer = Tokenizer::new(&dialect, &sql);
|
|
let tokens = tokenizer.tokenize().unwrap();
|
|
|
|
let expected = vec![
|
|
Token::make_keyword("CREATE"),
|
|
Token::Whitespace(Whitespace::Space),
|
|
Token::make_keyword("TABLE"),
|
|
Token::Whitespace(Whitespace::SingleLineComment {
|
|
prefix: "//".to_string(),
|
|
comment: " this is a comment \n".to_string(),
|
|
}),
|
|
Token::make_word("table_1", None),
|
|
];
|
|
|
|
assert_eq!(expected, tokens);
|
|
}
|
|
|
|
#[test]
|
|
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
|
|
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)",
|
|
);
|
|
}
|
|
|
|
#[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)",
|
|
);
|
|
|
|
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) 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 {
|
|
TestedDialects {
|
|
dialects: vec![Box::new(SnowflakeDialect {}), Box::new(GenericDialect {})],
|
|
}
|
|
}
|