Skip to content
Merged
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
37 changes: 32 additions & 5 deletions fe/fe-core/src/main/cup/sql_parser.cup
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,8 @@ terminal String KW_ADD, KW_ADMIN, KW_AFTER, KW_AGGREGATE, KW_ALL, KW_ALTER, KW_A
KW_CONFIG, KW_CONNECTION, KW_CONNECTION_ID, KW_CONSISTENT, KW_CONVERT, KW_COUNT, KW_CREATE, KW_CROSS, KW_CUBE, KW_CURRENT, KW_CURRENT_USER,
KW_DATA, KW_DATABASE, KW_DATABASES, KW_DATE, KW_DATETIME, KW_DAY, KW_DECIMAL, KW_DECOMMISSION, KW_DEFAULT, KW_DESC, KW_DESCRIBE,
KW_DELETE, KW_DISTINCT, KW_DISTINCTPC, KW_DISTINCTPCSA, KW_DISTRIBUTED, KW_DISTRIBUTION, KW_DYNAMIC, KW_BUCKETS, KW_DIV, KW_DOUBLE, KW_DROP, KW_DROPP, KW_DUPLICATE,
KW_ELSE, KW_ENABLE, KW_END, KW_ENGINE, KW_ENGINES, KW_ENTER, KW_ERRORS, KW_EVENTS, KW_EXCEPT, KW_EXISTS, KW_EXPORT, KW_EXTERNAL, KW_EXTRACT,
KW_ELSE, KW_ENABLE, KW_END, KW_ENGINE, KW_ENGINES, KW_ENTER, KW_ERRORS, KW_EVENTS, KW_EXCEPT, KW_EXCLUDE,
KW_EXISTS, KW_EXPORT, KW_EXTERNAL, KW_EXTRACT,
KW_FALSE, KW_FEATURE, KW_FOLLOWER, KW_FOLLOWING, KW_FREE, KW_FROM, KW_FILE, KW_FIRST, KW_FLOAT, KW_FOR, KW_FORCE, KW_FORMAT, KW_FRONTEND, KW_FRONTENDS, KW_FULL, KW_FUNCTION, KW_FUNCTIONS,
KW_GLOBAL, KW_GRANT, KW_GRANTS, KW_GROUP, KW_GROUPING,
KW_HASH, KW_HAVING, KW_HELP,KW_HLL, KW_HLL_UNION, KW_HOUR, KW_HUB,
Expand Down Expand Up @@ -326,6 +327,8 @@ nonterminal InsertTarget insert_target;
nonterminal InsertSource insert_source;

nonterminal BackupStmt backup_stmt;
nonterminal AbstractBackupTableRefClause opt_backup_table_ref_list;
nonterminal Boolean backup_exclude_or_not;
nonterminal RestoreStmt restore_stmt;

nonterminal SelectList select_clause, select_list, select_sublist;
Expand Down Expand Up @@ -2943,21 +2946,43 @@ insert_source ::=
backup_stmt ::=
KW_BACKUP KW_SNAPSHOT job_label:label
KW_TO ident:repoName
KW_ON LPAREN base_table_ref_list:tbls RPAREN
opt_backup_table_ref_list:tblRefClause
opt_properties:properties
{:
RESULT = new BackupStmt(label, repoName, tbls, properties);
RESULT = new BackupStmt(label, repoName, tblRefClause, properties);
:}
;

opt_backup_table_ref_list ::=
backup_exclude_or_not:isExclude LPAREN base_table_ref_list:tbls RPAREN
{:
RESULT = new AbstractBackupTableRefClause(isExclude, tbls);
:}
| /* empty */
{:
RESULT = null;
:}
;

backup_exclude_or_not ::=
KW_ON
{:
RESULT = false;
:}
| KW_EXCLUDE
{:
RESULT = true;
:}
;

// Restore statement
restore_stmt ::=
KW_RESTORE KW_SNAPSHOT job_label:label
KW_FROM ident:repoName
KW_ON LPAREN base_table_ref_list:tbls RPAREN
opt_backup_table_ref_list:tblRefClause
opt_properties:properties
{:
RESULT = new RestoreStmt(label, repoName, tbls, properties);
RESULT = new RestoreStmt(label, repoName, tblRefClause, properties);
:}
;

Expand Down Expand Up @@ -4677,6 +4702,8 @@ keyword ::=
{: RESULT = id; :}
| KW_ERRORS:id
{: RESULT = id; :}
| KW_EXCLUDE:id
{: RESULT = id; :}
| KW_EVENTS:id
{: RESULT = id; :}
| KW_EXTERNAL:id
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,12 @@
import org.apache.doris.mysql.privilege.PrivPredicate;
import org.apache.doris.qe.ConnectContext;

import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.List;
import java.util.Map;

public class AbstractBackupStmt extends DdlStmt {
Expand All @@ -45,69 +42,55 @@ public class AbstractBackupStmt extends DdlStmt {

protected LabelName labelName;
protected String repoName;
protected List<TableRef> tblRefs;
protected AbstractBackupTableRefClause abstractBackupTableRefClause;
protected Map<String, String> properties;

protected long timeoutMs;

public AbstractBackupStmt(LabelName labelName, String repoName, List<TableRef> tableRefs,
Map<String, String> properties) {
public AbstractBackupStmt(LabelName labelName, String repoName, AbstractBackupTableRefClause abstractBackupTableRefClause,
Map<String, String> properties) {
this.labelName = labelName;
this.repoName = repoName;
this.tblRefs = tableRefs;
if (this.tblRefs == null) {
this.tblRefs = Lists.newArrayList();
}

this.abstractBackupTableRefClause = abstractBackupTableRefClause;
this.properties = properties == null ? Maps.newHashMap() : properties;
}

@Override
public void analyze(Analyzer analyzer) throws AnalysisException, UserException {
public void analyze(Analyzer analyzer) throws UserException {
labelName.analyze(analyzer);

// user need database level privilege(not table level), because when doing restore operation,
// the restore table may be newly created, so we can not judge its privileges.
if (!Catalog.getCurrentCatalog().getAuth().checkDbPriv(ConnectContext.get(),
labelName.getDbName(), PrivPredicate.LOAD)) {
ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, "LOAD");
}

checkAndNormalizeBackupObjs();

analyzeTableRefClause();
analyzeProperties();
}

private void checkAndNormalizeBackupObjs() throws AnalysisException {
for (TableRef tblRef : tblRefs) {
private void analyzeTableRefClause() throws UserException {
if (abstractBackupTableRefClause == null) {
return;
}
checkTableRefWithoutDatabase();
abstractBackupTableRefClause.analyze(analyzer);
customAnalyzeTableRefClause();
}

private void checkTableRefWithoutDatabase() throws AnalysisException {
for (TableRef tblRef : abstractBackupTableRefClause.getTableRefList()) {
if (!Strings.isNullOrEmpty(tblRef.getName().getDb())) {
throw new AnalysisException("Cannot specify database name on backup objects: "
+ tblRef.getName().getTbl() + ". Specify database name before label");
}
// set db name because we can not persist empty string when writing bdbje log
tblRef.getName().setDb(labelName.getDbName());
}

// normalize
// table name => table ref
Map<String, TableRef> tblPartsMap = Maps.newTreeMap(String.CASE_INSENSITIVE_ORDER);
for (TableRef tblRef : tblRefs) {
String tblName = tblRef.getName().getTbl();

if (!tblPartsMap.containsKey(tblName)) {
tblPartsMap.put(tblName, tblRef);
} else {
throw new AnalysisException("Duplicated restore table: " + tblName);
}
}

// update table ref
tblRefs.clear();
for (TableRef tableRef : tblPartsMap.values()) {
tblRefs.add(tableRef);
}
}

LOG.debug("table refs after normalization: \n{}", Joiner.on("\n").join(tblRefs));
protected void customAnalyzeTableRefClause() throws AnalysisException {
}

protected void analyzeProperties() throws AnalysisException {
Expand All @@ -117,8 +100,7 @@ protected void analyzeProperties() throws AnalysisException {
timeoutMs = Long.valueOf(properties.get(PROP_TIMEOUT));
} catch (NumberFormatException e) {
ErrorReport.reportAnalysisException(ErrorCode.ERR_COMMON_ERROR,
"Invalid timeout format: "
+ properties.get(PROP_TIMEOUT));
"Invalid timeout format: " + properties.get(PROP_TIMEOUT));
}

if (timeoutMs * 1000 < MIN_TIMEOUT_MS) {
Expand Down Expand Up @@ -148,8 +130,8 @@ public String getRepoName() {
return repoName;
}

public List<TableRef> getTableRefs() {
return tblRefs;
public AbstractBackupTableRefClause getAbstractBackupTableRefClause() {
return abstractBackupTableRefClause;
}

public Map<String, String> getProperties() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// 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.common.AnalysisException;
import org.apache.doris.common.UserException;

import com.google.common.base.Joiner;
import com.google.common.collect.Maps;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.List;
import java.util.Map;

public class AbstractBackupTableRefClause implements ParseNode {
private static final Logger LOG = LogManager.getLogger(AbstractBackupTableRefClause.class);

private boolean isExclude;
private List<TableRef> tableRefList;

public AbstractBackupTableRefClause(boolean isExclude, List<TableRef> tableRefList) {
this.isExclude = isExclude;
this.tableRefList = tableRefList;
}

@Override
public void analyze(Analyzer analyzer) throws UserException {
// normalize
// table name => table ref
Map<String, TableRef> tblPartsMap = Maps.newTreeMap(String.CASE_INSENSITIVE_ORDER);
for (TableRef tblRef : tableRefList) {
String tblName = tblRef.getName().getTbl();
if (!tblPartsMap.containsKey(tblName)) {
tblPartsMap.put(tblName, tblRef);
} else {
throw new AnalysisException("Duplicated restore table: " + tblName);
}
}

// update table ref
tableRefList.clear();
for (TableRef tableRef : tblPartsMap.values()) {
tableRefList.add(tableRef);
}

LOG.debug("table refs after normalization: {}", Joiner.on(",").join(tableRefList));
}

public boolean isExclude() {
return isExclude;
}

public List<TableRef> getTableRefList() {
return tableRefList;
}

@Override
public String toSql() {
StringBuilder sb = new StringBuilder();
if (isExclude) {
sb.append("EXCLUDE ");
} else {
sb.append("ON ");
}
sb.append("\n(");
sb.append(Joiner.on(",\n").join(tableRefList));
sb.append("\n)");
return sb.toString();
}
}
Loading