From 43390acc7b08d8ec6f129d035aad20d24a4a8980 Mon Sep 17 00:00:00 2001 From: Sridhar R Manikarnike Date: Tue, 3 Dec 2024 11:46:56 +0530 Subject: [PATCH] [Enhancement] (nereids)implement showEncryptKeysCommand in nereids (#44717) Issue Number: close #42774 --- .../org/apache/doris/nereids/DorisParser.g4 | 3 +- .../nereids/parser/LogicalPlanBuilder.java | 17 +++ .../doris/nereids/trees/plans/PlanType.java | 1 + .../commands/ShowEncryptKeysCommand.java | 134 ++++++++++++++++++ .../trees/plans/visitor/CommandVisitor.java | 5 + .../test_nereids_encrypt_test.groovy | 4 +- 6 files changed, 162 insertions(+), 2 deletions(-) create mode 100644 fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowEncryptKeysCommand.java 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 cc5c223f9ec0dd..eac4c661de920d 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 @@ -235,6 +235,8 @@ supportedShowStatement ((FROM | IN) database=identifier)? #showView | SHOW PLUGINS #showPlugins | SHOW REPOSITORIES #showRepositories + | SHOW ENCRYPTKEYS ((FROM | IN) database=multipartIdentifier)? + (LIKE STRING_LITERAL)? #showEncryptKeys | SHOW BRIEF? CREATE TABLE name=multipartIdentifier #showCreateTable | SHOW FULL? PROCESSLIST #showProcessList | SHOW ROLES #showRoles @@ -352,7 +354,6 @@ unsupportedShowStatement | SHOW TRANSACTION ((FROM | IN) database=multipartIdentifier)? wildWhere? #showTransaction | SHOW QUERY PROFILE queryIdPath=STRING_LITERAL #showQueryProfile | SHOW CACHE HOTSPOT tablePath=STRING_LITERAL #showCacheHotSpot - | SHOW ENCRYPTKEYS ((FROM | IN) database=multipartIdentifier)? wildWhere? #showEncryptKeys | SHOW SYNC JOB ((FROM | IN) database=multipartIdentifier)? #showSyncJob | SHOW TABLE CREATION ((FROM | IN) database=multipartIdentifier)? wildWhere? #showTableCreation | SHOW CATALOG RECYCLE BIN wildWhere? #showCatalogRecycleBin 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 7ec8ce62f3001e..401d18775af106 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 @@ -243,6 +243,7 @@ import org.apache.doris.nereids.DorisParser.ShowDeleteContext; import org.apache.doris.nereids.DorisParser.ShowDiagnoseTabletContext; import org.apache.doris.nereids.DorisParser.ShowDynamicPartitionContext; +import org.apache.doris.nereids.DorisParser.ShowEncryptKeysContext; import org.apache.doris.nereids.DorisParser.ShowEventsContext; import org.apache.doris.nereids.DorisParser.ShowFrontendsContext; import org.apache.doris.nereids.DorisParser.ShowGrantsContext; @@ -538,6 +539,7 @@ import org.apache.doris.nereids.trees.plans.commands.ShowDeleteCommand; import org.apache.doris.nereids.trees.plans.commands.ShowDiagnoseTabletCommand; import org.apache.doris.nereids.trees.plans.commands.ShowDynamicPartitionCommand; +import org.apache.doris.nereids.trees.plans.commands.ShowEncryptKeysCommand; import org.apache.doris.nereids.trees.plans.commands.ShowEventsCommand; import org.apache.doris.nereids.trees.plans.commands.ShowFrontendsCommand; import org.apache.doris.nereids.trees.plans.commands.ShowGrantsCommand; @@ -1903,6 +1905,21 @@ public static String stripQuotes(String str) { return str; } + @Override + public LogicalPlan visitShowEncryptKeys(ShowEncryptKeysContext ctx) { + String dbName = null; + if (ctx.database != null) { + List nameParts = visitMultipartIdentifier(ctx.database); + dbName = nameParts.get(0); // only one entry possible + } + + String likeString = null; + if (ctx.LIKE() != null) { + likeString = stripQuotes(ctx.STRING_LITERAL().getText()); + } + return new ShowEncryptKeysCommand(dbName, likeString); + } + @Override public LogicalPlan visitAliasedQuery(AliasedQueryContext ctx) { if (ctx.tableAlias().getText().equals("")) { 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 8061309e8b137c..48e64acaab6377 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 @@ -210,6 +210,7 @@ public enum PlanType { SHOW_DELETE_COMMAND, SHOW_DIAGNOSE_TABLET_COMMAND, SHOW_DYNAMIC_PARTITION_COMMAND, + SHOW_ENCRYPT_KEYS_COMMAND, SHOW_EVENTS_COMMAND, SHOW_FRONTENDS_COMMAND, SHOW_GRANTS_COMMAND, diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowEncryptKeysCommand.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowEncryptKeysCommand.java new file mode 100644 index 00000000000000..c72d0437bf55d8 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowEncryptKeysCommand.java @@ -0,0 +1,134 @@ +// 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.Database; +import org.apache.doris.catalog.DatabaseIf; +import org.apache.doris.catalog.EncryptKey; +import org.apache.doris.catalog.Env; +import org.apache.doris.catalog.ScalarType; +import org.apache.doris.common.AnalysisException; +import org.apache.doris.common.ErrorCode; +import org.apache.doris.common.ErrorReport; +import org.apache.doris.common.util.ListComparator; +import org.apache.doris.common.util.OrderByPair; +import org.apache.doris.common.util.Util; +import org.apache.doris.mysql.privilege.PrivPredicate; +import org.apache.doris.nereids.trees.plans.PlanType; +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.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Represents the command for SHOW ENCRYPTKEYS. + */ +public class ShowEncryptKeysCommand extends ShowCommand { + private static final ShowResultSetMetaData META_DATA = + ShowResultSetMetaData.builder() + .addColumn(new Column("EncryptKey Name", ScalarType.createVarchar(20))) + .addColumn(new Column("EncryptKey String", ScalarType.createVarchar(1024))) + .build(); + + private String dbName; + private final String likeString; + + public ShowEncryptKeysCommand(String databaseName, String likeString) { + super(PlanType.SHOW_ENCRYPT_KEYS_COMMAND); + this.dbName = databaseName; + this.likeString = likeString; + } + + private void validate(ConnectContext ctx) throws AnalysisException { + if (Strings.isNullOrEmpty(dbName)) { + dbName = ctx.getDatabase(); + if (Strings.isNullOrEmpty(dbName)) { + ErrorReport.reportAnalysisException(ErrorCode.ERR_NO_DB_ERROR); + } + } + + // check auth + if (!Env.getCurrentEnv().getAccessManager().checkGlobalPriv(ConnectContext.get(), PrivPredicate.ADMIN)) { + ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, + PrivPredicate.ADMIN.getPrivs().toString()); + } + } + + @Override + public ShowResultSet doRun(ConnectContext ctx, StmtExecutor executor) throws Exception { + validate(ctx); + Util.prohibitExternalCatalog(ctx.getDefaultCatalog(), this.getClass().getSimpleName()); + DatabaseIf db = ctx.getCurrentCatalog().getDbOrAnalysisException(dbName); + List> resultRowSet = Lists.newArrayList(); + if (db instanceof Database) { + List encryptKeys = ((Database) db).getEncryptKeys(); + List> rowSet = Lists.newArrayList(); + for (EncryptKey encryptKey : encryptKeys) { + List row = encryptKey.getInfo(); + // like predicate + if (likeString == null || like(encryptKey.getEncryptKeyName().getKeyName())) { + rowSet.add(row); + } + + } + + // sort function rows by first column asc + ListComparator> comparator = null; + OrderByPair orderByPair = new OrderByPair(0, false); + comparator = new ListComparator<>(orderByPair); + Collections.sort(rowSet, comparator); + + Set encryptKeyNameSet = new HashSet<>(); + for (List row : rowSet) { + List resultRow = Lists.newArrayList(); + for (Comparable column : row) { + resultRow.add(column.toString()); + } + resultRowSet.add(resultRow); + encryptKeyNameSet.add(resultRow.get(0)); + } + } + + return new ShowResultSet(META_DATA, resultRowSet); + } + + private boolean like(String str) { + str = str.toLowerCase(); + return str.matches(likeString.replace(".", "\\.").replace("?", ".").replace("%", ".*").toLowerCase()); + } + + @Override + public R accept(PlanVisitor visitor, C context) { + return visitor.visitShowEncryptKeysCommand(this, context); + } + + public ShowResultSetMetaData getMetaData() { + return META_DATA; + } +} + 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 fe9d27d9f303a1..8561c934ec976f 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 @@ -90,6 +90,7 @@ import org.apache.doris.nereids.trees.plans.commands.ShowDeleteCommand; import org.apache.doris.nereids.trees.plans.commands.ShowDiagnoseTabletCommand; import org.apache.doris.nereids.trees.plans.commands.ShowDynamicPartitionCommand; +import org.apache.doris.nereids.trees.plans.commands.ShowEncryptKeysCommand; import org.apache.doris.nereids.trees.plans.commands.ShowEventsCommand; import org.apache.doris.nereids.trees.plans.commands.ShowFrontendsCommand; import org.apache.doris.nereids.trees.plans.commands.ShowGrantsCommand; @@ -533,6 +534,10 @@ default R visitShowTableIdCommand(ShowTableIdCommand showTableIdCommand, C conte return visitCommand(showTableIdCommand, context); } + default R visitShowEncryptKeysCommand(ShowEncryptKeysCommand showEncryptKeysCommand, C context) { + return visitCommand(showEncryptKeysCommand, context); + } + default R visitSyncCommand(SyncCommand syncCommand, C context) { return visitCommand(syncCommand, context); } diff --git a/regression-test/suites/nereids_p0/test_nereids_encrypt_test.groovy b/regression-test/suites/nereids_p0/test_nereids_encrypt_test.groovy index 8954090bb4c31d..bd984e4164c37b 100644 --- a/regression-test/suites/nereids_p0/test_nereids_encrypt_test.groovy +++ b/regression-test/suites/nereids_p0/test_nereids_encrypt_test.groovy @@ -21,9 +21,11 @@ suite("test_nereids_encrypt_test") { sql """ use ${dbName}; """ checkNereidsExecute("drop encryptkey if exists ${encryptkeyName}") checkNereidsExecute("""CREATE ENCRYPTKEY ${encryptkeyName} AS "ABCD123456789";""") + checkNereidsExecute("SHOW ENCRYPTKEYS FROM ${dbName}") qt_check_encrypt_1("SHOW ENCRYPTKEYS FROM ${dbName}") checkNereidsExecute("drop encryptkey ${encryptkeyName}") + checkNereidsExecute("SHOW ENCRYPTKEYS FROM ${dbName}") qt_check_encrypt_2("SHOW ENCRYPTKEYS FROM ${dbName}") checkNereidsExecute("drop encryptkey if exists ${encryptkeyName}") qt_check_encrypt_3("SHOW ENCRYPTKEYS FROM ${dbName}") -} \ No newline at end of file +}