Support parametric arguments to FUNCTION for ClickHouse dialect (#1315)

Co-authored-by: Andrew Lamb <andrew@nerdnetworks.org>
This commit is contained in:
hulk 2024-06-23 19:36:05 +08:00 committed by GitHub
parent 7a9793b72e
commit a685e11993
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 119 additions and 5 deletions

View file

@ -4695,6 +4695,16 @@ impl fmt::Display for CloseCursor {
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct Function {
pub name: ObjectName,
/// The parameters to the function, including any options specified within the
/// delimiting parentheses.
///
/// Example:
/// ```plaintext
/// HISTOGRAM(0.5, 0.6)(x, y)
/// ```
///
/// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/aggregate-functions/parametric-functions)
pub parameters: FunctionArguments,
/// The arguments to the function, including any options specified within the
/// delimiting parentheses.
pub args: FunctionArguments,
@ -4723,7 +4733,7 @@ pub struct Function {
impl fmt::Display for Function {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}{}", self.name, self.args)?;
write!(f, "{}{}{}", self.name, self.parameters, self.args)?;
if !self.within_group.is_empty() {
write!(

View file

@ -533,6 +533,7 @@ where
/// null_treatment: None,
/// filter: None,
/// over: None,
/// parameters: FunctionArguments::None,
/// within_group: vec![],
/// });
/// }

View file

@ -27,6 +27,7 @@ use core::{
use log::debug;
use recursion::RecursionCounter;
use IsLateral::*;
use IsOptional::*;
@ -146,8 +147,6 @@ mod recursion {
pub struct DepthGuard {}
}
use recursion::RecursionCounter;
#[derive(PartialEq, Eq)]
pub enum IsOptional {
Optional,
@ -1002,6 +1001,7 @@ impl<'a> Parser<'a> {
{
Ok(Expr::Function(Function {
name: ObjectName(vec![w.to_ident()]),
parameters: FunctionArguments::None,
args: FunctionArguments::None,
null_treatment: None,
filter: None,
@ -1058,6 +1058,7 @@ impl<'a> Parser<'a> {
self.expect_token(&Token::RParen)?;
Ok(Expr::Function(Function {
name: ObjectName(vec![w.to_ident()]),
parameters: FunctionArguments::None,
args: FunctionArguments::Subquery(query),
filter: None,
null_treatment: None,
@ -1293,6 +1294,7 @@ impl<'a> Parser<'a> {
self.expect_token(&Token::RParen)?;
return Ok(Expr::Function(Function {
name,
parameters: FunctionArguments::None,
args: FunctionArguments::Subquery(subquery),
filter: None,
null_treatment: None,
@ -1301,7 +1303,16 @@ impl<'a> Parser<'a> {
}));
}
let args = self.parse_function_argument_list()?;
let mut args = self.parse_function_argument_list()?;
let mut parameters = FunctionArguments::None;
// ClickHouse aggregations support parametric functions like `HISTOGRAM(0.5, 0.6)(x, y)`
// which (0.5, 0.6) is a parameter to the function.
if dialect_of!(self is ClickHouseDialect | GenericDialect)
&& self.consume_token(&Token::LParen)
{
parameters = FunctionArguments::List(args);
args = self.parse_function_argument_list()?;
}
let within_group = if self.parse_keywords(&[Keyword::WITHIN, Keyword::GROUP]) {
self.expect_token(&Token::LParen)?;
@ -1350,6 +1361,7 @@ impl<'a> Parser<'a> {
Ok(Expr::Function(Function {
name,
parameters,
args: FunctionArguments::List(args),
null_treatment,
filter,
@ -1382,6 +1394,7 @@ impl<'a> Parser<'a> {
};
Ok(Expr::Function(Function {
name,
parameters: FunctionArguments::None,
args,
filter: None,
over: None,
@ -6470,6 +6483,7 @@ impl<'a> Parser<'a> {
} else {
Ok(Statement::Call(Function {
name: object_name,
parameters: FunctionArguments::None,
args: FunctionArguments::None,
over: None,
filter: None,
@ -8092,7 +8106,7 @@ impl<'a> Parser<'a> {
pub fn parse_query_body(&mut self, precedence: u8) -> Result<SetExpr, ParserError> {
// We parse the expression using a Pratt parser, as in `parse_expr()`.
// Start by parsing a restricted SELECT or a `(subquery)`:
let mut expr = if self.parse_keyword(Keyword::SELECT) {
let expr = if self.parse_keyword(Keyword::SELECT) {
SetExpr::Select(self.parse_select().map(Box::new)?)
} else if self.consume_token(&Token::LParen) {
// CTEs are not allowed here, but the parser currently accepts them
@ -8111,6 +8125,17 @@ impl<'a> Parser<'a> {
);
};
self.parse_remaining_set_exprs(expr, precedence)
}
/// Parse any extra set expressions that may be present in a query body
///
/// (this is its own function to reduce required stack size in debug builds)
fn parse_remaining_set_exprs(
&mut self,
mut expr: SetExpr,
precedence: u8,
) -> Result<SetExpr, ParserError> {
loop {
// The query can be optionally followed by a set operator:
let op = self.parse_set_operator(&self.peek_token().token);

View file

@ -336,6 +336,7 @@ pub fn join(relation: TableFactor) -> Join {
pub fn call(function: &str, args: impl IntoIterator<Item = Expr>) -> Expr {
Expr::Function(Function {
name: ObjectName(vec![Ident::new(function)]),
parameters: FunctionArguments::None,
args: FunctionArguments::List(FunctionArgumentList {
duplicate_treatment: None,
args: args