Skip to content

Commit c645682

Browse files
committed
snowflake: handle pivot
1 parent 781170a commit c645682

3 files changed

Lines changed: 65 additions & 2 deletions

File tree

src/ast/query.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,12 @@ pub enum TableFactor {
267267
expr: Expr,
268268
alias: Option<TableAlias>,
269269
},
270+
/// https://docs.snowflake.com/en/sql-reference/constructs/pivot.html
271+
Pivot {
272+
expr: Expr,
273+
val: Ident,
274+
pivot_vals: Vec<Expr>,
275+
},
270276
/// Represents a parenthesized table factor. The SQL spec only allows a
271277
/// join expression (`(foo <JOIN> bar [ <JOIN> baz ... ])`) to be nested,
272278
/// possibly several times.
@@ -332,6 +338,19 @@ impl fmt::Display for TableFactor {
332338
}
333339
Ok(())
334340
}
341+
TableFactor::Pivot {
342+
expr,
343+
val,
344+
pivot_vals,
345+
} => {
346+
write!(f, "({} FOR {} IN (", expr, val)?;
347+
let mut delim = "";
348+
for pivot_val in pivot_vals {
349+
write!(f, "{}{}", delim, pivot_val)?;
350+
delim = ", ";
351+
}
352+
write!(f, "))")
353+
}
335354
TableFactor::NestedJoin(table_reference) => write!(f, "({})", table_reference),
336355
}
337356
}
@@ -414,6 +433,8 @@ impl fmt::Display for Join {
414433
self.relation,
415434
suffix(constraint)
416435
),
436+
JoinOperator::Pivot => write!(f, " PIVOT {}", self.relation),
437+
JoinOperator::Unpivot => write!(f, " UNPIVOT {}", self.relation),
417438
JoinOperator::CrossJoin => write!(f, " CROSS JOIN {}", self.relation),
418439
JoinOperator::CrossApply => write!(f, " CROSS APPLY {}", self.relation),
419440
JoinOperator::OuterApply => write!(f, " OUTER APPLY {}", self.relation),
@@ -428,6 +449,11 @@ pub enum JoinOperator {
428449
LeftOuter(JoinConstraint),
429450
RightOuter(JoinConstraint),
430451
FullOuter(JoinConstraint),
452+
/// [UN]PIVOT not actually a join but it seems to fit here syntactically
453+
/// https://docs.snowflake.com/en/sql-reference/constructs/pivot.html
454+
Pivot,
455+
/// https://docs.snowflake.com/en/sql-reference/constructs/unpivot.html
456+
Unpivot,
431457
CrossJoin,
432458
/// CROSS APPLY (non-standard)
433459
CrossApply,

src/dialect/keywords.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,7 @@ define_keywords!(
322322
PERCENTILE_DISC,
323323
PERCENT_RANK,
324324
PERIOD,
325+
PIVOT,
325326
PORTION,
326327
POSITION,
327328
POSITION_REGEX,
@@ -441,6 +442,7 @@ define_keywords!(
441442
UNIQUE,
442443
UNKNOWN,
443444
UNNEST,
445+
UNPIVOT,
444446
UPDATE,
445447
UPPER,
446448
USER,

src/parser.rs

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1802,10 +1802,17 @@ impl<'a> Parser<'a> {
18021802
&mut self,
18031803
reserved_kwds: &[Keyword],
18041804
) -> Result<Option<TableAlias>, ParserError> {
1805+
let orig_index = self.index;
18051806
match self.parse_optional_alias(reserved_kwds)? {
18061807
Some(name) => {
1807-
let columns = self.parse_parenthesized_column_list(Optional)?;
1808-
Ok(Some(TableAlias { name, columns }))
1808+
if let Ok(columns) = self.parse_parenthesized_column_list(Optional) {
1809+
Ok(Some(TableAlias { name, columns }))
1810+
} else {
1811+
// if column list doesn't parse correctly, reset back to
1812+
// original state
1813+
self.index = orig_index;
1814+
Ok(None)
1815+
}
18091816
}
18101817
None => Ok(None),
18111818
}
@@ -2176,6 +2183,16 @@ impl<'a> Parser<'a> {
21762183
relation: self.parse_table_factor()?,
21772184
join_operator: JoinOperator::OuterApply,
21782185
}
2186+
} else if self.parse_keyword(Keyword::PIVOT) {
2187+
Join {
2188+
relation: self.parse_pivot_body()?,
2189+
join_operator: JoinOperator::Pivot,
2190+
}
2191+
} else if self.parse_keyword(Keyword::UNPIVOT) {
2192+
Join {
2193+
relation: self.parse_pivot_body()?,
2194+
join_operator: JoinOperator::Unpivot,
2195+
}
21792196
} else {
21802197
let natural = self.parse_keyword(Keyword::NATURAL);
21812198
let peek_keyword = if let Token::Word(w) = self.peek_token() {
@@ -2315,6 +2332,7 @@ impl<'a> Parser<'a> {
23152332
// `(mytable AS alias)`
23162333
alias.replace(outer_alias);
23172334
}
2335+
TableFactor::Pivot { .. } => unreachable!(),
23182336
TableFactor::NestedJoin(_) => unreachable!(),
23192337
};
23202338
}
@@ -2354,6 +2372,23 @@ impl<'a> Parser<'a> {
23542372
}
23552373
}
23562374

2375+
pub fn parse_pivot_body(&mut self) -> Result<TableFactor, ParserError> {
2376+
self.expect_token(&Token::LParen)?;
2377+
let expr = self.parse_expr()?;
2378+
self.expect_keyword(Keyword::FOR)?;
2379+
let val = self.parse_identifier()?;
2380+
self.expect_keyword(Keyword::IN)?;
2381+
self.expect_token(&Token::LParen)?;
2382+
let pivot_vals = self.parse_comma_separated(Parser::parse_expr)?;
2383+
self.expect_token(&Token::RParen)?;
2384+
self.expect_token(&Token::RParen)?;
2385+
Ok(TableFactor::Pivot {
2386+
expr,
2387+
val,
2388+
pivot_vals,
2389+
})
2390+
}
2391+
23572392
pub fn parse_derived_table_factor(
23582393
&mut self,
23592394
lateral: IsLateral,

0 commit comments

Comments
 (0)