Skip to content

Commit 85e4b6f

Browse files
committed
Add support of parsing ON CLUSTER in ALTER TABLE for ClickHouse
ClickHouse dialect supports `ON CLUSTER` clause for ALTER TABLE: ```sql ALTER TABLE table_name ON CLUSTER cluster_name ADD COLUMN c UInt32 ``` Please refer to: https://clickhouse.com/docs/en/sql-reference/statements/alter
1 parent 547d82f commit 85e4b6f

File tree

8 files changed

+56
-17
lines changed

8 files changed

+56
-17
lines changed

src/ast/dml.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -206,11 +206,7 @@ impl Display for CreateTable {
206206
name = self.name,
207207
)?;
208208
if let Some(on_cluster) = &self.on_cluster {
209-
write!(
210-
f,
211-
" ON CLUSTER {}",
212-
on_cluster.replace('{', "'{").replace('}', "}'")
213-
)?;
209+
write!(f, " ON CLUSTER {}", on_cluster)?;
214210
}
215211
if !self.columns.is_empty() || !self.constraints.is_empty() {
216212
write!(f, " ({}", display_comma_separated(&self.columns))?;

src/ast/mod.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2162,6 +2162,10 @@ pub enum Statement {
21622162
only: bool,
21632163
operations: Vec<AlterTableOperation>,
21642164
location: Option<HiveSetLocation>,
2165+
/// ClickHouse dialect supports `ON CLUSTER` clause for ALTER TABLE
2166+
/// For example: `ALTER TABLE table_name ON CLUSTER cluster_name ADD COLUMN c UInt32`
2167+
/// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/alter)
2168+
on_cluster: Option<String>,
21652169
},
21662170
/// ```sql
21672171
/// ALTER INDEX
@@ -3623,6 +3627,7 @@ impl fmt::Display for Statement {
36233627
only,
36243628
operations,
36253629
location,
3630+
on_cluster,
36263631
} => {
36273632
write!(f, "ALTER TABLE ")?;
36283633
if *if_exists {
@@ -3631,9 +3636,13 @@ impl fmt::Display for Statement {
36313636
if *only {
36323637
write!(f, "ONLY ")?;
36333638
}
3639+
write!(f, "{name} ", name = name)?;
3640+
if let Some(cluster) = on_cluster {
3641+
write!(f, "ON CLUSTER {cluster} ")?;
3642+
}
36343643
write!(
36353644
f,
3636-
"{name} {operations}",
3645+
"{operations}",
36373646
operations = display_comma_separated(operations)
36383647
)?;
36393648
if let Some(loc) = location {

src/parser/mod.rs

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5372,6 +5372,19 @@ impl<'a> Parser<'a> {
53725372
}
53735373
}
53745374

5375+
fn parse_optional_on_cluster(&mut self) -> Result<Option<String>, ParserError> {
5376+
if self.parse_keywords(&[Keyword::ON, Keyword::CLUSTER]) {
5377+
let next_token = self.next_token();
5378+
match next_token.token {
5379+
Token::SingleQuotedString(s) => Ok(Some(format!("'{}'", s))),
5380+
Token::Word(s) => Ok(Some(s.to_string())),
5381+
_ => self.expected("identifier or cluster literal", next_token)?,
5382+
}
5383+
} else {
5384+
Ok(None)
5385+
}
5386+
}
5387+
53755388
pub fn parse_create_table(
53765389
&mut self,
53775390
or_replace: bool,
@@ -5384,16 +5397,7 @@ impl<'a> Parser<'a> {
53845397
let table_name = self.parse_object_name(allow_unquoted_hyphen)?;
53855398

53865399
// Clickhouse has `ON CLUSTER 'cluster'` syntax for DDLs
5387-
let on_cluster = if self.parse_keywords(&[Keyword::ON, Keyword::CLUSTER]) {
5388-
let next_token = self.next_token();
5389-
match next_token.token {
5390-
Token::SingleQuotedString(s) => Some(s),
5391-
Token::Word(s) => Some(s.to_string()),
5392-
_ => self.expected("identifier or cluster literal", next_token)?,
5393-
}
5394-
} else {
5395-
None
5396-
};
5400+
let on_cluster = self.parse_optional_on_cluster()?;
53975401

53985402
let like = if self.parse_keyword(Keyword::LIKE) || self.parse_keyword(Keyword::ILIKE) {
53995403
self.parse_object_name(allow_unquoted_hyphen).ok()
@@ -6576,6 +6580,7 @@ impl<'a> Parser<'a> {
65766580
let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]);
65776581
let only = self.parse_keyword(Keyword::ONLY); // [ ONLY ]
65786582
let table_name = self.parse_object_name(false)?;
6583+
let on_cluster = self.parse_optional_on_cluster()?;
65796584
let operations = self.parse_comma_separated(Parser::parse_alter_table_operation)?;
65806585

65816586
let mut location = None;
@@ -6597,6 +6602,7 @@ impl<'a> Parser<'a> {
65976602
only,
65986603
operations,
65996604
location,
6605+
on_cluster,
66006606
})
66016607
}
66026608
Keyword::INDEX => {

src/test_utils.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,7 @@ pub fn alter_table_op_with_name(stmt: Statement, expected_name: &str) -> AlterTa
274274
if_exists,
275275
only: is_only,
276276
operations,
277+
on_cluster: _,
277278
location: _,
278279
} => {
279280
assert_eq!(name.to_string(), expected_name);

tests/sqlparser_clickhouse.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ use sqlparser::ast::Value::Number;
2525
use sqlparser::ast::*;
2626
use sqlparser::dialect::ClickHouseDialect;
2727
use sqlparser::dialect::GenericDialect;
28+
use sqlparser::parser::ParserError::ParserError;
2829

2930
#[test]
3031
fn parse_map_access_expr() {
@@ -1091,6 +1092,27 @@ fn parse_create_table_on_commit_and_as_query() {
10911092
}
10921093
}
10931094

1095+
#[test]
1096+
fn test_alter_table_with_on_cluster() {
1097+
let sql = "ALTER TABLE t ON CLUSTER 'cluster' ADD CONSTRAINT bar PRIMARY KEY (baz)";
1098+
match clickhouse_and_generic().verified_stmt(sql) {
1099+
Statement::AlterTable {
1100+
name, on_cluster, ..
1101+
} => {
1102+
assert_eq!(name.to_string(), "t");
1103+
assert_eq!(on_cluster, Some("'cluster'".to_string()));
1104+
}
1105+
_ => unreachable!(),
1106+
}
1107+
1108+
let res = clickhouse_and_generic()
1109+
.parse_sql_statements("ALTER TABLE t ON CLUSTER 123 ADD CONSTRAINT bar PRIMARY KEY (baz)");
1110+
assert_eq!(
1111+
res.unwrap_err(),
1112+
ParserError("Expected: identifier or cluster literal, found: 123".to_string())
1113+
)
1114+
}
1115+
10941116
fn clickhouse() -> TestedDialects {
10951117
TestedDialects {
10961118
dialects: vec![Box::new(ClickHouseDialect {})],

tests/sqlparser_common.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3506,7 +3506,7 @@ fn parse_create_table_on_cluster() {
35063506
let sql = "CREATE TABLE t ON CLUSTER '{cluster}' (a INT, b INT)";
35073507
match generic.verified_stmt(sql) {
35083508
Statement::CreateTable(CreateTable { on_cluster, .. }) => {
3509-
assert_eq!(on_cluster.unwrap(), "{cluster}".to_string());
3509+
assert_eq!(on_cluster.unwrap(), "'{cluster}'".to_string());
35103510
}
35113511
_ => unreachable!(),
35123512
}

tests/sqlparser_mysql.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1975,6 +1975,7 @@ fn parse_alter_table_add_column() {
19751975
only,
19761976
operations,
19771977
location: _,
1978+
on_cluster: _,
19781979
} => {
19791980
assert_eq!(name.to_string(), "tab");
19801981
assert!(!if_exists);
@@ -2004,6 +2005,7 @@ fn parse_alter_table_add_column() {
20042005
only,
20052006
operations,
20062007
location: _,
2008+
on_cluster: _,
20072009
} => {
20082010
assert_eq!(name.to_string(), "tab");
20092011
assert!(!if_exists);
@@ -2041,6 +2043,7 @@ fn parse_alter_table_add_columns() {
20412043
only,
20422044
operations,
20432045
location: _,
2046+
on_cluster: _,
20442047
} => {
20452048
assert_eq!(name.to_string(), "tab");
20462049
assert!(!if_exists);

tests/sqlparser_postgres.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -677,6 +677,7 @@ fn parse_alter_table_add_columns() {
677677
only,
678678
operations,
679679
location: _,
680+
on_cluster: _,
680681
} => {
681682
assert_eq!(name.to_string(), "tab");
682683
assert!(if_exists);
@@ -759,6 +760,7 @@ fn parse_alter_table_owner_to() {
759760
only: _,
760761
operations,
761762
location: _,
763+
on_cluster: _,
762764
} => {
763765
assert_eq!(name.to_string(), "tab");
764766
assert_eq!(

0 commit comments

Comments
 (0)