Support multi-column aliases in SELECT items (#2289)
diff --git a/src/ast/query.rs b/src/ast/query.rs
index a52d518..49ba86f 100644
--- a/src/ast/query.rs
+++ b/src/ast/query.rs
@@ -872,6 +872,15 @@
/// The alias for the expression.
alias: Ident,
},
+ /// An expression, followed by `[ AS ] (alias1, alias2, ...)`
+ ///
+ /// [Spark SQL](https://spark.apache.org/docs/latest/sql-ref-syntax-qry-select.html)
+ ExprWithAliases {
+ /// The expression being projected.
+ expr: Expr,
+ /// The list of aliases for the expression.
+ aliases: Vec<Ident>,
+ },
/// An expression, followed by a wildcard expansion.
/// e.g. `alias.*`, `STRUCT<STRING>('foo').*`
QualifiedWildcard(SelectItemQualifiedWildcardKind, WildcardAdditionalOptions),
@@ -1175,6 +1184,12 @@
f.write_str(" AS ")?;
alias.fmt(f)
}
+ SelectItem::ExprWithAliases { expr, aliases } => {
+ expr.fmt(f)?;
+ f.write_str(" AS (")?;
+ display_comma_separated(aliases).fmt(f)?;
+ f.write_str(")")
+ }
SelectItem::QualifiedWildcard(kind, additional_options) => {
kind.fmt(f)?;
additional_options.fmt(f)
diff --git a/src/ast/spans.rs b/src/ast/spans.rs
index 70c12de..95d2e88 100644
--- a/src/ast/spans.rs
+++ b/src/ast/spans.rs
@@ -1823,6 +1823,9 @@
match self {
SelectItem::UnnamedExpr(expr) => expr.span(),
SelectItem::ExprWithAlias { expr, alias } => expr.span().union(&alias.span),
+ SelectItem::ExprWithAliases { expr, aliases } => {
+ union_spans(iter::once(expr.span()).chain(aliases.iter().map(|i| i.span)))
+ }
SelectItem::QualifiedWildcard(kind, wildcard_additional_options) => union_spans(
[kind.span()]
.into_iter()
diff --git a/src/dialect/databricks.rs b/src/dialect/databricks.rs
index c76b464..679d335 100644
--- a/src/dialect/databricks.rs
+++ b/src/dialect/databricks.rs
@@ -104,4 +104,8 @@
fn supports_cte_without_as(&self) -> bool {
true
}
+
+ fn supports_select_item_multi_column_alias(&self) -> bool {
+ true
+ }
}
diff --git a/src/dialect/generic.rs b/src/dialect/generic.rs
index 8809bb9..c8f5ce6 100644
--- a/src/dialect/generic.rs
+++ b/src/dialect/generic.rs
@@ -296,4 +296,8 @@
fn supports_cte_without_as(&self) -> bool {
true
}
+
+ fn supports_select_item_multi_column_alias(&self) -> bool {
+ true
+ }
}
diff --git a/src/dialect/mod.rs b/src/dialect/mod.rs
index bb11f3e..1a2b9b1 100644
--- a/src/dialect/mod.rs
+++ b/src/dialect/mod.rs
@@ -1691,6 +1691,17 @@
fn supports_cte_without_as(&self) -> bool {
false
}
+
+ /// Returns true if the dialect supports parenthesized multi-column
+ /// aliases in SELECT items. For example:
+ /// ```sql
+ /// SELECT stack(2, 'a', 'b') AS (col1, col2)
+ /// ```
+ ///
+ /// [Spark SQL](https://spark.apache.org/docs/latest/sql-ref-syntax-qry-select.html)
+ fn supports_select_item_multi_column_alias(&self) -> bool {
+ false
+ }
}
/// Operators for which precedence must be defined.
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index a63352e..60dd3e6 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -18092,6 +18092,19 @@
self.parse_wildcard_additional_options(wildcard_token)?,
))
}
+ expr if self.dialect.supports_select_item_multi_column_alias()
+ && self.peek_keyword(Keyword::AS)
+ && self.peek_nth_token(1).token == Token::LParen =>
+ {
+ self.expect_keyword(Keyword::AS)?;
+ self.expect_token(&Token::LParen)?;
+ let aliases = self.parse_comma_separated(|p| p.parse_identifier())?;
+ self.expect_token(&Token::RParen)?;
+ Ok(SelectItem::ExprWithAliases {
+ expr: maybe_prefixed_expr(expr, prefix),
+ aliases,
+ })
+ }
expr => self
.maybe_parse_select_item_alias()
.map(|alias| match alias {
diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs
index c21202f..f5add3a 100644
--- a/tests/sqlparser_common.rs
+++ b/tests/sqlparser_common.rs
@@ -18756,3 +18756,18 @@
dialects.verified_expr("HASH(* EXCLUDE (col1))");
dialects.verified_expr("HASH(* EXCLUDE (col1, col2))");
}
+
+#[test]
+fn parse_select_item_multi_column_alias() {
+ all_dialects_where(|d| d.supports_select_item_multi_column_alias())
+ .verified_stmt("SELECT stack(2, 'a', 'b', 'c', 'd') AS (col1, col2)");
+
+ all_dialects_where(|d| d.supports_select_item_multi_column_alias())
+ .verified_stmt("SELECT stack(2, 'a', 'b', 'c', 'd') AS (col1, col2) FROM t");
+
+ assert!(
+ all_dialects_where(|d| !d.supports_select_item_multi_column_alias())
+ .parse_sql_statements("SELECT stack(2, 'a', 'b') AS (col1, col2)")
+ .is_err()
+ );
+}