diff --git a/beeline/src/java/org/apache/hive/beeline/BeeLine.java b/beeline/src/java/org/apache/hive/beeline/BeeLine.java index ecd60f6..47d565f 100644 --- a/beeline/src/java/org/apache/hive/beeline/BeeLine.java +++ b/beeline/src/java/org/apache/hive/beeline/BeeLine.java @@ -84,6 +84,7 @@ import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.GnuParser; +import org.apache.commons.cli.Option; import org.apache.commons.cli.OptionBuilder; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; @@ -317,7 +318,7 @@ // -p options.addOption(OptionBuilder - .hasArg() + .hasOptionalArg() .withArgName("password") .withDescription("the password to connect as") .create('p')); @@ -632,23 +633,45 @@ ColorBuffer getColorBuffer(String msg) { public class BeelineParser extends GnuParser { + private boolean isPasswordOptionSet = false; @Override - protected void processOption(final String arg, final ListIterator iter) throws ParseException { - if ((arg.startsWith("--")) && !(arg.equals(HIVE_VAR_PREFIX) || (arg.equals(HIVE_CONF_PREFIX)) - || (arg.equals("--help") || (arg.equals(PROP_FILE_PREFIX))))) { - String stripped = arg.substring(2, arg.length()); - String[] parts = split(stripped, "="); - debug(loc("setting-prop", Arrays.asList(parts))); - if (parts.length >= 2) { - getOpts().set(parts[0], parts[1], true); - } else { - getOpts().set(parts[0], "true", true); - } + protected void processOption(String arg, final ListIterator iter) throws ParseException { + if (isBeeLineOpt(arg)) { + processBeeLineOpt(arg); } else { + //-p with the next argument being for BeeLineOpts + if (arg.equals("-p")) { + isPasswordOptionSet = true; + if(iter.hasNext()) { + String next = (String) iter.next(); + if(isBeeLineOpt(next)) { + processBeeLineOpt(next); + return; + } else { + iter.previous(); + } + } + } super.processOption(arg, iter); } } + + private void processBeeLineOpt(final String arg) { + String stripped = arg.substring(2, arg.length()); + String[] parts = split(stripped, "="); + debug(loc("setting-prop", Arrays.asList(parts))); + if (parts.length >= 2) { + getOpts().set(parts[0], parts[1], true); + } else { + getOpts().set(parts[0], "true", true); + } + } + + private boolean isBeeLineOpt(String arg) { + return arg.startsWith("--") && !(arg.equals(HIVE_VAR_PREFIX) || (arg.equals(HIVE_CONF_PREFIX)) + || (arg.equals("--help") || (arg.equals(PROP_FILE_PREFIX)))); + } } int initArgsFromCliVars(String[] args) { @@ -727,7 +750,7 @@ int initArgs(String[] args) { return -1; } - String driver = null, user = null, pass = null, url = null; + String driver = null, user = null, pass = "", url = null; String auth = null; @@ -754,7 +777,9 @@ int initArgs(String[] args) { if (cl.hasOption("w")) { pass = obtainPasswordFromFile(cl.getOptionValue("w")); } else { - pass = cl.getOptionValue("p"); + if(beelineParser.isPasswordOptionSet) { + pass = cl.getOptionValue("p"); + } } url = cl.getOptionValue("u"); if ((url == null) && cl.hasOption("reconnect")){ @@ -839,8 +864,8 @@ private String constructCmd(String url, String user, String pass, String driver, + (user == null || user.length() == 0 ? "''" : user) + " "; if (stripPasswd) { com += PASSWD_MASK + " "; - } else { - com += (pass == null || pass.length() == 0 ? "''" : pass) + " "; + } else if(pass != null) { + com += (pass.length() == 0 ? "''" : pass) + " "; } com += (driver == null ? "" : driver); return com; @@ -881,6 +906,8 @@ public int begin(String[] args, InputStream inputStream) throws IOException { } try { + ConsoleReader reader = getConsoleReader(inputStream); + setConsoleReader(reader); if (isBeeLine) { int code = initArgs(args); if (code != 0) { @@ -905,7 +932,6 @@ public int begin(String[] args, InputStream inputStream) throws IOException { } catch (Exception e) { // ignore } - ConsoleReader reader = getConsoleReader(inputStream); return execute(reader, false); } finally { close(); diff --git a/itests/hive-unit/src/test/java/org/apache/hive/beeline/TestBeelinePasswordOption.java b/itests/hive-unit/src/test/java/org/apache/hive/beeline/TestBeelinePasswordOption.java new file mode 100644 index 0000000..8e87f7d --- /dev/null +++ b/itests/hive-unit/src/test/java/org/apache/hive/beeline/TestBeelinePasswordOption.java @@ -0,0 +1,306 @@ +package org.apache.hive.beeline; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.PrintStream; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import org.apache.hadoop.hive.conf.HiveConf; +import org.apache.hive.jdbc.miniHS2.MiniHS2; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +public class TestBeelinePasswordOption { + private static final String tableName = "TestBeelineTable1"; + private static final String tableComment = "Test table comment"; + private static MiniHS2 miniHS2; + + /** + * Start up a local Hive Server 2 for these tests + */ + @BeforeClass + public static void preTests() throws Exception { + HiveConf hiveConf = new HiveConf(); + // Set to non-zk lock manager to prevent HS2 from trying to connect + hiveConf.setVar(HiveConf.ConfVars.HIVE_LOCK_MANAGER, + "org.apache.hadoop.hive.ql.lockmgr.EmbeddedLockManager"); + hiveConf.setBoolVar(HiveConf.ConfVars.HIVEOPTIMIZEMETADATAQUERIES, false); + miniHS2 = new MiniHS2(hiveConf); + miniHS2.start(new HashMap()); + createTable(); + } + + /** + * Test if beeline prompts for a password when optional password option is at the beginning of + * arguments + */ + @Test + public void testPromptPasswordOptionStart() throws Throwable { + List argList = new ArrayList<>(); + argList.add("-p"); + argList.addAll(getBaseArgs(miniHS2.getBaseJdbcURL())); + argList.add("-p"); + argList.add("-n"); + argList.add("hive"); + connectBeelineWithUserPrompt(argList, "hivepassword"); + } + + /** + * Test if beeline prompts for a password when optional password option is at the end of arguments + */ + @Test + public void testPromptPasswordOptionLast() throws Exception { + List argList = getBaseArgs(miniHS2.getBaseJdbcURL()); + argList.add("-n"); + argList.add("hive"); + argList.add("-p"); + connectBeelineWithUserPrompt(argList, "hivepassword"); + } + + /** + * Test if beeline prompts for a password when optional password option is at the middle of + * arguments + */ + @Test + public void testPromptPasswordOptionMiddle() throws Exception { + List argList = getBaseArgs(miniHS2.getBaseJdbcURL()); + argList.add("-p"); + argList.add("-n"); + argList.add("hive"); + connectBeelineWithUserPrompt(argList, "hivepassword"); + } + + /** + * Test if beeline prompts for a password when optional password option is used in conjunction + * with additional commandLine options after -p + */ + @Test + public void testPromptPasswordOptionWithOtherOptions() throws Exception { + List argList = getBaseArgs(miniHS2.getBaseJdbcURL()); + argList.add("-p"); + argList.add("-n"); + argList.add("hive"); + argList.add("-e"); + argList.add("show tables;"); + String output = connectBeelineWithUserPrompt(argList, "hivepassword"); + Assert.assertTrue("Table name " + tableName + " not found in the output", + output.contains(tableName.toLowerCase())); + } + + /** + * Test if beeline prompts for a password when optional password option is used in conjunction + * with additional BeeLineOpts options after -p + */ + @Test + public void testPromptPasswordOptionWithBeelineOpts() throws Exception { + List argList = getBaseArgs(miniHS2.getBaseJdbcURL()); + argList.add("-n"); + argList.add("hive"); + argList.add("-p"); + argList.add("--force=true"); + argList.add("-e"); + argList.add("show tables;"); + String output = connectBeelineWithUserPrompt(argList, "hivepassword"); + Assert.assertTrue("Table name " + tableName + " not found in the output", + output.contains(tableName.toLowerCase())); + } + + /** + * Test if beeline prompts for a password when optional password option is used in conjunction + * with additional BeeLineOpts options after -p. Also, verifies the beelineOpt value is set as + * expected + */ + @Test + public void testPromptPasswordVerifyBeelineOpts() throws Exception { + List argList = getBaseArgs(miniHS2.getBaseJdbcURL()); + argList.add("-n"); + argList.add("hive"); + argList.add("-p"); + argList.add("--maxColumnWidth=57"); + argList.add("-e"); + argList.add("show tables;"); + String output = connectWithPromptAndVerify(argList, "hivepassword", true, 57, null, null); + Assert.assertTrue("Table name " + tableName + " not found in the output", + output.contains(tableName.toLowerCase())); + } + + /** + * Tests if beeline prompts for a password and also confirms that --hiveconf + * argument works when given immediated after -p with no password + * @throws Exception + */ + @Test + public void testPromptPasswordWithHiveConf() throws Exception { + List argList = getBaseArgs(miniHS2.getBaseJdbcURL()); + argList.add("-n"); + argList.add("hive"); + argList.add("-p"); + argList.add("--hiveconf"); + argList.add("hive.cli.print.header=true"); + argList.add("-e"); + argList.add("show tables;"); + String output = connectWithPromptAndVerify(argList, "hivepassword", false, null, + "hive.cli.print.header", "true"); + Assert.assertTrue("Table name " + tableName + " not found in the output", + output.contains(tableName.toLowerCase())); + } + + /** + * Tests if beeline doesn't prompt for a password and connects with no password option provided + */ + @Test + public void testNoPasswordPrompt() throws Exception { + List argList = getBaseArgs(miniHS2.getBaseJdbcURL()); + argList.add("-n"); + argList.add("hive"); + argList.add("--force=true"); + argList.add("-e"); + argList.add("show tables;"); + String output = connectBeelineWithUserPrompt(argList); + Assert.assertTrue("Table name " + tableName + " not found in the output", + output.contains(tableName.toLowerCase())); + } + + /** + * Tests if beeline doesn't prompt for a password and connects with no password/username option + * provided + */ + @Test + public void testNoPasswordPrompt2() throws Exception { + List argList = getBaseArgs(miniHS2.getBaseJdbcURL()); + argList.add("--force=true"); + argList.add("-e"); + argList.add("show tables;"); + String output = connectBeelineWithUserPrompt(argList); + Assert.assertTrue("Table name " + tableName + " not found in the output", + output.contains(tableName.toLowerCase())); + } + + /** + * Tests if Beeline prompts for password when -p is the last argument and argList has CommandLine + * options as well as BeelineOpts + */ + @Test + public void testPromptPassOptionLastWithBeelineOpts() throws Exception { + List argList = getBaseArgs(miniHS2.getBaseJdbcURL()); + argList.add("-n"); + argList.add("hive"); + argList.add("--force=true"); + argList.add("-e"); + argList.add("show tables;"); + argList.add("-p"); + String output = connectBeelineWithUserPrompt(argList, "hivepassword"); + Assert.assertTrue("Table name " + tableName + " not found in the output", + output.contains(tableName.toLowerCase())); + } + + /** + * Connects to miniHS2 using beeline with the given string value for the prompt if the prompt is + * null, uses beeline with null inputstream in which this method expects that the argList is + * sufficient to make a successful Beeline connection with no prompt required from user + * + * @param argList - arguments list for the beeline + * @param prompt - String value to be given to beeline prompt during connection + * @param beelineOptName - Name of BeelineOpt to be verified + * @param beelineOptValue - Expected value of value of BeeLineOpt + * @param hiveConfKey - hive conf variable name to verify + * @param expectedHiveConfValue - Expected value of hive conf variable + * @return output of beeline from outputstream + * @throws Exception + */ + private String connectWithPromptAndVerify(List argList, String prompt, + boolean testMaxColumnWidthOption, Integer expectedMaxColumnWidth, String hiveConfKey, + String expectedHiveConfValue) throws Exception { + BeeLine beeLine = null; + try { + beeLine = new BeeLine(); + ByteArrayOutputStream os = new ByteArrayOutputStream(); + PrintStream beelineOutputStream = new PrintStream(os); + beeLine.setOutputStream(beelineOutputStream); + beeLine.setErrorStream(beelineOutputStream); + String[] args = argList.toArray(new String[argList.size()]); + InputStream inputStream = null; + if (prompt != null) { + inputStream = new ByteArrayInputStream(prompt.getBytes()); + } + Assert.assertTrue(beeLine.begin(args, inputStream) == 0); + if (testMaxColumnWidthOption) { + int maxColumnWidth = beeLine.getOpts().getMaxColumnWidth(); + Assert.assertTrue( + "Expected max columnWidth to be " + expectedMaxColumnWidth + " found " + maxColumnWidth, + maxColumnWidth == expectedMaxColumnWidth); + } + if (hiveConfKey != null) { + String hiveConfValue = beeLine.getOpts().getHiveConfVariables().get(hiveConfKey); + Assert.assertTrue( + "Expected " + expectedHiveConfValue + " got " + hiveConfValue + " for " + hiveConfKey, + expectedHiveConfValue.equalsIgnoreCase(hiveConfValue)); + } + String output = os.toString("UTF-8"); + System.out.println(output); + return output; + } finally { + if (beeLine != null) { + beeLine.close(); + } + } + } + + private String connectBeelineWithUserPrompt(List argList) throws Exception { + return connectBeelineWithUserPrompt(argList, null); + } + + private String connectBeelineWithUserPrompt(List argList, String prompt) + throws Exception { + return connectWithPromptAndVerify(argList, prompt, false, null, null, null); + } + + /** + * Create table for use by tests + * + * @throws ClassNotFoundException + * @throws SQLException + */ + private static void createTable() throws ClassNotFoundException, SQLException { + Class.forName(BeeLine.BEELINE_DEFAULT_JDBC_DRIVER); + Connection con = DriverManager.getConnection(miniHS2.getBaseJdbcURL(), "", ""); + + assertNotNull("Connection is null", con); + assertFalse("Connection should not be closed", con.isClosed()); + Statement stmt = con.createStatement(); + assertNotNull("Statement is null", stmt); + + stmt.execute("set hive.support.concurrency = false"); + try { + stmt.execute("drop table if exists " + tableName); + } catch (Exception ex) { + ex.printStackTrace(); + fail("Unable to create setup table " + tableName + ex.toString()); + } + // create table + stmt.execute("create table " + tableName + + " (under_col int comment 'the under column', value string) comment '" + tableComment + + "'"); + } + + private List getBaseArgs(String jdbcUrl) { + List argList = new ArrayList(8); + argList.add("-d"); + argList.add(BeeLine.BEELINE_DEFAULT_JDBC_DRIVER); + argList.add("-u"); + argList.add(jdbcUrl); + return argList; + } +}