You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@datafusion.apache.org by gi...@apache.org on 2026/04/09 15:16:47 UTC
(datafusion-sqlparser-rs) branch main updated: Support multi-column aliases in SELECT items (#2289)
This is an automated email from the ASF dual-hosted git repository.
github-bot pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datafusion-sqlparser-rs.git
The following commit(s) were added to refs/heads/main by this push:
new 531ebdf4 Support multi-column aliases in SELECT items (#2289)
531ebdf4 is described below
commit 531ebdf4ec4ebb204b096bec3e6a5386ca1fbf40
Author: Minjun Kim <48...@users.noreply.github.com>
AuthorDate: Fri Apr 10 00:14:59 2026 +0900
Support multi-column aliases in SELECT items (#2289)
---
src/ast/query.rs | 15 +++++++++++++++
src/ast/spans.rs | 3 +++
src/dialect/databricks.rs | 4 ++++
src/dialect/generic.rs | 4 ++++
src/dialect/mod.rs | 11 +++++++++++
src/parser/mod.rs | 13 +++++++++++++
tests/sqlparser_common.rs | 15 +++++++++++++++
7 files changed, 65 insertions(+)
diff --git a/src/ast/query.rs b/src/ast/query.rs
index a52d518b..49ba86f1 100644
--- a/src/ast/query.rs
+++ b/src/ast/query.rs
@@ -872,6 +872,15 @@ pub enum SelectItem {
/// 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 @@ impl fmt::Display for SelectItem {
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 70c12de1..95d2e881 100644
--- a/src/ast/spans.rs
+++ b/src/ast/spans.rs
@@ -1823,6 +1823,9 @@ impl Spanned for SelectItem {
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 c76b464a..679d335d 100644
--- a/src/dialect/databricks.rs
+++ b/src/dialect/databricks.rs
@@ -104,4 +104,8 @@ impl Dialect for DatabricksDialect {
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 8809bb97..c8f5ce66 100644
--- a/src/dialect/generic.rs
+++ b/src/dialect/generic.rs
@@ -296,4 +296,8 @@ impl Dialect for GenericDialect {
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 bb11f3eb..1a2b9b19 100644
--- a/src/dialect/mod.rs
+++ b/src/dialect/mod.rs
@@ -1691,6 +1691,17 @@ pub trait Dialect: Debug + Any {
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 a63352e1..60dd3e66 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -18092,6 +18092,19 @@ impl<'a> Parser<'a> {
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 c21202f0..f5add3a6 100644
--- a/tests/sqlparser_common.rs
+++ b/tests/sqlparser_common.rs
@@ -18756,3 +18756,18 @@ fn test_wildcard_func_arg() {
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()
+ );
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datafusion.apache.org
For additional commands, e-mail: commits-help@datafusion.apache.org