From 13f245306b45d96024d1983afe1f2c22bf6c3a69 Mon Sep 17 00:00:00 2001 From: lsy3993 Date: Fri, 7 Mar 2025 14:57:53 +0800 Subject: [PATCH 1/6] support show backup in nereids --- .../org/apache/doris/nereids/DorisParser.g4 | 2 +- .../nereids/parser/LogicalPlanBuilder.java | 16 ++ .../doris/nereids/trees/plans/PlanType.java | 1 + .../plans/commands/ShowBackupCommand.java | 191 ++++++++++++++++++ .../trees/plans/visitor/CommandVisitor.java | 5 + .../conf/regression-conf-custom.groovy | 1 + .../show/test_nereids_show_backup.groovy | 72 +++++++ 7 files changed, 287 insertions(+), 1 deletion(-) create mode 100644 fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowBackupCommand.java create mode 100644 regression-test/suites/nereids_p0/show/test_nereids_show_backup.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 9a819baaf0a84d..12f05c2149ab52 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 @@ -275,6 +275,7 @@ supportedShowStatement : SHOW statementScope? VARIABLES wildWhere? #showVariables | SHOW AUTHORS #showAuthors | SHOW CREATE (DATABASE | SCHEMA) name=multipartIdentifier #showCreateDatabase + | SHOW BACKUP ((FROM | IN) database=multipartIdentifier)? wildWhere? #showBackup | SHOW BROKER #showBroker | SHOW DYNAMIC PARTITION TABLES ((FROM | IN) database=multipartIdentifier)? #showDynamicPartition | SHOW EVENTS ((FROM | IN) database=multipartIdentifier)? wildWhere? #showEvents @@ -401,7 +402,6 @@ unsupportedShowStatement | SHOW TABLET tabletId=INTEGER_VALUE #showTabletId | SHOW TABLETS FROM tableName=multipartIdentifier partitionSpec? wildWhere? sortClause? limitClause? #showTabletsFromTable - | SHOW BACKUP ((FROM | IN) database=multipartIdentifier)? wildWhere? #showBackup | SHOW BRIEF? RESTORE ((FROM | IN) database=multipartIdentifier)? wildWhere? #showRestore | SHOW RESOURCES wildWhere? sortClause? limitClause? #showResources | SHOW WORKLOAD GROUPS wildWhere? #showWorkloadGroups 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 5129c0991a51c5..47833ac0ac6f0a 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 @@ -297,6 +297,7 @@ import org.apache.doris.nereids.DorisParser.ShowAnalyzeContext; import org.apache.doris.nereids.DorisParser.ShowAuthorsContext; import org.apache.doris.nereids.DorisParser.ShowBackendsContext; +import org.apache.doris.nereids.DorisParser.ShowBackupContext; import org.apache.doris.nereids.DorisParser.ShowBrokerContext; import org.apache.doris.nereids.DorisParser.ShowCharsetContext; import org.apache.doris.nereids.DorisParser.ShowCollationContext; @@ -600,6 +601,7 @@ import org.apache.doris.nereids.trees.plans.commands.ShowAnalyzeCommand; import org.apache.doris.nereids.trees.plans.commands.ShowAuthorsCommand; import org.apache.doris.nereids.trees.plans.commands.ShowBackendsCommand; +import org.apache.doris.nereids.trees.plans.commands.ShowBackupCommand; import org.apache.doris.nereids.trees.plans.commands.ShowBrokerCommand; import org.apache.doris.nereids.trees.plans.commands.ShowCatalogCommand; import org.apache.doris.nereids.trees.plans.commands.ShowCharsetCommand; @@ -4960,6 +4962,20 @@ public LogicalPlan visitShowBackends(ShowBackendsContext ctx) { return new ShowBackendsCommand(); } + @Override + public LogicalPlan visitShowBackup(ShowBackupContext ctx) { + String dbName = null; + Expression wildWhere = null; + if (ctx.database != null) { + List nameParts = visitMultipartIdentifier(ctx.database); + dbName = nameParts.get(0); + } + if (ctx.wildWhere() != null) { + wildWhere = getWildWhere(ctx.wildWhere()); + } + return new ShowBackupCommand(dbName, wildWhere); + } + @Override public LogicalPlan visitShowPlugins(ShowPluginsContext ctx) { return new ShowPluginsCommand(); 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 f6a3997dd0fa15..1d41cd8e675989 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 @@ -221,6 +221,7 @@ public enum PlanType { SHOW_ANALYZE_COMMAND, SHOW_QUEUED_ANALYZE_JOBS_COMMAND, SHOW_BACKENDS_COMMAND, + SHOW_BACKUP_COMMAND, SHOW_BLOCK_RULE_COMMAND, SHOW_BROKER_COMMAND, SHOW_CATALOG_COMMAND, diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowBackupCommand.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowBackupCommand.java new file mode 100644 index 00000000000000..5d95ddaecc5d7e --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowBackupCommand.java @@ -0,0 +1,191 @@ +// 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.backup.AbstractJob; +import org.apache.doris.backup.BackupJob; +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.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.common.UserException; +import org.apache.doris.datasource.InternalCatalog; +import org.apache.doris.mysql.privilege.PrivPredicate; +import org.apache.doris.nereids.analyzer.UnboundSlot; +import org.apache.doris.nereids.trees.expressions.EqualTo; +import org.apache.doris.nereids.trees.expressions.Expression; +import org.apache.doris.nereids.trees.expressions.Like; +import org.apache.doris.nereids.trees.expressions.literal.StringLikeLiteral; +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.ImmutableList; + +import java.util.List; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +/** + * show backup command + */ +public class ShowBackupCommand extends ShowCommand { + public static final ImmutableList TITLE_NAMES = new ImmutableList.Builder() + .add("JobId").add("SnapshotName").add("DbName").add("State").add("BackupObjs").add("CreateTime") + .add("SnapshotFinishedTime").add("UploadFinishedTime").add("FinishedTime").add("UnfinishedTasks") + .add("Progress").add("TaskErrMsg").add("Status").add("Timeout") + .build(); + + private String dbName; + private Expression where; + private boolean isAccurateMatch; + private String snapshotName; + + /** + * constructor + */ + public ShowBackupCommand(String dbName, Expression where) { + super(PlanType.SHOW_BACKUP_COMMAND); + this.dbName = dbName; + this.where = where; + } + + public String getDbName() { + return dbName; + } + + /** + * get metadata + */ + public ShowResultSetMetaData getMetaData() { + ShowResultSetMetaData.Builder builder = ShowResultSetMetaData.builder(); + for (String title : TITLE_NAMES) { + builder.addColumn(new Column(title, ScalarType.createVarchar(30))); + } + return builder.build(); + } + + /** + * get label predicate for show backup + */ + public Predicate getSnapshotPredicate() throws AnalysisException { + if (null == where) { + return label -> true; + } + if (isAccurateMatch) { + return CaseSensibility.LABEL.getCaseSensibility() + ? label -> label.equals(snapshotName) : label -> label.equalsIgnoreCase(snapshotName); + } else { + PatternMatcher patternMatcher = PatternMatcherWrapper.createMysqlPattern( + snapshotName, CaseSensibility.LABEL.getCaseSensibility()); + return patternMatcher::match; + } + } + + /** + * validate + */ + private boolean validate(ConnectContext ctx) throws UserException { + if (Strings.isNullOrEmpty(dbName)) { + dbName = ctx.getDatabase(); + if (Strings.isNullOrEmpty(dbName)) { + throw new AnalysisException("No database selected"); + } + } + + // check auth + if (!Env.getCurrentEnv().getAccessManager() + .checkDbPriv(ConnectContext.get(), InternalCatalog.INTERNAL_CATALOG_NAME, dbName, PrivPredicate.LOAD)) { + ErrorReport.reportAnalysisException(ErrorCode.ERR_DBACCESS_DENIED_ERROR, + ConnectContext.get().getQualifiedUser(), dbName); + } + + // SQL may be like : show backup from your_db_name; there is no where clause. + if (where == null) { + return true; + } + + if (!(where instanceof Like) && !(where instanceof EqualTo)) { + return false; + } + + if (where instanceof EqualTo) { + isAccurateMatch = true; + } + + // left child + if (!(where.child(0) instanceof UnboundSlot)) { + return false; + } + String leftKey = ((UnboundSlot) where.child(0)).getName(); + if (!"snapshotname".equalsIgnoreCase(leftKey)) { + return false; + } + + // right child + if (!(where.child(1) instanceof StringLikeLiteral)) { + return false; + } + snapshotName = ((StringLikeLiteral) where.child(1)).getStringValue(); + if (Strings.isNullOrEmpty(snapshotName)) { + return false; + } + + return true; + } + + /** + * handle show backup + */ + private ShowResultSet handleShowBackup(ConnectContext ctx, StmtExecutor executor) throws Exception { + boolean valid = validate(ctx); + if (!valid) { + throw new AnalysisException("Where clause should like: SnapshotName = \"your_snapshot_name\", " + + " or SnapshotName LIKE \"matcher\""); + } + + DatabaseIf database = ctx.getCurrentCatalog().getDbOrAnalysisException(dbName); + List jobs = Env.getCurrentEnv().getBackupHandler() + .getJobs(database.getId(), getSnapshotPredicate()); + List backupJobs = jobs.stream().filter(job -> job instanceof BackupJob) + .map(job -> (BackupJob) job).collect(Collectors.toList()); + List> infos = backupJobs.stream().map(BackupJob::getInfo).collect(Collectors.toList()); + + return new ShowResultSet(getMetaData(), infos); + } + + @Override + public ShowResultSet doRun(ConnectContext ctx, StmtExecutor executor) throws Exception { + return handleShowBackup(ctx, executor); + } + + @Override + public R accept(PlanVisitor visitor, C context) { + return visitor.visitShowBackupCommand(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 c1c805c4d24d65..78dff9a9abeb24 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 @@ -100,6 +100,7 @@ import org.apache.doris.nereids.trees.plans.commands.ShowAnalyzeCommand; import org.apache.doris.nereids.trees.plans.commands.ShowAuthorsCommand; import org.apache.doris.nereids.trees.plans.commands.ShowBackendsCommand; +import org.apache.doris.nereids.trees.plans.commands.ShowBackupCommand; import org.apache.doris.nereids.trees.plans.commands.ShowBrokerCommand; import org.apache.doris.nereids.trees.plans.commands.ShowCatalogCommand; import org.apache.doris.nereids.trees.plans.commands.ShowCharsetCommand; @@ -508,6 +509,10 @@ default R visitShowBackendsCommand(ShowBackendsCommand showBackendsCommand, C co return visitCommand(showBackendsCommand, context); } + default R visitShowBackupCommand(ShowBackupCommand showBackupCommand, C context) { + return visitCommand(showBackupCommand, context); + } + default R visitShowCreateTableCommand(ShowCreateTableCommand showCreateTableCommand, C context) { return visitCommand(showCreateTableCommand, context); } diff --git a/regression-test/pipeline/cloud_p0/conf/regression-conf-custom.groovy b/regression-test/pipeline/cloud_p0/conf/regression-conf-custom.groovy index e3f6b7e6f61831..46e9313c8db8ea 100644 --- a/regression-test/pipeline/cloud_p0/conf/regression-conf-custom.groovy +++ b/regression-test/pipeline/cloud_p0/conf/regression-conf-custom.groovy @@ -47,6 +47,7 @@ excludeSuites = "000_the_start_sentinel_do_not_touch," + // keep this line as th "test_topn_fault_injection," + "auto_partition_in_partition_prune," + // inserted data in too many tablets, txn to large. not suitable for cloud. "one_col_range_partition," + // inserted data in too many tablets, txn to large. not suitable for cloud. + "test_nereids_show_backup," + "zzz_the_end_sentinel_do_not_touch" // keep this line as the last line excludeDirectories = "000_the_start_sentinel_do_not_touch," + // keep this line as the first line diff --git a/regression-test/suites/nereids_p0/show/test_nereids_show_backup.groovy b/regression-test/suites/nereids_p0/show/test_nereids_show_backup.groovy new file mode 100644 index 00000000000000..f0551dd1a975d3 --- /dev/null +++ b/regression-test/suites/nereids_p0/show/test_nereids_show_backup.groovy @@ -0,0 +1,72 @@ +// 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_nereids_show_backup") { + String suiteName = "test_show_backup" + String repoName = "repo_" + UUID.randomUUID().toString().replace("-", "") + String dbName = "${suiteName}_db" + String tableName = "${suiteName}_table" + String snapshotName = "${suiteName}_snapshot" + + def syncer = getSyncer() + syncer.createS3Repository(repoName) + + sql "CREATE DATABASE IF NOT EXISTS ${dbName}" + sql "DROP TABLE IF EXISTS ${dbName}.${tableName}" + sql """ + CREATE TABLE ${dbName}.${tableName} ( + `id` LARGEINT NOT NULL, + `count` LARGEINT SUM DEFAULT "0") + AGGREGATE KEY(`id`) + DISTRIBUTED BY HASH(`id`) BUCKETS 2 + PROPERTIES + ( + "replication_num" = "1" + ) + """ + + List values = [] + for (int i = 1; i <= 10; ++i) { + values.add("(${i}, ${i})") + } + sql "INSERT INTO ${dbName}.${tableName} VALUES ${values.join(",")}" + def result = sql "SELECT * FROM ${dbName}.${tableName}" + assertEquals(result.size(), values.size()); + + sql """ + BACKUP SNAPSHOT ${dbName}.${snapshotName} + TO `${repoName}` + ON (${tableName}) + """ + + syncer.waitSnapshotFinish(dbName) + + def snapshot = syncer.getSnapshotTimestamp(repoName, snapshotName) + assertTrue(snapshot != null) + + checkNereidsExecute("show backup") + checkNereidsExecute("show backup from ${dbName}") + checkNereidsExecute("show backup in ${dbName}") + checkNereidsExecute("""show backup from ${dbName} where SnapshotName = "${snapshotName}" """) + checkNereidsExecute("""show backup in ${dbName} where SnapshotName = "${snapshotName}" """) + checkNereidsExecute("show backup from ${dbName} where SnapshotName like 'test%'") + checkNereidsExecute("show backup in ${dbName} where SnapshotName like 'test%'") + + sql "DROP TABLE ${dbName}.${tableName} FORCE" + sql "DROP DATABASE ${dbName} FORCE" + sql "DROP REPOSITORY `${repoName}`" +} From f0ea4e81d66c496f3cb7c5a86cb35a8dcddf9479 Mon Sep 17 00:00:00 2001 From: lsy3993 Date: Mon, 10 Mar 2025 14:51:07 +0800 Subject: [PATCH 2/6] delete useless method --- .../nereids/trees/plans/commands/ShowBackupCommand.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowBackupCommand.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowBackupCommand.java index 5d95ddaecc5d7e..2c61d3b9d13ac8 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowBackupCommand.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowBackupCommand.java @@ -75,14 +75,11 @@ public ShowBackupCommand(String dbName, Expression where) { this.where = where; } - public String getDbName() { - return dbName; - } /** * get metadata */ - public ShowResultSetMetaData getMetaData() { + private ShowResultSetMetaData getMetaData() { ShowResultSetMetaData.Builder builder = ShowResultSetMetaData.builder(); for (String title : TITLE_NAMES) { builder.addColumn(new Column(title, ScalarType.createVarchar(30))); @@ -93,7 +90,7 @@ public ShowResultSetMetaData getMetaData() { /** * get label predicate for show backup */ - public Predicate getSnapshotPredicate() throws AnalysisException { + private Predicate getSnapshotPredicate() throws AnalysisException { if (null == where) { return label -> true; } From 9da1437f0137bf8569e719e31049a661c8a42fd8 Mon Sep 17 00:00:00 2001 From: lsy3993 Date: Mon, 10 Mar 2025 18:14:42 +0800 Subject: [PATCH 3/6] format fix --- .../doris/nereids/trees/plans/commands/ShowBackupCommand.java | 1 - 1 file changed, 1 deletion(-) diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowBackupCommand.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowBackupCommand.java index 2c61d3b9d13ac8..80611b4d239c34 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowBackupCommand.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowBackupCommand.java @@ -75,7 +75,6 @@ public ShowBackupCommand(String dbName, Expression where) { this.where = where; } - /** * get metadata */ From 0e4427cd09349e40b59972eec9f0b6f168e8352b Mon Sep 17 00:00:00 2001 From: lsy3993 Date: Tue, 11 Mar 2025 22:19:26 +0800 Subject: [PATCH 4/6] add fe ut --- .../org/apache/doris/nereids/DorisParser.g4 | 2 +- .../nereids/parser/LogicalPlanBuilder.java | 3 +- .../plans/commands/ShowBackupCommand.java | 2 +- .../plans/commands/ShowBackupCommandTest.java | 66 +++++++++++++++++++ 4 files changed, 69 insertions(+), 4 deletions(-) create mode 100644 fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/ShowBackupCommandTest.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 12f05c2149ab52..6268ba13c27b19 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 @@ -275,7 +275,7 @@ supportedShowStatement : SHOW statementScope? VARIABLES wildWhere? #showVariables | SHOW AUTHORS #showAuthors | SHOW CREATE (DATABASE | SCHEMA) name=multipartIdentifier #showCreateDatabase - | SHOW BACKUP ((FROM | IN) database=multipartIdentifier)? wildWhere? #showBackup + | SHOW BACKUP ((FROM | IN) database=identifier)? wildWhere? #showBackup | SHOW BROKER #showBroker | SHOW DYNAMIC PARTITION TABLES ((FROM | IN) database=multipartIdentifier)? #showDynamicPartition | SHOW EVENTS ((FROM | IN) database=multipartIdentifier)? wildWhere? #showEvents 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 47833ac0ac6f0a..c93fe08985c03a 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 @@ -4967,8 +4967,7 @@ public LogicalPlan visitShowBackup(ShowBackupContext ctx) { String dbName = null; Expression wildWhere = null; if (ctx.database != null) { - List nameParts = visitMultipartIdentifier(ctx.database); - dbName = nameParts.get(0); + dbName = ctx.database.getText(); } if (ctx.wildWhere() != null) { wildWhere = getWildWhere(ctx.wildWhere()); diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowBackupCommand.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowBackupCommand.java index 80611b4d239c34..cbd7fdc019c710 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowBackupCommand.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowBackupCommand.java @@ -81,7 +81,7 @@ public ShowBackupCommand(String dbName, Expression where) { private ShowResultSetMetaData getMetaData() { ShowResultSetMetaData.Builder builder = ShowResultSetMetaData.builder(); for (String title : TITLE_NAMES) { - builder.addColumn(new Column(title, ScalarType.createVarchar(30))); + builder.addColumn(new Column(title, ScalarType.STRING)); } return builder.build(); } diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/ShowBackupCommandTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/ShowBackupCommandTest.java new file mode 100644 index 00000000000000..8841a27d8c7ac8 --- /dev/null +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/ShowBackupCommandTest.java @@ -0,0 +1,66 @@ +// 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.common.AnalysisException; +import org.apache.doris.common.CaseSensibility; +import org.apache.doris.common.UserException; +import org.apache.doris.nereids.analyzer.UnboundSlot; +import org.apache.doris.nereids.trees.expressions.EqualTo; +import org.apache.doris.nereids.trees.expressions.ExprId; +import org.apache.doris.nereids.trees.expressions.Expression; +import org.apache.doris.nereids.trees.expressions.Like; +import org.apache.doris.nereids.trees.expressions.SlotReference; +import org.apache.doris.nereids.trees.expressions.literal.StringLikeLiteral; +import org.apache.doris.nereids.types.IntegerType; + +import com.google.common.collect.Lists; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.function.Predicate; + +public class ShowBackupCommandTest { + private final ExprId exprId = new ExprId(1); + private final Expression left1 = new SlotReference(exprId, "left", + IntegerType.INSTANCE, false, Lists.newArrayList()); + private final Expression right1 = new SlotReference(exprId, "right", + IntegerType.INSTANCE, false, Lists.newArrayList()); + + @Test + void testGetSnapshotPredicate() throws AnalysisException { + String snapshotName = "ccrs_mysql_edu_mall_mall_source_occupy_1741669209_1741669211"; + Predicate predicate = CaseSensibility.LABEL.getCaseSensibility() + ? label -> label.equals(snapshotName) : label -> label.equalsIgnoreCase(snapshotName); + + Assertions.assertTrue(predicate.test("false")); + } + + @Test + void testValidate() throws UserException { + Like like = new Like(left1, right1); + EqualTo equalTo = new EqualTo(left1, right1); + Assertions.assertTrue(like.child(0) instanceof UnboundSlot); + + String leftKey = ((UnboundSlot) like.child(0)).getName(); + Assertions.assertEquals("left", leftKey); + + String rightName = ((StringLikeLiteral) equalTo.child(1)).getStringValue(); + Assertions.assertEquals("right", rightName); + } +} From 9f09143de7e39551a6a9ecdc15eef8fc3e7b5141 Mon Sep 17 00:00:00 2001 From: lsy3993 Date: Wed, 12 Mar 2025 12:26:59 +0800 Subject: [PATCH 5/6] fix fe ut --- .../plans/commands/ShowBackupCommandTest.java | 34 +++++++------------ 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/ShowBackupCommandTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/ShowBackupCommandTest.java index 8841a27d8c7ac8..8958ca08afe1fe 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/ShowBackupCommandTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/ShowBackupCommandTest.java @@ -22,13 +22,12 @@ import org.apache.doris.common.UserException; import org.apache.doris.nereids.analyzer.UnboundSlot; import org.apache.doris.nereids.trees.expressions.EqualTo; -import org.apache.doris.nereids.trees.expressions.ExprId; import org.apache.doris.nereids.trees.expressions.Expression; import org.apache.doris.nereids.trees.expressions.Like; -import org.apache.doris.nereids.trees.expressions.SlotReference; import org.apache.doris.nereids.trees.expressions.literal.StringLikeLiteral; -import org.apache.doris.nereids.types.IntegerType; +import org.apache.doris.nereids.trees.expressions.literal.VarcharLiteral; +import com.google.common.base.Strings; import com.google.common.collect.Lists; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -36,31 +35,24 @@ import java.util.function.Predicate; public class ShowBackupCommandTest { - private final ExprId exprId = new ExprId(1); - private final Expression left1 = new SlotReference(exprId, "left", - IntegerType.INSTANCE, false, Lists.newArrayList()); - private final Expression right1 = new SlotReference(exprId, "right", - IntegerType.INSTANCE, false, Lists.newArrayList()); - @Test void testGetSnapshotPredicate() throws AnalysisException { - String snapshotName = "ccrs_mysql_edu_mall_mall_source_occupy_1741669209_1741669211"; + String snapshotName = "mysql_edu_mall_mall_source_occupy_1741669209_1741669211"; Predicate predicate = CaseSensibility.LABEL.getCaseSensibility() ? label -> label.equals(snapshotName) : label -> label.equalsIgnoreCase(snapshotName); - - Assertions.assertTrue(predicate.test("false")); + Assertions.assertTrue(predicate.test(snapshotName)); } @Test void testValidate() throws UserException { - Like like = new Like(left1, right1); - EqualTo equalTo = new EqualTo(left1, right1); - Assertions.assertTrue(like.child(0) instanceof UnboundSlot); - - String leftKey = ((UnboundSlot) like.child(0)).getName(); - Assertions.assertEquals("left", leftKey); - - String rightName = ((StringLikeLiteral) equalTo.child(1)).getStringValue(); - Assertions.assertEquals("right", rightName); + Expression equalTo = new EqualTo(new UnboundSlot(Lists.newArrayList("snapshotname")), + new VarcharLiteral("mysql_edu_mall_mall_source_occupy_1741669209_174166921")); + String leftKey = ((UnboundSlot) equalTo.child(0)).getName(); + Assertions.assertEquals("snapshotname", leftKey); + + Expression like = new Like(new UnboundSlot(Lists.newArrayList("snapshotname")), + new VarcharLiteral("mysql_edu_mall_%")); + String snapshotNameLike = ((StringLikeLiteral) like.child(1)).getStringValue(); + Assertions.assertFalse(Strings.isNullOrEmpty(snapshotNameLike)); } } From fc4c435771f3473b94c8fb5e1a8ed318f9753ec6 Mon Sep 17 00:00:00 2001 From: lsy3993 Date: Fri, 14 Mar 2025 14:52:38 +0800 Subject: [PATCH 6/6] add more fe ut --- .../plans/commands/ShowBackupCommand.java | 7 +- .../plans/commands/ShowBackupCommandTest.java | 93 +++++++++++++++---- 2 files changed, 80 insertions(+), 20 deletions(-) diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowBackupCommand.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowBackupCommand.java index cbd7fdc019c710..18c13827c36aea 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowBackupCommand.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowBackupCommand.java @@ -44,6 +44,7 @@ import org.apache.doris.qe.ShowResultSetMetaData; import org.apache.doris.qe.StmtExecutor; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; @@ -89,7 +90,8 @@ private ShowResultSetMetaData getMetaData() { /** * get label predicate for show backup */ - private Predicate getSnapshotPredicate() throws AnalysisException { + @VisibleForTesting + protected Predicate getSnapshotPredicate() throws AnalysisException { if (null == where) { return label -> true; } @@ -106,7 +108,8 @@ private Predicate getSnapshotPredicate() throws AnalysisException { /** * validate */ - private boolean validate(ConnectContext ctx) throws UserException { + @VisibleForTesting + protected boolean validate(ConnectContext ctx) throws UserException { if (Strings.isNullOrEmpty(dbName)) { dbName = ctx.getDatabase(); if (Strings.isNullOrEmpty(dbName)) { diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/ShowBackupCommandTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/ShowBackupCommandTest.java index 8958ca08afe1fe..ce75598a38b219 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/ShowBackupCommandTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/ShowBackupCommandTest.java @@ -18,41 +18,98 @@ package org.apache.doris.nereids.trees.plans.commands; import org.apache.doris.common.AnalysisException; -import org.apache.doris.common.CaseSensibility; import org.apache.doris.common.UserException; import org.apache.doris.nereids.analyzer.UnboundSlot; +import org.apache.doris.nereids.parser.NereidsParser; import org.apache.doris.nereids.trees.expressions.EqualTo; import org.apache.doris.nereids.trees.expressions.Expression; +import org.apache.doris.nereids.trees.expressions.LessThan; import org.apache.doris.nereids.trees.expressions.Like; -import org.apache.doris.nereids.trees.expressions.literal.StringLikeLiteral; +import org.apache.doris.nereids.trees.expressions.literal.StringLiteral; import org.apache.doris.nereids.trees.expressions.literal.VarcharLiteral; +import org.apache.doris.nereids.trees.plans.logical.LogicalPlan; +import org.apache.doris.utframe.TestWithFeService; -import com.google.common.base.Strings; import com.google.common.collect.Lists; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.util.function.Predicate; -public class ShowBackupCommandTest { +public class ShowBackupCommandTest extends TestWithFeService { + @Override + protected void runBeforeAll() throws Exception { + createDatabase("test"); + connectContext.setDatabase("test"); + } + + @Override + public void createTable(String sql) throws Exception { + LogicalPlan plan = new NereidsParser().parseSingle(sql); + Assertions.assertTrue(plan instanceof CreateTableCommand); + ((CreateTableCommand) plan).run(connectContext, null); + } + @Test - void testGetSnapshotPredicate() throws AnalysisException { - String snapshotName = "mysql_edu_mall_mall_source_occupy_1741669209_1741669211"; - Predicate predicate = CaseSensibility.LABEL.getCaseSensibility() - ? label -> label.equals(snapshotName) : label -> label.equalsIgnoreCase(snapshotName); - Assertions.assertTrue(predicate.test(snapshotName)); + void testGetSnapshotPredicate() throws AnalysisException, UserException { + // test where is null + ShowBackupCommand bc = new ShowBackupCommand("", null); + Predicate re = bc.getSnapshotPredicate(); + Assertions.assertTrue(re.test("test")); + + // test isAccurateMatch = true + Expression where = new EqualTo(new UnboundSlot(Lists.newArrayList("snapshotname")), + new StringLiteral("mysql_edu")); + bc = new ShowBackupCommand("test", where); + bc.validate(connectContext); // set isAccurateMatch = true + Predicate re1 = bc.getSnapshotPredicate(); + Assertions.assertTrue(re1.test("mysql_EDU")); + + // test isAccurateMatch = false + Expression where1 = new Like(new UnboundSlot(Lists.newArrayList("snapshotname")), + new StringLiteral("mysql_edu%")); + bc = new ShowBackupCommand("", where1); + bc.validate(connectContext); + Predicate re2 = bc.getSnapshotPredicate(); + Assertions.assertTrue(re2.test("mysql_edu%")); } @Test void testValidate() throws UserException { - Expression equalTo = new EqualTo(new UnboundSlot(Lists.newArrayList("snapshotname")), - new VarcharLiteral("mysql_edu_mall_mall_source_occupy_1741669209_174166921")); - String leftKey = ((UnboundSlot) equalTo.child(0)).getName(); - Assertions.assertEquals("snapshotname", leftKey); - - Expression like = new Like(new UnboundSlot(Lists.newArrayList("snapshotname")), - new VarcharLiteral("mysql_edu_mall_%")); - String snapshotNameLike = ((StringLikeLiteral) like.child(1)).getStringValue(); - Assertions.assertFalse(Strings.isNullOrEmpty(snapshotNameLike)); + // test No database selected + ShowBackupCommand bc = new ShowBackupCommand("", null); + ShowBackupCommand finalBc = bc; + connectContext.setDatabase(""); + Assertions.assertThrows(AnalysisException.class, () -> finalBc.validate(connectContext)); + connectContext.setDatabase("test"); // reset database + + // test where is null + bc = new ShowBackupCommand("test_db", null); + Assertions.assertTrue(bc.validate(connectContext)); + + // test where is not Like and where is not EqualTo + bc = new ShowBackupCommand("test_db", new LessThan(new UnboundSlot(Lists.newArrayList("snapshotname")), + new VarcharLiteral("mysql_edu_mall_mall_source_occupy_1741669209_174166921"))); + Assertions.assertFalse(bc.validate(connectContext)); + + // test left key is not snapshotname + bc = new ShowBackupCommand("test_db", new EqualTo(new UnboundSlot(Lists.newArrayList("notsnapshotname")), + new VarcharLiteral("mysql_edu_mall_mall_source_occupy_1741669209_174166921"))); + Assertions.assertFalse(bc.validate(connectContext)); + + // test right key is StringLikeLiteral, class is EqualTo + bc = new ShowBackupCommand("test_db", new EqualTo(new UnboundSlot(Lists.newArrayList("snapshotname")), + new StringLiteral("mysql_edu%"))); + Assertions.assertTrue(bc.validate(connectContext)); + + // test right key is StringLikeLiteral, class is Like + bc = new ShowBackupCommand("test_db", new Like(new UnboundSlot(Lists.newArrayList("snapshotname")), + new StringLiteral("mysql_edu%"))); + Assertions.assertTrue(bc.validate(connectContext)); + + // test right key is StringLikeLiteral but value is empty, class is Like, + bc = new ShowBackupCommand("test_db", new Like(new UnboundSlot(Lists.newArrayList("snapshotname")), + new StringLiteral(""))); + Assertions.assertFalse(bc.validate(connectContext)); } }