用过hive的人都知道,可以通过在cli向hive传递参数,变量等,这里其实是通过下面两个类实现的。
1
2
|
org.apache.hadoop.hive.ql.processors.SetProcessor类 org.apache.hadoop.hive.ql.parse.VariableSubstitution类 |
其中SetProcessor类定义了对set 命令的处理,VariableSubstitution类负责把变量值进行转换。
VariableSubstitution用来实现在解析hive命令时把特殊字符进行转换,如果是以!开头的shell命令的话,直接在 CliDriver类的 processCmd方法中做转换
1
2
3
|
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 方法中发生的。
1
|
int processLocalCmd (String cmd, CommandProcessor proc, CliSessionState ss)
|
这里传入一个CommandProcessor 的实例,函数中会判断具体的实现类。如果实现类是Driver类(即执行的命令是sql),会调用Driver的run方法。
1
2
3
4
|
if (proc instanceof Driver) {
Driver qp = (Driver) proc;
..... ret = qp.run(cmd).getResponseCode();
|
如果是其他实现类(即执行的命令是set/add/compile等),会调用对应实现类(commandprocessor相关类)的run方法。
下面分析下VariableSubstitution的具体实现:
VariableSubstitution类有两个控制参数
1
2
|
hive.variable.substitute 控制是否打开Substitution功能,默认是 true
hive.variable.substitute.depth ,控制可以匹配到几层,默认是 40
|
主要有getSubstitute和substitute方法。
方法的调用顺序是
1
|
substitute----->getSubstitute |
其中substitute 方法如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
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<conf.getIntVar( ConfVars.HIVEVARIABLESUBSTITUTEDEPTH); s++) {
match.reset(eval);
if (!match.find()) { //判断输入的命令是否匹配"\\$\\{[^\\}\\$\u0020]+\\}",如果不匹配直接返回
return eval;
}
String var = match.group();
var = var.substring( 2 , var.length()- 1 ); // remove ${ .. }
String val = getSubstitute(conf, var);
if (val == null ) {
l4j.debug( "Interpolation result: " +eval);
return eval; // return literal, no substitution found
}
// substitute
eval = eval.substring( 0 , match.start())+val+eval.substring(match.end()); //完成替换
}
throw new IllegalStateException( "Variable substitution depth too large: "
+ conf.getIntVar(ConfVars.HIVEVARIABLESUBSTITUTEDEPTH) + " " + expr);
}
|
其中getSubstitute会调用SetProcessor类,来解析命令。
比如,以hiveconf:开头的命令会经过如下的处理:
1
2
3
|
if (var.startsWith( SetProcessor.HIVECONF_PREFIX)){
val = conf.get(var.substring( SetProcessor.HIVECONF_PREFIX.length()));
}
|
但是对于使用命名空间如hiveconf,system,env的,前缀则不可少,hivevar可以不需要前缀。
1
2
3
4
5
6
7
|
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);
}
}
|
这里有个例子:
1
2
|
set system:testdate= 20140816 ;
select * from chinacache_log where dt >= '${system:testdate}' limit 5 ;
|
1)第一个set命令在经过processLocalCmd 方法处理之后,传入substitute的expr是20140816 (取=号之后的数据)
因为不能匹配到正则,直接回返回。
1
2
3
4
5
|
String eval = expr; ..... if (!match.find()) {
return eval;
}
|
2)第2条sql
1
|
select * from xxxx where f1>= '${system:testdate}'
|
由Driver处理后,传入的expr是
1
|
select * from peter001 where f1>= '${system:testdate}'
|
由match.group()匹配到${system:testdate},然后通过
1
|
var.substring( 2 , var.length()- 1 )
|
去除掉${},并调用getSubstitute获取设置的变量值,并最终生成一个有效的sql。
最终由
1
|
eval.substring( 0 , match.start())+val+eval.substring(match.end())
|
返回的值是
1
|
select * from peter001 where f1>= '20140816'
|
完成了变量的替换。
本文转自菜菜光 51CTO博客,原文链接:http://blog.51cto.com/caiguangguang/1541758,如需转载请自行联系原作者