Skip to content

Commit 845a1aa

Browse files
nickprestaalamb
andauthored
[ClickHouse] Add support for WITH FILL to OrderByExpr (#1330)
Co-authored-by: Andrew Lamb <[email protected]>
1 parent 20f7ac5 commit 845a1aa

9 files changed

+397
-47
lines changed

src/ast/mod.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,15 @@ 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-
FormatClause, GroupByExpr, GroupByWithModifier, IdentWithAlias, IlikeSelectItem, 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, Setting, SymbolDefinition, Table, TableAlias, TableFactor, TableVersion,
53-
TableWithJoins, Top, TopQuantity, ValueTableMode, Values, WildcardAdditionalOptions, With,
46+
FormatClause, GroupByExpr, GroupByWithModifier, IdentWithAlias, IlikeSelectItem, Interpolate,
47+
InterpolateExpr, Join, JoinConstraint, JoinOperator, JsonTableColumn,
48+
JsonTableColumnErrorHandling, LateralView, LockClause, LockType, MatchRecognizePattern,
49+
MatchRecognizeSymbol, Measure, NamedWindowDefinition, NamedWindowExpr, NonBlock, Offset,
50+
OffsetRows, OrderBy, OrderByExpr, PivotValueSource, Query, RenameSelectItem,
51+
RepetitionQuantifier, ReplaceSelectElement, ReplaceSelectItem, RowsPerMatch, Select,
52+
SelectInto, SelectItem, SetExpr, SetOperator, SetQuantifier, Setting, SymbolDefinition, Table,
53+
TableAlias, TableFactor, TableVersion, TableWithJoins, Top, TopQuantity, ValueTableMode,
54+
Values, WildcardAdditionalOptions, With, WithFill,
5455
};
5556
pub use self::value::{
5657
escape_double_quote_string, escape_quoted_string, DateTimeField, DollarQuotedString,

src/ast/query.rs

Lines changed: 88 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ pub struct Query {
3333
/// SELECT or UNION / EXCEPT / INTERSECT
3434
pub body: Box<SetExpr>,
3535
/// ORDER BY
36-
pub order_by: Vec<OrderByExpr>,
36+
pub order_by: Option<OrderBy>,
3737
/// `LIMIT { <N> | ALL }`
3838
pub limit: Option<Expr>,
3939

@@ -67,8 +67,17 @@ impl fmt::Display for Query {
6767
write!(f, "{with} ")?;
6868
}
6969
write!(f, "{}", self.body)?;
70-
if !self.order_by.is_empty() {
71-
write!(f, " ORDER BY {}", display_comma_separated(&self.order_by))?;
70+
if let Some(ref order_by) = self.order_by {
71+
write!(f, " ORDER BY")?;
72+
if !order_by.exprs.is_empty() {
73+
write!(f, " {}", display_comma_separated(&order_by.exprs))?;
74+
}
75+
if let Some(ref interpolate) = order_by.interpolate {
76+
match &interpolate.exprs {
77+
Some(exprs) => write!(f, " INTERPOLATE ({})", display_comma_separated(exprs))?,
78+
None => write!(f, " INTERPOLATE")?,
79+
}
80+
}
7281
}
7382
if let Some(ref limit) = self.limit {
7483
write!(f, " LIMIT {limit}")?;
@@ -1668,6 +1677,18 @@ pub enum JoinConstraint {
16681677
None,
16691678
}
16701679

1680+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1681+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1682+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
1683+
pub struct OrderBy {
1684+
pub exprs: Vec<OrderByExpr>,
1685+
/// Optional: `INTERPOLATE`
1686+
/// Supported by [ClickHouse syntax]
1687+
///
1688+
/// [ClickHouse syntax]: <https://clickhouse.com/docs/en/sql-reference/statements/select/order-by#order-by-expr-with-fill-modifier>
1689+
pub interpolate: Option<Interpolate>,
1690+
}
1691+
16711692
/// An `ORDER BY` expression
16721693
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
16731694
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
@@ -1678,6 +1699,9 @@ pub struct OrderByExpr {
16781699
pub asc: Option<bool>,
16791700
/// Optional `NULLS FIRST` or `NULLS LAST`
16801701
pub nulls_first: Option<bool>,
1702+
/// Optional: `WITH FILL`
1703+
/// Supported by [ClickHouse syntax]: <https://clickhouse.com/docs/en/sql-reference/statements/select/order-by#order-by-expr-with-fill-modifier>
1704+
pub with_fill: Option<WithFill>,
16811705
}
16821706

16831707
impl fmt::Display for OrderByExpr {
@@ -1693,6 +1717,67 @@ impl fmt::Display for OrderByExpr {
16931717
Some(false) => write!(f, " NULLS LAST")?,
16941718
None => (),
16951719
}
1720+
if let Some(ref with_fill) = self.with_fill {
1721+
write!(f, " {}", with_fill)?
1722+
}
1723+
Ok(())
1724+
}
1725+
}
1726+
1727+
/// ClickHouse `WITH FILL` modifier for `ORDER BY` clause.
1728+
/// Supported by [ClickHouse syntax]
1729+
///
1730+
/// [ClickHouse syntax]: <https://clickhouse.com/docs/en/sql-reference/statements/select/order-by#order-by-expr-with-fill-modifier>
1731+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1732+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1733+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
1734+
pub struct WithFill {
1735+
pub from: Option<Expr>,
1736+
pub to: Option<Expr>,
1737+
pub step: Option<Expr>,
1738+
}
1739+
1740+
impl fmt::Display for WithFill {
1741+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1742+
write!(f, "WITH FILL")?;
1743+
if let Some(ref from) = self.from {
1744+
write!(f, " FROM {}", from)?;
1745+
}
1746+
if let Some(ref to) = self.to {
1747+
write!(f, " TO {}", to)?;
1748+
}
1749+
if let Some(ref step) = self.step {
1750+
write!(f, " STEP {}", step)?;
1751+
}
1752+
Ok(())
1753+
}
1754+
}
1755+
1756+
/// ClickHouse `INTERPOLATE` clause for use in `ORDER BY` clause when using `WITH FILL` modifier.
1757+
/// Supported by [ClickHouse syntax]
1758+
///
1759+
/// [ClickHouse syntax]: <https://clickhouse.com/docs/en/sql-reference/statements/select/order-by#order-by-expr-with-fill-modifier>
1760+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1761+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1762+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
1763+
pub struct InterpolateExpr {
1764+
pub column: Ident,
1765+
pub expr: Option<Expr>,
1766+
}
1767+
1768+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1769+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1770+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
1771+
pub struct Interpolate {
1772+
pub exprs: Option<Vec<InterpolateExpr>>,
1773+
}
1774+
1775+
impl fmt::Display for InterpolateExpr {
1776+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1777+
write!(f, "{}", self.column)?;
1778+
if let Some(ref expr) = self.expr {
1779+
write!(f, " AS {}", expr)?;
1780+
}
16961781
Ok(())
16971782
}
16981783
}

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,
@@ -682,6 +684,7 @@ define_keywords!(
682684
STDDEV_SAMP,
683685
STDIN,
684686
STDOUT,
687+
STEP,
685688
STORAGE_INTEGRATION,
686689
STORED,
687690
STRICT,

src/parser/mod.rs

Lines changed: 79 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7934,7 +7934,7 @@ impl<'a> Parser<'a> {
79347934
body: self.parse_insert_setexpr_boxed()?,
79357935
limit: None,
79367936
limit_by: vec![],
7937-
order_by: vec![],
7937+
order_by: None,
79387938
offset: None,
79397939
fetch: None,
79407940
locks: vec![],
@@ -7948,7 +7948,7 @@ impl<'a> Parser<'a> {
79487948
body: self.parse_update_setexpr_boxed()?,
79497949
limit: None,
79507950
limit_by: vec![],
7951-
order_by: vec![],
7951+
order_by: None,
79527952
offset: None,
79537953
fetch: None,
79547954
locks: vec![],
@@ -7960,9 +7960,19 @@ impl<'a> Parser<'a> {
79607960
let body = self.parse_boxed_query_body(0)?;
79617961

79627962
let order_by = if self.parse_keywords(&[Keyword::ORDER, Keyword::BY]) {
7963-
self.parse_comma_separated(Parser::parse_order_by_expr)?
7963+
let order_by_exprs = self.parse_comma_separated(Parser::parse_order_by_expr)?;
7964+
let interpolate = if dialect_of!(self is ClickHouseDialect | GenericDialect) {
7965+
self.parse_interpolations()?
7966+
} else {
7967+
None
7968+
};
7969+
7970+
Some(OrderBy {
7971+
exprs: order_by_exprs,
7972+
interpolate,
7973+
})
79647974
} else {
7965-
vec![]
7975+
None
79667976
};
79677977

79687978
let mut limit = None;
@@ -9193,7 +9203,7 @@ impl<'a> Parser<'a> {
91939203
subquery: Box::new(Query {
91949204
with: None,
91959205
body: Box::new(values),
9196-
order_by: vec![],
9206+
order_by: None,
91979207
limit: None,
91989208
limit_by: vec![],
91999209
offset: None,
@@ -10519,13 +10529,77 @@ impl<'a> Parser<'a> {
1051910529
None
1052010530
};
1052110531

10532+
let with_fill = if dialect_of!(self is ClickHouseDialect | GenericDialect)
10533+
&& self.parse_keywords(&[Keyword::WITH, Keyword::FILL])
10534+
{
10535+
Some(self.parse_with_fill()?)
10536+
} else {
10537+
None
10538+
};
10539+
1052210540
Ok(OrderByExpr {
1052310541
expr,
1052410542
asc,
1052510543
nulls_first,
10544+
with_fill,
1052610545
})
1052710546
}
1052810547

10548+
// Parse a WITH FILL clause (ClickHouse dialect)
10549+
// that follow the WITH FILL keywords in a ORDER BY clause
10550+
pub fn parse_with_fill(&mut self) -> Result<WithFill, ParserError> {
10551+
let from = if self.parse_keyword(Keyword::FROM) {
10552+
Some(self.parse_expr()?)
10553+
} else {
10554+
None
10555+
};
10556+
10557+
let to = if self.parse_keyword(Keyword::TO) {
10558+
Some(self.parse_expr()?)
10559+
} else {
10560+
None
10561+
};
10562+
10563+
let step = if self.parse_keyword(Keyword::STEP) {
10564+
Some(self.parse_expr()?)
10565+
} else {
10566+
None
10567+
};
10568+
10569+
Ok(WithFill { from, to, step })
10570+
}
10571+
10572+
// Parse a set of comma seperated INTERPOLATE expressions (ClickHouse dialect)
10573+
// that follow the INTERPOLATE keyword in an ORDER BY clause with the WITH FILL modifier
10574+
pub fn parse_interpolations(&mut self) -> Result<Option<Interpolate>, ParserError> {
10575+
if !self.parse_keyword(Keyword::INTERPOLATE) {
10576+
return Ok(None);
10577+
}
10578+
10579+
if self.consume_token(&Token::LParen) {
10580+
let interpolations = self.parse_comma_separated0(|p| p.parse_interpolation())?;
10581+
self.expect_token(&Token::RParen)?;
10582+
// INTERPOLATE () and INTERPOLATE ( ... ) variants
10583+
return Ok(Some(Interpolate {
10584+
exprs: Some(interpolations),
10585+
}));
10586+
}
10587+
10588+
// INTERPOLATE
10589+
Ok(Some(Interpolate { exprs: None }))
10590+
}
10591+
10592+
// Parse a INTERPOLATE expression (ClickHouse dialect)
10593+
pub fn parse_interpolation(&mut self) -> Result<InterpolateExpr, ParserError> {
10594+
let column = self.parse_identifier(false)?;
10595+
let expr = if self.parse_keyword(Keyword::AS) {
10596+
Some(self.parse_expr()?)
10597+
} else {
10598+
None
10599+
};
10600+
Ok(InterpolateExpr { column, expr })
10601+
}
10602+
1052910603
/// Parse a TOP clause, MSSQL equivalent of LIMIT,
1053010604
/// that follows after `SELECT [DISTINCT]`.
1053110605
pub fn parse_top(&mut self) -> Result<Top, ParserError> {

0 commit comments

Comments
 (0)