Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Enhancement](nereids)support show restore in nereids #48784

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ supportedShowStatement
(LIKE STRING_LITERAL)? #showEncryptKeys
| SHOW BRIEF? CREATE TABLE name=multipartIdentifier #showCreateTable
| SHOW FULL? PROCESSLIST #showProcessList
| SHOW BRIEF? RESTORE ((FROM | IN) database=multipartIdentifier)? wildWhere? #showRestore
| SHOW ROLES #showRoles
| SHOW PARTITION partitionId=INTEGER_VALUE #showPartitionId
| SHOW PRIVILEGES #showPrivileges
Expand Down Expand Up @@ -402,7 +403,6 @@ unsupportedShowStatement
| 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
| SHOW SNAPSHOT ON repo=identifier wildWhere? #showSnapshot
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,7 @@
import org.apache.doris.nereids.DorisParser.ShowQueuedAnalyzeJobsContext;
import org.apache.doris.nereids.DorisParser.ShowReplicaDistributionContext;
import org.apache.doris.nereids.DorisParser.ShowRepositoriesContext;
import org.apache.doris.nereids.DorisParser.ShowRestoreContext;
import org.apache.doris.nereids.DorisParser.ShowRolesContext;
import org.apache.doris.nereids.DorisParser.ShowSmallFilesContext;
import org.apache.doris.nereids.DorisParser.ShowSqlBlockRuleContext;
Expand Down Expand Up @@ -635,6 +636,7 @@
import org.apache.doris.nereids.trees.plans.commands.ShowQueuedAnalyzeJobsCommand;
import org.apache.doris.nereids.trees.plans.commands.ShowReplicaDistributionCommand;
import org.apache.doris.nereids.trees.plans.commands.ShowRepositoriesCommand;
import org.apache.doris.nereids.trees.plans.commands.ShowRestoreCommand;
import org.apache.doris.nereids.trees.plans.commands.ShowRolesCommand;
import org.apache.doris.nereids.trees.plans.commands.ShowSmallFilesCommand;
import org.apache.doris.nereids.trees.plans.commands.ShowSqlBlockRuleCommand;
Expand Down Expand Up @@ -5026,6 +5028,20 @@ public LogicalPlan visitShowRepositories(ShowRepositoriesContext ctx) {
return new ShowRepositoriesCommand();
}

@Override
public LogicalPlan visitShowRestore(ShowRestoreContext ctx) {
String dbName = null;
Expression wildWhere = null;
if (ctx.database != null) {
List<String> nameParts = visitMultipartIdentifier(ctx.database);
dbName = nameParts.get(0);
}
if (ctx.wildWhere() != null) {
wildWhere = getWildWhere(ctx.wildWhere());
}
return new ShowRestoreCommand(dbName, wildWhere, ctx.BRIEF() != null);
}

@Override
public LogicalPlan visitShowRoles(ShowRolesContext ctx) {
return new ShowRolesCommand();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ public enum PlanType {
SHOW_REPLICA_DISTRIBUTION_COMMAND,
SHOW_REPLICA_STATUS_COMMAND,
SHOW_REPOSITORIES_COMMAND,
SHOW_RESTORE_COMMAND,
SHOW_ROLE_COMMAND,
SHOW_SMALL_FILES_COMMAND,
SHOW_STAGES_COMMAND,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
// 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.RestoreJob;
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 restore command
*/
public class ShowRestoreCommand extends ShowCommand {
public static final ImmutableList<String> TITLE_NAMES = new ImmutableList.Builder<String>()
.add("JobId").add("Label").add("Timestamp").add("DbName").add("State")
.add("AllowLoad").add("ReplicationNum").add("ReplicaAllocation").add("ReserveReplica")
.add("ReserveDynamicPartitionEnable").add("RestoreObjs").add("CreateTime").add("MetaPreparedTime")
.add("SnapshotFinishedTime").add("DownloadFinishedTime").add("FinishedTime").add("UnfinishedTasks")
.add("Progress").add("TaskErrMsg").add("Status").add("Timeout")
.build();
public static final ImmutableList<String> BRIEF_TITLE_NAMES = new ImmutableList.Builder<String>()
.add("JobId").add("Label").add("Timestamp").add("DbName").add("State")
.add("AllowLoad").add("ReplicationNum").add("ReplicaAllocation").add("ReserveReplica")
.add("ReserveDynamicPartitionEnable").add("CreateTime").add("MetaPreparedTime")
.add("SnapshotFinishedTime").add("DownloadFinishedTime").add("FinishedTime").add("UnfinishedTasks")
.add("Status").add("Timeout")
.build();

private String dbName;
private Expression where;
private String labelValue;
private boolean isAccurateMatch;
private boolean needBriefResult;

/**
* constructor
*/
public ShowRestoreCommand(String dbName, Expression where, boolean needBriefResult) {
super(PlanType.SHOW_RESTORE_COMMAND);
this.dbName = dbName;
this.where = where;
this.needBriefResult = needBriefResult;
}

/**
* constructor
*/
public ShowRestoreCommand(String dbName, Expression where) {
super(PlanType.SHOW_RESTORE_COMMAND);
this.dbName = dbName;
this.where = where;
this.needBriefResult = false;
}

public String getDbName() {
return dbName;
}

public boolean isNeedBriefResult() {
return needBriefResult;
}

/**
* get meta for show restore
*/
public ShowResultSetMetaData getMetaData() {
ShowResultSetMetaData.Builder builder = ShowResultSetMetaData.builder();
List<String> titleNames = needBriefResult ? BRIEF_TITLE_NAMES : TITLE_NAMES;
for (String title : titleNames) {
builder.addColumn(new Column(title, ScalarType.createVarchar(30)));
}
return builder.build();
}

/**
* get label predicate for show restore
*/
public Predicate<String> getLabelPredicate() throws AnalysisException {
if (null == where) {
return label -> true;
}
if (isAccurateMatch) {
return CaseSensibility.LABEL.getCaseSensibility()
? label -> label.equals(labelValue) : label -> label.equalsIgnoreCase(labelValue);
} else {
PatternMatcher patternMatcher = PatternMatcherWrapper.createMysqlPattern(
labelValue, 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 restore 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 (!"label".equalsIgnoreCase(leftKey)) {
return false;
}

// right child
if (!(where.child(1) instanceof StringLikeLiteral)) {
return false;
}
labelValue = ((StringLikeLiteral) where.child(1)).getStringValue();
if (Strings.isNullOrEmpty(labelValue)) {
return false;
}

return true;
}

/**
* handle show restore
*/
private ShowResultSet handleShowRestore(ConnectContext ctx, StmtExecutor executor) throws Exception {
boolean valid = validate(ctx);
if (!valid) {
throw new AnalysisException("Where clause should like: LABEL = \"your_label_name\", "
+ " or LABEL LIKE \"matcher\"");
}

Env env = ctx.getEnv();
DatabaseIf database = ctx.getCurrentCatalog().getDbOrAnalysisException(dbName);
List<AbstractJob> jobs = env.getBackupHandler().getJobs(database.getId(), getLabelPredicate());
List<RestoreJob> restoreJobs = jobs.stream().filter(job -> job instanceof RestoreJob)
.map(job -> (RestoreJob) job).collect(Collectors.toList());
List<List<String>> infos;
if (needBriefResult) {
infos = restoreJobs.stream().map(RestoreJob::getBriefInfo).collect(Collectors.toList());
} else {
infos = restoreJobs.stream().map(RestoreJob::getFullInfo).collect(Collectors.toList());
}

return new ShowResultSet(getMetaData(), infos);
}

@Override
public ShowResultSet doRun(ConnectContext ctx, StmtExecutor executor) throws Exception {
return handleShowRestore(ctx, executor);
}

@Override
public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
return visitor.visitShowRestoreCommand(this, context);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@
import org.apache.doris.nereids.trees.plans.commands.ShowQueuedAnalyzeJobsCommand;
import org.apache.doris.nereids.trees.plans.commands.ShowReplicaDistributionCommand;
import org.apache.doris.nereids.trees.plans.commands.ShowRepositoriesCommand;
import org.apache.doris.nereids.trees.plans.commands.ShowRestoreCommand;
import org.apache.doris.nereids.trees.plans.commands.ShowRolesCommand;
import org.apache.doris.nereids.trees.plans.commands.ShowSmallFilesCommand;
import org.apache.doris.nereids.trees.plans.commands.ShowSqlBlockRuleCommand;
Expand Down Expand Up @@ -538,6 +539,10 @@ default R visitShowRepositoriesCommand(ShowRepositoriesCommand showRepositoriesC
return visitCommand(showRepositoriesCommand, context);
}

default R visitShowRestoreCommand(ShowRestoreCommand showRestoreCommand, C context) {
return visitCommand(showRestoreCommand, context);
}

default R visitShowRolesCommand(ShowRolesCommand showRolesCommand, C context) {
return visitCommand(showRolesCommand, context);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ excludeSuites = "000_the_start_sentinel_do_not_touch," + // keep this line as th
"set_replica_status," + // not a case for cloud mode, no need to run
"test_be_inject_publish_txn_fail," + // not a case for cloud mode, no need to run
"test_dump_image," +
"test_nereids_show_restore," +
"test_index_failure_injection," +
"test_information_schema_external," +
"test_profile," +
Expand Down
Loading