|
| 1 | +// Licensed to the Apache Software Foundation (ASF) under one |
| 2 | +// or more contributor license agreements. See the NOTICE file |
| 3 | +// distributed with this work for additional information |
| 4 | +// regarding copyright ownership. The ASF licenses this file |
| 5 | +// to you under the Apache License, Version 2.0 (the |
| 6 | +// "License"); you may not use this file except in compliance |
| 7 | +// with the License. You may obtain a copy of the License at |
| 8 | +// |
| 9 | +// http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | +// |
| 11 | +// Unless required by applicable law or agreed to in writing, |
| 12 | +// software distributed under the License is distributed on an |
| 13 | +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| 14 | +// KIND, either express or implied. See the License for the |
| 15 | +// specific language governing permissions and limitations |
| 16 | +// under the License. |
| 17 | + |
| 18 | +package org.apache.doris.nereids.trees.plans.commands; |
| 19 | + |
| 20 | +import org.apache.doris.backup.AbstractJob; |
| 21 | +import org.apache.doris.backup.BackupJob; |
| 22 | +import org.apache.doris.catalog.Column; |
| 23 | +import org.apache.doris.catalog.DatabaseIf; |
| 24 | +import org.apache.doris.catalog.Env; |
| 25 | +import org.apache.doris.catalog.ScalarType; |
| 26 | +import org.apache.doris.common.AnalysisException; |
| 27 | +import org.apache.doris.common.CaseSensibility; |
| 28 | +import org.apache.doris.common.ErrorCode; |
| 29 | +import org.apache.doris.common.ErrorReport; |
| 30 | +import org.apache.doris.common.PatternMatcher; |
| 31 | +import org.apache.doris.common.PatternMatcherWrapper; |
| 32 | +import org.apache.doris.common.UserException; |
| 33 | +import org.apache.doris.datasource.InternalCatalog; |
| 34 | +import org.apache.doris.mysql.privilege.PrivPredicate; |
| 35 | +import org.apache.doris.nereids.analyzer.UnboundSlot; |
| 36 | +import org.apache.doris.nereids.trees.expressions.EqualTo; |
| 37 | +import org.apache.doris.nereids.trees.expressions.Expression; |
| 38 | +import org.apache.doris.nereids.trees.expressions.Like; |
| 39 | +import org.apache.doris.nereids.trees.expressions.literal.StringLikeLiteral; |
| 40 | +import org.apache.doris.nereids.trees.plans.PlanType; |
| 41 | +import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor; |
| 42 | +import org.apache.doris.qe.ConnectContext; |
| 43 | +import org.apache.doris.qe.ShowResultSet; |
| 44 | +import org.apache.doris.qe.ShowResultSetMetaData; |
| 45 | +import org.apache.doris.qe.StmtExecutor; |
| 46 | + |
| 47 | +import com.google.common.base.Strings; |
| 48 | +import com.google.common.collect.ImmutableList; |
| 49 | + |
| 50 | +import java.util.List; |
| 51 | +import java.util.function.Predicate; |
| 52 | +import java.util.stream.Collectors; |
| 53 | + |
| 54 | +/** |
| 55 | + * show backup command |
| 56 | + */ |
| 57 | +public class ShowBackupCommand extends ShowCommand { |
| 58 | + public static final ImmutableList<String> TITLE_NAMES = new ImmutableList.Builder<String>() |
| 59 | + .add("JobId").add("SnapshotName").add("DbName").add("State").add("BackupObjs").add("CreateTime") |
| 60 | + .add("SnapshotFinishedTime").add("UploadFinishedTime").add("FinishedTime").add("UnfinishedTasks") |
| 61 | + .add("Progress").add("TaskErrMsg").add("Status").add("Timeout") |
| 62 | + .build(); |
| 63 | + |
| 64 | + private String dbName; |
| 65 | + private Expression where; |
| 66 | + private boolean isAccurateMatch; |
| 67 | + private String snapshotName; |
| 68 | + |
| 69 | + /** |
| 70 | + * constructor |
| 71 | + */ |
| 72 | + public ShowBackupCommand(String dbName, Expression where) { |
| 73 | + super(PlanType.SHOW_BACKUP_COMMAND); |
| 74 | + this.dbName = dbName; |
| 75 | + this.where = where; |
| 76 | + } |
| 77 | + |
| 78 | + public String getDbName() { |
| 79 | + return dbName; |
| 80 | + } |
| 81 | + |
| 82 | + /** |
| 83 | + * get metadata |
| 84 | + */ |
| 85 | + public ShowResultSetMetaData getMetaData() { |
| 86 | + ShowResultSetMetaData.Builder builder = ShowResultSetMetaData.builder(); |
| 87 | + for (String title : TITLE_NAMES) { |
| 88 | + builder.addColumn(new Column(title, ScalarType.createVarchar(30))); |
| 89 | + } |
| 90 | + return builder.build(); |
| 91 | + } |
| 92 | + |
| 93 | + /** |
| 94 | + * get label predicate for show backup |
| 95 | + */ |
| 96 | + public Predicate<String> getSnapshotPredicate() throws AnalysisException { |
| 97 | + if (null == where) { |
| 98 | + return label -> true; |
| 99 | + } |
| 100 | + if (isAccurateMatch) { |
| 101 | + return CaseSensibility.LABEL.getCaseSensibility() |
| 102 | + ? label -> label.equals(snapshotName) : label -> label.equalsIgnoreCase(snapshotName); |
| 103 | + } else { |
| 104 | + PatternMatcher patternMatcher = PatternMatcherWrapper.createMysqlPattern( |
| 105 | + snapshotName, CaseSensibility.LABEL.getCaseSensibility()); |
| 106 | + return patternMatcher::match; |
| 107 | + } |
| 108 | + } |
| 109 | + |
| 110 | + /** |
| 111 | + * validate |
| 112 | + */ |
| 113 | + private boolean validate(ConnectContext ctx) throws UserException { |
| 114 | + if (Strings.isNullOrEmpty(dbName)) { |
| 115 | + dbName = ctx.getDatabase(); |
| 116 | + if (Strings.isNullOrEmpty(dbName)) { |
| 117 | + throw new AnalysisException("No database selected"); |
| 118 | + } |
| 119 | + } |
| 120 | + |
| 121 | + // check auth |
| 122 | + if (!Env.getCurrentEnv().getAccessManager() |
| 123 | + .checkDbPriv(ConnectContext.get(), InternalCatalog.INTERNAL_CATALOG_NAME, dbName, PrivPredicate.LOAD)) { |
| 124 | + ErrorReport.reportAnalysisException(ErrorCode.ERR_DBACCESS_DENIED_ERROR, |
| 125 | + ConnectContext.get().getQualifiedUser(), dbName); |
| 126 | + } |
| 127 | + |
| 128 | + // SQL may be like : show backup from your_db_name; there is no where clause. |
| 129 | + if (where == null) { |
| 130 | + return true; |
| 131 | + } |
| 132 | + |
| 133 | + if (!(where instanceof Like) && !(where instanceof EqualTo)) { |
| 134 | + return false; |
| 135 | + } |
| 136 | + |
| 137 | + if (where instanceof EqualTo) { |
| 138 | + isAccurateMatch = true; |
| 139 | + } |
| 140 | + |
| 141 | + // left child |
| 142 | + if (!(where.child(0) instanceof UnboundSlot)) { |
| 143 | + return false; |
| 144 | + } |
| 145 | + String leftKey = ((UnboundSlot) where.child(0)).getName(); |
| 146 | + if (!"snapshotname".equalsIgnoreCase(leftKey)) { |
| 147 | + return false; |
| 148 | + } |
| 149 | + |
| 150 | + // right child |
| 151 | + if (!(where.child(1) instanceof StringLikeLiteral)) { |
| 152 | + return false; |
| 153 | + } |
| 154 | + snapshotName = ((StringLikeLiteral) where.child(1)).getStringValue(); |
| 155 | + if (Strings.isNullOrEmpty(snapshotName)) { |
| 156 | + return false; |
| 157 | + } |
| 158 | + |
| 159 | + return true; |
| 160 | + } |
| 161 | + |
| 162 | + /** |
| 163 | + * handle show backup |
| 164 | + */ |
| 165 | + private ShowResultSet handleShowBackup(ConnectContext ctx, StmtExecutor executor) throws Exception { |
| 166 | + boolean valid = validate(ctx); |
| 167 | + if (!valid) { |
| 168 | + throw new AnalysisException("Where clause should like: SnapshotName = \"your_snapshot_name\", " |
| 169 | + + " or SnapshotName LIKE \"matcher\""); |
| 170 | + } |
| 171 | + |
| 172 | + DatabaseIf database = ctx.getCurrentCatalog().getDbOrAnalysisException(dbName); |
| 173 | + List<AbstractJob> jobs = Env.getCurrentEnv().getBackupHandler() |
| 174 | + .getJobs(database.getId(), getSnapshotPredicate()); |
| 175 | + List<BackupJob> backupJobs = jobs.stream().filter(job -> job instanceof BackupJob) |
| 176 | + .map(job -> (BackupJob) job).collect(Collectors.toList()); |
| 177 | + List<List<String>> infos = backupJobs.stream().map(BackupJob::getInfo).collect(Collectors.toList()); |
| 178 | + |
| 179 | + return new ShowResultSet(getMetaData(), infos); |
| 180 | + } |
| 181 | + |
| 182 | + @Override |
| 183 | + public ShowResultSet doRun(ConnectContext ctx, StmtExecutor executor) throws Exception { |
| 184 | + return handleShowBackup(ctx, executor); |
| 185 | + } |
| 186 | + |
| 187 | + @Override |
| 188 | + public <R, C> R accept(PlanVisitor<R, C> visitor, C context) { |
| 189 | + return visitor.visitShowBackupCommand(this, context); |
| 190 | + } |
| 191 | +} |
0 commit comments