Skip to content

Commit 63eeaa0

Browse files
authored
[PIVOT] Optional AS keyword for aliases (#2209)
1 parent 2ea773a commit 63eeaa0

File tree

3 files changed

+49
-19
lines changed

3 files changed

+49
-19
lines changed

src/ast/query.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1589,6 +1589,7 @@ pub enum TableFactor {
15891589
///
15901590
/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax#pivot_operator)
15911591
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/constructs/pivot)
1592+
/// [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)
15921593
Pivot {
15931594
/// The input table to pivot.
15941595
table: Box<TableFactor>,
@@ -1610,8 +1611,10 @@ pub enum TableFactor {
16101611
/// table UNPIVOT [ { INCLUDE | EXCLUDE } NULLS ] (value FOR name IN (column1, [ column2, ... ])) [ alias ]
16111612
/// ```
16121613
///
1613-
/// See <https://docs.snowflake.com/en/sql-reference/constructs/unpivot>.
1614-
/// See <https://docs.databricks.com/aws/en/sql/language-manual/sql-ref-syntax-qry-select-unpivot>.
1614+
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/constructs/unpivot)
1615+
/// [Databricks](https://docs.databricks.com/aws/en/sql/language-manual/sql-ref-syntax-qry-select-unpivot)
1616+
/// [BigQuery](https://docs.cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax#unpivot_operator)
1617+
/// [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)
16151618
Unpivot {
16161619
/// The input table to unpivot.
16171620
table: Box<TableFactor>,

src/parser/mod.rs

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13617,7 +13617,7 @@ impl<'a> Parser<'a> {
1361713617
Keyword::PIVOT => {
1361813618
self.expect_token(&Token::LParen)?;
1361913619
let aggregate_functions =
13620-
self.parse_comma_separated(Self::parse_aliased_function_call)?;
13620+
self.parse_comma_separated(Self::parse_pivot_aggregate_function)?;
1362113621
self.expect_keyword_is(Keyword::FOR)?;
1362213622
let value_column = self.parse_period_separated(|p| p.parse_identifier())?;
1362313623
self.expect_keyword_is(Keyword::IN)?;
@@ -16242,20 +16242,6 @@ impl<'a> Parser<'a> {
1624216242
})
1624316243
}
1624416244

16245-
fn parse_aliased_function_call(&mut self) -> Result<ExprWithAlias, ParserError> {
16246-
let function_name = match self.next_token().token {
16247-
Token::Word(w) => Ok(w.value),
16248-
_ => self.expected("a function identifier", self.peek_token()),
16249-
}?;
16250-
let expr = self.parse_function(ObjectName::from(vec![Ident::new(function_name)]))?;
16251-
let alias = if self.parse_keyword(Keyword::AS) {
16252-
Some(self.parse_identifier()?)
16253-
} else {
16254-
None
16255-
};
16256-
16257-
Ok(ExprWithAlias { expr, alias })
16258-
}
1625916245
/// Parses an expression with an optional alias
1626016246
///
1626116247
/// Examples:
@@ -16289,13 +16275,40 @@ impl<'a> Parser<'a> {
1628916275
Ok(ExprWithAlias { expr, alias })
1629016276
}
1629116277

16278+
/// Parse an expression followed by an optional alias; Unlike
16279+
/// [Self::parse_expr_with_alias] the "AS" keyword between the expression
16280+
/// and the alias is optional.
16281+
fn parse_expr_with_alias_optional_as_keyword(&mut self) -> Result<ExprWithAlias, ParserError> {
16282+
let expr = self.parse_expr()?;
16283+
let alias = self.parse_identifier_optional_alias()?;
16284+
Ok(ExprWithAlias { expr, alias })
16285+
}
16286+
16287+
/// Parses a plain function call with an optional alias for the `PIVOT` clause
16288+
fn parse_pivot_aggregate_function(&mut self) -> Result<ExprWithAlias, ParserError> {
16289+
let function_name = match self.next_token().token {
16290+
Token::Word(w) => Ok(w.value),
16291+
_ => self.expected("a function identifier", self.peek_token()),
16292+
}?;
16293+
let expr = self.parse_function(ObjectName::from(vec![Ident::new(function_name)]))?;
16294+
let alias = {
16295+
fn validator(explicit: bool, kw: &Keyword, parser: &mut Parser) -> bool {
16296+
// ~ for a PIVOT aggregate function the alias must not be a "FOR"; in any dialect
16297+
kw != &Keyword::FOR && parser.dialect.is_select_item_alias(explicit, kw, parser)
16298+
}
16299+
self.parse_optional_alias_inner(None, validator)?
16300+
};
16301+
Ok(ExprWithAlias { expr, alias })
16302+
}
16303+
1629216304
/// Parse a PIVOT table factor (ClickHouse/Oracle style pivot), returning a TableFactor.
1629316305
pub fn parse_pivot_table_factor(
1629416306
&mut self,
1629516307
table: TableFactor,
1629616308
) -> Result<TableFactor, ParserError> {
1629716309
self.expect_token(&Token::LParen)?;
16298-
let aggregate_functions = self.parse_comma_separated(Self::parse_aliased_function_call)?;
16310+
let aggregate_functions =
16311+
self.parse_comma_separated(Self::parse_pivot_aggregate_function)?;
1629916312
self.expect_keyword_is(Keyword::FOR)?;
1630016313
let value_column = if self.peek_token_ref().token == Token::LParen {
1630116314
self.parse_parenthesized_column_list_inner(Mandatory, false, |p| {
@@ -16317,7 +16330,9 @@ impl<'a> Parser<'a> {
1631716330
} else if self.peek_sub_query() {
1631816331
PivotValueSource::Subquery(self.parse_query()?)
1631916332
} else {
16320-
PivotValueSource::List(self.parse_comma_separated(Self::parse_expr_with_alias)?)
16333+
PivotValueSource::List(
16334+
self.parse_comma_separated(Self::parse_expr_with_alias_optional_as_keyword)?,
16335+
)
1632116336
};
1632216337
self.expect_token(&Token::RParen)?;
1632316338

tests/sqlparser_common.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11357,6 +11357,18 @@ fn parse_pivot_table() {
1135711357
verified_stmt(multiple_value_columns_sql).to_string(),
1135811358
multiple_value_columns_sql
1135911359
);
11360+
11361+
// assert optional "AS" keyword for aliases for pivot values
11362+
one_statement_parses_to(
11363+
"SELECT * FROM t PIVOT(SUM(1) FOR a.abc IN (1 x, 'two' y, three z))",
11364+
"SELECT * FROM t PIVOT(SUM(1) FOR a.abc IN (1 AS x, 'two' AS y, three AS z))",
11365+
);
11366+
11367+
// assert optional "AS" keyword for aliases for pivot aggregate function
11368+
one_statement_parses_to(
11369+
"SELECT * FROM t PIVOT(SUM(1) x, COUNT(42) y FOR a.abc IN (1))",
11370+
"SELECT * FROM t PIVOT(SUM(1) AS x, COUNT(42) AS y FOR a.abc IN (1))",
11371+
);
1136011372
}
1136111373

1136211374
#[test]

0 commit comments

Comments
 (0)