Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions src/ast/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1653,6 +1653,21 @@ pub enum TableFactor {
/// Optional alias for the resulting table.
alias: Option<TableAlias>,
},
/// Object unpivoting on a SUPER expression in the FROM clause.
///
/// Syntax:
/// ```sql
/// UNPIVOT expression AS value_alias [AT attribute_alias]
/// ```

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add a link to the docs describing the syntax?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added link to docs

/// [Redshift](https://docs.aws.amazon.com/redshift/latest/dg/query-super.html#unpivoting)
UnpivotExpr {
/// SUPER expression to unpivot.
expression: Expr,
/// Alias for the generated unpivoted value.
value_alias: Ident,
/// Optional alias for the generated attribute key/index.
attribute_alias: Option<Ident>,
},
/// A `MATCH_RECOGNIZE` operation on a table.
///
/// See <https://docs.snowflake.com/en/sql-reference/constructs/match_recognize>.
Expand Down Expand Up @@ -2422,6 +2437,17 @@ impl fmt::Display for TableFactor {
}
Ok(())
}
TableFactor::UnpivotExpr {
expression,
value_alias,
attribute_alias,
} => {
write!(f, "UNPIVOT {expression} AS {value_alias}")?;
if let Some(attribute_alias) = attribute_alias {
write!(f, " AT {attribute_alias}")?;
}
Ok(())
}
TableFactor::MatchRecognize {
table,
partition_by,
Expand Down
9 changes: 9 additions & 0 deletions src/ast/spans.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2056,6 +2056,15 @@ impl Spanned for TableFactor {
.chain(columns.iter().map(|ilist| ilist.span()))
.chain(alias.as_ref().map(|alias| alias.span())),
),
TableFactor::UnpivotExpr {
expression,
value_alias,
attribute_alias,
} => union_spans(
core::iter::once(expression.span())
.chain(core::iter::once(value_alias.span))
.chain(attribute_alias.as_ref().map(|alias| alias.span)),
),
TableFactor::MatchRecognize {
table,
partition_by,
Expand Down
10 changes: 10 additions & 0 deletions src/dialect/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1267,6 +1267,16 @@ pub trait Dialect: Debug + Any {
false
}

/// Returns true if the dialect supports object-unpivot table factors in the FROM clause.
///
/// Syntax:
/// ```sql
/// UNPIVOT expression AS value_alias [AT attribute_alias]
/// ```
fn supports_unpivot_expr(&self) -> bool {
false
}

/// Returns true if the dialect supports the `CONSTRAINT` keyword without a name
/// in table constraint definitions.
///
Expand Down
4 changes: 4 additions & 0 deletions src/dialect/redshift.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ impl Dialect for RedshiftSqlDialect {
true
}

fn supports_unpivot_expr(&self) -> bool {
true
}

fn supports_string_escape_constant(&self) -> bool {
true
}
Expand Down
31 changes: 31 additions & 0 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16494,6 +16494,12 @@ impl<'a> Parser<'a> {
// `(mytable AS alias)`
alias.replace(outer_alias);
}
TableFactor::UnpivotExpr { .. } => {
return Err(ParserError::ParserError(
"alias after parenthesized UNPIVOT expression is not supported"
.to_string(),
))
}
};
}
// Do not store the extra set of parens in the AST
Expand Down Expand Up @@ -16575,6 +16581,10 @@ impl<'a> Parser<'a> {
with_offset_alias,
with_ordinality,
})
} else if self.dialect.supports_unpivot_expr()
&& self.parse_keyword(Keyword::UNPIVOT)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we change this to self.peek(UNPIVOT) so that the parse_unpivot_expr_table_factor is standalone since we're making it a public function?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you plwase explain it?

{
self.parse_unpivot_expr_table_factor()
} else if self.parse_keyword_with_tokens(Keyword::JSON_TABLE, &[Token::LParen]) {
let json_expr = self.parse_expr()?;
self.expect_token(&Token::Comma)?;
Expand Down Expand Up @@ -17573,6 +17583,27 @@ impl<'a> Parser<'a> {
})
}

/// Parse an object UNPIVOT table factor in FROM clause.
///
/// Syntax:
/// `UNPIVOT expression AS value_alias [AT attribute_alias]`
pub fn parse_unpivot_expr_table_factor(&mut self) -> Result<TableFactor, ParserError> {
let expression = self.parse_expr()?;
self.expect_keyword_is(Keyword::AS)?;
let value_alias = self.parse_identifier()?;
let attribute_alias = if self.parse_keyword(Keyword::AT) {
Some(self.parse_identifier()?)
} else {
None
};

Ok(TableFactor::UnpivotExpr {
expression,
value_alias,
attribute_alias,
})
}

/// Parse a JOIN constraint (`NATURAL`, `ON <expr>`, `USING (...)`, or no constraint).
pub fn parse_join_constraint(&mut self, natural: bool) -> Result<JoinConstraint, ParserError> {
if natural {
Expand Down
7 changes: 7 additions & 0 deletions tests/sqlparser_redshift.rs
Original file line number Diff line number Diff line change
Expand Up @@ -542,3 +542,10 @@ fn test_partiql_from_alias_with_at_index() {
_ => panic!("expected table factor"),
}
}

#[test]
fn parse_unpivot_expression() {
redshift().verified_stmt("SELECT t.id, k, v FROM test_colors AS t, UNPIVOT t.count_by_color AS v AT k",);
redshift().verified_stmt("SELECT t.id, k, v FROM test_colors AS t, UNPIVOT t AS v AT k");
}