Skip to content

Commit 815ee5b

Browse files
committed
Add support for WITH FILL to OrderByExpr
ClickHouse supports the ORDER BY ... WITH FILL modifier: https://clickhouse.com/docs/en/sql-reference/statements/select/order-by#order-by-expr-with-fill-modifier WITH FILL itself supports a simple "from", "to", and "step" parameters, and a more sophisticated INTERPOLATE option.
1 parent 44d7a20 commit 815ee5b

File tree

6 files changed

+244
-8
lines changed

6 files changed

+244
-8
lines changed

src/ast/mod.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,14 @@ pub use self::operator::{BinaryOperator, UnaryOperator};
4343
pub use self::query::{
4444
AfterMatchSkip, ConnectBy, Cte, CteAsMaterialized, Distinct, EmptyMatchesMode,
4545
ExceptSelectItem, ExcludeSelectItem, ExprWithAlias, Fetch, ForClause, ForJson, ForXml,
46-
GroupByExpr, GroupByWithModifier, IdentWithAlias, IlikeSelectItem, Join, JoinConstraint,
47-
JoinOperator, JsonTableColumn, JsonTableColumnErrorHandling, LateralView, LockClause, LockType,
48-
MatchRecognizePattern, MatchRecognizeSymbol, Measure, NamedWindowDefinition, NamedWindowExpr,
49-
NonBlock, Offset, OffsetRows, OrderByExpr, PivotValueSource, Query, RenameSelectItem,
50-
RepetitionQuantifier, ReplaceSelectElement, ReplaceSelectItem, RowsPerMatch, Select,
51-
SelectInto, SelectItem, SetExpr, SetOperator, SetQuantifier, SymbolDefinition, Table,
52-
TableAlias, TableFactor, TableVersion, TableWithJoins, Top, TopQuantity, ValueTableMode,
53-
Values, WildcardAdditionalOptions, With,
46+
GroupByExpr, GroupByWithModifier, IdentWithAlias, IlikeSelectItem, Interpolation, Join,
47+
JoinConstraint, JoinOperator, JsonTableColumn, JsonTableColumnErrorHandling, LateralView,
48+
LockClause, LockType, MatchRecognizePattern, MatchRecognizeSymbol, Measure,
49+
NamedWindowDefinition, NamedWindowExpr, NonBlock, Offset, OffsetRows, OrderByExpr,
50+
PivotValueSource, Query, RenameSelectItem, RepetitionQuantifier, ReplaceSelectElement,
51+
ReplaceSelectItem, RowsPerMatch, Select, SelectInto, SelectItem, SetExpr, SetOperator,
52+
SetQuantifier, SymbolDefinition, Table, TableAlias, TableFactor, TableVersion, TableWithJoins,
53+
Top, TopQuantity, ValueTableMode, Values, WildcardAdditionalOptions, With, WithFill,
5454
};
5555
pub use self::value::{
5656
escape_double_quote_string, escape_quoted_string, DateTimeField, DollarQuotedString,

src/ast/query.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1627,6 +1627,9 @@ pub struct OrderByExpr {
16271627
pub asc: Option<bool>,
16281628
/// Optional `NULLS FIRST` or `NULLS LAST`
16291629
pub nulls_first: Option<bool>,
1630+
/// Optional: `WITH FILL`
1631+
/// Supported by [ClickHouse syntax]: <https://clickhouse.com/docs/en/sql-reference/statements/select/order-by#order-by-expr-with-fill-modifier>
1632+
pub with_fill: Option<WithFill>,
16301633
}
16311634

16321635
impl fmt::Display for OrderByExpr {
@@ -1642,6 +1645,62 @@ impl fmt::Display for OrderByExpr {
16421645
Some(false) => write!(f, " NULLS LAST")?,
16431646
None => (),
16441647
}
1648+
if let Some(ref with_fill) = self.with_fill {
1649+
write!(f, " {}", with_fill)?
1650+
}
1651+
Ok(())
1652+
}
1653+
}
1654+
1655+
/// ClickHouse `WITH FILL` modifier for `ORDER BY` clause.
1656+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1657+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1658+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
1659+
pub struct WithFill {
1660+
pub from: Option<Expr>,
1661+
pub to: Option<Expr>,
1662+
pub step: Option<Expr>,
1663+
pub interpolate: Vec<Interpolation>,
1664+
}
1665+
1666+
impl fmt::Display for WithFill {
1667+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1668+
write!(f, "WITH FILL")?;
1669+
if let Some(ref from) = self.from {
1670+
write!(f, " FROM {}", from)?;
1671+
}
1672+
if let Some(ref to) = self.to {
1673+
write!(f, " TO {}", to)?;
1674+
}
1675+
if let Some(ref step) = self.step {
1676+
write!(f, " STEP {}", step)?;
1677+
}
1678+
if !self.interpolate.is_empty() {
1679+
write!(
1680+
f,
1681+
" INTERPOLATE ({})",
1682+
display_comma_separated(&self.interpolate)
1683+
)?;
1684+
}
1685+
Ok(())
1686+
}
1687+
}
1688+
1689+
/// ClickHouse `INTERPOLATE` clause for use in `WITH FILL` modifier.
1690+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1691+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1692+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
1693+
pub struct Interpolation {
1694+
pub column: Expr,
1695+
pub formula: Option<Expr>,
1696+
}
1697+
1698+
impl fmt::Display for Interpolation {
1699+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1700+
write!(f, "{}", self.column)?;
1701+
if let Some(ref formula) = self.formula {
1702+
write!(f, " AS {}", formula)?;
1703+
}
16451704
Ok(())
16461705
}
16471706
}

src/keywords.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,7 @@ define_keywords!(
297297
FILE,
298298
FILES,
299299
FILE_FORMAT,
300+
FILL,
300301
FILTER,
301302
FIRST,
302303
FIRST_VALUE,
@@ -382,6 +383,7 @@ define_keywords!(
382383
INT64,
383384
INT8,
384385
INTEGER,
386+
INTERPOLATE,
385387
INTERSECT,
386388
INTERSECTION,
387389
INTERVAL,
@@ -678,6 +680,7 @@ define_keywords!(
678680
STDDEV_SAMP,
679681
STDIN,
680682
STDOUT,
683+
STEP,
681684
STORAGE_INTEGRATION,
682685
STORED,
683686
STRICT,

src/parser/mod.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10408,13 +10408,75 @@ impl<'a> Parser<'a> {
1040810408
None
1040910409
};
1041010410

10411+
let with_fill = if self.parse_keywords(&[Keyword::WITH, Keyword::FILL]) {
10412+
Some(self.parse_with_fill()?)
10413+
} else {
10414+
None
10415+
};
10416+
1041110417
Ok(OrderByExpr {
1041210418
expr,
1041310419
asc,
1041410420
nulls_first,
10421+
with_fill,
10422+
})
10423+
}
10424+
10425+
// Parse a WITH FILL clause (ClickHouse dialect)
10426+
// that follow the WITH FILL keywords in a ORDER BY clause
10427+
pub fn parse_with_fill(&mut self) -> Result<WithFill, ParserError> {
10428+
let from = if self.parse_keyword(Keyword::FROM) {
10429+
Some(self.parse_expr()?)
10430+
} else {
10431+
None
10432+
};
10433+
10434+
let to = if self.parse_keyword(Keyword::TO) {
10435+
Some(self.parse_expr()?)
10436+
} else {
10437+
None
10438+
};
10439+
10440+
let step = if self.parse_keyword(Keyword::STEP) {
10441+
Some(self.parse_expr()?)
10442+
} else {
10443+
None
10444+
};
10445+
10446+
let interpolate =
10447+
if self.parse_keyword(Keyword::INTERPOLATE) && self.consume_token(&Token::LParen) {
10448+
let interpolations = self.parse_interpolations()?;
10449+
self.expect_token(&Token::RParen)?;
10450+
interpolations
10451+
} else {
10452+
vec![]
10453+
};
10454+
10455+
Ok(WithFill {
10456+
from,
10457+
to,
10458+
step,
10459+
interpolate,
1041510460
})
1041610461
}
1041710462

10463+
// Parse a set of comma seperated INTERPOLATE expressions (ClickHouse dialect)
10464+
// that follow the INTERPOLATE keyword in a WITH FILL clause
10465+
pub fn parse_interpolations(&mut self) -> Result<Vec<Interpolation>, ParserError> {
10466+
self.parse_comma_separated(|p| p.parse_interpolation())
10467+
}
10468+
10469+
// Parse a INTERPOLATE expression (ClickHouse dialect)
10470+
pub fn parse_interpolation(&mut self) -> Result<Interpolation, ParserError> {
10471+
let column = self.parse_expr()?;
10472+
let formula = if self.parse_keyword(Keyword::AS) {
10473+
Some(self.parse_expr()?)
10474+
} else {
10475+
None
10476+
};
10477+
Ok(Interpolation { column, formula })
10478+
}
10479+
1041810480
/// Parse a TOP clause, MSSQL equivalent of LIMIT,
1041910481
/// that follows after `SELECT [DISTINCT]`.
1042010482
pub fn parse_top(&mut self) -> Result<Top, ParserError> {

0 commit comments

Comments
 (0)