diff --git a/docs/en/docs/sql-manual/sql-reference/Show-Statements/SHOW-BACKUP.md b/docs/en/docs/sql-manual/sql-reference/Show-Statements/SHOW-BACKUP.md index 72c4512656..d72b7a07fd 100644 --- a/docs/en/docs/sql-manual/sql-reference/Show-Statements/SHOW-BACKUP.md +++ b/docs/en/docs/sql-manual/sql-reference/Show-Statements/SHOW-BACKUP.md @@ -38,32 +38,33 @@ grammar: ```sql SHOW BACKUP [FROM db_name] + [WHERE SnapshotName ( LIKE | = ) 'snapshot name'] ```` illustrate: -1. Only the most recent BACKUP task is saved in Doris. -1. The meaning of each column is as follows: - JobId: Unique job id - SnapshotName: The name of the backup - DbName: belongs to the database - State: current stage - PENDING: The initial state after submitting the job - SNAPSHOTING: Executing snapshot - UPLOAD_SNAPSHOT: Snapshot completed, ready to upload - UPLOADING: Snapshot uploading - SAVE_META: Save job meta information to a local file - UPLOAD_INFO: Upload job meta information - FINISHED: The job was successful - CANCELLED: Job failed - BackupObjs: Backed up tables and partitions - CreateTime: task submission time - SnapshotFinishedTime: Snapshot completion time - UploadFinishedTime: Snapshot upload completion time - FinishedTime: Job finish time - UnfinishedTasks: Displays unfinished subtask ids during SNAPSHOTING and UPLOADING stages - Status: If the job fails, display the failure message - Timeout: Job timeout, in seconds + 1. Only the most recent BACKUP task is saved in Doris. + 2. The meaning of each column is as follows: + - `JobId`: Unique job id + - `SnapshotName`: The name of the backup + - `DbName`: belongs to the database + - `State`: current stage + - `PENDING`: The initial state after submitting the job + - `SNAPSHOTING`: Executing snapshot + - `UPLOAD_SNAPSHOT`: Snapshot completed, ready to upload + - `UPLOADING`: Snapshot uploading + - `SAVE_META`: Save job meta information to a local file + - `UPLOAD_INFO`: Upload job meta information + - `FINISHED`: The job was successful + - `CANCELLED`: Job failed + - `BackupObjs`: Backed up tables and partitions + - `CreateTime`: task submission time + - `SnapshotFinishedTime`: Snapshot completion time + - `UploadFinishedTime`: Snapshot upload completion time + - `FinishedTime`: Job finish time + - `UnfinishedTasks`: Displays unfinished subtask ids during SNAPSHOTING and UPLOADING stages + - `Status`: If the job fails, display the failure message + - `Timeout`: Job timeout, in seconds ### Example diff --git a/docs/zh-CN/docs/sql-manual/sql-reference/Show-Statements/SHOW-BACKUP.md b/docs/zh-CN/docs/sql-manual/sql-reference/Show-Statements/SHOW-BACKUP.md index da6fe59b38..2cb838e9bb 100644 --- a/docs/zh-CN/docs/sql-manual/sql-reference/Show-Statements/SHOW-BACKUP.md +++ b/docs/zh-CN/docs/sql-manual/sql-reference/Show-Statements/SHOW-BACKUP.md @@ -38,6 +38,7 @@ SHOW BACKUP ```sql SHOW BACKUP [FROM db_name] + [WHERE SnapshotName ( LIKE | = ) 'snapshot name' ] ``` 说明: diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowBackupStmt.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowBackupStmt.java index a302a5742b..041345a198 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowBackupStmt.java +++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowBackupStmt.java @@ -136,13 +136,13 @@ public class ShowBackupStmt extends ShowStmt { @Override public String toSql() { StringBuilder builder = new StringBuilder(); - builder.append("SHOW BACKUP"); - if (dbName != null) { - builder.append(" FROM `").append(dbName).append("` "); + builder.append("SHOW BACKUP "); + if (Strings.isNullOrEmpty(dbName)) { + builder.append("FROM `").append(dbName).append("` "); } if (where != null) { - builder.append(where.toSql()); + builder.append("WHERE ").append(where.toSql()); } return builder.toString(); diff --git a/fe/fe-core/src/test/java/org/apache/doris/analysis/ShowBackupStmtTest.java b/fe/fe-core/src/test/java/org/apache/doris/analysis/ShowBackupStmtTest.java new file mode 100644 index 0000000000..6d340e5d6f --- /dev/null +++ b/fe/fe-core/src/test/java/org/apache/doris/analysis/ShowBackupStmtTest.java @@ -0,0 +1,150 @@ +// 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.analysis; + +import org.apache.doris.analysis.LikePredicate.Operator; +import org.apache.doris.catalog.ScalarType; +import org.apache.doris.common.AnalysisException; +import org.apache.doris.mysql.privilege.AccessControllerManager; +import org.apache.doris.mysql.privilege.PrivPredicate; +import org.apache.doris.qe.ConnectContext; +import org.apache.doris.qe.SessionVariable; +import org.apache.doris.qe.ShowResultSetMetaData; + +import mockit.Mock; +import mockit.MockUp; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; + +import java.util.concurrent.atomic.AtomicBoolean; + +public class ShowBackupStmtTest { + private Analyzer analyzer; + private ConnectContext ctx = new ConnectContext(); + + @Before + public void setUp() { + analyzer = AccessTestUtil.fetchAdminAnalyzer(true); + ctx.setSessionVariable(new SessionVariable()); + ctx.setThreadLocalInfo(); + } + + @After + public void tearDown() { + ConnectContext.remove(); + } + + @Test + public void getMetaData() { + ShowBackupStmt stmt = new ShowBackupStmt("", null); + ShowResultSetMetaData result = stmt.getMetaData(); + Assertions.assertEquals(result.getColumnCount(), 14); + result.getColumns().forEach(col -> Assertions.assertEquals(col.getType(), ScalarType.createVarchar(30))); + } + + @Test + public void testNormalAnalyze() throws Exception { + ShowBackupStmt stmt = new ShowBackupStmt("", null); + + AtomicBoolean privilege = new AtomicBoolean(true); + new MockUp() { + @Mock + public boolean checkDbPriv(ConnectContext ctx, String qualifiedDb, PrivPredicate wanted) { + return privilege.get(); + } + }; + + stmt.analyze(analyzer); + Assertions.assertEquals(stmt.toSql(), "SHOW BACKUP "); + + stmt = new ShowBackupStmt("", + new BinaryPredicate(BinaryPredicate.Operator.EQ, new SlotRef(new TableName("a.b.c"), "snapshotname"), + new StringLiteral("FINISHED"))); + stmt.analyze(analyzer); + Assertions.assertEquals(stmt.toSql(), "SHOW BACKUP WHERE `a`.`b`.`c`.`snapshotname` = 'FINISHED'"); + + stmt = new ShowBackupStmt("", + new LikePredicate(Operator.LIKE, new SlotRef(new TableName("a.b.c"), "snapshotname"), + new StringLiteral("%.b.%"))); + stmt.analyze(analyzer); + Assertions.assertEquals(stmt.toSql(), "SHOW BACKUP WHERE `a`.`b`.`c`.`snapshotname` LIKE '%.b.%'"); + } + + @Test + public void testExceptionAnalyze() { + AtomicBoolean privilege = new AtomicBoolean(false); + new MockUp() { + @Mock + public boolean checkDbPriv(ConnectContext ctx, String qualifiedDb, PrivPredicate wanted) { + return privilege.get(); + } + }; + + /* when no privilege */ { + ShowBackupStmt stmt = new ShowBackupStmt("", null); + Assertions.assertThrows(AnalysisException.class, () -> stmt.analyze(analyzer)); + } + + /* where clause not supported */ { + privilege.set(true); + ShowBackupStmt stmt = new ShowBackupStmt("test", new BoolLiteral(true)); + Assertions.assertThrows(AnalysisException.class, () -> stmt.analyze(analyzer)); + } + + /* where clause op not supported */ { + ShowBackupStmt stmt = new ShowBackupStmt("test", + new LikePredicate(Operator.REGEXP, new SlotRef(new TableName(), "STATE_NAME"), + new StringLiteral("%.b.%"))); + Assertions.assertThrows(AnalysisException.class, () -> stmt.analyze(analyzer)); + } + + /* where clause op not supported*/ { + ShowBackupStmt stmt = new ShowBackupStmt("test", new BinaryPredicate( + BinaryPredicate.Operator.GE, new SlotRef(new TableName(), "STATE_NAME"), + new StringLiteral("blah"))); + Assertions.assertThrows(AnalysisException.class, () -> stmt.analyze(analyzer)); + } + + /*where clause op not supported*/ { + ShowBackupStmt stmt = new ShowBackupStmt("test", new BinaryPredicate( + BinaryPredicate.Operator.EQ, new SlotRef(new TableName(), "STATE_NAME"), + new StringLiteral("blah"))); + Assertions.assertThrows(AnalysisException.class, () -> stmt.analyze(analyzer)); + } + + /* where clause op type(left) not supported */ { + ShowBackupStmt stmt = new ShowBackupStmt("test", new BinaryPredicate( + BinaryPredicate.Operator.EQ, new IntLiteral(1), new IntLiteral(1))); + Assertions.assertThrows(AnalysisException.class, () -> stmt.analyze(analyzer)); + } + + /* where clause op type(right) not supported */ { + ShowBackupStmt stmt = new ShowBackupStmt("test", new BinaryPredicate( + BinaryPredicate.Operator.EQ, new SlotRef(new TableName(), "snapshotname"), new IntLiteral(1))); + Assertions.assertThrows(AnalysisException.class, () -> stmt.analyze(analyzer)); + } + + /* where clause operand is empty */ { + ShowBackupStmt stmt = new ShowBackupStmt("test", new BinaryPredicate( + BinaryPredicate.Operator.EQ, new SlotRef(new TableName(), "snapshotname"), new StringLiteral(""))); + Assertions.assertThrows(AnalysisException.class, () -> stmt.analyze(analyzer)); + } + } +}