Hive文件執(zhí)行的方法主要是 CliDriver 中的 processFile撑柔,語句執(zhí)行的主要在 processLine 方法中饼煞,這一篇主要看這兩個方法中主要做了什么蜜猾?
1. processFile
processFile 中最主要的代碼如下:
rc = processReader(bufferReader);
我們可以看到其實 processReader 只是把文件讀進來泣特,去掉注釋語句,然后對其調(diào)用processLine 方法脱衙,所以我們主要需要分析 processLine 方法。
public int processReader(BufferedReader r) throws IOException {
String line;
StringBuilder qsb = new StringBuilder();
while ((line = r.readLine()) != null) {
// Skipping through comments
if (! line.startsWith("--")) {
qsb.append(line + "\n");
}
}
return (processLine(qsb.toString()));
}
2. processLine
2.1 整體代碼結(jié)構(gòu)
- processLine中的主要代碼如下:
public int processLine(String line, boolean allowInterrupting) {
...............
// we can not use "split" function directly as ";" may be quoted
int lastRet = 0, ret = 0;
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;
}
ret = processCmd(command);
command = "";
lastRet = ret;
}
................
}
從以上代碼可以看出例驹,processLine 會把我們寫的所有SQL語句用分號 ; 拆開然后對每一句執(zhí)行 processCmd
方法捐韩,有一句不成功就會返回失敗
- processCmd 方法
public int processCmd(String cmd) {
CliSessionState ss = (CliSessionState) SessionState.get();
ss.setLastCommand(cmd);
ss.updateThreadName();
// Flush the print stream, so it doesn't include output from the last command
ss.err.flush();
String cmd_trimmed = cmd.trim();
String[] tokens = tokenizeCmd(cmd_trimmed);
int ret = 0;
// 處理退出命令
if (cmd_trimmed.toLowerCase().equals("quit") || cmd_trimmed.toLowerCase().equals("exit")) {
ss.close();
System.exit(0);
} else if (tokens[0].equalsIgnoreCase("source")) { // 處理source 開頭語句
String cmd_1 = getFirstCmd(cmd_trimmed, tokens[0].length());
cmd_1 = .........
.........
ret = processFile(cmd_1); // source filename; 最終回歸到processFile方法
.........
} else if (cmd_trimmed.startsWith("!")) { // !開頭的命令則調(diào)用ShellCmdExecutor中的 execute方法執(zhí)行
String shell_cmd = cmd_trimmed.substring(1);
shell_cmd = .........
.........
ShellCmdExecutor executor = new ShellCmdExecutor(shell_cmd, ss.out, ss.err);
ret = executor.execute();
.........
} else { // local mode
..........
CommandProcessor proc = CommandProcessorFactory.get(tokens, (HiveConf) conf);
ret = processLocalCmd(cmd, proc, ss); // 這個是大多數(shù)sql的主要執(zhí)行方法
..........
}
從以上代碼可以看到最需要關(guān)注的方法是 processLocalCmd
- processLocalCmd 方法
int processLocalCmd(String cmd, CommandProcessor proc, CliSessionState ss) {
.............
int ret = 0;
.............
if (proc != null) {
if (proc instanceof Driver) {
Driver qp = (Driver) proc;
.........
ret = qp.run(cmd).getResponseCode(); // 當 proc 屬于 Driver對象的時候主要執(zhí)行的方法
.........
} else {
String firstToken = tokenizeCmd(cmd.trim())[0];
String cmd_1 = getFirstCmd(cmd.trim(), firstToken.length());
.........
CommandProcessorResponse res = proc.run(cmd_1); // 當 proc 為非 Driver對象的時候主要執(zhí)行的方法
.........
ret = res.getResponseCode();
}
.............
return ret;
}
從上面代碼可以看出,proc這個對象有Driver類和非Driver類兩種鹃锈,所以重點需要關(guān)注什么時候得到的是 Driver對象荤胁,什么時候得到的是非Driver對象,才能知道什么樣的sql執(zhí)行Driver類中的run方法屎债,什么樣的sql執(zhí)行非Driver類中的run方法仅政。
所以接下來就具體看proc這個對象是如何獲取的。
2.2 proc的獲取(CommandProcessor對象)
proc的獲取是通過processCmd中的如下這句代碼獲取的盆驹,其中tokens是一句SQL語句用空白符分隔開的字符串數(shù)組(即組成SQL的一個一個詞組成的數(shù)組)
CommandProcessor proc = CommandProcessorFactory.get(tokens, (HiveConf) conf);
工廠中對應(yīng)的get方法如下:
public static CommandProcessor get(String[] cmd, HiveConf conf) {
CommandProcessor result = getForHiveCommand(cmd, conf);
if (result != null) return result;
.........
if (conf == null) return new Driver();
Driver drv = mapDrivers.get(conf);
if (drv == null) {
drv = new Driver();
mapDrivers.put(conf, drv);
} else {
drv.resetQueryState();
}
drv.init();
return drv;
}
}
從代碼中可知除了getForHiveCommand(cmd, conf)能返回非空對象的情況外圆丹,其他情況得到的都是Driver對象,并且Driver對象可復(fù)用躯喇。
所以要看getForHiveCommand方法辫封,這個方法中就簡單調(diào)用了 getForHiveCommandInternal
方法,代碼如下
public static CommandProcessor getForHiveCommandInternal(String[] cmd, HiveConf conf, boolean testOnly)
HiveCommand hiveCommand = HiveCommand.find(cmd, testOnly);
if (hiveCommand == null || isBlank(cmd[0])) {
return null;
}
Set<String> availableCommands = new HashSet<String>();
// HIVE_SECURITY_COMMAND_WHITELIST默認值:set,reset,dfs,add,list,delete,reload,compile
for (String availableCommand : conf.getVar(HiveConf.ConfVars.HIVE_SECURITY_COMMAND_WHITELIST).split(",")) {
availableCommands.add(availableCommand.toLowerCase().trim());
}
if (cmd.length > 1 && "reload".equalsIgnoreCase(cmd[0]) && "function".equalsIgnoreCase(cmd[1])) {
// special handling for SQL "reload function"
return null;
}
switch (hiveCommand) {
case SET:
return new SetProcessor();
case RESET:
return new ResetProcessor();
case DFS:
SessionState ss = SessionState.get();
return new DfsProcessor(ss.getConf());
case ADD:
return new AddResourceProcessor();
case LIST:
return new ListResourceProcessor();
case DELETE:
return new DeleteResourceProcessor();
case COMPILE:
return new CompileProcessor();
case RELOAD:
return new ReloadProcessor();
case CRYPTO:
return new CryptoProcessor(SessionState.get().getHdfsEncryptionShim(), conf);
.........
}
}
// hiveCommand的獲取廉丽,HiveCommand是一個枚舉類倦微,
// 包含枚舉對象 SET(),RESET(),DFS(),CRYPTO(true),ADD(),LIST(),RELOAD(),DELETE(),COMPILE();
public static HiveCommand find(String[] command, boolean findOnlyForTesting) {
if (null == command){
return null;
}
String cmd = command[0];
if (cmd != null) {
cmd = cmd.trim().toUpperCase();
if (command.length > 1 && "role".equalsIgnoreCase(command[1])) {
// special handling for set role r1 statement
return null;
} else if(command.length > 1 && "from".equalsIgnoreCase(command[1])) {
//special handling for SQL "delete from <table> where..."
return null;
} else if(command.length > 1 && "set".equalsIgnoreCase(command[0]) && "autocommit".equalsIgnoreCase(command[1])) {
return null;//don't want set autocommit true|false to get mixed with set hive.foo.bar...
} else if (COMMANDS.contains(cmd)) {
HiveCommand hiveCommand = HiveCommand.valueOf(cmd);
return hiveCommand;
}
}
return null;
}
// COMMANDS的來源
private static final Set<String> COMMANDS = new HashSet<String>();
static {
for (HiveCommand command : HiveCommand.values()) {
COMMANDS.add(command.name());
}
}
由上述代碼可以看到不同SQL會得到不同的CommandProcessor對象proc,主要有如下這些情況:
SQL語句 | 返回的CommandProcessor對象 |
---|---|
set .... 并且不是 set role ..../ set autocommit... | new SetProcessor() |
reset .... | new ResetProcessor() |
dfs .... | new DfsProcessor(ss.getConf()) |
crypto .... | new CryptoProcessor(SessionState.get().getHdfsEncryptionShim(), conf) |
add .... | new AddResourceProcessor() |
list .... | new ListResourceProcessor() |
reload .... | new ReloadProcessor() |
delete .... 并且不是 delete from ...... | new DeleteResourceProcessor() |
compile .... | new CompileProcessor() |
其他語句 | Driver對象 |
3. 總結(jié)
由上面源碼的分析可以得出正压,除了set ...欣福、dfs ...、add ... 這種不是對表中數(shù)據(jù)進行操作的sql語句焦履,其他都是通過Driver類對象的run方法執(zhí)行劣欢。
下一節(jié)主要看Driver中的run方法如何運行sql語句棕诵。