This is an automated email from the ASF dual-hosted git repository.

github-bot pushed a commit to branch 
gh-readonly-queue/main/pr-2209-2ea773a1ad0f18d2577846bb8201c0769734c098
in repository https://gitbox.apache.org/repos/asf/datafusion-sqlparser-rs.git

commit 63eeaa0d7e4bdec852b34b6f125dfb90c1d7f3e9
Author: xitep <[email protected]>
AuthorDate: Wed Feb 18 10:24:01 2026 +0100

    [PIVOT] Optional AS keyword for aliases (#2209)
---
 src/ast/query.rs          |  7 +++++--
 src/parser/mod.rs         | 49 +++++++++++++++++++++++++++++++----------------
 tests/sqlparser_common.rs | 12 ++++++++++++
 3 files changed, 49 insertions(+), 19 deletions(-)

diff --git a/src/ast/query.rs b/src/ast/query.rs
index b8f605be..6d95216d 100644
--- a/src/ast/query.rs
+++ b/src/ast/query.rs
@@ -1589,6 +1589,7 @@ pub enum TableFactor {
     ///
     /// 
[BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax#pivot_operator)
     /// 
[Snowflake](https://docs.snowflake.com/en/sql-reference/constructs/pivot)
+    /// 
[Oracle](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/SELECT.html#GUID-CFA006CA-6FF1-4972-821E-6996142A51C6__GUID-68257B27-1C4C-4C47-8140-5C60E0E65D35)
     Pivot {
         /// The input table to pivot.
         table: Box<TableFactor>,
@@ -1610,8 +1611,10 @@ pub enum TableFactor {
     /// table UNPIVOT [ { INCLUDE | EXCLUDE } NULLS ] (value FOR name IN 
(column1, [ column2, ... ])) [ alias ]
     /// ```
     ///
-    /// See <https://docs.snowflake.com/en/sql-reference/constructs/unpivot>.
-    /// See 
<https://docs.databricks.com/aws/en/sql/language-manual/sql-ref-syntax-qry-select-unpivot>.
+    /// 
[Snowflake](https://docs.snowflake.com/en/sql-reference/constructs/unpivot)
+    /// 
[Databricks](https://docs.databricks.com/aws/en/sql/language-manual/sql-ref-syntax-qry-select-unpivot)
+    /// 
[BigQuery](https://docs.cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax#unpivot_operator)
+    /// 
[Oracle](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/SELECT.html#GUID-CFA006CA-6FF1-4972-821E-6996142A51C6__GUID-9B4E0389-413C-4014-94A1-0A0571BDF7E1)
     Unpivot {
         /// The input table to unpivot.
         table: Box<TableFactor>,
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index e708217d..eba9b32d 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -13617,7 +13617,7 @@ impl<'a> Parser<'a> {
                 Keyword::PIVOT => {
                     self.expect_token(&Token::LParen)?;
                     let aggregate_functions =
-                        
self.parse_comma_separated(Self::parse_aliased_function_call)?;
+                        
self.parse_comma_separated(Self::parse_pivot_aggregate_function)?;
                     self.expect_keyword_is(Keyword::FOR)?;
                     let value_column = self.parse_period_separated(|p| 
p.parse_identifier())?;
                     self.expect_keyword_is(Keyword::IN)?;
@@ -16242,20 +16242,6 @@ impl<'a> Parser<'a> {
         })
     }
 
-    fn parse_aliased_function_call(&mut self) -> Result<ExprWithAlias, 
ParserError> {
-        let function_name = match self.next_token().token {
-            Token::Word(w) => Ok(w.value),
-            _ => self.expected("a function identifier", self.peek_token()),
-        }?;
-        let expr = 
self.parse_function(ObjectName::from(vec![Ident::new(function_name)]))?;
-        let alias = if self.parse_keyword(Keyword::AS) {
-            Some(self.parse_identifier()?)
-        } else {
-            None
-        };
-
-        Ok(ExprWithAlias { expr, alias })
-    }
     /// Parses an expression with an optional alias
     ///
     /// Examples:
@@ -16289,13 +16275,40 @@ impl<'a> Parser<'a> {
         Ok(ExprWithAlias { expr, alias })
     }
 
+    /// Parse an expression followed by an optional alias; Unlike
+    /// [Self::parse_expr_with_alias] the "AS" keyword between the expression
+    /// and the alias is optional.
+    fn parse_expr_with_alias_optional_as_keyword(&mut self) -> 
Result<ExprWithAlias, ParserError> {
+        let expr = self.parse_expr()?;
+        let alias = self.parse_identifier_optional_alias()?;
+        Ok(ExprWithAlias { expr, alias })
+    }
+
+    /// Parses a plain function call with an optional alias for the `PIVOT` 
clause
+    fn parse_pivot_aggregate_function(&mut self) -> Result<ExprWithAlias, 
ParserError> {
+        let function_name = match self.next_token().token {
+            Token::Word(w) => Ok(w.value),
+            _ => self.expected("a function identifier", self.peek_token()),
+        }?;
+        let expr = 
self.parse_function(ObjectName::from(vec![Ident::new(function_name)]))?;
+        let alias = {
+            fn validator(explicit: bool, kw: &Keyword, parser: &mut Parser) -> 
bool {
+                // ~ for a PIVOT aggregate function the alias must not be a 
"FOR"; in any dialect
+                kw != &Keyword::FOR && 
parser.dialect.is_select_item_alias(explicit, kw, parser)
+            }
+            self.parse_optional_alias_inner(None, validator)?
+        };
+        Ok(ExprWithAlias { expr, alias })
+    }
+
     /// Parse a PIVOT table factor (ClickHouse/Oracle style pivot), returning 
a TableFactor.
     pub fn parse_pivot_table_factor(
         &mut self,
         table: TableFactor,
     ) -> Result<TableFactor, ParserError> {
         self.expect_token(&Token::LParen)?;
-        let aggregate_functions = 
self.parse_comma_separated(Self::parse_aliased_function_call)?;
+        let aggregate_functions =
+            self.parse_comma_separated(Self::parse_pivot_aggregate_function)?;
         self.expect_keyword_is(Keyword::FOR)?;
         let value_column = if self.peek_token_ref().token == Token::LParen {
             self.parse_parenthesized_column_list_inner(Mandatory, false, |p| {
@@ -16317,7 +16330,9 @@ impl<'a> Parser<'a> {
         } else if self.peek_sub_query() {
             PivotValueSource::Subquery(self.parse_query()?)
         } else {
-            
PivotValueSource::List(self.parse_comma_separated(Self::parse_expr_with_alias)?)
+            PivotValueSource::List(
+                
self.parse_comma_separated(Self::parse_expr_with_alias_optional_as_keyword)?,
+            )
         };
         self.expect_token(&Token::RParen)?;
 
diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs
index 5822153a..182854d1 100644
--- a/tests/sqlparser_common.rs
+++ b/tests/sqlparser_common.rs
@@ -11357,6 +11357,18 @@ fn parse_pivot_table() {
         verified_stmt(multiple_value_columns_sql).to_string(),
         multiple_value_columns_sql
     );
+
+    // assert optional "AS" keyword for aliases for pivot values
+    one_statement_parses_to(
+        "SELECT * FROM t PIVOT(SUM(1) FOR a.abc IN (1 x, 'two' y, three z))",
+        "SELECT * FROM t PIVOT(SUM(1) FOR a.abc IN (1 AS x, 'two' AS y, three 
AS z))",
+    );
+
+    // assert optional "AS" keyword for aliases for pivot aggregate function
+    one_statement_parses_to(
+        "SELECT * FROM t PIVOT(SUM(1) x, COUNT(42) y FOR a.abc IN (1))",
+        "SELECT * FROM t PIVOT(SUM(1) AS x, COUNT(42) AS y FOR a.abc IN (1))",
+    );
 }
 
 #[test]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to