Hive语法解析器是根据<上次分享的 词法分析 > 生成的语法树为基础,进行语法解析。根据语法token的情况实现了五个具体的语法解析器。

+

在你生成语法器的时候, SemanticAnalyzerFactory分别针对不同的情况生成对应的某个语法器,如下


SemanticAnalyzerFactory类:


+

现在有五个语法解析器 analyzer继承了BaseSemanticAnalyzer。

五个SemanticAnalyzer的简单介绍:

ExplainSemanticAnalyzer

对语法树、执行计划 做了一个打印操作,其他的基本上都是按照SemanticAnalyzer执行的,最重要的差别,就是在整个解析过程中它没有让context在构建文件真正的临时文件所需的文件及文件路径等。

FunctionSemanticAnalyzer :


主要操作是创建和消除一个的function的元信息。

如:

CREATE TEMPORARY FUNCTION str_to_date AS ‘com.taobao.hive.udf.UDFStrToDate’;

sql可以调用该自定义的function。

DDL SemanticAnalyzer:

主要是对表、view、partition的级别增删改查的操作。

如:show tables;

Load SemanticAnalyzer:

Load操作。

SemanticAnalyzer:

对于我们最重要关注的是SemanticAnalyzer:对应sql语句进行解析,这也是最核心最复杂的组件。

=

BeseSemanticAnalyze 中语法解析开始于下面:

public void analyze(ASTNode ast, Context ctx) throws SemanticException { 
this.ctx = ctx; 
analyzeInternal(ast); 
}
=
五个解析器都继承于它,并实现analyzeInternal(),不同的analyzer不同的实现过程,我们关注的是普通sql(select from )的解析,所以在这里直接看SemanticAnalyzer。 
= (注: ctx 是 context 类,很重要,在下面会提到)
+
所以解析过程的就从这里开始。 我们只说正常sql (select … from)的解析。
这就是hive源码里面的SemanticAnalyzer类(超大的一个类)。
因为很重要直接代码如下:

SemanticAnalyzer的analyzeInternal() 
public void analyzeInternal(ASTNode ast) throws SemanticException { 
reset(); 
QB qb = new QB(null, null,  false); 
this.qb = qb; 
this.ast = ast; 
ASTNode child = ast; 
LOG.info(“Starting Semantic Analysis”); 
System.out.print(“Starting Semantic Analysis”); 
// analyze create table command
// 
//建表或view 前处理 ,如: create table .. as select .. from 
if (ast.getToken().getType() == HiveParser.TOK_CREATETABLE) { 
// if it is not CTAS, we don’t need to go further and just return 
if ((child = analyzeCreateTable(ast, qb)) == null) { 
return; 
} 
} 
// analyze create view command 
if (ast.getToken().getType() == HiveParser.TOK_CREATEVIEW) { 
child = analyzeCreateView(ast, qb); 
if (child == null) { 
return; 
} 
viewSelect = child; 
} 
// 
// continue analyzing from the child ASTNode. 
doPhase1(child, qb, initPhase1Ctx());//获取subSql,table 等对应别名 
LOG.info(“Completed phase 1 of Semantic Analysis”); 
getMetaData(qb);//get 元数据 
LOG.info(“Completed getting MetaData in Semantic Analysis”); 
// Save the result schema derived from the sink operator produced 
// by genPlan. This has the correct column names, which clients 
// such as JDBC would prefer instead of the c0, c1 we’ll end 
// up with later. 
Operator sinkOp = genPlan(qb);//这个层次才开始column names,生产operator 
resultSchema = 
convertRowSchemaToViewSchema(opParseCtx.get(sinkOp).getRR()); 
if (createVwDesc != null) {//四面 
saveViewDefinition(); 
// Since we’re only creating a view (not executing it), we 
// don’t need to optimize or translate the plan (and in fact, those 
// procedures can interfere with the view creation). So 
// skip the rest of this method. 
ctx.setResDir(null); 
ctx.setResFile(null); 
return; 
} 
ParseContext pCtx = new ParseContext(conf, qb, child, opToPartPruner, 
topOps, topSelOps, opParseCtx, joinContext, topToTable, 
loadTableWork, loadFileWork, ctx, idToTableNameMap, destTableId, uCtx, 
listMapJoinOpsNoReducer, groupOpToInputTables, prunedPartitions, 
opToSamplePruner); 
//进入优化器,生成更好的operator tree 
Optimizer optm = new Optimizer(); 
optm.setPctx(pCtx); 
optm.initialize(conf); 
pCtx = optm.optimize(); 
init(pCtx); 
qb = pCtx.getQB(); 
// At this point we have the complete operator tree 
// from which we want to find the reduce operator 
genMapRedTasks(qb); 
LOG.info(“Completed plan generation”); 
return; 
}

关键方法:
doPhase1() 
这个方法相当于把tree的大枝叶先过滤了一遍,解决了一些别名问题和对应为问题,
包括:表和subsql的对应的别名,
Tree 的string 与 ast 对应等,只是没有涉及到字段级别。
1次解析
public void doPhase1(ASTNode ast, QB qb, Phase1Ctx ctx_1)
以下是官方注释。
/**
* Phase 1: (including, but not limited to):
*
* 1. Gets all the aliases for all the tables / subqueries and makes the  appropriatemappinginaliasToTabs,aliasToSubq
* 2. Gets the location of the destinationandnamestheclase“inclause“+i
* 3. Creates a map from a stringrepresentationofanaggregationtreetotheactualaggregationAST 
* 4. Creates a mapping from the clause name to the select expression AST in  destToSelExpr
* 5. Creates a mapping from a table alias to the lateral view
* AST’s in aliasToLateralViews
这里是递归的遍历这颗树,

代码示例,如面对 TOK_FROM
case HiveParser.TOK_FROM:
int child_count = ast.getChildCount();//
if (child_count != 1) {
throw new SemanticException(“Multiple Children “ + child_count);
}
// Check if this is a subquery / lateral view
// 正对不同情况,给出不同解决方法
ASTNode frm = (ASTNode) ast.getChild(0);
if (frm.getToken().getType() == HiveParser.TOK_TABREF) {
processTable(qb, frm);
} else if (frm.getToken().getType() == HiveParser.TOK_SUBQUERY) {
processSubQuery(qb, frm);
} else if (frm.getToken().getType() == HiveParser.TOK_LATERAL_VIEW) {
processLateralView(qb, frm);
} else if (isJoinToken(frm)) {
processJoin(qb, frm);
qbp.setJoinExpr(frm);
}
break;

把摘下来的信息放在QB、QBParseInfo等几个容器里面。如:如果是select 就把信息记录到QBParseInfo 中。 
skipRecursion 标示递归是否结束。 
其中涉及了几个容器:

QBParseInfo 是辅助analyzer语法解析的一个容器, 
而qb放的是sql block基本单元,包括表名别名问题。
这里我们可以拿到很多我们想要的东西。
QBParseInfo
Implementation of the parse information related to a query block.
各种对应关系,如: select , groupby , groupby 等的string –Map– astNode
private final boolean isSubQ;
private final String alias;
private ASTNode
private ASTNode
private final HashMap<String,  ASTNode> aliasToSrc;
private final HashMap<String,  ASTNode> nameToDest;
private final HashMap<String, TableSample> nameToSample;
private final Map<String, ASTNode> destToSelExpr;
private final HashMap<String,  ASTNode> destToWhereExpr;
private final HashMap<String,  ASTNode> destToGroupby;

==

Context 类在这里很重要

Context : 是query的一个context

主要功能:

1 标示explain: 如果是explain语句, explain为ture都不会实际的建立这些文件。

2可以建立tmp-file (在query执行过程中所需要的tmp-file 文件和路径 ),生成和清除中间临时文件及路径。

所以我们可以再这里获取整个过程中的临时文件,用于优化使用。

private Path makeMRScratchDir(HiveConf conf, boolean mkdir)