blob: c647a8777575059a1e5e02c663d1753124de014e [file] [log] [blame]
// 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.impala.analysis;
import java.util.List;
import com.google.common.annotations.VisibleForTesting;
import org.apache.impala.authorization.Privilege;
import org.apache.impala.authorization.User;
import org.apache.impala.catalog.FeDb;
import org.apache.impala.catalog.FeTable;
import org.apache.impala.catalog.TableLoadingException;
import org.apache.impala.common.AnalysisException;
import org.apache.impala.common.InternalException;
import org.apache.impala.service.BackendConfig;
import org.apache.impala.thrift.TCatalogServiceRequestHeader;
import org.apache.impala.thrift.TResetMetadataRequest;
import org.apache.impala.thrift.TTableName;
import org.apache.impala.util.AcidUtils;
import com.google.common.base.Preconditions;
/**
* Representation of the following statements:
* INVALIDATE METADATA
* INVALIDATE METADATA <table>
* REFRESH <table>
* REFRESH <table> PARTITION <partition>
* REFRESH FUNCTIONS <database>
* REFRESH AUTHORIZATION
*/
public class ResetMetadataStmt extends StatementBase {
public enum Action {
INVALIDATE_METADATA_ALL(false),
INVALIDATE_METADATA_TABLE(false),
REFRESH_TABLE(true),
REFRESH_PARTITION(true),
REFRESH_FUNCTIONS(true),
REFRESH_AUTHORIZATION(true);
private final boolean isRefresh_;
Action(boolean isRefresh) {
isRefresh_ = isRefresh;
}
public boolean isRefresh() { return isRefresh_; }
}
// Updated during analysis. Null if invalidating the entire catalog or refreshing
// database functions.
private TableName tableName_;
// not null when refreshing a single partition
private final PartitionSpec partitionSpec_;
// not null when refreshing functions in a database.
private final String database_;
// The type of action.
private final Action action_;
// Set during analysis.
private User requestingUser_;
private ResetMetadataStmt(Action action, String db, TableName tableName,
PartitionSpec partitionSpec) {
Preconditions.checkNotNull(action);
action_ = action;
database_ = db;
tableName_ = tableName;
partitionSpec_ = partitionSpec;
if (partitionSpec_ != null) partitionSpec_.setTableName(tableName_);
}
public static ResetMetadataStmt createInvalidateStmt() {
return new ResetMetadataStmt(Action.INVALIDATE_METADATA_ALL, /*db*/ null,
/*table*/ null, /*partition*/ null);
}
public static ResetMetadataStmt createInvalidateStmt(TableName tableName) {
return new ResetMetadataStmt(Action.INVALIDATE_METADATA_TABLE, /*db*/ null,
/*table*/ Preconditions.checkNotNull(tableName), /*partition*/ null);
}
public static ResetMetadataStmt createRefreshTableStmt(TableName tableName) {
return new ResetMetadataStmt(Action.REFRESH_TABLE, /*db*/ null,
Preconditions.checkNotNull(tableName), /*partition*/ null);
}
public static ResetMetadataStmt createRefreshPartitionStmt(TableName tableName,
PartitionSpec partitionSpec) {
return new ResetMetadataStmt(Action.REFRESH_PARTITION, /*db*/ null,
Preconditions.checkNotNull(tableName), Preconditions.checkNotNull(partitionSpec));
}
public static ResetMetadataStmt createRefreshFunctionsStmt(String db) {
return new ResetMetadataStmt(Action.REFRESH_FUNCTIONS, Preconditions.checkNotNull(db),
/*table*/ null, /*partition*/ null);
}
public static ResetMetadataStmt createRefreshAuthorizationStmt() {
return new ResetMetadataStmt(Action.REFRESH_AUTHORIZATION, /*db*/ null,
/*table*/ null, /*partition*/ null);
}
public TableName getTableName() { return tableName_; }
@VisibleForTesting
protected Action getAction() { return action_; }
@Override
public void collectTableRefs(List<TableRef> tblRefs) {
if (tableName_ != null && partitionSpec_ != null) {
tblRefs.add(new TableRef(tableName_.toPath(), null));
}
}
@Override
public void analyze(Analyzer analyzer) throws AnalysisException {
requestingUser_ = analyzer.getUser();
switch (action_) {
case INVALIDATE_METADATA_TABLE:
case REFRESH_TABLE:
case REFRESH_PARTITION:
Preconditions.checkNotNull(tableName_);
String dbName = analyzer.getTargetDbName(tableName_);
tableName_ = new TableName(dbName, tableName_.getTbl());
if (action_.isRefresh()) {
// Verify the user has privileges to access this table. Will throw if the parent
// database does not exists. Don't call getTable() to avoid loading the table
// metadata if it is not yet in this impalad's catalog cache.
if (!analyzer.dbContainsTable(dbName, tableName_.getTbl(), Privilege.REFRESH)) {
// Only throw an exception when the table does not exist for refresh
// statements since 'invalidate metadata' should add/remove tables
// created/dropped external to Impala.
throw new AnalysisException(Analyzer.TBL_DOES_NOT_EXIST_ERROR_MSG +
tableName_);
}
if (partitionSpec_ != null) {
try {
// Get local table info without reaching out to HMS
FeTable table = analyzer.getTable(dbName, tableName_.getTbl());
if (AcidUtils.isTransactionalTable(
table.getMetaStoreTable().getParameters())) {
throw new AnalysisException("Refreshing a partition is not allowed on " +
"transactional tables. Try to refresh the whole table instead.");
}
} catch (TableLoadingException e) {
throw new AnalysisException(e);
}
partitionSpec_.setPrivilegeRequirement(Privilege.ANY);
partitionSpec_.analyze(analyzer);
}
} else {
FeTable tbl = analyzer.getTableNoThrow(dbName, tableName_.getTbl());
// Verify the user has privileges to access this table.
if (tbl == null) {
analyzer.registerPrivReq(builder ->
builder.onTableUnknownOwner(
dbName, tableName_.getTbl()).allOf(Privilege.REFRESH).build());
} else {
analyzer.registerPrivReq(
builder -> builder.onTable(dbName, tableName_.getTbl(),
tbl.getOwnerUser()).allOf(Privilege.REFRESH).build());
}
}
break;
case REFRESH_AUTHORIZATION:
if (!analyzer.isAuthzEnabled()) {
throw new AnalysisException("Authorization is not enabled. To enable " +
"authorization restart Impala with the --server_name=<name> flag.");
}
analyzer.registerPrivReq(builder ->
builder.onServer(analyzer.getServerName())
.allOf(Privilege.REFRESH)
.build());
break;
case REFRESH_FUNCTIONS:
FeDb db = analyzer.getDb(database_, /*throwIfDoesNotExist*/ false);
String dbOwner = db == null ? null : db.getOwnerUser();
analyzer.registerPrivReq(builder ->
builder.onDb(database_, dbOwner)
.allOf(Privilege.REFRESH)
.build());
break;
case INVALIDATE_METADATA_ALL:
analyzer.registerPrivReq(builder ->
builder.onServer(analyzer.getServerName())
.allOf(Privilege.REFRESH)
.build());
break;
default:
throw new IllegalStateException("Invalid reset metadata action: " + action_);
}
}
@Override
public String toSql(ToSqlOptions options) {
StringBuilder result = new StringBuilder();
switch (action_) {
case REFRESH_AUTHORIZATION:
result.append("REFRESH AUTHORIZATION");
break;
case REFRESH_FUNCTIONS:
result.append("REFRESH FUNCTIONS ").append(database_);
break;
case REFRESH_TABLE:
result.append("REFRESH ").append(tableName_.toSql());
break;
case REFRESH_PARTITION:
result.append("REFRESH ").append(tableName_.toSql()).append(" ")
.append(partitionSpec_.toSql(options));
break;
case INVALIDATE_METADATA_ALL:
result.append("INVALIDATE METADATA");
break;
case INVALIDATE_METADATA_TABLE:
result.append("INVALIDATE METADATA ").append(tableName_.toSql());
break;
default:
throw new IllegalStateException("Invalid reset metadata action: " + action_);
}
return result.toString();
}
public TResetMetadataRequest toThrift() throws InternalException {
TResetMetadataRequest params = new TResetMetadataRequest();
params.setHeader(new TCatalogServiceRequestHeader());
params.header.setRequesting_user(requestingUser_.getShortName());
params.setIs_refresh(action_.isRefresh());
if (tableName_ != null) {
params.setTable_name(new TTableName(tableName_.getDb(), tableName_.getTbl()));
}
if (partitionSpec_ != null) params.setPartition_spec(partitionSpec_.toThrift());
if (database_ != null) params.setDb_name(database_);
if (action_ == Action.REFRESH_AUTHORIZATION) {
params.setAuthorization(true);
}
return params;
}
}