Skip to content

Commit f3d2f78

Browse files
Bidaya0iffyioalamb
authored
Support TO in CREATE VIEW clause for Clickhouse (#1313)
Co-authored-by: Ifeanyi Ubah <[email protected]> Co-authored-by: Andrew Lamb <[email protected]>
1 parent f16c1af commit f3d2f78

File tree

7 files changed

+51
-2
lines changed

7 files changed

+51
-2
lines changed

src/ast/mod.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2029,6 +2029,9 @@ pub enum Statement {
20292029
if_not_exists: bool,
20302030
/// if true, has SQLite `TEMP` or `TEMPORARY` clause <https://www.sqlite.org/lang_createview.html>
20312031
temporary: bool,
2032+
/// if not None, has Clickhouse `TO` clause, specify the table into which to insert results
2033+
/// <https://clickhouse.com/docs/en/sql-reference/statements/create/view#materialized-view>
2034+
to: Option<ObjectName>,
20322035
},
20332036
/// ```sql
20342037
/// CREATE TABLE
@@ -3329,15 +3332,20 @@ impl fmt::Display for Statement {
33293332
with_no_schema_binding,
33303333
if_not_exists,
33313334
temporary,
3335+
to,
33323336
} => {
33333337
write!(
33343338
f,
3335-
"CREATE {or_replace}{materialized}{temporary}VIEW {if_not_exists}{name}",
3339+
"CREATE {or_replace}{materialized}{temporary}VIEW {if_not_exists}{name}{to}",
33363340
or_replace = if *or_replace { "OR REPLACE " } else { "" },
33373341
materialized = if *materialized { "MATERIALIZED " } else { "" },
33383342
name = name,
33393343
temporary = if *temporary { "TEMPORARY " } else { "" },
3340-
if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" }
3344+
if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" },
3345+
to = to
3346+
.as_ref()
3347+
.map(|to| format!(" TO {to}"))
3348+
.unwrap_or_default()
33413349
)?;
33423350
if let Some(comment) = comment {
33433351
write!(

src/parser/mod.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4172,6 +4172,14 @@ impl<'a> Parser<'a> {
41724172
};
41734173
}
41744174

4175+
let to = if dialect_of!(self is ClickHouseDialect | GenericDialect)
4176+
&& self.parse_keyword(Keyword::TO)
4177+
{
4178+
Some(self.parse_object_name(false)?)
4179+
} else {
4180+
None
4181+
};
4182+
41754183
let comment = if dialect_of!(self is SnowflakeDialect | GenericDialect)
41764184
&& self.parse_keyword(Keyword::COMMENT)
41774185
{
@@ -4209,6 +4217,7 @@ impl<'a> Parser<'a> {
42094217
with_no_schema_binding,
42104218
if_not_exists,
42114219
temporary,
4220+
to,
42124221
})
42134222
}
42144223

tests/sqlparser_bigquery.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,7 @@ fn parse_create_view_if_not_exists() {
315315
with_no_schema_binding: late_binding,
316316
if_not_exists,
317317
temporary,
318+
..
318319
} => {
319320
assert_eq!("mydataset.newview", name.to_string());
320321
assert_eq!(Vec::<ViewColumnDef>::new(), columns);

tests/sqlparser_clickhouse.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,21 @@ fn parse_select_star_except_no_parens() {
561561
);
562562
}
563563

564+
#[test]
565+
fn parse_create_materialized_view() {
566+
// example sql
567+
// https://clickhouse.com/docs/en/guides/developer/cascading-materialized-views
568+
let sql = concat!(
569+
"CREATE MATERIALIZED VIEW analytics.monthly_aggregated_data_mv ",
570+
"TO analytics.monthly_aggregated_data ",
571+
"AS SELECT toDate(toStartOfMonth(event_time)) ",
572+
"AS month, domain_name, sumState(count_views) ",
573+
"AS sumCountViews FROM analytics.hourly_data ",
574+
"GROUP BY domain_name, month"
575+
);
576+
clickhouse_and_generic().verified_stmt(sql);
577+
}
578+
564579
fn clickhouse() -> TestedDialects {
565580
TestedDialects {
566581
dialects: vec![Box::new(ClickHouseDialect {})],

tests/sqlparser_common.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6279,6 +6279,7 @@ fn parse_create_view() {
62796279
with_no_schema_binding: late_binding,
62806280
if_not_exists,
62816281
temporary,
6282+
to,
62826283
} => {
62836284
assert_eq!("myschema.myview", name.to_string());
62846285
assert_eq!(Vec::<ViewColumnDef>::new(), columns);
@@ -6291,6 +6292,7 @@ fn parse_create_view() {
62916292
assert!(!late_binding);
62926293
assert!(!if_not_exists);
62936294
assert!(!temporary);
6295+
assert!(to.is_none())
62946296
}
62956297
_ => unreachable!(),
62966298
}
@@ -6335,6 +6337,7 @@ fn parse_create_view_with_columns() {
63356337
with_no_schema_binding: late_binding,
63366338
if_not_exists,
63376339
temporary,
6340+
to,
63386341
} => {
63396342
assert_eq!("v", name.to_string());
63406343
assert_eq!(
@@ -6357,6 +6360,7 @@ fn parse_create_view_with_columns() {
63576360
assert!(!late_binding);
63586361
assert!(!if_not_exists);
63596362
assert!(!temporary);
6363+
assert!(to.is_none())
63606364
}
63616365
_ => unreachable!(),
63626366
}
@@ -6378,6 +6382,7 @@ fn parse_create_view_temporary() {
63786382
with_no_schema_binding: late_binding,
63796383
if_not_exists,
63806384
temporary,
6385+
to,
63816386
} => {
63826387
assert_eq!("myschema.myview", name.to_string());
63836388
assert_eq!(Vec::<ViewColumnDef>::new(), columns);
@@ -6390,6 +6395,7 @@ fn parse_create_view_temporary() {
63906395
assert!(!late_binding);
63916396
assert!(!if_not_exists);
63926397
assert!(temporary);
6398+
assert!(to.is_none())
63936399
}
63946400
_ => unreachable!(),
63956401
}
@@ -6411,6 +6417,7 @@ fn parse_create_or_replace_view() {
64116417
with_no_schema_binding: late_binding,
64126418
if_not_exists,
64136419
temporary,
6420+
to,
64146421
} => {
64156422
assert_eq!("v", name.to_string());
64166423
assert_eq!(columns, vec![]);
@@ -6423,6 +6430,7 @@ fn parse_create_or_replace_view() {
64236430
assert!(!late_binding);
64246431
assert!(!if_not_exists);
64256432
assert!(!temporary);
6433+
assert!(to.is_none())
64266434
}
64276435
_ => unreachable!(),
64286436
}
@@ -6448,6 +6456,7 @@ fn parse_create_or_replace_materialized_view() {
64486456
with_no_schema_binding: late_binding,
64496457
if_not_exists,
64506458
temporary,
6459+
to,
64516460
} => {
64526461
assert_eq!("v", name.to_string());
64536462
assert_eq!(columns, vec![]);
@@ -6460,6 +6469,7 @@ fn parse_create_or_replace_materialized_view() {
64606469
assert!(!late_binding);
64616470
assert!(!if_not_exists);
64626471
assert!(!temporary);
6472+
assert!(to.is_none())
64636473
}
64646474
_ => unreachable!(),
64656475
}
@@ -6481,6 +6491,7 @@ fn parse_create_materialized_view() {
64816491
with_no_schema_binding: late_binding,
64826492
if_not_exists,
64836493
temporary,
6494+
to,
64846495
} => {
64856496
assert_eq!("myschema.myview", name.to_string());
64866497
assert_eq!(Vec::<ViewColumnDef>::new(), columns);
@@ -6493,6 +6504,7 @@ fn parse_create_materialized_view() {
64936504
assert!(!late_binding);
64946505
assert!(!if_not_exists);
64956506
assert!(!temporary);
6507+
assert!(to.is_none())
64966508
}
64976509
_ => unreachable!(),
64986510
}
@@ -6514,6 +6526,7 @@ fn parse_create_materialized_view_with_cluster_by() {
65146526
with_no_schema_binding: late_binding,
65156527
if_not_exists,
65166528
temporary,
6529+
to,
65176530
} => {
65186531
assert_eq!("myschema.myview", name.to_string());
65196532
assert_eq!(Vec::<ViewColumnDef>::new(), columns);
@@ -6526,6 +6539,7 @@ fn parse_create_materialized_view_with_cluster_by() {
65266539
assert!(!late_binding);
65276540
assert!(!if_not_exists);
65286541
assert!(!temporary);
6542+
assert!(to.is_none())
65296543
}
65306544
_ => unreachable!(),
65316545
}

tests/sqlparser_snowflake.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,7 @@ fn parse_sf_create_or_replace_with_comment_for_snowflake() {
552552
with_no_schema_binding: late_binding,
553553
if_not_exists,
554554
temporary,
555+
..
555556
} => {
556557
assert_eq!("v", name.to_string());
557558
assert_eq!(columns, vec![]);

tests/sqlparser_sqlite.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ fn parse_create_view_temporary_if_not_exists() {
171171
with_no_schema_binding: late_binding,
172172
if_not_exists,
173173
temporary,
174+
..
174175
} => {
175176
assert_eq!("myschema.myview", name.to_string());
176177
assert_eq!(Vec::<ViewColumnDef>::new(), columns);

0 commit comments

Comments
 (0)