2021SC@SDUSC
目录
①从analyzeInternal(ASTNode ast)到genResolvedParseTree(ASTNode ,PlannerContext)
概述
前面,我对BaseSemanticAnalyzer类进行了简单的分析,知道了该类是各个语义分析器的基类,由他派生的子类包括SemanticAnalyzer等诸多查询分析器。尽管查询分析器这个命名具有很强的概括性,或许其包括各种后续相关的查询过程,但指令在编译器中的走向暗示我们,语义分析的关键步骤就在此处。前面BaseSemanticAnalyzer类和SemanticAnalyzerFactory类的解读给了理解很大的帮助,后者能根据AST树所提供的信息,调用SemanticAnalyzerFactory.get()函数得到相应的查询分析子类(这些类都继承自BaseSemanticAnalyzer),不出意外,这些子类有各自特有的针对不同类型数据查询请求的处理功能,因为他们都重写了父类的analyzeInternal(ASTNode,Context)方法。而该方法正是AST所蕴含信息的下一步流向所在。
我重点关注到SemanticAnalyzer类,该类的代码量达到10000+行,根据查询有关资料也可知道,该类是大部分查询类型的主要处理逻辑,由此,我将重点分析该类的analyzeInternal()等函数。
SemanticAnalyzer类分析:
①从analyzeInternal(ASTNode ast)
到
genResolvedParseTree(ASTNode
,PlannerContext
)
由于该类为BaseSemanticAnalyzer的子类,它的一些简单的成员函数的套路与父类大相径庭,因此可以直奔主题,重点关注编译流程上一阶段所指向的analyzeInternal()函数即可,其代码如下。
public void analyzeInternal(ASTNode ast) throws SemanticException {
analyzeInternal(ast, new PlannerContext());
}
由此,进入analyzeInternal(ast, new PlannerContext());
void analyzeInternal(ASTNode ast, PlannerContext plannerCtx) throws SemanticException {
LOG.info("Starting Semantic Analysis");
processPositionAlias(ast);
if (!genResolvedParseTree(ast, plannerCtx)) {
return;
}
我们逐步分析这个函数,函数第一行,日志信息提示我们由此处开始进入查询分析。接着调用函数processPositionAlias(ast);其函数参数为外层参数引入的AST节点,该函数的作用是先将所有的别名给处理了。
之后,用if语句判断genResolvedParseTree(ast, plannerCtx)函数的返回值,所以我们看一下这个genResolvedParseTree(ast, plannerCtx)要做什么。
boolean genResolvedParseTree(ASTNode ast, PlannerContext plannerCtx) throws SemanticException {
ASTNode child = ast;
this.ast = ast;
viewsExpanded = new ArrayList<String>();
ctesExpanded = new ArrayList<String>();
首先,给出了一些函数所需要的一些临时变量。
后边的几个部分结构很清晰,主要是若干个条件判断语句引出相关的操作。这也是由于AST节点的令牌所表示的不同种类的操作所带来的必然判断。下面具体来看:
根据HiveParser.TOK_CREATETABLE,可以知道这部分将判断是否是分析创建表命令。如果不是CTAS(create table ... as select ...)语法,直接返回即可,如果是,执行setCommandType。
if (ast.getToken().getType() == HiveParser.TOK_CREATETABLE) {
if ((child = analyzeCreateTable(ast, qb, plannerCtx)) == null) {
return false;
}
} else {
queryState.setCommandType(HiveOperation.QUERY);
}
根据HiveParser.TOK_CREATEVIEW,可以知道这部分将判断是否是分析创建视图命令
主要通过switch case语句,将ast.getChild(0).getType()即孩子节点的和HiveParser.TOK_TRUE等预设标记进行匹配,以确定后续操作。
if (ast.getToken().getType() == HiveParser.TOK_CREATEVIEW ||
ast.getToken().getType() == HiveParser.TOK_CREATE_MATERIALIZED_VIEW ||
(ast.getToken().getType() == HiveParser.TOK_ALTERVIEW &&
ast.getChild(1).getType() == HiveParser.TOK_QUERY)) {
child = analyzeCreateView(ast, qb, plannerCtx);
if (child == null) {
return false;
}
viewSelect = child;
// 这一步确保视图不可以引用自身
viewsExpanded.add(createVwDesc.getViewName());
}
switch(ast.getToken().getType()) {
case HiveParser.TOK_SET_AUTOCOMMIT:
assert ast.getChildCount() == 1;
if(ast.getChild(0).getType() == HiveParser.TOK_TRUE) {
setAutoCommitValue(true);
}
else if(ast.getChild(0).getType() == HiveParser.TOK_FALSE) {
setAutoCommitValue(false);
}
else {
assert false : "Unexpected child of TOK_SET_AUTOCOMMIT: " + ast.getChild(0).getType();
}
// 计划落空时
case HiveParser.TOK_START_TRANSACTION:
case HiveParser.TOK_COMMIT:
case HiveParser.TOK_ROLLBACK:
if(!(conf.getBoolVar(ConfVars.HIVE_IN_TEST) || conf.getBoolVar(ConfVars.HIVE_IN_TEZ_TEST))) {
throw new IllegalStateException(SemanticAnalyzerFactory.getOperation(ast.getToken().getType()) +
" is not supported yet.");
}
queryState.setCommandType(SemanticAnalyzerFactory.getOperation(ast.getToken().getType()));
return false;
}
// 掩蔽与过滤解析
tableMask = new TableMask(this, conf, ctx.isSkipTableMasking());
下面的步骤作用是将AST转换为QB ,doPhase1 负责把 ASTTree 分解存入对应的QB,如果 phase1Result 错误返回false。
Phase1Ctx ctx_1 = initPhase1Ctx();
preProcessForInsert(child, qb);
if (!doPhase1(child, qb, ctx_1, plannerCtx)) {
return false;
}
LOG.info("Completed phase 1 of Semantic Analysis");
下面的步骤处理元数据相关,getMetaData 负责把表、字段等元数据信息存入QB
getMetaData(qb, createVwDesc == null);
LOG.info("Completed getting MetaData in Semantic Analysis");
plannerCtx.setParseTreeAttr(child, ctx_1);
return true;
}
小结:
这里简单根据流程进入analyzeInternal()函数,进而主要分析了genResolvedParseTree(ASTNode
,PlannerContext
)函数的基本结构和主要功能,更加深入的分析,将在之后进行。