其实根据hivehook的插入阶段来看,我们是可以拿到hive的执行计划的。

本次就从这个执行计划下手,在hook中实现血缘的解析。

  • Pre-semantic-analyzer hooks:在Hive在查询字符串上运行语义分析器之前调用。
  • Post-semantic-analyzer hooks:在Hive在查询字符串上运行语义分析器之后调用。
  • Pre-driver-run hooks:在driver执行查询之前调用。
  • Post-driver-run hooks:在driver执行查询之后调用。
  • Pre-execution hooks:在执行引擎执行查询之前调用。
  • Post-execution hooks:在查询执行完成之后以及将结果返回给用户之前调用。
  • Failure-execution hooks:当查询执行失败时调用。
public class LineageHook implements ExecuteWithHookContext {
    private static final HashSet<String> OPERATION_NAMES = new HashSet<String>();
    private static final HashSet<String> INPUTS = new HashSet<String>();
    private static final HashSet<String> OUTPUTS = new HashSet<String>();

    static {
        OPERATION_NAMES.add(HiveOperation.QUERY.getOperationName());
        OPERATION_NAMES.add(HiveOperation.CREATETABLE_AS_SELECT.getOperationName());
        OPERATION_NAMES.add(HiveOperation.ALTERVIEW_AS.getOperationName());
        OPERATION_NAMES.add(HiveOperation.CREATEVIEW.getOperationName());
        OPERATION_NAMES.add(HiveOperation.LOAD.getOperationName());//在原有基础上,开放load语句
    }
    @Override
    public void run(HookContext hookContext) throws Exception {
        INPUTS.clear();
        OUTPUTS.clear();

        QueryPlan plan = hookContext.getQueryPlan();
        LineageCtx.Index index = hookContext.getIndex();
        SessionState ss = SessionState.get();
        if (ss != null && index != null
                && OPERATION_NAMES.contains(plan.getOperationName())
                && !plan.isExplain()) {

            System.out.println(plan.getOperationName());

            //输出
            for (WriteEntity output : plan.getOutputs()) {
                Entity.Type entityType = output.getType();
                if (entityType == Entity.Type.TABLE
                        || entityType == Entity.Type.PARTITION
                        || entityType == Entity.Type.LOCAL_DIR //放行LOCAL_DIR
                        || entityType == Entity.Type.DFS_DIR //放行DFS_DIR
                ) {
                    Table trgTb = output.getTable();
                    String trgTbName = null;
                    if (trgTb!=null) {
                        trgTbName = trgTb.getDbName()+"."+trgTb.getTableName();
                    }else {
                        trgTbName = output.getD().toString();
                        //hdfs://master:8020/tmp/hive/admin/27808155-878a-4446-9c4e-a2f3388301fc/hive_2020-06-19_16-47-52_939_789950828629061887-1/-mr-10001
                        if (trgTbName.matches("hdfs://.+/tmp/hive/.+")) {// 过滤MR中间临时落地数据的路径
                            continue;
                        }
                    }
//                    System.out.println("target table "+trgTbName);l
                    if (OUTPUTS.contains(trgTbName)) {
                        continue;
                    }else {
                        OUTPUTS.add(trgTbName);
                    }
                    break;
                }
            }

            if (OUTPUTS.size()==0) {//如果没有输出,不获取输入,相当于屏蔽了无输出的简单Query
                return;
            }

            //输入
            for (ReadEntity input : plan.getInputs()) {
                Entity.Type entityType = input.getType();
                if (entityType == Entity.Type.TABLE
                        || entityType == Entity.Type.PARTITION
                        || entityType == Entity.Type.LOCAL_DIR
                        || entityType == Entity.Type.DFS_DIR
                ) {
                    Table srcTb = input.getTable();

                    String srcTbName = null;
                    if (srcTb!=null) {
                        srcTbName = srcTb.getDbName()+"."+srcTb.getTableName();
                    }else {
                        srcTbName = input.getD().toString();
                        if (srcTbName.matches("hdfs://.+/tmp/hive/.+")) {
                            continue;
                        }
                    }
                    INPUTS.add(srcTbName);  //用HashSet装输入源名称,因为多分区输入时会有多个ReadEntity 这些Entity表名是相同的
//                    System.out.println("src table "+srcTbName);
                }
            }

            System.out.println("INPUT="+String.join(",",INPUTS));
            System.out.println("OUTPUT="+String.join(",",OUTPUTS));
        }
    }
}

项目依赖跟上一篇hivehook一样。

一次性执行在hive命令行可以执行下面两条命令。但会话结束就结束了。
add jar /opt/module/hive-hook-1.0-SNAPSHOT.jar
set hive.exec.post.hooks=cc.eslink.hook.LineageHook;
永久生效的话,上传jar包,添加jar包到hive-lib,修改hive-site.xml
/opt/cloudera/parcels/CDH/lib/hive/lib

这样就进一步拿到了血缘关系。