Skip to content

Commit 700bd03

Browse files
git-hulkiffyio
andauthored
Support SETTINGS pairs for ClickHouse dialect (apache#1327)
Co-authored-by: Ifeanyi Ubah <[email protected]>
1 parent 44d7a20 commit 700bd03

File tree

9 files changed

+111
-5
lines changed

9 files changed

+111
-5
lines changed

src/ast/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ pub use self::query::{
4848
MatchRecognizePattern, MatchRecognizeSymbol, Measure, NamedWindowDefinition, NamedWindowExpr,
4949
NonBlock, Offset, OffsetRows, OrderByExpr, PivotValueSource, Query, RenameSelectItem,
5050
RepetitionQuantifier, ReplaceSelectElement, ReplaceSelectItem, RowsPerMatch, Select,
51-
SelectInto, SelectItem, SetExpr, SetOperator, SetQuantifier, SymbolDefinition, Table,
51+
SelectInto, SelectItem, SetExpr, SetOperator, SetQuantifier, Setting, SymbolDefinition, Table,
5252
TableAlias, TableFactor, TableVersion, TableWithJoins, Top, TopQuantity, ValueTableMode,
5353
Values, WildcardAdditionalOptions, With,
5454
};

src/ast/query.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ pub struct Query {
5050
/// `FOR JSON { AUTO | PATH } [ , INCLUDE_NULL_VALUES ]`
5151
/// (MSSQL-specific)
5252
pub for_clause: Option<ForClause>,
53+
/// ClickHouse syntax: `SELECT * FROM t SETTINGS key1 = value1, key2 = value2`
54+
///
55+
/// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/select#settings-in-select-query)
56+
pub settings: Option<Vec<Setting>>,
5357
}
5458

5559
impl fmt::Display for Query {
@@ -70,6 +74,9 @@ impl fmt::Display for Query {
7074
if !self.limit_by.is_empty() {
7175
write!(f, " BY {}", display_separated(&self.limit_by, ", "))?;
7276
}
77+
if let Some(ref settings) = self.settings {
78+
write!(f, " SETTINGS {}", display_comma_separated(settings))?;
79+
}
7380
if let Some(ref fetch) = self.fetch {
7481
write!(f, " {fetch}")?;
7582
}
@@ -828,6 +835,20 @@ impl fmt::Display for ConnectBy {
828835
}
829836
}
830837

838+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
839+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
840+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
841+
pub struct Setting {
842+
pub key: Ident,
843+
pub value: Value,
844+
}
845+
846+
impl fmt::Display for Setting {
847+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
848+
write!(f, "{} = {}", self.key, self.value)
849+
}
850+
}
851+
831852
/// An expression optionally followed by an alias.
832853
///
833854
/// Example:

src/keywords.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,7 @@ define_keywords!(
650650
SESSION_USER,
651651
SET,
652652
SETS,
653+
SETTINGS,
653654
SHARE,
654655
SHOW,
655656
SIMILAR,
@@ -850,6 +851,8 @@ pub const RESERVED_FOR_TABLE_ALIAS: &[Keyword] = &[
850851
Keyword::FOR,
851852
// for MYSQL PARTITION SELECTION
852853
Keyword::PARTITION,
854+
// for ClickHouse SELECT * FROM t SETTINGS ...
855+
Keyword::SETTINGS,
853856
// for Snowflake START WITH .. CONNECT BY
854857
Keyword::START,
855858
Keyword::CONNECT,

src/parser/mod.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7871,6 +7871,7 @@ impl<'a> Parser<'a> {
78717871
fetch: None,
78727872
locks: vec![],
78737873
for_clause: None,
7874+
settings: None,
78747875
})
78757876
} else if self.parse_keyword(Keyword::UPDATE) {
78767877
Ok(Query {
@@ -7883,6 +7884,7 @@ impl<'a> Parser<'a> {
78837884
fetch: None,
78847885
locks: vec![],
78857886
for_clause: None,
7887+
settings: None,
78867888
})
78877889
} else {
78887890
let body = self.parse_boxed_query_body(0)?;
@@ -7928,6 +7930,20 @@ impl<'a> Parser<'a> {
79287930
vec![]
79297931
};
79307932

7933+
let settings = if dialect_of!(self is ClickHouseDialect|GenericDialect)
7934+
&& self.parse_keyword(Keyword::SETTINGS)
7935+
{
7936+
let key_values = self.parse_comma_separated(|p| {
7937+
let key = p.parse_identifier(false)?;
7938+
p.expect_token(&Token::Eq)?;
7939+
let value = p.parse_value()?;
7940+
Ok(Setting { key, value })
7941+
})?;
7942+
Some(key_values)
7943+
} else {
7944+
None
7945+
};
7946+
79317947
let fetch = if self.parse_keyword(Keyword::FETCH) {
79327948
Some(self.parse_fetch()?)
79337949
} else {
@@ -7955,6 +7971,7 @@ impl<'a> Parser<'a> {
79557971
fetch,
79567972
locks,
79577973
for_clause,
7974+
settings,
79587975
})
79597976
}
79607977
}
@@ -9091,6 +9108,7 @@ impl<'a> Parser<'a> {
90919108
fetch: None,
90929109
locks: vec![],
90939110
for_clause: None,
9111+
settings: None,
90949112
}),
90959113
alias,
90969114
})

tests/sqlparser_clickhouse.rs

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ use test_utils::*;
2121
use sqlparser::ast::Expr::{BinaryOp, Identifier, MapAccess};
2222
use sqlparser::ast::SelectItem::UnnamedExpr;
2323
use sqlparser::ast::TableFactor::Table;
24+
use sqlparser::ast::Value::Number;
2425
use sqlparser::ast::*;
25-
2626
use sqlparser::dialect::ClickHouseDialect;
2727
use sqlparser::dialect::GenericDialect;
2828

@@ -549,6 +549,42 @@ fn parse_limit_by() {
549549
);
550550
}
551551

552+
#[test]
553+
fn parse_settings_in_query() {
554+
match clickhouse_and_generic()
555+
.verified_stmt(r#"SELECT * FROM t SETTINGS max_threads = 1, max_block_size = 10000"#)
556+
{
557+
Statement::Query(query) => {
558+
assert_eq!(
559+
query.settings,
560+
Some(vec![
561+
Setting {
562+
key: Ident::new("max_threads"),
563+
value: Number("1".parse().unwrap(), false)
564+
},
565+
Setting {
566+
key: Ident::new("max_block_size"),
567+
value: Number("10000".parse().unwrap(), false)
568+
},
569+
])
570+
);
571+
}
572+
_ => unreachable!(),
573+
}
574+
575+
let invalid_cases = vec![
576+
"SELECT * FROM t SETTINGS a",
577+
"SELECT * FROM t SETTINGS a=",
578+
"SELECT * FROM t SETTINGS a=1, b",
579+
"SELECT * FROM t SETTINGS a=1, b=",
580+
"SELECT * FROM t SETTINGS a=1, b=c",
581+
];
582+
for sql in invalid_cases {
583+
clickhouse_and_generic()
584+
.parse_sql_statements(sql)
585+
.expect_err("Expected: SETTINGS key = value, found: ");
586+
}
587+
}
552588
#[test]
553589
fn parse_select_star_except() {
554590
clickhouse().verified_stmt("SELECT * EXCEPT (prev_status) FROM anomalies");

tests/sqlparser_common.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,7 @@ fn parse_update_set_from() {
413413
fetch: None,
414414
locks: vec![],
415415
for_clause: None,
416+
settings: None,
416417
}),
417418
alias: Some(TableAlias {
418419
name: Ident::new("t2"),
@@ -3427,6 +3428,7 @@ fn parse_create_table_as_table() {
34273428
fetch: None,
34283429
locks: vec![],
34293430
for_clause: None,
3431+
settings: None,
34303432
});
34313433

34323434
match verified_stmt(sql1) {
@@ -3452,6 +3454,7 @@ fn parse_create_table_as_table() {
34523454
fetch: None,
34533455
locks: vec![],
34543456
for_clause: None,
3457+
settings: None,
34553458
});
34563459

34573460
match verified_stmt(sql2) {
@@ -4996,6 +4999,7 @@ fn parse_interval_and_or_xor() {
49964999
fetch: None,
49975000
locks: vec![],
49985001
for_clause: None,
5002+
settings: None,
49995003
}))];
50005004

50015005
assert_eq!(actual_ast, expected_ast);
@@ -7649,6 +7653,7 @@ fn parse_merge() {
76497653
fetch: None,
76507654
locks: vec![],
76517655
for_clause: None,
7656+
settings: None,
76527657
}),
76537658
alias: Some(TableAlias {
76547659
name: Ident {
@@ -9156,6 +9161,7 @@ fn parse_unload() {
91569161
locks: vec![],
91579162
for_clause: None,
91589163
order_by: vec![],
9164+
settings: None,
91599165
}),
91609166
to: Ident {
91619167
value: "s3://...".to_string(),

tests/sqlparser_mssql.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ fn parse_create_procedure() {
103103
locks: vec![],
104104
for_clause: None,
105105
order_by: vec![],
106+
settings: None,
106107
body: Box::new(SetExpr::Select(Box::new(Select {
107108
distinct: None,
108109
top: None,
@@ -546,6 +547,7 @@ fn parse_substring_in_select() {
546547
fetch: None,
547548
locks: vec![],
548549
for_clause: None,
550+
settings: None,
549551
}),
550552
query
551553
);

tests/sqlparser_mysql.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -925,6 +925,7 @@ fn parse_escaped_quote_identifiers_with_escape() {
925925
fetch: None,
926926
locks: vec![],
927927
for_clause: None,
928+
settings: None,
928929
}))
929930
);
930931
}
@@ -972,6 +973,7 @@ fn parse_escaped_quote_identifiers_with_no_escape() {
972973
fetch: None,
973974
locks: vec![],
974975
for_clause: None,
976+
settings: None,
975977
}))
976978
);
977979
}
@@ -1016,6 +1018,7 @@ fn parse_escaped_backticks_with_escape() {
10161018
fetch: None,
10171019
locks: vec![],
10181020
for_clause: None,
1021+
settings: None,
10191022
}))
10201023
);
10211024
}
@@ -1060,6 +1063,7 @@ fn parse_escaped_backticks_with_no_escape() {
10601063
fetch: None,
10611064
locks: vec![],
10621065
for_clause: None,
1066+
settings: None,
10631067
}))
10641068
);
10651069
}
@@ -1264,6 +1268,7 @@ fn parse_simple_insert() {
12641268
fetch: None,
12651269
locks: vec![],
12661270
for_clause: None,
1271+
settings: None,
12671272
})),
12681273
source
12691274
);
@@ -1306,6 +1311,7 @@ fn parse_ignore_insert() {
13061311
fetch: None,
13071312
locks: vec![],
13081313
for_clause: None,
1314+
settings: None,
13091315
})),
13101316
source
13111317
);
@@ -1348,6 +1354,7 @@ fn parse_priority_insert() {
13481354
fetch: None,
13491355
locks: vec![],
13501356
for_clause: None,
1357+
settings: None,
13511358
})),
13521359
source
13531360
);
@@ -1387,6 +1394,7 @@ fn parse_priority_insert() {
13871394
fetch: None,
13881395
locks: vec![],
13891396
for_clause: None,
1397+
settings: None,
13901398
})),
13911399
source
13921400
);
@@ -1434,6 +1442,7 @@ fn parse_insert_as() {
14341442
fetch: None,
14351443
locks: vec![],
14361444
for_clause: None,
1445+
settings: None,
14371446
})),
14381447
source
14391448
);
@@ -1493,6 +1502,7 @@ fn parse_insert_as() {
14931502
fetch: None,
14941503
locks: vec![],
14951504
for_clause: None,
1505+
settings: None,
14961506
})),
14971507
source
14981508
);
@@ -1536,6 +1546,7 @@ fn parse_replace_insert() {
15361546
fetch: None,
15371547
locks: vec![],
15381548
for_clause: None,
1549+
settings: None,
15391550
})),
15401551
source
15411552
);
@@ -1573,6 +1584,7 @@ fn parse_empty_row_insert() {
15731584
fetch: None,
15741585
locks: vec![],
15751586
for_clause: None,
1587+
settings: None,
15761588
})),
15771589
source
15781590
);
@@ -1633,6 +1645,7 @@ fn parse_insert_with_on_duplicate_update() {
16331645
fetch: None,
16341646
locks: vec![],
16351647
for_clause: None,
1648+
settings: None,
16361649
})),
16371650
source
16381651
);
@@ -2273,6 +2286,7 @@ fn parse_substring_in_select() {
22732286
fetch: None,
22742287
locks: vec![],
22752288
for_clause: None,
2289+
settings: None,
22762290
}),
22772291
query
22782292
);
@@ -2578,6 +2592,7 @@ fn parse_hex_string_introducer() {
25782592
fetch: None,
25792593
locks: vec![],
25802594
for_clause: None,
2595+
settings: None,
25812596
}))
25822597
)
25832598
}

0 commit comments

Comments
 (0)