用过hive的人都知道,可以通过在cli向hive传递参数,变量等,这里其实是通过下面两个类实现的。

org.apache.hadoop.hive.ql.processors.SetProcessor类org.apache.hadoop.hive.ql.parse.VariableSubstitution类

其中SetProcessor类定义了对set 命令的处理,VariableSubstitution类负责把变量值进行转换。

VariableSubstitution用来实现在解析hive命令时把特殊字符进行转换,如果是以!开头的shell命令的话,直接在 CliDriver类的 processCmd方法中做转换

else if (cmd_trimmed.startsWith("!")) {      String shell_cmd = cmd_trimmed.substring(1);      shell_cmd = new VariableSubstitution().substitute(ss.getConf(), shell_cmd);

其他的命令会在具体的CommandProcessor 实现类中做转换。关于CommandProcessor 类这里简单说下,后面会相信分析:

CommandProcessor 的调用是在在CliDriver类的processLocalCmd 方法中发生的。

int processLocalCmd (String cmd, CommandProcessor proc, CliSessionState ss)

这里传入一个CommandProcessor 的实例,函数中会判断具体的实现类。如果实现类是Driver类(即执行的命令是sql),会调用Driver的run方法。

           if (proc instanceof Driver) {            Driver qp = (Driver) proc;.....            ret = qp.run(cmd).getResponseCode();

如果是其他实现类(即执行的命令是set/add/compile等),会调用对应实现类(commandprocessor相关类)的run方法。

下面分析下VariableSubstitution的具体实现:

VariableSubstitution类有两个控制参数

hive.variable.substitute  控制是否打开Substitution功能,默认是truehive.variable.substitute.depth ,控制可以匹配到几层,默认是40

主要有getSubstitute和substitute方法。

方法的调用顺序是

substitute----->getSubstitute

其中substitute 方法如下:

public String substitute ( HiveConf conf, String expr) {    if (conf.getBoolVar( ConfVars.HIVEVARIABLESUBSTITUTE)){ //判断hive.variable.substitute是否设置true      l4j.debug( "Substitution is on: "+expr);    } else {      return expr;    }    if (expr == null) {      return null;    }    Matcher match = varPat.matcher( "");    String eval = expr;    for(int s=0;s

其中getSubstitute会调用SetProcessor类,来解析命令。

比如,以hiveconf:开头的命令会经过如下的处理:

      if (var.startsWith( SetProcessor.HIVECONF_PREFIX)){        val = conf.get(var.substring( SetProcessor.HIVECONF_PREFIX.length()));      }

但是对于使用命名空间如hiveconf,system,env的,前缀则不可少,hivevar可以不需要前缀。

    if (val == null){      if(var.startsWith( SetProcessor.HIVEVAR_PREFIX)){  //这里HIVEVAR_PREFIX的值是hivevar:        val =  SessionState.get().getHiveVariables().get(var.substring( SetProcessor.HIVEVAR_PREFIX.length()));      } else {        val = SessionState.get().getHiveVariables().get(var);      }    }

这里有个例子:

set system:testdate=20140816; select * from chinacache_log where dt >= '${system:testdate}'  limit 5;

1)第一个set命令在经过processLocalCmd 方法处理之后,传入substitute的expr是20140816 (取=号之后的数据)

因为不能匹配到正则,直接回返回。

String eval = expr;.....      if (!match.find()) {         return eval;      }

2)第2条sql

 select * from xxxx where f1>='${system:testdate}'

由Driver处理后,传入的expr是

select * from peter001 where f1>='${system:testdate}'

由match.group()匹配到${system:testdate},然后通过

var.substring(2, var.length()-1)

去除掉${},并调用getSubstitute获取设置的变量值,并最终生成一个有效的sql。

最终由

eval.substring(0, match.start())+val+eval.substring(match.end())

返回的值是

select * from peter001 where f1>='20140816'

完成了变量的替换。