hive源码解析之语法解析

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 ASTNode> aliasToSrc;

private final HashMap ASTNode> nameToDest;

private final HashMap nameToSample;

private final MapASTNode> destToSelExpr;

private final HashMap ASTNode> destToWhereExpr;

private final HashMap ASTNode> destToGroupby;


==

Context 类在这里很重要

Context : 是query的一个context

主要功能:

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

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

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

private Path makeMRScratchDir(HiveConf conf, boolean mkdir)


你可能感兴趣的:(hive)