Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 56848b0

Browse files
feat: support multiple value for pivot (#1970)
1 parent 4b8797e commit 56848b0

File tree

5 files changed

+104
-7
lines changed

5 files changed

+104
-7
lines changed

‎src/ast/query.rs‎

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1336,7 +1336,7 @@ pub enum TableFactor {
13361336
Pivot {
13371337
table: Box<TableFactor>,
13381338
aggregate_functions: Vec<ExprWithAlias>, // Function expression
1339-
value_column: Vec<Ident>,
1339+
value_column: Vec<Expr>,
13401340
value_source: PivotValueSource,
13411341
default_on_null: Option<Expr>,
13421342
alias: Option<TableAlias>,
@@ -2011,10 +2011,15 @@ impl fmt::Display for TableFactor {
20112011
} => {
20122012
write!(
20132013
f,
2014-
"{table} PIVOT({} FOR {} IN ({value_source})",
2014+
"{table} PIVOT({} FOR ",
20152015
display_comma_separated(aggregate_functions),
2016-
Expr::CompoundIdentifier(value_column.to_vec()),
20172016
)?;
2017+
if value_column.len() == 1 {
2018+
write!(f, "{}", value_column[0])?;
2019+
} else {
2020+
write!(f, "({})", display_comma_separated(value_column))?;
2021+
}
2022+
write!(f, " IN ({value_source})")?;
20182023
if let Some(expr) = default_on_null {
20192024
write!(f, " DEFAULT ON NULL ({expr})")?;
20202025
}

‎src/ast/spans.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2001,7 +2001,7 @@ impl Spanned for TableFactor {
20012001
} => union_spans(
20022002
core::iter::once(table.span())
20032003
.chain(aggregate_functions.iter().map(|i| i.span()))
2004-
.chain(value_column.iter().map(|i| i.span))
2004+
.chain(value_column.iter().map(|i| i.span()))
20052005
.chain(core::iter::once(value_source.span()))
20062006
.chain(default_on_null.as_ref().map(|i| i.span()))
20072007
.chain(alias.as_ref().map(|i| i.span())),

‎src/ast/visitor.rs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -884,6 +884,8 @@ mod tests {
884884
"PRE: EXPR: a.amount",
885885
"POST: EXPR: a.amount",
886886
"POST: EXPR: SUM(a.amount)",
887+
"PRE: EXPR: a.MONTH",
888+
"POST: EXPR: a.MONTH",
887889
"PRE: EXPR: 'JAN'",
888890
"POST: EXPR: 'JAN'",
889891
"PRE: EXPR: 'FEB'",

‎src/parser/mod.rs‎

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11067,6 +11067,18 @@ impl<'a> Parser<'a> {
1106711067
self.parse_parenthesized_column_list_inner(optional, allow_empty, |p| p.parse_identifier())
1106811068
}
1106911069

11070+
pub fn parse_parenthesized_compound_identifier_list(
11071+
&mut self,
11072+
optional: IsOptional,
11073+
allow_empty: bool,
11074+
) -> Result<Vec<Expr>, ParserError> {
11075+
self.parse_parenthesized_column_list_inner(optional, allow_empty, |p| {
11076+
Ok(Expr::CompoundIdentifier(
11077+
p.parse_period_separated(|p| p.parse_identifier())?,
11078+
))
11079+
})
11080+
}
11081+
1107011082
/// Parses a parenthesized comma-separated list of index columns, which can be arbitrary
1107111083
/// expressions with ordering information (and an opclass in some dialects).
1107211084
fn parse_parenthesized_index_column_list(&mut self) -> Result<Vec<IndexColumn>, ParserError> {
@@ -14187,7 +14199,13 @@ impl<'a> Parser<'a> {
1418714199
self.expect_token(&Token::LParen)?;
1418814200
let aggregate_functions = self.parse_comma_separated(Self::parse_aliased_function_call)?;
1418914201
self.expect_keyword_is(Keyword::FOR)?;
14190-
let value_column = self.parse_period_separated(|p| p.parse_identifier())?;
14202+
let value_column = if self.peek_token_ref().token == Token::LParen {
14203+
self.parse_parenthesized_column_list_inner(Mandatory, false, |p| {
14204+
p.parse_subexpr(self.dialect.prec_value(Precedence::Between))
14205+
})?
14206+
} else {
14207+
vec![self.parse_subexpr(self.dialect.prec_value(Precedence::Between))?]
14208+
};
1419114209
self.expect_keyword_is(Keyword::IN)?;
1419214210

1419314211
self.expect_token(&Token::LParen)?;

‎tests/sqlparser_common.rs‎

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10961,7 +10961,10 @@ fn parse_pivot_table() {
1096110961
expected_function("b", Some("t")),
1096210962
expected_function("c", Some("u")),
1096310963
],
10964-
value_column: vec![Ident::new("a"), Ident::new("MONTH")],
10964+
value_column: vec![Expr::CompoundIdentifier(vec![
10965+
Ident::new("a"),
10966+
Ident::new("MONTH")
10967+
])],
1096510968
value_source: PivotValueSource::List(vec![
1096610969
ExprWithAlias {
1096710970
expr: Expr::value(number("1")),
@@ -11008,6 +11011,75 @@ fn parse_pivot_table() {
1100811011
verified_stmt(sql_without_table_alias).to_string(),
1100911012
sql_without_table_alias
1101011013
);
11014+
11015+
let multiple_value_columns_sql = concat!(
11016+
"SELECT * FROM person ",
11017+
"PIVOT(",
11018+
"SUM(age) AS a, AVG(class) AS c ",
11019+
"FOR (name, age) IN (('John', 30) AS c1, ('Mike', 40) AS c2))",
11020+
);
11021+
11022+
assert_eq!(
11023+
verified_only_select(multiple_value_columns_sql).from[0].relation,
11024+
Pivot {
11025+
table: Box::new(TableFactor::Table {
11026+
name: ObjectName::from(vec![Ident::new("person")]),
11027+
alias: None,
11028+
args: None,
11029+
with_hints: vec![],
11030+
version: None,
11031+
partitions: vec![],
11032+
with_ordinality: false,
11033+
json_path: None,
11034+
sample: None,
11035+
index_hints: vec![],
11036+
}),
11037+
aggregate_functions: vec![
11038+
ExprWithAlias {
11039+
expr: call("SUM", [Expr::Identifier(Ident::new("age"))]),
11040+
alias: Some(Ident::new("a"))
11041+
},
11042+
ExprWithAlias {
11043+
expr: call("AVG", [Expr::Identifier(Ident::new("class"))]),
11044+
alias: Some(Ident::new("c"))
11045+
},
11046+
],
11047+
value_column: vec![
11048+
Expr::Identifier(Ident::new("name")),
11049+
Expr::Identifier(Ident::new("age")),
11050+
],
11051+
value_source: PivotValueSource::List(vec![
11052+
ExprWithAlias {
11053+
expr: Expr::Tuple(vec![
11054+
Expr::Value(
11055+
(Value::SingleQuotedString("John".to_string())).with_empty_span()
11056+
),
11057+
Expr::Value(
11058+
(Value::Number("30".parse().unwrap(), false)).with_empty_span()
11059+
),
11060+
]),
11061+
alias: Some(Ident::new("c1"))
11062+
},
11063+
ExprWithAlias {
11064+
expr: Expr::Tuple(vec![
11065+
Expr::Value(
11066+
(Value::SingleQuotedString("Mike".to_string())).with_empty_span()
11067+
),
11068+
Expr::Value(
11069+
(Value::Number("40".parse().unwrap(), false)).with_empty_span()
11070+
),
11071+
]),
11072+
alias: Some(Ident::new("c2"))
11073+
},
11074+
]),
11075+
default_on_null: None,
11076+
alias: None,
11077+
}
11078+
);
11079+
assert_eq!(
11080+
verified_stmt(multiple_value_columns_sql).to_string(),
11081+
multiple_value_columns_sql
11082+
);
1101111083
}
1101211084

1101311085
#[test]
@@ -11340,7 +11412,7 @@ fn parse_pivot_unpivot_table() {
1134011412
expr: call("sum", [Expr::Identifier(Ident::new("population"))]),
1134111413
alias: None
1134211414
}],
11343-
value_column: vec![Ident::new("year")],
11415+
value_column: vec![Expr::Identifier(Ident::new("year"))],
1134411416
value_source: PivotValueSource::List(vec![
1134511417
ExprWithAlias {
1134611418
expr: Expr::Value(

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /