package org.apache.howl.cli.SemanticAnalysis;
import java.io.Serializable;
import java.util.List;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.permission.FsAction;
import org.apache.hadoop.hive.metastore.Warehouse;
import org.apache.hadoop.hive.metastore.api.MetaException;
import org.apache.hadoop.hive.ql.exec.Task;
import org.apache.hadoop.hive.ql.metadata.HiveException;
import org.apache.hadoop.hive.ql.metadata.InvalidTableException;
import org.apache.hadoop.hive.ql.metadata.Table;
import org.apache.hadoop.hive.ql.parse.ASTNode;
import org.apache.hadoop.hive.ql.parse.AbstractSemanticAnalyzerHook;
import org.apache.hadoop.hive.ql.parse.BaseSemanticAnalyzer;
import org.apache.hadoop.hive.ql.parse.HiveParser;
import org.apache.hadoop.hive.ql.parse.HiveSemanticAnalyzerHookContext;
import org.apache.hadoop.hive.ql.parse.SemanticException;
import org.apache.howl.common.AuthUtils;
import org.apache.howl.common.ErrorType;
import org.apache.howl.common.HowlException;
public class HowlSemanticAnalyzer extends AbstractSemanticAnalyzerHook {
private AbstractSemanticAnalyzerHook hook;
private ASTNode ast;
@Override
public ASTNode preAnalyze(HiveSemanticAnalyzerHookContext context, ASTNode ast)
throws SemanticException {
this.ast = ast;
switch (ast.getToken().getType()) {
// Howl wants to intercept following tokens and special-handle them.
case HiveParser.TOK_CREATETABLE:
hook = new CreateTableHook();
return hook.preAnalyze(context, ast);
case HiveParser.TOK_CREATEDATABASE:
hook = new CreateDatabaseHook();
return hook.preAnalyze(context, ast);
// DML commands used in Howl where we use the same implementation as default Hive.
case HiveParser.TOK_SHOWDATABASES:
case HiveParser.TOK_DROPDATABASE:
case HiveParser.TOK_SWITCHDATABASE:
return ast;
// Howl will allow these operations to be performed since they are DDL statements.
case HiveParser.TOK_DROPTABLE:
case HiveParser.TOK_DESCTABLE:
case HiveParser.TOK_ALTERTABLE_ADDCOLS:
case HiveParser.TOK_ALTERTABLE_RENAME:
case HiveParser.TOK_ALTERTABLE_DROPPARTS:
case HiveParser.TOK_ALTERTABLE_PROPERTIES:
case HiveParser.TOK_ALTERTABLE_SERIALIZER:
case HiveParser.TOK_ALTERTABLE_SERDEPROPERTIES:
case HiveParser.TOK_SHOW_TABLESTATUS:
case HiveParser.TOK_SHOWTABLES:
case HiveParser.TOK_SHOWPARTITIONS:
return ast;
case HiveParser.TOK_ALTERTABLE_ADDPARTS:
hook = new AddPartitionHook();
return hook.preAnalyze(context, ast);
case HiveParser.TOK_ALTERTABLE_PARTITION:
if (((ASTNode)ast.getChild(1)).getToken().getType() == HiveParser.TOK_ALTERTABLE_FILEFORMAT) {
hook = new AlterTableFileFormatHook();
return hook.preAnalyze(context, ast);
} else {
return ast;
}
// In all other cases, throw an exception. Its a white-list of allowed operations.
default:
throw new SemanticException("Operation not supported.");
}
}
@Override
public void postAnalyze(HiveSemanticAnalyzerHookContext context,
List<Task<? extends Serializable>> rootTasks) throws SemanticException {
try{
switch (ast.getToken().getType()) {
case HiveParser.TOK_DESCTABLE:
authorize(getFullyQualifiedName((ASTNode) ast.getChild(0).getChild(0)), context, FsAction.READ, false);
break;
case HiveParser.TOK_SHOWPARTITIONS:
authorize(BaseSemanticAnalyzer.unescapeIdentifier(ast.getChild(0).getText()), context, FsAction.READ, false);
break;
case HiveParser.TOK_ALTERTABLE_ADDPARTS:
case HiveParser.TOK_DROPTABLE:
case HiveParser.TOK_ALTERTABLE_ADDCOLS:
case HiveParser.TOK_ALTERTABLE_RENAME:
case HiveParser.TOK_ALTERTABLE_DROPPARTS:
case HiveParser.TOK_ALTERTABLE_PROPERTIES:
case HiveParser.TOK_ALTERTABLE_SERIALIZER:
case HiveParser.TOK_ALTERTABLE_SERDEPROPERTIES:
authorize(BaseSemanticAnalyzer.unescapeIdentifier(ast.getChild(0).getText()), context, FsAction.WRITE, false);
break;
case HiveParser.TOK_ALTERTABLE_PARTITION:
authorize(BaseSemanticAnalyzer.unescapeIdentifier(((ASTNode)ast.getChild(0)).getChild(0).getText()), context, FsAction.WRITE, false);
break;
case HiveParser.TOK_SWITCHDATABASE:
authorize(BaseSemanticAnalyzer.unescapeIdentifier(ast.getChild(0).getText()), context, FsAction.READ, true);
break;
case HiveParser.TOK_DROPDATABASE:
authorize(BaseSemanticAnalyzer.unescapeIdentifier(ast.getChild(0).getText()), context, FsAction.WRITE, true);
break;
case HiveParser.TOK_CREATEDATABASE:
case HiveParser.TOK_SHOWDATABASES:
case HiveParser.TOK_SHOW_TABLESTATUS:
case HiveParser.TOK_SHOWTABLES:
// We do no checks for show tables/db , create db. Its always allowed.
case HiveParser.TOK_CREATETABLE:
// No checks for Create Table, since its not possible to compute location
// here easily. So, it is especially handled in CreateTable post hook.
break;
default:
throw new HowlException(ErrorType.ERROR_INTERNAL_EXCEPTION, "Unexpected token: "+ast.getToken());
}
} catch(HowlException e){
throw new SemanticException(e);
} catch (MetaException e) {
throw new SemanticException(e);
} catch (HiveException e) {
throw new SemanticException(e);
}
if(hook != null){
hook.postAnalyze(context, rootTasks);
}
}
private void authorize(String name, HiveSemanticAnalyzerHookContext cntxt, FsAction action, boolean isDBOp)
throws MetaException, HiveException, HowlException{
Warehouse wh = new Warehouse(cntxt.getConf());
if(!isDBOp){
// Do validations for table path.
Table tbl;
try{
tbl = cntxt.getHive().getTable(name);
}
catch(InvalidTableException ite){
// Table itself doesn't exist in metastore, nothing to validate.
return;
}
Path path = tbl.getPath();
if(path != null){
AuthUtils.authorize(wh.getDnsPath(path), action, cntxt.getConf());
} else{
// This will happen, if table exists in metastore for a given
// tablename, but has no path associated with it, so there is nothing to check.
// In such cases, do no checks and allow whatever hive behavior is for it.
return;
}
} else{
// Else, its a DB operation.
AuthUtils.authorize(wh.getDefaultDatabasePath(name), action, cntxt.getConf());
}
}
private String getFullyQualifiedName(ASTNode ast) {
// Copied verbatim from DDLSemanticAnalyzer, since its private there.
if (ast.getChildCount() == 0) {
return ast.getText();
}
return getFullyQualifiedName((ASTNode) ast.getChild(0)) + "."
+ getFullyQualifiedName((ASTNode) ast.getChild(1));
}
}