diff --git a/docs/en/sql-reference/sql-statements/Administration/SHOW VIEW.md b/docs/en/sql-reference/sql-statements/Administration/SHOW VIEW.md new file mode 100644 index 00000000000000..976dfc29f4008e --- /dev/null +++ b/docs/en/sql-reference/sql-statements/Administration/SHOW VIEW.md @@ -0,0 +1,46 @@ +--- +{ + "title": "SHOW TABLE STATUS", + "language": "en" +} +--- + + + +# SHOW VIEW + +## description + + This statement is used to show all views based on a given table + + Syntax: + + SHOW VIEW { FROM | IN } table [ FROM db ] + +## Example + + 1. Show all views based on the table testTbl + + show view from testTbl; + + +## Keyword + + SHOW,VIEW \ No newline at end of file diff --git a/docs/zh-CN/sql-reference/sql-statements/Administration/SHOW VIEW.md b/docs/zh-CN/sql-reference/sql-statements/Administration/SHOW VIEW.md new file mode 100644 index 00000000000000..907104f2ec61b6 --- /dev/null +++ b/docs/zh-CN/sql-reference/sql-statements/Administration/SHOW VIEW.md @@ -0,0 +1,39 @@ +--- +{ + "title": "SHOW DELETE", + "language": "zh-CN" +} +--- + + + +# SHOW VIEW +## description + 该语句用于展示基于给定表建立的所有视图 + 语法: + SHOW VIEW { FROM | IN } table [ FROM db ] + +## example + 1. 展示基于表 testTbl 建立的所有视图 view + SHOW VIEW FROM testTbl; + +## keyword + SHOW,VIEW + diff --git a/fe/fe-core/src/main/cup/sql_parser.cup b/fe/fe-core/src/main/cup/sql_parser.cup index ff7691332c0d65..8e889d2b1ade4d 100644 --- a/fe/fe-core/src/main/cup/sql_parser.cup +++ b/fe/fe-core/src/main/cup/sql_parser.cup @@ -2555,6 +2555,10 @@ show_param ::= {: RESULT = new ShowIndexStmt(dbName, dbTblName); :} + | KW_VIEW from_or_in table_name:dbTblName opt_db:dbName + {: + RESULT = new ShowViewStmt(dbName, dbTblName); + :} | KW_TRANSACTION opt_db:dbName opt_wild_where {: RESULT = new ShowTransactionStmt(dbName, parser.where); diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowViewStmt.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowViewStmt.java new file mode 100644 index 00000000000000..713b2f17cafbcb --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowViewStmt.java @@ -0,0 +1,148 @@ +// 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.analysis; + +import org.apache.doris.catalog.Catalog; +import org.apache.doris.catalog.Column; +import org.apache.doris.catalog.Database; +import org.apache.doris.catalog.OlapTable; +import org.apache.doris.catalog.ScalarType; +import org.apache.doris.catalog.Table; +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.common.UserException; +import org.apache.doris.mysql.privilege.PrivPredicate; +import org.apache.doris.qe.ConnectContext; +import org.apache.doris.qe.ShowResultSetMetaData; +import com.google.common.base.Strings; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +// Show view statement, used to show view information of one table. +// +// Syntax: +// SHOW VIEW { FROM | IN } table [ FROM db ] +public class ShowViewStmt extends ShowStmt { + private static final ShowResultSetMetaData META_DATA = + ShowResultSetMetaData.builder() + .addColumn(new Column("View", ScalarType.createVarchar(30))) + .addColumn(new Column("Create View", ScalarType.createVarchar(65535))) + .build(); + + private String db; + private TableName tbl; + + private List matchViews = Lists.newArrayList();; + + public ShowViewStmt(String db, TableName tbl) { + this.db = db; + this.tbl = tbl; + } + + public String getDb() { + return tbl.getDb(); + } + + public String getTbl() { + return tbl.getTbl(); + } + + public List getMatchViews() { + return matchViews; + } + + @Override + public void analyze(Analyzer analyzer) throws AnalysisException, UserException { + super.analyze(analyzer); + if (tbl == null) { + ErrorReport.reportAnalysisException(ErrorCode.ERR_NO_TABLES_USED); + } + if (!Strings.isNullOrEmpty(db)) { + // if user specify the `from db`, overwrite the db in `tbl` with this db. + // for example: + // show view from db1.tbl1 from db2; + // will be rewrote to: + // show view from db2.tbl1; + // this act same as in MySQL + tbl.setDb(db); + } + tbl.analyze(analyzer); + + String dbName = tbl.getDb(); + if (!Catalog.getCurrentCatalog().getAuth().checkTblPriv(ConnectContext.get(), dbName, getTbl(), PrivPredicate.SHOW)) { + ErrorReport.reportAnalysisException(ErrorCode.ERR_TABLEACCESS_DENIED_ERROR, "SHOW VIEW", + ConnectContext.get().getQualifiedUser(), + ConnectContext.get().getRemoteIP(), + getTbl()); + } + + Database database = Catalog.getCurrentCatalog().getDb(dbName); + if (database == null) { + ErrorReport.reportAnalysisException(ErrorCode.ERR_BAD_DB_ERROR, dbName); + } + + Table showTable = database.getTable(tbl.getTbl()); + if (showTable == null) { + ErrorReport.reportAnalysisException(ErrorCode.ERR_BAD_TABLE_ERROR, getTbl()); + } + + if (!(showTable instanceof OlapTable)) { + ErrorReport.reportAnalysisException(ErrorCode.ERR_NOT_OLAP_TABLE, getTbl()); + } + + for (Table table : database.getViews()) { + View view = (View) table; + Map tableMap = Maps.newHashMap(); + // get tables from view sql + getTables(analyzer, view, tableMap); + if (tableMap.containsKey(showTable.getId())) { + matchViews.add(view); + } + } + } + + private void getTables(Analyzer analyzer, View view, Map tableMap) throws UserException { + Set parentViewNameSet = Sets.newHashSet(); + QueryStmt queryStmt = view.getQueryStmt(); + queryStmt.getTables(analyzer, tableMap, parentViewNameSet); + } + + @Override + public String toSql() { + StringBuilder sb = new StringBuilder("SHOW VIEW FROM "); + sb.append(tbl.toSql()); + return sb.toString(); + } + + @Override + public String toString() { + return toSql(); + } + + @Override + public ShowResultSetMetaData getMetaData() { + return META_DATA; + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/ShowExecutor.java b/fe/fe-core/src/main/java/org/apache/doris/qe/ShowExecutor.java index ec42fc4e4229b7..0ac3ec0d1ce5e4 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/qe/ShowExecutor.java +++ b/fe/fe-core/src/main/java/org/apache/doris/qe/ShowExecutor.java @@ -69,6 +69,7 @@ import org.apache.doris.analysis.ShowTransactionStmt; import org.apache.doris.analysis.ShowUserPropertyStmt; import org.apache.doris.analysis.ShowVariablesStmt; +import org.apache.doris.analysis.ShowViewStmt; import org.apache.doris.backup.AbstractJob; import org.apache.doris.backup.BackupJob; import org.apache.doris.backup.Repository; @@ -211,7 +212,7 @@ public ShowResultSet execute() throws AnalysisException { } else if (stmt instanceof ShowLoadStmt) { handleShowLoad(); } else if (stmt instanceof ShowStreamLoadStmt) { - handleShowStreamLoad(); + handleShowStreamLoad(); } else if (stmt instanceof ShowLoadWarningsStmt) { handleShowLoadWarnings(); } else if (stmt instanceof ShowRoutineLoadStmt) { @@ -270,6 +271,8 @@ public ShowResultSet execute() throws AnalysisException { handleShowDynamicPartition(); } else if (stmt instanceof ShowIndexStmt) { handleShowIndex(); + } else if (stmt instanceof ShowViewStmt) { + handleShowView(); } else if (stmt instanceof ShowTransactionStmt) { handleShowTransaction(); } else if (stmt instanceof ShowPluginsStmt) { @@ -721,6 +724,26 @@ private void handleShowIndex() throws AnalysisException { resultSet = new ShowResultSet(showStmt.getMetaData(), rows); } + //Show view statement. + private void handleShowView() { + ShowViewStmt showStmt = (ShowViewStmt) stmt; + List> rows = Lists.newArrayList(); + List matchViews = showStmt.getMatchViews(); + for (View view : matchViews) { + view.readLock(); + try { + List createViewStmt = Lists.newArrayList(); + Catalog.getDdlStmt(view, createViewStmt, null, null, false, true /* hide password */); + if (!createViewStmt.isEmpty()) { + rows.add(Lists.newArrayList(view.getName(), createViewStmt.get(0))); + } + } finally { + view.readUnlock(); + } + } + resultSet = new ShowResultSet(showStmt.getMetaData(), rows); + } + // Handle help statement. private void handleHelp() { HelpStmt helpStmt = (HelpStmt) stmt; diff --git a/fe/fe-core/src/test/java/org/apache/doris/analysis/ShowViewStmtTest.java b/fe/fe-core/src/test/java/org/apache/doris/analysis/ShowViewStmtTest.java new file mode 100644 index 00000000000000..d26019576ca35b --- /dev/null +++ b/fe/fe-core/src/test/java/org/apache/doris/analysis/ShowViewStmtTest.java @@ -0,0 +1,58 @@ +package org.apache.doris.analysis; + +import org.apache.doris.catalog.Catalog; +import org.apache.doris.common.UserException; +import org.apache.doris.mysql.privilege.MockedAuth; +import org.apache.doris.mysql.privilege.PaloAuth; +import org.apache.doris.qe.ConnectContext; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import mockit.Expectations; +import mockit.Mocked; + +public class ShowViewStmtTest { + private Analyzer analyzer; + private Catalog catalog; + + @Mocked + private PaloAuth auth; + @Mocked + private ConnectContext ctx; + + @Before + public void setUp() { + analyzer = AccessTestUtil.fetchAdminAnalyzer(true); + catalog = AccessTestUtil.fetchAdminCatalog(); + MockedAuth.mockedAuth(auth); + MockedAuth.mockedConnectContext(ctx, "root", "192.168.1.1"); + } + + @Test + public void testNormal() throws UserException { + new Expectations(catalog) { + { + Catalog.getCurrentCatalog(); + result = catalog; + } + }; + + ShowViewStmt stmt = new ShowViewStmt("", new TableName("testDb", "testTbl")); + stmt.analyze(analyzer); + Assert.assertEquals("SHOW VIEW FROM `testCluster:testDb`.`testTbl`", stmt.toString()); + Assert.assertEquals("testCluster:testDb", stmt.getDb()); + Assert.assertEquals("testTbl", stmt.getTbl()); + Assert.assertEquals(2, stmt.getMetaData().getColumnCount()); + Assert.assertEquals("View", stmt.getMetaData().getColumn(0).getName()); + Assert.assertEquals("Create View", stmt.getMetaData().getColumn(1).getName()); + } + + @Test(expected = UserException.class) + public void testNoDb() throws UserException { + ShowViewStmt stmt = new ShowViewStmt("", new TableName("", "testTbl")); + stmt.analyze(analyzer); + Assert.fail(); + } +} diff --git a/fe/fe-core/src/test/java/org/apache/doris/qe/ShowExecutorTest.java b/fe/fe-core/src/test/java/org/apache/doris/qe/ShowExecutorTest.java index 33f20ff7baee4f..a958bb53188094 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/qe/ShowExecutorTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/qe/ShowExecutorTest.java @@ -31,6 +31,7 @@ import org.apache.doris.analysis.ShowProcedureStmt; import org.apache.doris.analysis.ShowTableStmt; import org.apache.doris.analysis.ShowVariablesStmt; +import org.apache.doris.analysis.ShowViewStmt; import org.apache.doris.analysis.TableName; import org.apache.doris.catalog.Catalog; import org.apache.doris.catalog.Column; @@ -52,7 +53,6 @@ import org.apache.doris.mysql.privilege.PaloAuth; import org.apache.doris.system.SystemInfoService; import org.apache.doris.thrift.TStorageType; - import com.google.common.collect.Lists; import org.junit.Assert; @@ -65,7 +65,6 @@ import java.io.IOException; import java.net.URL; import java.util.List; - import mockit.Expectations; import mockit.Mock; import mockit.MockUp; @@ -464,6 +463,18 @@ public void testShowColumn() throws AnalysisException { Assert.assertFalse(resultSet.next()); } + @Test + public void testShowView() throws UserException { + ctx.setCatalog(catalog); + ctx.setQualifiedUser("testCluster:testUser"); + ShowViewStmt stmt = new ShowViewStmt("", new TableName("testDb", "testTbl")); + stmt.analyze(AccessTestUtil.fetchAdminAnalyzer(true)); + ShowExecutor executor = new ShowExecutor(ctx, stmt); + ShowResultSet resultSet = executor.execute(); + + Assert.assertFalse(resultSet.next()); + } + @Test public void testShowColumnFromUnknownTable() throws AnalysisException { ShowColumnStmt stmt = new ShowColumnStmt(new TableName("testCluster:emptyDb", "testTable"), null, null, false);