欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Hive源码阅读–查询分析器–SemanticAnalyzer

程序员文章站 2022-04-15 20:08:18
SemanticAnalyzer可以说是对查询所有的优化都在其中。代码量有13514行,我们主要关注他对analyzeInternal的实现。 void analyzeInternal(ASTNode ast, PlannerContext plannerCtx) throws SemanticException { // 1.从语法树生成解析树 LOG.info("Starting Semantic Analysis"); processPositionAlias(ast);...

SemanticAnalyzer可以说是对查询所有的优化都在其中。代码量有13514行,我们主要关注他对analyzeInternal的实现。

 void analyzeInternal(ASTNode ast, PlannerContext plannerCtx) throws SemanticException {
    // 1.从语法树生成解析树
    LOG.info("Starting Semantic Analysis"); 
    processPositionAlias(ast);
    // 语义解析
    if (!genResolvedParseTree(ast, plannerCtx)) {
      return;
    }

    // 2. 从解析树生成逻辑执行计划
    // 解析QB生成一个Operator Tree,其实是生成一个DAG
    Operator sinkOp = genOPTree(ast, plannerCtx);

    if (!unparseTranslator.isEnabled() && tableMask.isEnabled()) {
      // 重写表列的遮蔽
      ASTNode tree = rewriteASTWithMaskAndFilter(tableMask, ast, ctx.getTokenRewriteStream(),
              ctx, db, tabNameToTabObject, ignoredTokens);
      if (tree != ast) {
        ctx.setSkipTableMasking(true);
        init(true);
        // change the location of position alias process here
        processPositionAlias(tree);
        genResolvedParseTree(tree, plannerCtx);
        if (this instanceof CalcitePlanner) {
          ((CalcitePlanner) this).resetCalciteConfiguration();
        }
        sinkOp = genOPTree(tree, plannerCtx);
      }
    }

    // 3. 从逻辑执行计划推导结果集模式
    if (createVwDesc != null && !this.ctx.isCboSucceeded()) {
      resultSchema = convertRowSchemaToViewSchema(opParseCtx.get(sinkOp).getRowResolver());
    } else {
      // 在hive1.1.0之后,feature是默认开启的,他可以自动优化多join的顺序,并选择适合的join算法。
      // 如果满足以下条件,则resultSchema将为null
      // (1) cbo已禁用;
      // (2) cbo已通过AST返回路径启用 (无论成功与否,resultSchema将被重新初始化)
      // 只有使用新的返回路径启用cbo并且它会不为null最后才会成功
      if (resultSchema == null) {
        resultSchema = convertRowSchemaToResultSetSchema(opParseCtx.get(sinkOp).getRowResolver(),
            HiveConf.getBoolVar(conf, HiveConf.ConfVars.HIVE_RESULTSET_USE_UNIQUE_COLUMN_NAMES));
      }
    }

    // 4.为优化器和物理编译器生成解析上下文
    copyInfoToQueryProperties(queryProperties);
    ParseContext pCtx = new ParseContext(queryState, opToPartPruner, opToPartList, topOps,
        new HashSet<JoinOperator>(joinContext.keySet()),
        new HashSet<SMBMapJoinOperator>(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);

    // 5. 创建视图
    if (createVwDesc != null) {
      if (ctx.getExplainAnalyze() == AnalyzeState.RUNNING) {
        return;
      }
      
      if (!ctx.isCboSucceeded()) {
        saveViewDefinition();
      }

      // 此时验证create view语句,createVwDesc将获取所有信息以进行语义检查
      validateCreateView();

      if (!createVwDesc.isMaterialized()) {
        // 由于我们仅创建视图(不执行视图),因此不需要优化或转换计划(实际上,这些过程会干扰视图的创建)。 
        // 因此,跳过此方法的其余部分。
        ctx.setResDir(null);
        ctx.setResFile(null);

        try {
          PlanUtils.addInputsForView(pCtx);
        } catch (HiveException e) {
          throw new SemanticException(e);
        }

        // 生成创建视图的语句如果配置LineageLogger hook ,
        // 添加计算血缘信息
        Set<String> postExecHooks = Sets.newHashSet(Splitter.on(",").trimResults()
            .omitEmptyStrings()
            .split(Strings.nullToEmpty(HiveConf.getVar(conf, HiveConf.ConfVars.POSTEXECHOOKS))));
        if (postExecHooks.contains("org.apache.hadoop.hive.ql.hooks.PostExecutePrinter")
            || postExecHooks.contains("org.apache.hadoop.hive.ql.hooks.LineageLogger")
            || postExecHooks.contains("org.apache.atlas.hive.hook.HiveHook")) {
          ArrayList<Transform> transformations = new ArrayList<Transform>();
          transformations.add(new HiveOpConverterPostProc());
          transformations.add(new Generator());
          for (Transform t : transformations) {
            pCtx = t.transform(pCtx);
          }
          // 使用视图名称
          SessionState.get().getLineageState()
              .mapDirToOp(new Path(createVwDesc.getViewName()), sinkOp);
        }
        return;
      }
    }

    // 6. 生成表访问统计信息
    if (HiveConf.getBoolVar(this.conf, HiveConf.ConfVars.HIVE_STATS_COLLECT_TABLEKEYS)) {
      TableAccessAnalyzer tableAccessAnalyzer = new TableAccessAnalyzer(pCtx);
      setTableAccessInfo(tableAccessAnalyzer.analyzeTableAccess());
    }

    // 7.执行逻辑优化
    if (LOG.isDebugEnabled()) {
      LOG.debug("Before logical optimization\n" + Operator.toString(pCtx.getTopOps().values()));
    }
    Optimizer optm = new Optimizer();
    optm.setPctx(pCtx);
    // 初始化的时候,加入很多优化器
    optm.initialize(conf);
    // 优化逻辑执行计划
    pCtx = optm.optimize();
    if (pCtx.getColumnAccessInfo() != null) {
      // 为视图列授权设置ColumnAccessInfo
      setColumnAccessInfo(pCtx.getColumnAccessInfo());
    }
    FetchTask origFetchTask = pCtx.getFetchTask();
    if (LOG.isDebugEnabled()) {
      LOG.debug("After logical optimization\n" + Operator.toString(pCtx.getTopOps().values()));
    }

    // 8. 可能的话,进行列裁剪
    boolean isColumnInfoNeedForAuth = SessionState.get().isAuthorizationModeV2()
        && HiveConf.getBoolVar(conf, HiveConf.ConfVars.HIVE_AUTHORIZATION_ENABLED);
    if (isColumnInfoNeedForAuth
        || HiveConf.getBoolVar(this.conf, HiveConf.ConfVars.HIVE_STATS_COLLECT_SCANCOLS)) {
      ColumnAccessAnalyzer columnAccessAnalyzer = new ColumnAccessAnalyzer(pCtx);
      // 查看列访问信息
      setColumnAccessInfo(columnAccessAnalyzer.analyzeColumnAccess(this.getColumnAccessInfo()));
    }

    // 9. 优化物理操作树并转换为目标执行引擎
    if (!ctx.getExplainLogical()) {
      TaskCompiler compiler = TaskCompilerFactory.getCompiler(conf, pCtx);
      compiler.init(queryState, console, db);
      compiler.compile(pCtx, rootTasks, inputs, outputs);
      fetchTask = pCtx.getFetchTask();
    }
    LOG.info("Completed plan generation");

    // 10. 将访问的列放入readEntity
    if (HiveConf.getBoolVar(this.conf, HiveConf.ConfVars.HIVE_STATS_COLLECT_SCANCOLS)) {
      putAccessedColumnsToReadEntity(inputs, columnAccessInfo);
    }

    // 11. 如果需要检查,只会在limit范围内进行
    if (!ctx.isExplainSkipExecution()) {
      enforceScanLimits(pCtx, origFetchTask);
    }

    return;
  }

里面涵盖了 语法树–》解析树–》逻辑执行计划–》结果结果集模式–》构建上下文–》查询视图–》获取表信息–》逻辑优化–》获取列信息–》查询所需数据–》检查。

全套的流程,从查询SQL到最后的结果,流程全部都在这里。

然后看一下重中之重的语义解析,

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

    // 1. 分析处理别名

    // 2. 分析创建表命令
    if (ast.getToken().getType() == HiveParser.TOK_CREATETABLE) {
      // 如果不是CTAS(create table ... as select ...)语法,直接返回即可,
      // 如果是,执行setCommandType
      if ((child = analyzeCreateTable(ast, qb, plannerCtx)) == null) {
        return false;
      }
    } else {
      queryState.setCommandType(HiveOperation.QUERY);
    }

    // 3. 分析创建视图命令
    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());

    // 4. AST node  ==》 QB
    Phase1Ctx ctx_1 = initPhase1Ctx();
    preProcessForInsert(child, qb);
    // doPhase1 负责把 ASTTree 分解存入对应的QB
    if (!doPhase1(child, qb, ctx_1, plannerCtx)) {
      // 如果 phase1Result 错误返回false
      return false;
    }
    LOG.info("Completed phase 1 of Semantic Analysis");

    // 5. 处理元数据相关
    // getMetaData 负责把表、字段等元数据信息存入QB
    getMetaData(qb, createVwDesc == null);
    LOG.info("Completed getting MetaData in Semantic Analysis");

    plannerCtx.setParseTreeAttr(child, ctx_1);

    return true;
  }

本文地址:https://blog.csdn.net/qq_41106844/article/details/108195151