Skip to content

Commit

Permalink
Support: Databricks and generic: fix for values as table name (#1278)
Browse files Browse the repository at this point in the history
  • Loading branch information
jmhain committed May 21, 2024
1 parent 5418446 commit 9d15f7e
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 2 deletions.
11 changes: 11 additions & 0 deletions src/ast/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,17 @@ pub enum SetExpr {
Table(Box<Table>),
}

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 {
Expand Down
13 changes: 12 additions & 1 deletion src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
66 changes: 65 additions & 1 deletion tests/sqlparser_databricks.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use sqlparser::ast::*;
use sqlparser::dialect::DatabricksDialect;
use sqlparser::dialect::{DatabricksDialect, GenericDialect};
use sqlparser::parser::ParserError;
use test_utils::*;

Expand All @@ -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
Expand Down Expand Up @@ -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");
}

0 comments on commit 9d15f7e

Please sign in to comment.