mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-09-23 06:02:30 +00:00
Support SETTINGS
pairs for ClickHouse dialect (#1327)
Co-authored-by: Ifeanyi Ubah <ify1992@yahoo.com>
This commit is contained in:
parent
44d7a20f64
commit
700bd03d6f
9 changed files with 111 additions and 5 deletions
|
@ -48,7 +48,7 @@ pub use self::query::{
|
||||||
MatchRecognizePattern, MatchRecognizeSymbol, Measure, NamedWindowDefinition, NamedWindowExpr,
|
MatchRecognizePattern, MatchRecognizeSymbol, Measure, NamedWindowDefinition, NamedWindowExpr,
|
||||||
NonBlock, Offset, OffsetRows, OrderByExpr, PivotValueSource, Query, RenameSelectItem,
|
NonBlock, Offset, OffsetRows, OrderByExpr, PivotValueSource, Query, RenameSelectItem,
|
||||||
RepetitionQuantifier, ReplaceSelectElement, ReplaceSelectItem, RowsPerMatch, Select,
|
RepetitionQuantifier, ReplaceSelectElement, ReplaceSelectItem, RowsPerMatch, Select,
|
||||||
SelectInto, SelectItem, SetExpr, SetOperator, SetQuantifier, SymbolDefinition, Table,
|
SelectInto, SelectItem, SetExpr, SetOperator, SetQuantifier, Setting, SymbolDefinition, Table,
|
||||||
TableAlias, TableFactor, TableVersion, TableWithJoins, Top, TopQuantity, ValueTableMode,
|
TableAlias, TableFactor, TableVersion, TableWithJoins, Top, TopQuantity, ValueTableMode,
|
||||||
Values, WildcardAdditionalOptions, With,
|
Values, WildcardAdditionalOptions, With,
|
||||||
};
|
};
|
||||||
|
|
|
@ -50,6 +50,10 @@ pub struct Query {
|
||||||
/// `FOR JSON { AUTO | PATH } [ , INCLUDE_NULL_VALUES ]`
|
/// `FOR JSON { AUTO | PATH } [ , INCLUDE_NULL_VALUES ]`
|
||||||
/// (MSSQL-specific)
|
/// (MSSQL-specific)
|
||||||
pub for_clause: Option<ForClause>,
|
pub for_clause: Option<ForClause>,
|
||||||
|
/// ClickHouse syntax: `SELECT * FROM t SETTINGS key1 = value1, key2 = value2`
|
||||||
|
///
|
||||||
|
/// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/select#settings-in-select-query)
|
||||||
|
pub settings: Option<Vec<Setting>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Query {
|
impl fmt::Display for Query {
|
||||||
|
@ -70,6 +74,9 @@ impl fmt::Display for Query {
|
||||||
if !self.limit_by.is_empty() {
|
if !self.limit_by.is_empty() {
|
||||||
write!(f, " BY {}", display_separated(&self.limit_by, ", "))?;
|
write!(f, " BY {}", display_separated(&self.limit_by, ", "))?;
|
||||||
}
|
}
|
||||||
|
if let Some(ref settings) = self.settings {
|
||||||
|
write!(f, " SETTINGS {}", display_comma_separated(settings))?;
|
||||||
|
}
|
||||||
if let Some(ref fetch) = self.fetch {
|
if let Some(ref fetch) = self.fetch {
|
||||||
write!(f, " {fetch}")?;
|
write!(f, " {fetch}")?;
|
||||||
}
|
}
|
||||||
|
@ -828,6 +835,20 @@ impl fmt::Display for ConnectBy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
||||||
|
pub struct Setting {
|
||||||
|
pub key: Ident,
|
||||||
|
pub value: Value,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Setting {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{} = {}", self.key, self.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// An expression optionally followed by an alias.
|
/// An expression optionally followed by an alias.
|
||||||
///
|
///
|
||||||
/// Example:
|
/// Example:
|
||||||
|
|
|
@ -650,6 +650,7 @@ define_keywords!(
|
||||||
SESSION_USER,
|
SESSION_USER,
|
||||||
SET,
|
SET,
|
||||||
SETS,
|
SETS,
|
||||||
|
SETTINGS,
|
||||||
SHARE,
|
SHARE,
|
||||||
SHOW,
|
SHOW,
|
||||||
SIMILAR,
|
SIMILAR,
|
||||||
|
@ -850,6 +851,8 @@ pub const RESERVED_FOR_TABLE_ALIAS: &[Keyword] = &[
|
||||||
Keyword::FOR,
|
Keyword::FOR,
|
||||||
// for MYSQL PARTITION SELECTION
|
// for MYSQL PARTITION SELECTION
|
||||||
Keyword::PARTITION,
|
Keyword::PARTITION,
|
||||||
|
// for ClickHouse SELECT * FROM t SETTINGS ...
|
||||||
|
Keyword::SETTINGS,
|
||||||
// for Snowflake START WITH .. CONNECT BY
|
// for Snowflake START WITH .. CONNECT BY
|
||||||
Keyword::START,
|
Keyword::START,
|
||||||
Keyword::CONNECT,
|
Keyword::CONNECT,
|
||||||
|
|
|
@ -7871,6 +7871,7 @@ impl<'a> Parser<'a> {
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
settings: None,
|
||||||
})
|
})
|
||||||
} else if self.parse_keyword(Keyword::UPDATE) {
|
} else if self.parse_keyword(Keyword::UPDATE) {
|
||||||
Ok(Query {
|
Ok(Query {
|
||||||
|
@ -7883,6 +7884,7 @@ impl<'a> Parser<'a> {
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
settings: None,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
let body = self.parse_boxed_query_body(0)?;
|
let body = self.parse_boxed_query_body(0)?;
|
||||||
|
@ -7928,6 +7930,20 @@ impl<'a> Parser<'a> {
|
||||||
vec![]
|
vec![]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let settings = if dialect_of!(self is ClickHouseDialect|GenericDialect)
|
||||||
|
&& self.parse_keyword(Keyword::SETTINGS)
|
||||||
|
{
|
||||||
|
let key_values = self.parse_comma_separated(|p| {
|
||||||
|
let key = p.parse_identifier(false)?;
|
||||||
|
p.expect_token(&Token::Eq)?;
|
||||||
|
let value = p.parse_value()?;
|
||||||
|
Ok(Setting { key, value })
|
||||||
|
})?;
|
||||||
|
Some(key_values)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
let fetch = if self.parse_keyword(Keyword::FETCH) {
|
let fetch = if self.parse_keyword(Keyword::FETCH) {
|
||||||
Some(self.parse_fetch()?)
|
Some(self.parse_fetch()?)
|
||||||
} else {
|
} else {
|
||||||
|
@ -7955,6 +7971,7 @@ impl<'a> Parser<'a> {
|
||||||
fetch,
|
fetch,
|
||||||
locks,
|
locks,
|
||||||
for_clause,
|
for_clause,
|
||||||
|
settings,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9091,6 +9108,7 @@ impl<'a> Parser<'a> {
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
settings: None,
|
||||||
}),
|
}),
|
||||||
alias,
|
alias,
|
||||||
})
|
})
|
||||||
|
|
|
@ -21,8 +21,8 @@ use test_utils::*;
|
||||||
use sqlparser::ast::Expr::{BinaryOp, Identifier, MapAccess};
|
use sqlparser::ast::Expr::{BinaryOp, Identifier, MapAccess};
|
||||||
use sqlparser::ast::SelectItem::UnnamedExpr;
|
use sqlparser::ast::SelectItem::UnnamedExpr;
|
||||||
use sqlparser::ast::TableFactor::Table;
|
use sqlparser::ast::TableFactor::Table;
|
||||||
|
use sqlparser::ast::Value::Number;
|
||||||
use sqlparser::ast::*;
|
use sqlparser::ast::*;
|
||||||
|
|
||||||
use sqlparser::dialect::ClickHouseDialect;
|
use sqlparser::dialect::ClickHouseDialect;
|
||||||
use sqlparser::dialect::GenericDialect;
|
use sqlparser::dialect::GenericDialect;
|
||||||
|
|
||||||
|
@ -549,6 +549,42 @@ 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)
|
||||||
|
},
|
||||||
|
])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
|
||||||
|
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",
|
||||||
|
];
|
||||||
|
for sql in invalid_cases {
|
||||||
|
clickhouse_and_generic()
|
||||||
|
.parse_sql_statements(sql)
|
||||||
|
.expect_err("Expected: SETTINGS key = value, found: ");
|
||||||
|
}
|
||||||
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_select_star_except() {
|
fn parse_select_star_except() {
|
||||||
clickhouse().verified_stmt("SELECT * EXCEPT (prev_status) FROM anomalies");
|
clickhouse().verified_stmt("SELECT * EXCEPT (prev_status) FROM anomalies");
|
||||||
|
|
|
@ -413,6 +413,7 @@ fn parse_update_set_from() {
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
settings: None,
|
||||||
}),
|
}),
|
||||||
alias: Some(TableAlias {
|
alias: Some(TableAlias {
|
||||||
name: Ident::new("t2"),
|
name: Ident::new("t2"),
|
||||||
|
@ -3427,6 +3428,7 @@ fn parse_create_table_as_table() {
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
settings: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
match verified_stmt(sql1) {
|
match verified_stmt(sql1) {
|
||||||
|
@ -3452,6 +3454,7 @@ fn parse_create_table_as_table() {
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
settings: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
match verified_stmt(sql2) {
|
match verified_stmt(sql2) {
|
||||||
|
@ -4996,6 +4999,7 @@ fn parse_interval_and_or_xor() {
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
settings: None,
|
||||||
}))];
|
}))];
|
||||||
|
|
||||||
assert_eq!(actual_ast, expected_ast);
|
assert_eq!(actual_ast, expected_ast);
|
||||||
|
@ -7649,6 +7653,7 @@ fn parse_merge() {
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
settings: None,
|
||||||
}),
|
}),
|
||||||
alias: Some(TableAlias {
|
alias: Some(TableAlias {
|
||||||
name: Ident {
|
name: Ident {
|
||||||
|
@ -9156,6 +9161,7 @@ fn parse_unload() {
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
order_by: vec![],
|
order_by: vec![],
|
||||||
|
settings: None,
|
||||||
}),
|
}),
|
||||||
to: Ident {
|
to: Ident {
|
||||||
value: "s3://...".to_string(),
|
value: "s3://...".to_string(),
|
||||||
|
|
|
@ -103,6 +103,7 @@ fn parse_create_procedure() {
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
order_by: vec![],
|
order_by: vec![],
|
||||||
|
settings: None,
|
||||||
body: Box::new(SetExpr::Select(Box::new(Select {
|
body: Box::new(SetExpr::Select(Box::new(Select {
|
||||||
distinct: None,
|
distinct: None,
|
||||||
top: None,
|
top: None,
|
||||||
|
@ -546,6 +547,7 @@ fn parse_substring_in_select() {
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
settings: None,
|
||||||
}),
|
}),
|
||||||
query
|
query
|
||||||
);
|
);
|
||||||
|
|
|
@ -925,6 +925,7 @@ fn parse_escaped_quote_identifiers_with_escape() {
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
settings: None,
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -972,6 +973,7 @@ fn parse_escaped_quote_identifiers_with_no_escape() {
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
settings: None,
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1016,6 +1018,7 @@ fn parse_escaped_backticks_with_escape() {
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
settings: None,
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1060,6 +1063,7 @@ fn parse_escaped_backticks_with_no_escape() {
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
settings: None,
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1264,6 +1268,7 @@ fn parse_simple_insert() {
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
settings: None,
|
||||||
})),
|
})),
|
||||||
source
|
source
|
||||||
);
|
);
|
||||||
|
@ -1306,6 +1311,7 @@ fn parse_ignore_insert() {
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
settings: None,
|
||||||
})),
|
})),
|
||||||
source
|
source
|
||||||
);
|
);
|
||||||
|
@ -1348,6 +1354,7 @@ fn parse_priority_insert() {
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
settings: None,
|
||||||
})),
|
})),
|
||||||
source
|
source
|
||||||
);
|
);
|
||||||
|
@ -1387,6 +1394,7 @@ fn parse_priority_insert() {
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
settings: None,
|
||||||
})),
|
})),
|
||||||
source
|
source
|
||||||
);
|
);
|
||||||
|
@ -1434,6 +1442,7 @@ fn parse_insert_as() {
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
settings: None,
|
||||||
})),
|
})),
|
||||||
source
|
source
|
||||||
);
|
);
|
||||||
|
@ -1493,6 +1502,7 @@ fn parse_insert_as() {
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
settings: None,
|
||||||
})),
|
})),
|
||||||
source
|
source
|
||||||
);
|
);
|
||||||
|
@ -1536,6 +1546,7 @@ fn parse_replace_insert() {
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
settings: None,
|
||||||
})),
|
})),
|
||||||
source
|
source
|
||||||
);
|
);
|
||||||
|
@ -1573,6 +1584,7 @@ fn parse_empty_row_insert() {
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
settings: None,
|
||||||
})),
|
})),
|
||||||
source
|
source
|
||||||
);
|
);
|
||||||
|
@ -1633,6 +1645,7 @@ fn parse_insert_with_on_duplicate_update() {
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
settings: None,
|
||||||
})),
|
})),
|
||||||
source
|
source
|
||||||
);
|
);
|
||||||
|
@ -2273,6 +2286,7 @@ fn parse_substring_in_select() {
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
settings: None,
|
||||||
}),
|
}),
|
||||||
query
|
query
|
||||||
);
|
);
|
||||||
|
@ -2578,6 +2592,7 @@ fn parse_hex_string_introducer() {
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
settings: None,
|
||||||
}))
|
}))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1093,6 +1093,7 @@ fn parse_copy_to() {
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
settings: None,
|
||||||
})),
|
})),
|
||||||
to: true,
|
to: true,
|
||||||
target: CopyTarget::File {
|
target: CopyTarget::File {
|
||||||
|
@ -2421,6 +2422,7 @@ fn parse_array_subquery_expr() {
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None,
|
for_clause: None,
|
||||||
|
settings: None,
|
||||||
})),
|
})),
|
||||||
filter: None,
|
filter: None,
|
||||||
null_treatment: None,
|
null_treatment: None,
|
||||||
|
@ -3941,7 +3943,8 @@ fn test_simple_postgres_insert_with_alias() {
|
||||||
offset: None,
|
offset: None,
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None
|
for_clause: None,
|
||||||
|
settings: None,
|
||||||
})),
|
})),
|
||||||
partitioned: None,
|
partitioned: None,
|
||||||
after_columns: vec![],
|
after_columns: vec![],
|
||||||
|
@ -4008,7 +4011,8 @@ fn test_simple_postgres_insert_with_alias() {
|
||||||
offset: None,
|
offset: None,
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None
|
for_clause: None,
|
||||||
|
settings: None,
|
||||||
})),
|
})),
|
||||||
partitioned: None,
|
partitioned: None,
|
||||||
after_columns: vec![],
|
after_columns: vec![],
|
||||||
|
@ -4071,7 +4075,8 @@ fn test_simple_insert_with_quoted_alias() {
|
||||||
offset: None,
|
offset: None,
|
||||||
fetch: None,
|
fetch: None,
|
||||||
locks: vec![],
|
locks: vec![],
|
||||||
for_clause: None
|
for_clause: None,
|
||||||
|
settings: None,
|
||||||
})),
|
})),
|
||||||
partitioned: None,
|
partitioned: None,
|
||||||
after_columns: vec![],
|
after_columns: vec![],
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue