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 joinExpr;
private ASTNode hints;
private final HashMap
private final HashMap
private final HashMap
private final Map
private final HashMap
private final HashMap
==
Context 类在这里很重要
Context : 是query的一个context
主要功能:
1 标示explain: 如果是explain语句, explain为ture都不会实际的建立这些文件。
2可以建立tmp-file (在query执行过程中所需要的tmp-file 文件和路径 ),生成和清除中间临时文件及路径。
所以我们可以再这里获取整个过程中的临时文件,用于优化使用。
private Path makeMRScratchDir(HiveConf conf, boolean mkdir)