Hive代码分析报告(八):语义分析③

本文详细分析了Hive查询分析器 SemanticAnalyzer 的核心方法analyzeInternal()及其内部关键函数genResolvedParseTree()。该函数从ASTNode开始,处理别名、创建表和视图的命令,以及设置命令类型等,逐步构建解析树并获取元数据信息,为后续的查询计划生成奠定基础。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

2021SC@SDUSC

目录

概述

SemanticAnalyzer类分析:

①从analyzeInternal(ASTNode ast)到genResolvedParseTree(ASTNode ,PlannerContext)


概述

前面,我对BaseSemanticAnalyzer类进行了简单的分析,知道了该类是各个语义分析器的基类,由他派生的子类包括SemanticAnalyzer等诸多查询分析器。尽管查询分析器这个命名具有很强的概括性,或许其包括各种后续相关的查询过程,但指令在编译器中的走向暗示我们,语义分析的关键步骤就在此处。前面BaseSemanticAnalyzer类和SemanticAnalyzerFactory类的解读给了理解很大的帮助,后者能根据AST树所提供的信息,调用SemanticAnalyzerFactory.get()函数得到相应的查询分析子类(这些类都继承自BaseSemanticAnalyzer),不出意外,这些子类有各自特有的针对不同类型数据查询请求的处理功能,因为他们都重写了父类的analyzeInternal(ASTNode,Context)方法。而该方法正是AST所蕴含信息的下一步流向所在。

我重点关注到SemanticAnalyzer类,该类的代码量达到10000+行,根据查询有关资料也可知道,该类是大部分查询类型的主要处理逻辑,由此,我将重点分析该类的analyzeInternal()等函数。

SemanticAnalyzer类分析:

①从analyzeInternal(ASTNode ast)genResolvedParseTree(ASTNode ,PlannerContext)

由于该类为BaseSemanticAnalyzer的子类,它的一些简单的成员函数的套路与父类大相径庭,因此可以直奔主题,重点关注编译流程上一阶段所指向的analyzeInternal()函数即可,其代码如下。

public void analyzeInternal(ASTNode ast) throws SemanticException {
    analyzeInternal(ast, new PlannerContext());
  }

由此,进入analyzeInternal(ast, new PlannerContext());

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

   

    LOG.info("Starting Semantic Analysis");

  

    processPositionAlias(ast);

    if (!genResolvedParseTree(ast, plannerCtx)) {

      return;

    }

我们逐步分析这个函数,函数第一行,日志信息提示我们由此处开始进入查询分析。接着调用函数processPositionAlias(ast);其函数参数为外层参数引入的AST节点,该函数的作用是先将所有的别名给处理了。

之后,用if语句判断genResolvedParseTree(ast, plannerCtx)函数的返回值,所以我们看一下这个genResolvedParseTree(ast, plannerCtx)要做什么。

boolean genResolvedParseTree(ASTNode ast, PlannerContext plannerCtx) throws SemanticException {
ASTNode child = ast;
    this.ast = ast;
    viewsExpanded = new ArrayList<String>();
    ctesExpanded = new ArrayList<String>();

 

首先,给出了一些函数所需要的一些临时变量。

后边的几个部分结构很清晰,主要是若干个条件判断语句引出相关的操作。这也是由于AST节点的令牌所表示的不同种类的操作所带来的必然判断。下面具体来看:

根据HiveParser.TOK_CREATETABLE,可以知道这部分将判断是否是分析创建表命令。如果不是CTAS(create table ... as select ...)语法,直接返回即可,如果是,执行setCommandType。

 if (ast.getToken().getType() == HiveParser.TOK_CREATETABLE) {

      if ((child = analyzeCreateTable(ast, qb, plannerCtx)) == null) {

        return false;

      }

    } else {

      queryState.setCommandType(HiveOperation.QUERY);

    }

   根据HiveParser.TOK_CREATEVIEW,可以知道这部分将判断是否是分析创建视图命令

主要通过switch case语句,将ast.getChild(0).getType()即孩子节点的和HiveParser.TOK_TRUE等预设标记进行匹配,以确定后续操作。   

 if (ast.getToken().getType() == HiveParser.TOK_CREATEVIEW ||

        ast.getToken().getType() == HiveParser.TOK_CREATE_MATERIALIZED_VIEW ||

        (ast.getToken().getType() == HiveParser.TOK_ALTERVIEW &&

            ast.getChild(1).getType() == HiveParser.TOK_QUERY)) {

      child = analyzeCreateView(ast, qb, plannerCtx);

      if (child == null) {

        return false;

      }

      viewSelect = child;

      // 这一步确保视图不可以引用自身

      viewsExpanded.add(createVwDesc.getViewName());

    }


    switch(ast.getToken().getType()) {

      case HiveParser.TOK_SET_AUTOCOMMIT:

        assert ast.getChildCount() == 1;

        if(ast.getChild(0).getType() == HiveParser.TOK_TRUE) {

          setAutoCommitValue(true);

        }

        else if(ast.getChild(0).getType() == HiveParser.TOK_FALSE) {

          setAutoCommitValue(false);

        }

        else {

          assert false : "Unexpected child of TOK_SET_AUTOCOMMIT: " + ast.getChild(0).getType();

        }

        // 计划落空时

      case HiveParser.TOK_START_TRANSACTION:

      case HiveParser.TOK_COMMIT:

      case HiveParser.TOK_ROLLBACK:

        if(!(conf.getBoolVar(ConfVars.HIVE_IN_TEST) || conf.getBoolVar(ConfVars.HIVE_IN_TEZ_TEST))) {

          throw new IllegalStateException(SemanticAnalyzerFactory.getOperation(ast.getToken().getType()) +

            " is not supported yet.");

        }

        queryState.setCommandType(SemanticAnalyzerFactory.getOperation(ast.getToken().getType()));

        return false;

    }


    // 掩蔽与过滤解析

    tableMask = new TableMask(this, conf, ctx.isSkipTableMasking());

   下面的步骤作用是将AST转换为QB ,doPhase1 负责把 ASTTree 分解存入对应的QB,如果 phase1Result 错误返回false。

Phase1Ctx ctx_1 = initPhase1Ctx();

    preProcessForInsert(child, qb);

  

    if (!doPhase1(child, qb, ctx_1, plannerCtx)) {

    

      return false;

    }

    LOG.info("Completed phase 1 of Semantic Analysis");

 下面的步骤处理元数据相关,getMetaData 负责把表、字段等元数据信息存入QB   

getMetaData(qb, createVwDesc == null);

    LOG.info("Completed getting MetaData in Semantic Analysis");


    plannerCtx.setParseTreeAttr(child, ctx_1);


    return true;

  }

小结:

这里简单根据流程进入analyzeInternal()函数,进而主要分析了genResolvedParseTree(ASTNode ,PlannerContext)函数的基本结构和主要功能,更加深入的分析,将在之后进行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值