diff --git a/src/ast/query.rs b/src/ast/query.rs index 08a0bc5af..07863bd7c 100644 --- a/src/ast/query.rs +++ b/src/ast/query.rs @@ -108,6 +108,17 @@ pub enum SetExpr { Table(Box), } +impl SetExpr { + /// If this `SetExpr` is a `SELECT`, returns the [`Select`]. + pub fn as_select(&self) -> Option<&Select> { + if let Self::Select(select) = self { + Some(&**select) + } else { + None + } + } +} + impl fmt::Display for SetExpr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { diff --git a/src/parser/mod.rs b/src/parser/mod.rs index a7ec4d093..8132921f1 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -8594,8 +8594,19 @@ impl<'a> Parser<'a> { self.expected("joined table", self.peek_token()) } } else if dialect_of!(self is SnowflakeDialect | DatabricksDialect | GenericDialect) - && self.parse_keyword(Keyword::VALUES) + && matches!( + self.peek_tokens(), + [ + Token::Word(Word { + keyword: Keyword::VALUES, + .. + }), + Token::LParen + ] + ) { + self.expect_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. diff --git a/tests/sqlparser_databricks.rs b/tests/sqlparser_databricks.rs index 8f0579fc9..430647ded 100644 --- a/tests/sqlparser_databricks.rs +++ b/tests/sqlparser_databricks.rs @@ -1,5 +1,5 @@ use sqlparser::ast::*; -use sqlparser::dialect::DatabricksDialect; +use sqlparser::dialect::{DatabricksDialect, GenericDialect}; use sqlparser::parser::ParserError; use test_utils::*; @@ -13,6 +13,13 @@ fn databricks() -> TestedDialects { } } +fn databricks_and_generic() -> TestedDialects { + TestedDialects { + dialects: vec![Box::new(DatabricksDialect {}), Box::new(GenericDialect {})], + options: None, + } +} + #[test] fn test_databricks_identifiers() { // databricks uses backtick for delimited identifiers @@ -124,3 +131,60 @@ fn test_databricks_lambdas() { ); databricks().verified_expr("transform(array(1, 2, 3), x -> x + 1)"); } + +#[test] +fn test_values_clause() { + let values = Values { + explicit_row: false, + rows: vec![ + vec![ + Expr::Value(Value::DoubleQuotedString("one".to_owned())), + Expr::Value(number("1")), + ], + vec![ + Expr::Value(Value::SingleQuotedString("two".to_owned())), + Expr::Value(number("2")), + ], + ], + }; + + let query = databricks().verified_query(r#"VALUES ("one", 1), ('two', 2)"#); + assert_eq!(SetExpr::Values(values.clone()), *query.body); + + // VALUES is permitted in a FROM clause without a subquery + let query = databricks().verified_query_with_canonical( + r#"SELECT * FROM VALUES ("one", 1), ('two', 2)"#, + r#"SELECT * FROM (VALUES ("one", 1), ('two', 2))"#, + ); + let Some(TableFactor::Derived { subquery, .. }) = query + .body + .as_select() + .map(|select| &select.from[0].relation) + else { + panic!("expected subquery"); + }; + assert_eq!(SetExpr::Values(values), *subquery.body); + + // values is also a valid table name + let query = databricks_and_generic().verified_query(concat!( + "WITH values AS (SELECT 42) ", + "SELECT * FROM values", + )); + assert_eq!( + Some(&TableFactor::Table { + name: ObjectName(vec![Ident::new("values")]), + alias: None, + args: None, + with_hints: vec![], + version: None, + partitions: vec![] + }), + query + .body + .as_select() + .map(|select| &select.from[0].relation) + ); + + // TODO: support this example from https://docs.databricks.com/en/sql/language-manual/sql-ref-syntax-qry-select-values.html#examples + // databricks().verified_query("VALUES 1, 2, 3"); +}