Repository: incubator-zeppelin
Updated Branches:
refs/heads/master dcfa3b5df -> cc5b4bcfa
ZEPPELIN-70: Add PostgreSQL Interpreter
modified: conf/zeppelin-site.xml.template
modified: pom.xml
new file: postgresql/pom.xml
new file:
postgresql/src/main/java/org/apache/zeppelin/postgresql/PostgreSqlInterpreter.java
new file:
postgresql/src/test/java/org/apache/zeppelin/postgresql/PostgreSqlInterpreterTest.java
Author: tzolov <[email protected]>
Closes #172 from tzolov/ZEPPELIN-70 and squashes the following commits:
5de507d [tzolov] ZEPPELIN-70: Remove the obsolete dependencies from the POM
a3db5f7 [tzolov] ZEPPELING-70: Rebase to upstream master
745ad3d [tzolov] ZEPPELIN-70: Add PostgreSQL JDBC license statement
a7d26dd [tzolov] ZEPPELIN-70: Closed previous connection before open new one.
Register psql in ZeppelinConfiguration
886eab5 [tzolov] ZEPPELIN-70: Add property to control the number of SQL results
shown in the display.
880bde0 [tzolov] ZEPPELIN-70: Improve javadoc. Clean pom formating
ba1b5b8 [tzolov] ZEPPELIN-70: Fix spelling
60544a1 [tzolov] ZEPPELIN-70: Add jdbc driver name as configuration property
841d67c [tzolov] ZEPPELIN-70: Add PostgreSQL Interpreter
Project: http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/repo
Commit:
http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/commit/cc5b4bcf
Tree: http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/tree/cc5b4bcf
Diff: http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/diff/cc5b4bcf
Branch: refs/heads/master
Commit: cc5b4bcfa734221eb13915b045339fbefbb96f42
Parents: dcfa3b5
Author: tzolov <[email protected]>
Authored: Sun Aug 9 07:08:07 2015 +0200
Committer: Lee moon soo <[email protected]>
Committed: Sun Aug 9 12:51:27 2015 -0700
----------------------------------------------------------------------
LICENSE | 37 +++
conf/zeppelin-site.xml.template | 2 +-
pom.xml | 1 +
postgresql/pom.xml | 151 ++++++++++
.../postgresql/PostgreSqlInterpreter.java | 278 +++++++++++++++++++
.../postgresql/PostgreSqlInterpreterTest.java | 224 +++++++++++++++
.../zeppelin/conf/ZeppelinConfiguration.java | 3 +-
7 files changed, 694 insertions(+), 2 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/cc5b4bcf/LICENSE
----------------------------------------------------------------------
diff --git a/LICENSE b/LICENSE
index aec76a6..0fb8237 100644
--- a/LICENSE
+++ b/LICENSE
@@ -200,3 +200,40 @@
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.
+
+
+ APACHE ZEPPELIN SUBCOMPONENTS:
+
+ The Apache Zeppelin project contains subcomponents with separate copyright
+ notices and license terms. Your use of the source code for the these
+ subcomponents is subject to the terms and conditions of the following
+ licenses.
+
+ For the PostgreSQL JDBC driver jar file:
+
+ Copyright (c) 1997-2011, PostgreSQL Global Development Group
+ 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.
+ 3. Neither the name of the PostgreSQL Global Development Group nor the names
+ of its contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ 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 OWNER 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.
http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/cc5b4bcf/conf/zeppelin-site.xml.template
----------------------------------------------------------------------
diff --git a/conf/zeppelin-site.xml.template b/conf/zeppelin-site.xml.template
index f48a960..6d03f6e 100644
--- a/conf/zeppelin-site.xml.template
+++ b/conf/zeppelin-site.xml.template
@@ -72,7 +72,7 @@
<property>
<name>zeppelin.interpreters</name>
-
<value>org.apache.zeppelin.spark.SparkInterpreter,org.apache.zeppelin.spark.PySparkInterpreter,org.apache.zeppelin.spark.SparkSqlInterpreter,org.apache.zeppelin.spark.DepInterpreter,org.apache.zeppelin.markdown.Markdown,org.apache.zeppelin.angular.AngularInterpreter,org.apache.zeppelin.shell.ShellInterpreter,org.apache.zeppelin.hive.HiveInterpreter,org.apache.zeppelin.tajo.TajoInterpreter,org.apache.zeppelin.flink.FlinkInterpreter,org.apache.zeppelin.lens.LensInterpreter,org.apache.zeppelin.ignite.IgniteInterpreter,org.apache.zeppelin.ignite.IgniteSqlInterpreter,org.apache.zeppelin.cassandra.CassandraInterpreter,org.apache.zeppelin.geode.GeodeOqlInterpreter</value>
+
<value>org.apache.zeppelin.spark.SparkInterpreter,org.apache.zeppelin.spark.PySparkInterpreter,org.apache.zeppelin.spark.SparkSqlInterpreter,org.apache.zeppelin.spark.DepInterpreter,org.apache.zeppelin.markdown.Markdown,org.apache.zeppelin.angular.AngularInterpreter,org.apache.zeppelin.shell.ShellInterpreter,org.apache.zeppelin.hive.HiveInterpreter,org.apache.zeppelin.tajo.TajoInterpreter,org.apache.zeppelin.flink.FlinkInterpreter,org.apache.zeppelin.lens.LensInterpreter,org.apache.zeppelin.ignite.IgniteInterpreter,org.apache.zeppelin.ignite.IgniteSqlInterpreter,org.apache.zeppelin.cassandra.CassandraInterpreter,org.apache.zeppelin.geode.GeodeOqlInterpreter,org.apache.zeppelin.postgresql.PostgreSqlInterpreter</value>
<description>Comma separated interpreter configurations. First interpreter
become a default</description>
</property>
http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/cc5b4bcf/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index ecdebdd..791681b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -92,6 +92,7 @@
<module>shell</module>
<module>hive</module>
<module>geode</module>
+ <module>postgresql</module>
<module>tajo</module>
<module>flink</module>
<module>ignite</module>
http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/cc5b4bcf/postgresql/pom.xml
----------------------------------------------------------------------
diff --git a/postgresql/pom.xml b/postgresql/pom.xml
new file mode 100644
index 0000000..1a64c91
--- /dev/null
+++ b/postgresql/pom.xml
@@ -0,0 +1,151 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <artifactId>zeppelin</artifactId>
+ <groupId>org.apache.zeppelin</groupId>
+ <version>0.6.0-incubating-SNAPSHOT</version>
+ </parent>
+
+ <groupId>org.apache.zeppelin</groupId>
+ <artifactId>zeppelin-postgresql</artifactId>
+ <packaging>jar</packaging>
+ <version>0.6.0-incubating-SNAPSHOT</version>
+ <name>Zeppelin: PostgreSQL interpreter</name>
+ <url>http://www.apache.org</url>
+
+ <properties>
+ <postgresql.version>9.4-1201-jdbc41</postgresql.version>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.zeppelin</groupId>
+ <artifactId>zeppelin-interpreter</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.postgresql</groupId>
+ <artifactId>postgresql</artifactId>
+ <version>${postgresql.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>1.9.5</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.mockrunner</groupId>
+ <artifactId>mockrunner-jdbc</artifactId>
+ <version>1.0.8</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-deploy-plugin</artifactId>
+ <version>2.7</version>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <artifactId>maven-enforcer-plugin</artifactId>
+ <version>1.3.1</version>
+ <executions>
+ <execution>
+ <id>enforce</id>
+ <phase>none</phase>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <version>2.8</version>
+ <executions>
+ <execution>
+ <id>copy-dependencies</id>
+ <phase>package</phase>
+ <goals>
+ <goal>copy-dependencies</goal>
+ </goals>
+ <configuration>
+
<outputDirectory>${project.build.directory}/../../interpreter/psql</outputDirectory>
+ <overWriteReleases>false</overWriteReleases>
+ <overWriteSnapshots>false</overWriteSnapshots>
+ <overWriteIfNewer>true</overWriteIfNewer>
+ <includeScope>runtime</includeScope>
+ </configuration>
+ </execution>
+ <execution>
+ <id>copy-artifact</id>
+ <phase>package</phase>
+ <goals>
+ <goal>copy</goal>
+ </goals>
+ <configuration>
+
<outputDirectory>${project.build.directory}/../../interpreter/psql</outputDirectory>
+ <overWriteReleases>false</overWriteReleases>
+ <overWriteSnapshots>false</overWriteSnapshots>
+ <overWriteIfNewer>true</overWriteIfNewer>
+ <includeScope>runtime</includeScope>
+ <artifactItems>
+ <artifactItem>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>${project.artifactId}</artifactId>
+ <version>${project.version}</version>
+ <type>${project.packaging}</type>
+ </artifactItem>
+ </artifactItems>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/cc5b4bcf/postgresql/src/main/java/org/apache/zeppelin/postgresql/PostgreSqlInterpreter.java
----------------------------------------------------------------------
diff --git
a/postgresql/src/main/java/org/apache/zeppelin/postgresql/PostgreSqlInterpreter.java
b/postgresql/src/main/java/org/apache/zeppelin/postgresql/PostgreSqlInterpreter.java
new file mode 100644
index 0000000..7ac18d1
--- /dev/null
+++
b/postgresql/src/main/java/org/apache/zeppelin/postgresql/PostgreSqlInterpreter.java
@@ -0,0 +1,278 @@
+/**
+ * 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.postgresql;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.zeppelin.interpreter.Interpreter;
+import org.apache.zeppelin.interpreter.InterpreterContext;
+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;
+import org.apache.zeppelin.scheduler.SchedulerFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * PostgreSQL interpreter for Zeppelin. This interpreter can also be used for
accessing HAWQ and
+ * GreenplumDB.
+ *
+ * <ul>
+ * <li>{@code postgresql.url} - JDBC URL to connect to.</li>
+ * <li>{@code postgresql.user} - JDBC user name..</li>
+ * <li>{@code postgresql.password} - JDBC password..</li>
+ * <li>{@code postgresql.driver.name} - JDBC driver name.</li>
+ * <li>{@code postgresql.max.result} - Max number of SQL result to
display.</li>
+ * </ul>
+ *
+ * <p>
+ * How to use: <br/>
+ * {@code %psql.sql} <br/>
+ * {@code
+ * SELECT store_id, count(*)
+ * FROM retail_demo.order_lineitems_pxf
+ * GROUP BY store_id;
+ * }
+ * </p>
+ */
+public class PostgreSqlInterpreter extends Interpreter {
+
+ private Logger logger = LoggerFactory.getLogger(PostgreSqlInterpreter.class);
+
+ private static final char WhITESPACE = ' ';
+ private static final char NEWLINE = '\n';
+ private static final char TAB = '\t';
+ private static final String TABLE_MAGIC_TAG = "%table ";
+ private static final String EXPLAIN_PREDICATE = "EXPLAIN ";
+ private static final String UPDATE_COUNT_HEADER = "Update Count";
+
+ static final String DEFAULT_JDBC_URL = "jdbc:postgresql://localhost:5432/";
+ static final String DEFAULT_JDBC_USER_PASSWORD = "";
+ static final String DEFAULT_JDBC_USER_NAME = "gpadmin";
+ static final String DEFAULT_JDBC_DRIVER_NAME = "org.postgresql.Driver";
+ static final String DEFAULT_MAX_RESULT = "1000";
+
+ static final String POSTGRESQL_SERVER_URL = "postgresql.url";
+ static final String POSTGRESQL_SERVER_USER = "postgresql.user";
+ static final String POSTGRESQL_SERVER_PASSWORD = "postgresql.password";
+ static final String POSTGRESQL_SERVER_DRIVER_NAME = "postgresql.driver.name";
+ static final String POSTGRESQL_SERVER_MAX_RESULT = "postgresql.max.result";
+
+ static {
+ Interpreter.register(
+ "sql",
+ "psql",
+ PostgreSqlInterpreter.class.getName(),
+ new InterpreterPropertyBuilder()
+ .add(POSTGRESQL_SERVER_URL, DEFAULT_JDBC_URL, "The URL for
PostgreSQL.")
+ .add(POSTGRESQL_SERVER_USER, DEFAULT_JDBC_USER_NAME, "The
PostgreSQL user name")
+ .add(POSTGRESQL_SERVER_PASSWORD, DEFAULT_JDBC_USER_PASSWORD,
+ "The PostgreSQL user password")
+ .add(POSTGRESQL_SERVER_DRIVER_NAME, DEFAULT_JDBC_DRIVER_NAME,
"JDBC Driver Name")
+ .add(POSTGRESQL_SERVER_MAX_RESULT, DEFAULT_MAX_RESULT,
+ "Max number of SQL result to display.").build());
+ }
+
+ private Connection jdbcConnection;
+ private Statement currentStatement;
+ private Exception exceptionOnConnect;
+ private int maxResult;
+
+ public PostgreSqlInterpreter(Properties property) {
+ super(property);
+ }
+
+ @Override
+ public void open() {
+
+ logger.info("Open psql connection!");
+
+ // Ensure that no previous connections are left open.
+ close();
+
+ try {
+
+ String driverName = getProperty(POSTGRESQL_SERVER_DRIVER_NAME);
+ String url = getProperty(POSTGRESQL_SERVER_URL);
+ String user = getProperty(POSTGRESQL_SERVER_USER);
+ String password = getProperty(POSTGRESQL_SERVER_PASSWORD);
+ maxResult = Integer.valueOf(getProperty(POSTGRESQL_SERVER_MAX_RESULT));
+
+ Class.forName(driverName);
+
+ jdbcConnection = DriverManager.getConnection(url, user, password);
+
+ exceptionOnConnect = null;
+ logger.info("Successfully created psql connection");
+
+ } catch (ClassNotFoundException | SQLException e) {
+ logger.error("Cannot open connection", e);
+ exceptionOnConnect = e;
+ }
+ }
+
+ @Override
+ public void close() {
+
+ logger.info("Close psql connection!");
+
+ try {
+ if (getJdbcConnection() != null) {
+ getJdbcConnection().close();
+ }
+ } catch (SQLException e) {
+ logger.error("Cannot close connection", e);
+ } finally {
+ exceptionOnConnect = null;
+ }
+ }
+
+ private InterpreterResult executeSql(String sql) {
+ try {
+
+ if (exceptionOnConnect != null) {
+ return new InterpreterResult(Code.ERROR,
exceptionOnConnect.getMessage());
+ }
+
+ currentStatement = getJdbcConnection().createStatement();
+
+ StringBuilder msg = null;
+ boolean isTableType = false;
+
+ if (StringUtils.containsIgnoreCase(sql, EXPLAIN_PREDICATE)) {
+ msg = new StringBuilder();
+ } else {
+ msg = new StringBuilder(TABLE_MAGIC_TAG);
+ isTableType = true;
+ }
+
+ ResultSet resultSet = null;
+ try {
+
+ boolean isResultSetAvailable = currentStatement.execute(sql);
+
+ if (isResultSetAvailable) {
+ resultSet = currentStatement.getResultSet();
+
+ ResultSetMetaData md = resultSet.getMetaData();
+
+ for (int i = 1; i < md.getColumnCount() + 1; i++) {
+ if (i > 1) {
+ msg.append(TAB);
+ }
+ msg.append(replaceReservedChars(isTableType, md.getColumnName(i)));
+ }
+ msg.append(NEWLINE);
+
+ int displayRowCount = 0;
+ while (resultSet.next() && displayRowCount < getMaxResult()) {
+ for (int i = 1; i < md.getColumnCount() + 1; i++) {
+ msg.append(replaceReservedChars(isTableType,
resultSet.getString(i)));
+ if (i != md.getColumnCount()) {
+ msg.append(TAB);
+ }
+ }
+ msg.append(NEWLINE);
+ displayRowCount++;
+ }
+ } else {
+ // Response contains either an update count or there are no results.
+ int updateCount = currentStatement.getUpdateCount();
+ msg.append(UPDATE_COUNT_HEADER).append(NEWLINE);
+ msg.append(updateCount).append(NEWLINE);
+ }
+ } finally {
+ try {
+ if (resultSet != null) {
+ resultSet.close();
+ }
+ currentStatement.close();
+ } finally {
+ currentStatement = null;
+ }
+ }
+
+ return new InterpreterResult(Code.SUCCESS, msg.toString());
+
+ } catch (SQLException ex) {
+ logger.error("Cannot run " + sql, ex);
+ return new InterpreterResult(Code.ERROR, ex.getMessage());
+ }
+ }
+
+ /**
+ * For %table response replace Tab and Newline characters from the content.
+ */
+ private String replaceReservedChars(boolean isTableResponseType, String str)
{
+ return (!isTableResponseType) ? str : str.replace(TAB,
WhITESPACE).replace(NEWLINE, WhITESPACE);
+ }
+
+ @Override
+ public InterpreterResult interpret(String cmd, InterpreterContext
contextInterpreter) {
+ logger.info("Run SQL command '{}'", cmd);
+ return executeSql(cmd);
+ }
+
+ @Override
+ public void cancel(InterpreterContext context) {
+ if (currentStatement != null) {
+ try {
+ currentStatement.cancel();
+ } catch (SQLException ex) {
+ } finally {
+ currentStatement = null;
+ }
+ }
+ }
+
+ @Override
+ public FormType getFormType() {
+ return FormType.SIMPLE;
+ }
+
+ @Override
+ public int getProgress(InterpreterContext context) {
+ return 0;
+ }
+
+ @Override
+ public Scheduler getScheduler() {
+ return SchedulerFactory.singleton().createOrGetFIFOScheduler(
+ PostgreSqlInterpreter.class.getName() + this.hashCode());
+ }
+
+ @Override
+ public List<String> completion(String buf, int cursor) {
+ return null;
+ }
+
+ public int getMaxResult() {
+ return maxResult;
+ }
+
+ // Test only method
+ protected Connection getJdbcConnection() {
+ return jdbcConnection;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/cc5b4bcf/postgresql/src/test/java/org/apache/zeppelin/postgresql/PostgreSqlInterpreterTest.java
----------------------------------------------------------------------
diff --git
a/postgresql/src/test/java/org/apache/zeppelin/postgresql/PostgreSqlInterpreterTest.java
b/postgresql/src/test/java/org/apache/zeppelin/postgresql/PostgreSqlInterpreterTest.java
new file mode 100644
index 0000000..df793a6
--- /dev/null
+++
b/postgresql/src/test/java/org/apache/zeppelin/postgresql/PostgreSqlInterpreterTest.java
@@ -0,0 +1,224 @@
+/**
+ * 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.postgresql;
+
+import static org.apache.zeppelin.postgresql.PostgreSqlInterpreter.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.times;
+
+import java.sql.SQLException;
+import java.util.Properties;
+
+import org.apache.zeppelin.interpreter.InterpreterResult;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Matchers;
+import org.mockito.Mockito;
+
+import com.mockrunner.jdbc.BasicJDBCTestCaseAdapter;
+import com.mockrunner.jdbc.StatementResultSetHandler;
+import com.mockrunner.mock.jdbc.MockConnection;
+import com.mockrunner.mock.jdbc.MockResultSet;
+
+/**
+ * PostgreSQL interpreter unit tests
+ */
+public class PostgreSqlInterpreterTest extends BasicJDBCTestCaseAdapter {
+
+ private PostgreSqlInterpreter psqlInterpreter = null;
+ private MockResultSet result = null;
+
+ @Before
+ public void beforeTest() {
+ MockConnection connection = getJDBCMockObjectFactory().getMockConnection();
+
+ StatementResultSetHandler statementHandler =
connection.getStatementResultSetHandler();
+ result = statementHandler.createResultSet();
+ statementHandler.prepareGlobalResultSet(result);
+
+ Properties properties = new Properties();
+ properties.put(POSTGRESQL_SERVER_DRIVER_NAME, DEFAULT_JDBC_DRIVER_NAME);
+ properties.put(POSTGRESQL_SERVER_URL, DEFAULT_JDBC_URL);
+ properties.put(POSTGRESQL_SERVER_USER, DEFAULT_JDBC_USER_NAME);
+ properties.put(POSTGRESQL_SERVER_PASSWORD, DEFAULT_JDBC_USER_PASSWORD);
+ properties.put(POSTGRESQL_SERVER_MAX_RESULT, DEFAULT_MAX_RESULT);
+
+ psqlInterpreter = spy(new PostgreSqlInterpreter(properties));
+ when(psqlInterpreter.getJdbcConnection()).thenReturn(connection);
+ }
+
+ @Test
+ public void testOpenCommandIndempotency() throws SQLException {
+ // Ensure that an attempt to open new connection will clean any remaining
connections
+ psqlInterpreter.open();
+ psqlInterpreter.open();
+ psqlInterpreter.open();
+
+ verify(psqlInterpreter, times(3)).open();
+ verify(psqlInterpreter, times(3)).close();
+ }
+
+ @Test
+ public void testDefaultProperties() throws SQLException {
+
+ PostgreSqlInterpreter psqlInterpreter = new PostgreSqlInterpreter(new
Properties());
+
+ assertEquals(DEFAULT_JDBC_DRIVER_NAME,
+ psqlInterpreter.getProperty(POSTGRESQL_SERVER_DRIVER_NAME));
+ assertEquals(DEFAULT_JDBC_URL,
psqlInterpreter.getProperty(POSTGRESQL_SERVER_URL));
+ assertEquals(DEFAULT_JDBC_USER_NAME,
psqlInterpreter.getProperty(POSTGRESQL_SERVER_USER));
+ assertEquals(DEFAULT_JDBC_USER_PASSWORD,
+ psqlInterpreter.getProperty(POSTGRESQL_SERVER_PASSWORD));
+ assertEquals(DEFAULT_MAX_RESULT,
psqlInterpreter.getProperty(POSTGRESQL_SERVER_MAX_RESULT));
+ }
+
+ @Test
+ public void testConnectionClose() throws SQLException {
+
+ PostgreSqlInterpreter psqlInterpreter = spy(new PostgreSqlInterpreter(new
Properties()));
+
+ when(psqlInterpreter.getJdbcConnection()).thenReturn(
+ getJDBCMockObjectFactory().getMockConnection());
+
+ psqlInterpreter.close();
+
+ verifyAllResultSetsClosed();
+ verifyAllStatementsClosed();
+ verifyConnectionClosed();
+ }
+
+ @Test
+ public void testStatementCancel() throws SQLException {
+
+ PostgreSqlInterpreter psqlInterpreter = spy(new PostgreSqlInterpreter(new
Properties()));
+
+ when(psqlInterpreter.getJdbcConnection()).thenReturn(
+ getJDBCMockObjectFactory().getMockConnection());
+
+ psqlInterpreter.cancel(null);
+
+ verifyAllResultSetsClosed();
+ verifyAllStatementsClosed();
+ assertFalse("Cancel operation should not close the connection",
psqlInterpreter
+ .getJdbcConnection().isClosed());
+ }
+
+ @Test
+ public void testSelectQuery() throws SQLException {
+
+ when(psqlInterpreter.getMaxResult()).thenReturn(1000);
+
+ String sqlQuery = "select * from t";
+
+ result.addColumn("col1", new String[] {"val11", "val12"});
+ result.addColumn("col2", new String[] {"val21", "val22"});
+
+ InterpreterResult interpreterResult = psqlInterpreter.interpret(sqlQuery,
null);
+
+ assertEquals(InterpreterResult.Code.SUCCESS, interpreterResult.code());
+ assertEquals(InterpreterResult.Type.TABLE, interpreterResult.type());
+ assertEquals("col1\tcol2\nval11\tval21\nval12\tval22\n",
interpreterResult.message());
+
+ verifySQLStatementExecuted(sqlQuery);
+ verifyAllResultSetsClosed();
+ verifyAllStatementsClosed();
+ }
+
+ @Test
+ public void testSelectQueryMaxResult() throws SQLException {
+
+ when(psqlInterpreter.getMaxResult()).thenReturn(1);
+
+ String sqlQuery = "select * from t";
+
+ result.addColumn("col1", new String[] {"val11", "val12"});
+ result.addColumn("col2", new String[] {"val21", "val22"});
+
+ InterpreterResult interpreterResult = psqlInterpreter.interpret(sqlQuery,
null);
+
+ assertEquals(InterpreterResult.Code.SUCCESS, interpreterResult.code());
+ assertEquals(InterpreterResult.Type.TABLE, interpreterResult.type());
+ assertEquals("col1\tcol2\nval11\tval21\n", interpreterResult.message());
+
+ verifySQLStatementExecuted(sqlQuery);
+ verifyAllResultSetsClosed();
+ verifyAllStatementsClosed();
+ }
+
+ @Test
+ public void testSelectQueryWithSpecialCharacters() throws SQLException {
+
+ when(psqlInterpreter.getMaxResult()).thenReturn(1000);
+
+ String sqlQuery = "select * from t";
+
+ result.addColumn("co\tl1", new String[] {"val11", "va\tl1\n2"});
+ result.addColumn("co\nl2", new String[] {"v\nal21", "val\t22"});
+
+ InterpreterResult interpreterResult = psqlInterpreter.interpret(sqlQuery,
null);
+
+ assertEquals(InterpreterResult.Code.SUCCESS, interpreterResult.code());
+ assertEquals(InterpreterResult.Type.TABLE, interpreterResult.type());
+ assertEquals("co l1\tco l2\nval11\tv al21\nva l1 2\tval 22\n",
interpreterResult.message());
+
+ verifySQLStatementExecuted(sqlQuery);
+ verifyAllResultSetsClosed();
+ verifyAllStatementsClosed();
+ }
+
+ @Test
+ public void testExplainQuery() throws SQLException {
+
+ when(psqlInterpreter.getMaxResult()).thenReturn(1000);
+
+ String sqlQuery = "explain select * from t";
+
+ result.addColumn("col1", new String[] {"val11", "val12"});
+
+ InterpreterResult interpreterResult = psqlInterpreter.interpret(sqlQuery,
null);
+
+ assertEquals(InterpreterResult.Code.SUCCESS, interpreterResult.code());
+ assertEquals(InterpreterResult.Type.TEXT, interpreterResult.type());
+ assertEquals("col1\nval11\nval12\n", interpreterResult.message());
+
+ verifySQLStatementExecuted(sqlQuery);
+ verifyAllResultSetsClosed();
+ verifyAllStatementsClosed();
+ }
+
+ @Test
+ public void testExplainQueryWithSpecialCharachters() throws SQLException {
+
+ when(psqlInterpreter.getMaxResult()).thenReturn(1000);
+
+ String sqlQuery = "explain select * from t";
+
+ result.addColumn("co\tl\n1", new String[] {"va\nl11", "va\tl\n12"});
+
+ InterpreterResult interpreterResult = psqlInterpreter.interpret(sqlQuery,
null);
+
+ assertEquals(InterpreterResult.Code.SUCCESS, interpreterResult.code());
+ assertEquals(InterpreterResult.Type.TEXT, interpreterResult.type());
+ assertEquals("co\tl\n1\nva\nl11\nva\tl\n12\n",
interpreterResult.message());
+
+ verifySQLStatementExecuted(sqlQuery);
+ verifyAllResultSetsClosed();
+ verifyAllStatementsClosed();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/cc5b4bcf/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java
----------------------------------------------------------------------
diff --git
a/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java
b/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java
index e25d3c0..0d2eb58 100644
---
a/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java
+++
b/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java
@@ -395,7 +395,8 @@ public class ZeppelinConfiguration extends XMLConfiguration
{
+ "org.apache.zeppelin.ignite.IgniteSqlInterpreter,"
+ "org.apache.zeppelin.lens.LensInterpreter,"
+ "org.apache.zeppelin.cassandra.CassandraInterpreter,"
- + "org.apache.zeppelin.geode.GeodeOqlInterpreter"),
+ + "org.apache.zeppelin.geode.GeodeOqlInterpreter,"
+ + "org.apache.zeppelin.postgresql.PostgreSqlInterpreter"),
ZEPPELIN_INTERPRETER_DIR("zeppelin.interpreter.dir", "interpreter"),
ZEPPELIN_INTERPRETER_CONNECT_TIMEOUT("zeppelin.interpreter.connect.timeout",
30000),
ZEPPELIN_ENCODING("zeppelin.encoding", "UTF-8"),