Repository: tajo Updated Branches: refs/heads/master d25bec87a -> 17e61bd69
TAJO-2064: Supporting auto-completion in Tsql. Closes #955 Signed-off-by: JaeHwa Jung <[email protected]> Project: http://git-wip-us.apache.org/repos/asf/tajo/repo Commit: http://git-wip-us.apache.org/repos/asf/tajo/commit/17e61bd6 Tree: http://git-wip-us.apache.org/repos/asf/tajo/tree/17e61bd6 Diff: http://git-wip-us.apache.org/repos/asf/tajo/diff/17e61bd6 Branch: refs/heads/master Commit: 17e61bd6917be93a5486c861f61e8ef2ab32ab29 Parents: d25bec8 Author: Jongyoung Park <[email protected]> Authored: Fri Mar 18 11:21:26 2016 +0900 Committer: JaeHwa Jung <[email protected]> Committed: Fri Mar 18 11:21:26 2016 +0900 ---------------------------------------------------------------------- CHANGES | 2 + tajo-cli/pom.xml | 4 ++ .../java/org/apache/tajo/cli/tsql/TajoCli.java | 58 +++++++++++++++++- .../tsql/commands/ConnectDatabaseCommand.java | 11 ++++ .../cli/tsql/commands/DescFunctionCommand.java | 11 +++- .../cli/tsql/commands/DescTableCommand.java | 11 ++++ .../tajo/cli/tsql/commands/HelpCommand.java | 18 ++++++ .../tajo/cli/tsql/commands/SetCommand.java | 11 ++++ .../cli/tsql/commands/TajoGetConfCommand.java | 55 +++++++++++++++++ .../cli/tsql/commands/TajoShellCommand.java | 62 ++++++++++++++++++++ .../tajo/cli/tsql/commands/UnsetCommand.java | 11 ++++ tajo-project/pom.xml | 2 +- 12 files changed, 253 insertions(+), 3 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/tajo/blob/17e61bd6/CHANGES ---------------------------------------------------------------------- diff --git a/CHANGES b/CHANGES index 7fa4087..f18cede 100644 --- a/CHANGES +++ b/CHANGES @@ -8,6 +8,8 @@ Release 0.12.0 - unreleased IMPROVEMENT + TAJO-2064: Supporting auto-completion in Tsql. (Jongyoung Park via jaehwa) + TAJO-2043: Implement new data type and schema. (hyunsik) TAJO-2090: Bump up orc version to 0.141. (hyunsik) http://git-wip-us.apache.org/repos/asf/tajo/blob/17e61bd6/tajo-cli/pom.xml ---------------------------------------------------------------------- diff --git a/tajo-cli/pom.xml b/tajo-cli/pom.xml index e670529..187b25d 100644 --- a/tajo-cli/pom.xml +++ b/tajo-cli/pom.xml @@ -138,6 +138,10 @@ <artifactId>tajo-rpc-protobuf</artifactId> </dependency> <dependency> + <groupId>org.apache.tajo</groupId> + <artifactId>tajo-sql-parser</artifactId> + </dependency> + <dependency> <groupId>commons-cli</groupId> <artifactId>commons-cli</artifactId> <version>1.2</version> http://git-wip-us.apache.org/repos/asf/tajo/blob/17e61bd6/tajo-cli/src/main/java/org/apache/tajo/cli/tsql/TajoCli.java ---------------------------------------------------------------------- diff --git a/tajo-cli/src/main/java/org/apache/tajo/cli/tsql/TajoCli.java b/tajo-cli/src/main/java/org/apache/tajo/cli/tsql/TajoCli.java index 1fa7862..70defb1 100644 --- a/tajo-cli/src/main/java/org/apache/tajo/cli/tsql/TajoCli.java +++ b/tajo-cli/src/main/java/org/apache/tajo/cli/tsql/TajoCli.java @@ -18,12 +18,16 @@ package org.apache.tajo.cli.tsql; -import com.google.common.collect.Maps; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; +import com.google.common.collect.Maps; import com.google.protobuf.ServiceException; import jline.UnsupportedTerminal; import jline.console.ConsoleReader; +import jline.console.completer.AggregateCompleter; +import jline.console.completer.ArgumentCompleter; +import jline.console.completer.Completer; +import jline.console.completer.StringsCompleter; import org.apache.commons.cli.*; import org.apache.tajo.*; import org.apache.tajo.TajoProtos.QueryState; @@ -39,6 +43,7 @@ import org.apache.tajo.exception.ExceptionUtil; import org.apache.tajo.exception.ReturnStateUtil; import org.apache.tajo.exception.TajoException; import org.apache.tajo.ipc.ClientProtos; +import org.apache.tajo.parser.sql.SQLLexer; import org.apache.tajo.service.ServiceTrackerFactory; import org.apache.tajo.util.FileUtil; import org.apache.tajo.util.KeyValueSet; @@ -50,6 +55,8 @@ import java.lang.reflect.Constructor; import java.sql.ResultSet; import java.sql.SQLException; import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; public class TajoCli implements Closeable { public static final int SHUTDOWN_HOOK_PRIORITY = 50; @@ -92,6 +99,10 @@ public class TajoCli implements Closeable { TajoGetConfCommand.class, TajoHAAdminCommand.class }; + + private AggregateCompleter cliCompleter; + private ArgumentCompleter sqlCompleter; + private final Map<String, TajoShellCommand> commands = new TreeMap<>(); protected static final Options options; @@ -256,6 +267,9 @@ public class TajoCli implements Closeable { initHistory(); initCommands(); + reader.addCompleter(cliCompleter); + reader.addCompleter(sqlCompleter); + if (cmd.getOptionValues("conf") != null) { processSessionVarCommand(cmd.getOptionValues("conf")); } @@ -365,8 +379,10 @@ public class TajoCli implements Closeable { } private void initCommands() { + List<Completer> compList = new ArrayList<>(); for (Class clazz : registeredCommands) { TajoShellCommand cmd = null; + try { Constructor cons = clazz.getConstructor(new Class[] {TajoCliContext.class}); cmd = (TajoShellCommand) cons.newInstance(context); @@ -374,11 +390,51 @@ public class TajoCli implements Closeable { System.err.println(e.getMessage()); throw new RuntimeException(e.getMessage()); } + + // make completers for console auto-completion + compList.add(cmd.getArgumentCompleter()); + commands.put(cmd.getCommand(), cmd); for (String alias : cmd.getAliases()) { commands.put(alias, cmd); } } + + cliCompleter = new AggregateCompleter(compList); + + sqlCompleter = new ArgumentCompleter( + new ArgumentCompleter.AbstractArgumentDelimiter() { + @Override + public boolean isDelimiterChar(CharSequence buf, int pos) { + char c = buf.charAt(pos); + return Character.isWhitespace(c) || !(Character.isLetterOrDigit(c)) && c != '_'; + } + }, + new StringsCompleter(getKeywords()) + ); + } + + private Collection<String> getKeywords() { + // SQL reserved keywords + Stream<String> tokens = Arrays.stream(SQLLexer.tokenNames); + Stream<String> rules = Arrays.stream(SQLLexer.ruleNames); + + List<String> keywords = Stream.concat(tokens, rules) + .filter((str) -> str.matches("[A-Z_]+") && str.length() > 1) + .distinct() + .map(String::toLowerCase) + .collect(Collectors.toList()); + + // DB and table names + for (String db: client.getAllDatabaseNames()) { + keywords.add(db); + keywords.addAll(client.getTableList(db)); + } + + tokens.close(); + rules.close(); + + return keywords; } private void addShutdownHook() { http://git-wip-us.apache.org/repos/asf/tajo/blob/17e61bd6/tajo-cli/src/main/java/org/apache/tajo/cli/tsql/commands/ConnectDatabaseCommand.java ---------------------------------------------------------------------- diff --git a/tajo-cli/src/main/java/org/apache/tajo/cli/tsql/commands/ConnectDatabaseCommand.java b/tajo-cli/src/main/java/org/apache/tajo/cli/tsql/commands/ConnectDatabaseCommand.java index d524d80..93cb62b 100644 --- a/tajo-cli/src/main/java/org/apache/tajo/cli/tsql/commands/ConnectDatabaseCommand.java +++ b/tajo-cli/src/main/java/org/apache/tajo/cli/tsql/commands/ConnectDatabaseCommand.java @@ -19,6 +19,9 @@ package org.apache.tajo.cli.tsql.commands; import com.google.common.base.Preconditions; +import jline.console.completer.ArgumentCompleter; +import jline.console.completer.NullCompleter; +import jline.console.completer.StringsCompleter; import org.apache.tajo.cli.tsql.TajoCli; import org.apache.tajo.exception.TajoException; @@ -79,4 +82,12 @@ public class ConnectDatabaseCommand extends TajoShellCommand { public String getDescription() { return "connect to new database"; } + + @Override + public ArgumentCompleter getArgumentCompleter() { + return new ArgumentCompleter( + new StringsCompleter(getCommand()), + new DbNameCompleter(), + NullCompleter.INSTANCE); + } } http://git-wip-us.apache.org/repos/asf/tajo/blob/17e61bd6/tajo-cli/src/main/java/org/apache/tajo/cli/tsql/commands/DescFunctionCommand.java ---------------------------------------------------------------------- diff --git a/tajo-cli/src/main/java/org/apache/tajo/cli/tsql/commands/DescFunctionCommand.java b/tajo-cli/src/main/java/org/apache/tajo/cli/tsql/commands/DescFunctionCommand.java index b168d3f..944ad0f 100644 --- a/tajo-cli/src/main/java/org/apache/tajo/cli/tsql/commands/DescFunctionCommand.java +++ b/tajo-cli/src/main/java/org/apache/tajo/cli/tsql/commands/DescFunctionCommand.java @@ -18,6 +18,9 @@ package org.apache.tajo.cli.tsql.commands; +import jline.console.completer.ArgumentCompleter; +import jline.console.completer.NullCompleter; +import jline.console.completer.StringsCompleter; import org.apache.tajo.catalog.proto.CatalogProtos; import org.apache.tajo.function.FunctionUtil; import org.apache.tajo.cli.tsql.TajoCli; @@ -122,5 +125,11 @@ public class DescFunctionCommand extends TajoShellCommand { return "show function description"; } - + @Override + public ArgumentCompleter getArgumentCompleter() { + return new ArgumentCompleter( + new StringsCompleter(getCommand()), + new FunctionNameCompleter(), + NullCompleter.INSTANCE); + } } http://git-wip-us.apache.org/repos/asf/tajo/blob/17e61bd6/tajo-cli/src/main/java/org/apache/tajo/cli/tsql/commands/DescTableCommand.java ---------------------------------------------------------------------- diff --git a/tajo-cli/src/main/java/org/apache/tajo/cli/tsql/commands/DescTableCommand.java b/tajo-cli/src/main/java/org/apache/tajo/cli/tsql/commands/DescTableCommand.java index 12d23cf..8760a37 100644 --- a/tajo-cli/src/main/java/org/apache/tajo/cli/tsql/commands/DescTableCommand.java +++ b/tajo-cli/src/main/java/org/apache/tajo/cli/tsql/commands/DescTableCommand.java @@ -18,6 +18,9 @@ package org.apache.tajo.cli.tsql.commands; +import jline.console.completer.ArgumentCompleter; +import jline.console.completer.NullCompleter; +import jline.console.completer.StringsCompleter; import org.apache.commons.lang.CharUtils; import org.apache.commons.lang.StringEscapeUtils; import org.apache.tajo.TajoConstants; @@ -156,4 +159,12 @@ public class DescTableCommand extends TajoShellCommand { return sb.toString(); } + + @Override + public ArgumentCompleter getArgumentCompleter() { + return new ArgumentCompleter( + new StringsCompleter(getCommand()), + new TableNameCompleter(), + NullCompleter.INSTANCE); + } } http://git-wip-us.apache.org/repos/asf/tajo/blob/17e61bd6/tajo-cli/src/main/java/org/apache/tajo/cli/tsql/commands/HelpCommand.java ---------------------------------------------------------------------- diff --git a/tajo-cli/src/main/java/org/apache/tajo/cli/tsql/commands/HelpCommand.java b/tajo-cli/src/main/java/org/apache/tajo/cli/tsql/commands/HelpCommand.java index f051e6a..e484d72 100644 --- a/tajo-cli/src/main/java/org/apache/tajo/cli/tsql/commands/HelpCommand.java +++ b/tajo-cli/src/main/java/org/apache/tajo/cli/tsql/commands/HelpCommand.java @@ -18,10 +18,16 @@ package org.apache.tajo.cli.tsql.commands; +import jline.console.completer.ArgumentCompleter; +import jline.console.completer.NullCompleter; +import jline.console.completer.StringsCompleter; import org.apache.tajo.cli.tsql.TajoCli; import org.apache.tajo.util.VersionInfo; import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; public class HelpCommand extends TajoShellCommand { private String targetDocVersion = ""; @@ -130,4 +136,16 @@ public class HelpCommand extends TajoShellCommand { public String getDescription() { return "show command lists and their usages"; } + + @Override + public ArgumentCompleter getArgumentCompleter() { + List<String> cmds = new ArrayList<>(Arrays.asList(getAliases())); + cmds.add(getCommand()); + + return new ArgumentCompleter( + new StringsCompleter(cmds.toArray(new String[cmds.size()])), + new StringsCompleter("copyright", "version", "?", "help", "q", "l", "c", "d", "df", "!", "dfs", "admin", + "set", "unset", "haadmin", "getconf"), // same order as help string + NullCompleter.INSTANCE); + } } http://git-wip-us.apache.org/repos/asf/tajo/blob/17e61bd6/tajo-cli/src/main/java/org/apache/tajo/cli/tsql/commands/SetCommand.java ---------------------------------------------------------------------- diff --git a/tajo-cli/src/main/java/org/apache/tajo/cli/tsql/commands/SetCommand.java b/tajo-cli/src/main/java/org/apache/tajo/cli/tsql/commands/SetCommand.java index 5cff2c7..bd887d5 100644 --- a/tajo-cli/src/main/java/org/apache/tajo/cli/tsql/commands/SetCommand.java +++ b/tajo-cli/src/main/java/org/apache/tajo/cli/tsql/commands/SetCommand.java @@ -18,6 +18,9 @@ package org.apache.tajo.cli.tsql.commands; +import jline.console.completer.ArgumentCompleter; +import jline.console.completer.NullCompleter; +import jline.console.completer.StringsCompleter; import org.apache.tajo.SessionVars; import org.apache.tajo.cli.tsql.TajoCli; import org.apache.tajo.exception.NoSuchSessionVariableException; @@ -130,4 +133,12 @@ public class SetCommand extends TajoShellCommand { return clazz.getSimpleName(); } } + + @Override + public ArgumentCompleter getArgumentCompleter() { + return new ArgumentCompleter( + new StringsCompleter(getCommand()), + new SessionVarCompleter(), + NullCompleter.INSTANCE); + } } http://git-wip-us.apache.org/repos/asf/tajo/blob/17e61bd6/tajo-cli/src/main/java/org/apache/tajo/cli/tsql/commands/TajoGetConfCommand.java ---------------------------------------------------------------------- diff --git a/tajo-cli/src/main/java/org/apache/tajo/cli/tsql/commands/TajoGetConfCommand.java b/tajo-cli/src/main/java/org/apache/tajo/cli/tsql/commands/TajoGetConfCommand.java index 5c7dd0e..a499766 100644 --- a/tajo-cli/src/main/java/org/apache/tajo/cli/tsql/commands/TajoGetConfCommand.java +++ b/tajo-cli/src/main/java/org/apache/tajo/cli/tsql/commands/TajoGetConfCommand.java @@ -18,8 +18,15 @@ package org.apache.tajo.cli.tsql.commands; +import com.google.common.base.Splitter; +import jline.console.completer.ArgumentCompleter; +import jline.console.completer.NullCompleter; +import jline.console.completer.StringsCompleter; import org.apache.tajo.cli.tsql.TajoCli; import org.apache.tajo.cli.tools.TajoGetConf; +import org.apache.tajo.conf.TajoConf; + +import java.util.*; public class TajoGetConfCommand extends TajoShellCommand { private TajoGetConf getconf; @@ -55,4 +62,52 @@ public class TajoGetConfCommand extends TajoShellCommand { public String getDescription() { return "execute a tajo getconf command."; } + + @Override + public ArgumentCompleter getArgumentCompleter() { + TajoConf.ConfVars[] vars = TajoConf.ConfVars.values(); + List<String> confNames = new ArrayList<>(); + + for(TajoConf.ConfVars varname: vars) { + confNames.add(varname.varname); + } + + return new ArgumentCompleter( + new StringsCompleter(getCommand()), + new ConfCompleter(confNames.toArray(new String[confNames.size()])), + NullCompleter.INSTANCE); + } + + private static class ConfCompleter extends StringsCompleter { + ConfCompleter(String [] confs) { + super(confs); + } + + @Override + public int complete(final String buf, final int cur, final List<CharSequence> candidates) { + int result = super.complete(buf, cur, candidates); + + // it means just "if candidates are too many". 10 is arbitrary default. + if (candidates.size() > 10) { + Set<CharSequence> delimited = new LinkedHashSet<>(); + for (CharSequence candidate : candidates) { + Iterator<String> it = Splitter.on(".").split( + candidate.subSequence(cur, candidate.length())).iterator(); + if (it.hasNext()) { + String next = it.next(); + if (next.isEmpty()) { + next = "."; + } + candidate = buf != null ? buf.substring(0, cur) + next : next; + } + delimited.add(candidate); + } + + candidates.clear(); + candidates.addAll(delimited); + } + + return result; + } + } } http://git-wip-us.apache.org/repos/asf/tajo/blob/17e61bd6/tajo-cli/src/main/java/org/apache/tajo/cli/tsql/commands/TajoShellCommand.java ---------------------------------------------------------------------- diff --git a/tajo-cli/src/main/java/org/apache/tajo/cli/tsql/commands/TajoShellCommand.java b/tajo-cli/src/main/java/org/apache/tajo/cli/tsql/commands/TajoShellCommand.java index bc48c8a..6511b36 100644 --- a/tajo-cli/src/main/java/org/apache/tajo/cli/tsql/commands/TajoShellCommand.java +++ b/tajo-cli/src/main/java/org/apache/tajo/cli/tsql/commands/TajoShellCommand.java @@ -18,10 +18,20 @@ package org.apache.tajo.cli.tsql.commands; +import jline.console.completer.ArgumentCompleter; +import jline.console.completer.Completer; +import jline.console.completer.NullCompleter; +import jline.console.completer.StringsCompleter; +import org.apache.tajo.catalog.proto.CatalogProtos; import org.apache.tajo.client.TajoClient; import org.apache.tajo.conf.TajoConf; import org.apache.tajo.cli.tsql.TajoCli; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + public abstract class TajoShellCommand { public abstract String getCommand(); public String [] getAliases() { @@ -126,4 +136,56 @@ public abstract class TajoShellCommand { println(); return columnWidths; } + + public ArgumentCompleter getArgumentCompleter() { + List<String> cmds = new ArrayList<>(Arrays.asList(getAliases())); + cmds.add(getCommand()); + + return new ArgumentCompleter(new StringsCompleter(cmds.toArray(new String[cmds.size()])), NullCompleter.INSTANCE); + } + + class SessionVarCompleter implements Completer { + @Override + public int complete(String s, int i, List<CharSequence> list) { + return new StringsCompleter(client.getAllSessionVariables().keySet().toArray(new String[1])) + .complete(s, i, list); + + } + } + + class DbNameCompleter implements Completer { + @Override + public int complete(String s, int i, List<CharSequence> list) { + return new StringsCompleter(client.getAllDatabaseNames().toArray(new String[1])) + .complete(s, i, list); + } + } + + class TableNameCompleter implements Completer { + @Override + public int complete(String s, int i, List<CharSequence> list) { + List<String> tableList = client.getTableList(client.getCurrentDatabase()); + + if (tableList.isEmpty()) { + return -1; + } + + return new StringsCompleter(tableList.toArray(new String[1])).complete(s, i, list); + } + } + + class FunctionNameCompleter implements Completer { + @Override + public int complete(String s, int i, List<CharSequence> list) { + List<CatalogProtos.FunctionDescProto> functionProtos = client.getFunctions(""); + if (functionProtos.isEmpty()) { + return -1; + } + + List<String> names = functionProtos.stream().map( + (proto) -> proto.getSignature().getName()).collect(Collectors.toList()); + + return new StringsCompleter(names.toArray(new String [1])).complete(s, i, list); + } + } } http://git-wip-us.apache.org/repos/asf/tajo/blob/17e61bd6/tajo-cli/src/main/java/org/apache/tajo/cli/tsql/commands/UnsetCommand.java ---------------------------------------------------------------------- diff --git a/tajo-cli/src/main/java/org/apache/tajo/cli/tsql/commands/UnsetCommand.java b/tajo-cli/src/main/java/org/apache/tajo/cli/tsql/commands/UnsetCommand.java index 73b9773..5db0e40 100644 --- a/tajo-cli/src/main/java/org/apache/tajo/cli/tsql/commands/UnsetCommand.java +++ b/tajo-cli/src/main/java/org/apache/tajo/cli/tsql/commands/UnsetCommand.java @@ -19,6 +19,9 @@ package org.apache.tajo.cli.tsql.commands; import com.google.common.collect.Lists; +import jline.console.completer.ArgumentCompleter; +import jline.console.completer.NullCompleter; +import jline.console.completer.StringsCompleter; import org.apache.tajo.cli.tsql.TajoCli; public class UnsetCommand extends TajoShellCommand { @@ -50,4 +53,12 @@ public class UnsetCommand extends TajoShellCommand { public String getDescription() { return "unset a session variable"; } + + @Override + public ArgumentCompleter getArgumentCompleter() { + return new ArgumentCompleter( + new StringsCompleter(getCommand()), + new SessionVarCompleter(), + NullCompleter.INSTANCE); + } } http://git-wip-us.apache.org/repos/asf/tajo/blob/17e61bd6/tajo-project/pom.xml ---------------------------------------------------------------------- diff --git a/tajo-project/pom.xml b/tajo-project/pom.xml index 87fa8aa..cd86d3b 100644 --- a/tajo-project/pom.xml +++ b/tajo-project/pom.xml @@ -1157,7 +1157,7 @@ <dependency> <groupId>jline</groupId> <artifactId>jline</artifactId> - <version>2.11</version> + <version>2.12</version> </dependency> <dependency> <groupId>com.github.stephenc.jcip</groupId>
