diff --git a/LICENSE b/LICENSE
index db076c66d99..67a5b7934ae 100644
--- a/LICENSE
+++ b/LICENSE
@@ -256,4 +256,12 @@ The following components are provided under the BSD 3-Clause license. See file
r/src/main/scala/org/apache/zeppelin/rinterpreter/rscala/{Package.scala, RClient.scala}
(BSD 3 Clause) portions of Scala (http://www.scala-lang.org/download) - http://www.scala-lang.org/download/#License
- r/src/main/scala/scala/Console.scala
\ No newline at end of file
+ r/src/main/scala/scala/Console.scala
+
+========================================================================
+BSD 2-Clause licenses
+========================================================================
+The following components are provided under the BSD 3-Clause license. See file headers and project links for details.
+
+ (BSD 2 Clause) portions of SQLLine (http://sqlline.sourceforge.net/) - http://sqlline.sourceforge.net/#license
+ jdbc/src/main/java/org/apache/zeppelin/jdbc/SqlCompleter.java
\ No newline at end of file
diff --git a/docs/interpreter/postgresql.md b/docs/interpreter/postgresql.md
index 54def09f3ec..5985b188791 100644
--- a/docs/interpreter/postgresql.md
+++ b/docs/interpreter/postgresql.md
@@ -6,7 +6,49 @@ group: manual
---
{% include JB/setup %}
+## Important Notice
+Postgresql Interpreter will be deprecated and merged into JDBC Interpreter. You can use Postgresql by using JDBC Interpreter with same functionality. See the example below of settings and dependencies.
+
+### Properties
+
+
+ | Property |
+ Value |
+
+
+ | psql.driver |
+ org.postgresql.Driver |
+
+
+ | psql.url |
+ jdbc:postgresql://localhost:5432/ |
+
+
+ | psql.user |
+ psqlUser |
+
+
+ | psql.password |
+ psqlPassword |
+
+
+
+### Dependencies
+
+
+ | Artifact |
+ Exclude |
+
+
+ | org.postgresql:postgresql:9.4-1201-jdbc41 |
+ |
+
+
+
+----
+
## PostgreSQL, HAWQ Interpreter for Apache Zeppelin
+
| Name |
diff --git a/jdbc/src/main/java/org/apache/zeppelin/jdbc/JDBCInterpreter.java b/jdbc/src/main/java/org/apache/zeppelin/jdbc/JDBCInterpreter.java
index 6495264c26c..610618afad4 100644
--- a/jdbc/src/main/java/org/apache/zeppelin/jdbc/JDBCInterpreter.java
+++ b/jdbc/src/main/java/org/apache/zeppelin/jdbc/JDBCInterpreter.java
@@ -33,8 +33,6 @@
import org.apache.zeppelin.interpreter.Interpreter;
import org.apache.zeppelin.interpreter.InterpreterContext;
-import org.apache.zeppelin.interpreter.InterpreterException;
-import org.apache.zeppelin.interpreter.InterpreterPropertyBuilder;
import org.apache.zeppelin.interpreter.InterpreterResult;
import org.apache.zeppelin.interpreter.InterpreterResult.Code;
import org.apache.zeppelin.scheduler.Scheduler;
@@ -50,7 +48,7 @@
/**
* JDBC interpreter for Zeppelin. This interpreter can also be used for accessing HAWQ,
* GreenplumDB, MariaDB, MySQL, Postgres and Redshit.
- *
+ *
*
* - {@code default.url} - JDBC URL to connect to.
* - {@code default.user} - JDBC user name..
@@ -58,22 +56,21 @@
* - {@code default.driver.name} - JDBC driver name.
* - {@code common.max.result} - Max number of SQL result to display.
*
- *
+ *
*
* How to use:
* {@code %jdbc.sql}
- * {@code
- * SELECT store_id, count(*)
- * FROM retail_demo.order_lineitems_pxf
- * GROUP BY store_id;
+ * {@code
+ * SELECT store_id, count(*)
+ * FROM retail_demo.order_lineitems_pxf
+ * GROUP BY store_id;
* }
*
- *
*/
public class JDBCInterpreter extends Interpreter {
private Logger logger = LoggerFactory.getLogger(JDBCInterpreter.class);
-
+
static final String COMMON_KEY = "common";
static final String MAX_LINE_KEY = "max_count";
static final String MAX_LINE_DEFAULT = "1000";
@@ -84,7 +81,7 @@ public class JDBCInterpreter extends Interpreter {
static final String USER_KEY = "user";
static final String PASSWORD_KEY = "password";
static final String DOT = ".";
-
+
private static final char WHITESPACE = ' ';
private static final char NEWLINE = '\n';
private static final char TAB = '\t';
@@ -100,21 +97,33 @@ public class JDBCInterpreter extends Interpreter {
static final String DEFAULT_PASSWORD = DEFAULT_KEY + DOT + PASSWORD_KEY;
static final String EMPTY_COLUMN_VALUE = "";
-
+
private final HashMap propertiesMap;
private final Map paragraphIdStatementMap;
private final Map> propertyKeyUnusedConnectionListMap;
private final Map paragraphIdConnectionMap;
+ private final Map propertyKeySqlCompleterMap;
+
+ private static final Function sequenceToStringTransformer =
+ new Function() {
+ public String apply(CharSequence seq) {
+ return seq.toString();
+ }
+ };
+
+ private static final List NO_COMPLETION = new ArrayList<>();
+
public JDBCInterpreter(Properties property) {
super(property);
propertiesMap = new HashMap<>();
propertyKeyUnusedConnectionListMap = new HashMap<>();
paragraphIdStatementMap = new HashMap<>();
paragraphIdConnectionMap = new HashMap<>();
+ propertyKeySqlCompleterMap = new HashMap<>();
}
-
+
public HashMap getPropertiesMap() {
return propertiesMap;
}
@@ -154,9 +163,38 @@ public void open() {
}
logger.debug("propertiesMap: {}", propertiesMap);
+
+ Connection connection = null;
+ SqlCompleter sqlCompleter = null;
+ for (String propertyKey : propertiesMap.keySet()) {
+ try {
+ connection = getConnection(propertyKey);
+ sqlCompleter = createSqlCompleter(connection);
+ } catch (Exception e) {
+ sqlCompleter = createSqlCompleter(null);
+ }
+ propertyKeySqlCompleterMap.put(propertyKey, sqlCompleter);
+ }
}
-
- public Connection getConnection(String propertyKey) throws ClassNotFoundException, SQLException {
+
+ private SqlCompleter createSqlCompleter(Connection jdbcConnection) {
+
+ SqlCompleter completer = null;
+ try {
+ Set keywordsCompletions = SqlCompleter.getSqlKeywordsCompletions(jdbcConnection);
+ Set dataModelCompletions =
+ SqlCompleter.getDataModelMetadataCompletions(jdbcConnection);
+ SetView allCompletions = Sets.union(keywordsCompletions, dataModelCompletions);
+ completer = new SqlCompleter(allCompletions, dataModelCompletions);
+
+ } catch (IOException | SQLException e) {
+ logger.error("Cannot create SQL completer", e);
+ }
+
+ return completer;
+ }
+
+ public Connection getConnection(String propertyKey) throws ClassNotFoundException, SQLException {
Connection connection = null;
if (propertyKey == null || propertiesMap.get(propertyKey) == null) {
return null;
@@ -180,7 +218,7 @@ public Connection getConnection(String propertyKey) throws ClassNotFoundExcepti
}
return connection;
}
-
+
public Statement getStatement(String propertyKey, String paragraphId)
throws SQLException, ClassNotFoundException {
Connection connection;
@@ -189,7 +227,7 @@ public Statement getStatement(String propertyKey, String paragraphId)
} else {
connection = getConnection(propertyKey);
}
-
+
if (connection == null) {
return null;
}
@@ -213,7 +251,7 @@ private boolean isStatementClosed(Statement statement) {
return false;
}
}
-
+
@Override
public void close() {
@@ -241,13 +279,13 @@ public void close() {
private InterpreterResult executeSql(String propertyKey, String sql,
InterpreterContext interpreterContext) {
-
+
String paragraphId = interpreterContext.getParagraphId();
try {
Statement statement = getStatement(propertyKey, paragraphId);
-
+
if (statement == null) {
return new InterpreterResult(Code.ERROR, "Prefix not found.");
}
@@ -346,7 +384,7 @@ public InterpreterResult interpret(String cmd, InterpreterContext contextInterpr
if (null != propertyKey && !propertyKey.equals(DEFAULT_KEY)) {
cmd = cmd.substring(propertyKey.length() + 2);
}
-
+
cmd = cmd.trim();
logger.info("PropertyKey: {}, SQL command: '{}'", propertyKey, cmd);
@@ -382,7 +420,7 @@ public String getPropertyKey(String cmd) {
return DEFAULT_KEY;
}
}
-
+
@Override
public FormType getFormType() {
return FormType.SIMPLE;
@@ -401,7 +439,13 @@ public Scheduler getScheduler() {
@Override
public List completion(String buf, int cursor) {
- return null;
+ List candidates = new ArrayList<>();
+ SqlCompleter sqlCompleter = propertyKeySqlCompleterMap.get(getPropertyKey(buf));
+ if (sqlCompleter != null && sqlCompleter.complete(buf, cursor, candidates) >= 0) {
+ return Lists.transform(candidates, sequenceToStringTransformer);
+ } else {
+ return NO_COMPLETION;
+ }
}
public int getMaxResult() {
diff --git a/jdbc/src/main/java/org/apache/zeppelin/jdbc/SqlCompleter.java b/jdbc/src/main/java/org/apache/zeppelin/jdbc/SqlCompleter.java
new file mode 100644
index 00000000000..41dcaabdb04
--- /dev/null
+++ b/jdbc/src/main/java/org/apache/zeppelin/jdbc/SqlCompleter.java
@@ -0,0 +1,250 @@
+/**
+ * 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.zeppelin.jdbc;
+
+/*
+ * This source file is based on code taken from SQLLine 1.0.2 See SQLLine notice in LICENSE
+ */
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.Sets;
+import com.google.common.collect.Sets.SetView;
+import jline.console.completer.ArgumentCompleter.ArgumentList;
+import jline.console.completer.ArgumentCompleter.WhitespaceArgumentDelimiter;
+import jline.console.completer.StringsCompleter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.*;
+import java.util.regex.Pattern;
+
+import static org.apache.commons.lang.StringUtils.isBlank;
+
+/**
+ * SQL auto complete functionality for the JdbcInterpreter.
+ */
+public class SqlCompleter extends StringsCompleter {
+
+ private static Logger logger = LoggerFactory.getLogger(SqlCompleter.class);
+
+ /**
+ * Delimiter that can split SQL statement in keyword list
+ */
+ private WhitespaceArgumentDelimiter sqlDelimiter = new WhitespaceArgumentDelimiter() {
+
+ private Pattern pattern = Pattern.compile("[\\.:;,]");
+
+ @Override
+ public boolean isDelimiterChar(CharSequence buffer, int pos) {
+ return pattern.matcher("" + buffer.charAt(pos)).matches()
+ || super.isDelimiterChar(buffer, pos);
+ }
+ };
+
+ private Set modelCompletions = new HashSet<>();
+
+ public SqlCompleter(Set allCompletions, Set dataModelCompletions) {
+ super(allCompletions);
+ this.modelCompletions = dataModelCompletions;
+ }
+
+ @Override
+ public int complete(String buffer, int cursor, List candidates) {
+
+ if (isBlank(buffer) || (cursor > buffer.length() + 1)) {
+ return -1;
+ }
+
+ // The delimiter breaks the buffer into separate words (arguments), separated by the
+ // white spaces.
+ ArgumentList argumentList = sqlDelimiter.delimit(buffer, cursor);
+ String argument = argumentList.getCursorArgument();
+ // cursor in the selected argument
+ int argumentPosition = argumentList.getArgumentPosition();
+
+ if (isBlank(argument)) {
+ int argumentsCount = argumentList.getArguments().length;
+ if (argumentsCount <= 0 || ((buffer.length() + 2) < cursor)
+ || sqlDelimiter.isDelimiterChar(buffer, cursor - 2)) {
+ return -1;
+ }
+ argument = argumentList.getArguments()[argumentsCount - 1];
+ argumentPosition = argument.length();
+ }
+
+ int complete = super.complete(argument, argumentPosition, candidates);
+
+ logger.debug("complete:" + complete + ", size:" + candidates.size());
+
+ return complete;
+ }
+
+ public void updateDataModelMetaData(Connection connection) {
+
+ try {
+ Set newModelCompletions = getDataModelMetadataCompletions(connection);
+ logger.debug("New model metadata is:" + Joiner.on(',').join(newModelCompletions));
+
+ // Sets.difference(set1, set2) - returned set contains all elements that are contained by set1
+ // and not contained by set2. set2 may also contain elements not present in set1; these are
+ // simply ignored.
+ SetView removedCompletions = Sets.difference(modelCompletions, newModelCompletions);
+ logger.debug("Removed Model Completions: " + Joiner.on(',').join(removedCompletions));
+ this.getStrings().removeAll(removedCompletions);
+
+ SetView newCompletions = Sets.difference(newModelCompletions, modelCompletions);
+ logger.debug("New Completions: " + Joiner.on(',').join(newCompletions));
+ this.getStrings().addAll(newCompletions);
+
+ modelCompletions = newModelCompletions;
+
+ } catch (SQLException e) {
+ logger.error("Failed to update the metadata conmpletions", e);
+ }
+ }
+
+ public static Set getSqlKeywordsCompletions(Connection connection) throws IOException,
+ SQLException {
+
+ // Add the default SQL completions
+ String keywords =
+ new BufferedReader(new InputStreamReader(
+ SqlCompleter.class.getResourceAsStream("/ansi.sql.keywords"))).readLine();
+
+ Set completions = new TreeSet<>();
+
+ if (null != connection) {
+ DatabaseMetaData metaData = connection.getMetaData();
+
+ // Add the driver specific SQL completions
+ String driverSpecificKeywords =
+ "/" + metaData.getDriverName().replace(" ", "-").toLowerCase() + "-sql.keywords";
+
+ logger.info("JDBC DriverName:" + driverSpecificKeywords);
+
+ if (SqlCompleter.class.getResource(driverSpecificKeywords) != null) {
+ String driverKeywords =
+ new BufferedReader(new InputStreamReader(
+ SqlCompleter.class.getResourceAsStream(driverSpecificKeywords))).readLine();
+ keywords += "," + driverKeywords.toUpperCase();
+ }
+
+
+ // Add the keywords from the current JDBC connection
+ try {
+ keywords += "," + metaData.getSQLKeywords();
+ } catch (Exception e) {
+ logger.debug("fail to get SQL key words from database metadata: " + e, e);
+ }
+ try {
+ keywords += "," + metaData.getStringFunctions();
+ } catch (Exception e) {
+ logger.debug("fail to get string function names from database metadata: " + e, e);
+ }
+ try {
+ keywords += "," + metaData.getNumericFunctions();
+ } catch (Exception e) {
+ logger.debug("fail to get numeric function names from database metadata: " + e, e);
+ }
+ try {
+ keywords += "," + metaData.getSystemFunctions();
+ } catch (Exception e) {
+ logger.debug("fail to get system function names from database metadata: " + e, e);
+ }
+ try {
+ keywords += "," + metaData.getTimeDateFunctions();
+ } catch (Exception e) {
+ logger.debug("fail to get time date function names from database metadata: " + e, e);
+ }
+
+ // Also allow lower-case versions of all the keywords
+ keywords += "," + keywords.toLowerCase();
+
+ }
+
+ StringTokenizer tok = new StringTokenizer(keywords, ", ");
+ while (tok.hasMoreTokens()) {
+ completions.add(tok.nextToken());
+ }
+
+ return completions;
+ }
+
+ public static Set getDataModelMetadataCompletions(Connection connection)
+ throws SQLException {
+ Set completions = new TreeSet<>();
+ if (null != connection) {
+ getColumnNames(connection.getMetaData(), completions);
+ getSchemaNames(connection.getMetaData(), completions);
+ }
+ return completions;
+ }
+
+ private static void getColumnNames(DatabaseMetaData meta, Set names) throws SQLException {
+
+ try {
+ ResultSet columns = meta.getColumns(meta.getConnection().getCatalog(), null, "%", "%");
+ try {
+
+ while (columns.next()) {
+ // Add the following strings: (1) column name, (2) table name
+ String name = columns.getString("TABLE_NAME");
+ if (!isBlank(name)) {
+ names.add(name);
+ names.add(columns.getString("COLUMN_NAME"));
+ // names.add(columns.getString("TABLE_NAME") + "." + columns.getString("COLUMN_NAME"));
+ }
+ }
+ } finally {
+ columns.close();
+ }
+
+ logger.debug(Joiner.on(',').join(names));
+ } catch (Exception e) {
+ logger.error("Failed to retrieve the column name", e);
+ }
+ }
+
+ private static void getSchemaNames(DatabaseMetaData meta, Set names) throws SQLException {
+
+ try {
+ ResultSet schemas = meta.getSchemas();
+ try {
+ while (schemas.next()) {
+ String schemaName = schemas.getString("TABLE_SCHEM");
+ if (!isBlank(schemaName)) {
+ names.add(schemaName + ".");
+ }
+ }
+ } finally {
+ schemas.close();
+ }
+ } catch (Exception e) {
+ logger.error("Failed to retrieve the column name", e);
+ }
+ }
+
+ // test purpose only
+ WhitespaceArgumentDelimiter getSqlDelimiter() {
+ return this.sqlDelimiter;
+ }
+}
diff --git a/jdbc/src/main/resources/ansi.sql.keywords b/jdbc/src/main/resources/ansi.sql.keywords
new file mode 100644
index 00000000000..1f25a812ffd
--- /dev/null
+++ b/jdbc/src/main/resources/ansi.sql.keywords
@@ -0,0 +1 @@
+ABSOLUTE,ACTION,ADD,ALL,ALLOCATE,ALTER,AND,ANY,ARE,AS,ASC,ASSERTION,AT,AUTHORIZATION,AVG,BEGIN,BETWEEN,BIT,BIT_LENGTH,BOTH,BY,CASCADE,CASCADED,CASE,CAST,CATALOG,CHAR,CHARACTER,CHAR_LENGTH,CHARACTER_LENGTH,CHECK,CLOSE,CLUSTER,COALESCE,COLLATE,COLLATION,COLUMN,COMMIT,CONNECT,CONNECTION,CONSTRAINT,CONSTRAINTS,CONTINUE,CONVERT,CORRESPONDING,COUNT,CREATE,CROSS,CURRENT,CURRENT_DATE,CURRENT_TIME,CURRENT_TIMESTAMP,CURRENT_USER,CURSOR,DATE,DAY,DEALLOCATE,DEC,DECIMAL,DECLARE,DEFAULT,DEFERRABLE,DEFERRED,DELETE,DESC,DESCRIBE,DESCRIPTOR,DIAGNOSTICS,DISCONNECT,DISTINCT,DOMAIN,DOUBLE,DROP,ELSE,END,END-EXEC,ESCAPE,EXCEPT,EXCEPTION,EXEC,EXECUTE,EXISTS,EXTERNAL,EXTRACT,FALSE,FETCH,FIRST,FLOAT,FOR,FOREIGN,FOUND,FROM,FULL,GET,GLOBAL,GO,GOTO,GRANT,GROUP,HAVING,HOUR,IDENTITY,IMMEDIATE,IN,INDICATOR,INITIALLY,INNER,INPUT,INSENSITIVE,INSERT,INT,INTEGER,INTERSECT,INTERVAL,INTO,IS,ISOLATION,JOIN,KEY,LANGUAGE,LAST,LEADING,LEFT,LEVEL,LIKE,LOCAL,LOWER,MATCH,MAX,MIN,MINUTE,MODULE,MONTH,NAMES,NATIONAL,NATURAL,NCHAR,NEXT,NO,NOT,NULL,NULLIF,NUMERIC,OCTET_LENGTH,OF,ON,ONLY,OPEN,OPTION,OR,ORDER,OUTER,OUTPUT,OVERLAPS,OVERWRITE,PAD,PARTIAL,PARTITION,POSITION,PRECISION,PREPARE,PRESERVE,PRIMARY,PRIOR,PRIVILEGES,PROCEDURE,PUBLIC,READ,REAL,REFERENCES,RELATIVE,RESTRICT,REVOKE,RIGHT,ROLLBACK,ROWS,SCHEMA,SCROLL,SECOND,SECTION,SELECT,SESSION,SESSION_USER,SET,SIZE,SMALLINT,SOME,SPACE,SQL,SQLCODE,SQLERROR,SQLSTATE,SUBSTRING,SUM,SYSTEM_USER,TABLE,TEMPORARY,THEN,TIME,TIMESTAMP,TIMEZONE_HOUR,TIMEZONE_MINUTE,TO,TRAILING,TRANSACTION,TRANSLATE,TRANSLATION,TRIM,TRUE,UNION,UNIQUE,UNKNOWN,UPDATE,UPPER,USAGE,USER,USING,VALUE,VALUES,VARCHAR,VARYING,VIEW,WHEN,WHENEVER,WHERE,WITH,WORK,WRITE,YEAR,ZONE,ADA,C,CATALOG_NAME,CHARACTER_SET_CATALOG,CHARACTER_SET_NAME,CHARACTER_SET_SCHEMA,CLASS_ORIGIN,COBOL,COLLATION_CATALOG,COLLATION_NAME,COLLATION_SCHEMA,COLUMN_NAME,COMMAND_FUNCTION,COMMITTED,CONDITION_NUMBER,CONNECTION_NAME,CONSTRAINT_CATALOG,CONSTRAINT_NAME,CONSTRAINT_SCHEMA,CURSOR_NAME,DATA,DATETIME_INTERVAL_CODE,DATETIME_INTERVAL_PRECISION,DYNAMIC_FUNCTION,FORTRAN,LENGTH,MESSAGE_LENGTH,MESSAGE_OCTET_LENGTH,MESSAGE_TEXT,MORE,MUMPS,NAME,NULLABLE,NUMBER,PASCAL,PLI,REPEATABLE,RETURNED_LENGTH,RETURNED_OCTET_LENGTH,RETURNED_SQLSTATE,ROW_COUNT,SCALE,SCHEMA_NAME,SERIALIZABLE,SERVER_NAME,SUBCLASS_ORIGIN,TABLE_NAME,TYPE,UNCOMMITTED,UNNAMED,LIMIT
diff --git a/jdbc/src/main/resources/postgresql-native-driver-sql.keywords b/jdbc/src/main/resources/postgresql-native-driver-sql.keywords
new file mode 100644
index 00000000000..a857cbdbbd5
--- /dev/null
+++ b/jdbc/src/main/resources/postgresql-native-driver-sql.keywords
@@ -0,0 +1 @@
+A,ABORT,ABS,ABSENT,ABSOLUTE,ACCESS,ACCORDING,ACTION,ADA,ADD,ADMIN,AFTER,AGGREGATE,ALL,ALLOCATE,ALSO,ALTER,ALWAYS,ANALYSE,ANALYZE,AND,ANY,ARE,ARRAY,ARRAY_AGG,ARRAY_MAX_CARDINALITY,AS,ASC,ASENSITIVE,ASSERTION,ASSIGNMENT,ASYMMETRIC,AT,ATOMIC,ATTRIBUTE,ATTRIBUTES,AUTHORIZATION,AVG,BACKWARD,BASE64,BEFORE,BEGIN,BEGIN_FRAME,BEGIN_PARTITION,BERNOULLI,BETWEEN,BIGINT,BINARY,BIT,BIT_LENGTH,BLOB,BLOCKED,BOM,BOOLEAN,BOTH,BREADTH,BY,C,CACHE,CALL,CALLED,CARDINALITY,CASCADE,CASCADED,CASE,CAST,CATALOG,CATALOG_NAME,CEIL,CEILING,CHAIN,CHAR,CHARACTER,CHARACTERISTICS,CHARACTERS,CHARACTER_LENGTH,CHARACTER_SET_CATALOG,CHARACTER_SET_NAME,CHARACTER_SET_SCHEMA,CHAR_LENGTH,CHECK,CHECKPOINT,CLASS,CLASS_ORIGIN,CLOB,CLOSE,CLUSTER,COALESCE,COBOL,COLLATE,COLLATION,COLLATION_CATALOG,COLLATION_NAME,COLLATION_SCHEMA,COLLECT,COLUMN,COLUMNS,COLUMN_NAME,COMMAND_FUNCTION,COMMAND_FUNCTION_CODE,COMMENT,COMMENTS,COMMIT,COMMITTED,CONCURRENTLY,CONDITION,CONDITION_NUMBER,CONFIGURATION,CONNECT,CONNECTION,CONNECTION_NAME,CONSTRAINT,CONSTRAINTS,CONSTRAINT_CATALOG,CONSTRAINT_NAME,CONSTRAINT_SCHEMA,CONSTRUCTOR,CONTAINS,CONTENT,CONTINUE,CONTROL,CONVERSION,CONVERT,COPY,CORR,CORRESPONDING,COST,COUNT,COVAR_POP,COVAR_SAMP,CREATE,CROSS,CSV,CUBE,CUME_DIST,CURRENT,CURRENT_CATALOG,CURRENT_DATE,CURRENT_DEFAULT_TRANSFORM_GROUP,CURRENT_PATH,CURRENT_ROLE,CURRENT_ROW,CURRENT_SCHEMA,CURRENT_TIME,CURRENT_TIMESTAMP,CURRENT_TRANSFORM_GROUP_FOR_TYPE,CURRENT_USER,CURSOR,CURSOR_NAME,CYCLE,DATA,DATABASE,DATALINK,DATE,DATETIME_INTERVAL_CODE,DATETIME_INTERVAL_PRECISION,DAY,DB,DEALLOCATE,DEC,DECIMAL,DECLARE,DEFAULT,DEFAULTS,DEFERRABLE,DEFERRED,DEFINED,DEFINER,DEGREE,DELETE,DELIMITER,DELIMITERS,DENSE_RANK,DEPTH,DEREF,DERIVED,DESC,DESCRIBE,DESCRIPTOR,DETERMINISTIC,DIAGNOSTICS,DICTIONARY,DISABLE,DISCARD,DISCONNECT,DISPATCH,DISTINCT,DLNEWCOPY,DLPREVIOUSCOPY,DLURLCOMPLETE,DLURLCOMPLETEONLY,DLURLCOMPLETEWRITE,DLURLPATH,DLURLPATHONLY,DLURLPATHWRITE,DLURLSCHEME,DLURLSERVER,DLVALUE,DO,DOCUMENT,DOMAIN,DOUBLE,DROP,DYNAMIC,DYNAMIC_FUNCTION,DYNAMIC_FUNCTION_CODE,EACH,ELEMENT,ELSE,EMPTY,ENABLE,ENCODING,ENCRYPTED,END,END-EXEC,END_FRAME,END_PARTITION,ENFORCED,ENUM,EQUALS,ESCAPE,EVENT,EVERY,EXCEPT,EXCEPTION,EXCLUDE,EXCLUDING,EXCLUSIVE,EXEC,EXECUTE,EXISTS,EXP,EXPLAIN,EXPRESSION,EXTENSION,EXTERNAL,EXTRACT,FALSE,FAMILY,FETCH,FILE,FILTER,FINAL,FIRST,FIRST_VALUE,FLAG,FLOAT,FLOOR,FOLLOWING,FOR,FORCE,FOREIGN,FORTRAN,FORWARD,FOUND,FRAME_ROW,FREE,FREEZE,FROM,FS,FULL,FUNCTION,FUNCTIONS,FUSION,G,GENERAL,GENERATED,GET,GLOBAL,GO,GOTO,GRANT,GRANTED,GREATEST,GROUP,GROUPING,GROUPS,HANDLER,HAVING,HEADER,HEX,HIERARCHY,HOLD,HOUR,ID,IDENTITY,IF,IGNORE,ILIKE,IMMEDIATE,IMMEDIATELY,IMMUTABLE,IMPLEMENTATION,IMPLICIT,IMPORT,IN,INCLUDING,INCREMENT,INDENT,INDEX,INDEXES,INDICATOR,INHERIT,INHERITS,INITIALLY,INLINE,INNER,INOUT,INPUT,INSENSITIVE,INSERT,INSTANCE,INSTANTIABLE,INSTEAD,INT,INTEGER,INTEGRITY,INTERSECT,INTERSECTION,INTERVAL,INTO,INVOKER,IS,ISNULL,ISOLATION,JOIN,K,KEY,KEY_MEMBER,KEY_TYPE,LABEL,LAG,LANGUAGE,LARGE,LAST,LAST_VALUE,LATERAL,LC_COLLATE,LC_CTYPE,LEAD,LEADING,LEAKPROOF,LEAST,LEFT,LENGTH,LEVEL,LIBRARY,LIKE,LIKE_REGEX,LIMIT,LINK,LISTEN,LN,LOAD,LOCAL,LOCALTIME,LOCALTIMESTAMP,LOCATION,LOCATOR,LOCK,LOWER,M,MAP,MAPPING,MATCH,MATCHED,MATERIALIZED,MAX,MAXVALUE,MAX_CARDINALITY,MEMBER,MERGE,MESSAGE_LENGTH,MESSAGE_OCTET_LENGTH,MESSAGE_TEXT,METHOD,MIN,MINUTE,MINVALUE,MOD,MODE,MODIFIES,MODULE,MONTH,MORE,MOVE,MULTISET,MUMPS,NAME,NAMES,NAMESPACE,NATIONAL,NATURAL,NCHAR,NCLOB,NESTING,NEW,NEXT,NFC,NFD,NFKC,NFKD,NIL,NO,NONE,NORMALIZE,NORMALIZED,NOT,NOTHING,NOTIFY,NOTNULL,NOWAIT,NTH_VALUE,NTILE,NULL,NULLABLE,NULLIF,NULLS,NUMBER,NUMERIC,OBJECT,OCCURRENCES_REGEX,OCTETS,OCTET_LENGTH,OF,OFF,OFFSET,OIDS,OLD,ON,ONLY,OPEN,OPERATOR,OPTION,OPTIONS,OR,ORDER,ORDERING,ORDINALITY,OTHERS,OUT,OUTER,OUTPUT,OVER,OVERLAPS,OVERLAY,OVERRIDING,OWNED,OWNER,P,PAD,PARAMETER,PARAMETER_MODE,PARAMETER_NAME,PARAMETER_ORDINAL_POSITION,PARAMETER_SPECIFIC_CATALOG,PARAMETER_SPECIFIC_NAME,PARAMETER_SPECIFIC_SCHEMA,PARSER,PARTIAL,PARTITION,PASCAL,PASSING,PASSTHROUGH,PASSWORD,PATH,PERCENT,PERCENTILE_CONT,PERCENTILE_DISC,PERCENT_RANK,PERIOD,PERMISSION,PLACING,PLANS,PLI,PORTION,POSITION,POSITION_REGEX,POWER,PRECEDES,PRECEDING,PRECISION,PREPARE,PREPARED,PRESERVE,PRIMARY,PRIOR,PRIVILEGES,PROCEDURAL,PROCEDURE,PROGRAM,PUBLIC,QUOTE,RANGE,RANK,READ,READS,REAL,REASSIGN,RECHECK,RECOVERY,RECURSIVE,REF,REFERENCES,REFERENCING,REFRESH,REGR_AVGX,REGR_AVGY,REGR_COUNT,REGR_INTERCEPT,REGR_R2,REGR_SLOPE,REGR_SXX,REGR_SXY,REGR_SYY,REINDEX,RELATIVE,RELEASE,RENAME,REPEATABLE,REPLACE,REPLICA,REQUIRING,RESET,RESPECT,RESTART,RESTORE,RESTRICT,RESULT,RETURN,RETURNED_CARDINALITY,RETURNED_LENGTH,RETURNED_OCTET_LENGTH,RETURNED_SQLSTATE,RETURNING,RETURNS,REVOKE,RIGHT,ROLE,ROLLBACK,ROLLUP,ROUTINE,ROUTINE_CATALOG,ROUTINE_NAME,ROUTINE_SCHEMA,ROW,ROWS,ROW_COUNT,ROW_NUMBER,RULE,SAVEPOINT,SCALE,SCHEMA,SCHEMA_NAME,SCOPE,SCOPE_CATALOG,SCOPE_NAME,SCOPE_SCHEMA,SCROLL,SEARCH,SECOND,SECTION,SECURITY,SELECT,SELECTIVE,SELF,SENSITIVE,SEQUENCE,SEQUENCES,SERIALIZABLE,SERVER,SERVER_NAME,SESSION,SESSION_USER,SET,SETOF,SETS,SHARE,SHOW,SIMILAR,SIMPLE,SIZE,SMALLINT,SNAPSHOT,SOME,SOURCE,SPACE,SPECIFIC,SPECIFICTYPE,SPECIFIC_NAME,SQL,SQLCODE,SQLERROR,SQLEXCEPTION,SQLSTATE,SQLWARNING,SQRT,STABLE,STANDALONE,START,STATE,STATEMENT,STATIC,STATISTICS,STDDEV_POP,STDDEV_SAMP,STDIN,STDOUT,STORAGE,STRICT,STRIP,STRUCTURE,STYLE,SUBCLASS_ORIGIN,SUBMULTISET,SUBSTRING,SUBSTRING_REGEX,SUCCEEDS,SUM,SYMMETRIC,SYSID,SYSTEM,SYSTEM_TIME,SYSTEM_USER,T,TABLE,TABLES,TABLESAMPLE,TABLESPACE,TABLE_NAME,TEMP,TEMPLATE,TEMPORARY,TEXT,THEN,TIES,TIME,TIMESTAMP,TIMEZONE_HOUR,TIMEZONE_MINUTE,TO,TOKEN,TOP_LEVEL_COUNT,TRAILING,TRANSACTION,TRANSACTIONS_COMMITTED,TRANSACTIONS_ROLLED_BACK,TRANSACTION_ACTIVE,TRANSFORM,TRANSFORMS,TRANSLATE,TRANSLATE_REGEX,TRANSLATION,TREAT,TRIGGER,TRIGGER_CATALOG,TRIGGER_NAME,TRIGGER_SCHEMA,TRIM,TRIM_ARRAY,TRUE,TRUNCATE,TRUSTED,TYPE,TYPES,UESCAPE,UNBOUNDED,UNCOMMITTED,UNDER,UNENCRYPTED,UNION,UNIQUE,UNKNOWN,UNLINK,UNLISTEN,UNLOGGED,UNNAMED,UNNEST,UNTIL,UNTYPED,UPDATE,UPPER,URI,USAGE,USER,USER_DEFINED_TYPE_CATALOG,USER_DEFINED_TYPE_CODE,USER_DEFINED_TYPE_NAME,USER_DEFINED_TYPE_SCHEMA,USING,VACUUM,VALID,VALIDATE,VALIDATOR,VALUE,VALUES,VALUE_OF,VARBINARY,VARCHAR,VARIADIC,VARYING,VAR_POP,VAR_SAMP,VERBOSE,VERSION,VERSIONING,VIEW,VIEWS,VOLATILE,WHEN,WHENEVER,WHERE,WHITESPACE,WIDTH_BUCKET,WINDOW,WITH,WITHIN,WITHOUT,WORK,WRAPPER,WRITE,XML,XMLAGG,XMLATTRIBUTES,XMLBINARY,XMLCAST,XMLCOMMENT,XMLCONCAT,XMLDECLARATION,XMLDOCUMENT,XMLELEMENT,XMLEXISTS,XMLFOREST,XMLITERATE,XMLNAMESPACES,XMLPARSE,XMLPI,XMLQUERY,XMLROOT,XMLSCHEMA,XMLSERIALIZE,XMLTABLE,XMLTEXT,XMLVALIDATE,YEAR,YES,ZONE
diff --git a/licenses/LICENSE-sqlline-1.0.2 b/licenses/LICENSE-sqlline-1.0.2
new file mode 100644
index 00000000000..970974c0b31
--- /dev/null
+++ b/licenses/LICENSE-sqlline-1.0.2
@@ -0,0 +1,10 @@
+Copyright (c)
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file