2021SC@SDUSC

目录

概述

补充说明doPhase1()

getMetaData(QB, ReadEntity)分析


概述

上一篇文章中,我分析了doPhase1()函数,这是语义分析的起始阶段,程序的最终目标是将AST的数据载入QB,doPhase1这一阶段主要思想是递归地遍历AST,建立一些必要的映射关系,从而将一些关键信息传给QB,如表、子查询的别名信息、内部子句的名字、聚合操作信息等,进而上面所有这些映射关系都保存在QB/QBParseInfo 中。

补充说明doPhase1()

再来看一次此处代码的大致结构和工作流程

public boolean doPhase1(ASTNode ast, QB qb, Phase1Ctx ctx_1, PlannerContext plannerCtx)

      throws SemanticException {

            。。。。。。。。。。。。。。。。略。。。。。。。。

        case HiveParser.TOK_SELECT://select类型的token

        qb.countSel();//对qb做标记

        qbp.setSelExprForClause(ctx_1.dest, ast);

        。。。。。。。。。。。。。。。。。略。。。。。。

       case HiveParser.TOK_WHERE://where类型token

       //对where的孩子进行处理,为什么是ast.getChild(0)?这个是和之前的HiveParser.g结构相辅相成的。

        qbp.setWhrExprForClause(ctx_1.dest, ast);

        if (!SubQueryUtils.findSubQueries((ASTNode) ast.getChild(0)).isEmpty())

            queryProperties.setFilterWithSubQuery(true);

        break;

      。。。。。。。。。。。。。。。。略。。。。。。。。

      case HiveParser.TOK_GROUPBY:

      case HiveParser.TOK_ROLLUP_GROUPBY:

      case HiveParser.TOK_CUBE_GROUPBY:

      case HiveParser.TOK_GROUPING_SETS:

      。。。。。。。。。。。。略。。。。。。。。

          if (!skipRecursion) {

      // Iterate over the rest of the children

      int child_count = ast.getChildCount();

      for (int child_pos = 0; child_pos < child_count && phase1Result; ++child_pos) {

        // Recurse

        phase1Result = phase1Result && doPhase1(

            (ASTNode)ast.getChild(child_pos), qb, ctx_1, plannerCtx);

      }

    }

    。。。。。。。。。。。。。。。略。。。。。。。。。

大体过程:

doPhase1对ASTTree中的每个元素的TOK类型进行case,针对于不同的case对节点数据进行填充。for遍历整棵ASTTree,中间对每个元素递归调用doPhase1,这种方式是一种深度优先搜索的算法。

经过一轮深度优先遍历,不带元数据的QB树就生成了。

getMetaData(QB, ReadEntity)分析

doPhase1执行完毕之后得到QB,QB里边的只是一些关键字还有一些表的名字,但是和hdfs的文件路径对应不起来,所以需要元数据metaData映射关系,之后在SemanticAnalyzer中调用了 getMetaData()函数。

private void getMetaData(QB qb, ReadEntity parentInput)

      throws HiveException {

LOG.info("Get metadata for source tables");

根据日志信息,可以知道,该函数的目标是从源头表中获取元数据

List<String> tabAliases = new ArrayList<String>(qb.getTabAliases());

上面这个列表记录了别名,因为别名可能在中间被修改

Map<String, ObjectPair<String, ReadEntity>> aliasToViewInfo =

        new HashMap<String, ObjectPair<String, ReadEntity>>();

上面这个MAP的作用,我的理解是:

跟踪视图别名,查看名称和读取实体

例如:对于像'select * from V3'的查询,其中V3 -> V2, V2 -> V1, V1 -> T

map用于跟踪输入的依赖项及其父类。
   

Map<String, String> sqAliasToCTEName = new HashMap<String, String>();

    for (String alias : tabAliases) {

      String tabName = qb.getTabNameForAlias(alias);

      String cteName = tabName.toLowerCase();

      从tabNameToTabObject缓存中获取表的详细信息:

Table tab = getTableObjectByName(tabName, false);

      if (tab != null) {

        // do a deep copy, in case downstream changes it.

        tab = new Table(tab.getTTable().deepCopy());

      }

      if (tab == null ||

          tab.getDbName().equals(SessionState.get().getCurrentDatabase())) {

        Table materializedTab = ctx.getMaterializedTable(cteName);

        if (materializedTab == null) {

          // we first look for this alias from CTE, and then from catalog.

          CTEClause cte = findCTEFromName(qb, cteName);

          if (cte != null) {

            if (!cte.materialize) {

              addCTEAsSubQuery(qb, cteName, alias);

              sqAliasToCTEName.put(alias, cteName);

              continue;

            }

            tab = materializeCTE(cteName, cte);

          }

        } else {

          tab = materializedTab;

        }

      }

      throw new SemanticException(e.getMessage(), e);

    }

  }

这个函数多次出现词CTE,而且程序中有很多涉及CTE的函数,这个CTE是做什么的?查阅资料了解到,CTE是很多数据库系统中常用的一种公用表达式

公用表表达式(CTE)可以被认为是在单个SELECT,INSERT,UPDATE,DELETE或CREATE VIEW语句的执行范围内定义的临时结果集。CTE类似于派生表,因为它不作为对象存储,并且仅在查询期间持续。与派生表不同,CTE可以是自引用的,并且可以在同一查询中多次引用。

CTE由表示CTE的表达式名称,AS关键字和SELECT语句组成。定义CTE后,可以在SELECT,INSERT,UPDATE或DELETE语句中像表或视图一样引用它。CTE也可以在CREATE VIEW语句中用作其定义SELECT语句的一部分。

小结:

getMetaData():获取源表、目标表、的元数据(主要是schema 等信息)
获取的元数据同样存储在QB/QBParseInfo 中。
①获取source table 的元数据,如果一个table 实际上是一个view,将其重写为view 的定义;
②递归的为每个子查询中的源表获得元数据;
③获取所有destination table/dir/local dir 的元数据;

至此,才把比较完整的信息传给QB