diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 5f6f2808..7b92fdf7 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -8422,6 +8422,29 @@ impl<'a> Parser<'a> { // appearing alone in parentheses (e.g. `FROM (mytable)`) self.expected("joined table", self.peek_token()) } + } else if dialect_of!(self is SnowflakeDialect | DatabricksDialect | GenericDialect) + && self.parse_keyword(Keyword::VALUES) + { + // Snowflake and Databricks allow syntax like below: + // SELECT * FROM VALUES (1, 'a'), (2, 'b') AS t (col1, col2) + // where there are no parentheses around the VALUES clause. + let values = SetExpr::Values(self.parse_values(false)?); + let alias = self.parse_optional_table_alias(keywords::RESERVED_FOR_TABLE_ALIAS)?; + Ok(TableFactor::Derived { + lateral: false, + subquery: Box::new(Query { + with: None, + body: Box::new(values), + order_by: vec![], + limit: None, + limit_by: vec![], + offset: None, + fetch: None, + locks: vec![], + for_clause: None, + }), + alias, + }) } else if dialect_of!(self is BigQueryDialect | PostgreSqlDialect | GenericDialect) && self.parse_keyword(Keyword::UNNEST) { diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 9baf380a..e08f5c4e 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -25,9 +25,9 @@ use sqlparser::ast::SelectItem::UnnamedExpr; use sqlparser::ast::TableFactor::{Pivot, Unpivot}; use sqlparser::ast::*; use sqlparser::dialect::{ - AnsiDialect, BigQueryDialect, ClickHouseDialect, Dialect, DuckDbDialect, GenericDialect, - HiveDialect, MsSqlDialect, MySqlDialect, PostgreSqlDialect, RedshiftSqlDialect, SQLiteDialect, - SnowflakeDialect, + AnsiDialect, BigQueryDialect, ClickHouseDialect, DatabricksDialect, Dialect, DuckDbDialect, + GenericDialect, HiveDialect, MsSqlDialect, MySqlDialect, PostgreSqlDialect, RedshiftSqlDialect, + SQLiteDialect, SnowflakeDialect, }; use sqlparser::keywords::ALL_KEYWORDS; use sqlparser::parser::{Parser, ParserError, ParserOptions}; @@ -9728,3 +9728,52 @@ fn parse_within_group() { "OVER (PARTITION BY department)", )); } + +#[test] +fn tests_select_values_without_parens() { + let dialects = TestedDialects { + dialects: vec![ + Box::new(GenericDialect {}), + Box::new(SnowflakeDialect {}), + Box::new(DatabricksDialect {}), + ], + options: None, + }; + let sql = "SELECT * FROM VALUES (1, 2), (2,3) AS tbl (id, val)"; + let canonical = "SELECT * FROM (VALUES (1, 2), (2, 3)) AS tbl (id, val)"; + dialects.verified_only_select_with_canonical(sql, canonical); +} + +#[test] +fn tests_select_values_without_parens_and_set_op() { + let dialects = TestedDialects { + dialects: vec![ + Box::new(GenericDialect {}), + Box::new(SnowflakeDialect {}), + Box::new(DatabricksDialect {}), + ], + options: None, + }; + let sql = "SELECT id + 1, name FROM VALUES (1, 'Apple'), (2, 'Banana'), (3, 'Orange') AS fruits (id, name) UNION ALL SELECT 5, 'Strawberry'"; + let canonical = "SELECT id + 1, name FROM (VALUES (1, 'Apple'), (2, 'Banana'), (3, 'Orange')) AS fruits (id, name) UNION ALL SELECT 5, 'Strawberry'"; + let query = dialects.verified_query_with_canonical(sql, canonical); + match *query.body { + SetExpr::SetOperation { + op, + set_quantifier: _, + left, + right, + } => { + assert_eq!(SetOperator::Union, op); + match *left { + SetExpr::Select(_) => {} + _ => panic!("Expected a SELECT statement"), + } + match *right { + SetExpr::Select(_) => {} + _ => panic!("Expected a SELECT statement"), + } + } + _ => panic!("Expected a SET OPERATION"), + } +}