Skip to content

Commit 7ccedd4

Browse files
nickprestaalamb
authored andcommitted
[ClickHouse] Add support for WITH FILL to OrderByExpr (apache#1330)
Co-authored-by: Andrew Lamb <[email protected]>
1 parent e710836 commit 7ccedd4

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}")?;
@@ -1680,6 +1689,18 @@ pub enum JoinConstraint {
16801689
None,
16811690
}
16821691

1692+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1693+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1694+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
1695+
pub struct OrderBy {
1696+
pub exprs: Vec<OrderByExpr>,
1697+
/// Optional: `INTERPOLATE`
1698+
/// Supported by [ClickHouse syntax]
1699+
///
1700+
/// [ClickHouse syntax]: <https://clickhouse.com/docs/en/sql-reference/statements/select/order-by#order-by-expr-with-fill-modifier>
1701+
pub interpolate: Option<Interpolate>,
1702+
}
1703+
16831704
/// An `ORDER BY` expression
16841705
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
16851706
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
@@ -1690,6 +1711,9 @@ pub struct OrderByExpr {
16901711
pub asc: Option<bool>,
16911712
/// Optional `NULLS FIRST` or `NULLS LAST`
16921713
pub nulls_first: Option<bool>,
1714+
/// Optional: `WITH FILL`
1715+
/// Supported by [ClickHouse syntax]: <https://clickhouse.com/docs/en/sql-reference/statements/select/order-by#order-by-expr-with-fill-modifier>
1716+
pub with_fill: Option<WithFill>,
16931717
}
16941718

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

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,
@@ -683,6 +685,7 @@ define_keywords!(
683685
STDDEV_SAMP,
684686
STDIN,
685687
STDOUT,
688+
STEP,
686689
STORAGE_INTEGRATION,
687690
STORED,
688691
STRICT,

src/parser/mod.rs

Lines changed: 79 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7948,7 +7948,7 @@ impl<'a> Parser<'a> {
79487948
body: self.parse_insert_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![],
@@ -7962,7 +7962,7 @@ impl<'a> Parser<'a> {
79627962
body: self.parse_update_setexpr_boxed()?,
79637963
limit: None,
79647964
limit_by: vec![],
7965-
order_by: vec![],
7965+
order_by: None,
79667966
offset: None,
79677967
fetch: None,
79687968
locks: vec![],
@@ -7974,9 +7974,19 @@ impl<'a> Parser<'a> {
79747974
let body = self.parse_boxed_query_body(0)?;
79757975

79767976
let order_by = if self.parse_keywords(&[Keyword::ORDER, Keyword::BY]) {
7977-
self.parse_comma_separated(Parser::parse_order_by_expr)?
7977+
let order_by_exprs = self.parse_comma_separated(Parser::parse_order_by_expr)?;
7978+
let interpolate = if dialect_of!(self is ClickHouseDialect | GenericDialect) {
7979+
self.parse_interpolations()?
7980+
} else {
7981+
None
7982+
};
7983+
7984+
Some(OrderBy {
7985+
exprs: order_by_exprs,
7986+
interpolate,
7987+
})
79787988
} else {
7979-
vec![]
7989+
None
79807990
};
79817991

79827992
let mut limit = None;
@@ -9208,7 +9218,7 @@ impl<'a> Parser<'a> {
92089218
subquery: Box::new(Query {
92099219
with: None,
92109220
body: Box::new(values),
9211-
order_by: vec![],
9221+
order_by: None,
92129222
limit: None,
92139223
limit_by: vec![],
92149224
offset: None,
@@ -10548,13 +10558,77 @@ impl<'a> Parser<'a> {
1054810558
None
1054910559
};
1055010560

10561+
let with_fill = if dialect_of!(self is ClickHouseDialect | GenericDialect)
10562+
&& self.parse_keywords(&[Keyword::WITH, Keyword::FILL])
10563+
{
10564+
Some(self.parse_with_fill()?)
10565+
} else {
10566+
None
10567+
};
10568+
1055110569
Ok(OrderByExpr {
1055210570
expr,
1055310571
asc,
1055410572
nulls_first,
10573+
with_fill,
1055510574
})
1055610575
}
1055710576

10577+
// Parse a WITH FILL clause (ClickHouse dialect)
10578+
// that follow the WITH FILL keywords in a ORDER BY clause
10579+
pub fn parse_with_fill(&mut self) -> Result<WithFill, ParserError> {
10580+
let from = if self.parse_keyword(Keyword::FROM) {
10581+
Some(self.parse_expr()?)
10582+
} else {
10583+
None
10584+
};
10585+
10586+
let to = if self.parse_keyword(Keyword::TO) {
10587+
Some(self.parse_expr()?)
10588+
} else {
10589+
None
10590+
};
10591+
10592+
let step = if self.parse_keyword(Keyword::STEP) {
10593+
Some(self.parse_expr()?)
10594+
} else {
10595+
None
10596+
};
10597+
10598+
Ok(WithFill { from, to, step })
10599+
}
10600+
10601+
// Parse a set of comma seperated INTERPOLATE expressions (ClickHouse dialect)
10602+
// that follow the INTERPOLATE keyword in an ORDER BY clause with the WITH FILL modifier
10603+
pub fn parse_interpolations(&mut self) -> Result<Option<Interpolate>, ParserError> {
10604+
if !self.parse_keyword(Keyword::INTERPOLATE) {
10605+
return Ok(None);
10606+
}
10607+
10608+
if self.consume_token(&Token::LParen) {
10609+
let interpolations = self.parse_comma_separated0(|p| p.parse_interpolation())?;
10610+
self.expect_token(&Token::RParen)?;
10611+
// INTERPOLATE () and INTERPOLATE ( ... ) variants
10612+
return Ok(Some(Interpolate {
10613+
exprs: Some(interpolations),
10614+
}));
10615+
}
10616+
10617+
// INTERPOLATE
10618+
Ok(Some(Interpolate { exprs: None }))
10619+
}
10620+
10621+
// Parse a INTERPOLATE expression (ClickHouse dialect)
10622+
pub fn parse_interpolation(&mut self) -> Result<InterpolateExpr, ParserError> {
10623+
let column = self.parse_identifier(false)?;
10624+
let expr = if self.parse_keyword(Keyword::AS) {
10625+
Some(self.parse_expr()?)
10626+
} else {
10627+
None
10628+
};
10629+
Ok(InterpolateExpr { column, expr })
10630+
}
10631+
1055810632
/// Parse a TOP clause, MSSQL equivalent of LIMIT,
1055910633
/// that follows after `SELECT [DISTINCT]`.
1056010634
pub fn parse_top(&mut self) -> Result<Top, ParserError> {

0 commit comments

Comments
 (0)