- 从前文中可以看到
runInternal
调用了compile
方法,在compile
方法我们可以看到执行了以下步骤:
1.1 首先通过ParseUtils.parse
将sql解析为抽象的语法树ASTNode
1.2 然后通过sem.analyze(tree, ctx);
对解析出来的抽象语法树进行编译和优化 - 我们先进入
ParseUtils.parse
中进行查看
/** Parses the Hive query. */
public static ASTNode parse(String command, Context ctx) throws ParseException {
return parse(command, ctx, null);
}
/** Parses the Hive query. */
public static ASTNode parse(
String command, Context ctx, String viewFullyQualifiedName) throws ParseException {
ParseDriver pd = new ParseDriver();
ASTNode tree = pd.parse(command, ctx, viewFullyQualifiedName);
tree = findRootNonNullToken(tree);
handleSetColRefs(tree);
return tree;
}
2.1 进入pd.parse(command, ctx, viewFullyQualifiedName);
2.1.1首先调用new HiveLexerX(new ANTLRNoCaseStringStream(command))
对sql进行词法语法解析得到一个词法解析器,使用的是antlr框架(其是什么后续介绍)
2.1.2 根据词法解析器得到token(具有特殊意义的字符串的封装)tokens = new TokenRewriteStream(lexer)
2.1.3 根据token生成解析器parser = new HiveParser(tokens)
2.1.4 根据解析器可以获取statment然后获取抽象语法树tree = (ASTNode) r.getTree()
public ASTNode parse(String command, Context ctx, String viewFullyQualifiedName)
throws ParseException {
if (LOG.isDebugEnabled()) {
LOG.debug("Parsing command: " + command);
}
// 语法分析 HiveLexerX 是一个词法解析类
HiveLexerX lexer = new HiveLexerX(new ANTLRNoCaseStringStream(command));//词法语法的解析(使用antlr框架)
// 根据词法分析的结果得到tokens的,此时不只是单纯的字符串,而是具有特殊意义的字符串的封装,其本身是一个流。
TokenRewriteStream tokens = new TokenRewriteStream(lexer);//得到token
if (ctx != null) {
if (viewFullyQualifiedName == null) {
// Top level query
ctx.setTokenRewriteStream(tokens);
} else {
// It is a view
ctx.addViewTokenRewriteStream(viewFullyQualifiedName, tokens);
}
lexer.setHiveConf(ctx.getConf());
}
// HiveParser是一个语法解析类
HiveParser parser = new HiveParser(tokens);//转化为解析器
if (ctx != null) {
parser.setHiveConf(ctx.getConf());
}
parser.setTreeAdaptor(adaptor);
HiveParser.statement_return r = null;
try {
r = parser.statement();
} catch (RecognitionException e) {
e.printStackTrace();
throw new ParseException(parser.errors);
}
if (lexer.getErrors().size() == 0 && parser.errors.size() == 0) {
LOG.debug("Parse Completed");
} else if (lexer.getErrors().size() != 0) {
throw new ParseException(lexer.getErrors());
} else {
throw new ParseException(parser.errors);
}
ASTNode tree = (ASTNode) r.getTree();//得到抽象语法树
tree.setUnknownTokenBoundaries();
return tree;
}
- 获取到抽象语法树以后,下一步就是执行编译和优化
sem.analyze(tree, ctx);
3.1 进入analyze方法
public void analyze(ASTNode ast, Context ctx) throws SemanticException {
//初始化上下文
initCtx(ctx);
init(true);
// 其中analyzeInternal是抽象方法,由不同的子类实现,较为复杂。
// 大致功能为将ASTNode转化为Task,包括可能的optimize
analyzeInternal(ast);
}
3.2 从上文可以看到其调用了analyzeInternal
方法
其中analyzeInternal是抽象方法,由不同的子类实现,较为复杂。大致功能为将ASTNode转化为Task,包括可能的optimize
4. 编译优化执行完以后,将任务转化为QueryPlanplan = new QueryPlan()
5. 最后回到前文的类中,执行execute方法将QueryPlan转化为MR任务
6. 最终sql的解析、编译、优化可以按以下图片的步骤执行
6.1 将sql语句转化为抽象语法树
6.2 根据抽象语法树生成基本的执行单元QueryBlock
6.3 根据QueryBlock生成操作树OperatorTree
6.4 对操作树OperatorTree进行优化
6.5 将优化后的操作树转化为任务树TaskTree
6.6 对任务树TaskTree进行物理优化