Java解析SQL生成语法树_04. Hive源码 — HQL解析(抽象语法树的生成和语义分析)

HQL的解析过程主要在Driver中的compile方法,这一些主要看这个方法中的代码。

1. compile中的主要内容

public int compile(String command, boolean resetTaskIds, boolean deferClose) {

..........

// 对sql语句进行处理(敏感信息、变量替换等)

String queryStr = command;

queryStr = HookUtils.redactLogString(conf, command); // 处理敏感信息(这里应该是可以自定义扩展的)

............

// Step1. 获取抽象语法树

ASTNode tree = ParseUtils.parse(command, ctx);

.............

// Step2. 进行语义分析

BaseSemanticAnalyzer sem = SemanticAnalyzerFactory.get(queryState, tree);

sem.analyze(tree, ctx);

sem.validate();

// Step3. 生成执行计划

schema = getSchema(sem, conf);

plan = new QueryPlan(queryStr, sem, perfLogger.getStartTime(PerfLogger.DRIVER_RUN), queryId, queryState.getHiveOperation(), schema);

.............

return 0;

}

compile中主要有三大部分内容:

根据SQL生成抽象语法树

进行语义分析

执行计划的生成

2. 获取抽象语法树

主要是通过ParseUtils中的parse方法来生成语法树,最后转向ParseDriver中的parse方法,代码如下:

public ASTNode parse(String command, Context ctx, String viewFullyQualifiedName) {

.............

// 创建词法规则

HiveLexerX lexer = new HiveLexerX(new ANTLRNoCaseStringStream(command));

TokenRewriteStream tokens = new TokenRewriteStream(lexer);

// 创建语法分析器

HiveParser parser = new HiveParser(tokens);

r = parser.statement();

ASTNode tree = (ASTNode) r.getTree();

.............

return tree;

}

Hive主要是通过用ANTLR语法定义的词法和文法文件来进行解析,最后生成抽象语法树。

3. 进行语义分析

语义分析主要通过 BaseSemanticAnalyzer 实现类中的 analyze 方法进行。

不同的sql语句会用不同的 BaseSemanticAnalyzer实现类来进行分析,主要有以下语义分析器:

3.1 语义分析器

语法

语义分析器

explain .......

ExplainSemanticAnalyzer

rewirte ..... ??

ExplainSQRewriteSemanticAnalyzer

load ...

LoadSemanticAnalyzer

export ...

ExportSemanticAnalyzer

import ...

ImportSemanticAnalyzer

repl dump ... / repl load ... / repl status ... ??

ReplicationSemanticAnalyzer

alter table .../ alter view ...

DDLSemanticAnalyzer

create database .../ drop database .../ use databaseName / drop table .../ drop view .../ drop materialized view .../ desc database .../ desc table .../ desc function .../ msck ...??/ rebuild ...??/show databases... / show tables.../ show columns.../ show create .... / show functions .../ show partitions .../ show ....../ abort transcations .../ lock .../ grant ... / revoke ... / set role ...

DDLSemanticAnalyzer

create function ... / drop function ... / reload function ...

FunctionSemanticAnalyzer

analyze ...

ColumnStatsSemanticAnalyzer

create macro.../ drop macro... (宏)

MacroSemanticAnalyzer

update table.../ delete from ... / merge ...

UpdateDeleteSemanticAnalyzer

其他语句

如果参数 hive.cbo.enable 被置为 true(默认情况下是true),则创建 CalcitePlanner对象,否则创建SemanticAnalyzer对象。CalcitePlanner继承了SemanticAnalyzer

从上面对应关系看来,我们常写的查询语句主要是通过 SemanticAnalyzer 中的 analyze 方法解析的。

3.2 SemanticAnalyzer语义分析器中的analyze方法

主要看其中的analyze方法,anlyze最终转向的是各个语义分析器中的 analyzeInternal 方法,SemanticAnalyzer中的analyzeInternal 代码如下:

// PlannerContext 是SemanticAnalyzer中的一个静态类,

// 如果参数hive.cbo.enable为false,这里 plannerCtx 是新建的 PlannerContext 的对象

// 如果参数hive.cbo.enable为true,这里 plannerCtx 是 PreCboCtx 对象,PreCboCtx继承了PlannerContext

void analyzeInternal(ASTNode ast, PlannerContext plannerCtx) throws SemanticException {

...........

// step1. 从抽象语法树生成 resolved parse tree

genResolvedParseTree(ast, plannerCtx)

...........

// step2. 从 resolved parse tree 生成 op tree

Operator sinkOp = genOPTree(ast, plannerCtx);

...........

// step3. 推断结果集表结构

resultSchema = convertRowSchemaToViewSchema(opParseCtx.get(sinkOp).getRowResolver());

或者

resultSchema = convertRowSchemaToResultSetSchema(opParseCtx.get(sinkOp).getRowResolver(), HiveConf.getBoolVar(conf, HiveConf.ConfVars.HIVE_RESULTSET_USE_UNIQUE_COLUMN_NAMES));

............

// step4. 为优化器和物理编译器生成上下文

ParseContext pCtx = new ParseContext(queryState, opToPartPruner, opToPartList, topOps,

new HashSet(joinContext.keySet()),

new HashSet(smbMapJoinContext.keySet()),

loadTableWork, loadFileWork, columnStatsAutoGatherContexts, ctx, idToTableNameMap, destTableId, uCtx,

listMapJoinOpsNoReducer, prunedPartitions, tabNameToTabObject, opToSamplePruner,

globalLimitCtx, nameToSplitSample, inputs, rootTasks, opToPartToSkewedPruner,

viewAliasToInput, reduceSinkOperatorsAddedByEnforceBucketingSorting,

analyzeRewrite, tableDesc, createVwDesc, queryProperties, viewProjectToTableSchema, acidFileSinks);

...........

// step5. 执行逻辑优化

Optimizer optm = new Optimizer();

optm.setPctx(pCtx);

optm.initialize(conf);

pCtx = optm.optimize();

FetchTask origFetchTask = pCtx.getFetchTask();

.............

// step6.优化物理执行树 & 翻译成目标执行引擎

TaskCompiler compiler = TaskCompilerFactory.getCompiler(conf, pCtx);

compiler.init(queryState, console, db);

compiler.compile(pCtx, rootTasks, inputs, outputs);

fetchTask = pCtx.getFetchTask();

...............

return;

}

从以上代码中可以看出语义分析中主要包括以下几部分:

从抽象语法树生成 resolved parse tree

从 resolved parse tree 生成 op tree

推断结果集表结构

为优化器和物理编译器生成上下文

执行逻辑优化

优化物理执行树 & 翻译成目标执行引擎

4. 小结

这一节主要看了一下HQL解析中抽象语法树的生成和语义分析器中的analyze方法都做了什么,后面开始分析从抽象语法树 到 执行计划的过程中都做了什么。


版权声明:本文为weixin_32519033原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。