Skip to content

Commit a7b49b5

Browse files
authored
Add support of DROP|CLEAR|MATERIALIZE PROJECTION syntax for ClickHouse (#1417)
1 parent 4875dad commit a7b49b5

File tree

4 files changed

+198
-0
lines changed

4 files changed

+198
-0
lines changed

src/ast/ddl.rs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,33 @@ pub enum AlterTableOperation {
5757
name: Ident,
5858
select: ProjectionSelect,
5959
},
60+
61+
/// `DROP PROJECTION [IF EXISTS] name`
62+
///
63+
/// Note: this is a ClickHouse-specific operation.
64+
/// Please refer to [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/alter/projection#drop-projection)
65+
DropProjection { if_exists: bool, name: Ident },
66+
67+
/// `MATERIALIZE PROJECTION [IF EXISTS] name [IN PARTITION partition_name]`
68+
///
69+
/// Note: this is a ClickHouse-specific operation.
70+
/// Please refer to [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/alter/projection#materialize-projection)
71+
MaterializeProjection {
72+
if_exists: bool,
73+
name: Ident,
74+
partition: Option<Ident>,
75+
},
76+
77+
/// `CLEAR PROJECTION [IF EXISTS] name [IN PARTITION partition_name]`
78+
///
79+
/// Note: this is a ClickHouse-specific operation.
80+
/// Please refer to [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/alter/projection#clear-projection)
81+
ClearProjection {
82+
if_exists: bool,
83+
name: Ident,
84+
partition: Option<Ident>,
85+
},
86+
6087
/// `DISABLE ROW LEVEL SECURITY`
6188
///
6289
/// Note: this is a PostgreSQL-specific operation.
@@ -275,6 +302,43 @@ impl fmt::Display for AlterTableOperation {
275302
}
276303
write!(f, " {} ({})", name, query)
277304
}
305+
AlterTableOperation::DropProjection { if_exists, name } => {
306+
write!(f, "DROP PROJECTION")?;
307+
if *if_exists {
308+
write!(f, " IF EXISTS")?;
309+
}
310+
write!(f, " {}", name)
311+
}
312+
AlterTableOperation::MaterializeProjection {
313+
if_exists,
314+
name,
315+
partition,
316+
} => {
317+
write!(f, "MATERIALIZE PROJECTION")?;
318+
if *if_exists {
319+
write!(f, " IF EXISTS")?;
320+
}
321+
write!(f, " {}", name)?;
322+
if let Some(partition) = partition {
323+
write!(f, " IN PARTITION {}", partition)?;
324+
}
325+
Ok(())
326+
}
327+
AlterTableOperation::ClearProjection {
328+
if_exists,
329+
name,
330+
partition,
331+
} => {
332+
write!(f, "CLEAR PROJECTION")?;
333+
if *if_exists {
334+
write!(f, " IF EXISTS")?;
335+
}
336+
write!(f, " {}", name)?;
337+
if let Some(partition) = partition {
338+
write!(f, " IN PARTITION {}", partition)?;
339+
}
340+
Ok(())
341+
}
278342
AlterTableOperation::AlterColumn { column_name, op } => {
279343
write!(f, "ALTER COLUMN {column_name} {op}")
280344
}

src/keywords.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ define_keywords!(
153153
CHARSET,
154154
CHAR_LENGTH,
155155
CHECK,
156+
CLEAR,
156157
CLOB,
157158
CLONE,
158159
CLOSE,
@@ -450,6 +451,7 @@ define_keywords!(
450451
MATCHES,
451452
MATCH_CONDITION,
452453
MATCH_RECOGNIZE,
454+
MATERIALIZE,
453455
MATERIALIZED,
454456
MAX,
455457
MAXVALUE,

src/parser/mod.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6615,6 +6615,36 @@ impl<'a> Parser<'a> {
66156615
self.peek_token(),
66166616
);
66176617
}
6618+
} else if self.parse_keywords(&[Keyword::CLEAR, Keyword::PROJECTION])
6619+
&& dialect_of!(self is ClickHouseDialect|GenericDialect)
6620+
{
6621+
let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]);
6622+
let name = self.parse_identifier(false)?;
6623+
let partition = if self.parse_keywords(&[Keyword::IN, Keyword::PARTITION]) {
6624+
Some(self.parse_identifier(false)?)
6625+
} else {
6626+
None
6627+
};
6628+
AlterTableOperation::ClearProjection {
6629+
if_exists,
6630+
name,
6631+
partition,
6632+
}
6633+
} else if self.parse_keywords(&[Keyword::MATERIALIZE, Keyword::PROJECTION])
6634+
&& dialect_of!(self is ClickHouseDialect|GenericDialect)
6635+
{
6636+
let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]);
6637+
let name = self.parse_identifier(false)?;
6638+
let partition = if self.parse_keywords(&[Keyword::IN, Keyword::PARTITION]) {
6639+
Some(self.parse_identifier(false)?)
6640+
} else {
6641+
None
6642+
};
6643+
AlterTableOperation::MaterializeProjection {
6644+
if_exists,
6645+
name,
6646+
partition,
6647+
}
66186648
} else if self.parse_keyword(Keyword::DROP) {
66196649
if self.parse_keywords(&[Keyword::IF, Keyword::EXISTS, Keyword::PARTITION]) {
66206650
self.expect_token(&Token::LParen)?;
@@ -6645,6 +6675,12 @@ impl<'a> Parser<'a> {
66456675
&& dialect_of!(self is MySqlDialect | GenericDialect)
66466676
{
66476677
AlterTableOperation::DropPrimaryKey
6678+
} else if self.parse_keyword(Keyword::PROJECTION)
6679+
&& dialect_of!(self is ClickHouseDialect|GenericDialect)
6680+
{
6681+
let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]);
6682+
let name = self.parse_identifier(false)?;
6683+
AlterTableOperation::DropProjection { if_exists, name }
66486684
} else {
66496685
let _ = self.parse_keyword(Keyword::COLUMN); // [ COLUMN ]
66506686
let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]);

tests/sqlparser_clickhouse.rs

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,102 @@ fn parse_alter_table_add_projection() {
359359
);
360360
}
361361

362+
#[test]
363+
fn parse_alter_table_drop_projection() {
364+
match clickhouse_and_generic().verified_stmt("ALTER TABLE t0 DROP PROJECTION IF EXISTS my_name")
365+
{
366+
Statement::AlterTable {
367+
name, operations, ..
368+
} => {
369+
assert_eq!(name, ObjectName(vec!["t0".into()]));
370+
assert_eq!(1, operations.len());
371+
assert_eq!(
372+
operations[0],
373+
AlterTableOperation::DropProjection {
374+
if_exists: true,
375+
name: "my_name".into(),
376+
}
377+
)
378+
}
379+
_ => unreachable!(),
380+
}
381+
// allow to skip `IF EXISTS`
382+
clickhouse_and_generic().verified_stmt("ALTER TABLE t0 DROP PROJECTION my_name");
383+
384+
assert_eq!(
385+
clickhouse_and_generic()
386+
.parse_sql_statements("ALTER TABLE t0 DROP PROJECTION")
387+
.unwrap_err(),
388+
ParserError("Expected: identifier, found: EOF".to_string())
389+
);
390+
}
391+
392+
#[test]
393+
fn parse_alter_table_clear_and_materialize_projection() {
394+
for keyword in ["CLEAR", "MATERIALIZE"] {
395+
match clickhouse_and_generic().verified_stmt(
396+
format!("ALTER TABLE t0 {keyword} PROJECTION IF EXISTS my_name IN PARTITION p0",)
397+
.as_str(),
398+
) {
399+
Statement::AlterTable {
400+
name, operations, ..
401+
} => {
402+
assert_eq!(name, ObjectName(vec!["t0".into()]));
403+
assert_eq!(1, operations.len());
404+
assert_eq!(
405+
operations[0],
406+
if keyword == "CLEAR" {
407+
AlterTableOperation::ClearProjection {
408+
if_exists: true,
409+
name: "my_name".into(),
410+
partition: Some(Ident::new("p0")),
411+
}
412+
} else {
413+
AlterTableOperation::MaterializeProjection {
414+
if_exists: true,
415+
name: "my_name".into(),
416+
partition: Some(Ident::new("p0")),
417+
}
418+
}
419+
)
420+
}
421+
_ => unreachable!(),
422+
}
423+
// allow to skip `IF EXISTS`
424+
clickhouse_and_generic().verified_stmt(
425+
format!("ALTER TABLE t0 {keyword} PROJECTION my_name IN PARTITION p0",).as_str(),
426+
);
427+
// allow to skip `IN PARTITION partition_name`
428+
clickhouse_and_generic()
429+
.verified_stmt(format!("ALTER TABLE t0 {keyword} PROJECTION my_name",).as_str());
430+
431+
assert_eq!(
432+
clickhouse_and_generic()
433+
.parse_sql_statements(format!("ALTER TABLE t0 {keyword} PROJECTION",).as_str())
434+
.unwrap_err(),
435+
ParserError("Expected: identifier, found: EOF".to_string())
436+
);
437+
438+
assert_eq!(
439+
clickhouse_and_generic()
440+
.parse_sql_statements(
441+
format!("ALTER TABLE t0 {keyword} PROJECTION my_name IN PARTITION",).as_str()
442+
)
443+
.unwrap_err(),
444+
ParserError("Expected: identifier, found: EOF".to_string())
445+
);
446+
447+
assert_eq!(
448+
clickhouse_and_generic()
449+
.parse_sql_statements(
450+
format!("ALTER TABLE t0 {keyword} PROJECTION my_name IN",).as_str()
451+
)
452+
.unwrap_err(),
453+
ParserError("Expected: end of statement, found: IN".to_string())
454+
);
455+
}
456+
}
457+
362458
#[test]
363459
fn parse_optimize_table() {
364460
clickhouse_and_generic().verified_stmt("OPTIMIZE TABLE t0");

0 commit comments

Comments
 (0)