Skip to content

Commit 39322c8

Browse files
committed
feat: PERCENTILE_CONT function support
1 parent 6a54d27 commit 39322c8

File tree

3 files changed

+60
-0
lines changed

3 files changed

+60
-0
lines changed

Diff for: src/ast/mod.rs

+22
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,8 @@ pub enum Expr {
348348
ListAgg(ListAgg),
349349
/// The `ARRAY_AGG` function `SELECT ARRAY_AGG(... ORDER BY ...)`
350350
ArrayAgg(ArrayAgg),
351+
/// The `PERCENTILE_CONT` function `SELECT PERCENTILE_CONT(...) WITHIN GROUP (ORDER BY ...)`
352+
PercentileCont(PercentileCont),
351353
/// The `GROUPING SETS` expr.
352354
GroupingSets(Vec<Vec<Expr>>),
353355
/// The `CUBE` expr.
@@ -549,6 +551,7 @@ impl fmt::Display for Expr {
549551
Expr::ArraySubquery(s) => write!(f, "ARRAY({})", s),
550552
Expr::ListAgg(listagg) => write!(f, "{}", listagg),
551553
Expr::ArrayAgg(arrayagg) => write!(f, "{}", arrayagg),
554+
Expr::PercentileCont(percentilecont) => write!(f, "{}", percentilecont),
552555
Expr::GroupingSets(sets) => {
553556
write!(f, "GROUPING SETS (")?;
554557
let mut sep = "";
@@ -2523,6 +2526,25 @@ impl fmt::Display for ArrayAgg {
25232526
}
25242527
}
25252528

2529+
/// A `PERCENTILE_CONT` invocation `PERCENTILE_CONT( <expr> ) WITHIN GROUP (ORDER BY <sort_expr> )``
2530+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2531+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2532+
pub struct PercentileCont {
2533+
pub expr: Box<Expr>,
2534+
pub within_group: Box<OrderByExpr>,
2535+
}
2536+
2537+
impl fmt::Display for PercentileCont {
2538+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2539+
write!(
2540+
f,
2541+
"PERCENTILE_CONT({}) WITHIN GROUP (ORDER BY {})",
2542+
self.expr, self.within_group,
2543+
)?;
2544+
Ok(())
2545+
}
2546+
}
2547+
25262548
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
25272549
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
25282550
pub enum ObjectType {

Diff for: src/parser.rs

+14
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,7 @@ impl<'a> Parser<'a> {
464464
Keyword::TRIM => self.parse_trim_expr(),
465465
Keyword::INTERVAL => self.parse_literal_interval(),
466466
Keyword::LISTAGG => self.parse_listagg_expr(),
467+
Keyword::PERCENTILE_CONT => self.parse_percentile_cont_expr(),
467468
// Treat ARRAY[1,2,3] as an array [1,2,3], otherwise try as function call
468469
Keyword::ARRAY if self.peek_token() == Token::LBracket => {
469470
self.expect_token(&Token::LBracket)?;
@@ -1056,6 +1057,19 @@ impl<'a> Parser<'a> {
10561057
}))
10571058
}
10581059

1060+
/// Parse a SQL PERCENTILE_CONT expression, e.g. `PERCENTILE_CONT(...) WITHIN GROUP (ORDER BY ...)`.
1061+
pub fn parse_percentile_cont_expr(&mut self) -> Result<Expr, ParserError> {
1062+
self.expect_token(&Token::LParen)?;
1063+
let expr = Box::new(self.parse_expr()?);
1064+
self.expect_token(&Token::RParen)?;
1065+
self.expect_keywords(&[Keyword::WITHIN, Keyword::GROUP])?;
1066+
self.expect_token(&Token::LParen)?;
1067+
self.expect_keywords(&[Keyword::ORDER, Keyword::BY])?;
1068+
let within_group = Box::new(self.parse_order_by_expr()?);
1069+
self.expect_token(&Token::RParen)?;
1070+
Ok(Expr::PercentileCont(PercentileCont { expr, within_group }))
1071+
}
1072+
10591073
// This function parses date/time fields for both the EXTRACT function-like
10601074
// operator and interval qualifiers. EXTRACT supports a wider set of
10611075
// date/time fields than interval qualifiers, so this function may need to

Diff for: tests/sqlparser_common.rs

+24
Original file line numberDiff line numberDiff line change
@@ -1736,6 +1736,30 @@ fn parse_array_agg_func() {
17361736
}
17371737
}
17381738

1739+
#[test]
1740+
fn parse_percentile_cont() {
1741+
let sql = "SELECT PERCENTILE_CONT(0.25) WITHIN GROUP (ORDER BY name ASC NULLS FIRST)";
1742+
let select = verified_only_select(sql);
1743+
1744+
#[cfg(feature = "bigdecimal")]
1745+
let value = bigdecimal::BigDecimal::from(0);
1746+
#[cfg(not(feature = "bigdecimal"))]
1747+
let value = "0".to_string();
1748+
let expr = Box::new(Expr::Value(Value::Number(value, false)));
1749+
let within_group = Box::new(OrderByExpr {
1750+
expr: Expr::Identifier(Ident {
1751+
value: "name".to_string(),
1752+
quote_style: None,
1753+
}),
1754+
asc: Some(true),
1755+
nulls_first: Some(true),
1756+
});
1757+
assert_eq!(
1758+
&Expr::PercentileCont(PercentileCont { expr, within_group }),
1759+
expr_from_projection(only(&select.projection))
1760+
);
1761+
}
1762+
17391763
#[test]
17401764
fn parse_create_table() {
17411765
let sql = "CREATE TABLE uk_cities (\

0 commit comments

Comments
 (0)