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 839c277878b [Enhancement] (nereids)implement showColumnsCommand in 
nereids (#45832)
839c277878b is described below

commit 839c277878b6e4af90a4650bc4402da1ac3b619e
Author: Sridhar R Manikarnike <[email protected]>
AuthorDate: Fri May 23 07:11:23 2025 +0530

    [Enhancement] (nereids)implement showColumnsCommand in nereids (#45832)
    
    Issue Number: close #42747
---
 .../antlr4/org/apache/doris/nereids/DorisParser.g4 |   4 +-
 .../doris/nereids/parser/LogicalPlanBuilder.java   |  20 ++
 .../apache/doris/nereids/trees/plans/PlanType.java |   1 +
 .../trees/plans/commands/ShowColumnsCommand.java   | 254 +++++++++++++++++++++
 .../trees/plans/visitor/CommandVisitor.java        |   5 +
 .../nereids_p0/show/test_show_columns_command.out  | Bin 0 -> 340 bytes
 .../show/test_show_columns_command.groovy          |  57 +++++
 7 files changed, 339 insertions(+), 2 deletions(-)

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 4bfce104713..19fb9a7d1cc 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
@@ -372,6 +372,8 @@ supportedShowStatement
     | SHOW OPEN TABLES ((FROM | IN) database=multipartIdentifier)? wildWhere?  
     #showOpenTables
     | SHOW FRONTENDS name=identifier?                                          
     #showFrontends
     | SHOW DATABASE databaseId=INTEGER_VALUE                                   
     #showDatabaseId
+    | SHOW FULL? (COLUMNS | FIELDS) (FROM | IN) tableName=multipartIdentifier
+        ((FROM | IN) database=multipartIdentifier)? wildWhere?          
#showColumns    
     | SHOW TABLE tableId=INTEGER_VALUE                                         
     #showTableId
     | SHOW TRASH (ON backend=STRING_LITERAL)?                                  
     #showTrash
     | SHOW (CLUSTERS | (COMPUTE GROUPS))                                       
     #showClusters    
@@ -459,8 +461,6 @@ unsupportedShowStatement
     | SHOW CREATE statementScope? FUNCTION functionIdentifier
         LEFT_PAREN functionArguments? RIGHT_PAREN
         ((FROM | IN) database=multipartIdentifier)?                            
     #showCreateFunction
-    | SHOW FULL? (COLUMNS | FIELDS) (FROM | IN) tableName=multipartIdentifier
-        ((FROM | IN) database=multipartIdentifier)? wildWhere?                 
     #showColumns
     | SHOW LOAD WARNINGS ((((FROM | IN) database=multipartIdentifier)?
         wildWhere? limitClause?) | (ON url=STRING_LITERAL))                    
     #showLoadWarings
     | SHOW EXPORT ((FROM | IN) database=multipartIdentifier)? wildWhere?
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 f3a70391cf2..1f8a60005cc 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
@@ -329,6 +329,7 @@ import 
org.apache.doris.nereids.DorisParser.ShowCharsetContext;
 import org.apache.doris.nereids.DorisParser.ShowClustersContext;
 import org.apache.doris.nereids.DorisParser.ShowCollationContext;
 import org.apache.doris.nereids.DorisParser.ShowColumnHistogramStatsContext;
+import org.apache.doris.nereids.DorisParser.ShowColumnsContext;
 import org.apache.doris.nereids.DorisParser.ShowConfigContext;
 import org.apache.doris.nereids.DorisParser.ShowConstraintContext;
 import org.apache.doris.nereids.DorisParser.ShowConvertLscContext;
@@ -683,6 +684,7 @@ import 
org.apache.doris.nereids.trees.plans.commands.ShowCharsetCommand;
 import org.apache.doris.nereids.trees.plans.commands.ShowClustersCommand;
 import org.apache.doris.nereids.trees.plans.commands.ShowCollationCommand;
 import 
org.apache.doris.nereids.trees.plans.commands.ShowColumnHistogramStatsCommand;
+import org.apache.doris.nereids.trees.plans.commands.ShowColumnsCommand;
 import org.apache.doris.nereids.trees.plans.commands.ShowConfigCommand;
 import org.apache.doris.nereids.trees.plans.commands.ShowConstraintsCommand;
 import org.apache.doris.nereids.trees.plans.commands.ShowConvertLSCCommand;
@@ -5971,6 +5973,24 @@ public class LogicalPlanBuilder extends 
DorisParserBaseVisitor<Object> {
         return new ShowSyncJobCommand(databaseName);
     }
 
+    @Override
+    public LogicalPlan visitShowColumns(ShowColumnsContext ctx) {
+        boolean isFull = ctx.FULL() != null;
+        List<String> nameParts = visitMultipartIdentifier(ctx.tableName);
+        String databaseName = ctx.database != null ? ctx.database.getText() : 
null;
+        String likePattern = null;
+        Expression expr = null;
+        if (ctx.wildWhere() != null) {
+            if (ctx.wildWhere().LIKE() != null) {
+                likePattern = 
stripQuotes(ctx.wildWhere().STRING_LITERAL().getText());
+            } else {
+                expr = (Expression) ctx.wildWhere().expression().accept(this);
+            }
+        }
+
+        return new ShowColumnsCommand(isFull, new TableNameInfo(nameParts), 
databaseName, likePattern, expr);
+    }
+
     @Override
     public LogicalPlan visitDropFile(DropFileContext ctx) {
         String dbName = 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 28cb18b20a8..dc0acc1fdcc 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
@@ -244,6 +244,7 @@ public enum PlanType {
     SHOW_CLUSTERS_COMMAND,
     SHOW_COLLATION_COMMAND,
     SHOW_COLUMN_HISTOGRAM,
+    SHOW_COLUMNS_COMMAND,
     SHOW_CONFIG_COMMAND,
     SHOW_CREATE_CATALOG_COMMAND,
     SHOW_CREATE_DATABASE_COMMAND,
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowColumnsCommand.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowColumnsCommand.java
new file mode 100644
index 00000000000..cdc7804afce
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowColumnsCommand.java
@@ -0,0 +1,254 @@
+// 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.ScalarType;
+import org.apache.doris.catalog.TableIf;
+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.PatternMatcher;
+import org.apache.doris.common.PatternMatcherWrapper;
+import org.apache.doris.mysql.privilege.PrivPredicate;
+import org.apache.doris.nereids.analyzer.UnboundSlot;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import 
org.apache.doris.nereids.trees.expressions.visitor.DefaultExpressionRewriter;
+import org.apache.doris.nereids.trees.plans.PlanType;
+import org.apache.doris.nereids.trees.plans.commands.info.AliasInfo;
+import org.apache.doris.nereids.trees.plans.commands.info.TableNameInfo;
+import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
+import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
+import org.apache.doris.nereids.util.Utils;
+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.base.Strings;
+import com.google.common.collect.Lists;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Represents the SHOW COLUMNS command.
+ */
+public class ShowColumnsCommand extends ShowCommand {
+    private static final ShowResultSetMetaData META_DATA = 
ShowResultSetMetaData.builder()
+            .addColumn(new Column("Field", ScalarType.createVarchar(20)))
+            .addColumn(new Column("Type", ScalarType.createVarchar(20)))
+            .addColumn(new Column("Null", ScalarType.createVarchar(20)))
+            .addColumn(new Column("Key", ScalarType.createVarchar(20)))
+            .addColumn(new Column("Default", ScalarType.createVarchar(20)))
+            .addColumn(new Column("Extra", 
ScalarType.createVarchar(20))).build();
+
+    private static final ShowResultSetMetaData META_DATA_VERBOSE =
+            ShowResultSetMetaData.builder()
+                    .addColumn(new Column("Field", 
ScalarType.createVarchar(20)))
+                    .addColumn(new Column("Type", 
ScalarType.createVarchar(20)))
+                    .addColumn(new Column("Collation", 
ScalarType.createVarchar(20)))
+                    .addColumn(new Column("Null", 
ScalarType.createVarchar(20)))
+                    .addColumn(new Column("Key", ScalarType.createVarchar(20)))
+                    .addColumn(new Column("Default", 
ScalarType.createVarchar(20)))
+                    .addColumn(new Column("Extra", 
ScalarType.createVarchar(20)))
+                    .addColumn(new Column("Privileges", 
ScalarType.createVarchar(20)))
+                    .addColumn(new Column("Comment", 
ScalarType.createVarchar(20)))
+                    .build();
+
+    private final boolean isFull;
+    private TableNameInfo tableNameInfo;
+    private final String databaseName;
+    private final String likePattern;
+    private final Expression whereClause;
+
+    /**
+     * SHOW COLUMNS command Constructor.
+     */
+    public ShowColumnsCommand(boolean isFull, TableNameInfo tableNameInfo, 
String databaseName, String likePattern,
+                Expression whereClause) {
+        super(PlanType.SHOW_COLUMNS_COMMAND);
+        this.isFull = isFull;
+        this.tableNameInfo = tableNameInfo;
+        this.databaseName = databaseName;
+        this.likePattern = likePattern;
+        this.whereClause = whereClause;
+    }
+
+    /**
+     * SHOW COLUMNS validate.
+     */
+    public void validate(ConnectContext ctx) throws AnalysisException {
+        if (!Strings.isNullOrEmpty(databaseName)) {
+            tableNameInfo.setDb(databaseName);
+        }
+        tableNameInfo.analyze(ctx);
+        if (!Env.getCurrentEnv().getAccessManager()
+                .checkTblPriv(ConnectContext.get(), tableNameInfo.getCtl(), 
tableNameInfo.getDb(),
+                        tableNameInfo.getTbl(), PrivPredicate.SHOW)) {
+            
ErrorReport.reportAnalysisException(ErrorCode.ERR_TABLE_ACCESS_DENIED_ERROR,
+                    PrivPredicate.SHOW.getPrivs().toString(), tableNameInfo);
+        }
+    }
+
+    /**
+     * replaceColumnNameVisitor
+     * replace column name to real column name
+     */
+    private static class ReplaceColumnNameVisitor extends 
DefaultExpressionRewriter<Void> {
+        @Override
+        public Expression visitUnboundSlot(UnboundSlot slot, Void context) {
+            String name = slot.getName().toLowerCase(Locale.ROOT);
+            switch (name) {
+                case "field":
+                    return UnboundSlot.quoted("COLUMN_NAME");
+                case "type":
+                    return UnboundSlot.quoted("COLUMN_TYPE");
+                case "null":
+                    return UnboundSlot.quoted("IS_NULLABLE");
+                case "default":
+                    return UnboundSlot.quoted("COLUMN_DEFAULT");
+                case "comment":
+                    return UnboundSlot.quoted("COLUMN_COMMENT");
+                default:
+                    return slot;
+            }
+        }
+    }
+
+    @Override
+    public ShowResultSet doRun(ConnectContext ctx, StmtExecutor executor) 
throws Exception {
+        validate(ctx);
+        if (whereClause != null) {
+            Expression rewritten = whereClause.accept(new 
ReplaceColumnNameVisitor(), null);
+            String whereCondition = " WHERE TABLE_NAME = '" + 
tableNameInfo.getTbl() + "' AND " + rewritten.toSql();
+            TableNameInfo info = new TableNameInfo(tableNameInfo.getCtl(), 
"information_schema", "columns");
+
+            List<AliasInfo> selectList = new ArrayList<>();
+            if (isFull) {
+                selectList.add(AliasInfo.of("COLUMN_NAME", "Field"));
+                selectList.add(AliasInfo.of("COLUMN_TYPE", "Type"));
+                selectList.add(AliasInfo.of("COLLATION_NAME", "Collation"));
+                selectList.add(AliasInfo.of("IS_NULLABLE", "Null"));
+                selectList.add(AliasInfo.of("COLUMN_KEY", "Key"));
+                selectList.add(AliasInfo.of("COLUMN_DEFAULT", "Default"));
+                selectList.add(AliasInfo.of("EXTRA", "Extra"));
+                selectList.add(AliasInfo.of("PRIVILEGES", "Privileges")); // 
optional, can be set to ''
+                selectList.add(AliasInfo.of("COLUMN_COMMENT", "Comment"));
+            } else {
+                selectList.add(AliasInfo.of("COLUMN_NAME", "Field"));
+                selectList.add(AliasInfo.of("COLUMN_TYPE", "Type"));
+                selectList.add(AliasInfo.of("IS_NULLABLE", "Null"));
+                selectList.add(AliasInfo.of("COLUMN_KEY", "Key"));
+                selectList.add(AliasInfo.of("COLUMN_DEFAULT", "Default"));
+                selectList.add(AliasInfo.of("EXTRA", "Extra"));
+            }
+
+            LogicalPlan plan = Utils.buildLogicalPlan(selectList, info, 
whereCondition);
+            List<List<String>> rows = Utils.executePlan(ctx, executor, plan);
+            for (List<String> row : rows) {
+                String rawType = row.get(1);
+                row.set(1, normalizeSqlColumnType(rawType));
+            }
+
+            return new ShowResultSet(getMetaData(), rows);
+        }
+        List<List<String>> rows = Lists.newArrayList();
+        String ctl = tableNameInfo.getCtl();
+        DatabaseIf db = 
Env.getCurrentEnv().getCatalogMgr().getCatalogOrAnalysisException(ctl)
+                .getDbOrAnalysisException(tableNameInfo.getDb());
+        TableIf table = db.getTableOrAnalysisException(tableNameInfo.getTbl());
+        PatternMatcher matcher = null;
+        if (likePattern != null) {
+            matcher = PatternMatcherWrapper.createMysqlPattern(likePattern,
+                    CaseSensibility.COLUMN.getCaseSensibility());
+        }
+        table.readLock();
+        try {
+            List<Column> columns = table.getBaseSchema();
+            for (Column col : columns) {
+                if (matcher != null && !matcher.match(col.getName())) {
+                    continue;
+                }
+                final String columnName = col.getName();
+                final String columnType = 
col.getOriginType().toString().toLowerCase(Locale.ROOT);
+                final String isAllowNull = col.isAllowNull() ? "YES" : "NO";
+                final String isKey = col.isKey() ? "YES" : "NO";
+                final String defaultValue = col.getDefaultValue();
+                final String aggType = col.getAggregationType() == null ? "" : 
col.getAggregationType().toSql();
+                if (isFull) {
+                    // Field Type Collation Null Key Default Extra
+                    // Privileges Comment
+                    rows.add(Lists.newArrayList(columnName,
+                            columnType,
+                            "",
+                            isAllowNull,
+                            isKey,
+                            defaultValue,
+                            aggType,
+                            "",
+                            col.getComment()));
+                } else {
+                    // Field Type Null Key Default Extra
+                    rows.add(Lists.newArrayList(columnName,
+                            columnType,
+                            isAllowNull,
+                            isKey,
+                            defaultValue,
+                            aggType));
+                }
+            }
+        } finally {
+            table.readUnlock();
+        }
+
+        return new ShowResultSet(getMetaData(), rows);
+    }
+
+    @Override
+    public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
+        return visitor.visitShowColumnsCommand(this, context);
+    }
+
+    @Override
+    public ShowResultSetMetaData getMetaData() {
+        if (isFull) {
+            return META_DATA_VERBOSE;
+        } else {
+            return META_DATA;
+        }
+    }
+
+    private static String normalizeSqlColumnType(String type) {
+        if (type == null) {
+            return null;
+        }
+
+        type = type.toLowerCase().trim();
+
+        if (type.matches("^[a-z]+\\s*\\(.*\\)$")) {
+            int parenIndex = type.indexOf('(');
+            return type.substring(0, parenIndex).trim();
+        }
+        return type;
+    }
+}
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 95be19629da..7034628aef7 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
@@ -145,6 +145,7 @@ import 
org.apache.doris.nereids.trees.plans.commands.ShowCharsetCommand;
 import org.apache.doris.nereids.trees.plans.commands.ShowClustersCommand;
 import org.apache.doris.nereids.trees.plans.commands.ShowCollationCommand;
 import 
org.apache.doris.nereids.trees.plans.commands.ShowColumnHistogramStatsCommand;
+import org.apache.doris.nereids.trees.plans.commands.ShowColumnsCommand;
 import org.apache.doris.nereids.trees.plans.commands.ShowConfigCommand;
 import org.apache.doris.nereids.trees.plans.commands.ShowConstraintsCommand;
 import org.apache.doris.nereids.trees.plans.commands.ShowConvertLSCCommand;
@@ -481,6 +482,10 @@ public interface CommandVisitor<R, C> {
         return visitCommand(alterViewCommand, context);
     }
 
+    default R visitShowColumnsCommand(ShowColumnsCommand showColumnsCommand, C 
context) {
+        return visitCommand(showColumnsCommand, context);
+    }
+
     default R visitDropCatalogCommand(DropCatalogCommand dropCatalogCommand, C 
context) {
         return visitCommand(dropCatalogCommand, context);
     }
diff --git a/regression-test/data/nereids_p0/show/test_show_columns_command.out 
b/regression-test/data/nereids_p0/show/test_show_columns_command.out
new file mode 100644
index 00000000000..950d64388a3
Binary files /dev/null and 
b/regression-test/data/nereids_p0/show/test_show_columns_command.out differ
diff --git 
a/regression-test/suites/nereids_p0/show/test_show_columns_command.groovy 
b/regression-test/suites/nereids_p0/show/test_show_columns_command.groovy
new file mode 100644
index 00000000000..3d21cd9b6f1
--- /dev/null
+++ b/regression-test/suites/nereids_p0/show/test_show_columns_command.groovy
@@ -0,0 +1,57 @@
+// 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_columns_command", "nereids_p0") {
+    def dbName = "test_show_columns_db"
+    def tableName = "test_show_columns_table"
+
+    try {
+        sql """CREATE DATABASE IF NOT EXISTS ${dbName}"""
+        sql """
+            CREATE TABLE IF NOT EXISTS ${dbName}.${tableName} (
+                id INT,
+                name STRING,
+                score FLOAT
+            )
+            DISTRIBUTED BY HASH(id) BUCKETS 3
+            PROPERTIES ("replication_num" = "1");
+        """
+
+        // Test SHOW COLUMNS
+        checkNereidsExecute("""SHOW COLUMNS FROM ${dbName}.${tableName}""")
+        qt_cmd("""SHOW COLUMNS FROM ${dbName}.${tableName}""")
+
+        // Test SHOW FULL COLUMNS
+        checkNereidsExecute("""SHOW FULL COLUMNS FROM 
${dbName}.${tableName}""")
+        qt_cmd("""SHOW FULL COLUMNS FROM ${dbName}.${tableName}""")
+
+        // Test SHOW COLUMNS with LIKE
+        checkNereidsExecute("""SHOW COLUMNS FROM ${dbName}.${tableName} LIKE 
's%'""")
+        qt_cmd("""SHOW COLUMNS FROM ${dbName}.${tableName} LIKE 's%'""")
+
+        // Test SHOW COLUMNS with WHERE clause
+        checkNereidsExecute("""SHOW COLUMNS FROM ${dbName}.${tableName} WHERE 
Field = 'id'""")
+        qt_cmd("""SHOW COLUMNS FROM ${dbName}.${tableName} WHERE Field = 
'id'""")
+
+        // Test SHOW FULL COLUMNS with WHERE clause
+        checkNereidsExecute("""SHOW FULL COLUMNS FROM ${dbName}.${tableName} 
WHERE Field LIKE '%name%'""")
+
+    } finally {
+        sql """DROP TABLE IF EXISTS ${dbName}.${tableName}"""
+        sql """DROP DATABASE IF EXISTS ${dbName}"""
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to