Skip to content

Commit abe5c9e

Browse files
committed
Add support of parsing ON CLUSTER in ALTER TABLE for ClickHouse (apache#1342)
1 parent e4e0dc0 commit abe5c9e

File tree

8 files changed

+67
-21
lines changed

8 files changed

+67
-21
lines changed

src/ast/dml.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ pub struct CreateTable {
126126
pub on_commit: Option<OnCommit>,
127127
/// ClickHouse "ON CLUSTER" clause:
128128
/// <https://clickhouse.com/docs/en/sql-reference/distributed-ddl/>
129-
pub on_cluster: Option<String>,
129+
pub on_cluster: Option<Ident>,
130130
/// ClickHouse "PRIMARY KEY " clause.
131131
/// <https://clickhouse.com/docs/en/sql-reference/statements/create/table/>
132132
pub primary_key: Option<Box<Expr>>,
@@ -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/helpers/stmt_create_table.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ pub struct CreateTableBuilder {
7373
pub default_charset: Option<String>,
7474
pub collation: Option<String>,
7575
pub on_commit: Option<OnCommit>,
76-
pub on_cluster: Option<String>,
76+
pub on_cluster: Option<Ident>,
7777
pub primary_key: Option<Box<Expr>>,
7878
pub order_by: Option<OneOrManyWithParens<Expr>>,
7979
pub partition_by: Option<Box<Expr>>,
@@ -261,7 +261,7 @@ impl CreateTableBuilder {
261261
self
262262
}
263263

264-
pub fn on_cluster(mut self, on_cluster: Option<String>) -> Self {
264+
pub fn on_cluster(mut self, on_cluster: Option<Ident>) -> Self {
265265
self.on_cluster = on_cluster;
266266
self
267267
}

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/update)
2168+
on_cluster: Option<Ident>,
21652169
},
21662170
/// ```sql
21672171
/// ALTER INDEX
@@ -3644,6 +3648,7 @@ impl fmt::Display for Statement {
36443648
only,
36453649
operations,
36463650
location,
3651+
on_cluster,
36473652
} => {
36483653
write!(f, "ALTER TABLE ")?;
36493654
if *if_exists {
@@ -3652,9 +3657,13 @@ impl fmt::Display for Statement {
36523657
if *only {
36533658
write!(f, "ONLY ")?;
36543659
}
3660+
write!(f, "{name} ", name = name)?;
3661+
if let Some(cluster) = on_cluster {
3662+
write!(f, "ON CLUSTER {cluster} ")?;
3663+
}
36553664
write!(
36563665
f,
3657-
"{name} {operations}",
3666+
"{operations}",
36583667
operations = display_comma_separated(operations)
36593668
)?;
36603669
if let Some(loc) = location {

src/parser/mod.rs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5383,6 +5383,14 @@ impl<'a> Parser<'a> {
53835383
}
53845384
}
53855385

5386+
fn parse_optional_on_cluster(&mut self) -> Result<Option<Ident>, ParserError> {
5387+
if self.parse_keywords(&[Keyword::ON, Keyword::CLUSTER]) {
5388+
Ok(Some(self.parse_identifier(false)?))
5389+
} else {
5390+
Ok(None)
5391+
}
5392+
}
5393+
53865394
pub fn parse_create_table(
53875395
&mut self,
53885396
or_replace: bool,
@@ -5395,16 +5403,7 @@ impl<'a> Parser<'a> {
53955403
let table_name = self.parse_object_name(allow_unquoted_hyphen)?;
53965404

53975405
// Clickhouse has `ON CLUSTER 'cluster'` syntax for DDLs
5398-
let on_cluster = if self.parse_keywords(&[Keyword::ON, Keyword::CLUSTER]) {
5399-
let next_token = self.next_token();
5400-
match next_token.token {
5401-
Token::SingleQuotedString(s) => Some(s),
5402-
Token::Word(s) => Some(s.to_string()),
5403-
_ => self.expected("identifier or cluster literal", next_token)?,
5404-
}
5405-
} else {
5406-
None
5407-
};
5406+
let on_cluster = self.parse_optional_on_cluster()?;
54085407

54095408
let like = if self.parse_keyword(Keyword::LIKE) || self.parse_keyword(Keyword::ILIKE) {
54105409
self.parse_object_name(allow_unquoted_hyphen).ok()
@@ -6587,6 +6586,7 @@ impl<'a> Parser<'a> {
65876586
let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]);
65886587
let only = self.parse_keyword(Keyword::ONLY); // [ ONLY ]
65896588
let table_name = self.parse_object_name(false)?;
6589+
let on_cluster = self.parse_optional_on_cluster()?;
65906590
let operations = self.parse_comma_separated(Parser::parse_alter_table_operation)?;
65916591

65926592
let mut location = None;
@@ -6608,6 +6608,7 @@ impl<'a> Parser<'a> {
66086608
only,
66096609
operations,
66106610
location,
6611+
on_cluster,
66116612
})
66126613
}
66136614
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_common.rs

Lines changed: 36 additions & 2 deletions
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().to_string(), "'{cluster}'".to_string());
35103510
}
35113511
_ => unreachable!(),
35123512
}
@@ -3515,7 +3515,7 @@ fn parse_create_table_on_cluster() {
35153515
let sql = "CREATE TABLE t ON CLUSTER my_cluster (a INT, b INT)";
35163516
match generic.verified_stmt(sql) {
35173517
Statement::CreateTable(CreateTable { on_cluster, .. }) => {
3518-
assert_eq!(on_cluster.unwrap(), "my_cluster".to_string());
3518+
assert_eq!(on_cluster.unwrap().to_string(), "my_cluster".to_string());
35193519
}
35203520
_ => unreachable!(),
35213521
}
@@ -3822,6 +3822,40 @@ fn parse_alter_table() {
38223822
}
38233823
}
38243824

3825+
#[test]
3826+
fn test_alter_table_with_on_cluster() {
3827+
match all_dialects()
3828+
.verified_stmt("ALTER TABLE t ON CLUSTER 'cluster' ADD CONSTRAINT bar PRIMARY KEY (baz)")
3829+
{
3830+
Statement::AlterTable {
3831+
name, on_cluster, ..
3832+
} => {
3833+
std::assert_eq!(name.to_string(), "t");
3834+
std::assert_eq!(on_cluster, Some(Ident::with_quote('\'', "cluster")));
3835+
}
3836+
_ => unreachable!(),
3837+
}
3838+
3839+
match all_dialects()
3840+
.verified_stmt("ALTER TABLE t ON CLUSTER cluster_name ADD CONSTRAINT bar PRIMARY KEY (baz)")
3841+
{
3842+
Statement::AlterTable {
3843+
name, on_cluster, ..
3844+
} => {
3845+
std::assert_eq!(name.to_string(), "t");
3846+
std::assert_eq!(on_cluster, Some(Ident::new("cluster_name")));
3847+
}
3848+
_ => unreachable!(),
3849+
}
3850+
3851+
let res = all_dialects()
3852+
.parse_sql_statements("ALTER TABLE t ON CLUSTER 123 ADD CONSTRAINT bar PRIMARY KEY (baz)");
3853+
std::assert_eq!(
3854+
res.unwrap_err(),
3855+
ParserError::ParserError("Expected: identifier, found: 123".to_string())
3856+
)
3857+
}
3858+
38253859
#[test]
38263860
fn parse_alter_index() {
38273861
let rename_index = "ALTER INDEX idx RENAME TO new_idx";

tests/sqlparser_mysql.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1976,6 +1976,7 @@ fn parse_alter_table_add_column() {
19761976
only,
19771977
operations,
19781978
location: _,
1979+
on_cluster: _,
19791980
} => {
19801981
assert_eq!(name.to_string(), "tab");
19811982
assert!(!if_exists);
@@ -2005,6 +2006,7 @@ fn parse_alter_table_add_column() {
20052006
only,
20062007
operations,
20072008
location: _,
2009+
on_cluster: _,
20082010
} => {
20092011
assert_eq!(name.to_string(), "tab");
20102012
assert!(!if_exists);
@@ -2042,6 +2044,7 @@ fn parse_alter_table_add_columns() {
20422044
only,
20432045
operations,
20442046
location: _,
2047+
on_cluster: _,
20452048
} => {
20462049
assert_eq!(name.to_string(), "tab");
20472050
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)