Skip to content

Commit c98241e

Browse files
Spark: TABLE t, VALUES ... AS t(a, b), DESC TABLE t (#13 onto the v0.63.0 branch) (#14)
* Spark: TABLE t as a statement, VALUES ... AS t(a, b) as a query body (#13) * Spark: TABLE t as a statement, VALUES ... AS t(a, b) as a query body Two dialect hooks, on for Spark, Databricks and Hive. supports_table_statement lets a statement start with TABLE, which parse_query already reads as SetExpr::Table. supports_values_alias reads a table alias with a column list after a VALUES body and rewrites the body as SELECT * FROM (VALUES ...) AS t(a, b), since a bare SetExpr::Values has nowhere to carry the alias; a reserved word after VALUES (UNION, ORDER, LIMIT) is not an alias. * parse_as_table: take a second name part only after a period It read three tokens unconditionally, so TABLE t ORDER BY a swallowed ORDER BY and then failed on what followed; the statement form is the first place a clause can follow the name. * Spark: read TABLE t as SELECT * FROM t in every query-body position The string-only SetExpr::Table drops identifier quoting and leaves consumers to special-case it at the top level. Under supports_table_statement, build a SELECT * over the parsed ObjectName instead, so TABLE works on set-operation sides, in CTEs, INSERT and CTAS, and keeps its quote style. * Spark: DESC TABLE t describes the table, not the query TABLE t With TABLE able to start a query, DESCRIBE TABLE t parsed as a describe of that query, and DESCRIBE TABLE EXTENDED t took EXTENDED for the table name. For DESC/DESCRIBE under supports_table_statement, a TABLE followed by a name now goes straight to the describe-table form, which accepts EXTENDED/FORMATTED after TABLE as Spark writes it and prints TABLE first. DESCRIBE table (a table named table) and EXPLAIN are unchanged. * Spark: read a VALUES alias on each side of a set operation The alias was read after the whole query body, by which point a set operator had been consumed, so VALUES ... AS t(a) UNION ... failed. Read it where the VALUES operand is parsed, the way TABLE is.
1 parent c4f6658 commit c98241e

8 files changed

Lines changed: 310 additions & 22 deletions

File tree

‎src/ast/mod.rs‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5184,12 +5184,12 @@ impl fmt::Display for Statement {
51845184
} => {
51855185
write!(f, "{describe_alias} ")?;
51865186

5187-
if let Some(format) = hive_format {
5188-
write!(f, "{format} ")?;
5189-
}
51905187
if *has_table_keyword {
51915188
write!(f, "TABLE ")?;
51925189
}
5190+
if let Some(format) = hive_format {
5191+
write!(f, "{format} ")?;
5192+
}
51935193

51945194
write!(f, "{table_name}")
51955195
}

‎src/dialect/databricks.rs‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,16 @@ impl Dialect for DatabricksDialect {
5454
true
5555
}
5656

57+
/// `TABLE t` as a statement
58+
fn supports_table_statement(&self) -> bool {
59+
true
60+
}
61+
62+
/// `VALUES (1, 2) AS t(a, b)` as a query body
63+
fn supports_values_alias(&self) -> bool {
64+
true
65+
}
66+
5767
/// `1Y`, `1S`, `1L`, `1.0F`, `1.0D`, `1.0BD`
5868
fn supports_typed_numeric_literal_suffix(&self) -> bool {
5969
true

‎src/dialect/hive.rs‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,16 @@ impl Dialect for HiveDialect {
5252
true
5353
}
5454

55+
/// `TABLE t` as a statement
56+
fn supports_table_statement(&self) -> bool {
57+
true
58+
}
59+
60+
/// `VALUES (1, 2) AS t(a, b)` as a query body
61+
fn supports_values_alias(&self) -> bool {
62+
true
63+
}
64+
5565
/// `1Y`, `1S`, `1L`, `1.0F`, `1.0D`, `1.0BD`
5666
fn supports_typed_numeric_literal_suffix(&self) -> bool {
5767
true

‎src/dialect/mod.rs‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,19 @@ pub trait Dialect: Debug + Any {
508508
false
509509
}
510510

511+
/// Returns true if a statement may be a bare `TABLE t`, meaning
512+
/// `SELECT * FROM t`, as in Spark SQL.
513+
fn supports_table_statement(&self) -> bool {
514+
false
515+
}
516+
517+
/// Returns true if a `VALUES` query body may carry a table alias with a
518+
/// column list, `VALUES (1, 2) AS t(a, b)`, as in Spark SQL. It parses to
519+
/// `SELECT * FROM (VALUES (1, 2)) AS t(a, b)`.
520+
fn supports_values_alias(&self) -> bool {
521+
false
522+
}
523+
511524
/// Returns true if the dialect supports numbers containing underscores, e.g. `10_000_000`
512525
fn supports_numeric_literal_underscores(&self) -> bool {
513526
false

‎src/dialect/spark.rs‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,16 @@ impl Dialect for SparkSqlDialect {
5555
true
5656
}
5757

58+
/// `TABLE t` as a statement
59+
fn supports_table_statement(&self) -> bool {
60+
true
61+
}
62+
63+
/// `VALUES (1, 2) AS t(a, b)` as a query body
64+
fn supports_values_alias(&self) -> bool {
65+
true
66+
}
67+
5868
/// `1Y`, `1S`, `1L`, `1.0F`, `1.0D`, `1.0BD`
5969
fn supports_typed_numeric_literal_suffix(&self) -> bool {
6070
true

‎src/parser/mod.rs‎

Lines changed: 137 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,10 @@ impl<'a> Parser<'a> {
650650
self.prev_token();
651651
self.parse_query().map(Into::into)
652652
}
653+
Keyword::TABLE if self.dialect.supports_table_statement() => {
654+
self.prev_token();
655+
self.parse_query().map(Into::into)
656+
}
653657
Keyword::TRUNCATE => self.parse_truncate().map(Into::into),
654658
Keyword::ATTACH => {
655659
if dialect_of!(self is DuckDbDialect) {
@@ -14585,7 +14589,22 @@ impl<'a> Parser<'a> {
1458514589
}
1458614590
}
1458714591

14588-
match self.maybe_parse(|parser| parser.parse_statement())? {
14592+
// Spark's `DESC [TABLE] [EXTENDED | FORMATTED] name`: where `TABLE` can
14593+
// start a query, `DESC TABLE t` would otherwise describe the query `TABLE t`.
14594+
// `DESCRIBE table` and `DESCRIBE table.x` still name a table called `table`.
14595+
let describe_table = describe_alias != DescribeAlias::Explain
14596+
&& self.dialect.supports_table_statement()
14597+
&& self.peek_keyword(Keyword::TABLE)
14598+
&& !matches!(
14599+
self.peek_nth_token_ref(1).token,
14600+
Token::EOF | Token::SemiColon | Token::Period
14601+
);
14602+
let statement = if describe_table {
14603+
None
14604+
} else {
14605+
self.maybe_parse(|parser| parser.parse_statement())?
14606+
};
14607+
match statement {
1458914608
Some(Statement::Explain { .. }) | Some(Statement::ExplainTable { .. }) => Err(
1459014609
ParserError::ParserError("Explain must be root of the plan".to_string()),
1459114610
),
@@ -14600,19 +14619,18 @@ impl<'a> Parser<'a> {
1460014619
options,
1460114620
}),
1460214621
_ => {
14603-
let hive_format =
14604-
match self.parse_one_of_keywords(&[Keyword::EXTENDED, Keyword::FORMATTED]) {
14605-
Some(Keyword::EXTENDED) => Some(HiveDescribeFormat::Extended),
14606-
Some(Keyword::FORMATTED) => Some(HiveDescribeFormat::Formatted),
14607-
_ => None,
14608-
};
14622+
let mut hive_format = self.parse_hive_describe_format();
1460914623

14610-
let has_table_keyword = if self.dialect.describe_requires_table_keyword() {
14611-
// only allow to use TABLE keyword for DESC|DESCRIBE statement
14612-
self.parse_keyword(Keyword::TABLE)
14613-
} else {
14614-
false
14615-
};
14624+
let has_table_keyword =
14625+
if self.dialect.describe_requires_table_keyword() || describe_table {
14626+
// only allow to use TABLE keyword for DESC|DESCRIBE statement
14627+
self.parse_keyword(Keyword::TABLE)
14628+
} else {
14629+
false
14630+
};
14631+
if has_table_keyword && hive_format.is_none() {
14632+
hive_format = self.parse_hive_describe_format();
14633+
}
1461614634

1461714635
let table_name = self.parse_object_name(false)?;
1461814636
Ok(Statement::ExplainTable {
@@ -14625,6 +14643,14 @@ impl<'a> Parser<'a> {
1462514643
}
1462614644
}
1462714645

14646+
fn parse_hive_describe_format(&mut self) -> Option<HiveDescribeFormat> {
14647+
match self.parse_one_of_keywords(&[Keyword::EXTENDED, Keyword::FORMATTED]) {
14648+
Some(Keyword::EXTENDED) => Some(HiveDescribeFormat::Extended),
14649+
Some(Keyword::FORMATTED) => Some(HiveDescribeFormat::Formatted),
14650+
_ => None,
14651+
}
14652+
}
14653+
1462814654
/// Parse a query expression, i.e. a `SELECT` statement optionally
1462914655
/// preceded with some `WITH` CTE declarations and optionally followed
1463014656
/// by `ORDER BY`. Unlike some other parse_... methods, this one doesn't
@@ -15209,12 +15235,40 @@ impl<'a> Parser<'a> {
1520915235
SetExpr::Query(subquery)
1521015236
} else if self.parse_keyword(Keyword::VALUES) {
1521115237
let is_mysql = dialect_of!(self is MySqlDialect);
15212-
SetExpr::Values(self.parse_values(is_mysql, false)?)
15238+
let values = SetExpr::Values(self.parse_values(is_mysql, false)?);
15239+
// The alias belongs to this operand, so read it before any set operator.
15240+
let alias = if self.dialect.supports_values_alias() {
15241+
self.maybe_parse_table_alias()?
15242+
} else {
15243+
None
15244+
};
15245+
match alias {
15246+
Some(alias) => Self::values_with_alias(Box::new(values), alias),
15247+
None => values,
15248+
}
1521315249
} else if self.parse_keyword(Keyword::VALUE) {
1521415250
let is_mysql = dialect_of!(self is MySqlDialect);
1521515251
SetExpr::Values(self.parse_values(is_mysql, true)?)
1521615252
} else if self.parse_keyword(Keyword::TABLE) {
15217-
SetExpr::Table(Box::new(self.parse_as_table()?))
15253+
if self.dialect.supports_table_statement() {
15254+
// `TABLE t` is `SELECT * FROM t`; keeping the ObjectName
15255+
// preserves quoting, which the string-only `Table` drops.
15256+
let name = self.parse_object_name(false)?;
15257+
Self::select_star_from(TableFactor::Table {
15258+
name,
15259+
alias: None,
15260+
args: None,
15261+
with_hints: vec![],
15262+
version: None,
15263+
partitions: vec![],
15264+
with_ordinality: false,
15265+
json_path: None,
15266+
sample: None,
15267+
index_hints: vec![],
15268+
})
15269+
} else {
15270+
SetExpr::Table(Box::new(self.parse_as_table()?))
15271+
}
1521815272
} else {
1521915273
return self.expected_ref(
1522015274
"SELECT, VALUES, or a subquery in the query body",
@@ -15796,11 +15850,77 @@ impl<'a> Parser<'a> {
1579615850
Ok(clauses)
1579715851
}
1579815852

15853+
/// `VALUES ... AS t(a, b)` as `SELECT * FROM (VALUES ...) AS t(a, b)`:
15854+
/// the alias has nowhere to go on a bare `SetExpr::Values`.
15855+
fn values_with_alias(values: Box<SetExpr>, alias: TableAlias) -> SetExpr {
15856+
let subquery = Box::new(Query {
15857+
with: None,
15858+
body: values,
15859+
order_by: None,
15860+
limit_clause: None,
15861+
fetch: None,
15862+
locks: vec![],
15863+
for_clause: None,
15864+
settings: None,
15865+
format_clause: None,
15866+
pipe_operators: vec![],
15867+
});
15868+
Self::select_star_from(TableFactor::Derived {
15869+
lateral: false,
15870+
subquery,
15871+
alias: Some(alias),
15872+
sample: None,
15873+
})
15874+
}
15875+
15876+
fn select_star_from(relation: TableFactor) -> SetExpr {
15877+
SetExpr::Select(Box::new(Select {
15878+
select_token: AttachedToken::empty(),
15879+
optimizer_hints: vec![],
15880+
distinct: None,
15881+
select_modifiers: None,
15882+
top: None,
15883+
top_before_distinct: false,
15884+
projection: vec![SelectItem::Wildcard(WildcardAdditionalOptions::default())],
15885+
exclude: None,
15886+
into: None,
15887+
from: vec![TableWithJoins {
15888+
relation,
15889+
joins: vec![],
15890+
}],
15891+
lateral_views: vec![],
15892+
prewhere: None,
15893+
selection: None,
15894+
group_by: GroupByExpr::Expressions(vec![], vec![]),
15895+
cluster_by: vec![],
15896+
distribute_by: vec![],
15897+
sort_by: vec![],
15898+
having: None,
15899+
named_window: vec![],
15900+
window_before_qualify: false,
15901+
qualify: None,
15902+
value_table_mode: None,
15903+
connect_by: vec![],
15904+
flavor: SelectFlavor::Standard,
15905+
}))
15906+
}
15907+
1579915908
/// Parse `CREATE TABLE x AS TABLE y`
1580015909
pub fn parse_as_table(&mut self) -> Result<Table, ParserError> {
1580115910
let token1 = self.next_token();
15802-
let token2 = self.next_token();
15803-
let token3 = self.next_token();
15911+
// Only a `.` continues the name; anything else (ORDER BY, a pipe
15912+
// operator, the end of the statement) belongs to the caller.
15913+
let qualified = self.peek_token_ref().token == Token::Period;
15914+
let token2 = if qualified {
15915+
self.next_token()
15916+
} else {
15917+
TokenWithSpan::wrap(Token::EOF)
15918+
};
15919+
let token3 = if qualified {
15920+
self.next_token()
15921+
} else {
15922+
TokenWithSpan::wrap(Token::EOF)
15923+
};
1580415924

1580515925
let table_name;
1580615926
let schema_name;

‎tests/sqlparser_common.rs‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4843,6 +4843,8 @@ fn parse_create_table_as() {
48434843

48444844
#[test]
48454845
fn parse_create_table_as_table() {
4846+
// Dialects with `supports_table_statement` read `TABLE t` as `SELECT * FROM t`.
4847+
let dialects = all_dialects_where(|d| !d.supports_table_statement());
48464848
let sql1 = "CREATE TABLE new_table AS TABLE old_table";
48474849

48484850
let expected_query1 = Box::new(Query {
@@ -4861,7 +4863,7 @@ fn parse_create_table_as_table() {
48614863
pipe_operators: vec![],
48624864
});
48634865

4864-
match verified_stmt(sql1) {
4866+
match dialects.verified_stmt(sql1) {
48654867
Statement::CreateTable(CreateTable { query, name, .. }) => {
48664868
assert_eq!(name, ObjectName::from(vec![Ident::new("new_table")]));
48674869
assert_eq!(query.unwrap(), expected_query1);
@@ -4887,7 +4889,7 @@ fn parse_create_table_as_table() {
48874889
pipe_operators: vec![],
48884890
});
48894891

4890-
match verified_stmt(sql2) {
4892+
match dialects.verified_stmt(sql2) {
48914893
Statement::CreateTable(CreateTable { query, name, .. }) => {
48924894
assert_eq!(name, ObjectName::from(vec![Ident::new("new_table")]));
48934895
assert_eq!(query.unwrap(), expected_query2);

0 commit comments

Comments
 (0)