/** * 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. */ /* * This source file is based on code taken from SQLLine 1.0.2 * See SQLLine notice in LICENSE */ package org.apache.hive.beeline; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.Closeable; import java.io.EOFException; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintStream; import java.io.SequenceInputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.net.JarURLConnection; import java.net.URL; import java.net.URLConnection; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.Driver; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.SQLWarning; import java.sql.Statement; import java.text.ChoiceFormat; import java.text.MessageFormat; import java.util.Arrays; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.Properties; import java.util.ResourceBundle; import java.util.Set; import java.util.SortedSet; import java.util.StringTokenizer; import java.util.TreeMap; import java.util.TreeSet; import java.util.jar.Attributes; import java.util.jar.Manifest; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import jline.console.completer.Completer; import jline.console.completer.StringsCompleter; import jline.console.completer.FileNameCompleter; import jline.console.ConsoleReader; import jline.console.history.FileHistory; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.GnuParser; import org.apache.commons.cli.OptionBuilder; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.apache.hadoop.fs.FSDataInputStream; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.hive.conf.HiveConf; import org.apache.hadoop.io.IOUtils; import org.apache.hive.beeline.cli.CliOptionsProcessor; import org.apache.hive.common.util.ShutdownHookManager; import org.apache.hive.beeline.hs2connection.BeelineHS2ConnectionFileParseException; import org.apache.hive.beeline.hs2connection.HS2ConnectionFileUtils; import org.apache.hive.beeline.hs2connection.UserHS2ConnectionFileParser; import org.apache.hive.beeline.hs2connection.HS2ConnectionFileParser; import org.apache.hive.beeline.hs2connection.HiveSiteHS2ConnectionFileParser; import org.apache.thrift.transport.TTransportException; import com.google.common.annotations.VisibleForTesting; import org.apache.hive.jdbc.Utils; import org.apache.hive.jdbc.Utils.JdbcConnectionParams; /** * A console SQL shell with command completion. * <p> * TODO: * <ul> * <li>User-friendly connection prompts</li> * <li>Page results</li> * <li>Handle binary data (blob fields)</li> * <li>Implement command aliases</li> * <li>Stored procedure execution</li> * <li>Binding parameters to prepared statements</li> * <li>Scripting language</li> * <li>XA transactions</li> * </ul> * */ @SuppressWarnings("static-access") public class BeeLine implements Closeable { private static final ResourceBundle resourceBundle = ResourceBundle.getBundle(BeeLine.class.getSimpleName()); private final BeeLineSignalHandler signalHandler; private final Runnable shutdownHook; private static final String separator = System.getProperty("line.separator"); private boolean exit = false; private final DatabaseConnections connections = new DatabaseConnections(); public static final String COMMAND_PREFIX = "!"; private Collection<Driver> drivers = null; private final BeeLineOpts opts = new BeeLineOpts(this, System.getProperties()); private String lastProgress = null; private final Map<SQLWarning, Date> seenWarnings = new HashMap<SQLWarning, Date>(); private final Commands commands = new Commands(this); private OutputFile scriptOutputFile = null; private OutputFile recordOutputFile = null; private PrintStream outputStream = new PrintStream(System.out, true); private PrintStream errorStream = new PrintStream(System.err, true); private InputStream inputStream = System.in; private ConsoleReader consoleReader; private List<String> batch = null; private final Reflector reflector = new Reflector(this); private String dbName = null; private String currentDatabase = null; private FileHistory history; // Indicates if this instance of beeline is running in compatibility mode, or beeline mode private boolean isBeeLine = true; // Indicates that we are in test mode. // Print only the errors, the operation log and the query results. private boolean isTestMode = false; private static final Options options = new Options(); public static final String BEELINE_DEFAULT_JDBC_DRIVER = "org.apache.hive.jdbc.HiveDriver"; public static final String DEFAULT_DATABASE_NAME = "default"; private static final String SCRIPT_OUTPUT_PREFIX = ">>>"; private static final int SCRIPT_OUTPUT_PAD_SIZE = 5; private static final int ERRNO_OK = 0; private static final int ERRNO_ARGS = 1; private static final int ERRNO_OTHER = 2; private static final String HIVE_VAR_PREFIX = "--hivevar"; private static final String HIVE_CONF_PREFIX = "--hiveconf"; private static final String PROP_FILE_PREFIX = "--property-file"; static final String PASSWD_MASK = "[passwd stripped]"; private final Map<Object, Object> formats = map(new Object[] { "vertical", new VerticalOutputFormat(this), "table", new TableOutputFormat(this), "csv2", new SeparatedValuesOutputFormat(this, ','), "tsv2", new SeparatedValuesOutputFormat(this, '\t'), "dsv", new SeparatedValuesOutputFormat(this, BeeLineOpts.DEFAULT_DELIMITER_FOR_DSV), "csv", new DeprecatedSeparatedValuesOutputFormat(this, ','), "tsv", new DeprecatedSeparatedValuesOutputFormat(this, '\t'), "xmlattr", new XMLAttributeOutputFormat(this), "xmlelements", new XMLElementOutputFormat(this), }); private List<String> supportedLocalDriver = new ArrayList<String>(Arrays.asList("com.mysql.jdbc.Driver", "org.postgresql.Driver")); final CommandHandler[] commandHandlers = new CommandHandler[] { new ReflectiveCommandHandler(this, new String[] {"quit", "done", "exit"}, null), new ReflectiveCommandHandler(this, new String[] {"connect", "open"}, new Completer[] {new StringsCompleter(getConnectionURLExamples())}), new ReflectiveCommandHandler(this, new String[] {"describe"}, new Completer[] {new TableNameCompletor(this)}), new ReflectiveCommandHandler(this, new String[] {"indexes"}, new Completer[] {new TableNameCompletor(this)}), new ReflectiveCommandHandler(this, new String[] {"primarykeys"}, new Completer[] {new TableNameCompletor(this)}), new ReflectiveCommandHandler(this, new String[] {"exportedkeys"}, new Completer[] {new TableNameCompletor(this)}), new ReflectiveCommandHandler(this, new String[] {"manual"}, null), new ReflectiveCommandHandler(this, new String[] {"importedkeys"}, new Completer[] {new TableNameCompletor(this)}), new ReflectiveCommandHandler(this, new String[] {"procedures"}, null), new ReflectiveCommandHandler(this, new String[] {"tables"}, null), new ReflectiveCommandHandler(this, new String[] {"typeinfo"}, null), new ReflectiveCommandHandler(this, new String[] {"columns"}, new Completer[] {new TableNameCompletor(this)}), new ReflectiveCommandHandler(this, new String[] {"reconnect"}, null), new ReflectiveCommandHandler(this, new String[] {"dropall"}, new Completer[] {new TableNameCompletor(this)}), new ReflectiveCommandHandler(this, new String[] {"history"}, null), new ReflectiveCommandHandler(this, new String[] {"metadata"}, new Completer[] { new StringsCompleter(getMetadataMethodNames())}), new ReflectiveCommandHandler(this, new String[] {"nativesql"}, null), new ReflectiveCommandHandler(this, new String[] {"dbinfo"}, null), new ReflectiveCommandHandler(this, new String[] {"rehash"}, null), new ReflectiveCommandHandler(this, new String[] {"verbose"}, null), new ReflectiveCommandHandler(this, new String[] {"run"}, new Completer[] {new FileNameCompleter()}), new ReflectiveCommandHandler(this, new String[] {"batch"}, null), new ReflectiveCommandHandler(this, new String[] {"list"}, null), new ReflectiveCommandHandler(this, new String[] {"all"}, null), new ReflectiveCommandHandler(this, new String[] {"go", "#"}, null), new ReflectiveCommandHandler(this, new String[] {"script"}, new Completer[] {new FileNameCompleter()}), new ReflectiveCommandHandler(this, new String[] {"record"}, new Completer[] {new FileNameCompleter()}), new ReflectiveCommandHandler(this, new String[] {"brief"}, null), new ReflectiveCommandHandler(this, new String[] {"close"}, null), new ReflectiveCommandHandler(this, new String[] {"closeall"}, null), new ReflectiveCommandHandler(this, new String[] {"isolation"}, new Completer[] {new StringsCompleter(getIsolationLevels())}), new ReflectiveCommandHandler(this, new String[] {"outputformat"}, new Completer[] {new StringsCompleter( formats.keySet().toArray(new String[0]))}), new ReflectiveCommandHandler(this, new String[] {"autocommit"}, null), new ReflectiveCommandHandler(this, new String[] {"commit"}, null), new ReflectiveCommandHandler(this, new String[] {"properties"}, new Completer[] {new FileNameCompleter()}), new ReflectiveCommandHandler(this, new String[] {"rollback"}, null), new ReflectiveCommandHandler(this, new String[] {"help", "?"}, null), new ReflectiveCommandHandler(this, new String[] {"set"}, getOpts().optionCompleters()), new ReflectiveCommandHandler(this, new String[] {"save"}, null), new ReflectiveCommandHandler(this, new String[] {"scan"}, null), new ReflectiveCommandHandler(this, new String[] {"sql"}, null), new ReflectiveCommandHandler(this, new String[] {"sh"}, null), new ReflectiveCommandHandler(this, new String[] {"call"}, null), new ReflectiveCommandHandler(this, new String[] {"nullemptystring"}, new Completer[] {new BooleanCompleter()}), new ReflectiveCommandHandler(this, new String[]{"addlocaldriverjar"}, null), new ReflectiveCommandHandler(this, new String[]{"addlocaldrivername"}, null), new ReflectiveCommandHandler(this, new String[]{"delimiter"}, null) }; private final Completer beeLineCommandCompleter = new BeeLineCommandCompleter(Arrays.asList(commandHandlers)); static final SortedSet<String> KNOWN_DRIVERS = new TreeSet<String>(Arrays.asList( new String[] { "org.apache.hive.jdbc.HiveDriver", "org.apache.hadoop.hive.jdbc.HiveDriver", })); static { try { Class.forName("jline.console.ConsoleReader"); } catch (Throwable t) { throw new ExceptionInInitializerError("jline-missing"); } } static { // -d <driver class> options.addOption(OptionBuilder .hasArg() .withArgName("driver class") .withDescription("the driver class to use") .create('d')); // -u <database url> options.addOption(OptionBuilder .hasArg() .withArgName("database url") .withDescription("the JDBC URL to connect to") .create('u')); // -r options.addOption(OptionBuilder .withLongOpt("reconnect") .withDescription("Reconnect to last saved connect url (in conjunction with !save)") .create('r')); // -n <username> options.addOption(OptionBuilder .hasArg() .withArgName("username") .withDescription("the username to connect as") .create('n')); // -p <password> options.addOption(OptionBuilder .hasArg() .withArgName("password") .withDescription("the password to connect as") .hasOptionalArg() .create('p')); // -w (or) --password-file <file> options.addOption(OptionBuilder .hasArg() .withArgName("password-file") .withDescription("the password file to read password from") .withLongOpt("password-file") .create('w')); // -a <authType> options.addOption(OptionBuilder .hasArg() .withArgName("authType") .withDescription("the authentication type") .create('a')); // -i <init file> options.addOption(OptionBuilder .hasArg() .withArgName("init") .withDescription("script file for initialization") .create('i')); // -e <query> options.addOption(OptionBuilder .hasArgs() .withArgName("query") .withDescription("query that should be executed") .create('e')); // -f <script file> options.addOption(OptionBuilder .hasArg() .withArgName("file") .withDescription("script file that should be executed") .create('f')); // -help options.addOption(OptionBuilder .withLongOpt("help") .withDescription("display this message") .create('h')); // Substitution option --hivevar options.addOption(OptionBuilder .withValueSeparator() .hasArgs(2) .withArgName("key=value") .withLongOpt("hivevar") .withDescription("hive variable name and value") .create()); //hive conf option --hiveconf options.addOption(OptionBuilder .withValueSeparator() .hasArgs(2) .withArgName("property=value") .withLongOpt("hiveconf") .withDescription("Use value for given property") .create()); // --property-file <file> options.addOption(OptionBuilder .hasArg() .withLongOpt("property-file") .withDescription("the file to read configuration properties from") .create()); } static Manifest getManifest() throws IOException { URL base = BeeLine.class.getResource("/META-INF/MANIFEST.MF"); URLConnection c = base.openConnection(); if (c instanceof JarURLConnection) { return ((JarURLConnection) c).getManifest(); } return null; } String getManifestAttribute(String name) { try { Manifest m = getManifest(); if (m == null) { return "??"; } Attributes attrs = m.getAttributes("beeline"); if (attrs == null) { return "???"; } String val = attrs.getValue(name); if (val == null || "".equals(val)) { return "????"; } return val; } catch (Exception e) { e.printStackTrace(errorStream); return "?????"; } } String getApplicationTitle() { Package pack = BeeLine.class.getPackage(); return loc("app-introduction", new Object[] { "Beeline", pack.getImplementationVersion() == null ? "???" : pack.getImplementationVersion(), "Apache Hive", // getManifestAttribute ("Specification-Title"), // getManifestAttribute ("Implementation-Version"), // getManifestAttribute ("Implementation-ReleaseDate"), // getManifestAttribute ("Implementation-Vendor"), // getManifestAttribute ("Implementation-License"), }); } String getApplicationContactInformation() { return getManifestAttribute("Implementation-Vendor"); } String loc(String res) { return loc(res, new Object[0]); } String loc(String res, int param) { try { return MessageFormat.format( new ChoiceFormat(resourceBundle.getString(res)).format(param), new Object[] {new Integer(param)}); } catch (Exception e) { return res + ": " + param; } } String loc(String res, Object param1) { return loc(res, new Object[] {param1}); } String loc(String res, Object param1, Object param2) { return loc(res, new Object[] {param1, param2}); } String loc(String res, Object[] params) { try { return MessageFormat.format(resourceBundle.getString(res), params); } catch (Exception e) { e.printStackTrace(getErrorStream()); try { return res + ": " + Arrays.asList(params); } catch (Exception e2) { return res; } } } protected String locElapsedTime(long milliseconds) { if (getOpts().getShowElapsedTime()) { return loc("time-ms", new Object[] {new Double(milliseconds / 1000d)}); } return ""; } /** * Starts the program. */ public static void main(String[] args) throws IOException { mainWithInputRedirection(args, null); } /** * Starts the program with redirected input. For redirected output, * setOutputStream() and setErrorStream can be used. * Exits with 0 on success, 1 on invalid arguments, and 2 on any other error * * @param args * same as main() * * @param inputStream * redirected input, or null to use standard input */ public static void mainWithInputRedirection(String[] args, InputStream inputStream) throws IOException { BeeLine beeLine = new BeeLine(); try { int status = beeLine.begin(args, inputStream); if (!Boolean.getBoolean(BeeLineOpts.PROPERTY_NAME_EXIT)) { System.exit(status); } } finally { beeLine.close(); } } public BeeLine() { this(true); } public BeeLine(boolean isBeeLine) { this.isBeeLine = isBeeLine; this.signalHandler = new SunSignalHandler(this); this.shutdownHook = new Runnable() { @Override public void run() { try { if (history != null) { history.setMaxSize(getOpts().getMaxHistoryRows()); history.flush(); } } catch (IOException e) { error(e); } finally { close(); } } }; } DatabaseConnection getDatabaseConnection() { return getDatabaseConnections().current(); } Connection getConnection() throws SQLException { if (getDatabaseConnections().current() == null || getDatabaseConnections().current().getConnection() == null) { throw new IllegalArgumentException(loc("no-current-connection")); } return getDatabaseConnections().current().getConnection(); } DatabaseMetaData getDatabaseMetaData() { if (getDatabaseConnections().current() == null) { throw new IllegalArgumentException(loc("no-current-connection")); } if (getDatabaseConnections().current().getDatabaseMetaData() == null) { throw new IllegalArgumentException(loc("no-current-connection")); } return getDatabaseConnections().current().getDatabaseMetaData(); } public String[] getIsolationLevels() { return new String[] { "TRANSACTION_NONE", "TRANSACTION_READ_COMMITTED", "TRANSACTION_READ_UNCOMMITTED", "TRANSACTION_REPEATABLE_READ", "TRANSACTION_SERIALIZABLE", }; } public String[] getMetadataMethodNames() { try { TreeSet<String> mnames = new TreeSet<String>(); Method[] m = DatabaseMetaData.class.getDeclaredMethods(); for (int i = 0; m != null && i < m.length; i++) { mnames.add(m[i].getName()); } return mnames.toArray(new String[0]); } catch (Throwable t) { return new String[0]; } } public String[] getConnectionURLExamples() { return new String[] { "jdbc:JSQLConnect://<hostname>/database=<database>", "jdbc:cloudscape:<database>;create=true", "jdbc:twtds:sqlserver://<hostname>/<database>", "jdbc:daffodilDB_embedded:<database>;create=true", "jdbc:datadirect:db2://<hostname>:50000;databaseName=<database>", "jdbc:inetdae:<hostname>:1433", "jdbc:datadirect:oracle://<hostname>:1521;SID=<database>;MaxPooledStatements=0", "jdbc:datadirect:sqlserver://<hostname>:1433;SelectMethod=cursor;DatabaseName=<database>", "jdbc:datadirect:sybase://<hostname>:5000", "jdbc:db2://<hostname>/<database>", "jdbc:hive2://<hostname>", "jdbc:hsqldb:<database>", "jdbc:idb:<database>.properties", "jdbc:informix-sqli://<hostname>:1526/<database>:INFORMIXSERVER=<database>", "jdbc:interbase://<hostname>//<database>.gdb", "jdbc:microsoft:sqlserver://<hostname>:1433;DatabaseName=<database>;SelectMethod=cursor", "jdbc:mysql://<hostname>/<database>?autoReconnect=true", "jdbc:oracle:thin:@<hostname>:1521:<database>", "jdbc:pointbase:<database>,database.home=<database>,create=true", "jdbc:postgresql://<hostname>:5432/<database>", "jdbc:postgresql:net//<hostname>/<database>", "jdbc:sybase:Tds:<hostname>:4100/<database>?ServiceName=<database>", "jdbc:weblogic:mssqlserver4:<database>@<hostname>:1433", "jdbc:odbc:<database>", "jdbc:sequelink://<hostname>:4003/[Oracle]", "jdbc:sequelink://<hostname>:4004/[Informix];Database=<database>", "jdbc:sequelink://<hostname>:4005/[Sybase];Database=<database>", "jdbc:sequelink://<hostname>:4006/[SQLServer];Database=<database>", "jdbc:sequelink://<hostname>:4011/[ODBC MS Access];Database=<database>", "jdbc:openlink://<hostname>/DSN=SQLServerDB/UID=sa/PWD=", "jdbc:solid://<hostname>:<port>/<UID>/<PWD>", "jdbc:dbaw://<hostname>:8889/<database>", }; } /** * Entry point to creating a {@link ColorBuffer} with color * enabled or disabled depending on the value of {@link BeeLineOpts#getColor}. */ ColorBuffer getColorBuffer() { return new ColorBuffer(getOpts().getColor()); } /** * Entry point to creating a {@link ColorBuffer} with color * enabled or disabled depending on the value of {@link BeeLineOpts#getColor}. */ ColorBuffer getColorBuffer(String msg) { return new ColorBuffer(msg, getOpts().getColor()); } public class BeelineParser extends GnuParser { private boolean isPasswordOptionSet = false; @Override 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 ("-p".equals(arg)) { 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("--") && !(HIVE_VAR_PREFIX.equals(arg) || (HIVE_CONF_PREFIX.equals(arg)) || "--help".equals(arg) || PROP_FILE_PREFIX.equals(arg)); } } int initArgsFromCliVars(String[] args) { List<String> commands = Collections.emptyList(); CliOptionsProcessor optionsProcessor = new CliOptionsProcessor(); if (!optionsProcessor.process(args)) { return 1; } CommandLine commandLine = optionsProcessor.getCommandLine(); Properties confProps = commandLine.getOptionProperties("hiveconf"); for (String propKey : confProps.stringPropertyNames()) { setHiveConfVar(propKey, confProps.getProperty(propKey)); } Properties hiveVars = commandLine.getOptionProperties("define"); for (String propKey : hiveVars.stringPropertyNames()) { getOpts().getHiveConfVariables().put(propKey, hiveVars.getProperty(propKey)); } Properties hiveVars2 = commandLine.getOptionProperties("hivevar"); for (String propKey : hiveVars2.stringPropertyNames()) { getOpts().getHiveConfVariables().put(propKey, hiveVars2.getProperty(propKey)); } getOpts().setScriptFile(commandLine.getOptionValue("f")); if (commandLine.getOptionValues("i") != null) { getOpts().setInitFiles(commandLine.getOptionValues("i")); } dbName = commandLine.getOptionValue("database"); getOpts().setVerbose(Boolean.parseBoolean(commandLine.getOptionValue("verbose"))); getOpts().setSilent(Boolean.parseBoolean(commandLine.getOptionValue("slient"))); int code = 0; if (commandLine.getOptionValues("e") != null) { commands = Arrays.asList(commandLine.getOptionValues("e")); } if (!commands.isEmpty() && getOpts().getScriptFile() != null) { System.err.println("The '-e' and '-f' options cannot be specified simultaneously"); optionsProcessor.printCliUsage(); return 1; } if (!commands.isEmpty()) { embeddedConnect(); connectDBInEmbededMode(); for (Iterator<String> i = commands.iterator(); i.hasNext(); ) { String command = i.next().toString(); debug(loc("executing-command", command)); if (!dispatch(command)) { code++; } } exit = true; // execute and exit } return code; } int initArgs(String[] args) { List<String> commands = Collections.emptyList(); CommandLine cl; BeelineParser beelineParser; try { beelineParser = new BeelineParser(); cl = beelineParser.parse(options, args); } catch (ParseException e1) { output(e1.getMessage()); usage(); return -1; } boolean connSuccessful = connectUsingArgs(beelineParser, cl); // checks if default hs2 connection configuration file is present // and uses it to connect if found // no-op if the file is not present if(!connSuccessful && !exit) { connSuccessful = defaultBeelineConnect(); } int code = 0; if (cl.getOptionValues('e') != null) { commands = Arrays.asList(cl.getOptionValues('e')); } if (!commands.isEmpty() && getOpts().getScriptFile() != null) { error("The '-e' and '-f' options cannot be specified simultaneously"); return 1; } else if(!commands.isEmpty() && !connSuccessful) { error("Cannot run commands specified using -e. No current connection"); return 1; } if (!commands.isEmpty()) { for (Iterator<String> i = commands.iterator(); i.hasNext();) { String command = i.next().toString(); debug(loc("executing-command", command)); if (!dispatch(command)) { code++; } } exit = true; // execute and exit } return code; } /* * Connects using the command line arguments. There are two * possible ways to connect here 1. using the cmd line arguments like -u * or using !properties <property-file> */ private boolean connectUsingArgs(BeelineParser beelineParser, CommandLine cl) { String driver = null, user = null, pass = "", url = null; String auth = null; if (cl.hasOption("help")) { usage(); getOpts().setHelpAsked(true); return true; } Properties hiveVars = cl.getOptionProperties("hivevar"); for (String key : hiveVars.stringPropertyNames()) { getOpts().getHiveVariables().put(key, hiveVars.getProperty(key)); } Properties hiveConfs = cl.getOptionProperties("hiveconf"); for (String key : hiveConfs.stringPropertyNames()) { setHiveConfVar(key, hiveConfs.getProperty(key)); } driver = cl.getOptionValue("d"); auth = cl.getOptionValue("a"); user = cl.getOptionValue("n"); getOpts().setAuthType(auth); if (cl.hasOption("w")) { pass = obtainPasswordFromFile(cl.getOptionValue("w")); } else { if (beelineParser.isPasswordOptionSet) { pass = cl.getOptionValue("p"); } } url = cl.getOptionValue("u"); if ((url == null) && cl.hasOption("reconnect")){ // If url was not specified with -u, but -r was present, use that. url = getOpts().getLastConnectedUrl(); } getOpts().setInitFiles(cl.getOptionValues("i")); getOpts().setScriptFile(cl.getOptionValue("f")); if (url != null) { String com; String comForDebug; if(pass != null) { com = constructCmd(url, user, pass, driver, false); comForDebug = constructCmd(url, user, pass, driver, true); } else { com = constructCmdUrl(url, user, driver, false); comForDebug = constructCmdUrl(url, user, driver, true); } debug(comForDebug); return dispatch(com); } // load property file String propertyFile = cl.getOptionValue("property-file"); if (propertyFile != null) { try { this.consoleReader = new ConsoleReader(); } catch (IOException e) { handleException(e); } if (!dispatch("!properties " + propertyFile)) { exit = true; return false; } } return false; } private void setHiveConfVar(String key, String val) { getOpts().getHiveConfVariables().put(key, val); if (HiveConf.ConfVars.HIVE_EXECUTION_ENGINE.varname.equals(key) && "mr".equals(val)) { info(HiveConf.generateMrDeprecationWarning()); } } private String constructCmd(String url, String user, String pass, String driver, boolean stripPasswd) { return new StringBuilder() .append("!connect ") .append(url) .append(" ") .append(user == null || user.length() == 0 ? "''" : user) .append(" ") .append(stripPasswd ? PASSWD_MASK : (pass.length() == 0 ? "''" : pass)) .append(" ") .append((driver == null ? "" : driver)) .toString(); } /** * This is an internal method used to create !connect command when -p option is used without * providing the password on the command line. Connect command returned should be ; separated * key-value pairs along with the url. We cannot use space separated !connect url user [password] * [driver] here since both password and driver are optional and there would be no way to * distinguish if the last string is password or driver * * @param url connection url passed using -u argument on the command line * @param user username passed through command line * @param driver driver passed through command line -d option * @param stripPasswd when set to true generates a !connect command which strips the password for * logging purposes * @return !connect command */ private String constructCmdUrl(String url, String user, String driver, boolean stripPasswd) { StringBuilder command = new StringBuilder("!connect "); command.append(url); //if the url does not have a database name add the trailing '/' if(isTrailingSlashNeeded(url)) { command.append('/'); } command.append(';'); // if the username is not already available in the URL add the one provided if (Utils.parsePropertyFromUrl(url, JdbcConnectionParams.AUTH_USER) == null) { command.append(JdbcConnectionParams.AUTH_USER); command.append('='); command.append((user == null || user.length() == 0 ? "''" : user)); } if (stripPasswd) { // if password is available in url it needs to be striped int startIndex = command.indexOf(JdbcConnectionParams.AUTH_PASSWD + "=") + JdbcConnectionParams.AUTH_PASSWD.length() + 2; if(startIndex != -1) { int endIndex = command.toString().indexOf(";", startIndex); command.replace(startIndex, (endIndex == -1 ? command.length() : endIndex), BeeLine.PASSWD_MASK); } } // if the driver is not already available in the URL add the one provided if (Utils.parsePropertyFromUrl(url, JdbcConnectionParams.PROPERTY_DRIVER) == null && driver != null) { command.append(';'); command.append(JdbcConnectionParams.PROPERTY_DRIVER); command.append("="); command.append(driver); } return command.toString(); } /* * Returns true if trailing slash is needed to be appended to the url */ private boolean isTrailingSlashNeeded(String url) { if (url.toLowerCase().startsWith("jdbc:hive2://")) { return url.indexOf('/', "jdbc:hive2://".length()) < 0; } return false; } /** * Obtains a password from the passed file path. */ private String obtainPasswordFromFile(String passwordFilePath) { try { Path path = Paths.get(passwordFilePath); byte[] passwordFileContents = Files.readAllBytes(path); return new String(passwordFileContents, "UTF-8").trim(); } catch (Exception e) { throw new RuntimeException("Unable to read user password from the password file: " + passwordFilePath, e); } } public void updateOptsForCli() { getOpts().updateBeeLineOptsFromConf(); getOpts().setShowHeader(false); getOpts().setOutputFormat("dsv"); getOpts().setDelimiterForDSV(' '); getOpts().setNullEmptyString(true); } /** * Start accepting input from stdin, and dispatch it * to the appropriate {@link CommandHandler} until the * global variable <code>exit</code> is true. */ public int begin(String[] args, InputStream inputStream) throws IOException { try { // load the options first, so we can override on the command line getOpts().load(); } catch (Exception e) { // nothing } setupHistory(); //add shutdown hook to cleanup the beeline for smooth exit addBeelineShutdownHook(); //this method also initializes the consoleReader which is //needed by initArgs for certain execution paths ConsoleReader reader = initializeConsoleReader(inputStream); if (isBeeLine) { int code = initArgs(args); if (code != 0) { return code; } } else { int code = initArgsFromCliVars(args); if (code != 0 || exit) { return code; } defaultConnect(false); } if (getOpts().isHelpAsked()) { return 0; } if (getOpts().getScriptFile() != null) { return executeFile(getOpts().getScriptFile()); } try { info(getApplicationTitle()); } catch (Exception e) { // ignore } return execute(reader, false); } /* * Attempts to make a connection using default HS2 connection config file if available * if there connection is not made return false * */ private boolean defaultBeelineConnect() { String url; try { url = getDefaultConnectionUrl(); if (url == null) { debug("Default hs2 connection config file not found"); return false; } } catch (BeelineHS2ConnectionFileParseException e) { error(e); return false; } return dispatch("!connect " + url); } private String getDefaultConnectionUrl() throws BeelineHS2ConnectionFileParseException { HS2ConnectionFileParser userHS2ConnFileParser = getUserHS2ConnFileParser(); if (!userHS2ConnFileParser.configExists()) { // nothing to do if there is no user HS2 connection configuration file return null; } // get the connection properties from user specific config file Properties userConnectionProperties = userHS2ConnFileParser.getConnectionProperties(); // load the HS2 connection url properties from hive-site.xml if it is present in the classpath HS2ConnectionFileParser hiveSiteParser = getHiveSiteHS2ConnectionFileParser(); Properties hiveSiteConnectionProperties = hiveSiteParser.getConnectionProperties(); // add/override properties found from hive-site with user-specific properties for (String key : userConnectionProperties.stringPropertyNames()) { if (hiveSiteConnectionProperties.containsKey(key)) { debug("Overriding connection url property " + key + " from user connection configuration file"); } hiveSiteConnectionProperties.setProperty(key, userConnectionProperties.getProperty(key)); } // return the url based on the aggregated connection properties return HS2ConnectionFileUtils.getUrl(hiveSiteConnectionProperties); } /* * Increased visibility of this method is only for providing better test coverage */ @VisibleForTesting public HS2ConnectionFileParser getUserHS2ConnFileParser() { return new UserHS2ConnectionFileParser(); } /* * Increased visibility of this method is only for providing better test coverage */ @VisibleForTesting public HS2ConnectionFileParser getHiveSiteHS2ConnectionFileParser() { return new HiveSiteHS2ConnectionFileParser(); } int runInit() { String initFiles[] = getOpts().getInitFiles(); if (initFiles != null && initFiles.length != 0) { for (String initFile : initFiles) { info("Running init script " + initFile); try { return executeFile(initFile); } finally { exit = false; } } } return ERRNO_OK; } private int embeddedConnect() { if (!execCommandWithPrefix("!connect " + Utils.URL_PREFIX + " '' ''")) { return ERRNO_OTHER; } else { return ERRNO_OK; } } private int connectDBInEmbededMode() { if (dbName != null && !dbName.isEmpty()) { if (!dispatch("use " + dbName + ";")) { return ERRNO_OTHER; } } return ERRNO_OK; } public int defaultConnect(boolean exitOnError) { if (embeddedConnect() != ERRNO_OK && exitOnError) { return ERRNO_OTHER; } if (connectDBInEmbededMode() != ERRNO_OK && exitOnError) { return ERRNO_OTHER; } return ERRNO_OK; } private int executeFile(String fileName) { InputStream fileStream = null; try { if (!isBeeLine) { org.apache.hadoop.fs.Path path = new org.apache.hadoop.fs.Path(fileName); FileSystem fs; HiveConf conf = getCommands().getHiveConf(true); if (!path.toUri().isAbsolute()) { fs = FileSystem.getLocal(conf); path = fs.makeQualified(path); } else { fs = FileSystem.get(path.toUri(), conf); } fileStream = fs.open(path); } else { fileStream = new FileInputStream(fileName); } return execute(initializeConsoleReader(fileStream), !getOpts().getForce()); } catch (Throwable t) { handleException(t); return ERRNO_OTHER; } finally { IOUtils.closeStream(fileStream); output(""); // dummy new line } } private int execute(ConsoleReader reader, boolean exitOnError) { int lastExecutionResult = ERRNO_OK; while (!exit) { try { // Execute one instruction; terminate on executing a script if there is an error // in silent mode, prevent the query and prompt being echoed back to terminal String line = (getOpts().isSilent() && getOpts().getScriptFile() != null) ? reader .readLine(null, ConsoleReader.NULL_MASK) : reader.readLine(getPrompt()); // trim line if (line != null) { line = line.trim(); } if (!dispatch(line)) { lastExecutionResult = ERRNO_OTHER; if (exitOnError) break; } else if (line != null) { lastExecutionResult = ERRNO_OK; } } catch (Throwable t) { handleException(t); return ERRNO_OTHER; } } return lastExecutionResult; } @Override public void close() { commands.closeall(null); } private void setupHistory() throws IOException { if (this.history != null) { return; } this.history = new FileHistory(new File(getOpts().getHistoryFile())); } private void addBeelineShutdownHook() throws IOException { // add shutdown hook to flush the history to history file and it also close all open connections ShutdownHookManager.addShutdownHook(getShutdownHook()); } public ConsoleReader initializeConsoleReader(InputStream inputStream) throws IOException { if (inputStream != null) { // ### NOTE: fix for sf.net bug 879425. // Working around an issue in jline-2.1.2, see https://github.com/jline/jline/issues/10 // by appending a newline to the end of inputstream InputStream inputStreamAppendedNewline = new SequenceInputStream(inputStream, new ByteArrayInputStream((new String("\n")).getBytes())); consoleReader = new ConsoleReader(inputStreamAppendedNewline, getErrorStream()); consoleReader.setCopyPasteDetection(true); // jline will detect if <tab> is regular character } else { consoleReader = new ConsoleReader(getInputStream(), getErrorStream()); } //disable the expandEvents for the purpose of backward compatibility consoleReader.setExpandEvents(false); try { // now set the output for the history consoleReader.setHistory(this.history); } catch (Exception e) { handleException(e); } if (inputStream instanceof FileInputStream || inputStream instanceof FSDataInputStream) { // from script.. no need to load history and no need of completer, either return consoleReader; } consoleReader.addCompleter(new BeeLineCompleter(this)); return consoleReader; } void usage() { output(loc("cmd-usage")); } /** * This method is used for executing commands beginning with ! * @param line * @return */ public boolean execCommandWithPrefix(String line) { Map<String, CommandHandler> cmdMap = new TreeMap<String, CommandHandler>(); line = line.substring(1); for (int i = 0; i < commandHandlers.length; i++) { String match = commandHandlers[i].matches(line); if (match != null) { cmdMap.put(match, commandHandlers[i]); } } 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); } /** * Dispatch the specified line to the appropriate {@link CommandHandler}. * * @param line * the command-line to dispatch * @return true if the command was "successful" */ boolean dispatch(String line) { if (line == null) { // exit exit = true; return true; } if (line.trim().length() == 0) { return true; } if (isComment(line)) { return true; } line = line.trim(); // save it to the current script, if any if (scriptOutputFile != null) { scriptOutputFile.addLine(line); } if (isHelpRequest(line)) { line = "!help"; } if (isBeeLine) { if (line.startsWith(COMMAND_PREFIX)) { // handle SQLLine command in beeline which starts with ! and does not end with ; return execCommandWithPrefix(line); } else { return commands.sql(line, getOpts().getEntireLineAsCommand()); } } else { return commands.sql(line, getOpts().getEntireLineAsCommand()); } } /** * Test whether a line requires a continuation. * * @param line * the line to be tested * * @return true if continuation required */ boolean needsContinuation(String line) { if (isHelpRequest(line)) { return false; } if (line.startsWith(COMMAND_PREFIX)) { return false; } if (isComment(line)) { return false; } String trimmed = line.trim(); if (trimmed.length() == 0) { return false; } if (!getOpts().isAllowMultiLineCommand()) { return false; } return !trimmed.endsWith(getOpts().getDelimiter()); } /** * Test whether a line is a help request other than !help. * * @param line * the line to be tested * * @return true if a help request */ boolean isHelpRequest(String line) { return line.equals("?") || line.equalsIgnoreCase("help"); } /** * Test whether a line is a comment. * * @param line * the line to be tested * * @return true if a comment */ boolean isComment(String line) { // SQL92 comment prefix is "--" // beeline also supports shell-style "#" prefix String lineTrimmed = line.trim(); return lineTrimmed.startsWith("#") || lineTrimmed.startsWith("--"); } String[] getCommands(File file) throws IOException { List<String> cmds = new LinkedList<String>(); try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"))) { StringBuilder cmd = null; while (true) { String scriptLine = reader.readLine(); if (scriptLine == null) { break; } String trimmedLine = scriptLine.trim(); if (getOpts().getTrimScripts()) { scriptLine = trimmedLine; } if (cmd != null) { // we're continuing an existing command cmd.append("\n"); cmd.append(scriptLine); if (trimmedLine.endsWith(getOpts().getDelimiter())) { // this command has terminated cmds.add(cmd.toString()); cmd = null; } } else { // we're starting a new command if (needsContinuation(scriptLine)) { // multi-line cmd = new StringBuilder(scriptLine); } else { // single-line cmds.add(scriptLine); } } } if (cmd != null) { // ### REVIEW: oops, somebody left the last command // unterminated; should we fix it for them or complain? // For now be nice and fix it. cmd.append(getOpts().getDelimiter()); cmds.add(cmd.toString()); } } return cmds.toArray(new String[0]); } /** * Print the specified message to the console * * @param msg * the message to print */ void output(String msg) { output(msg, true); } void info(String msg) { if (!(getOpts().isSilent())) { output(msg, true, getErrorStream()); } } void info(ColorBuffer msg) { if (!(getOpts().isSilent())) { output(msg, true, getErrorStream()); } } /** * Issue the specified error message * * @param msg * the message to issue * @return false always */ boolean error(String msg) { output(getColorBuffer().red(msg), true, getErrorStream()); return false; } boolean error(Throwable t) { handleException(t); return false; } void debug(String msg) { if (getOpts().getVerbose()) { output(getColorBuffer().blue(msg), true, getErrorStream()); } } void output(ColorBuffer msg) { output(msg, true); } void output(String msg, boolean newline, PrintStream out) { output(getColorBuffer(msg), newline, out); } void output(ColorBuffer msg, boolean newline) { output(msg, newline, getOutputStream()); } void output(ColorBuffer msg, boolean newline, PrintStream out) { if (newline) { out.println(msg.getColor()); } else { out.print(msg.getColor()); } if (recordOutputFile == null) { return; } // only write to the record file if we are writing a line ... // otherwise we might get garbage from backspaces and such. if (newline) { recordOutputFile.addLine(msg.getMono()); // always just write mono } else { recordOutputFile.print(msg.getMono()); } } /** * Print the specified message to the console * * @param msg * the message to print * @param newline * if false, do not append a newline */ void output(String msg, boolean newline) { output(getColorBuffer(msg), newline); } void autocommitStatus(Connection c) throws SQLException { info(loc("autocommit-status", c.getAutoCommit() + "")); } /** * Ensure that autocommit is on for the current connection * * @return true if autocommit is set */ boolean assertAutoCommit() { if (!(assertConnection())) { return false; } try { if (getDatabaseConnection().getConnection().getAutoCommit()) { return error(loc("autocommit-needs-off")); } } catch (Exception e) { return error(e); } return true; } /** * Assert that we have an active, living connection. Print * an error message if we do not. * * @return true if there is a current, active connection */ boolean assertConnection() { try { if (getDatabaseConnection() == null || getDatabaseConnection().getConnection() == null) { return error(loc("no-current-connection")); } if (getDatabaseConnection().getConnection().isClosed()) { return error(loc("connection-is-closed")); } } catch (SQLException sqle) { return error(loc("no-current-connection")); } return true; } /** * Print out any warnings that exist for the current connection. */ void showWarnings() { try { if (getDatabaseConnection().getConnection() == null || !getOpts().getVerbose()) { return; } showWarnings(getDatabaseConnection().getConnection().getWarnings()); } catch (Exception e) { handleException(e); } } /** * Print the specified warning on the console, as well as * any warnings that are returned from {@link SQLWarning#getNextWarning}. * * @param warn * the {@link SQLWarning} to print */ void showWarnings(SQLWarning warn) { if (warn == null) { return; } if (seenWarnings.get(warn) == null) { // don't re-display warnings we have already seen seenWarnings.put(warn, new java.util.Date()); handleSQLException(warn); } SQLWarning next = warn.getNextWarning(); if (next != warn) { showWarnings(next); } } String getPrompt() { if (isBeeLine) { return getPromptForBeeline(); } else { return getPromptForCli(); } } String getPromptForCli() { String prompt; // read prompt configuration and substitute variables. HiveConf conf = getCommands().getHiveConf(true); prompt = conf.getVar(HiveConf.ConfVars.CLIPROMPT); prompt = getCommands().substituteVariables(conf, prompt); return prompt + getFormattedDb() + "> "; } /** * Retrieve the current database name string to display, based on the * configuration value. * * @return String to show user for current db value */ String getFormattedDb() { if (!getOpts().getShowDbInPrompt()) { return ""; } String currDb = getCurrentDatabase(); if (currDb == null) { return ""; } return " (" + currDb + ")"; } String getPromptForBeeline() { if (getDatabaseConnection() == null || getDatabaseConnection().getUrl() == null) { return "beeline> "; } else { String printClosed = getDatabaseConnection().isClosed() ? " (closed)" : ""; return getPromptForBeeline(getDatabaseConnections().getIndex() + ": " + getDatabaseConnection().getUrl()) + printClosed + getFormattedDb() + "> "; } } static String getPromptForBeeline(String url) { if (url == null || url.length() == 0) { url = "beeline"; } if (url.indexOf(";") > -1) { url = url.substring(0, url.indexOf(";")); } if (url.indexOf("?") > -1) { url = url.substring(0, url.indexOf("?")); } if (url.length() > 45) { url = url.substring(0, 45); } return url; } /** * Try to obtain the current size of the specified {@link ResultSet} by jumping to the last row * and getting the row number. * * @param rs * the {@link ResultSet} to get the size for * @return the size, or -1 if it could not be obtained */ int getSize(ResultSet rs) { try { if (rs.getType() == rs.TYPE_FORWARD_ONLY) { return -1; } rs.last(); int total = rs.getRow(); rs.beforeFirst(); return total; } catch (SQLException sqle) { return -1; } // JDBC 1 driver error catch (AbstractMethodError ame) { return -1; } } ResultSet getColumns(String table) throws SQLException { if (!(assertConnection())) { return null; } return getDatabaseConnection().getDatabaseMetaData().getColumns( getDatabaseConnection().getDatabaseMetaData().getConnection().getCatalog(), null, table, "%"); } ResultSet getTables() throws SQLException { if (!(assertConnection())) { return null; } return getDatabaseConnection().getDatabaseMetaData().getTables( getDatabaseConnection().getDatabaseMetaData().getConnection().getCatalog(), null, "%", new String[] {"TABLE"}); } String[] getColumnNames(DatabaseMetaData meta) throws SQLException { Set<String> names = new HashSet<String>(); info(loc("building-tables")); try { ResultSet columns = getColumns("%"); try { int total = getSize(columns); int index = 0; while (columns.next()) { // add the following strings: // 1. column name // 2. table name // 3. tablename.columnname progress(index++, total); String name = columns.getString("TABLE_NAME"); names.add(name); names.add(columns.getString("COLUMN_NAME")); names.add(columns.getString("TABLE_NAME") + "." + columns.getString("COLUMN_NAME")); } progress(index, index); } finally { columns.close(); } info(loc("done")); return names.toArray(new String[0]); } catch (Throwable t) { handleException(t); return new String[0]; } } // ////////////////// // String utilities // ////////////////// /** * Split the line into an array by tokenizing on space characters * * @param line * the line to break up * @return an array of individual words */ String[] split(String line) { return split(line, " "); } String dequote(String str) { if (str == null) { return null; } while ((str.startsWith("'") && str.endsWith("'")) || (str.startsWith("\"") && str.endsWith("\""))) { str = str.substring(1, str.length() - 1); } return str; } String[] split(String line, String delim) { StringTokenizer tok = new StringTokenizer(line, delim); String[] ret = new String[tok.countTokens()]; int index = 0; while (tok.hasMoreTokens()) { String t = tok.nextToken(); t = dequote(t); ret[index++] = t; } return ret; } static Map<Object, Object> map(Object[] obs) { Map<Object, Object> m = new HashMap<Object, Object>(); for (int i = 0; i < obs.length - 1; i += 2) { m.put(obs[i], obs[i + 1]); } return Collections.unmodifiableMap(m); } static boolean getMoreResults(Statement stmnt) { try { return stmnt.getMoreResults(); } catch (Throwable t) { return false; } } static String xmlattrencode(String str) { str = replace(str, "\"", """); str = replace(str, "<", "<"); return str; } static String replace(String source, String from, String to) { if (source == null) { return null; } if (from.equals(to)) { return source; } StringBuilder replaced = new StringBuilder(); int index = -1; while ((index = source.indexOf(from)) != -1) { replaced.append(source.substring(0, index)); replaced.append(to); source = source.substring(index + from.length()); } replaced.append(source); return replaced.toString(); } /** * Split the line based on spaces, asserting that the * number of words is correct. * * @param line * the line to split * @param assertLen * the number of words to assure * @param usage * the message to output if there are an incorrect * number of words. * @return the split lines, or null if the assertion failed. */ String[] split(String line, int assertLen, String usage) { String[] ret = split(line); if (ret.length != assertLen) { error(usage); return null; } return ret; } /** * Wrap the specified string by breaking on space characters. * * @param toWrap * the string to wrap * @param len * the maximum length of any line * @param start * the number of spaces to pad at the * beginning of a line * @return the wrapped string */ String wrap(String toWrap, int len, int start) { StringBuilder buff = new StringBuilder(); StringBuilder line = new StringBuilder(); char[] head = new char[start]; Arrays.fill(head, ' '); for (StringTokenizer tok = new StringTokenizer(toWrap, " "); tok.hasMoreTokens();) { String next = tok.nextToken(); if (line.length() + next.length() > len) { buff.append(line).append(separator).append(head); line.setLength(0); } line.append(line.length() == 0 ? "" : " ").append(next); } buff.append(line); return buff.toString(); } /** * Output a progress indicator to the console. * * @param cur * the current progress * @param max * the maximum progress, or -1 if unknown */ void progress(int cur, int max) { StringBuilder out = new StringBuilder(); if (lastProgress != null) { char[] back = new char[lastProgress.length()]; Arrays.fill(back, '\b'); out.append(back); } String progress = cur + "/" + (max == -1 ? "?" : "" + max) + " " + (max == -1 ? "(??%)" : ("(" + (cur * 100 / (max == 0 ? 1 : max)) + "%)")); if (cur >= max && max != -1) { progress += " " + loc("done") + separator; lastProgress = null; } else { lastProgress = progress; } out.append(progress); outputStream.print(out.toString()); outputStream.flush(); } // ///////////////////////////// // Exception handling routines // ///////////////////////////// void handleException(Throwable e) { while (e instanceof InvocationTargetException) { e = ((InvocationTargetException) e).getTargetException(); } if (e instanceof SQLException) { handleSQLException((SQLException) e); } else if (e instanceof EOFException) { setExit(true); // CTRL-D } else if (!(getOpts().getVerbose())) { if (e.getMessage() == null) { error(e.getClass().getName()); } else { error(e.getMessage()); } } else { e.printStackTrace(getErrorStream()); } } void handleSQLException(SQLException e) { if (e instanceof SQLWarning && !(getOpts().getShowWarnings())) { return; } if (e.getCause() instanceof TTransportException) { switch (((TTransportException)e.getCause()).getType()) { case TTransportException.ALREADY_OPEN: error(loc("hs2-connection-already-open")); break; case TTransportException.END_OF_FILE: error(loc("hs2-unexpected-end-of-file")); break; case TTransportException.NOT_OPEN: error(loc("hs2-could-not-open-connection")); break; case TTransportException.TIMED_OUT: error(loc("hs2-connection-timed-out")); break; case TTransportException.UNKNOWN: error(loc("hs2-unknown-connection-problem")); break; default: error(loc("hs2-unexpected-error")); } } error(loc(e instanceof SQLWarning ? "Warning" : "Error", new Object[] { e.getMessage() == null ? "" : e.getMessage().trim(), e.getSQLState() == null ? "" : e.getSQLState().trim(), new Integer(e.getErrorCode())})); if (getOpts().getVerbose()) { e.printStackTrace(getErrorStream()); } if (!getOpts().getShowNestedErrs()) { return; } for (SQLException nested = e.getNextException(); nested != null && nested != e; nested = nested .getNextException()) { handleSQLException(nested); } } boolean scanForDriver(String url) { try { // already registered if (findRegisteredDriver(url) != null) { return true; } // first try known drivers... scanDrivers(true); if (findRegisteredDriver(url) != null) { return true; } // now really scan... scanDrivers(false); if (findRegisteredDriver(url) != null) { return true; } // find whether exists a local driver to accept the url if (findLocalDriver(url) != null) { return true; } return false; } catch (Exception e) { debug(e.toString()); return false; } } private Driver findRegisteredDriver(String url) { for (Enumeration drivers = DriverManager.getDrivers(); drivers != null && drivers.hasMoreElements();) { Driver driver = (Driver) drivers.nextElement(); try { if (driver.acceptsURL(url)) { return driver; } } catch (Exception e) { } } return null; } public Driver findLocalDriver(String url) throws Exception { if(drivers == null){ return null; } for (Driver d : drivers) { try { String clazzName = d.getClass().getName(); Driver driver = (Driver) Class.forName(clazzName, true, Thread.currentThread().getContextClassLoader()).newInstance(); if (driver.acceptsURL(url) && isSupportedLocalDriver(driver)) { return driver; } } catch (SQLException e) { error(e); throw new Exception(e); } } return null; } public boolean isSupportedLocalDriver(Driver driver) { String driverName = driver.getClass().getName(); for (String name : supportedLocalDriver) { if (name.equals(driverName)) { return true; } } return false; } public void addLocalDriverClazz(String driverClazz) { supportedLocalDriver.add(driverClazz); } Driver[] scanDrivers(String line) throws IOException { return scanDrivers(false); } Driver[] scanDrivers(boolean knownOnly) throws IOException { long start = System.currentTimeMillis(); Set<String> classNames = new HashSet<String>(); if (!knownOnly) { classNames.addAll(Arrays.asList( ClassNameCompleter.getClassNames())); } classNames.addAll(KNOWN_DRIVERS); Set driverClasses = new HashSet(); for (Iterator<String> i = classNames.iterator(); i.hasNext();) { String className = i.next().toString(); if (className.toLowerCase().indexOf("driver") == -1) { continue; } try { Class c = Class.forName(className, false, Thread.currentThread().getContextClassLoader()); if (!Driver.class.isAssignableFrom(c)) { continue; } if (Modifier.isAbstract(c.getModifiers())) { continue; } // now instantiate and initialize it driverClasses.add(c.newInstance()); } catch (Throwable t) { } } info("scan complete in " + (System.currentTimeMillis() - start) + "ms"); return (Driver[]) driverClasses.toArray(new Driver[0]); } private Driver[] scanDriversOLD(String line) { long start = System.currentTimeMillis(); Set<String> paths = new HashSet<String>(); Set driverClasses = new HashSet(); for (StringTokenizer tok = new StringTokenizer( System.getProperty("java.ext.dirs"), System.getProperty("path.separator")); tok.hasMoreTokens();) { File[] files = new File(tok.nextToken()).listFiles(); for (int i = 0; files != null && i < files.length; i++) { paths.add(files[i].getAbsolutePath()); } } for (StringTokenizer tok = new StringTokenizer( System.getProperty("java.class.path"), System.getProperty("path.separator")); tok.hasMoreTokens();) { paths.add(new File(tok.nextToken()).getAbsolutePath()); } for (Iterator<String> i = paths.iterator(); i.hasNext();) { File f = new File(i.next()); output(getColorBuffer().pad(loc("scanning", f.getAbsolutePath()), 60), false); try { ZipFile zf = new ZipFile(f); int total = zf.size(); int index = 0; for (Enumeration zfEnum = zf.entries(); zfEnum.hasMoreElements();) { ZipEntry entry = (ZipEntry) zfEnum.nextElement(); String name = entry.getName(); progress(index++, total); if (name.endsWith(".class")) { name = name.replace('/', '.'); name = name.substring(0, name.length() - 6); try { // check for the string "driver" in the class // to see if we should load it. Not perfect, but // it is far too slow otherwise. if (name.toLowerCase().indexOf("driver") != -1) { Class c = Class.forName(name, false, getClass().getClassLoader()); if (Driver.class.isAssignableFrom(c) && !(Modifier.isAbstract( c.getModifiers()))) { try { // load and initialize Class.forName(name); } catch (Exception e) { } driverClasses.add(c.newInstance()); } } } catch (Throwable t) { } } } progress(total, total); } catch (Exception e) { } } info("scan complete in " + (System.currentTimeMillis() - start) + "ms"); return (Driver[]) driverClasses.toArray(new Driver[0]); } // ///////////////////////////////////// // ResultSet output formatting classes // ///////////////////////////////////// int print(ResultSet rs) throws SQLException { String format = getOpts().getOutputFormat(); OutputFormat f = (OutputFormat) formats.get(format); if (f == null) { error(loc("unknown-format", new Object[] { format, formats.keySet()})); f = new TableOutputFormat(this); } Rows rows; if (f instanceof TableOutputFormat) { if (getOpts().getIncremental()) { rows = new IncrementalRowsWithNormalization(this, rs); } else { rows = new BufferedRows(this, rs); } } else { rows = new IncrementalRows(this, rs); } return f.print(rows); } Statement createStatement() throws SQLException { Statement stmnt = getDatabaseConnection().getConnection().createStatement(); if (getOpts().timeout > -1) { stmnt.setQueryTimeout(getOpts().timeout); } if (signalHandler != null) { signalHandler.setStatement(stmnt); } return stmnt; } void runBatch(List<String> statements) { try { Statement stmnt = createStatement(); try { for (Iterator<String> i = statements.iterator(); i.hasNext();) { stmnt.addBatch(i.next().toString()); } int[] counts = stmnt.executeBatch(); output(getColorBuffer().pad(getColorBuffer().bold("COUNT"), 8) .append(getColorBuffer().bold("STATEMENT"))); for (int i = 0; counts != null && i < counts.length; i++) { output(getColorBuffer().pad(counts[i] + "", 8) .append(statements.get(i).toString())); } } finally { try { stmnt.close(); } catch (Exception e) { } } } catch (Exception e) { handleException(e); } } public int runCommands(String[] cmds) { return runCommands(Arrays.asList(cmds)); } public int runCommands(List<String> cmds) { int successCount = 0; try { // TODO: Make script output prefixing configurable. Had to disable this since // it results in lots of test diffs. for (String cmd : cmds) { info(getColorBuffer().pad(SCRIPT_OUTPUT_PREFIX, SCRIPT_OUTPUT_PAD_SIZE).append(cmd)); // if we do not force script execution, abort // when a failure occurs. if (dispatch(cmd) || getOpts().getForce()) { ++successCount; } else { error(loc("abort-on-error", cmd)); return successCount; } } } catch (Exception e) { handleException(e); } return successCount; } // //////////////////////// // Command methods follow // //////////////////////// void setCompletions() throws SQLException, IOException { if (getDatabaseConnection() != null) { getDatabaseConnection().setCompletions(getOpts().getFastConnect()); } } public BeeLineOpts getOpts() { return opts; } DatabaseConnections getDatabaseConnections() { return connections; } Runnable getShutdownHook() { return shutdownHook; } Completer getCommandCompletor() { return beeLineCommandCompleter; } public boolean isExit() { return exit; } public void setExit(boolean exit) { this.exit = exit; } Collection<Driver> getDrivers() { return drivers; } void setDrivers(Collection<Driver> drivers) { this.drivers = drivers; } public static String getSeparator() { return separator; } Commands getCommands() { return commands; } OutputFile getScriptOutputFile() { return scriptOutputFile; } void setScriptOutputFile(OutputFile script) { this.scriptOutputFile = script; } OutputFile getRecordOutputFile() { return recordOutputFile; } void setRecordOutputFile(OutputFile record) { this.recordOutputFile = record; } public void setOutputStream(PrintStream outputStream) { this.outputStream = new PrintStream(outputStream, true); } PrintStream getOutputStream() { return outputStream; } public void setErrorStream(PrintStream errorStream) { this.errorStream = new PrintStream(errorStream, true); } PrintStream getErrorStream() { return errorStream; } InputStream getInputStream() { return inputStream; } ConsoleReader getConsoleReader() { return consoleReader; } void setConsoleReader(ConsoleReader reader) { this.consoleReader = reader; } List<String> getBatch() { return batch; } void setBatch(List<String> batch) { this.batch = batch; } protected Reflector getReflector() { return reflector; } public boolean isBeeLine() { return 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; } /** * Setting the BeeLine into test mode. * Print only the errors, the operation log and the query results. * Should be used only by tests. * * @param isTestMode */ void setIsTestMode(boolean isTestMode) { this.isTestMode = isTestMode; } boolean isTestMode() { return isTestMode; } }