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");
+}