Skip to content

Commit

Permalink
[Enhancement] (nereids)implement showCreateViewCommand in nereids
Browse files Browse the repository at this point in the history
  • Loading branch information
msridhar78 committed Nov 4, 2024
1 parent 3b0d50f commit 260945b
Show file tree
Hide file tree
Showing 6 changed files with 257 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ statementBase
| constraintStatement #constraintStatementAlias
| supportedDropStatement #supportedDropStatementAlias
| supportedSetStatement #supportedSetStatementAlias
| supportedShowStatement #supportedShowStatementAlias
| unsupportedStatement #unsupported
;

Expand Down Expand Up @@ -216,6 +217,10 @@ lockTable
(READ (LOCAL)? | (LOW_PRIORITY)? WRITE)
;

supportedShowStatement
: SHOW CREATE VIEW name=multipartIdentifier #showCreateView
;

unsupportedShowStatement
: SHOW SQL_BLOCK_RULE (FOR ruleName=identifier)? #showSqlBlockRule
| SHOW ROW POLICY (FOR (userIdentify | (ROLE role=identifier)))? #showRowPolicy
Expand All @@ -238,7 +243,6 @@ unsupportedShowStatement
| SHOW STORAGE? ENGINES #showStorageEngines
| SHOW AUTHORS #showAuthors
| 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@
import org.apache.doris.nereids.DorisParser.ShowConstraintContext;
import org.apache.doris.nereids.DorisParser.ShowCreateMTMVContext;
import org.apache.doris.nereids.DorisParser.ShowCreateProcedureContext;
import org.apache.doris.nereids.DorisParser.ShowCreateViewContext;
import org.apache.doris.nereids.DorisParser.ShowProcedureStatusContext;
import org.apache.doris.nereids.DorisParser.SimpleColumnDefContext;
import org.apache.doris.nereids.DorisParser.SimpleColumnDefsContext;
Expand Down Expand Up @@ -421,6 +422,7 @@
import org.apache.doris.nereids.trees.plans.commands.ShowConstraintsCommand;
import org.apache.doris.nereids.trees.plans.commands.ShowCreateMTMVCommand;
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.ShowProcedureStatusCommand;
import org.apache.doris.nereids.trees.plans.commands.UnsupportedCommand;
import org.apache.doris.nereids.trees.plans.commands.UpdateCommand;
Expand Down Expand Up @@ -3977,4 +3979,10 @@ public SetUserPropertiesCommand visitSetUserProperties(SetUserPropertiesContext
public SetDefaultStorageVaultCommand visitSetDefaultStorageVault(SetDefaultStorageVaultContext ctx) {
return new SetDefaultStorageVaultCommand(stripQuotes(ctx.identifier().getText()));
}

@Override
public LogicalPlan visitShowCreateView(ShowCreateViewContext ctx) {
List<String> nameParts = visitMultipartIdentifier(ctx.name);
return new ShowCreateViewCommand(new TableNameInfo(nameParts));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -174,5 +174,6 @@ public enum PlanType {
PREPARED_COMMAND,
EXECUTE_COMMAND,
SHOW_CONFIG_COMMAND,
REPLAY_COMMAND
REPLAY_COMMAND,
SHOW_CREATE_VIEW_COMMAND
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
// 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.analysis.Analyzer;
import org.apache.doris.analysis.StmtType;
import org.apache.doris.analysis.TableName;
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 Command implements NoForward {
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 String catalog;
private final String dbName;
private final String viewName;

public ShowCreateViewCommand(TableNameInfo tableNameInfo) {
super(PlanType.SHOW_CREATE_VIEW_COMMAND);
this.catalog = tableNameInfo.getCtl();
this.dbName = tableNameInfo.getDb();
this.viewName = tableNameInfo.getTbl();
}

private void validate(ConnectContext ctx) throws AnalysisException {
TableName tbl = new TableName(catalog, dbName, viewName);

Analyzer analyzer = new Analyzer(ctx.getEnv(), ctx);
tbl.analyze(analyzer);

TableIf tableIf = Env.getCurrentEnv().getCatalogMgr()
.getCatalogOrAnalysisException(tbl.getCtl())
.getDbOrAnalysisException(tbl.getDb()).getTableOrAnalysisException(tbl.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(), tbl.getCtl(), tbl.getDb(),
tbl.getTbl(), wanted)) {
ErrorReport.reportAnalysisException(ErrorCode.ERR_TABLEACCESS_DENIED_ERROR, "SHOW CREATE TABLE",
ConnectContext.get().getQualifiedUser(),
ConnectContext.get().getRemoteIP(),
tbl.getDb() + ": " + tbl.getTbl());
}
}

public String getCatalog() {
return catalog;
}

public String getDbName() {
return dbName;
}

public String getViewName() {
return viewName;
}

@Override
public void run(ConnectContext ctx, StmtExecutor executor) throws Exception {
validate(ctx);
// If dbName is not provided, use the default database from the context
String effectiveDbName = (dbName == null || dbName.isEmpty()) ? ctx.getDatabase() : dbName;

// If catalog is not provided, use the default catalog (e.g., internal)
String effectiveCatalog = (catalog == null || catalog.isEmpty()) ? "internal" : catalog;

// Fetch the catalog, database, and view metadata
Env env = Env.getCurrentEnv();
DatabaseIf db = env.getCatalogMgr().getCatalogOrAnalysisException(effectiveCatalog)
.getDbOrMetaException(effectiveDbName);
TableIf view = db.getTableOrMetaException(viewName);

if (!(view instanceof View)) {
ErrorReport.reportAnalysisException(ErrorCode.ERR_WRONG_OBJECT, effectiveDbName, viewName,
"VIEW", "Use 'SHOW CREATE TABLE '" + viewName);
}

List<List<String>> rows = Lists.newArrayList();
// Lock the view to ensure consistent metadata access
view.readLock();
try {
List<String> createViewStmt = Lists.newArrayList();
env.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
ShowResultSet resultSet = new ShowResultSet(VIEW_META_DATA, rows);
executor.sendResultSet(resultSet);
}

@Override
public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
return visitor.visitShowCreateViewCommand(this, context);
}

@Override
public StmtType stmtType() {
return StmtType.SHOW;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
import org.apache.doris.nereids.trees.plans.commands.ShowConstraintsCommand;
import org.apache.doris.nereids.trees.plans.commands.ShowCreateMTMVCommand;
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.ShowProcedureStatusCommand;
import org.apache.doris.nereids.trees.plans.commands.UnsupportedCommand;
import org.apache.doris.nereids.trees.plans.commands.UpdateCommand;
Expand Down Expand Up @@ -221,4 +222,8 @@ default R visitSetUserPropertiesCommand(SetUserPropertiesCommand setUserProperti
default R visitSetDefaultStorageVault(SetDefaultStorageVaultCommand setDefaultStorageVaultCommand, C context) {
return visitCommand(setDefaultStorageVaultCommand, context);
}

default R visitShowCreateViewCommand(ShowCreateViewCommand showCreateViewCommand, C context) {
return visitCommand(showCreateViewCommand, context);
}
}
77 changes: 77 additions & 0 deletions regression-test/suites/query_p0/show/test_show_create_view.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// 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
def res = sql "show create view `${view_name}`"
assertTrue(res.size() != 0)

} 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
def res2 = sql "show create view `${view_name_2}`"
assertTrue(res2.size() != 0)

} 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}`")
}
}

0 comments on commit 260945b

Please sign in to comment.