Skip to content

Commit 87e3c4a

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 20f7ac5 commit 87e3c4a

File tree

8 files changed

+55
-17
lines changed

8 files changed

+55
-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
@@ -2119,6 +2119,10 @@ pub enum Statement {
21192119
only: bool,
21202120
operations: Vec<AlterTableOperation>,
21212121
location: Option<HiveSetLocation>,
2122+
/// ClickHouse dialect supports `ON CLUSTER` clause for ALTER TABLE
2123+
/// For example: `ALTER TABLE table_name ON CLUSTER cluster_name ADD COLUMN c UInt32`
2124+
/// [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/alter)
2125+
on_cluster: Option<String>,
21222126
},
21232127
/// ```sql
21242128
/// ALTER INDEX
@@ -3580,6 +3584,7 @@ impl fmt::Display for Statement {
35803584
only,
35813585
operations,
35823586
location,
3587+
on_cluster,
35833588
} => {
35843589
write!(f, "ALTER TABLE ")?;
35853590
if *if_exists {
@@ -3588,9 +3593,13 @@ impl fmt::Display for Statement {
35883593
if *only {
35893594
write!(f, "ONLY ")?;
35903595
}
3596+
write!(f, "{name} ", name = name)?;
3597+
if let Some(cluster) = on_cluster {
3598+
write!(f, "ON CLUSTER {cluster} ")?;
3599+
}
35913600
write!(
35923601
f,
3593-
"{name} {operations}",
3602+
"{operations}",
35943603
operations = display_comma_separated(operations)
35953604
)?;
35963605
if let Some(loc) = location {

src/parser/mod.rs

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5313,6 +5313,19 @@ impl<'a> Parser<'a> {
53135313
}
53145314
}
53155315

5316+
fn parse_optional_on_cluster(&mut self) -> Result<Option<String>, ParserError> {
5317+
if self.parse_keywords(&[Keyword::ON, Keyword::CLUSTER]) {
5318+
let next_token = self.next_token();
5319+
match next_token.token {
5320+
Token::SingleQuotedString(s) => Ok(Some(format!("'{}'", s))),
5321+
Token::Word(s) => Ok(Some(s.to_string())),
5322+
_ => self.expected("identifier or cluster literal", next_token)?,
5323+
}
5324+
} else {
5325+
Ok(None)
5326+
}
5327+
}
5328+
53165329
pub fn parse_create_table(
53175330
&mut self,
53185331
or_replace: bool,
@@ -5325,16 +5338,7 @@ impl<'a> Parser<'a> {
53255338
let table_name = self.parse_object_name(allow_unquoted_hyphen)?;
53265339

53275340
// Clickhouse has `ON CLUSTER 'cluster'` syntax for DDLs
5328-
let on_cluster = if self.parse_keywords(&[Keyword::ON, Keyword::CLUSTER]) {
5329-
let next_token = self.next_token();
5330-
match next_token.token {
5331-
Token::SingleQuotedString(s) => Some(s),
5332-
Token::Word(s) => Some(s.to_string()),
5333-
_ => self.expected("identifier or cluster literal", next_token)?,
5334-
}
5335-
} else {
5336-
None
5337-
};
5341+
let on_cluster = self.parse_optional_on_cluster()?;
53385342

53395343
let like = if self.parse_keyword(Keyword::LIKE) || self.parse_keyword(Keyword::ILIKE) {
53405344
self.parse_object_name(allow_unquoted_hyphen).ok()
@@ -6499,6 +6503,7 @@ impl<'a> Parser<'a> {
64996503
let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]);
65006504
let only = self.parse_keyword(Keyword::ONLY); // [ ONLY ]
65016505
let table_name = self.parse_object_name(false)?;
6506+
let on_cluster = self.parse_optional_on_cluster()?;
65026507
let operations = self.parse_comma_separated(Parser::parse_alter_table_operation)?;
65036508

65046509
let mut location = None;
@@ -6520,6 +6525,7 @@ impl<'a> Parser<'a> {
65206525
only,
65216526
operations,
65226527
location,
6528+
on_cluster,
65236529
})
65246530
}
65256531
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: 21 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() {
@@ -826,6 +827,26 @@ fn parse_create_table_on_commit_and_as_query() {
826827
}
827828
}
828829

830+
fn test_alter_table_with_on_cluster() {
831+
let sql = "ALTER TABLE t ON CLUSTER 'cluster' ADD CONSTRAINT bar PRIMARY KEY (baz)";
832+
match clickhouse_and_generic().verified_stmt(sql) {
833+
Statement::AlterTable {
834+
name, on_cluster, ..
835+
} => {
836+
assert_eq!(name.to_string(), "t");
837+
assert_eq!(on_cluster, Some("'cluster'".to_string()));
838+
}
839+
_ => unreachable!(),
840+
}
841+
842+
let res = clickhouse_and_generic()
843+
.parse_sql_statements("ALTER TABLE t ON CLUSTER 123 ADD CONSTRAINT bar PRIMARY KEY (baz)");
844+
assert_eq!(
845+
res.unwrap_err(),
846+
ParserError("Expected: identifier or cluster literal, found: 123".to_string())
847+
)
848+
}
849+
829850
fn clickhouse() -> TestedDialects {
830851
TestedDialects {
831852
dialects: vec![Box::new(ClickHouseDialect {})],

tests/sqlparser_common.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3495,7 +3495,7 @@ fn parse_create_table_on_cluster() {
34953495
let sql = "CREATE TABLE t ON CLUSTER '{cluster}' (a INT, b INT)";
34963496
match generic.verified_stmt(sql) {
34973497
Statement::CreateTable(CreateTable { on_cluster, .. }) => {
3498-
assert_eq!(on_cluster.unwrap(), "{cluster}".to_string());
3498+
assert_eq!(on_cluster.unwrap(), "'{cluster}'".to_string());
34993499
}
35003500
_ => unreachable!(),
35013501
}

tests/sqlparser_mysql.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1974,6 +1974,7 @@ fn parse_alter_table_add_column() {
19741974
only,
19751975
operations,
19761976
location: _,
1977+
on_cluster: _,
19771978
} => {
19781979
assert_eq!(name.to_string(), "tab");
19791980
assert!(!if_exists);
@@ -2003,6 +2004,7 @@ fn parse_alter_table_add_column() {
20032004
only,
20042005
operations,
20052006
location: _,
2007+
on_cluster: _,
20062008
} => {
20072009
assert_eq!(name.to_string(), "tab");
20082010
assert!(!if_exists);
@@ -2040,6 +2042,7 @@ fn parse_alter_table_add_columns() {
20402042
only,
20412043
operations,
20422044
location: _,
2045+
on_cluster: _,
20432046
} => {
20442047
assert_eq!(name.to_string(), "tab");
20452048
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)