This is an automated email from the ASF dual-hosted git repository.

starocean999 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/master by this push:
     new 97ad3e4e5e3 [Feat](Nereids) support show tables command (#48505)
97ad3e4e5e3 is described below

commit 97ad3e4e5e3f46664b8a1aca4463ffa380e7d3b0
Author: Jensen <czjour...@163.com>
AuthorDate: Tue Mar 4 09:30:40 2025 +0800

    [Feat](Nereids) support show tables command (#48505)
---
 .../antlr4/org/apache/doris/nereids/DorisParser.g4 |   2 +-
 .../doris/nereids/parser/LogicalPlanBuilder.java   |  31 +++
 .../apache/doris/nereids/trees/plans/PlanType.java |   1 +
 .../trees/plans/commands/ShowTableCommand.java     | 243 +++++++++++++++++++++
 .../trees/plans/visitor/CommandVisitor.java        |   5 +
 5 files changed, 281 insertions(+), 1 deletion(-)

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 99797ea7236..74b73f0c023 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
@@ -331,6 +331,7 @@ supportedShowStatement
     | SHOW TABLET STORAGE FORMAT VERBOSE?                                      
     #showTabletStorageFormat
     | SHOW QUERY PROFILE queryIdPath=STRING_LITERAL? limitClause?              
      #showQueryProfile
     | SHOW CONVERT_LSC ((FROM | IN) database=multipartIdentifier)?             
     #showConvertLsc
+    | SHOW FULL? TABLES ((FROM | IN) database=multipartIdentifier)? wildWhere? 
     #showTables
     ;
 
 supportedLoadStatement
@@ -374,7 +375,6 @@ unsupportedShowStatement
     | SHOW STORAGE (VAULT | VAULTS)                                            
     #showStorageVault
     | SHOW OPEN TABLES ((FROM | IN) database=multipartIdentifier)? wildWhere?  
     #showOpenTables
     | SHOW TABLE STATUS ((FROM | IN) database=multipartIdentifier)? wildWhere? 
     #showTableStatus
-    | SHOW FULL? TABLES ((FROM | IN) database=multipartIdentifier)? wildWhere? 
     #showTables
     | SHOW FULL? VIEWS ((FROM | IN) database=multipartIdentifier)? wildWhere?  
     #showViews
     | SHOW CREATE MATERIALIZED VIEW name=multipartIdentifier                   
     #showMaterializedView
     | SHOW CREATE statementScope? FUNCTION functionIdentifier
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 7b2cc6de3c3..a8db953f76d 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
@@ -640,6 +640,7 @@ import 
org.apache.doris.nereids.trees.plans.commands.ShowStagesCommand;
 import org.apache.doris.nereids.trees.plans.commands.ShowStatusCommand;
 import org.apache.doris.nereids.trees.plans.commands.ShowStorageEnginesCommand;
 import org.apache.doris.nereids.trees.plans.commands.ShowSyncJobCommand;
+import org.apache.doris.nereids.trees.plans.commands.ShowTableCommand;
 import org.apache.doris.nereids.trees.plans.commands.ShowTableCreationCommand;
 import org.apache.doris.nereids.trees.plans.commands.ShowTableIdCommand;
 import 
org.apache.doris.nereids.trees.plans.commands.ShowTabletStorageFormatCommand;
@@ -5888,6 +5889,36 @@ public class LogicalPlanBuilder extends 
DorisParserBaseVisitor<Object> {
         return new ShowQueuedAnalyzeJobsCommand(tableName, stateKey, 
stateValue);
     }
 
+    @Override
+    public LogicalPlan visitShowTables(DorisParser.ShowTablesContext ctx) {
+        String ctlName = null;
+        String dbName = null;
+        if (ctx.database != null) {
+            List<String> nameParts = visitMultipartIdentifier(ctx.database);
+            if (nameParts.size() == 1) {
+                dbName = nameParts.get(0);
+            } else if (nameParts.size() == 2) {
+                ctlName = nameParts.get(0);
+                dbName = nameParts.get(1);
+            } else {
+                throw new AnalysisException("nameParts in analyze database 
should be [ctl.]db");
+            }
+        }
+
+        boolean isVerbose = ctx.FULL() != null;
+
+        if (ctx.wildWhere() != null) {
+            if (ctx.wildWhere().LIKE() != null) {
+                return new ShowTableCommand(dbName, ctlName, isVerbose,
+                        
stripQuotes(ctx.wildWhere().STRING_LITERAL().getText()), null);
+            } else {
+                return new ShowTableCommand(dbName, ctlName, isVerbose, null,
+                        getOriginSql(ctx.wildWhere()));
+            }
+        }
+        return new ShowTableCommand(dbName, ctlName, isVerbose);
+    }
+
     @Override
     public LogicalPlan visitDescribeTable(DorisParser.DescribeTableContext 
ctx) {
         TableNameInfo tableName = new 
TableNameInfo(visitMultipartIdentifier(ctx.multipartIdentifier()));
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 7ed70ba2428..bf7b10a1063 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
@@ -259,6 +259,7 @@ public enum PlanType {
     SHOW_STORAGE_ENGINES_COMMAND,
     SHOW_SYNC_JOB_COMMAND,
     SHOW_TABLE_ID_COMMAND,
+    SHOW_TABLES,
     SHOW_TRASH_COMMAND,
     SHOW_TABLET_STORAGE_FORMAT_COMMAND,
     SHOW_TRIGGERS_COMMAND,
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowTableCommand.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowTableCommand.java
new file mode 100644
index 00000000000..ab9e970c8ed
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowTableCommand.java
@@ -0,0 +1,243 @@
+// 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.InfoSchemaDb;
+import org.apache.doris.catalog.OlapTable;
+import org.apache.doris.catalog.ScalarType;
+import org.apache.doris.catalog.TableIf;
+import org.apache.doris.cluster.ClusterNamespace;
+import org.apache.doris.common.AnalysisException;
+import org.apache.doris.common.CaseSensibility;
+import org.apache.doris.common.ErrorCode;
+import org.apache.doris.common.ErrorReport;
+import org.apache.doris.common.FeConstants;
+import org.apache.doris.common.Pair;
+import org.apache.doris.common.PatternMatcher;
+import org.apache.doris.common.PatternMatcherWrapper;
+import org.apache.doris.mysql.privilege.PrivPredicate;
+import org.apache.doris.nereids.glue.LogicalPlanAdapter;
+import org.apache.doris.nereids.parser.NereidsParser;
+import org.apache.doris.nereids.trees.plans.PlanType;
+import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
+import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
+import org.apache.doris.qe.ConnectContext;
+import org.apache.doris.qe.GlobalVariable;
+import org.apache.doris.qe.ShowResultSet;
+import org.apache.doris.qe.ShowResultSetMetaData;
+import org.apache.doris.qe.StmtExecutor;
+import org.apache.doris.statistics.ResultRow;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * ShowTableCommand
+ */
+public class ShowTableCommand extends ShowCommand {
+    private static final String NAME_COL_PREFIX = "Tables_in_";
+    private static final String TYPE_COL = "Table_type";
+    private static final String STORAGE_FORMAT_COL = "Storage_format";
+    private static final String INVERTED_INDEX_STORAGE_FORMAT_COL = 
"Inverted_index_storage_format";
+    private String db;
+    private String catalog;
+    private final boolean isVerbose;
+    private final String likePattern;
+    private final String whereClause;
+
+    public ShowTableCommand(String db, String catalog, boolean isVerbose) {
+        this(db, catalog, isVerbose, null, null);
+    }
+
+    /**
+     * ShowTableCommand
+     */
+    public ShowTableCommand(String db, String catalog, boolean isVerbose,
+            String likePattern, String whereClause) {
+        super(PlanType.SHOW_TABLES);
+        this.catalog = catalog;
+        this.db = db;
+        this.isVerbose = isVerbose;
+        this.likePattern = likePattern;
+        this.whereClause = whereClause;
+    }
+
+    /**
+     * validate
+     */
+    public void validate(ConnectContext ctx) throws AnalysisException {
+        if (Strings.isNullOrEmpty(db)) {
+            db = ctx.getDatabase();
+            if (Strings.isNullOrEmpty(db)) {
+                ErrorReport.reportAnalysisException(ErrorCode.ERR_NO_DB_ERROR);
+            }
+        }
+        if (Strings.isNullOrEmpty(catalog)) {
+            catalog = ctx.getDefaultCatalog();
+            if (Strings.isNullOrEmpty(catalog)) {
+                
ErrorReport.reportAnalysisException(ErrorCode.ERR_WRONG_NAME_FOR_CATALOG);
+            }
+        }
+
+        // we do not check db privs here. because user may not have any db 
privs,
+        // but if it has privs of tbls inside this db,it should be allowed to 
see this db.
+    }
+
+    /**
+     * sql to logical plan
+     * @param sql sql
+     */
+    private LogicalPlan toLogicalPlan(String sql) {
+        return new NereidsParser().parseSingle(sql);
+    }
+
+    /**
+     * Construct a basic SQL without query conditions
+     * @param columns (original name, alias)
+     * @param tableName ctl.db.tbl
+     */
+    private String toBaseSql(List<Pair<String, String>> columns, String 
tableName) throws AnalysisException {
+        if (columns == null || columns.isEmpty()) {
+            throw new AnalysisException("columns cannot be empty");
+        }
+        if (tableName == null || tableName.isEmpty()) {
+            throw new AnalysisException("tableName cannot be empty");
+        }
+
+        StringBuilder sb = new StringBuilder("SELECT ");
+        columns.forEach(column -> {
+            sb.append(column.first);
+            if (column.second != null && !column.second.isEmpty()) {
+                sb.append(" AS ").append(column.second);
+            }
+            sb.append(", ");
+        });
+        sb.setLength(sb.length() - 2);
+        sb.append(" FROM ").append(tableName);
+        return sb.toString();
+    }
+
+    public boolean isShowTablesCaseSensitive() {
+        if (GlobalVariable.lowerCaseTableNames == 0) {
+            return CaseSensibility.TABLE.getCaseSensibility();
+        }
+        return false;
+    }
+
+    private ShowResultSet executeWhere(ConnectContext ctx, StmtExecutor 
executor)
+            throws AnalysisException {
+        List<Pair<String, String>> columns = new ArrayList<>();
+        columns.add(Pair.of("`TABLE_NAME`",
+                NAME_COL_PREFIX + ClusterNamespace.getNameFromFullName(db)));
+        if (isVerbose) {
+            columns.add(Pair.of("`TABLE_TYPE`", TYPE_COL));
+        }
+
+        String fullTblName = String.format("`%s`.`%s`.`%s`",
+                catalog,
+                InfoSchemaDb.DATABASE_NAME,
+                "tables");
+
+        // We need to use TABLE_SCHEMA as a condition to query When querying 
external catalogs.
+        // This also applies to the internal catalog.
+        LogicalPlan plan = toLogicalPlan(toBaseSql(columns, fullTblName) + " " 
+ whereClause
+                + " and `TABLE_SCHEMA` = '" + db + "'");
+        LogicalPlanAdapter adapter = new LogicalPlanAdapter(plan, 
ctx.getStatementContext());
+        executor.setParsedStmt(adapter);
+        List<ResultRow> resultRows = executor.executeInternalQuery();
+        List<List<String>> rows = resultRows.stream()
+                .map(ResultRow::getValues).collect(Collectors.toList());
+        return new ShowResultSet(getMetaData(), rows);
+    }
+
+    @Override
+    public ShowResultSet doRun(ConnectContext ctx, StmtExecutor executor) 
throws Exception {
+        validate(ctx);
+        if (whereClause != null) {
+            return executeWhere(ctx, executor);
+        }
+        List<List<String>> rows = Lists.newArrayList();
+        DatabaseIf<TableIf> dbIf = ctx.getEnv().getCatalogMgr()
+                .getCatalogOrAnalysisException(catalog)
+                .getDbOrAnalysisException(db);
+        PatternMatcher matcher = null;
+        if (likePattern != null) {
+            matcher = PatternMatcherWrapper.createMysqlPattern(likePattern, 
isShowTablesCaseSensitive());
+        }
+        for (TableIf tbl : dbIf.getTables()) {
+            if 
(tbl.getName().startsWith(FeConstants.TEMP_MATERIZLIZE_DVIEW_PREFIX)) {
+                continue;
+            }
+            if (matcher != null && !matcher.match(tbl.getName())) {
+                continue;
+            }
+            // check tbl privs
+            if (!Env.getCurrentEnv().getAccessManager()
+                    .checkTblPriv(ConnectContext.get(), catalog, 
dbIf.getFullName(), tbl.getName(),
+                            PrivPredicate.SHOW)) {
+                continue;
+            }
+            if (isVerbose) {
+                String storageFormat = "NONE";
+                String invertedIndexFileStorageFormat = "NONE";
+                if (tbl instanceof OlapTable) {
+                    storageFormat = ((OlapTable) 
tbl).getStorageFormat().toString();
+                    invertedIndexFileStorageFormat = ((OlapTable) 
tbl).getInvertedIndexFileStorageFormat().toString();
+                }
+                rows.add(Lists.newArrayList(tbl.getName(), tbl.getMysqlType(), 
storageFormat,
+                        invertedIndexFileStorageFormat));
+            } else {
+                rows.add(Lists.newArrayList(tbl.getName()));
+            }
+        }
+        // sort by table name
+        rows.sort(Comparator.comparing(x -> x.get(0)));
+        return new ShowResultSet(getMetaData(), rows);
+    }
+
+    /**
+     * getMetaData
+     */
+    public ShowResultSetMetaData getMetaData() {
+        ShowResultSetMetaData.Builder builder = 
ShowResultSetMetaData.builder();
+        builder.addColumn(
+                new Column(NAME_COL_PREFIX + 
ClusterNamespace.getNameFromFullName(db), ScalarType.createVarchar(20)));
+        if (isVerbose) {
+            builder.addColumn(new Column(TYPE_COL, 
ScalarType.createVarchar(20)));
+            // TODO: using where can only show two columns, maybe this is a 
bug?
+            if (whereClause == null) {
+                builder.addColumn(new Column(STORAGE_FORMAT_COL, 
ScalarType.createVarchar(20)));
+                builder.addColumn(new 
Column(INVERTED_INDEX_STORAGE_FORMAT_COL, ScalarType.createVarchar(20)));
+            }
+        }
+        return builder.build();
+    }
+
+    @Override
+    public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
+        return visitor.visitShowTableCommand(this, context);
+    }
+}
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 b7b63debc55..c990a7c496c 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
@@ -143,6 +143,7 @@ import 
org.apache.doris.nereids.trees.plans.commands.ShowStagesCommand;
 import org.apache.doris.nereids.trees.plans.commands.ShowStatusCommand;
 import org.apache.doris.nereids.trees.plans.commands.ShowStorageEnginesCommand;
 import org.apache.doris.nereids.trees.plans.commands.ShowSyncJobCommand;
+import org.apache.doris.nereids.trees.plans.commands.ShowTableCommand;
 import org.apache.doris.nereids.trees.plans.commands.ShowTableCreationCommand;
 import org.apache.doris.nereids.trees.plans.commands.ShowTableIdCommand;
 import 
org.apache.doris.nereids.trees.plans.commands.ShowTabletStorageFormatCommand;
@@ -808,4 +809,8 @@ public interface CommandVisitor<R, C> {
     default R visitDescribeCommand(DescribeCommand describeCommand, C context) 
{
         return visitCommand(describeCommand, context);
     }
+
+    default R visitShowTableCommand(ShowTableCommand showTableCommand, C 
context) {
+        return visitCommand(showTableCommand, context);
+    }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org
For additional commands, e-mail: commits-h...@doris.apache.org

Reply via email to