From 56331956e1e43264a1913ddcb9d4629acf9e8ecc Mon Sep 17 00:00:00 2001 From: Sridhar R Manikarnike Date: Mon, 23 Dec 2024 18:46:18 +0000 Subject: [PATCH] [Enhancement] (nereids)implement showColumnsCommand in nereids --- .../org/apache/doris/nereids/DorisParser.g4 | 4 +- .../nereids/parser/LogicalPlanBuilder.java | 15 ++ .../doris/nereids/trees/plans/PlanType.java | 1 + .../plans/commands/ShowColumnsCommand.java | 161 ++++++++++++++++++ .../trees/plans/visitor/CommandVisitor.java | 5 + .../show/test_show_columns_command.out | 14 ++ .../show/test_show_columns_command.groovy | 50 ++++++ 7 files changed, 248 insertions(+), 2 deletions(-) create mode 100644 fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowColumnsCommand.java create mode 100644 regression-test/data/nereids_p0/show/test_show_columns_command.out create mode 100644 regression-test/suites/nereids_p0/show/test_show_columns_command.groovy 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 37e1c68cefb91c..03efdc616d55f3 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 @@ -272,6 +272,8 @@ supportedShowStatement | SHOW TABLET DIAGNOSIS tabletId=INTEGER_VALUE #showDiagnoseTablet | SHOW FRONTENDS name=identifier? #showFrontends | SHOW DATABASE databaseId=INTEGER_VALUE #showDatabaseId + | SHOW FULL? (COLUMNS | FIELDS) (FROM | IN) tableName=multipartIdentifier + ((FROM | IN) database=multipartIdentifier)? (LIKE STRING_LITERAL)? #showColumns | SHOW TABLE tableId=INTEGER_VALUE #showTableId | SHOW TRASH (ON backend=STRING_LITERAL)? #showTrash | SHOW (GLOBAL | SESSION | LOCAL)? STATUS #showStatus @@ -333,8 +335,6 @@ unsupportedShowStatement | SHOW (DATABASES | SCHEMAS) (FROM catalog=identifier)? wildWhere? #showDatabases | SHOW CATALOGS wildWhere? #showCatalogs | SHOW CATALOG name=identifier #showCatalog - | SHOW FULL? (COLUMNS | FIELDS) (FROM | IN) tableName=multipartIdentifier - ((FROM | IN) database=multipartIdentifier)? wildWhere? #showColumns | SHOW COUNT LEFT_PAREN ASTERISK RIGHT_PAREN (WARNINGS | ERRORS) #showWaringErrorCount | SHOW LOAD WARNINGS ((((FROM | IN) database=multipartIdentifier)? wildWhere? limitClause?) | (ON url=STRING_LITERAL)) #showLoadWarings 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 d98d0660f5c9cb..2e07b019a7fe31 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 @@ -246,6 +246,7 @@ import org.apache.doris.nereids.DorisParser.ShowBrokerContext; import org.apache.doris.nereids.DorisParser.ShowCharsetContext; import org.apache.doris.nereids.DorisParser.ShowCollationContext; +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.ShowCreateCatalogContext; @@ -570,6 +571,7 @@ import org.apache.doris.nereids.trees.plans.commands.ShowBrokerCommand; import org.apache.doris.nereids.trees.plans.commands.ShowCharsetCommand; import org.apache.doris.nereids.trees.plans.commands.ShowCollationCommand; +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.ShowCreateCatalogCommand; @@ -5036,6 +5038,19 @@ public LogicalPlan visitShowSyncJob(ShowSyncJobContext ctx) { return new ShowSyncJobCommand(databaseName); } + @Override + public LogicalPlan visitShowColumns(ShowColumnsContext ctx) { + boolean isFull = ctx.FULL() != null; + List nameParts = visitMultipartIdentifier(ctx.tableName); + String databaseName = ctx.database != null ? ctx.database.getText() : null; + String pattern = null; + if (ctx.LIKE() != null) { + pattern = stripQuotes(ctx.STRING_LITERAL().getText()); + } + + return new ShowColumnsCommand(isFull, new TableNameInfo(nameParts), databaseName, pattern); + } + @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 2860ec10092312..6ee7060410144f 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 @@ -211,6 +211,7 @@ public enum PlanType { SHOW_BROKER_COMMAND, SHOW_CHARSET_COMMAND, SHOW_COLLATION_COMMAND, + 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 00000000000000..de018d2d458f0e --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowColumnsCommand.java @@ -0,0 +1,161 @@ +// 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.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.base.Strings; +import com.google.common.collect.Lists; + +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 ShowResultSetMetaData metaData; + private final boolean isFull; + private TableNameInfo tableNameInfo; + private final String databaseName; + private final String likePattern; + + public ShowColumnsCommand(boolean isFull, TableNameInfo tableNameInfo, String databaseName, String likePattern) { + super(PlanType.SHOW_COLUMNS_COMMAND); + this.isFull = isFull; + this.tableNameInfo = tableNameInfo; + this.databaseName = databaseName; + this.likePattern = likePattern; + } + + private void validate(ConnectContext ctx) throws AnalysisException { + if (!Strings.isNullOrEmpty(databaseName)) { + tableNameInfo.setDb(databaseName); + } + tableNameInfo.analyze(ctx); + if (isFull) { + metaData = META_DATA_VERBOSE; + } else { + metaData = META_DATA; + } + 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); + } + } + + @Override + public ShowResultSet doRun(ConnectContext ctx, StmtExecutor executor) throws Exception { + validate(ctx); + List> 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 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(metaData, rows); + } + + @Override + public R accept(PlanVisitor visitor, C context) { + return visitor.visitShowColumnsCommand(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 122e513a08cb57..24b1d1b64180e0 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 @@ -91,6 +91,7 @@ import org.apache.doris.nereids.trees.plans.commands.ShowBrokerCommand; import org.apache.doris.nereids.trees.plans.commands.ShowCharsetCommand; import org.apache.doris.nereids.trees.plans.commands.ShowCollationCommand; +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.ShowCreateCatalogCommand; @@ -349,6 +350,10 @@ default R visitAlterViewCommand(AlterViewCommand alterViewCommand, C context) { 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 00000000000000..4d3f8a2ef31c58 --- /dev/null +++ b/regression-test/data/nereids_p0/show/test_show_columns_command.out @@ -0,0 +1,14 @@ +-- This file is automatically generated. You should know what you did if you want to edit this +-- !cmd -- +id int YES YES \N +name text YES NO \N NONE +score float YES NO \N NONE + +-- !cmd -- +id int YES YES \N +name text YES NO \N NONE +score float YES NO \N NONE + +-- !cmd -- +score float YES NO \N NONE + 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 00000000000000..a05c78cc779952 --- /dev/null +++ b/regression-test/suites/nereids_p0/show/test_show_columns_command.groovy @@ -0,0 +1,50 @@ +// 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%'""") + + } finally { + sql """DROP TABLE IF EXISTS ${dbName}.${tableName}""" + sql """DROP DATABASE IF EXISTS ${dbName}""" + } +}