diff --git a/beeline/src/java/org/apache/hive/beeline/BeeLine.java b/beeline/src/java/org/apache/hive/beeline/BeeLine.java index 406059d..28ff133 100644 --- a/beeline/src/java/org/apache/hive/beeline/BeeLine.java +++ b/beeline/src/java/org/apache/hive/beeline/BeeLine.java @@ -94,6 +94,8 @@ import org.apache.hive.beeline.cli.CliOptionsProcessor; import org.apache.hive.jdbc.Utils; +import static org.apache.hadoop.hive.metastore.MetaStoreUtils.DEFAULT_DATABASE_NAME; + /** * A console SQL shell with command completion. *

@@ -132,6 +134,7 @@ private List batch = null; private final Reflector reflector; private String dbName = null; + private String currentDatabase = null; private History history; private boolean isBeeLine = true; @@ -692,6 +695,7 @@ int initArgsFromCliVars(String[] args) { if (!commands.isEmpty()) { embeddedConnect(); connectDBInEmbededMode(); + updateOptsForCli(); for (Iterator i = commands.iterator(); i.hasNext(); ) { String command = i.next().toString(); debug(loc("executing-command", command)); @@ -808,6 +812,13 @@ private String obtainPasswordFromFile(String passwordFilePath) { } } + private void updateOptsForCli() { + getOpts().updateBeeLineOptsFromConf(); + getOpts().setShowHeader(false); + getOpts().setOutputFormat("dsv"); + getOpts().setDelimiterForDSV(' '); + } + /** * Start accepting input from stdin, and dispatch it * to the appropriate {@link CommandHandler} until the @@ -833,10 +844,7 @@ public int begin(String[] args, InputStream inputStream) throws IOException { return code; } defaultConnect(false); - getOpts().updateBeeLineOptsFromConf(); - getOpts().setShowHeader(false); - getOpts().setOutputFormat("dsv"); - getOpts().setDelimiterForDSV(' '); + updateOptsForCli(); processInitFiles(opts.getInitFiles()); } @@ -955,6 +963,7 @@ private int execute(ConsoleReader reader, boolean exitOnError) { // trim line line = (line == null) ? null : line.trim(); + if (!dispatch(line) && exitOnError) { return ERRNO_OTHER; } @@ -1056,8 +1065,32 @@ void usage() { output(loc("cmd-usage")); } - private String[] tokenizeCmd(String cmd) { - return cmd.split("\\s+"); + public boolean execCommandWithPrefix(String line) { + Map cmdMap = new TreeMap(); + line = line.substring(1); + for (int i = 0; i < commandHandlers.length; i++) { + String match = commandHandlers[i].matches(line); + if (match != null) { + CommandHandler prev = cmdMap.put(match, commandHandlers[i]); + if (prev != null) { + return error( + loc("multiple-matches", Arrays.asList(prev.getName(), commandHandlers[i].getName()))); + } + } + } + + if (cmdMap.size() == 0) { + return error(loc("unknown-command", line)); + } + if (cmdMap.size() > 1) { + // any exact match? + CommandHandler handler = cmdMap.get(line); + if (handler == null) { + return error(loc("multiple-matches", cmdMap.keySet().toString())); + } + return handler.execute(line); + } + return cmdMap.values().iterator().next().execute(line); } /** @@ -1092,64 +1125,14 @@ boolean dispatch(String line) { if (isHelpRequest(line)) { line = "!help"; } - - if (line.startsWith(COMMAND_PREFIX)) { - Map cmdMap = new TreeMap(); - line = line.substring(1); - for (int i = 0; i < commandHandlers.length; i++) { - String match = commandHandlers[i].matches(line); - if (match != null) { - CommandHandler prev = cmdMap.put(match, commandHandlers[i]); - if (prev != null) { - return error(loc("multiple-matches", - Arrays.asList(prev.getName(), commandHandlers[i].getName()))); - } - } - } - - if (cmdMap.size() == 0) { - return error(loc("unknown-command", line)); - } - if (cmdMap.size() > 1) { - // any exact match? - CommandHandler handler = cmdMap.get(line); - if (handler == null) { - return error(loc("multiple-matches", cmdMap.keySet().toString())); - } - return handler.execute(line); - } - return cmdMap.values().iterator().next() - .execute(line); + if (line.startsWith("!connect")) { + return execCommandWithPrefix(line); } else { - boolean needsUpdate = isConfNeedsUpdate(line); - boolean res = commands.sql(line, getOpts().getEntireLineAsCommand()); - if (needsUpdate) { - getOpts().setHiveConf(getCommands().getHiveConf(false)); - } - return res; + return commands.sql(line, getOpts().getEntireLineAsCommand()); } } /** - * Update the configurations for the CLI mode in the client side - * - * @param line - */ - private boolean isConfNeedsUpdate(String line) { - if (isBeeLine) { - return false; - } - String[] cmds = line.split(";"); - boolean containsSetCMD = false; - for (String s : cmds) { - if (s.toLowerCase().startsWith("set")) { - return true; - } - } - return containsSetCMD; - } - - /** * Test whether a line requires a continuation. * * @param line @@ -1410,7 +1393,27 @@ String getPromptForCli() { HiveConf conf = getCommands().getHiveConf(true); prompt = conf.getVar(HiveConf.ConfVars.CLIPROMPT); prompt = getCommands().substituteVariables(conf, prompt); - return prompt + "> "; + return prompt + getFormattedDb(conf) + "> "; + } + + /** + * Retrieve the current database name string to display, based on the + * configuration value. + * + * @param conf storing whether or not to show current db + * @return String to show user for current db value + */ + String getFormattedDb(HiveConf conf) { + if (!HiveConf.getBoolVar(conf, HiveConf.ConfVars.CLIPRINTCURRENTDB)) { + return ""; + } + String currDb = getCurrentDatabase(); + + if (currDb == null) { + return ""; + } + + return " (" + currDb + ")"; } String getPromptForBeeline() { @@ -2158,4 +2161,15 @@ public boolean isBeeLine() { public void setBeeLine(boolean isBeeLine) { this.isBeeLine = isBeeLine; } + + public String getCurrentDatabase() { + if (currentDatabase == null) { + currentDatabase = DEFAULT_DATABASE_NAME; + } + return currentDatabase; + } + + public void setCurrentDatabase(String currentDatabase) { + this.currentDatabase = currentDatabase; + } } diff --git a/beeline/src/java/org/apache/hive/beeline/ClientCommandHookFactory.java b/beeline/src/java/org/apache/hive/beeline/ClientCommandHookFactory.java new file mode 100644 index 0000000..73c88dd --- /dev/null +++ b/beeline/src/java/org/apache/hive/beeline/ClientCommandHookFactory.java @@ -0,0 +1,85 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hive.beeline; + +/** + * We need to update some client side information after executing some Hive Commands + */ +public class ClientCommandHookFactory { + private static ClientCommandHookFactory instance = new ClientCommandHookFactory(); + + private ClientCommandHookFactory() { + } + + public static ClientCommandHookFactory get() { + return instance; + } + + public class SetCommandHook extends ClientHook { + + public SetCommandHook(String sql) { + super(sql); + } + + @Override + public void postHook(BeeLine beeLine) { + if (!beeLine.isBeeLine()) { + beeLine.getOpts().setHiveConf(beeLine.getCommands().getHiveConf(false)); + } + } + } + + public class UseCommandHook extends ClientHook { + + public UseCommandHook(String sql) { + super(sql); + } + + @Override + public void postHook(BeeLine beeLine) { + if (!beeLine.isBeeLine()) { + // Handler multi-line sql + String line = sql.replaceAll("\\s+", " "); + String strs[] = line.split(" "); + String dbName; + if (strs == null || strs.length != 2) { + // unable to parse the use command + dbName = ""; + } else { + dbName = line.split(" ")[1]; + } + beeLine.setCurrentDatabase(dbName); + } + } + } + + public ClientHook getHook(String cmdLine) { + if (cmdLine.toLowerCase().startsWith("set")) { + // Only set A = B command needs updating the configuration stored in client side. + if (cmdLine.contains("=")) { + return new SetCommandHook(cmdLine); + } else { + return null; + } + } else if (cmdLine.toLowerCase().startsWith("use")) { + return new UseCommandHook(cmdLine); + } else { + return null; + } + } +} diff --git a/beeline/src/java/org/apache/hive/beeline/ClientHook.java b/beeline/src/java/org/apache/hive/beeline/ClientHook.java new file mode 100644 index 0000000..87c249a --- /dev/null +++ b/beeline/src/java/org/apache/hive/beeline/ClientHook.java @@ -0,0 +1,31 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hive.beeline; + +/** + * This is the client's hook + */ +public abstract class ClientHook { + protected String sql; + + public ClientHook(String sql) { + this.sql = sql; + } + + abstract void postHook(BeeLine beeLine); +} diff --git a/beeline/src/java/org/apache/hive/beeline/Commands.java b/beeline/src/java/org/apache/hive/beeline/Commands.java index 01349e2..868d6a7 100644 --- a/beeline/src/java/org/apache/hive/beeline/Commands.java +++ b/beeline/src/java/org/apache/hive/beeline/Commands.java @@ -55,6 +55,7 @@ import java.util.Map; import java.util.Properties; import java.util.Set; +import java.util.TreeMap; import java.util.TreeSet; import org.apache.hadoop.hive.common.cli.ShellCmdExecutor; @@ -824,7 +825,7 @@ private boolean isSourceCMD(String cmd) { if (cmd == null || cmd.isEmpty()) return false; String[] tokens = tokenizeCmd(cmd); - return tokens[0].equalsIgnoreCase("!source"); + return tokens[0].equalsIgnoreCase("source"); } private boolean sourceFile(String cmd) { @@ -881,10 +882,7 @@ private boolean sourceFileInternal(File sourceFile) throws IOException { private String cliToBeelineCmd(String cmd) { if (cmd == null) return null; - String[] tokens = tokenizeCmd(cmd); - if (tokens[0].equalsIgnoreCase("source")) { - return BeeLine.COMMAND_PREFIX + cmd; - } else if (cmd.startsWith("!")) { + if (cmd.startsWith("!")) { String shell_cmd = cmd.substring(1); return "!sh " + shell_cmd; } else { // local mode @@ -921,6 +919,15 @@ private boolean executeInternal(String sql, boolean call) { return true; } + if (!(beeLine.assertConnection())) { + return false; + } + + ClientHook hook = null; + if (!beeLine.isBeeLine()) { + hook = ClientCommandHookFactory.get().getHook(sql); + } + try { Statement stmnt = null; boolean hasResults; @@ -988,6 +995,9 @@ private boolean executeInternal(String sql, boolean call) { return beeLine.error(e); } beeLine.showWarnings(); + if (hook != null) { + hook.postHook(beeLine); + } return true; } @@ -1070,6 +1080,8 @@ public boolean call(String line) { return execute(line, true, false); } + public static final String COMMAND_PREFIX = "!"; + private boolean execute(String line, boolean call, boolean entireLineAsCommand) { if (line == null || line.length() == 0) { return false; // ??? @@ -1088,10 +1100,6 @@ private boolean execute(String line, boolean call, boolean entireLineAsCommand) beeLine.handleException(e); } - if (!(beeLine.assertConnection())) { - return false; - } - line = line.trim(); List cmdList = new ArrayList(); if (entireLineAsCommand) { @@ -1120,6 +1128,13 @@ private boolean execute(String line, boolean call, boolean entireLineAsCommand) } } + if (sql.startsWith(COMMAND_PREFIX)) { + if (!beeLine.execCommandWithPrefix(sql)) { + return false; + } else { + continue; + } + } // is source CMD if (isSourceCMD(sql)) { sourceFile(sql); diff --git a/beeline/src/test/org/apache/hive/beeline/TestClientCommandHookFactory.java b/beeline/src/test/org/apache/hive/beeline/TestClientCommandHookFactory.java new file mode 100644 index 0000000..c86de0a --- /dev/null +++ b/beeline/src/test/org/apache/hive/beeline/TestClientCommandHookFactory.java @@ -0,0 +1,32 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hive.beeline; + +import junit.framework.Assert; +import org.junit.Test; + +public class TestClientCommandHookFactory { + @Test + public void testGetHook() { + Assert.assertNull(ClientCommandHookFactory.get().getHook("set a;")); + Assert.assertTrue(ClientCommandHookFactory.get() + .getHook("set a=b;") instanceof ClientCommandHookFactory.SetCommandHook); + Assert.assertTrue(ClientCommandHookFactory.get() + .getHook("USE a.b") instanceof ClientCommandHookFactory.UseCommandHook); + } +} diff --git a/beeline/src/test/org/apache/hive/beeline/cli/TestHiveCli.java b/beeline/src/test/org/apache/hive/beeline/cli/TestHiveCli.java index 7f6ab13..88857b0 100644 --- a/beeline/src/test/org/apache/hive/beeline/cli/TestHiveCli.java +++ b/beeline/src/test/org/apache/hive/beeline/cli/TestHiveCli.java @@ -74,92 +74,116 @@ private void executeCMD(String[] args, String input, int retCode) { } } - private void verifyCMD(String CMD, String keywords, OutputStream os, String[] options, - int retCode) { + /** + * This method is used for verifying CMD to see whether the output contains the keywords provided. + * + * @param CMD + * @param keywords + * @param os + * @param options + * @param retCode + * @param contains + */ + public void verifyCMD(String CMD, String keywords, OutputStream os, String[] options, int retCode, + boolean contains) { executeCMD(options, CMD, retCode); String output = os.toString(); LOG.debug(output); - Assert.assertTrue( - "The expected keyword \"" + keywords + "\" doesn't occur in the output: " + output, - output.contains(keywords)); + if (contains) { + Assert.assertTrue( + "The expected keyword \"" + keywords + "\" doesn't occur in the output: " + output, + output.contains(keywords)); + } else { + Assert.assertFalse( + "The expected keyword \"" + keywords + "\" are't occur in the output: " + output, + output.contains(keywords)); + } } @Test public void testInValidCmd() { - verifyCMD("!lss\n", "Unknown command: lss", errS, null, ERRNO_OK); + verifyCMD("!lss\n", "Failed to execute lss", errS, null, ERRNO_OK, true); + } + + @Test + public void testCmd() { + verifyCMD("show tables;!ls;show tables;\n", "src", os, null, ERRNO_OK, true); } @Test public void testSetPromptValue() { - verifyCMD("set hive.cli.prompt=MYCLI;SHOW\nTABLES;", "MYCLI> ", os, null, ERRNO_OK); + verifyCMD("set hive.cli.prompt=MYCLI;SHOW\nTABLES;", "MYCLI> ", os, null, + ERRNO_OK, true); } @Test public void testSetHeaderValue() { verifyCMD( "create database if not exists test;\ncreate table if not exists test.testTbl(a string, b string);\nset hive.cli.print.header=true;\n select * from test.testTbl;\n", - "testtbl.a testtbl.b", os, null, ERRNO_OK); + "testtbl.a testtbl.b", os, null, ERRNO_OK, true); } @Test public void testHelp() { - verifyCMD(null, "usage: hive", os, new String[] { "-H" }, ERRNO_ARGS); + verifyCMD(null, "usage: hive", os, new String[] { "-H" }, ERRNO_ARGS, true); } @Test public void testInvalidDatabaseOptions() { - verifyCMD("\nshow tables;\nquit;\n", "Database does not exist: invalidDB", errS, - new String[] { "--database", "invalidDB" }, ERRNO_OK); + verifyCMD("\nshow tables;\nquit;\n", "Database does not exist: invalidDB", + errS, new String[] { "--database", "invalidDB" }, ERRNO_OK, true); } @Test public void testDatabaseOptions() { - verifyCMD("\nshow tables;\nquit;", "testtbl", os, new String[] { "--database", "test" }, - ERRNO_OK); + verifyCMD("\nshow tables;\nquit;", "testtbl", os, + new String[] { "--database", "test" }, ERRNO_OK, true); } @Test public void testSourceCmd() { File f = generateTmpFile(SOURCE_CONTEXT); - verifyCMD("source " + f.getPath() + ";" + "desc testSrcTbl;\nquit;\n", "sc1", os, - new String[] { "--database", "test" }, ERRNO_OK); + verifyCMD("source " + f.getPath() + ";" + "desc testSrcTbl;\nquit;\n", + "sc1", os, new String[] { "--database", "test" }, ERRNO_OK, true); f.delete(); } @Test public void testSourceCmd2() { File f = generateTmpFile(SOURCE_CONTEXT3); - verifyCMD("source " + f.getPath() + ";" + "desc testSrcTbl3;\nquit;\n", "sc3", os, - new String[] { "--database", "test" }, ERRNO_OK); + verifyCMD("source " + f.getPath() + ";" + "desc testSrcTbl3;\nquit;\n", + "sc3", os, new String[] { "--database", "test" }, ERRNO_OK, true); f.delete(); } @Test public void testSqlFromCmd() { - verifyCMD(null, "", os, new String[] { "-e", "show databases;" }, ERRNO_OK); + verifyCMD(null, "", os, new String[] { "-e", "show databases;" }, ERRNO_OK, true); } @Test public void testSqlFromCmdWithDBName() { - verifyCMD(null, "testtbl", os, new String[] { "-e", "show tables;", "--database", "test" }, - ERRNO_OK); + verifyCMD(null, "testtbl", os, + new String[] { "-e", "show tables;", "--database", "test" }, ERRNO_OK, true); } @Test public void testInvalidOptions() { - verifyCMD(null, "The '-e' and '-f' options cannot be specified simultaneously", errS, - new String[] { "-e", "show tables;", "-f", "path/to/file" }, ERRNO_ARGS); + verifyCMD(null, + "The '-e' and '-f' options cannot be specified simultaneously", errS, + new String[] { "-e", "show tables;", "-f", "path/to/file" }, ERRNO_ARGS, true); } @Test public void testInvalidOptions2() { - verifyCMD(null, "Unrecognized option: -k", errS, new String[] { "-k" }, ERRNO_ARGS); + verifyCMD(null, "Unrecognized option: -k", errS, new String[] { "-k" }, + ERRNO_ARGS, true); } @Test public void testVariables() { - verifyCMD("set system:xxx=5;\nset system:yyy=${system:xxx};\nset system:yyy;", "", os, null, - ERRNO_OK); + verifyCMD( + "set system:xxx=5;\nset system:yyy=${system:xxx};\nset system:yyy;", "", os, null, ERRNO_OK, true); } @Test @@ -167,14 +191,47 @@ public void testVariablesForSource() { File f = generateTmpFile(SOURCE_CONTEXT2); verifyCMD( "set hiveconf:zzz=" + f.getAbsolutePath() + ";\nsource ${hiveconf:zzz};\ndesc testSrcTbl2;", - "sc2", os, new String[] { "--database", "test" }, ERRNO_OK); + "sc2", os, new String[] { "--database", "test" }, ERRNO_OK, true); f.delete(); } @Test public void testErrOutput() { verifyCMD("show tables;set system:xxx=5;set system:yyy=${system:xxx};\nlss;", - "cannot recognize input near 'lss' '' ''", errS, null, ERRNO_OK); + "cannot recognize input near 'lss' '' ''", errS, null, ERRNO_OK, true); + } + + @Test + public void testUseCurrentDB1() { + verifyCMD( + "create database if not exists testDB; set hive.cli.print.current.db=true;use testDB;\n" + + "use default;drop if exists testDB;", "hive (testDB)>", os, null, ERRNO_OK, true); + } + + @Test + public void testUseCurrentDB2() { + verifyCMD( + "create database if not exists testDB; set hive.cli.print.current.db=true;use\ntestDB;\nuse default;drop if exists testDB;", + "hive (testDB)>", os, null, ERRNO_OK, true); + } + + @Test + public void testUseCurrentDB3() { + verifyCMD( + "create database if not exists testDB; set hive.cli.print.current.db=true;use testDB;\n" + + "use default;drop if exists testDB;", "hive (testDB)>", os, null, ERRNO_OK, true); + } + + @Test + public void testUseInvalidDB() { + verifyCMD("set hive.cli.print.current.db=true;use invalidDB;", + "hive (invalidDB)>", os, null, ERRNO_OK, false); + } + + @Test + public void testNoErrorDB() { + verifyCMD(null, "Error: Method not supported (state=,code=0)", errS, new String[] { "-e", "show tables;" }, + ERRNO_OK, false); } private void redirectOutputStream() {