mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-07-07 17:04:59 +00:00
Support for Map values in ClickHouse settings (#1896)
Some checks are pending
Rust / test (beta) (push) Waiting to run
license / Release Audit Tool (RAT) (push) Waiting to run
Rust / test (nightly) (push) Waiting to run
Rust / test (stable) (push) Waiting to run
Rust / codestyle (push) Waiting to run
Rust / lint (push) Waiting to run
Rust / benchmark-lint (push) Waiting to run
Rust / compile (push) Waiting to run
Rust / docs (push) Waiting to run
Rust / compile-no-std (push) Waiting to run
Some checks are pending
Rust / test (beta) (push) Waiting to run
license / Release Audit Tool (RAT) (push) Waiting to run
Rust / test (nightly) (push) Waiting to run
Rust / test (stable) (push) Waiting to run
Rust / codestyle (push) Waiting to run
Rust / lint (push) Waiting to run
Rust / benchmark-lint (push) Waiting to run
Rust / compile (push) Waiting to run
Rust / docs (push) Waiting to run
Rust / compile-no-std (push) Waiting to run
Co-authored-by: Ifeanyi Ubah <ify1992@yahoo.com>
This commit is contained in:
parent
6c38cdcadb
commit
50c605a471
5 changed files with 112 additions and 43 deletions
|
@ -1047,7 +1047,7 @@ impl fmt::Display for ConnectBy {
|
|||
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
||||
pub struct Setting {
|
||||
pub key: Ident,
|
||||
pub value: Value,
|
||||
pub value: Expr,
|
||||
}
|
||||
|
||||
impl fmt::Display for Setting {
|
||||
|
|
|
@ -116,7 +116,6 @@ impl From<ValueWithSpan> for Value {
|
|||
derive(Visit, VisitMut),
|
||||
visit(with = "visit_value")
|
||||
)]
|
||||
|
||||
pub enum Value {
|
||||
/// Numeric literal
|
||||
#[cfg(not(feature = "bigdecimal"))]
|
||||
|
|
|
@ -2770,7 +2770,7 @@ impl<'a> Parser<'a> {
|
|||
|
||||
if self.dialect.supports_dictionary_syntax() {
|
||||
self.prev_token(); // Put back the '{'
|
||||
return self.parse_duckdb_struct_literal();
|
||||
return self.parse_dictionary();
|
||||
}
|
||||
|
||||
self.expected("an expression", token)
|
||||
|
@ -3139,7 +3139,7 @@ impl<'a> Parser<'a> {
|
|||
Ok(fields)
|
||||
}
|
||||
|
||||
/// DuckDB specific: Parse a duckdb [dictionary]
|
||||
/// DuckDB and ClickHouse specific: Parse a duckdb [dictionary] or a clickhouse [map] setting
|
||||
///
|
||||
/// Syntax:
|
||||
///
|
||||
|
@ -3148,18 +3148,18 @@ impl<'a> Parser<'a> {
|
|||
/// ```
|
||||
///
|
||||
/// [dictionary]: https://duckdb.org/docs/sql/data_types/struct#creating-structs
|
||||
fn parse_duckdb_struct_literal(&mut self) -> Result<Expr, ParserError> {
|
||||
/// [map]: https://clickhouse.com/docs/operations/settings/settings#additional_table_filters
|
||||
fn parse_dictionary(&mut self) -> Result<Expr, ParserError> {
|
||||
self.expect_token(&Token::LBrace)?;
|
||||
|
||||
let fields =
|
||||
self.parse_comma_separated0(Self::parse_duckdb_dictionary_field, Token::RBrace)?;
|
||||
let fields = self.parse_comma_separated0(Self::parse_dictionary_field, Token::RBrace)?;
|
||||
|
||||
self.expect_token(&Token::RBrace)?;
|
||||
|
||||
Ok(Expr::Dictionary(fields))
|
||||
}
|
||||
|
||||
/// Parse a field for a duckdb [dictionary]
|
||||
/// Parse a field for a duckdb [dictionary] or a clickhouse [map] setting
|
||||
///
|
||||
/// Syntax
|
||||
///
|
||||
|
@ -3168,7 +3168,8 @@ impl<'a> Parser<'a> {
|
|||
/// ```
|
||||
///
|
||||
/// [dictionary]: https://duckdb.org/docs/sql/data_types/struct#creating-structs
|
||||
fn parse_duckdb_dictionary_field(&mut self) -> Result<DictionaryField, ParserError> {
|
||||
/// [map]: https://clickhouse.com/docs/operations/settings/settings#additional_table_filters
|
||||
fn parse_dictionary_field(&mut self) -> Result<DictionaryField, ParserError> {
|
||||
let key = self.parse_identifier()?;
|
||||
|
||||
self.expect_token(&Token::Colon)?;
|
||||
|
@ -11216,7 +11217,7 @@ impl<'a> Parser<'a> {
|
|||
let key_values = self.parse_comma_separated(|p| {
|
||||
let key = p.parse_identifier()?;
|
||||
p.expect_token(&Token::Eq)?;
|
||||
let value = p.parse_value()?.value;
|
||||
let value = p.parse_expr()?;
|
||||
Ok(Setting { key, value })
|
||||
})?;
|
||||
Some(key_values)
|
||||
|
|
|
@ -366,6 +366,11 @@ pub fn number(n: &str) -> Value {
|
|||
Value::Number(n.parse().unwrap(), false)
|
||||
}
|
||||
|
||||
/// Creates a [Value::SingleQuotedString]
|
||||
pub fn single_quoted_string(s: impl Into<String>) -> Value {
|
||||
Value::SingleQuotedString(s.into())
|
||||
}
|
||||
|
||||
pub fn table_alias(name: impl Into<String>) -> Option<TableAlias> {
|
||||
Some(TableAlias {
|
||||
name: Ident::new(name),
|
||||
|
|
|
@ -28,7 +28,7 @@ use test_utils::*;
|
|||
use sqlparser::ast::Expr::{BinaryOp, Identifier};
|
||||
use sqlparser::ast::SelectItem::UnnamedExpr;
|
||||
use sqlparser::ast::TableFactor::Table;
|
||||
use sqlparser::ast::Value::Number;
|
||||
use sqlparser::ast::Value::Boolean;
|
||||
use sqlparser::ast::*;
|
||||
use sqlparser::dialect::ClickHouseDialect;
|
||||
use sqlparser::dialect::GenericDialect;
|
||||
|
@ -965,38 +965,103 @@ fn parse_limit_by() {
|
|||
|
||||
#[test]
|
||||
fn parse_settings_in_query() {
|
||||
match clickhouse_and_generic()
|
||||
.verified_stmt(r#"SELECT * FROM t SETTINGS max_threads = 1, max_block_size = 10000"#)
|
||||
{
|
||||
Statement::Query(query) => {
|
||||
assert_eq!(
|
||||
query.settings,
|
||||
Some(vec![
|
||||
Setting {
|
||||
key: Ident::new("max_threads"),
|
||||
value: Number("1".parse().unwrap(), false)
|
||||
},
|
||||
Setting {
|
||||
key: Ident::new("max_block_size"),
|
||||
value: Number("10000".parse().unwrap(), false)
|
||||
},
|
||||
])
|
||||
);
|
||||
fn check_settings(sql: &str, expected: Vec<Setting>) {
|
||||
match clickhouse_and_generic().verified_stmt(sql) {
|
||||
Statement::Query(q) => {
|
||||
assert_eq!(q.settings, Some(expected));
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
for (sql, expected_settings) in [
|
||||
(
|
||||
r#"SELECT * FROM t SETTINGS max_threads = 1, max_block_size = 10000"#,
|
||||
vec![
|
||||
Setting {
|
||||
key: Ident::new("max_threads"),
|
||||
value: Expr::value(number("1")),
|
||||
},
|
||||
Setting {
|
||||
key: Ident::new("max_block_size"),
|
||||
value: Expr::value(number("10000")),
|
||||
},
|
||||
],
|
||||
),
|
||||
(
|
||||
r#"SELECT * FROM t SETTINGS additional_table_filters = {'table_1': 'x != 2'}"#,
|
||||
vec![Setting {
|
||||
key: Ident::new("additional_table_filters"),
|
||||
value: Expr::Dictionary(vec![DictionaryField {
|
||||
key: Ident::with_quote('\'', "table_1"),
|
||||
value: Expr::value(single_quoted_string("x != 2")).into(),
|
||||
}]),
|
||||
}],
|
||||
),
|
||||
(
|
||||
r#"SELECT * FROM t SETTINGS additional_result_filter = 'x != 2', query_plan_optimize_lazy_materialization = false"#,
|
||||
vec![
|
||||
Setting {
|
||||
key: Ident::new("additional_result_filter"),
|
||||
value: Expr::value(single_quoted_string("x != 2")),
|
||||
},
|
||||
Setting {
|
||||
key: Ident::new("query_plan_optimize_lazy_materialization"),
|
||||
value: Expr::value(Boolean(false)),
|
||||
},
|
||||
],
|
||||
),
|
||||
] {
|
||||
check_settings(sql, expected_settings);
|
||||
}
|
||||
|
||||
let invalid_cases = vec![
|
||||
"SELECT * FROM t SETTINGS a",
|
||||
"SELECT * FROM t SETTINGS a=",
|
||||
"SELECT * FROM t SETTINGS a=1, b",
|
||||
"SELECT * FROM t SETTINGS a=1, b=",
|
||||
"SELECT * FROM t SETTINGS a=1, b=c",
|
||||
("SELECT * FROM t SETTINGS a", "Expected: =, found: EOF"),
|
||||
(
|
||||
"SELECT * FROM t SETTINGS a=",
|
||||
"Expected: an expression, found: EOF",
|
||||
),
|
||||
("SELECT * FROM t SETTINGS a=1, b", "Expected: =, found: EOF"),
|
||||
(
|
||||
"SELECT * FROM t SETTINGS a=1, b=",
|
||||
"Expected: an expression, found: EOF",
|
||||
),
|
||||
(
|
||||
"SELECT * FROM t SETTINGS a = {",
|
||||
"Expected: identifier, found: EOF",
|
||||
),
|
||||
(
|
||||
"SELECT * FROM t SETTINGS a = {'b'",
|
||||
"Expected: :, found: EOF",
|
||||
),
|
||||
(
|
||||
"SELECT * FROM t SETTINGS a = {'b': ",
|
||||
"Expected: an expression, found: EOF",
|
||||
),
|
||||
(
|
||||
"SELECT * FROM t SETTINGS a = {'b': 'c',}",
|
||||
"Expected: identifier, found: }",
|
||||
),
|
||||
(
|
||||
"SELECT * FROM t SETTINGS a = {'b': 'c', 'd'}",
|
||||
"Expected: :, found: }",
|
||||
),
|
||||
(
|
||||
"SELECT * FROM t SETTINGS a = {'b': 'c', 'd': }",
|
||||
"Expected: an expression, found: }",
|
||||
),
|
||||
(
|
||||
"SELECT * FROM t SETTINGS a = {ANY(b)}",
|
||||
"Expected: :, found: (",
|
||||
),
|
||||
];
|
||||
for sql in invalid_cases {
|
||||
clickhouse_and_generic()
|
||||
.parse_sql_statements(sql)
|
||||
.expect_err("Expected: SETTINGS key = value, found: ");
|
||||
for (sql, error_msg) in invalid_cases {
|
||||
assert_eq!(
|
||||
clickhouse_and_generic()
|
||||
.parse_sql_statements(sql)
|
||||
.unwrap_err(),
|
||||
ParserError(error_msg.to_string())
|
||||
);
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
|
@ -1550,11 +1615,11 @@ fn parse_select_table_function_settings() {
|
|||
settings: Some(vec![
|
||||
Setting {
|
||||
key: "s0".into(),
|
||||
value: Value::Number("3".parse().unwrap(), false),
|
||||
value: Expr::value(number("3")),
|
||||
},
|
||||
Setting {
|
||||
key: "s1".into(),
|
||||
value: Value::SingleQuotedString("s".into()),
|
||||
value: Expr::value(single_quoted_string("s")),
|
||||
},
|
||||
]),
|
||||
},
|
||||
|
@ -1575,11 +1640,11 @@ fn parse_select_table_function_settings() {
|
|||
settings: Some(vec![
|
||||
Setting {
|
||||
key: "s0".into(),
|
||||
value: Value::Number("3".parse().unwrap(), false),
|
||||
value: Expr::value(number("3")),
|
||||
},
|
||||
Setting {
|
||||
key: "s1".into(),
|
||||
value: Value::SingleQuotedString("s".into()),
|
||||
value: Expr::value(single_quoted_string("s")),
|
||||
},
|
||||
]),
|
||||
},
|
||||
|
@ -1589,7 +1654,6 @@ fn parse_select_table_function_settings() {
|
|||
"SELECT * FROM t(SETTINGS a=)",
|
||||
"SELECT * FROM t(SETTINGS a=1, b)",
|
||||
"SELECT * FROM t(SETTINGS a=1, b=)",
|
||||
"SELECT * FROM t(SETTINGS a=1, b=c)",
|
||||
];
|
||||
for sql in invalid_cases {
|
||||
clickhouse_and_generic()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue