Skip to content

Commit c48f157

Browse files
committed
Parse SETTINGS clause for ClickHouse table-valued functions
1 parent 6c64d43 commit c48f157

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
@@ -3419,6 +3419,25 @@ impl<'a> Parser<'a> {
34193419
Ok(values)
34203420
}
34213421

3422+
fn parse_comma_separated_end(&mut self) -> Option<Token> {
3423+
if !self.consume_token(&Token::Comma) {
3424+
Some(Token::Comma)
3425+
} else if self.options.trailing_commas {
3426+
let token = self.peek_token().token;
3427+
match token {
3428+
Token::Word(ref kw) if keywords::RESERVED_FOR_COLUMN_ALIAS.contains(&kw.keyword) => {
3429+
Some(token)
3430+
}
3431+
Token::RParen | Token::SemiColon | Token::EOF | Token::RBracket | Token::RBrace => {
3432+
Some(token)
3433+
}
3434+
_ => None,
3435+
}
3436+
} else {
3437+
None
3438+
}
3439+
}
3440+
34223441
/// Parse a comma-separated list of 1+ items accepted by `F`
34233442
pub fn parse_comma_separated<T, F>(&mut self, mut f: F) -> Result<Vec<T>, ParserError>
34243443
where
@@ -3427,22 +3446,8 @@ impl<'a> Parser<'a> {
34273446
let mut values = vec![];
34283447
loop {
34293448
values.push(f(self)?);
3430-
if !self.consume_token(&Token::Comma) {
3449+
if self.parse_comma_separated_end().is_some() {
34313450
break;
3432-
} else if self.options.trailing_commas {
3433-
match self.peek_token().token {
3434-
Token::Word(kw)
3435-
if keywords::RESERVED_FOR_COLUMN_ALIAS.contains(&kw.keyword) =>
3436-
{
3437-
break;
3438-
}
3439-
Token::RParen
3440-
| Token::SemiColon
3441-
| Token::EOF
3442-
| Token::RBracket
3443-
| Token::RBrace => break,
3444-
_ => continue,
3445-
}
34463451
}
34473452
}
34483453
Ok(values)
@@ -8087,19 +8092,7 @@ impl<'a> Parser<'a> {
80878092
vec![]
80888093
};
80898094

8090-
let settings = if dialect_of!(self is ClickHouseDialect|GenericDialect)
8091-
&& self.parse_keyword(Keyword::SETTINGS)
8092-
{
8093-
let key_values = self.parse_comma_separated(|p| {
8094-
let key = p.parse_identifier(false)?;
8095-
p.expect_token(&Token::Eq)?;
8096-
let value = p.parse_value()?;
8097-
Ok(Setting { key, value })
8098-
})?;
8099-
Some(key_values)
8100-
} else {
8101-
None
8102-
};
8095+
let settings = self.parse_settings()?;
81038096

81048097
let fetch = if self.parse_keyword(Keyword::FETCH) {
81058098
Some(self.parse_fetch()?)
@@ -8146,6 +8139,23 @@ impl<'a> Parser<'a> {
81468139
}
81478140
}
81488141

8142+
fn parse_settings(&mut self) -> Result<Option<Vec<Setting>>, ParserError> {
8143+
let settings = if dialect_of!(self is ClickHouseDialect|GenericDialect)
8144+
&& self.parse_keyword(Keyword::SETTINGS)
8145+
{
8146+
let key_values = self.parse_comma_separated(|p| {
8147+
let key = p.parse_identifier(false)?;
8148+
p.expect_token(&Token::Eq)?;
8149+
let value = p.parse_value()?;
8150+
Ok(Setting { key, value })
8151+
})?;
8152+
Some(key_values)
8153+
} else {
8154+
None
8155+
};
8156+
Ok(settings)
8157+
}
8158+
81498159
/// Parse a mssql `FOR [XML | JSON | BROWSE]` clause
81508160
pub fn parse_for_clause(&mut self) -> Result<Option<ForClause>, ParserError> {
81518161
if self.parse_keyword(Keyword::XML) {
@@ -9360,9 +9370,9 @@ impl<'a> Parser<'a> {
93609370
// Parse potential version qualifier
93619371
let version = self.parse_table_version()?;
93629372

9363-
// Postgres, MSSQL: table-valued functions:
9373+
// Postgres, MSSQL, ClickHouse: table-valued functions:
93649374
let args = if self.consume_token(&Token::LParen) {
9365-
Some(self.parse_optional_args()?)
9375+
Some(self.parse_table_function_args()?)
93669376
} else {
93679377
None
93689378
};
@@ -10305,6 +10315,30 @@ impl<'a> Parser<'a> {
1030510315
}
1030610316
}
1030710317

10318+
fn parse_table_function_args(&mut self) -> Result<TableFunctionArgs, ParserError> {
10319+
{
10320+
let settings = self.parse_settings()?;
10321+
if self.consume_token(&Token::RParen) {
10322+
return Ok(TableFunctionArgs {
10323+
args: vec![],
10324+
settings,
10325+
});
10326+
}
10327+
}
10328+
let mut args = vec![];
10329+
let settings = loop {
10330+
if let Some(settings) = self.parse_settings()? {
10331+
break Some(settings);
10332+
}
10333+
args.push(self.parse_function_args()?);
10334+
if self.parse_comma_separated_end().is_some() {
10335+
break None;
10336+
}
10337+
};
10338+
self.expect_token(&Token::RParen)?;
10339+
Ok(TableFunctionArgs { args, settings })
10340+
}
10341+
1030810342
/// Parses a potentially empty list of arguments to a window function
1030910343
/// (including the closing parenthesis).
1031010344
///

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)