用过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'
完成了变量的替换。