Skip to content

Commit

Permalink
Add support for WITH FILL to OrderByExpr
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
nickpresta committed Jul 3, 2024
1 parent 44d7a20 commit 972f46a
Show file tree
Hide file tree
Showing 6 changed files with 229 additions and 2 deletions.
4 changes: 2 additions & 2 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,14 @@ pub use self::operator::{BinaryOperator, UnaryOperator};
pub use self::query::{
AfterMatchSkip, ConnectBy, Cte, CteAsMaterialized, Distinct, EmptyMatchesMode,
ExceptSelectItem, ExcludeSelectItem, ExprWithAlias, Fetch, ForClause, ForJson, ForXml,
GroupByExpr, GroupByWithModifier, IdentWithAlias, IlikeSelectItem, Join, JoinConstraint,
GroupByExpr, GroupByWithModifier, IdentWithAlias, IlikeSelectItem, Interpolation, Join, JoinConstraint,
JoinOperator, JsonTableColumn, JsonTableColumnErrorHandling, LateralView, LockClause, LockType,
MatchRecognizePattern, MatchRecognizeSymbol, Measure, NamedWindowDefinition, NamedWindowExpr,
NonBlock, Offset, OffsetRows, OrderByExpr, PivotValueSource, Query, RenameSelectItem,
RepetitionQuantifier, ReplaceSelectElement, ReplaceSelectItem, RowsPerMatch, Select,
SelectInto, SelectItem, SetExpr, SetOperator, SetQuantifier, SymbolDefinition, Table,
TableAlias, TableFactor, TableVersion, TableWithJoins, Top, TopQuantity, ValueTableMode,
Values, WildcardAdditionalOptions, With,
Values, WildcardAdditionalOptions, With, WithFill,
};
pub use self::value::{
escape_double_quote_string, escape_quoted_string, DateTimeField, DollarQuotedString,
Expand Down
55 changes: 55 additions & 0 deletions src/ast/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1627,6 +1627,9 @@ pub struct OrderByExpr {
pub asc: Option<bool>,
/// Optional `NULLS FIRST` or `NULLS LAST`
pub nulls_first: Option<bool>,
/// Optional: `WITH FILL`
/// Supported by [ClickHouse syntax]: <https://clickhouse.com/docs/en/sql-reference/statements/select/order-by#order-by-expr-with-fill-modifier>
pub with_fill: Option<WithFill>,
}

impl fmt::Display for OrderByExpr {
Expand All @@ -1642,6 +1645,58 @@ impl fmt::Display for OrderByExpr {
Some(false) => write!(f, " NULLS LAST")?,
None => (),
}
if let Some(ref with_fill) = self.with_fill {
write!(f, " {}", with_fill)?
}
Ok(())
}
}

/// ClickHouse `WITH FILL` modifier for `ORDER BY` clause.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct WithFill {
pub from: Option<Expr>,
pub to: Option<Expr>,
pub step: Option<Expr>,
pub interpolate: Vec<Interpolation>,
}

impl fmt::Display for WithFill {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "WITH FILL")?;
if let Some(ref from) = self.from {
write!(f, " FROM {}", from)?;
}
if let Some(ref to) = self.to {
write!(f, " TO {}", to)?;
}
if let Some(ref step) = self.step {
write!(f, " STEP {}", step)?;
}
if !self.interpolate.is_empty() {
write!(f, " INTERPOLATE ({})", display_comma_separated(&self.interpolate))?;
}
Ok(())
}
}

/// ClickHouse `INTERPOLATE` clause for use in `WITH FILL` modifier.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct Interpolation {
pub column: Expr,
pub formula: Option<Expr>,
}

impl fmt::Display for Interpolation {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.column)?;
if let Some(ref formula) = self.formula {
write!(f, " AS {}", formula)?;
}
Ok(())
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/keywords.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,7 @@ define_keywords!(
FILE,
FILES,
FILE_FORMAT,
FILL,
FILTER,
FIRST,
FIRST_VALUE,
Expand Down Expand Up @@ -382,6 +383,7 @@ define_keywords!(
INT64,
INT8,
INTEGER,
INTERPOLATE,
INTERSECT,
INTERSECTION,
INTERVAL,
Expand Down Expand Up @@ -678,6 +680,7 @@ define_keywords!(
STDDEV_SAMP,
STDIN,
STDOUT,
STEP,
STORAGE_INTEGRATION,
STORED,
STRICT,
Expand Down
57 changes: 57 additions & 0 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10408,13 +10408,70 @@ impl<'a> Parser<'a> {
None
};

let with_fill = if self.parse_keywords(&[Keyword::WITH, Keyword::FILL]) {
Some(self.parse_with_fill()?)
} else {
None
};

Ok(OrderByExpr {
expr,
asc,
nulls_first,
with_fill,
})
}

// Parse a WITH FILL clause (ClickHouse dialect)
// that follow the WITH FILL keywords in a ORDER BY clause
pub fn parse_with_fill(&mut self) -> Result<WithFill, ParserError> {
let from = if self.parse_keyword(Keyword::FROM) {
Some(self.parse_expr()?)
} else {
None
};

let to = if self.parse_keyword(Keyword::TO) {
Some(self.parse_expr()?)
} else {
None
};

let step = if self.parse_keyword(Keyword::STEP) {
Some(self.parse_expr()?)
} else {
None
};

let interpolate = if self.parse_keyword(Keyword::INTERPOLATE) && self.consume_token(&Token::LParen) {
let interpolations = self.parse_interpolations()?;
self.expect_token(&Token::RParen)?;
interpolations
} else {
vec![]
};

Ok(WithFill { from, to, step, interpolate })
}

// Parse a set of comma seperated INTERPOLATE expressions (ClickHouse dialect)
// that follow the INTERPOLATE keyword in a WITH FILL clause
pub fn parse_interpolations(&mut self) -> Result<Vec<Interpolation>, ParserError> {
self.parse_comma_separated(|p| p.parse_interpolation())
}


// Parse a INTERPOLATE expression (ClickHouse dialect)
pub fn parse_interpolation(&mut self) -> Result<Interpolation, ParserError> {
let column = self.parse_expr()?;
let formula = if self.parse_keyword(Keyword::AS) {
Some(self.parse_expr()?)
} else {
None
};
Ok(Interpolation { column, formula })
}

/// Parse a TOP clause, MSSQL equivalent of LIMIT,
/// that follows after `SELECT [DISTINCT]`.
pub fn parse_top(&mut self) -> Result<Top, ParserError> {
Expand Down
Loading

0 comments on commit 972f46a

Please sign in to comment.