From 357d8559ab557c04580f28626fad320d2cd81ea0 Mon Sep 17 00:00:00 2001 From: Sridhar R M Date: Sat, 2 Nov 2024 15:00:13 +0000 Subject: [PATCH] [Enhancement] (nereids)implement showCreateViewCommand in nereids --- .../org/apache/doris/nereids/DorisParser.g4 | 2 +- .../nereids/parser/LogicalPlanBuilder.java | 9 ++ .../doris/nereids/trees/plans/PlanType.java | 1 + .../plans/commands/ShowCreateViewCommand.java | 126 ++++++++++++++++++ .../trees/plans/visitor/CommandVisitor.java | 5 + .../nereids_p0/show/test_show_create_view.out | 7 + .../show/test_show_create_view.groovy | 76 +++++++++++ 7 files changed, 225 insertions(+), 1 deletion(-) create mode 100644 fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowCreateViewCommand.java create mode 100644 regression-test/data/nereids_p0/show/test_show_create_view.out create mode 100644 regression-test/suites/nereids_p0/show/test_show_create_view.groovy diff --git a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4 b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4 index 0b851440bfe98bb..83f3439a616cb4b 100644 --- a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4 +++ b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4 @@ -205,6 +205,7 @@ supportedShowStatement | SHOW REPOSITORIES #showRepositories | SHOW ROLES #showRoles | SHOW PROC path=STRING_LITERAL #showProc + | SHOW CREATE VIEW name=multipartIdentifier #showCreateView | SHOW CREATE MATERIALIZED VIEW mvName=identifier ON tableName=multipartIdentifier #showCreateMaterializedView ; @@ -257,7 +258,6 @@ unsupportedShowStatement | SHOW PLUGINS #showPlugins | SHOW STORAGE? ENGINES #showStorageEngines | SHOW BRIEF? CREATE TABLE name=multipartIdentifier #showCreateTable - | SHOW CREATE VIEW name=multipartIdentifier #showCreateView | SHOW CREATE MATERIALIZED VIEW name=multipartIdentifier #showMaterializedView | SHOW CREATE (DATABASE | SCHEMA) name=multipartIdentifier #showCreateDatabase | SHOW CREATE CATALOG name=identifier #showCreateCatalog diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java index a7a0c970f0b6485..06fd438278a2d2e 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java @@ -200,6 +200,7 @@ import org.apache.doris.nereids.DorisParser.ShowCreateMTMVContext; import org.apache.doris.nereids.DorisParser.ShowCreateMaterializedViewContext; import org.apache.doris.nereids.DorisParser.ShowCreateProcedureContext; +import org.apache.doris.nereids.DorisParser.ShowCreateViewContext; import org.apache.doris.nereids.DorisParser.ShowLastInsertContext; import org.apache.doris.nereids.DorisParser.ShowProcContext; import org.apache.doris.nereids.DorisParser.ShowProcedureStatusContext; @@ -440,6 +441,7 @@ import org.apache.doris.nereids.trees.plans.commands.ShowCreateMTMVCommand; import org.apache.doris.nereids.trees.plans.commands.ShowCreateMaterializedViewCommand; import org.apache.doris.nereids.trees.plans.commands.ShowCreateProcedureCommand; +import org.apache.doris.nereids.trees.plans.commands.ShowCreateViewCommand; import org.apache.doris.nereids.trees.plans.commands.ShowLastInsertCommand; import org.apache.doris.nereids.trees.plans.commands.ShowProcCommand; import org.apache.doris.nereids.trees.plans.commands.ShowProcedureStatusCommand; @@ -4097,6 +4099,12 @@ public LogicalPlan visitShowProc(ShowProcContext ctx) { return new ShowProcCommand(path); } + @Override + public LogicalPlan visitShowCreateView(ShowCreateViewContext ctx) { + List nameParts = visitMultipartIdentifier(ctx.name); + return new ShowCreateViewCommand(new TableNameInfo(nameParts)); + } + @Override public LogicalPlan visitShowCreateMaterializedView(ShowCreateMaterializedViewContext ctx) { List nameParts = visitMultipartIdentifier(ctx.tableName); @@ -4117,6 +4125,7 @@ public LogicalPlan visitRecoverDatabase(RecoverDatabaseContext ctx) { return new RecoverDatabaseCommand(dbName, dbId, newDbName); } + @Override public LogicalPlan visitDropRole(DropRoleContext ctx) { return new DropRoleCommand(ctx.name.getText(), ctx.EXISTS() != null); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlanType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlanType.java index 99ba411f1e8f02c..216deb7b3b260f2 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlanType.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlanType.java @@ -178,6 +178,7 @@ public enum PlanType { EXECUTE_COMMAND, SHOW_CONFIG_COMMAND, SHOW_CREATE_MATERIALIZED_VIEW_COMMAND, + SHOW_CREATE_VIEW_COMMAND, SHOW_LAST_INSERT_COMMAND, SHOW_PROC_COMMAND, SHOW_REPOSITORIES_COMMAND, diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowCreateViewCommand.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowCreateViewCommand.java new file mode 100644 index 000000000000000..96fb047c542f85a --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowCreateViewCommand.java @@ -0,0 +1,126 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.trees.plans.commands; + +import org.apache.doris.catalog.Column; +import org.apache.doris.catalog.DatabaseIf; +import org.apache.doris.catalog.Env; +import org.apache.doris.catalog.MTMV; +import org.apache.doris.catalog.ScalarType; +import org.apache.doris.catalog.TableIf; +import org.apache.doris.catalog.View; +import org.apache.doris.common.AnalysisException; +import org.apache.doris.common.ErrorCode; +import org.apache.doris.common.ErrorReport; +import org.apache.doris.mysql.privilege.PrivPredicate; +import org.apache.doris.nereids.trees.plans.PlanType; +import org.apache.doris.nereids.trees.plans.commands.info.TableNameInfo; +import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor; +import org.apache.doris.qe.ConnectContext; +import org.apache.doris.qe.ShowResultSet; +import org.apache.doris.qe.ShowResultSetMetaData; +import org.apache.doris.qe.StmtExecutor; + +import com.google.common.collect.Lists; + +import java.util.List; + +/** + * Represents the command for SHOW CREATE VIEW. + */ +public class ShowCreateViewCommand extends ShowCommand { + private static final ShowResultSetMetaData VIEW_META_DATA = + ShowResultSetMetaData.builder() + .addColumn(new Column("View", ScalarType.createVarchar(20))) + .addColumn(new Column("Create View", ScalarType.createVarchar(30))) + .addColumn(new Column("character_set_client", ScalarType.createVarchar(30))) + .addColumn(new Column("collation_connection", ScalarType.createVarchar(30))) + .build(); + + private final TableNameInfo tblNameInfo; + + public ShowCreateViewCommand(TableNameInfo tableNameInfo) { + super(PlanType.SHOW_CREATE_VIEW_COMMAND); + this.tblNameInfo = tableNameInfo; + } + + private void validate(ConnectContext ctx) throws AnalysisException { + tblNameInfo.analyze(ctx); + + TableIf tableIf = Env.getCurrentEnv().getCatalogMgr() + .getCatalogOrAnalysisException(tblNameInfo.getCtl()) + .getDbOrAnalysisException(tblNameInfo.getDb()).getTableOrAnalysisException(tblNameInfo.getTbl()); + + if (tableIf instanceof MTMV) { + ErrorReport.reportAnalysisException("not support async materialized view, " + + "please use `show create materialized view`"); + } + + PrivPredicate wanted; + if (tableIf instanceof View) { + wanted = PrivPredicate.SHOW_VIEW; + } else { + wanted = PrivPredicate.SHOW; + } + + if (!Env.getCurrentEnv().getAccessManager().checkTblPriv(ConnectContext.get(), + tblNameInfo.getCtl(), tblNameInfo.getDb(), tblNameInfo.getTbl(), wanted)) { + ErrorReport.reportAnalysisException(ErrorCode.ERR_TABLEACCESS_DENIED_ERROR, "SHOW CREATE TABLE", + ConnectContext.get().getQualifiedUser(), + ConnectContext.get().getRemoteIP(), + tblNameInfo.getDb() + ": " + tblNameInfo.getTbl()); + } + } + + @Override + public R accept(PlanVisitor visitor, C context) { + return visitor.visitShowCreateViewCommand(this, context); + } + + @Override + public ShowResultSet doRun(ConnectContext ctx, StmtExecutor executor) throws Exception { + validate(ctx); + // Fetch the catalog, database, and view metadata + DatabaseIf db = ctx.getEnv().getCatalogMgr().getCatalogOrAnalysisException(tblNameInfo.getCtl()) + .getDbOrMetaException(tblNameInfo.getDb()); + TableIf view = db.getTableOrMetaException(tblNameInfo.getTbl()); + + if (!(view instanceof View)) { + ErrorReport.reportAnalysisException(ErrorCode.ERR_WRONG_OBJECT, tblNameInfo.getDb(), tblNameInfo.getTbl(), + "VIEW", "Use 'SHOW CREATE TABLE '" + tblNameInfo.getTbl()); + } + + List> rows = Lists.newArrayList(); + // Lock the view to ensure consistent metadata access + view.readLock(); + try { + List createViewStmt = Lists.newArrayList(); + ctx.getEnv().getDdlStmt(null, null, view, createViewStmt, null, null, false, true, + false, -1L, false, false); + + if (!createViewStmt.isEmpty()) { + rows.add(Lists.newArrayList(view.getName(), createViewStmt.get(0), "utf8mb4", "utf8mb4_0900_bin")); + } + } finally { + view.readUnlock(); + } + + // Set the result set and send it using the executor + return new ShowResultSet(VIEW_META_DATA, rows); + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/CommandVisitor.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/CommandVisitor.java index b38684582a1a748..8e2d8cfa3f88ed8 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/CommandVisitor.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/CommandVisitor.java @@ -56,6 +56,7 @@ import org.apache.doris.nereids.trees.plans.commands.ShowCreateMTMVCommand; import org.apache.doris.nereids.trees.plans.commands.ShowCreateMaterializedViewCommand; import org.apache.doris.nereids.trees.plans.commands.ShowCreateProcedureCommand; +import org.apache.doris.nereids.trees.plans.commands.ShowCreateViewCommand; import org.apache.doris.nereids.trees.plans.commands.ShowLastInsertCommand; import org.apache.doris.nereids.trees.plans.commands.ShowProcCommand; import org.apache.doris.nereids.trees.plans.commands.ShowProcedureStatusCommand; @@ -277,6 +278,10 @@ default R visitShowCreateMaterializedViewCommand(ShowCreateMaterializedViewComma return visitCommand(showCreateMtlzViewCommand, context); } + default R visitShowCreateViewCommand(ShowCreateViewCommand showCreateViewCommand, C context) { + return visitCommand(showCreateViewCommand, context); + } + default R visitAlterRoleCommand(AlterRoleCommand alterRoleCommand, C context) { return visitCommand(alterRoleCommand, context); } diff --git a/regression-test/data/nereids_p0/show/test_show_create_view.out b/regression-test/data/nereids_p0/show/test_show_create_view.out new file mode 100644 index 000000000000000..8ac669337b482c7 --- /dev/null +++ b/regression-test/data/nereids_p0/show/test_show_create_view.out @@ -0,0 +1,7 @@ +-- This file is automatically generated. You should know what you did if you want to edit this +-- !cmd -- +view_show_create_view CREATE VIEW `view_show_create_view` AS SELECT `internal`.`regression_test_nereids_p0_show`.`table_for_view_test`.`id`, `internal`.`regression_test_nereids_p0_show`.`table_for_view_test`.`name` FROM `internal`.`regression_test_nereids_p0_show`.`table_for_view_test`; utf8mb4 utf8mb4_0900_bin + +-- !cmd -- +view_show_create_view_2 CREATE VIEW `view_show_create_view_2` AS SELECT `internal`.`regression_test_nereids_p0_show`.`table_for_view_test_2`.`key_field`, `internal`.`regression_test_nereids_p0_show`.`table_for_view_test_2`.`value` FROM `internal`.`regression_test_nereids_p0_show`.`table_for_view_test_2`; utf8mb4 utf8mb4_0900_bin + diff --git a/regression-test/suites/nereids_p0/show/test_show_create_view.groovy b/regression-test/suites/nereids_p0/show/test_show_create_view.groovy new file mode 100644 index 000000000000000..65ed4758a9ae66d --- /dev/null +++ b/regression-test/suites/nereids_p0/show/test_show_create_view.groovy @@ -0,0 +1,76 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +suite("test_show_create_view", "query,arrow_flight_sql") { + String view_name = "view_show_create_view"; + String table_name = "table_for_view_test"; + try { + // Create a table for testing + sql """ + CREATE TABLE IF NOT EXISTS ${table_name} ( + id INT COMMENT "Primary key", + name STRING COMMENT "Name field" + ) + DISTRIBUTED BY HASH(id) BUCKETS 5 + PROPERTIES ("replication_num" = "1"); + """ + + // Create a view based on the created table + sql """ + CREATE VIEW IF NOT EXISTS ${view_name} AS + SELECT id, name FROM ${table_name} + """ + + // Execute the SHOW CREATE VIEW command + checkNereidsExecute("""show create view `${view_name}`;""") + qt_cmd("""show create view `${view_name}`;""") + } finally { + // Drop the view and table after testing + try_sql("DROP VIEW IF EXISTS `${view_name}`") + try_sql("DROP TABLE IF EXISTS `${table_name}`") + } + + // Additional case: Create another view based on a different table + String view_name_2 = "view_show_create_view_2"; + String table_name_2 = "table_for_view_test_2"; + try { + // Create another table for testing + sql """ + CREATE TABLE IF NOT EXISTS ${table_name_2} ( + `key_field` INT COMMENT "Key field", + `value` STRING COMMENT "Value field" + ) + DISTRIBUTED BY HASH(key_field) BUCKETS 3 + PROPERTIES ("replication_num" = "1"); + """ + + // Create a view based on the new table + sql """ + CREATE VIEW IF NOT EXISTS ${view_name_2} AS + SELECT key_field, value FROM ${table_name_2} + """ + + // Execute the SHOW CREATE VIEW command for the new view + checkNereidsExecute("""show create view `${view_name_2}`;""") + qt_cmd("""show create view `${view_name_2}`;""") + + } finally { + // Drop the view and table after testing + try_sql("DROP VIEW IF EXISTS `${view_name_2}`") + try_sql("DROP TABLE IF EXISTS `${table_name_2}`") + } +}