Skip to content

Commit dc33462

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

File tree

4 files changed

+115
-34
lines changed

4 files changed

+115
-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: 66 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3426,6 +3426,27 @@ 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)
3436+
if keywords::RESERVED_FOR_COLUMN_ALIAS.contains(&kw.keyword) =>
3437+
{
3438+
Some(token)
3439+
}
3440+
Token::RParen | Token::SemiColon | Token::EOF | Token::RBracket | Token::RBrace => {
3441+
Some(token)
3442+
}
3443+
_ => None,
3444+
}
3445+
} else {
3446+
None
3447+
}
3448+
}
3449+
34293450
/// Parse a comma-separated list of 1+ items accepted by `F`
34303451
pub fn parse_comma_separated<T, F>(&mut self, mut f: F) -> Result<Vec<T>, ParserError>
34313452
where
@@ -3434,22 +3455,8 @@ impl<'a> Parser<'a> {
34343455
let mut values = vec![];
34353456
loop {
34363457
values.push(f(self)?);
3437-
if !self.consume_token(&Token::Comma) {
3458+
if self.parse_comma_separated_end().is_some() {
34383459
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-
}
34533460
}
34543461
}
34553462
Ok(values)
@@ -8099,19 +8106,7 @@ impl<'a> Parser<'a> {
80998106
vec![]
81008107
};
81018108

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-
};
8109+
let settings = self.parse_settings()?;
81158110

81168111
let fetch = if self.parse_keyword(Keyword::FETCH) {
81178112
Some(self.parse_fetch()?)
@@ -8158,6 +8153,23 @@ impl<'a> Parser<'a> {
81588153
}
81598154
}
81608155

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

9375-
// Postgres, MSSQL: table-valued functions:
9387+
// Postgres, MSSQL, ClickHouse: table-valued functions:
93769388
let args = if self.consume_token(&Token::LParen) {
9377-
Some(self.parse_optional_args()?)
9389+
Some(self.parse_table_function_args()?)
93789390
} else {
93799391
None
93809392
};
@@ -10317,6 +10329,30 @@ impl<'a> Parser<'a> {
1031710329
}
1031810330
}
1031910331

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

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)