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