http://git-wip-us.apache.org/repos/asf/tajo/blob/c59baa3a/tajo-client/src/main/java/org/apache/tajo/cli/tsql/ParsedResult.java ---------------------------------------------------------------------- diff --git a/tajo-client/src/main/java/org/apache/tajo/cli/tsql/ParsedResult.java b/tajo-client/src/main/java/org/apache/tajo/cli/tsql/ParsedResult.java new file mode 100644 index 0000000..7894731 --- /dev/null +++ b/tajo-client/src/main/java/org/apache/tajo/cli/tsql/ParsedResult.java @@ -0,0 +1,53 @@ +/** + * 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.tajo.cli.tsql; + + +public class ParsedResult { + public static enum StatementType { + META, + STATEMENT + } + + private final StatementType type; + private final String historyStatement; + private final String statement; + + public ParsedResult(StatementType type, String statement, String historyStatement) { + this.type = type; + this.statement = statement; + this.historyStatement = historyStatement; + } + + public StatementType getType() { + return type; + } + + public String getHistoryStatement() { + return historyStatement.trim(); + } + + public String getStatement() { + return statement.trim(); + } + + public String toString() { + return "(" + type.name() + ") " + historyStatement; + } +}
http://git-wip-us.apache.org/repos/asf/tajo/blob/c59baa3a/tajo-client/src/main/java/org/apache/tajo/cli/tsql/SimpleParser.java ---------------------------------------------------------------------- diff --git a/tajo-client/src/main/java/org/apache/tajo/cli/tsql/SimpleParser.java b/tajo-client/src/main/java/org/apache/tajo/cli/tsql/SimpleParser.java new file mode 100644 index 0000000..56315a8 --- /dev/null +++ b/tajo-client/src/main/java/org/apache/tajo/cli/tsql/SimpleParser.java @@ -0,0 +1,388 @@ +/** + * 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.tajo.cli.tsql; + +import java.util.ArrayList; +import java.util.List; + +import static org.apache.tajo.cli.tsql.ParsedResult.StatementType.META; +import static org.apache.tajo.cli.tsql.ParsedResult.StatementType.STATEMENT; + +/** + * This is a parser used in tsql to parse multiple SQL lines into SQL statements. + * It helps tsql recognizes the termination of each SQL statement and quotation mark (') while + * parses multiple separate lines. + */ +public class SimpleParser { + + public static enum ParsingState { + TOK_START, // Start State + META, // Meta Command + STATEMENT, // Statement + WITHIN_QUOTE, // Within Quote + INVALID, // Invalid Statement + STATEMENT_EOS, // End State (End of Statement) + META_EOS // End State (End of Statement) + } + + ParsingState state = START_STATE; + int lineNum; + + /** + * It will be used to store a query statement into Jline history. + * the query statement for history does not include unnecessary white spaces and new line. + */ + private StringBuilder historyAppender = new StringBuilder(); + /** + * It will be used to submit a query statement to the TajoMaster. It just contains a raw query statement string. + */ + private StringBuilder rawAppender = new StringBuilder(); + + public static final ParsingState START_STATE = ParsingState.TOK_START; + + /** + * <h2>State Machine</h2> + * All whitespace are ignored in all cases except for + * + * <pre> + * (start) TOK_START --> META ---------------------> META_EOS + * | + * | + * | + * |-----------> STATEMENT ----------> STMT_EOS + * \ ^ + * \ / + * \-> WITHIN_QUOTE + * \ ^ + * \---/ + * </pre> + */ + + public static List<ParsedResult> parseScript(String str) throws InvalidStatementException { + SimpleParser parser = new SimpleParser(); + List<ParsedResult> parsedResults = new ArrayList<ParsedResult>(); + parsedResults.addAll(parser.parseLines(str)); + parsedResults.addAll(parser.EOF()); + return parsedResults; + } + + public List<ParsedResult> parseLines(String str) throws InvalidStatementException { + List<ParsedResult> statements = new ArrayList<ParsedResult>(); + int lineStartIdx; + int idx = 0; + char [] chars = str.toCharArray(); + + // if parsing continues, it means that the previous line is broken by '\n'. + // So, we should add new line to rawAppender. + if (isStatementContinue()) { + rawAppender.append("\n"); + } + + while(idx < str.length()) { + + // initialization for new statement + if (state == ParsingState.TOK_START) { + lineNum = 0; + + // ignore all whitespace before start + if (Character.isWhitespace(chars[idx])) { + idx++; + continue; + } + } + + //////////////////////////// + // TOK_START --> META + //////////////////////////// + + lineStartIdx = idx; + + if (state == ParsingState.TOK_START && chars[idx] == '\\') { + state = ParsingState.META; + + //////////////////////////// + // META --> TOK_EOS + //////////////////////////// + while (state != ParsingState.META_EOS && idx < chars.length) { + char character = chars[idx++]; + + if (isEndOfMeta(character)) { + state = ParsingState.META_EOS; + } else if (Character.isWhitespace(character)) { + // skip + } + } + + if (state == ParsingState.META_EOS) { + historyAppender.append(str.subSequence(lineStartIdx, idx - 1).toString()); + appendToRawStatement(str.subSequence(lineStartIdx, idx - 1).toString(), true); + } else { + historyAppender.append(str.subSequence(lineStartIdx, idx).toString()); + appendToRawStatement(str.subSequence(lineStartIdx, idx).toString(), true); + } + + } else if (isInlineCommentStart(chars, idx)) { + idx = consumeInlineComment(chars, idx); + appendToRawStatement(str.subSequence(lineStartIdx, idx).toString(), true); + + ///////////////////////////////// + // TOK_START -> STATEMENT + // or TOK_STATEMENT -> STATEMENT + //////////////////////////////// + } else if (isStatementContinue() || isStatementStart(chars[idx])) { + if (!isStatementContinue()) { // TOK_START -> STATEMENT + state = ParsingState.STATEMENT; + rawAppender.append("\n"); + } + + while (!isTerminateState(state) && idx < chars.length) { + char character = chars[idx++]; + + /////////////////////////////////////////////////////// + // in-statement loop BEGIN + /////////////////////////////////////////////////////// + if (isEndOfStatement(character)) { + state = ParsingState.STATEMENT_EOS; + + } else if (state == ParsingState.STATEMENT && character == '\n') { + appendToBothStatements(chars, lineStartIdx, idx, 1); // omit new line chacter '\n' from history statement + lineStartIdx = idx; + + } else if (state == ParsingState.STATEMENT && character == '\'') { // TOK_STATEMENT -> WITHIN_QUOTE + state = ParsingState.WITHIN_QUOTE; + + if (idx < chars.length) { + character = chars[idx++]; + } else { + continue; + } + + + // idx points the characters followed by the current character. So, we should use 'idx - 1' + // in order to point the current character. + } else if (state == ParsingState.STATEMENT && idx < chars.length && isInlineCommentStart(chars, idx - 1)) { + idx++; + appendToBothStatements(chars, lineStartIdx, idx, 2); // omit two dash characters '--' from history statement + int commentStartIdx = idx; + idx = consumeInlineComment(chars, idx); + appendToRawStatement(str.subSequence(commentStartIdx, idx).toString(), true); + lineStartIdx = idx; + } + /////////////////////////////////////////////////////// + // in-statement loop END + /////////////////////////////////////////////////////// + + if (state == ParsingState.WITHIN_QUOTE) { + while(idx < chars.length) { + /////////////////////////////// + // WITHIN_QUOTE --> STATEMENT + /////////////////////////////// + if (character == '\'') { + state = ParsingState.STATEMENT; + break; + } + character = chars[idx++]; + } + if (state == ParsingState.WITHIN_QUOTE && character == '\'') { + state = ParsingState.STATEMENT; + } + } + } + + // After all characters are consumed + + if (state == ParsingState.STATEMENT_EOS) { // If one query statement is terminated + appendToBothStatements(chars, lineStartIdx, idx - 1); // skip semicolon (;) + } else { + appendToBothStatements(chars, lineStartIdx, idx); + + // if it is not within quote and there is no space between lines, adds a space. + if (state == ParsingState.STATEMENT && (historyAppender.charAt(historyAppender.length() - 1) != ' ')) { + historyAppender.append(" "); + rawAppender.append("\n"); + } + } + } else { // skip unknown character + idx++; + } + + lineNum++; + statements.addAll(doProcessEndOfStatement(state == ParsingState.META)); + } + + return statements; + } + + /** + * Append the range of characters into a given StringBuilder instance. + * + * @param chars Characters + * @param fromIdx start character index + * @param toIdx end character index + */ + private void appendToStatement(StringBuilder builder, char[] chars, int fromIdx, int toIdx) { + builder.append(chars, fromIdx, toIdx - fromIdx); + } + + /** + * Append the range of characters into both history and raw appenders. It omits the number of characters specified by + * <code>omitCharNums</code>. + * + * + * @param chars Characters + * @param fromIdx start character index + * @param toIdx end character index + * @param omitCharNums how many characters will be omitted from history statement + */ + private void appendToBothStatements(char[] chars, int fromIdx, int toIdx, int omitCharNums) { + appendToStatement(historyAppender, chars, fromIdx, toIdx - omitCharNums); + if (historyAppender.charAt(historyAppender.length() - 1) != ' ') { + historyAppender.append(" "); + } + appendToStatement(rawAppender, chars, fromIdx, toIdx); + } + + /** + * Append the range of characters into both history and raw appenders. + * + * + * @param chars Characters + * @param fromIdx start character index + * @param toIdx end character index + */ + private void appendToBothStatements(char[] chars, int fromIdx, int toIdx) { + historyAppender.append(chars, fromIdx, toIdx - fromIdx); + rawAppender.append(chars, fromIdx, toIdx - fromIdx); + } + + private int consumeInlineComment(char [] chars, int currentIdx) { + currentIdx++; + while (currentIdx < chars.length && !isNewLine(chars[currentIdx])) { + currentIdx++; + } + return currentIdx; + } + + private void appendToRawStatement(String str, boolean addLF) { + if (!str.isEmpty() && !"\n".equals(str) && + rawAppender.length() > 0 && addLF && rawAppender.charAt(rawAppender.length() - 1) != '\n') { + rawAppender.append(str); + } else { + rawAppender.append(str); + } + } + + private static boolean isEndOfMeta(char character) { + return character == ';' || character == '\n'; + } + + private static boolean isEndOfStatement(char character) { + return character == ';'; + } + + /** + * It checks if inline comment '--' begins. + * @param chars + * @param idx + * @return + */ + private boolean isInlineCommentStart(char[] chars, int idx) { + if (idx >= chars.length - 1) { + return false; + } + return (state == ParsingState.STATEMENT || state == ParsingState.TOK_START) && + (chars[idx] == '-' && chars[idx + 1] == '-'); + } + + private boolean isNewLine(char character) { + return character == '\n'; + } + + private boolean isStatementStart(char character) { + return state == ParsingState.TOK_START && (Character.isLetterOrDigit(character)); + } + + private boolean isStatementContinue() { + return state == ParsingState.WITHIN_QUOTE || state == ParsingState.STATEMENT; + } + + /** + * process all parsed statements so far and return a list of parsed results. + * + * @param endOfFile TRUE if the end of file. + * @return the list of parsed results, each of result contains one query statement or meta command. + * @throws InvalidStatementException + */ + private List<ParsedResult> doProcessEndOfStatement(boolean endOfFile) throws InvalidStatementException { + List<ParsedResult> parsedResults = new ArrayList<ParsedResult>(); + String errorMessage = ""; + if (endOfFile) { + if (state == ParsingState.META) { + state = ParsingState.META_EOS; + } else if (state == ParsingState.STATEMENT) { + state = ParsingState.STATEMENT_EOS; + } else if (state == ParsingState.WITHIN_QUOTE) { + state = ParsingState.INVALID; + errorMessage = "unterminated quoted string at LINE " + lineNum; + } + } + + if (isTerminateState(state)) { + String historyStatement = historyAppender.toString(); + String rawStatement = rawAppender.toString(); + if (state == ParsingState.META_EOS) { + parsedResults.add(new ParsedResult(META, rawStatement, historyStatement)); + state = ParsingState.TOK_START; + } else if (state == ParsingState.STATEMENT_EOS) { + parsedResults.add(new ParsedResult(STATEMENT, rawStatement, historyStatement)); + } else { + throw new InvalidStatementException("ERROR: " + errorMessage); + } + + // reset all states + historyAppender.delete(0, historyAppender.length()); + rawAppender.delete(0, rawAppender.length()); + state = START_STATE; + } + + return parsedResults; + } + + /** + * It manually triggers the end of file. + * + * @return the list of parsed results, each of result contains one query statement or meta command. + * @throws InvalidStatementException + */ + public List<ParsedResult> EOF() throws InvalidStatementException { + return doProcessEndOfStatement(true); + } + + private static boolean isTerminateState(ParsingState state) { + return (state == ParsingState.META_EOS || state == ParsingState.STATEMENT_EOS || state == ParsingState.INVALID); + } + + public ParsingState getState() { + return state; + } + + public String toString() { + return "[" + state.name() + "]: " + historyAppender.toString(); + } +} http://git-wip-us.apache.org/repos/asf/tajo/blob/c59baa3a/tajo-client/src/main/java/org/apache/tajo/cli/tsql/TajoCli.java ---------------------------------------------------------------------- diff --git a/tajo-client/src/main/java/org/apache/tajo/cli/tsql/TajoCli.java b/tajo-client/src/main/java/org/apache/tajo/cli/tsql/TajoCli.java new file mode 100644 index 0000000..34e2170 --- /dev/null +++ b/tajo-client/src/main/java/org/apache/tajo/cli/tsql/TajoCli.java @@ -0,0 +1,691 @@ +/** + * 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.tajo.cli.tsql; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import com.google.protobuf.ServiceException; +import jline.UnsupportedTerminal; +import jline.console.ConsoleReader; +import org.apache.commons.cli.*; +import org.apache.tajo.*; +import org.apache.tajo.TajoProtos.QueryState; +import org.apache.tajo.catalog.TableDesc; +import org.apache.tajo.cli.tsql.commands.*; +import org.apache.tajo.client.*; +import org.apache.tajo.conf.TajoConf; +import org.apache.tajo.conf.TajoConf.ConfVars; +import org.apache.tajo.ipc.ClientProtos; +import org.apache.tajo.util.FileUtil; + +import java.io.*; +import java.lang.reflect.Constructor; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import static org.apache.tajo.cli.tsql.ParsedResult.StatementType.META; +import static org.apache.tajo.cli.tsql.ParsedResult.StatementType.STATEMENT; +import static org.apache.tajo.cli.tsql.SimpleParser.ParsingState; + +public class TajoCli { + public static final String ERROR_PREFIX = "ERROR: "; + public static final String KILL_PREFIX = "KILL: "; + + private final TajoConf conf; + private TajoClient client; + private final TajoCliContext context; + + // Jline and Console related things + private final ConsoleReader reader; + private final InputStream sin; + private final PrintWriter sout; + private TajoFileHistory history; + + // Current States + private String currentDatabase; + + private TajoCliOutputFormatter displayFormatter; + + private boolean wasError = false; + + private static final Class [] registeredCommands = { + DescTableCommand.class, + DescFunctionCommand.class, + HelpCommand.class, + ExitCommand.class, + CopyrightCommand.class, + VersionCommand.class, + ConnectDatabaseCommand.class, + ListDatabaseCommand.class, + SetCommand.class, + UnsetCommand.class, + ExecExternalShellCommand.class, + HdfsCommand.class, + TajoAdminCommand.class, + TajoGetConfCommand.class, + TajoHAAdminCommand.class + }; + private final Map<String, TajoShellCommand> commands = new TreeMap<String, TajoShellCommand>(); + + protected static final Options options; + private static final String HOME_DIR = System.getProperty("user.home"); + private static final String HISTORY_FILE = ".tajo_history"; + + static { + options = new Options(); + options.addOption("c", "command", true, "execute only single command, then exit"); + options.addOption("f", "file", true, "execute commands from file, then exit"); + options.addOption("h", "host", true, "Tajo server host"); + options.addOption("p", "port", true, "Tajo server port"); + options.addOption("B", "background", false, "execute as background process"); + options.addOption("conf", "conf", true, "configuration value"); + options.addOption("param", "param", true, "parameter value in SQL file"); + options.addOption("help", "help", false, "help"); + } + + public class TajoCliContext extends OverridableConf { + public TajoCliContext(TajoConf conf) { + super(conf, ConfigKey.ConfigType.SESSION); + } + + public TajoClient getTajoClient() { + return client; + } + + public void setCurrentDatabase(String databasae) { + currentDatabase = databasae; + } + + public String getCurrentDatabase() { + return currentDatabase; + } + + public PrintWriter getOutput() { + return sout; + } + + public TajoConf getConf() { + return conf; + } + + @VisibleForTesting + public String getCliSideVar(String key) { + if (SessionVars.exists(key)) { + ConfigKey configKey = SessionVars.get(key); + return get(configKey); + } else { + return get(key); + } + } + + public void setCliSideVar(String key, String value) { + Preconditions.checkNotNull(key); + Preconditions.checkNotNull(value); + + boolean shouldReloadFormatter = false; + + if (SessionVars.exists(key)) { + SessionVars configKey = SessionVars.get(key); + put(configKey, value); + shouldReloadFormatter = configKey.getMode() == SessionVars.VariableMode.CLI_SIDE_VAR; + } else { + set(key, value); + + // It is hard to recognize it is a client side variable. So, we always reload formatter. + shouldReloadFormatter = true; + } + + if (shouldReloadFormatter) { + try { + initFormatter(); + } catch (Exception e) { + System.err.println(ERROR_PREFIX + e.getMessage()); + } + } + } + + public Map<String, TajoShellCommand> getCommands() { + return commands; + } + } + + public TajoCli(TajoConf c, String [] args, InputStream in, OutputStream out) throws Exception { + CommandLineParser parser = new PosixParser(); + CommandLine cmd = parser.parse(options, args); + + this.conf = new TajoConf(c); + context = new TajoCliContext(conf); + this.sin = in; + if (cmd.hasOption("B")) { + this.reader = new ConsoleReader(sin, out, new UnsupportedTerminal()); + } else { + this.reader = new ConsoleReader(sin, out); + } + + this.reader.setExpandEvents(false); + this.sout = new PrintWriter(reader.getOutput()); + initFormatter(); + + if (cmd.hasOption("help")) { + printUsage(); + System.exit(0); + } + + String hostName = null; + Integer port = null; + if (cmd.hasOption("h")) { + hostName = cmd.getOptionValue("h"); + } + if (cmd.hasOption("p")) { + port = Integer.parseInt(cmd.getOptionValue("p")); + } + + String baseDatabase = null; + if (cmd.getArgList().size() > 0) { + baseDatabase = (String) cmd.getArgList().get(0); + } + + if (cmd.getOptionValues("conf") != null) { + processConfVarCommand(cmd.getOptionValues("conf")); + } + + // if there is no "-h" option, + if(hostName == null) { + if (conf.getVar(ConfVars.TAJO_MASTER_CLIENT_RPC_ADDRESS) != null) { + // it checks if the client service address is given in configuration and distributed mode. + // if so, it sets entryAddr. + hostName = conf.getVar(ConfVars.TAJO_MASTER_CLIENT_RPC_ADDRESS).split(":")[0]; + } + } + if (port == null) { + if (conf.getVar(ConfVars.TAJO_MASTER_CLIENT_RPC_ADDRESS) != null) { + // it checks if the client service address is given in configuration and distributed mode. + // if so, it sets entryAddr. + port = Integer.parseInt(conf.getVar(ConfVars.TAJO_MASTER_CLIENT_RPC_ADDRESS).split(":")[1]); + } + } + + if ((hostName == null) ^ (port == null)) { + System.err.println(ERROR_PREFIX + "cannot find valid Tajo server address"); + throw new RuntimeException("cannot find valid Tajo server address"); + } else if (hostName != null && port != null) { + conf.setVar(ConfVars.TAJO_MASTER_CLIENT_RPC_ADDRESS, hostName+":"+port); + client = new TajoClientImpl(conf, baseDatabase); + } else if (hostName == null && port == null) { + client = new TajoClientImpl(conf, baseDatabase); + } + + try { + checkMasterStatus(); + context.setCurrentDatabase(client.getCurrentDatabase()); + initHistory(); + initCommands(); + + if (cmd.getOptionValues("conf") != null) { + processSessionVarCommand(cmd.getOptionValues("conf")); + } + + if (cmd.hasOption("c")) { + displayFormatter.setScirptMode(); + int exitCode = executeScript(cmd.getOptionValue("c")); + sout.flush(); + System.exit(exitCode); + } + if (cmd.hasOption("f")) { + displayFormatter.setScirptMode(); + cmd.getOptionValues(""); + File sqlFile = new File(cmd.getOptionValue("f")); + if (sqlFile.exists()) { + String script = FileUtil.readTextFile(new File(cmd.getOptionValue("f"))); + script = replaceParam(script, cmd.getOptionValues("param")); + int exitCode = executeScript(script); + sout.flush(); + System.exit(exitCode); + } else { + System.err.println(ERROR_PREFIX + "No such a file \"" + cmd.getOptionValue("f") + "\""); + System.exit(-1); + } + } + } catch (Exception e) { + System.err.println(ERROR_PREFIX + "Exception was thrown. Caused by " + e.getMessage()); + + if (client != null) { + client.close(); + } + + throw e; + } + + addShutdownHook(); + } + + private void processConfVarCommand(String[] confCommands) throws ServiceException { + for (String eachParam: confCommands) { + String[] tokens = eachParam.split("="); + if (tokens.length != 2) { + continue; + } + + if (!SessionVars.exists(tokens[0])) { + conf.set(tokens[0], tokens[1]); + } + } + } + + private void processSessionVarCommand(String[] confCommands) throws ServiceException { + for (String eachParam: confCommands) { + String[] tokens = eachParam.split("="); + if (tokens.length != 2) { + continue; + } + + if (SessionVars.exists(tokens[0])) { + ((SetCommand)commands.get("\\set")).set(tokens[0], tokens[1]); + } + } + } + + private void initFormatter() throws Exception { + Class formatterClass = context.getClass(SessionVars.CLI_FORMATTER_CLASS); + if (displayFormatter == null || !displayFormatter.getClass().equals(formatterClass)) { + displayFormatter = (TajoCliOutputFormatter)formatterClass.newInstance(); + } + displayFormatter.init(context); + } + + public TajoCliContext getContext() { + return context; + } + + protected static String replaceParam(String script, String[] params) { + if (params == null || params.length == 0) { + return script; + } + + for (String eachParam: params) { + String[] tokens = eachParam.split("="); + if (tokens.length != 2) { + continue; + } + script = script.replace("${" + tokens[0] + "}", tokens[1]); + } + + return script; + } + + private void initHistory() { + try { + String historyPath = HOME_DIR + File.separator + HISTORY_FILE; + if ((new File(HOME_DIR)).exists()) { + history = new TajoFileHistory(new File(historyPath)); + reader.setHistory(history); + } else { + System.err.println(ERROR_PREFIX + "home directory : '" + HOME_DIR +"' does not exist."); + } + } catch (Exception e) { + System.err.println(ERROR_PREFIX + e.getMessage()); + } + } + + private void initCommands() { + for (Class clazz : registeredCommands) { + TajoShellCommand cmd = null; + try { + Constructor cons = clazz.getConstructor(new Class[] {TajoCliContext.class}); + cmd = (TajoShellCommand) cons.newInstance(context); + } catch (Exception e) { + System.err.println(e.getMessage()); + throw new RuntimeException(e.getMessage()); + } + commands.put(cmd.getCommand(), cmd); + for (String alias : cmd.getAliases()) { + commands.put(alias, cmd); + } + } + } + + private void addShutdownHook() { + Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { + @Override + public void run() { + try { + history.flush(); + } catch (IOException e) { + } + client.close(); + } + })); + } + + private String updatePrompt(ParsingState state) throws ServiceException { + if (state == ParsingState.WITHIN_QUOTE) { + return "'"; + } else if (state == ParsingState.TOK_START) { + return context.getCurrentDatabase(); + } else { + return ""; + } + } + + public int runShell() throws Exception { + String line; + String currentPrompt = context.getCurrentDatabase(); + int exitCode = 0; + + sout.write("Try \\? for help.\n"); + + SimpleParser parser = new SimpleParser(); + + try { + while((line = reader.readLine(currentPrompt + "> ")) != null) { + if (line.equals("")) { + continue; + } + wasError = false; + if (line.startsWith("{")) { + executeJsonQuery(line); + } else { + List<ParsedResult> parsedResults = parser.parseLines(line); + + if (parsedResults.size() > 0) { + for (ParsedResult parsed : parsedResults) { + history.addStatement(parsed.getHistoryStatement() + (parsed.getType() == STATEMENT ? ";" : "")); + } + } + + exitCode = executeParsedResults(parsedResults); + currentPrompt = updatePrompt(parser.getState()); + + if (exitCode != 0 && context.getBool(SessionVars.ON_ERROR_STOP)) { + return exitCode; + } + } + } + } catch (Exception e) { + System.err.println(ERROR_PREFIX + "Exception was thrown. Casued by " + e.getMessage()); + + if (client != null) { + client.close(); + } + + throw e; + } + return exitCode; + } + + private int executeParsedResults(Collection<ParsedResult> parsedResults) throws Exception { + int exitCode = 0; + for (ParsedResult parsedResult : parsedResults) { + if (parsedResult.getType() == META) { + exitCode = executeMetaCommand(parsedResult.getStatement()); + } else { + exitCode = executeQuery(parsedResult.getStatement()); + } + + if (exitCode != 0) { + return exitCode; + } + } + + return exitCode; + } + + public int executeMetaCommand(String line) throws Exception { + checkMasterStatus(); + String [] metaCommands = line.split(";"); + for (String metaCommand : metaCommands) { + String arguments [] = metaCommand.split(" "); + + TajoShellCommand invoked = commands.get(arguments[0]); + if (invoked == null) { + printInvalidCommand(arguments[0]); + wasError = true; + return -1; + } + + try { + invoked.invoke(arguments); + } catch (IllegalArgumentException ige) { + displayFormatter.printErrorMessage(sout, ige); + wasError = true; + return -1; + } catch (Exception e) { + displayFormatter.printErrorMessage(sout, e); + wasError = true; + return -1; + } finally { + context.getOutput().flush(); + } + + if (wasError && context.getBool(SessionVars.ON_ERROR_STOP)) { + break; + } + } + + return 0; + } + + private void executeJsonQuery(String json) throws ServiceException, IOException { + checkMasterStatus(); + long startTime = System.currentTimeMillis(); + ClientProtos.SubmitQueryResponse response = client.executeQueryWithJson(json); + if (response == null) { + displayFormatter.printErrorMessage(sout, "response is null"); + wasError = true; + } else if (response.getResultCode() == ClientProtos.ResultCode.OK) { + if (response.getIsForwarded()) { + QueryId queryId = new QueryId(response.getQueryId()); + waitForQueryCompleted(queryId); + } else { + if (!response.hasTableDesc() && !response.hasResultSet()) { + displayFormatter.printMessage(sout, "OK"); + wasError = true; + } else { + localQueryCompleted(response, startTime); + } + } + } else { + if (response.hasErrorMessage()) { + displayFormatter.printErrorMessage(sout, response.getErrorMessage()); + wasError = true; + } + } + } + + private int executeQuery(String statement) throws ServiceException, IOException { + checkMasterStatus(); + long startTime = System.currentTimeMillis(); + ClientProtos.SubmitQueryResponse response = null; + try{ + response = client.executeQuery(statement); + } catch (ServiceException e){ + displayFormatter.printErrorMessage(sout, e.getMessage()); + wasError = true; + } catch(Throwable te){ + displayFormatter.printErrorMessage(sout, te); + wasError = true; + } + + if (response == null) { + displayFormatter.printErrorMessage(sout, "response is null"); + wasError = true; + } else if (response.getResultCode() == ClientProtos.ResultCode.OK) { + if (response.getIsForwarded()) { + QueryId queryId = new QueryId(response.getQueryId()); + waitForQueryCompleted(queryId); + } else { + if (!response.hasTableDesc() && !response.hasResultSet()) { + displayFormatter.printMessage(sout, "OK"); + } else { + localQueryCompleted(response, startTime); + } + } + } else { + if (response.hasErrorMessage()) { + displayFormatter.printErrorMessage(sout, response.getErrorMessage()); + wasError = true; + } + } + + return wasError ? -1 : 0; + } + + private void localQueryCompleted(ClientProtos.SubmitQueryResponse response, long startTime) { + ResultSet res = null; + try { + QueryId queryId = new QueryId(response.getQueryId()); + float responseTime = ((float)(System.currentTimeMillis() - startTime) / 1000.0f); + TableDesc desc = new TableDesc(response.getTableDesc()); + + // non-forwarded INSERT INTO query does not have any query id. + // In this case, it just returns succeeded query information without printing the query results. + if (response.getMaxRowNum() < 0 && queryId.equals(QueryIdFactory.NULL_QUERY_ID)) { + displayFormatter.printResult(sout, sin, desc, responseTime, res); + } else { + res = TajoClientUtil.createResultSet(conf, client, response); + displayFormatter.printResult(sout, sin, desc, responseTime, res); + } + } catch (Throwable t) { + displayFormatter.printErrorMessage(sout, t); + wasError = true; + } finally { + if (res != null) { + try { + res.close(); + } catch (SQLException e) { + } + } + } + } + + private void waitForQueryCompleted(QueryId queryId) { + // if query is empty string + if (queryId.equals(QueryIdFactory.NULL_QUERY_ID)) { + return; + } + + // query execute + ResultSet res = null; + QueryStatus status = null; + try { + + int initRetries = 0; + int progressRetries = 0; + while (true) { + // TODO - configurable + status = client.getQueryStatus(queryId); + if(TajoClientUtil.isQueryWaitingForSchedule(status.getState())) { + Thread.sleep(Math.min(20 * initRetries, 1000)); + initRetries++; + continue; + } + + if (TajoClientUtil.isQueryRunning(status.getState()) || status.getState() == QueryState.QUERY_SUCCEEDED) { + displayFormatter.printProgress(sout, status); + } + + if (TajoClientUtil.isQueryComplete(status.getState()) && status.getState() != QueryState.QUERY_KILL_WAIT) { + break; + } else { + Thread.sleep(Math.min(200 * progressRetries, 1000)); + progressRetries += 2; + } + } + + if (status.getState() == QueryState.QUERY_ERROR || status.getState() == QueryState.QUERY_FAILED) { + displayFormatter.printErrorMessage(sout, status); + wasError = true; + } else if (status.getState() == QueryState.QUERY_KILLED) { + displayFormatter.printKilledMessage(sout, queryId); + wasError = true; + } else { + if (status.getState() == QueryState.QUERY_SUCCEEDED) { + float responseTime = ((float)(status.getFinishTime() - status.getSubmitTime()) / 1000.0f); + ClientProtos.GetQueryResultResponse response = client.getResultResponse(queryId); + if (status.hasResult()) { + res = TajoClientUtil.createResultSet(conf, client, queryId, response); + TableDesc desc = new TableDesc(response.getTableDesc()); + displayFormatter.printResult(sout, sin, desc, responseTime, res); + } else { + TableDesc desc = new TableDesc(response.getTableDesc()); + displayFormatter.printResult(sout, sin, desc, responseTime, res); + } + } + } + } catch (Throwable t) { + displayFormatter.printErrorMessage(sout, t); + wasError = true; + } finally { + if (res != null) { + try { + res.close(); + } catch (SQLException e) { + } + } else { + if (status != null && status.getQueryId() != null) { + client.closeQuery(status.getQueryId()); + } + } + } + } + + public int executeScript(String script) throws Exception { + wasError = false; + List<ParsedResult> results = SimpleParser.parseScript(script); + return executeParsedResults(results); + } + + private void printUsage() { + HelpFormatter formatter = new HelpFormatter(); + formatter.printHelp("tsql [options] [database]", options); + } + + private void printInvalidCommand(String command) { + sout.println("Invalid command " + command + ". Try \\? for help."); + } + + public void close() { + //for testcase + if (client != null) { + client.close(); + } + } + + private void checkMasterStatus() throws IOException, ServiceException { + String sessionId = client.getSessionId() != null ? client.getSessionId().getId() : null; + client = TajoHAClientUtil.getTajoClient(conf, client, context); + if(sessionId != null && (client.getSessionId() == null || + !sessionId.equals(client.getSessionId().getId()))) { + commands.clear(); + initHistory(); + initCommands(); + } + } + + public static void main(String [] args) throws Exception { + TajoConf conf = new TajoConf(); + TajoCli shell = new TajoCli(conf, args, System.in, System.out); + System.out.println(); + System.exit(shell.runShell()); + } +} http://git-wip-us.apache.org/repos/asf/tajo/blob/c59baa3a/tajo-client/src/main/java/org/apache/tajo/cli/tsql/TajoCliOutputFormatter.java ---------------------------------------------------------------------- diff --git a/tajo-client/src/main/java/org/apache/tajo/cli/tsql/TajoCliOutputFormatter.java b/tajo-client/src/main/java/org/apache/tajo/cli/tsql/TajoCliOutputFormatter.java new file mode 100644 index 0000000..b3632f0 --- /dev/null +++ b/tajo-client/src/main/java/org/apache/tajo/cli/tsql/TajoCliOutputFormatter.java @@ -0,0 +1,98 @@ +/** + * 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.tajo.cli.tsql; + +import org.apache.tajo.QueryId; +import org.apache.tajo.catalog.TableDesc; +import org.apache.tajo.cli.tsql.TajoCli; +import org.apache.tajo.client.QueryStatus; + +import java.io.InputStream; +import java.io.PrintWriter; +import java.sql.ResultSet; + +public interface TajoCliOutputFormatter { + /** + * Initialize formatter + * @param context + */ + public void init(TajoCli.TajoCliContext context); + + /** + * print query result to console + * @param sout + * @param sin + * @param tableDesc + * @param responseTime + * @param res + * @throws Exception + */ + public void printResult(PrintWriter sout, InputStream sin, TableDesc tableDesc, + float responseTime, ResultSet res) throws Exception; + + /** + * print no result message + * @param sout + */ + public void printNoResult(PrintWriter sout); + + /** + * print simple message + * @param sout + * @param message + */ + public void printMessage(PrintWriter sout, String message); + + /** + * print query progress message + * @param sout + * @param status + */ + public void printProgress(PrintWriter sout, QueryStatus status); + + /** + * print error message + * @param sout + * @param t + */ + public void printErrorMessage(PrintWriter sout, Throwable t); + + /** + * print error message + * @param sout + * @param message + */ + public void printErrorMessage(PrintWriter sout, String message); + + /** + * print error message + * @param sout + * @param queryId + */ + public void printKilledMessage(PrintWriter sout, QueryId queryId); + + /** + * print query status error message + * @param sout + * @param status + */ + void printErrorMessage(PrintWriter sout, QueryStatus status); + + void setScirptMode(); +} http://git-wip-us.apache.org/repos/asf/tajo/blob/c59baa3a/tajo-client/src/main/java/org/apache/tajo/cli/tsql/TajoFileHistory.java ---------------------------------------------------------------------- diff --git a/tajo-client/src/main/java/org/apache/tajo/cli/tsql/TajoFileHistory.java b/tajo-client/src/main/java/org/apache/tajo/cli/tsql/TajoFileHistory.java new file mode 100644 index 0000000..ec0275c --- /dev/null +++ b/tajo-client/src/main/java/org/apache/tajo/cli/tsql/TajoFileHistory.java @@ -0,0 +1,39 @@ +/** + * 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.tajo.cli.tsql; + +import jline.console.history.FileHistory; + +import java.io.File; +import java.io.IOException; + +public class TajoFileHistory extends FileHistory { + + public TajoFileHistory(File file) throws IOException { + super(file); + } + + public void add(CharSequence item) { + // skip add + } + + public void addStatement(String item) { + internalAdd(item); + } +} http://git-wip-us.apache.org/repos/asf/tajo/blob/c59baa3a/tajo-client/src/main/java/org/apache/tajo/cli/tsql/commands/ConnectDatabaseCommand.java ---------------------------------------------------------------------- diff --git a/tajo-client/src/main/java/org/apache/tajo/cli/tsql/commands/ConnectDatabaseCommand.java b/tajo-client/src/main/java/org/apache/tajo/cli/tsql/commands/ConnectDatabaseCommand.java new file mode 100644 index 0000000..ae644bd --- /dev/null +++ b/tajo-client/src/main/java/org/apache/tajo/cli/tsql/commands/ConnectDatabaseCommand.java @@ -0,0 +1,72 @@ +/** + * 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.tajo.cli.tsql.commands; + +import com.google.protobuf.ServiceException; +import org.apache.tajo.cli.tsql.TajoCli; + +public class ConnectDatabaseCommand extends TajoShellCommand { + + public ConnectDatabaseCommand(TajoCli.TajoCliContext context) { + super(context); + } + + @Override + public String getCommand() { + return "\\c"; + } + + @Override + public void invoke(String[] cmd) throws Exception { + if (cmd.length == 1) { + context.getOutput().write(String.format("You are now connected to database \"%s\" as user \"%s\".%n", + client.getCurrentDatabase(), client.getUserInfo().getUserName())); + } else if (cmd.length == 2) { + String databaseName = cmd[1]; + databaseName = databaseName.replace("\"", ""); + if (!client.existDatabase(databaseName)) { + context.getOutput().write("Database '" + databaseName + "' not found\n"); + } else { + try { + if (client.selectDatabase(databaseName)) { + context.setCurrentDatabase(client.getCurrentDatabase()); + context.getOutput().write(String.format("You are now connected to database \"%s\" as user \"%s\".%n", + context.getCurrentDatabase(), client.getUserInfo().getUserName())); + } + } catch (ServiceException se) { + if (se.getMessage() != null) { + context.getOutput().write(se.getMessage()); + } else { + context.getOutput().write(String.format("cannot connect the database \"%s\"", databaseName)); + } + } + } + } + } + + @Override + public String getUsage() { + return ""; + } + + @Override + public String getDescription() { + return "connect to new database"; + } +} http://git-wip-us.apache.org/repos/asf/tajo/blob/c59baa3a/tajo-client/src/main/java/org/apache/tajo/cli/tsql/commands/CopyrightCommand.java ---------------------------------------------------------------------- diff --git a/tajo-client/src/main/java/org/apache/tajo/cli/tsql/commands/CopyrightCommand.java b/tajo-client/src/main/java/org/apache/tajo/cli/tsql/commands/CopyrightCommand.java new file mode 100644 index 0000000..24276a2 --- /dev/null +++ b/tajo-client/src/main/java/org/apache/tajo/cli/tsql/commands/CopyrightCommand.java @@ -0,0 +1,65 @@ +/** + * 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.tajo.cli.tsql.commands; + +import org.apache.tajo.cli.tsql.TajoCli; + +public class CopyrightCommand extends TajoShellCommand { + public CopyrightCommand(TajoCli.TajoCliContext context) { + super(context); + } + + @Override + public String getCommand() { + return "\\copyright"; + } + + @Override + public void invoke(String[] cmd) throws Exception { + context.getOutput().println(); + context.getOutput().println( + " Licensed to the Apache Software Foundation (ASF) under one\n" + + " or more contributor license agreements. See the NOTICE file\n" + + " distributed with this work for additional information\n" + + " regarding copyright ownership. The ASF licenses this file\n" + + " to you under the Apache License, Version 2.0 (the\n" + + " \"License\"); you may not use this file except in compliance\n" + + " with the License. You may obtain a copy of the License at\n" + + "\n" + + " http://www.apache.org/licenses/LICENSE-2.0\n" + + "\n" + + " Unless required by applicable law or agreed to in writing, software\n" + + " distributed under the License is distributed on an \"AS IS\" BASIS,\n" + + " WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" + + " See the License for the specific language governing permissions and\n" + + " limitations under the License."); + context.getOutput().println(); + } + + @Override + public String getUsage() { + return ""; + } + + @Override + public String getDescription() { + return "show Apache License 2.0"; + } + +} http://git-wip-us.apache.org/repos/asf/tajo/blob/c59baa3a/tajo-client/src/main/java/org/apache/tajo/cli/tsql/commands/DescFunctionCommand.java ---------------------------------------------------------------------- diff --git a/tajo-client/src/main/java/org/apache/tajo/cli/tsql/commands/DescFunctionCommand.java b/tajo-client/src/main/java/org/apache/tajo/cli/tsql/commands/DescFunctionCommand.java new file mode 100644 index 0000000..0a7f79b --- /dev/null +++ b/tajo-client/src/main/java/org/apache/tajo/cli/tsql/commands/DescFunctionCommand.java @@ -0,0 +1,136 @@ +/** + * 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.tajo.cli.tsql.commands; + +import org.apache.tajo.catalog.proto.CatalogProtos; +import org.apache.tajo.cli.tsql.TajoCli; +import org.apache.tajo.function.FunctionUtil; + +import java.util.*; + +import static org.apache.tajo.common.TajoDataTypes.DataType; + +public class DescFunctionCommand extends TajoShellCommand { + public DescFunctionCommand(TajoCli.TajoCliContext context) { + super(context); + } + + @Override + public String getCommand() { + return "\\df"; + } + + @Override + public void invoke(String[] cmd) throws Exception { + boolean printDetail = false; + String functionName = ""; + if(cmd.length == 0) { + throw new IllegalArgumentException(); + } + + if (cmd.length == 2) { + printDetail = true; + functionName = cmd[1]; + } + + List<CatalogProtos.FunctionDescProto> functions = + new ArrayList<CatalogProtos.FunctionDescProto>(client.getFunctions(functionName)); + + Collections.sort(functions, new Comparator<CatalogProtos.FunctionDescProto>() { + @Override + public int compare(CatalogProtos.FunctionDescProto f1, CatalogProtos.FunctionDescProto f2) { + int nameCompared = f1.getSignature().getName().compareTo(f2.getSignature().getName()); + if (nameCompared != 0) { + return nameCompared; + } else { + return f1.getSignature().getReturnType().getType().compareTo(f2.getSignature().getReturnType().getType()); + } + } + }); + + String[] headers = new String[]{"Name", "Result type", "Argument types", "Description", "Type"}; + float[] columnWidthRates = new float[]{0.15f, 0.15f, 0.2f, 0.4f, 0.1f}; + int[] columnWidths = printHeader(headers, columnWidthRates); + + for(CatalogProtos.FunctionDescProto eachFunction: functions) { + String name = eachFunction.getSignature().getName(); + String resultDataType = eachFunction.getSignature().getReturnType().getType().toString(); + String arguments = FunctionUtil.buildParamTypeString( + eachFunction.getSignature().getParameterTypesList().toArray( + new DataType[eachFunction.getSignature().getParameterTypesCount()])); + String functionType = eachFunction.getSignature().getType().toString(); + String description = eachFunction.getSupplement().getShortDescription(); + + int index = 0; + printLeft(" " + name, columnWidths[index++]); + context.getOutput().print("|"); + printLeft(" " + resultDataType, columnWidths[index++]); + context.getOutput().print("|"); + printLeft(" " + arguments, columnWidths[index++]); + context.getOutput().print("|"); + printLeft(" " + description, columnWidths[index++]); + context.getOutput().print("|"); + printLeft(" " + functionType, columnWidths[index++]); + + println(); + } + + println(); + context.getOutput().println("(" + functions.size() + ") rows"); + println(); + + if (printDetail && !functions.isEmpty()) { + Map<String, CatalogProtos.FunctionDescProto> functionMap = + new HashMap<String, CatalogProtos.FunctionDescProto>(); + + for (CatalogProtos.FunctionDescProto eachFunction: functions) { + if (!functionMap.containsKey(eachFunction.getSupplement().getShortDescription())) { + functionMap.put(eachFunction.getSupplement().getShortDescription(), eachFunction); + } + } + + for (CatalogProtos.FunctionDescProto eachFunction: functionMap.values()) { + String signature = eachFunction.getSignature().getReturnType().getType() + " " + + FunctionUtil.buildSimpleFunctionSignature(eachFunction.getSignature().getName(), + eachFunction.getSignature().getParameterTypesList()); + String fullDescription = eachFunction.getSupplement().getShortDescription(); + if(eachFunction.getSupplement().getDetail() != null && !eachFunction.getSupplement().getDetail().isEmpty()) { + fullDescription += "\n" + eachFunction.getSupplement().getDetail(); + } + + context.getOutput().println("Function: " + signature); + context.getOutput().println("Description: " + fullDescription); + context.getOutput().println("Example:\n" + eachFunction.getSupplement().getExample()); + println(); + } + } + } + + @Override + public String getUsage() { + return "[function_name]"; + } + + @Override + public String getDescription() { + return "show function description"; + } + + +} http://git-wip-us.apache.org/repos/asf/tajo/blob/c59baa3a/tajo-client/src/main/java/org/apache/tajo/cli/tsql/commands/DescTableCommand.java ---------------------------------------------------------------------- diff --git a/tajo-client/src/main/java/org/apache/tajo/cli/tsql/commands/DescTableCommand.java b/tajo-client/src/main/java/org/apache/tajo/cli/tsql/commands/DescTableCommand.java new file mode 100644 index 0000000..2d4408b --- /dev/null +++ b/tajo-client/src/main/java/org/apache/tajo/cli/tsql/commands/DescTableCommand.java @@ -0,0 +1,136 @@ +/** + * 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.tajo.cli.tsql.commands; + +import org.apache.commons.lang.CharUtils; +import org.apache.commons.lang.StringEscapeUtils; +import org.apache.tajo.catalog.Column; +import org.apache.tajo.catalog.TableDesc; +import org.apache.tajo.catalog.partition.PartitionMethodDesc; +import org.apache.tajo.cli.tsql.TajoCli; +import org.apache.tajo.client.QueryClient; +import org.apache.tajo.util.FileUtil; +import org.apache.tajo.util.TUtil; + +import java.util.List; +import java.util.Map; + +public class DescTableCommand extends TajoShellCommand { + public DescTableCommand(TajoCli.TajoCliContext context) { + super(context); + } + + @Override + public String getCommand() { + return "\\d"; + } + + @Override + public void invoke(String[] cmd) throws Exception { + if (cmd.length == 2) { + String tableName = cmd[1]; + tableName = tableName.replace("\"", ""); + TableDesc desc = client.getTableDesc(tableName); + if (desc == null) { + context.getOutput().println("Did not find any relation named \"" + tableName + "\""); + } else { + context.getOutput().println(toFormattedString(desc)); + } + } else if (cmd.length == 1) { + List<String> tableList = client.getTableList(null); + if (tableList.size() == 0) { + context.getOutput().println("No Relation Found"); + } + for (String table : tableList) { + context.getOutput().println(table); + } + } else { + throw new IllegalArgumentException(); + } + } + + @Override + public String getUsage() { + return "[table_name]"; + } + + @Override + public String getDescription() { + return "show table description"; + } + + protected String toFormattedString(TableDesc desc) { + StringBuilder sb = new StringBuilder(); + sb.append("\ntable name: ").append(desc.getName()).append("\n"); + sb.append("table path: ").append(desc.getPath()).append("\n"); + sb.append("store type: ").append(desc.getMeta().getStoreType()).append("\n"); + if (desc.getStats() != null) { + + long row = desc.getStats().getNumRows(); + String rowText = row == QueryClient.UNKNOWN_ROW_NUMBER ? "unknown" : row + ""; + sb.append("number of rows: ").append(rowText).append("\n"); + sb.append("volume: ").append( + FileUtil.humanReadableByteCount(desc.getStats().getNumBytes(), + true)).append("\n"); + } + sb.append("Options: \n"); + for(Map.Entry<String, String> entry : desc.getMeta().toMap().entrySet()){ + + /* + * Checks whether the character is ASCII 7 bit printable. + * For example, a printable unicode '\u007c' become the character â|â. + * + * Control-chars : ctrl-a(\u0001), tab(\u0009) .. + * Printable-chars : '|'(\u007c), ','(\u002c) .. + * */ + + String value = entry.getValue(); + String unescaped = StringEscapeUtils.unescapeJava(value); + if (unescaped.length() == 1 && CharUtils.isAsciiPrintable(unescaped.charAt(0))) { + value = unescaped; + } + sb.append("\t").append("'").append(entry.getKey()).append("'").append("=") + .append("'").append(value).append("'").append("\n"); + } + sb.append("\n"); + sb.append("schema: \n"); + + for(int i = 0; i < desc.getSchema().size(); i++) { + Column col = desc.getSchema().getColumn(i); + sb.append(col.getSimpleName()).append("\t").append(col.getDataType().getType()); + if (col.getDataType().hasLength()) { + sb.append("(").append(col.getDataType().getLength()).append(")"); + } + sb.append("\n"); + } + + sb.append("\n"); + if (desc.getPartitionMethod() != null) { + PartitionMethodDesc partition = desc.getPartitionMethod(); + sb.append("Partitions: \n"); + + sb.append("type:").append(partition.getPartitionType().name()).append("\n"); + + sb.append("columns:").append(":"); + sb.append(TUtil.arrayToString(partition.getExpressionSchema().toArray())); + } + + return sb.toString(); + } +} http://git-wip-us.apache.org/repos/asf/tajo/blob/c59baa3a/tajo-client/src/main/java/org/apache/tajo/cli/tsql/commands/ExecExternalShellCommand.java ---------------------------------------------------------------------- diff --git a/tajo-client/src/main/java/org/apache/tajo/cli/tsql/commands/ExecExternalShellCommand.java b/tajo-client/src/main/java/org/apache/tajo/cli/tsql/commands/ExecExternalShellCommand.java new file mode 100644 index 0000000..ac97959 --- /dev/null +++ b/tajo-client/src/main/java/org/apache/tajo/cli/tsql/commands/ExecExternalShellCommand.java @@ -0,0 +1,124 @@ +/** + * 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.tajo.cli.tsql.commands; + +import org.apache.tajo.cli.tsql.TajoCli; + +import java.io.*; +import java.util.concurrent.CountDownLatch; + +public class ExecExternalShellCommand extends TajoShellCommand { + public ExecExternalShellCommand(TajoCli.TajoCliContext context) { + super(context); + } + + @Override + public String getCommand() { + return "\\!"; + } + + @Override + public void invoke(String[] command) throws Exception { + StringBuilder shellCommand = new StringBuilder(); + String prefix = ""; + for(int i = 1; i < command.length; i++) { + shellCommand.append(prefix).append(command[i]); + prefix = " "; + } + + String builtCommand = shellCommand.toString(); + if (command.length < 2) { + throw new IOException("ERROR: '" + builtCommand + "' is an invalid command."); + } + + String[] execCommand = new String[3]; + execCommand[0] = "/bin/bash"; + execCommand[1] = "-c"; + execCommand[2] = builtCommand; + + PrintWriter sout = context.getOutput(); + + CountDownLatch latch = new CountDownLatch(2); + Process process = Runtime.getRuntime().exec(execCommand); + try { + InputStreamConsoleWriter inWriter = new InputStreamConsoleWriter(process.getInputStream(), sout, "", latch); + InputStreamConsoleWriter errWriter = new InputStreamConsoleWriter(process.getErrorStream(), sout, "ERROR: ", latch); + + inWriter.start(); + errWriter.start(); + + int processResult = process.waitFor(); + latch.await(); + if (processResult != 0) { + throw new IOException("ERROR: Failed with exit code = " + processResult); + } + } finally { + org.apache.commons.io.IOUtils.closeQuietly(process.getInputStream()); + org.apache.commons.io.IOUtils.closeQuietly(process.getOutputStream()); + org.apache.commons.io.IOUtils.closeQuietly(process.getErrorStream()); + } + } + + @Override + public String getUsage() { + return "<command> [params]"; + } + + @Override + public String getDescription() { + return "executes external shell command in TAJO shell"; + } + + static class InputStreamConsoleWriter extends Thread { + private InputStream in; + private PrintWriter writer; + private String prefix; + private CountDownLatch latch; + + public InputStreamConsoleWriter(InputStream in, PrintWriter writer, String prefix, CountDownLatch latch) { + this.in = in; + this.writer = writer; + this.prefix = prefix; + this.latch = latch; + } + + @Override + public void run() { + BufferedReader reader = null; + try { + reader = new BufferedReader(new InputStreamReader(in)); + String line; + while ((line = reader.readLine()) != null) { + writer.println(prefix + line); + writer.flush(); + } + } catch (Exception e) { + writer.println("ERROR: " + e.getMessage()); + } finally { + if (reader != null) { + try { + reader.close(); + } catch (IOException e) { + } + } + latch.countDown(); + } + } + } +} http://git-wip-us.apache.org/repos/asf/tajo/blob/c59baa3a/tajo-client/src/main/java/org/apache/tajo/cli/tsql/commands/ExitCommand.java ---------------------------------------------------------------------- diff --git a/tajo-client/src/main/java/org/apache/tajo/cli/tsql/commands/ExitCommand.java b/tajo-client/src/main/java/org/apache/tajo/cli/tsql/commands/ExitCommand.java new file mode 100644 index 0000000..499e92a --- /dev/null +++ b/tajo-client/src/main/java/org/apache/tajo/cli/tsql/commands/ExitCommand.java @@ -0,0 +1,49 @@ +/** + * 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.tajo.cli.tsql.commands; + +import org.apache.tajo.cli.tsql.TajoCli; + +public class ExitCommand extends TajoShellCommand { + + public ExitCommand(TajoCli.TajoCliContext context) { + super(context); + } + + @Override + public String getCommand() { + return "\\q"; + } + + @Override + public void invoke(String[] cmd) throws Exception { + context.getOutput().println("bye!"); + System.exit(0); + } + + @Override + public String getUsage() { + return ""; + } + + @Override + public String getDescription() { + return "quit"; + } +} http://git-wip-us.apache.org/repos/asf/tajo/blob/c59baa3a/tajo-client/src/main/java/org/apache/tajo/cli/tsql/commands/HdfsCommand.java ---------------------------------------------------------------------- diff --git a/tajo-client/src/main/java/org/apache/tajo/cli/tsql/commands/HdfsCommand.java b/tajo-client/src/main/java/org/apache/tajo/cli/tsql/commands/HdfsCommand.java new file mode 100644 index 0000000..8f57b74 --- /dev/null +++ b/tajo-client/src/main/java/org/apache/tajo/cli/tsql/commands/HdfsCommand.java @@ -0,0 +1,58 @@ +/** + * 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.tajo.cli.tsql.commands; + +import org.apache.hadoop.fs.FsShell; +import org.apache.tajo.cli.tsql.TajoCli; + +public class HdfsCommand extends TajoShellCommand { + private FsShell fsShell; + + public HdfsCommand(TajoCli.TajoCliContext context) { + super(context); + fsShell = new FsShell(context.getConf()); + } + + @Override + public String getCommand() { + return "\\dfs"; + } + + @Override + public void invoke(String[] command) throws Exception { + try { + String[] dfsCommands = new String[command.length - 1]; + System.arraycopy(command, 1, dfsCommands, 0, dfsCommands.length); + + fsShell.run(dfsCommands); + } catch (Exception e) { + context.getOutput().println("ERROR: " + e.getMessage()); + } + } + + @Override + public String getUsage() { + return "<hdfs command> [options]"; + } + + @Override + public String getDescription() { + return "executes a dfs command in TAJO shell "; + } +} http://git-wip-us.apache.org/repos/asf/tajo/blob/c59baa3a/tajo-client/src/main/java/org/apache/tajo/cli/tsql/commands/HelpCommand.java ---------------------------------------------------------------------- diff --git a/tajo-client/src/main/java/org/apache/tajo/cli/tsql/commands/HelpCommand.java b/tajo-client/src/main/java/org/apache/tajo/cli/tsql/commands/HelpCommand.java new file mode 100644 index 0000000..76f8561 --- /dev/null +++ b/tajo-client/src/main/java/org/apache/tajo/cli/tsql/commands/HelpCommand.java @@ -0,0 +1,133 @@ +/** + * 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.tajo.cli.tsql.commands; + +import java.io.PrintWriter; + +import org.apache.tajo.cli.tsql.TajoCli; +import org.apache.tajo.util.VersionInfo; + +public class HelpCommand extends TajoShellCommand { + private String targetDocVersion = ""; + + public HelpCommand(TajoCli.TajoCliContext context) { + super(context); + } + + @Override + public String getCommand() { + return "\\?"; + } + + @Override + public String [] getAliases() { + return new String [] {"\\help"}; + } + + @Override + public void invoke(String[] cmd) throws Exception { + if(targetDocVersion.equalsIgnoreCase("")) { + targetDocVersion = getDocumentationVersion(); + } + + if (cmd.length == 1) { + PrintWriter sout = context.getOutput(); + sout.println(); + + sout.println("General"); + sout.println(" \\copyright show Apache License 2.0"); + sout.println(" \\version show Tajo version"); + sout.println(" \\? show help"); + sout.println(" \\? [COMMAND] show help of a given command"); + sout.println(" \\help alias of \\?"); + sout.println(" \\q quit tsql"); + sout.println(); + sout.println(); + + sout.println("Informational"); + sout.println(" \\l list databases"); + sout.println(" \\c show current database"); + sout.println(" \\c [DBNAME] connect to new database"); + sout.println(" \\d list tables"); + sout.println(" \\d [TBNAME] describe table"); + sout.println(" \\df list functions"); + sout.println(" \\df NAME describe function"); + sout.println(); + sout.println(); + + sout.println("Tool"); + sout.println(" \\! execute a linux shell command"); + sout.println(" \\dfs execute a dfs command"); + sout.println(" \\admin execute tajo admin command"); + sout.println(); + sout.println(); + + sout.println("Variables"); + sout.println(" \\set [[NAME] [VALUE] set session variable or list session variables"); + sout.println(" \\unset NAME unset session variable"); + sout.println(); + sout.println(); + + sout.println("Documentations"); + sout.println(" tsql guide http://tajo.apache.org/docs/" + targetDocVersion + "/cli.html"); + sout.println(" Query language http://tajo.apache.org/docs/" + targetDocVersion + "/sql_language.html"); + sout.println(" Functions http://tajo.apache.org/docs/" + targetDocVersion + "/functions.html"); + sout.println(" Backup & restore http://tajo.apache.org/docs/" + targetDocVersion + "/backup_and_restore.html"); + sout.println(" Configuration http://tajo.apache.org/docs/" + targetDocVersion + "/configuration.html"); + sout.println(); + } else if (cmd.length == 2) { + String slashCommand = "\\" + cmd[1]; + if (context.getCommands().containsKey(slashCommand)) { + context.getCommands().get(slashCommand).printHelp(); + } else { + context.getOutput().println("Command not found: " + cmd[1]); + } + } + } + + private String getDocumentationVersion() { + String tajoVersion = "", docVersion = "", docDefaultVersion = "current"; + String tajoFullVersion = VersionInfo.getVersion(); + + int delimiterIdx = tajoFullVersion.indexOf("-"); + if (delimiterIdx > -1) { + tajoVersion = tajoFullVersion.substring(0, delimiterIdx); + } else { + tajoVersion = tajoFullVersion; + } + + if(tajoVersion.equalsIgnoreCase("")) { + docVersion = docDefaultVersion; + } else { + docVersion = tajoVersion; + } + + return docVersion; + } + + @Override + public String getUsage() { + return ""; + } + + @Override + public String getDescription() { + return "show command lists and their usages"; + } +} http://git-wip-us.apache.org/repos/asf/tajo/blob/c59baa3a/tajo-client/src/main/java/org/apache/tajo/cli/tsql/commands/ListDatabaseCommand.java ---------------------------------------------------------------------- diff --git a/tajo-client/src/main/java/org/apache/tajo/cli/tsql/commands/ListDatabaseCommand.java b/tajo-client/src/main/java/org/apache/tajo/cli/tsql/commands/ListDatabaseCommand.java new file mode 100644 index 0000000..534bece --- /dev/null +++ b/tajo-client/src/main/java/org/apache/tajo/cli/tsql/commands/ListDatabaseCommand.java @@ -0,0 +1,50 @@ +/** + * 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.tajo.cli.tsql.commands; + +import org.apache.tajo.cli.tsql.TajoCli; + +public class ListDatabaseCommand extends TajoShellCommand { + + public ListDatabaseCommand(TajoCli.TajoCliContext context) { + super(context); + } + + @Override + public String getCommand() { + return "\\l"; + } + + @Override + public void invoke(String[] cmd) throws Exception { + for (String databaseName : client.getAllDatabaseNames()) { + context.getOutput().println(databaseName); + } + } + + @Override + public String getUsage() { + return ""; + } + + @Override + public String getDescription() { + return "list all databases"; + } +} http://git-wip-us.apache.org/repos/asf/tajo/blob/c59baa3a/tajo-client/src/main/java/org/apache/tajo/cli/tsql/commands/SetCommand.java ---------------------------------------------------------------------- diff --git a/tajo-client/src/main/java/org/apache/tajo/cli/tsql/commands/SetCommand.java b/tajo-client/src/main/java/org/apache/tajo/cli/tsql/commands/SetCommand.java new file mode 100644 index 0000000..21c4be5 --- /dev/null +++ b/tajo-client/src/main/java/org/apache/tajo/cli/tsql/commands/SetCommand.java @@ -0,0 +1,127 @@ +/** + * 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.tajo.cli.tsql.commands; + +import com.google.protobuf.ServiceException; +import org.apache.tajo.SessionVars; +import org.apache.tajo.cli.tsql.TajoCli; +import org.apache.tajo.util.StringUtils; + +import java.util.HashMap; +import java.util.Map; + +import static org.apache.tajo.SessionVars.VariableMode; + +public class SetCommand extends TajoShellCommand { + + public SetCommand(TajoCli.TajoCliContext context) { + super(context); + } + + @Override + public String getCommand() { + return "\\set"; + } + + private void showAllSessionVars() throws ServiceException { + for (Map.Entry<String, String> entry: client.getAllSessionVariables().entrySet()) { + context.getOutput().println(StringUtils.quote(entry.getKey()) + "=" + StringUtils.quote(entry.getValue())); + } + } + + private void updateSessionVariable(String key, String val) throws ServiceException { + Map<String, String> variables = new HashMap<String, String>(); + variables.put(key, val); + client.updateSessionVariables(variables); + } + + public void set(String key, String val) throws ServiceException { + SessionVars sessionVar = null; + + if (SessionVars.exists(key)) { // if the variable is one of the session variables + sessionVar = SessionVars.get(key); + + // is it cli-side variable? + if (sessionVar.getMode() == VariableMode.CLI_SIDE_VAR) { + context.setCliSideVar(key, val); + } else { + updateSessionVariable(key, val); + } + + if (SessionVars.isDeprecated(key)) { + context.getOutput().println("Warning: deprecated to directly use config key in TajoConf.ConfVars. " + + "Please execute '\\help set'."); + } + } else { + updateSessionVariable(key, val); + } + } + + @Override + public void invoke(String[] cmd) throws Exception { + if (cmd.length == 1) { + showAllSessionVars(); + } else if (cmd.length == 3) { + set(cmd[1], cmd[2]); + } else { + context.getOutput().println("usage: \\set [[NAME] VALUE]"); + } + } + + @Override + public String getUsage() { + return ""; + } + + @Override + public String getDescription() { + return "set session variable or shows all session variables"; + } + + @Override + public void printHelp() { + context.getOutput().println("\nAvailable Session Variables:\n"); + for (SessionVars var : SessionVars.values()) { + + if (var.getMode() == VariableMode.DEFAULT || + var.getMode() == VariableMode.CLI_SIDE_VAR || + var.getMode() == VariableMode.FROM_SHELL_ENV) { + + context.getOutput().println("\\set " + var.keyname() + " " + getDisplayType(var.getVarType()) + " - " + var + .getDescription()); + } + } + } + + public static String getDisplayType(Class<?> clazz) { + if (clazz == String.class) { + return "[text value]"; + } else if (clazz == Integer.class) { + return "[int value]"; + } else if (clazz == Long.class) { + return "[long value]"; + } else if (clazz == Float.class) { + return "[real value]"; + } else if (clazz == Boolean.class) { + return "[true or false]"; + } else { + return clazz.getSimpleName(); + } + } +} http://git-wip-us.apache.org/repos/asf/tajo/blob/c59baa3a/tajo-client/src/main/java/org/apache/tajo/cli/tsql/commands/TajoAdminCommand.java ---------------------------------------------------------------------- diff --git a/tajo-client/src/main/java/org/apache/tajo/cli/tsql/commands/TajoAdminCommand.java b/tajo-client/src/main/java/org/apache/tajo/cli/tsql/commands/TajoAdminCommand.java new file mode 100644 index 0000000..53f66b0 --- /dev/null +++ b/tajo-client/src/main/java/org/apache/tajo/cli/tsql/commands/TajoAdminCommand.java @@ -0,0 +1,58 @@ +/** + * 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.tajo.cli.tsql.commands; + +import org.apache.tajo.cli.tools.TajoAdmin; +import org.apache.tajo.cli.tsql.TajoCli; + +public class TajoAdminCommand extends TajoShellCommand { + private TajoAdmin admin; + + public TajoAdminCommand(TajoCli.TajoCliContext context) { + super(context); + admin = new TajoAdmin(context.getConf(), context.getOutput(), context.getTajoClient()); + } + + @Override + public String getCommand() { + return "\\admin"; + } + + @Override + public void invoke(String[] command) throws Exception { + try { + String[] dfsCommands = new String[command.length - 1]; + System.arraycopy(command, 1, dfsCommands, 0, dfsCommands.length); + + admin.runCommand(dfsCommands); + } catch (Exception e) { + context.getOutput().println("ERROR: " + e.getMessage()); + } + } + + @Override + public String getUsage() { + return "<command> [options]"; + } + + @Override + public String getDescription() { + return "execute a tajo amdin command."; + } +} http://git-wip-us.apache.org/repos/asf/tajo/blob/c59baa3a/tajo-client/src/main/java/org/apache/tajo/cli/tsql/commands/TajoGetConfCommand.java ---------------------------------------------------------------------- diff --git a/tajo-client/src/main/java/org/apache/tajo/cli/tsql/commands/TajoGetConfCommand.java b/tajo-client/src/main/java/org/apache/tajo/cli/tsql/commands/TajoGetConfCommand.java new file mode 100644 index 0000000..5c7dd0e --- /dev/null +++ b/tajo-client/src/main/java/org/apache/tajo/cli/tsql/commands/TajoGetConfCommand.java @@ -0,0 +1,58 @@ +/** + * 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.tajo.cli.tsql.commands; + +import org.apache.tajo.cli.tsql.TajoCli; +import org.apache.tajo.cli.tools.TajoGetConf; + +public class TajoGetConfCommand extends TajoShellCommand { + private TajoGetConf getconf; + + public TajoGetConfCommand(TajoCli.TajoCliContext context) { + super(context); + getconf = new TajoGetConf(context.getConf(), context.getOutput(), context.getTajoClient()); + } + + @Override + public String getCommand() { + return "\\getconf"; + } + + @Override + public void invoke(String[] command) throws Exception { + try { + String[] getConfCommands = new String[command.length - 1]; + System.arraycopy(command, 1, getConfCommands, 0, getConfCommands.length); + + getconf.runCommand(getConfCommands); + } catch (Exception e) { + context.getOutput().println("ERROR: " + e.getMessage()); + } + } + + @Override + public String getUsage() { + return "<command> [options]"; + } + + @Override + public String getDescription() { + return "execute a tajo getconf command."; + } +}
