当前位置: 移动技术网 > IT编程>数据库>Mysql > Hive源码阅读–查询分析器–SemanticAnalyzer

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

2020年08月01日  | 移动技术网IT编程  | 我要评论
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

如您对本文有疑问或者有任何想说的,请点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网