From ad86b046747d45c9d9e9617568ae317fd3b44b6d Mon Sep 17 00:00:00 2001 From: codedump Date: Mon, 5 Feb 2024 10:02:06 +0800 Subject: [PATCH] feat: add create or replace view support (#14599) --- src/query/ast/src/ast/format/syntax/ddl.rs | 18 ++- src/query/ast/src/ast/statements/view.rs | 16 ++- src/query/ast/src/parser/statement.rs | 23 ++- src/query/ast/tests/it/parser.rs | 1 + src/query/ast/tests/it/testdata/statement.txt | 132 +++++++++++++++++- .../interpreters/interpreter_view_create.rs | 3 +- src/query/sql/src/planner/binder/ddl/view.rs | 4 +- src/query/sql/src/planner/plans/ddl/view.rs | 4 +- .../base/05_ddl/05_0019_ddl_create_view.test | 17 +++ 9 files changed, 198 insertions(+), 20 deletions(-) diff --git a/src/query/ast/src/ast/format/syntax/ddl.rs b/src/query/ast/src/ast/format/syntax/ddl.rs index 59bdea461964..3197f9358971 100644 --- a/src/query/ast/src/ast/format/syntax/ddl.rs +++ b/src/query/ast/src/ast/format/syntax/ddl.rs @@ -243,12 +243,24 @@ pub(crate) fn pretty_alter_table_action(action: AlterTableAction) -> RcDoc<'stat } pub(crate) fn pretty_create_view(stmt: CreateViewStmt) -> RcDoc<'static> { - RcDoc::text("CREATE VIEW") - .append(if stmt.if_not_exists { - RcDoc::space().append(RcDoc::text("IF NOT EXISTS")) + RcDoc::text("CREATE") + .append(if let CreateOption::CreateOrReplace = stmt.create_option { + RcDoc::space().append(RcDoc::text("OR REPLACE")) } else { RcDoc::nil() }) + .append(RcDoc::space().append(RcDoc::text("VIEW"))) + .append( + if let CreateOption::CreateIfNotExists(if_not_exists) = stmt.create_option { + if if_not_exists { + RcDoc::space().append(RcDoc::text("IF NOT EXISTS")) + } else { + RcDoc::nil() + } + } else { + RcDoc::nil() + }, + ) .append( RcDoc::space() .append(if let Some(catalog) = stmt.catalog { diff --git a/src/query/ast/src/ast/statements/view.rs b/src/query/ast/src/ast/statements/view.rs index 5a06ff23297a..dac06def5352 100644 --- a/src/query/ast/src/ast/statements/view.rs +++ b/src/query/ast/src/ast/statements/view.rs @@ -15,6 +15,8 @@ use std::fmt::Display; use std::fmt::Formatter; +use databend_common_meta_app::schema::CreateOption; + use crate::ast::write_comma_separated_list; use crate::ast::write_dot_separated_list; use crate::ast::Identifier; @@ -22,7 +24,7 @@ use crate::ast::Query; #[derive(Debug, Clone, PartialEq)] pub struct CreateViewStmt { - pub if_not_exists: bool, + pub create_option: CreateOption, pub catalog: Option, pub database: Option, pub view: Identifier, @@ -32,9 +34,15 @@ pub struct CreateViewStmt { impl Display for CreateViewStmt { fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { - write!(f, "CREATE VIEW ")?; - if self.if_not_exists { - write!(f, "IF NOT EXISTS ")?; + write!(f, "CREATE ")?; + if let CreateOption::CreateOrReplace = self.create_option { + write!(f, "OR REPLACE ")?; + } + write!(f, "VIEW ")?; + if let CreateOption::CreateIfNotExists(if_not_exists) = self.create_option { + if if_not_exists { + write!(f, "IF NOT EXISTS ")?; + } } write_dot_separated_list( f, diff --git a/src/query/ast/src/parser/statement.rs b/src/query/ast/src/parser/statement.rs index ca60a9aa8ae5..e4a392a8cf07 100644 --- a/src/query/ast/src/parser/statement.rs +++ b/src/query/ast/src/parser/statement.rs @@ -889,16 +889,27 @@ pub fn statement(i: Input) -> IResult { }, ); - let create_view = map( + let create_view = map_res( rule! { - CREATE ~ VIEW ~ ( IF ~ ^NOT ~ ^EXISTS )? + CREATE ~ (OR ~ REPLACE)? ~ VIEW ~ ( IF ~ ^NOT ~ ^EXISTS )? ~ #dot_separated_idents_1_to_3 ~ ( "(" ~ #comma_separated_list1(ident) ~ ")" )? ~ AS ~ #query }, - |(_, _, opt_if_not_exists, (catalog, database, view), opt_columns, _, query)| { - Statement::CreateView(CreateViewStmt { - if_not_exists: opt_if_not_exists.is_some(), + |( + _, + opt_or_replace, + _, + opt_if_not_exists, + (catalog, database, view), + opt_columns, + _, + query, + )| { + let create_option = + parse_create_option(opt_or_replace.is_some(), opt_if_not_exists.is_some())?; + Ok(Statement::CreateView(CreateViewStmt { + create_option, catalog, database, view, @@ -906,7 +917,7 @@ pub fn statement(i: Input) -> IResult { .map(|(_, columns, _)| columns) .unwrap_or_default(), query: Box::new(query), - }) + })) }, ); let drop_view = map( diff --git a/src/query/ast/tests/it/parser.rs b/src/query/ast/tests/it/parser.rs index 58b529547466..2bab409a8125 100644 --- a/src/query/ast/tests/it/parser.rs +++ b/src/query/ast/tests/it/parser.rs @@ -132,6 +132,7 @@ fn test_statement() { r#"alter view v as select number % 3 as a from numbers(1000);"#, r#"drop view v;"#, r#"create view v1(c1) as select number % 3 as a from numbers(1000);"#, + r#"create or replace view v1(c1) as select number % 3 as a from numbers(1000);"#, r#"alter view v1(c2) as select number % 3 as a from numbers(1000);"#, r#"create stream test2.s1 on table test.t append_only = false;"#, r#"create stream if not exists test2.s2 on table test.t at (stream => test1.s1) comment = 'this is a stream';"#, diff --git a/src/query/ast/tests/it/testdata/statement.txt b/src/query/ast/tests/it/testdata/statement.txt index 5eb3408f993e..205aa4f9a448 100644 --- a/src/query/ast/tests/it/testdata/statement.txt +++ b/src/query/ast/tests/it/testdata/statement.txt @@ -2083,7 +2083,9 @@ CREATE VIEW v AS SELECT (number % 3) AS a FROM numbers(1000) ---------- AST ------------ CreateView( CreateViewStmt { - if_not_exists: false, + create_option: CreateIfNotExists( + false, + ), catalog: None, database: None, view: Identifier { @@ -2335,7 +2337,9 @@ CREATE VIEW v1 (c1) AS SELECT (number % 3) AS a FROM numbers(1000) ---------- AST ------------ CreateView( CreateViewStmt { - if_not_exists: false, + create_option: CreateIfNotExists( + false, + ), catalog: None, database: None, view: Identifier { @@ -2452,6 +2456,130 @@ CreateView( ) +---------- Input ---------- +create or replace view v1(c1) as select number % 3 as a from numbers(1000); +---------- Output --------- +CREATE OR REPLACE VIEW v1 (c1) AS SELECT (number % 3) AS a FROM numbers(1000) +---------- AST ------------ +CreateView( + CreateViewStmt { + create_option: CreateOrReplace, + catalog: None, + database: None, + view: Identifier { + name: "v1", + quote: None, + span: Some( + 23..25, + ), + }, + columns: [ + Identifier { + name: "c1", + quote: None, + span: Some( + 26..28, + ), + }, + ], + query: Query { + span: Some( + 33..74, + ), + with: None, + body: Select( + SelectStmt { + span: Some( + 33..74, + ), + hints: None, + distinct: false, + select_list: [ + AliasedExpr { + expr: BinaryOp { + span: Some( + 47..48, + ), + op: Modulo, + left: ColumnRef { + span: Some( + 40..46, + ), + database: None, + table: None, + column: Name( + Identifier { + name: "number", + quote: None, + span: Some( + 40..46, + ), + }, + ), + }, + right: Literal { + span: Some( + 49..50, + ), + lit: UInt64( + 3, + ), + }, + }, + alias: Some( + Identifier { + name: "a", + quote: None, + span: Some( + 54..55, + ), + }, + ), + }, + ], + from: [ + TableFunction { + span: Some( + 61..74, + ), + lateral: false, + name: Identifier { + name: "numbers", + quote: None, + span: Some( + 61..68, + ), + }, + params: [ + Literal { + span: Some( + 69..73, + ), + lit: UInt64( + 1000, + ), + }, + ], + named_params: [], + alias: None, + }, + ], + selection: None, + group_by: None, + having: None, + window_list: None, + qualify: None, + }, + ), + order_by: [], + limit: [], + offset: None, + ignore_result: false, + }, + }, +) + + ---------- Input ---------- alter view v1(c2) as select number % 3 as a from numbers(1000); ---------- Output --------- diff --git a/src/query/service/src/interpreters/interpreter_view_create.rs b/src/query/service/src/interpreters/interpreter_view_create.rs index 2db7fddb6200..8be4ae0e0aba 100644 --- a/src/query/service/src/interpreters/interpreter_view_create.rs +++ b/src/query/service/src/interpreters/interpreter_view_create.rs @@ -17,7 +17,6 @@ use std::sync::Arc; use databend_common_exception::ErrorCode; use databend_common_exception::Result; -use databend_common_meta_app::schema::CreateOption; use databend_common_meta_app::schema::CreateTableReq; use databend_common_meta_app::schema::TableMeta; use databend_common_meta_app::schema::TableNameIdent; @@ -104,7 +103,7 @@ impl Interpreter for CreateViewInterpreter { options.insert(QUERY.to_string(), subquery); let plan = CreateTableReq { - create_option: CreateOption::CreateIfNotExists(self.plan.if_not_exists), + create_option: self.plan.create_option, name_ident: TableNameIdent { tenant: self.plan.tenant.clone(), db_name: self.plan.database.clone(), diff --git a/src/query/sql/src/planner/binder/ddl/view.rs b/src/query/sql/src/planner/binder/ddl/view.rs index 860f45503f94..f15ec4a6906d 100644 --- a/src/query/sql/src/planner/binder/ddl/view.rs +++ b/src/query/sql/src/planner/binder/ddl/view.rs @@ -33,7 +33,7 @@ impl Binder { stmt: &CreateViewStmt, ) -> Result { let CreateViewStmt { - if_not_exists, + create_option, catalog, database, view, @@ -55,7 +55,7 @@ impl Binder { let subquery = format!("{}", query); let plan = CreateViewPlan { - if_not_exists: *if_not_exists, + create_option: *create_option, tenant, catalog, database, diff --git a/src/query/sql/src/planner/plans/ddl/view.rs b/src/query/sql/src/planner/plans/ddl/view.rs index 1bd23c64a888..4541c357d639 100644 --- a/src/query/sql/src/planner/plans/ddl/view.rs +++ b/src/query/sql/src/planner/plans/ddl/view.rs @@ -12,9 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. +use databend_common_meta_app::schema::CreateOption; + #[derive(Clone, Debug, PartialEq, Eq)] pub struct CreateViewPlan { - pub if_not_exists: bool, + pub create_option: CreateOption, pub tenant: String, pub catalog: String, pub database: String, diff --git a/tests/sqllogictests/suites/base/05_ddl/05_0019_ddl_create_view.test b/tests/sqllogictests/suites/base/05_ddl/05_0019_ddl_create_view.test index 6cbe1b90479f..817287409332 100644 --- a/tests/sqllogictests/suites/base/05_ddl/05_0019_ddl_create_view.test +++ b/tests/sqllogictests/suites/base/05_ddl/05_0019_ddl_create_view.test @@ -137,3 +137,20 @@ create view loop_view2 as select * from loop_view1; statement error 1025 create view loop_view3 as select * from loop_view2; + +statement ok +create view replace_view(a) as select * from numbers(3); + +statement error 1005 +create or replace view if not exists replace_view(a) as select * from numbers(3); + +statement ok +create or replace view replace_view(b) as select * from numbers(1); + +query I +select b from replace_view; +---- +0 + +statement ok +drop view if exists replace_view;