IT技术博客大学习 共学习 共进步

Hive源码解析-之-语法解析器

淘宝数据平台与产品部官方博客 tbdata.org 2011-05-03 23:34:04 浏览 5,526 次

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

+

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


<!--[endif]-->

SemanticAnalyzerFactory类:

<!--[endif]-->


+

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

五个SemanticAnalyzer的简单介绍:

ExplainSemanticAnalyzer

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

FunctionSemanticAnalyzer


<!--[endif]-->

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

如:

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

sql可以调用该自定义的function

DDL SemanticAnalyzer

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

如:show tables;

Load SemanticAnalyzer:

Load操作。

SemanticAnalyzer

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

=

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

publicvoid analyze(ASTNode ast, Context ctx) throws SemanticException {

this.ctx = ctx;

analyzeInternal(ast);

}

=

五个解析器都继承于它,并实现analyzeInternal(),不同的analyzer不同的实现过程,我们关注的是普通sqlselectfrom )的解析,所以在这里直接看SemanticAnalyzer

= (注: ctx 是 context 类,很重要,在下面会提到)

+

所以解析过程的就从这里开始。 我们只说正常sql (select … from)的解析。

这就是hive源码里面的SemanticAnalyzer类(超大的一个类)。

因为很重要直接代码如下:

SemanticAnalyzeranalyzeInternal()

publicvoid 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次解析

publicvoid doPhase1(ASTNode ast, QB qb, Phase1Ctx ctx_1)

以下是官方注释。

/**

*Phase1:(including,butnotlimitedto):

*

*1.Getsallthealiasesforallthetables/subqueriesandmakesthe appropriatemappinginaliasToTabs,aliasToSubq

*2.Getsthelocationofthe destinationandnamestheclaseinclause+i

*3.Createsamapfroma stringrepresentationofanaggregationtreetotheactualaggregationAST

*4.CreatesamappingfromtheclausenametotheselectexpressionASTin destToSelExpr

*5.Createsamappingfromatablealiastothelateralview

*AST’sinaliasToLateralViews

这里是递归的遍历这颗树,

代码示例,如面对 TOK_FROM

case HiveParser.TOK_FROM:

int child_count = ast.getChildCount();//

if (child_count != 1) {

thrownew 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);

} elseif (frm.getToken().getType() == HiveParser.TOK_SUBQUERY) {

processSubQuery(qb, frm);

} elseif (frm.getToken().getType() == HiveParser.TOK_LATERAL_VIEW) {

processLateralView(qb, frm);

} elseif (isJoinToken(frm)) {

processJoin(qb, frm);

qbp.setJoinExpr(frm);

}

break;

把摘下来的信息放在QBQBParseInfo等几个容器里面。如:如果是select 就把信息记录到QBParseInfo中。

skipRecursion标示递归是否结束。

其中涉及了几个容器:

QBParseInfo是辅助analyzer语法解析的一个容器,

qb放的是sql block基本单元,包括表名别名问题。

这里我们可以拿到很多我们想要的东西。

QBParseInfo

Implementationoftheparseinformationrelatedtoaqueryblock.

各种对应关系,如: select , groupby , groupby 等的string -Map- astNode

privatefinalbooleanisSubQ;

privatefinal String alias;

private ASTNode joinExpr;

private ASTNode hints;

privatefinal HashMap<String, ASTNode> aliasToSrc;

privatefinal HashMap<String, ASTNode> nameToDest;

privatefinal HashMap<String, TableSample> nameToSample;

privatefinal Map<String, ASTNode> destToSelExpr;

privatefinal HashMap<String, ASTNode> destToWhereExpr;

privatefinal HashMap<String, ASTNode> destToGroupby;

==

Context 在这里很重要

Context : query的一个context

主要功能:

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

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

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

private Path makeMRScratchDir(HiveConf conf, boolean mkdir)

建议继续学习

  1. 如何获取hive建表语句 (阅读 7,123)
  2. Hive源码解析-之-词法分析器 parser (阅读 6,947)
  3. 如何编写一个JSON解析器 (阅读 6,243)
  4. Nginx源码分析-事件循环 (阅读 6,126)
  5. HIVE中UDTF编写和使用 (阅读 5,886)
  6. Hive的入口 -- Hive源码解析 (阅读 5,827)
  7. Storm源码浅析之topology的提交 (阅读 5,763)
  8. Nginx源码分析-内存池 (阅读 5,343)
  9. UglifyJS有个不错的JavaScript解析器 (阅读 5,205)
  10. Nginx源码分析-Epoll模块 (阅读 4,985)