Skip to content

Commit 50c605a

Browse files
solontseviffyio
andauthored
Support for Map values in ClickHouse settings (apache#1896)
Co-authored-by: Ifeanyi Ubah <ify1992@yahoo.com>
1 parent 6c38cdc commit 50c605a

File tree

5 files changed

+112
-43
lines changed

5 files changed

+112
-43
lines changed

src/ast/query.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1047,7 +1047,7 @@ impl fmt::Display for ConnectBy {
10471047
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
10481048
pub struct Setting {
10491049
pub key: Ident,
1050-
pub value: Value,
1050+
pub value: Expr,
10511051
}
10521052

10531053
impl fmt::Display for Setting {

src/ast/value.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,6 @@ impl From<ValueWithSpan> for Value {
116116
derive(Visit, VisitMut),
117117
visit(with = "visit_value")
118118
)]
119-
120119
pub enum Value {
121120
/// Numeric literal
122121
#[cfg(not(feature = "bigdecimal"))]

src/parser/mod.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2770,7 +2770,7 @@ impl<'a> Parser<'a> {
27702770

27712771
if self.dialect.supports_dictionary_syntax() {
27722772
self.prev_token(); // Put back the '{'
2773-
return self.parse_duckdb_struct_literal();
2773+
return self.parse_dictionary();
27742774
}
27752775

27762776
self.expected("an expression", token)
@@ -3139,7 +3139,7 @@ impl<'a> Parser<'a> {
31393139
Ok(fields)
31403140
}
31413141

3142-
/// DuckDB specific: Parse a duckdb [dictionary]
3142+
/// DuckDB and ClickHouse specific: Parse a duckdb [dictionary] or a clickhouse [map] setting
31433143
///
31443144
/// Syntax:
31453145
///
@@ -3148,18 +3148,18 @@ impl<'a> Parser<'a> {
31483148
/// ```
31493149
///
31503150
/// [dictionary]: https://duckdb.org/docs/sql/data_types/struct#creating-structs
3151-
fn parse_duckdb_struct_literal(&mut self) -> Result<Expr, ParserError> {
3151+
/// [map]: https://clickhouse.com/docs/operations/settings/settings#additional_table_filters
3152+
fn parse_dictionary(&mut self) -> Result<Expr, ParserError> {
31523153
self.expect_token(&Token::LBrace)?;
31533154

3154-
let fields =
3155-
self.parse_comma_separated0(Self::parse_duckdb_dictionary_field, Token::RBrace)?;
3155+
let fields = self.parse_comma_separated0(Self::parse_dictionary_field, Token::RBrace)?;
31563156

31573157
self.expect_token(&Token::RBrace)?;
31583158

31593159
Ok(Expr::Dictionary(fields))
31603160
}
31613161

3162-
/// Parse a field for a duckdb [dictionary]
3162+
/// Parse a field for a duckdb [dictionary] or a clickhouse [map] setting
31633163
///
31643164
/// Syntax
31653165
///
@@ -3168,7 +3168,8 @@ impl<'a> Parser<'a> {
31683168
/// ```
31693169
///
31703170
/// [dictionary]: https://duckdb.org/docs/sql/data_types/struct#creating-structs
3171-
fn parse_duckdb_dictionary_field(&mut self) -> Result<DictionaryField, ParserError> {
3171+
/// [map]: https://clickhouse.com/docs/operations/settings/settings#additional_table_filters
3172+
fn parse_dictionary_field(&mut self) -> Result<DictionaryField, ParserError> {
31723173
let key = self.parse_identifier()?;
31733174

31743175
self.expect_token(&Token::Colon)?;
@@ -11216,7 +11217,7 @@ impl<'a> Parser<'a> {
1121611217
let key_values = self.parse_comma_separated(|p| {
1121711218
let key = p.parse_identifier()?;
1121811219
p.expect_token(&Token::Eq)?;
11219-
let value = p.parse_value()?.value;
11220+
let value = p.parse_expr()?;
1122011221
Ok(Setting { key, value })
1122111222
})?;
1122211223
Some(key_values)

src/test_utils.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,11 @@ pub fn number(n: &str) -> Value {
366366
Value::Number(n.parse().unwrap(), false)
367367
}
368368

369+
/// Creates a [Value::SingleQuotedString]
370+
pub fn single_quoted_string(s: impl Into<String>) -> Value {
371+
Value::SingleQuotedString(s.into())
372+
}
373+
369374
pub fn table_alias(name: impl Into<String>) -> Option<TableAlias> {
370375
Some(TableAlias {
371376
name: Ident::new(name),

tests/sqlparser_clickhouse.rs

Lines changed: 97 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ use test_utils::*;
2828
use sqlparser::ast::Expr::{BinaryOp, Identifier};
2929
use sqlparser::ast::SelectItem::UnnamedExpr;
3030
use sqlparser::ast::TableFactor::Table;
31-
use sqlparser::ast::Value::Number;
31+
use sqlparser::ast::Value::Boolean;
3232
use sqlparser::ast::*;
3333
use sqlparser::dialect::ClickHouseDialect;
3434
use sqlparser::dialect::GenericDialect;
@@ -965,38 +965,103 @@ fn parse_limit_by() {
965965

966966
#[test]
967967
fn parse_settings_in_query() {
968-
match clickhouse_and_generic()
969-
.verified_stmt(r#"SELECT * FROM t SETTINGS max_threads = 1, max_block_size = 10000"#)
970-
{
971-
Statement::Query(query) => {
972-
assert_eq!(
973-
query.settings,
974-
Some(vec![
975-
Setting {
976-
key: Ident::new("max_threads"),
977-
value: Number("1".parse().unwrap(), false)
978-
},
979-
Setting {
980-
key: Ident::new("max_block_size"),
981-
value: Number("10000".parse().unwrap(), false)
982-
},
983-
])
984-
);
968+
fn check_settings(sql: &str, expected: Vec<Setting>) {
969+
match clickhouse_and_generic().verified_stmt(sql) {
970+
Statement::Query(q) => {
971+
assert_eq!(q.settings, Some(expected));
972+
}
973+
_ => unreachable!(),
985974
}
986-
_ => unreachable!(),
975+
}
976+
977+
for (sql, expected_settings) in [
978+
(
979+
r#"SELECT * FROM t SETTINGS max_threads = 1, max_block_size = 10000"#,
980+
vec![
981+
Setting {
982+
key: Ident::new("max_threads"),
983+
value: Expr::value(number("1")),
984+
},
985+
Setting {
986+
key: Ident::new("max_block_size"),
987+
value: Expr::value(number("10000")),
988+
},
989+
],
990+
),
991+
(
992+
r#"SELECT * FROM t SETTINGS additional_table_filters = {'table_1': 'x != 2'}"#,
993+
vec![Setting {
994+
key: Ident::new("additional_table_filters"),
995+
value: Expr::Dictionary(vec![DictionaryField {
996+
key: Ident::with_quote('\'', "table_1"),
997+
value: Expr::value(single_quoted_string("x != 2")).into(),
998+
}]),
999+
}],
1000+
),
1001+
(
1002+
r#"SELECT * FROM t SETTINGS additional_result_filter = 'x != 2', query_plan_optimize_lazy_materialization = false"#,
1003+
vec![
1004+
Setting {
1005+
key: Ident::new("additional_result_filter"),
1006+
value: Expr::value(single_quoted_string("x != 2")),
1007+
},
1008+
Setting {
1009+
key: Ident::new("query_plan_optimize_lazy_materialization"),
1010+
value: Expr::value(Boolean(false)),
1011+
},
1012+
],
1013+
),
1014+
] {
1015+
check_settings(sql, expected_settings);
9871016
}
9881017

9891018
let invalid_cases = vec![
990-
"SELECT * FROM t SETTINGS a",
991-
"SELECT * FROM t SETTINGS a=",
992-
"SELECT * FROM t SETTINGS a=1, b",
993-
"SELECT * FROM t SETTINGS a=1, b=",
994-
"SELECT * FROM t SETTINGS a=1, b=c",
1019+
("SELECT * FROM t SETTINGS a", "Expected: =, found: EOF"),
1020+
(
1021+
"SELECT * FROM t SETTINGS a=",
1022+
"Expected: an expression, found: EOF",
1023+
),
1024+
("SELECT * FROM t SETTINGS a=1, b", "Expected: =, found: EOF"),
1025+
(
1026+
"SELECT * FROM t SETTINGS a=1, b=",
1027+
"Expected: an expression, found: EOF",
1028+
),
1029+
(
1030+
"SELECT * FROM t SETTINGS a = {",
1031+
"Expected: identifier, found: EOF",
1032+
),
1033+
(
1034+
"SELECT * FROM t SETTINGS a = {'b'",
1035+
"Expected: :, found: EOF",
1036+
),
1037+
(
1038+
"SELECT * FROM t SETTINGS a = {'b': ",
1039+
"Expected: an expression, found: EOF",
1040+
),
1041+
(
1042+
"SELECT * FROM t SETTINGS a = {'b': 'c',}",
1043+
"Expected: identifier, found: }",
1044+
),
1045+
(
1046+
"SELECT * FROM t SETTINGS a = {'b': 'c', 'd'}",
1047+
"Expected: :, found: }",
1048+
),
1049+
(
1050+
"SELECT * FROM t SETTINGS a = {'b': 'c', 'd': }",
1051+
"Expected: an expression, found: }",
1052+
),
1053+
(
1054+
"SELECT * FROM t SETTINGS a = {ANY(b)}",
1055+
"Expected: :, found: (",
1056+
),
9951057
];
996-
for sql in invalid_cases {
997-
clickhouse_and_generic()
998-
.parse_sql_statements(sql)
999-
.expect_err("Expected: SETTINGS key = value, found: ");
1058+
for (sql, error_msg) in invalid_cases {
1059+
assert_eq!(
1060+
clickhouse_and_generic()
1061+
.parse_sql_statements(sql)
1062+
.unwrap_err(),
1063+
ParserError(error_msg.to_string())
1064+
);
10001065
}
10011066
}
10021067
#[test]
@@ -1550,11 +1615,11 @@ fn parse_select_table_function_settings() {
15501615
settings: Some(vec![
15511616
Setting {
15521617
key: "s0".into(),
1553-
value: Value::Number("3".parse().unwrap(), false),
1618+
value: Expr::value(number("3")),
15541619
},
15551620
Setting {
15561621
key: "s1".into(),
1557-
value: Value::SingleQuotedString("s".into()),
1622+
value: Expr::value(single_quoted_string("s")),
15581623
},
15591624
]),
15601625
},
@@ -1575,11 +1640,11 @@ fn parse_select_table_function_settings() {
15751640
settings: Some(vec![
15761641
Setting {
15771642
key: "s0".into(),
1578-
value: Value::Number("3".parse().unwrap(), false),
1643+
value: Expr::value(number("3")),
15791644
},
15801645
Setting {
15811646
key: "s1".into(),
1582-
value: Value::SingleQuotedString("s".into()),
1647+
value: Expr::value(single_quoted_string("s")),
15831648
},
15841649
]),
15851650
},
@@ -1589,7 +1654,6 @@ fn parse_select_table_function_settings() {
15891654
"SELECT * FROM t(SETTINGS a=)",
15901655
"SELECT * FROM t(SETTINGS a=1, b)",
15911656
"SELECT * FROM t(SETTINGS a=1, b=)",
1592-
"SELECT * FROM t(SETTINGS a=1, b=c)",
15931657
];
15941658
for sql in invalid_cases {
15951659
clickhouse_and_generic()

0 commit comments

Comments
 (0)