Skip to content

Commit 531ebdf

Browse files
authored
Support multi-column aliases in SELECT items (apache#2289)
1 parent 827978d commit 531ebdf

File tree

7 files changed

+65
-0
lines changed

7 files changed

+65
-0
lines changed

src/ast/query.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -872,6 +872,15 @@ pub enum SelectItem {
872872
/// The alias for the expression.
873873
alias: Ident,
874874
},
875+
/// An expression, followed by `[ AS ] (alias1, alias2, ...)`
876+
///
877+
/// [Spark SQL](https://spark.apache.org/docs/latest/sql-ref-syntax-qry-select.html)
878+
ExprWithAliases {
879+
/// The expression being projected.
880+
expr: Expr,
881+
/// The list of aliases for the expression.
882+
aliases: Vec<Ident>,
883+
},
875884
/// An expression, followed by a wildcard expansion.
876885
/// e.g. `alias.*`, `STRUCT<STRING>('foo').*`
877886
QualifiedWildcard(SelectItemQualifiedWildcardKind, WildcardAdditionalOptions),
@@ -1175,6 +1184,12 @@ impl fmt::Display for SelectItem {
11751184
f.write_str(" AS ")?;
11761185
alias.fmt(f)
11771186
}
1187+
SelectItem::ExprWithAliases { expr, aliases } => {
1188+
expr.fmt(f)?;
1189+
f.write_str(" AS (")?;
1190+
display_comma_separated(aliases).fmt(f)?;
1191+
f.write_str(")")
1192+
}
11781193
SelectItem::QualifiedWildcard(kind, additional_options) => {
11791194
kind.fmt(f)?;
11801195
additional_options.fmt(f)

src/ast/spans.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1823,6 +1823,9 @@ impl Spanned for SelectItem {
18231823
match self {
18241824
SelectItem::UnnamedExpr(expr) => expr.span(),
18251825
SelectItem::ExprWithAlias { expr, alias } => expr.span().union(&alias.span),
1826+
SelectItem::ExprWithAliases { expr, aliases } => {
1827+
union_spans(iter::once(expr.span()).chain(aliases.iter().map(|i| i.span)))
1828+
}
18261829
SelectItem::QualifiedWildcard(kind, wildcard_additional_options) => union_spans(
18271830
[kind.span()]
18281831
.into_iter()

src/dialect/databricks.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,4 +104,8 @@ impl Dialect for DatabricksDialect {
104104
fn supports_cte_without_as(&self) -> bool {
105105
true
106106
}
107+
108+
fn supports_select_item_multi_column_alias(&self) -> bool {
109+
true
110+
}
107111
}

src/dialect/generic.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,4 +296,8 @@ impl Dialect for GenericDialect {
296296
fn supports_cte_without_as(&self) -> bool {
297297
true
298298
}
299+
300+
fn supports_select_item_multi_column_alias(&self) -> bool {
301+
true
302+
}
299303
}

src/dialect/mod.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1691,6 +1691,17 @@ pub trait Dialect: Debug + Any {
16911691
fn supports_cte_without_as(&self) -> bool {
16921692
false
16931693
}
1694+
1695+
/// Returns true if the dialect supports parenthesized multi-column
1696+
/// aliases in SELECT items. For example:
1697+
/// ```sql
1698+
/// SELECT stack(2, 'a', 'b') AS (col1, col2)
1699+
/// ```
1700+
///
1701+
/// [Spark SQL](https://spark.apache.org/docs/latest/sql-ref-syntax-qry-select.html)
1702+
fn supports_select_item_multi_column_alias(&self) -> bool {
1703+
false
1704+
}
16941705
}
16951706

16961707
/// Operators for which precedence must be defined.

src/parser/mod.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18092,6 +18092,19 @@ impl<'a> Parser<'a> {
1809218092
self.parse_wildcard_additional_options(wildcard_token)?,
1809318093
))
1809418094
}
18095+
expr if self.dialect.supports_select_item_multi_column_alias()
18096+
&& self.peek_keyword(Keyword::AS)
18097+
&& self.peek_nth_token(1).token == Token::LParen =>
18098+
{
18099+
self.expect_keyword(Keyword::AS)?;
18100+
self.expect_token(&Token::LParen)?;
18101+
let aliases = self.parse_comma_separated(|p| p.parse_identifier())?;
18102+
self.expect_token(&Token::RParen)?;
18103+
Ok(SelectItem::ExprWithAliases {
18104+
expr: maybe_prefixed_expr(expr, prefix),
18105+
aliases,
18106+
})
18107+
}
1809518108
expr => self
1809618109
.maybe_parse_select_item_alias()
1809718110
.map(|alias| match alias {

tests/sqlparser_common.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18756,3 +18756,18 @@ fn test_wildcard_func_arg() {
1875618756
dialects.verified_expr("HASH(* EXCLUDE (col1))");
1875718757
dialects.verified_expr("HASH(* EXCLUDE (col1, col2))");
1875818758
}
18759+
18760+
#[test]
18761+
fn parse_select_item_multi_column_alias() {
18762+
all_dialects_where(|d| d.supports_select_item_multi_column_alias())
18763+
.verified_stmt("SELECT stack(2, 'a', 'b', 'c', 'd') AS (col1, col2)");
18764+
18765+
all_dialects_where(|d| d.supports_select_item_multi_column_alias())
18766+
.verified_stmt("SELECT stack(2, 'a', 'b', 'c', 'd') AS (col1, col2) FROM t");
18767+
18768+
assert!(
18769+
all_dialects_where(|d| !d.supports_select_item_multi_column_alias())
18770+
.parse_sql_statements("SELECT stack(2, 'a', 'b') AS (col1, col2)")
18771+
.is_err()
18772+
);
18773+
}

0 commit comments

Comments
 (0)