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]