Skip to content

Commit 39d3063

Browse files
committed
Parse SETTINGS clause for ClickHouse table-valued functions
1 parent bc15f7b commit 39d3063

File tree

4 files changed

+113
-34
lines changed

4 files changed

+113
-34
lines changed

src/ast/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ pub use self::query::{
5050
OffsetRows, OrderBy, OrderByExpr, PivotValueSource, Query, RenameSelectItem,
5151
RepetitionQuantifier, ReplaceSelectElement, ReplaceSelectItem, RowsPerMatch, Select,
5252
SelectInto, SelectItem, SetExpr, SetOperator, SetQuantifier, Setting, SymbolDefinition, Table,
53-
TableAlias, TableFactor, TableVersion, TableWithJoins, Top, TopQuantity, ValueTableMode,
54-
Values, WildcardAdditionalOptions, With, WithFill,
53+
TableAlias, TableFactor, TableFunctionArgs, TableVersion, TableWithJoins, Top, TopQuantity,
54+
ValueTableMode, Values, WildcardAdditionalOptions, With, WithFill,
5555
};
5656
pub use self::value::{
5757
escape_double_quote_string, escape_quoted_string, DateTimeField, DollarQuotedString,

src/ast/query.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -899,6 +899,14 @@ impl fmt::Display for ExprWithAlias {
899899
}
900900
}
901901

902+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
903+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
904+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
905+
pub struct TableFunctionArgs {
906+
pub args: Vec<FunctionArg>,
907+
pub settings: Option<Vec<Setting>>,
908+
}
909+
902910
/// A table name or a parenthesized subquery with an optional alias
903911
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
904912
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
@@ -916,7 +924,7 @@ pub enum TableFactor {
916924
/// This field's value is `Some(v)`, where `v` is a (possibly empty)
917925
/// vector of arguments, in the case of a table-valued function call,
918926
/// whereas it's `None` in the case of a regular table name.
919-
args: Option<Vec<FunctionArg>>,
927+
args: Option<TableFunctionArgs>,
920928
/// MSSQL-specific `WITH (...)` hints such as NOLOCK.
921929
with_hints: Vec<Expr>,
922930
/// Optional version qualifier to facilitate table time-travel, as
@@ -1314,7 +1322,12 @@ impl fmt::Display for TableFactor {
13141322
write!(f, "PARTITION ({})", display_comma_separated(partitions))?;
13151323
}
13161324
if let Some(args) = args {
1317-
write!(f, "({})", display_comma_separated(args))?;
1325+
write!(f, "(")?;
1326+
write!(f, "{}", display_comma_separated(&args.args))?;
1327+
if let Some(ref settings) = args.settings {
1328+
write!(f, ", SETTINGS {}", display_comma_separated(settings))?;
1329+
}
1330+
write!(f, ")")?;
13181331
}
13191332
if *with_ordinality {
13201333
write!(f, " WITH ORDINALITY")?;

src/parser/mod.rs

Lines changed: 64 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3426,6 +3426,25 @@ impl<'a> Parser<'a> {
34263426
Ok(values)
34273427
}
34283428

3429+
fn parse_comma_separated_end(&mut self) -> Option<Token> {
3430+
if !self.consume_token(&Token::Comma) {
3431+
Some(Token::Comma)
3432+
} else if self.options.trailing_commas {
3433+
let token = self.peek_token().token;
3434+
match token {
3435+
Token::Word(ref kw) if keywords::RESERVED_FOR_COLUMN_ALIAS.contains(&kw.keyword) => {
3436+
Some(token)
3437+
}
3438+
Token::RParen | Token::SemiColon | Token::EOF | Token::RBracket | Token::RBrace => {
3439+
Some(token)
3440+
}
3441+
_ => None,
3442+
}
3443+
} else {
3444+
None
3445+
}
3446+
}
3447+
34293448
/// Parse a comma-separated list of 1+ items accepted by `F`
34303449
pub fn parse_comma_separated<T, F>(&mut self, mut f: F) -> Result<Vec<T>, ParserError>
34313450
where
@@ -3434,22 +3453,8 @@ impl<'a> Parser<'a> {
34343453
let mut values = vec![];
34353454
loop {
34363455
values.push(f(self)?);
3437-
if !self.consume_token(&Token::Comma) {
3456+
if self.parse_comma_separated_end().is_some() {
34383457
break;
3439-
} else if self.options.trailing_commas {
3440-
match self.peek_token().token {
3441-
Token::Word(kw)
3442-
if keywords::RESERVED_FOR_COLUMN_ALIAS.contains(&kw.keyword) =>
3443-
{
3444-
break;
3445-
}
3446-
Token::RParen
3447-
| Token::SemiColon
3448-
| Token::EOF
3449-
| Token::RBracket
3450-
| Token::RBrace => break,
3451-
_ => continue,
3452-
}
34533458
}
34543459
}
34553460
Ok(values)
@@ -8099,19 +8104,7 @@ impl<'a> Parser<'a> {
80998104
vec![]
81008105
};
81018106

8102-
let settings = if dialect_of!(self is ClickHouseDialect|GenericDialect)
8103-
&& self.parse_keyword(Keyword::SETTINGS)
8104-
{
8105-
let key_values = self.parse_comma_separated(|p| {
8106-
let key = p.parse_identifier(false)?;
8107-
p.expect_token(&Token::Eq)?;
8108-
let value = p.parse_value()?;
8109-
Ok(Setting { key, value })
8110-
})?;
8111-
Some(key_values)
8112-
} else {
8113-
None
8114-
};
8107+
let settings = self.parse_settings()?;
81158108

81168109
let fetch = if self.parse_keyword(Keyword::FETCH) {
81178110
Some(self.parse_fetch()?)
@@ -8158,6 +8151,23 @@ impl<'a> Parser<'a> {
81588151
}
81598152
}
81608153

8154+
fn parse_settings(&mut self) -> Result<Option<Vec<Setting>>, ParserError> {
8155+
let settings = if dialect_of!(self is ClickHouseDialect|GenericDialect)
8156+
&& self.parse_keyword(Keyword::SETTINGS)
8157+
{
8158+
let key_values = self.parse_comma_separated(|p| {
8159+
let key = p.parse_identifier(false)?;
8160+
p.expect_token(&Token::Eq)?;
8161+
let value = p.parse_value()?;
8162+
Ok(Setting { key, value })
8163+
})?;
8164+
Some(key_values)
8165+
} else {
8166+
None
8167+
};
8168+
Ok(settings)
8169+
}
8170+
81618171
/// Parse a mssql `FOR [XML | JSON | BROWSE]` clause
81628172
pub fn parse_for_clause(&mut self) -> Result<Option<ForClause>, ParserError> {
81638173
if self.parse_keyword(Keyword::XML) {
@@ -9372,9 +9382,9 @@ impl<'a> Parser<'a> {
93729382
// Parse potential version qualifier
93739383
let version = self.parse_table_version()?;
93749384

9375-
// Postgres, MSSQL: table-valued functions:
9385+
// Postgres, MSSQL, ClickHouse: table-valued functions:
93769386
let args = if self.consume_token(&Token::LParen) {
9377-
Some(self.parse_optional_args()?)
9387+
Some(self.parse_table_function_args()?)
93789388
} else {
93799389
None
93809390
};
@@ -10317,6 +10327,30 @@ impl<'a> Parser<'a> {
1031710327
}
1031810328
}
1031910329

10330+
fn parse_table_function_args(&mut self) -> Result<TableFunctionArgs, ParserError> {
10331+
{
10332+
let settings = self.parse_settings()?;
10333+
if self.consume_token(&Token::RParen) {
10334+
return Ok(TableFunctionArgs {
10335+
args: vec![],
10336+
settings,
10337+
});
10338+
}
10339+
}
10340+
let mut args = vec![];
10341+
let settings = loop {
10342+
if let Some(settings) = self.parse_settings()? {
10343+
break Some(settings);
10344+
}
10345+
args.push(self.parse_function_args()?);
10346+
if self.parse_comma_separated_end().is_some() {
10347+
break None;
10348+
}
10349+
};
10350+
self.expect_token(&Token::RParen)?;
10351+
Ok(TableFunctionArgs { args, settings })
10352+
}
10353+
1032010354
/// Parses a potentially empty list of arguments to a window function
1032110355
/// (including the closing parenthesis).
1032210356
///

tests/sqlparser_clickhouse.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1091,6 +1091,38 @@ fn parse_create_table_on_commit_and_as_query() {
10911091
}
10921092
}
10931093

1094+
#[test]
1095+
fn parse_select_table_function_settings() {
1096+
let sql = r#"SELECT * FROM table_function(arg, SETTINGS setting = 3)"#;
1097+
match clickhouse_and_generic().verified_stmt(sql) {
1098+
Statement::Query(q) => {
1099+
let from = &q.body.as_select().unwrap().from;
1100+
assert_eq!(from.len(), 1);
1101+
assert_eq!(from[0].joins, vec![]);
1102+
match &from[0].relation {
1103+
Table { args, .. } => {
1104+
let args = args.as_ref().unwrap();
1105+
assert_eq!(
1106+
args.args,
1107+
vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(
1108+
Expr::Identifier("arg".into())
1109+
))]
1110+
);
1111+
assert_eq!(
1112+
args.settings,
1113+
Some(vec![Setting {
1114+
key: "setting".into(),
1115+
value: Value::Number("3".into(), false)
1116+
}])
1117+
)
1118+
}
1119+
_ => unreachable!(),
1120+
}
1121+
}
1122+
_ => unreachable!(),
1123+
}
1124+
}
1125+
10941126
fn clickhouse() -> TestedDialects {
10951127
TestedDialects {
10961128
dialects: vec![Box::new(ClickHouseDialect {})],

0 commit comments

Comments
 (0)