Hive 源码解读 CliDriver HQL 语句拆分

Posted @SmartSi

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Hive 源码解读 CliDriver HQL 语句拆分相关的知识,希望对你有一定的参考价值。

Hive 版本:2.3.4

1. HQL 文件拆分

1.1 入口

从上一篇文章 Hive 源码解读 CliDriver HQL 读取与参数解析 中了解到,如果 Hive CLI 命令行中指定了 -f <query-file> 选项,需要调用 processFile 来执行文件中的一个或者多个 HQL 语句:

try 
  if (ss.fileName != null) 
    return cli.processFile(ss.fileName);
  
 catch (FileNotFoundException e) 
  ...

除了 -f <query-file> 选项需要调用 processFile 处理 HQL 文件之外,-i <filename> 选项也需要:

public void processInitFiles(CliSessionState ss) throws IOException 
    ...
    // 调用 processFile 处理初始文件
    for (String initFile : ss.initFiles) 
      int rc = processFile(initFile);
      ...
    
    ...

1.2 文件处理 processFile

现在具体看一下 Hive 是如何通过 processFile 处理一个 HQL 文件中的语句。processFile 函数根据输入的文件路径名读取文件,实际处理逻辑交由 processReader 函数处理:

public int processFile(String fileName) throws IOException 
    Path path = new Path(fileName);
    FileSystem fs;
    // 绝对路径
    if (!path.toUri().isAbsolute()) 
      fs = FileSystem.getLocal(conf);
      path = fs.makeQualified(path);
     else 
      fs = FileSystem.get(path.toUri(), conf);
    
    BufferedReader bufferReader = null;
    int rc = 0;
    try 
      bufferReader = new BufferedReader(new InputStreamReader(fs.open(path)));
      rc = processReader(bufferReader);
     finally 
      IOUtils.closeStream(bufferReader);
    
    return rc;

我们可以看到 processReader 处理逻辑比较简单,只是去掉注释行,其他的调用 processLine 方法处理:

public int processReader(BufferedReader r) throws IOException 
    String line;
    StringBuilder qsb = new StringBuilder();
    // 一行一行的处理
    while ((line = r.readLine()) != null) 
      // 跳过注释
      if (! line.startsWith("--")) 
        qsb.append(line + "\\n");
      
    
    return (processLine(qsb.toString()));

2. HQL 语句拆分

2.1 入口

除了上述 processFile 最终会调用 processLine 处理之外,hive -e <query-string> 执行查询字符串时也会调用:

CliDriver#executeDriver

if (ss.execString != null) 
    int cmdProcessStatus = cli.processLine(ss.execString);
    return cmdProcessStatus;

除此之外,在交互式模式下从标准化输入中解析出 HQL 语句也会交由 processLine 来执行:

CliDriver#executeDriver

while ((line = reader.readLine(curPrompt + "> ")) != null) 
  ...
  // 直到遇到分号结尾才确定一个一条 HQL 语句的终结
  if (line.trim().endsWith(";") && !line.trim().endsWith("\\\\;")) 
    line = prefix + line;
    // HQL 语句的执行实际上交由 processLine 处理
    ret = cli.processLine(line, true);
    ...
   else 
    ...
  

2.2 行处理 processLine

现在具体看一下 Hive 是如何通过 processLine 处理一个 HQL 语句。

首先第一部分的逻辑是对中断信号的处理。当调用的是 processLine(line, true) 方法时,表示当前作业可以被允许打断。当第一次用户输入 Ctrl+C 时,在交互式界面中输出 Interrupting... Be patient, this might take some time. 表示正在中断中,CLI 线程会中断并杀死正在进行的 MR 作业。当第一次用户输入 Ctrl+C 时,则会退出 JVM:

SignalHandler oldSignal = null;
Signal interruptSignal = null;
if (allowInterrupting) 
  interruptSignal = new Signal("INT");
  oldSignal = Signal.handle(interruptSignal, new SignalHandler() 
    private boolean interruptRequested;
    @Override
    public void handle(Signal signal) 
      boolean initialRequest = !interruptRequested;
      interruptRequested = true;
      if (!initialRequest) 
        // 第二次 ctrl+c 退出 JVM
        console.printInfo("Exiting the JVM");
        System.exit(127);
      
      // 第一次 Ctrl+C 中断 CLI 线程、停止当前语句并杀死正在进行的 MR 作业
      console.printInfo("Interrupting... Be patient, this might take some time.");
      console.printInfo("Press Ctrl+C again to kill JVM");
      HadoopJobExecHelper.killRunningJobs();
      TezJobExecHelper.killRunningJobs();
      HiveInterruptUtils.interrupt();
    
  );

第二部分逻辑是对 HQL 语句进行拆分:

// 拆分为不同的 HQL 命令
List<String> commands = splitSemiColon(line);
String command = "";
for (String oneCmd : commands) 
  if (StringUtils.endsWith(oneCmd, "\\\\")) 
    command += StringUtils.chop(oneCmd) + ";";
    continue;
   else 
    command += oneCmd;
  
  if (StringUtils.isBlank(command)) 
    continue;
  
  // 交由 processCmd 处理命令
  ret = processCmd(command);
  command = "";
  lastRet = ret;
  boolean ignoreErrors = HiveConf.getBoolVar(conf, HiveConf.ConfVars.CLIIGNOREERRORS);
  if (ret != 0 && !ignoreErrors) 
    CommandProcessorFactory.clean((HiveConf) conf);
    return ret;
  

上游处理时只是针对每行末尾是分号 ; 时才调用 processLine 进行处理,所以一行中可能存在多条 HQL 命令,如下所示一行中就存在两条 HQL 命令:

hive > USE default;SELECT concat(uid, ";") FROM behavior LIMIT 1;

首先第一步就是将 HQL 语句拆分为不同的 HQL 命令,需要注意的是不能使用 split 函数直接根据 ; 进行拆分,因为 HQL 语句中可能存在 ;,例如上述语句中的第二个命令,所以需要单独调用 splitSemiColon 方法进行处理。最后将拆分后的 HQL 命令交由 processCmd 方法执行。

以上是关于Hive 源码解读 CliDriver HQL 语句拆分的主要内容,如果未能解决你的问题,请参考以下文章