Repository: incubator-zeppelin Updated Branches: refs/heads/master cbef1be48 -> 7ba743eec
ZEPPELIN-186: Apache Phoenix interpreter Include an interpreter for Apache Phoenix Almost identical to Hive interpreter. Could/should generic JDBC interpreters be combined in future work? Author: Randy Gelhausen <[email protected]> Author: = <[email protected]> Closes #167 from randerzander/phoenix-interpreter and squashes the following commits: 72b5c71 [=] bumped HBase version down to 1.0 bbaadc3 [=] Switched to Apache artifacts 0a3f8cf [=] Merge branch 'master' of github.com:apache/incubator-zeppelin into phoenix-interpreter 060fa44 [Randy Gelhausen] Moved currentStatement and resultSet to finally, added commit call to finally 531b7b4 [Randy Gelhausen] Moved result set close to finally block df407ec [Randy Gelhausen] Made Update output more clear 8282cc2 [Randy Gelhausen] fixed handling of null values in PhoenixInterpreter b69e7aa [Randy Gelhausen] cleaned up PhoenixInterpreter and tests, switched to execute from executeQuery b7ad803 [Randy Gelhausen] Merge branch 'master' of github.com:apache/incubator-zeppelin into phoenix-interpreter 1b24d47 [Randy Gelhausen] Merge branch 'master' of github.com:apache/incubator-zeppelin into phoenix-interpreter c99d66d [Randy Gelhausen] added safety call to close() before open, added maxResult check 8c405a8 [Randy Gelhausen] Fixed pom for phoenix jdbc driver dependency inclusion, updated interpreter properties 0f8e18b [Randy Gelhausen] fixed old reference to ZK_URL 6e816bc [Randy Gelhausen] Updated zeppelin-site.xml.template to include PhoenixInterpreter, changed Phoenix properties from zk.url to phoenix.url d7716cc [Randy Gelhausen] fixed hive.PhoenixInterpreter to phoenix.PhoenixInterpreter, set phoenix magic to pql 1d568ed [Randy Gelhausen] Added Phoenix to ZeppelinConfiguration.java 96db8b5 [Randy Gelhausen] fixing default phoenix.url 2f3fb54 [Randy Gelhausen] initial commit for phoenix 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/7ba743ee Tree: http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/tree/7ba743ee Diff: http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/diff/7ba743ee Branch: refs/heads/master Commit: 7ba743eec77819397460c91b6fa49dfa4604c5de Parents: cbef1be Author: Randy Gelhausen <[email protected]> Authored: Wed Aug 19 18:49:44 2015 -0400 Committer: Lee moon soo <[email protected]> Committed: Sun Aug 23 19:43:16 2015 -0700 ---------------------------------------------------------------------- conf/zeppelin-site.xml.template | 2 +- phoenix/pom.xml | 143 +++++++++++ .../zeppelin/phoenix/PhoenixInterpreter.java | 239 +++++++++++++++++++ .../phoenix/PhoenixInterpreterTest.java | 226 ++++++++++++++++++ pom.xml | 1 + .../zeppelin/conf/ZeppelinConfiguration.java | 1 + 6 files changed, 611 insertions(+), 1 deletion(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/7ba743ee/conf/zeppelin-site.xml.template ---------------------------------------------------------------------- diff --git a/conf/zeppelin-site.xml.template b/conf/zeppelin-site.xml.template index 5d3b97d..ad90f88 100644 --- a/conf/zeppelin-site.xml.template +++ b/conf/zeppelin-site.xml.template @@ -85,7 +85,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,org.apache.zeppelin.postgresql.PostgreSqlInterpreter</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,org.apache.zeppelin.phoenix.PhoenixInterpreter</value> <description>Comma separated interpreter configurations. First interpreter become a default</description> </property> http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/7ba743ee/phoenix/pom.xml ---------------------------------------------------------------------- diff --git a/phoenix/pom.xml b/phoenix/pom.xml new file mode 100644 index 0000000..3cca32d --- /dev/null +++ b/phoenix/pom.xml @@ -0,0 +1,143 @@ +<?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/xsd/maven-4.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-phoenix</artifactId> + <packaging>jar</packaging> + <version>0.6.0-incubating-SNAPSHOT</version> + <name>Zeppelin: Apache Phoenix Interpreter</name> + <description>Zeppelin interprter for Apache Phoenix</description> + <url>http://zeppelin.incubator.apache.org</url> + + <properties> + <phoenix.version>4.4.0-HBase-1.0</phoenix.version> + </properties> + + <dependencies> + <dependency> + <groupId>org.apache.zeppelin</groupId> + <artifactId>zeppelin-interpreter</artifactId> + <version>${project.version}</version> + <scope>provided</scope> + </dependency> + + <dependency> + <groupId>org.apache.phoenix</groupId> + <artifactId>phoenix-core</artifactId> + <version>${phoenix.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/phoenix</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/phoenix</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/7ba743ee/phoenix/src/main/java/org/apache/zeppelin/phoenix/PhoenixInterpreter.java ---------------------------------------------------------------------- diff --git a/phoenix/src/main/java/org/apache/zeppelin/phoenix/PhoenixInterpreter.java b/phoenix/src/main/java/org/apache/zeppelin/phoenix/PhoenixInterpreter.java new file mode 100644 index 0000000..c90aece --- /dev/null +++ b/phoenix/src/main/java/org/apache/zeppelin/phoenix/PhoenixInterpreter.java @@ -0,0 +1,239 @@ +/** + * 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.phoenix; + +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; + +/** + * Phoenix interpreter for Zeppelin. + */ +public class PhoenixInterpreter extends Interpreter { + Logger logger = LoggerFactory.getLogger(PhoenixInterpreter.class); + + private static final String EXPLAIN_PREDICATE = "EXPLAIN "; + private static final String UPDATE_HEADER = "UPDATES "; + + private static final String WS = " "; + private static final String NEWLINE = "\n"; + private static final String TAB = "\t"; + private static final String TABLE_MAGIC_TAG = "%table "; + + static final String PHOENIX_JDBC_URL = "phoenix.jdbc.url"; + static final String PHOENIX_JDBC_USER = "phoenix.user"; + static final String PHOENIX_JDBC_PASSWORD = "phoenix.password"; + static final String PHOENIX_MAX_RESULT = "phoenix.max.result"; + static final String PHOENIX_JDBC_DRIVER_NAME = "phoenix.driver.name"; + + static final String DEFAULT_JDBC_URL = "jdbc:phoenix:localhost:2181:/hbase-unsecure"; + static final String DEFAULT_JDBC_USER = ""; + static final String DEFAULT_JDBC_PASSWORD = ""; + static final String DEFAULT_MAX_RESULT = "1000"; + static final String DEFAULT_JDBC_DRIVER_NAME = "org.apache.phoenix.jdbc.PhoenixDriver"; + + private Connection jdbcConnection; + private Statement currentStatement; + private Exception exceptionOnConnect; + private int maxResult; + + static { + Interpreter.register( + "sql", + "phoenix", + PhoenixInterpreter.class.getName(), + new InterpreterPropertyBuilder() + .add(PHOENIX_JDBC_URL, DEFAULT_JDBC_URL, "Phoenix JDBC connection string") + .add(PHOENIX_JDBC_USER, DEFAULT_JDBC_USER, "The Phoenix user") + .add(PHOENIX_JDBC_PASSWORD, DEFAULT_JDBC_PASSWORD, "The password for the Phoenix user") + .add(PHOENIX_MAX_RESULT, DEFAULT_MAX_RESULT, "Max number of SQL results to display.") + .add(PHOENIX_JDBC_DRIVER_NAME, DEFAULT_JDBC_DRIVER_NAME, "Phoenix Driver classname.") + .build() + ); + } + + public PhoenixInterpreter(Properties property) { + super(property); + } + + @Override + public void open() { + logger.info("Jdbc open connection called!"); + close(); + + try { + Class.forName(getProperty(PHOENIX_JDBC_DRIVER_NAME)); + + maxResult = Integer.valueOf(getProperty(PHOENIX_MAX_RESULT)); + jdbcConnection = DriverManager.getConnection( + getProperty(PHOENIX_JDBC_URL), + getProperty(PHOENIX_JDBC_USER), + getProperty(PHOENIX_JDBC_PASSWORD) + ); + exceptionOnConnect = null; + logger.info("Successfully created Jdbc connection"); + } + catch (ClassNotFoundException | SQLException e) { + logger.error("Cannot open connection", e); + exceptionOnConnect = e; + } + } + + @Override + public void close() { + logger.info("Jdbc close connection called!"); + + try { + if (getJdbcConnection() != null) { + getJdbcConnection().close(); + } + } catch (SQLException e) { + logger.error("Cannot close connection", e); + } + finally { + exceptionOnConnect = null; + } + } + + private String clean(boolean isExplain, String str){ + return (isExplain || str == null) ? str : str.replace(TAB, WS).replace(NEWLINE, WS); + } + + private InterpreterResult executeSql(String sql) { + try { + if (exceptionOnConnect != null) { + return new InterpreterResult(Code.ERROR, exceptionOnConnect.getMessage()); + } + + currentStatement = getJdbcConnection().createStatement(); + + boolean isExplain = StringUtils.containsIgnoreCase(sql, EXPLAIN_PREDICATE); + StringBuilder msg = (isExplain) ? new StringBuilder() : new StringBuilder(TABLE_MAGIC_TAG); + + ResultSet res = null; + try { + boolean hasResult = currentStatement.execute(sql); + if (hasResult){ //If query had results + res = currentStatement.getResultSet(); + //Append column names + ResultSetMetaData md = res.getMetaData(); + String row = clean(isExplain, md.getColumnName(1)); + for (int i = 2; i < md.getColumnCount() + 1; i++) + row += TAB + clean(isExplain, md.getColumnName(i)); + msg.append(row + NEWLINE); + + //Append rows + int rowCount = 0; + while (res.next() && rowCount < getMaxResult()) { + row = clean(isExplain, res.getString(1)); + for (int i = 2; i < md.getColumnCount() + 1; i++) + row += TAB + clean(isExplain, res.getString(i)); + msg.append(row + NEWLINE); + rowCount++; + } + } + else { // May have been upsert or DDL + msg.append(UPDATE_HEADER + NEWLINE + + "Rows affected: " + currentStatement.getUpdateCount() + + NEWLINE); + } + + } finally { + try { + if (res != null) res.close(); + getJdbcConnection().commit(); + currentStatement.close(); + } finally { + currentStatement = null; + } + } + + return new InterpreterResult(Code.SUCCESS, msg.toString()); + } + catch (SQLException ex) { + logger.error("Can not run " + sql, ex); + return new InterpreterResult(Code.ERROR, ex.getMessage()); + } + } + + @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( + PhoenixInterpreter.class.getName() + this.hashCode()); + } + + @Override + public List<String> completion(String buf, int cursor) { + return null; + } + + public Connection getJdbcConnection() { + return jdbcConnection; + } + + public int getMaxResult() { + return maxResult; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/7ba743ee/phoenix/src/test/java/org/apache/zeppelin/phoenix/PhoenixInterpreterTest.java ---------------------------------------------------------------------- diff --git a/phoenix/src/test/java/org/apache/zeppelin/phoenix/PhoenixInterpreterTest.java b/phoenix/src/test/java/org/apache/zeppelin/phoenix/PhoenixInterpreterTest.java new file mode 100644 index 0000000..9270d4b --- /dev/null +++ b/phoenix/src/test/java/org/apache/zeppelin/phoenix/PhoenixInterpreterTest.java @@ -0,0 +1,226 @@ +/** + * 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.phoenix; + +import static org.apache.zeppelin.phoenix.PhoenixInterpreter.*; +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; + +/** + * Phoenix interpreter unit tests + */ +public class PhoenixInterpreterTest extends BasicJDBCTestCaseAdapter { + private PhoenixInterpreter phoenixInterpreter = 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(PHOENIX_JDBC_DRIVER_NAME, DEFAULT_JDBC_DRIVER_NAME); + properties.put(PHOENIX_JDBC_URL, DEFAULT_JDBC_URL); + properties.put(PHOENIX_JDBC_USER, DEFAULT_JDBC_USER); + properties.put(PHOENIX_JDBC_PASSWORD, DEFAULT_JDBC_PASSWORD); + properties.put(PHOENIX_MAX_RESULT, DEFAULT_MAX_RESULT); + + phoenixInterpreter = spy(new PhoenixInterpreter(properties)); + when(phoenixInterpreter.getJdbcConnection()).thenReturn(connection); + } + + @Test + public void testOpenCommandIdempotency() throws SQLException { + // Ensure that an attempt to open new connection will clean any remaining connections + phoenixInterpreter.open(); + phoenixInterpreter.open(); + phoenixInterpreter.open(); + + verify(phoenixInterpreter, times(3)).open(); + verify(phoenixInterpreter, times(3)).close(); + } + + @Test + public void testDefaultProperties() throws SQLException { + + PhoenixInterpreter phoenixInterpreter = new PhoenixInterpreter(new Properties()); + + assertEquals(DEFAULT_JDBC_DRIVER_NAME, + phoenixInterpreter.getProperty(PHOENIX_JDBC_DRIVER_NAME)); + assertEquals(DEFAULT_JDBC_URL, phoenixInterpreter.getProperty(PHOENIX_JDBC_URL)); + assertEquals(DEFAULT_JDBC_USER, phoenixInterpreter.getProperty(PHOENIX_JDBC_USER)); + assertEquals(DEFAULT_JDBC_PASSWORD, + phoenixInterpreter.getProperty(PHOENIX_JDBC_PASSWORD)); + assertEquals(DEFAULT_MAX_RESULT, phoenixInterpreter.getProperty(PHOENIX_MAX_RESULT)); + } + + @Test + public void testConnectionClose() throws SQLException { + + PhoenixInterpreter phoenixInterpreter = spy(new PhoenixInterpreter(new Properties())); + + when(phoenixInterpreter.getJdbcConnection()).thenReturn( + getJDBCMockObjectFactory().getMockConnection()); + + phoenixInterpreter.close(); + + verifyAllResultSetsClosed(); + verifyAllStatementsClosed(); + verifyConnectionClosed(); + } + + @Test + public void testStatementCancel() throws SQLException { + + PhoenixInterpreter phoenixInterpreter = spy(new PhoenixInterpreter(new Properties())); + + when(phoenixInterpreter.getJdbcConnection()).thenReturn( + getJDBCMockObjectFactory().getMockConnection()); + + phoenixInterpreter.cancel(null); + + verifyAllResultSetsClosed(); + verifyAllStatementsClosed(); + assertFalse("Cancel operation should not close the connection", phoenixInterpreter + .getJdbcConnection().isClosed()); + } + + @Test + public void testSelectQuery() throws SQLException { + + when(phoenixInterpreter.getMaxResult()).thenReturn(1000); + + String sqlQuery = "select * from t"; + + result.addColumn("col1", new String[] {"val11", "val12"}); + result.addColumn("col2", new String[] {"val21", "val22"}); + + InterpreterResult interpreterResult = phoenixInterpreter.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(phoenixInterpreter.getMaxResult()).thenReturn(1); + + String sqlQuery = "select * from t"; + + result.addColumn("col1", new String[] {"val11", "val12"}); + result.addColumn("col2", new String[] {"val21", "val22"}); + + InterpreterResult interpreterResult = phoenixInterpreter.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(phoenixInterpreter.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 = phoenixInterpreter.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(phoenixInterpreter.getMaxResult()).thenReturn(1000); + + String sqlQuery = "explain select * from t"; + + result.addColumn("col1", new String[] {"val11", "val12"}); + + InterpreterResult interpreterResult = phoenixInterpreter.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(phoenixInterpreter.getMaxResult()).thenReturn(1000); + + String sqlQuery = "explain select * from t"; + + result.addColumn("co\tl\n1", new String[] {"va\nl11", "va\tl\n12"}); + + InterpreterResult interpreterResult = phoenixInterpreter.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/7ba743ee/pom.xml ---------------------------------------------------------------------- diff --git a/pom.xml b/pom.xml index 1c67f47..79546a9 100644 --- a/pom.xml +++ b/pom.xml @@ -91,6 +91,7 @@ <module>angular</module> <module>shell</module> <module>hive</module> + <module>phoenix</module> <module>geode</module> <module>postgresql</module> <module>tajo</module> http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/7ba743ee/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 2fdaee1..363b154 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 @@ -402,6 +402,7 @@ public class ZeppelinConfiguration extends XMLConfiguration { + "org.apache.zeppelin.angular.AngularInterpreter," + "org.apache.zeppelin.shell.ShellInterpreter," + "org.apache.zeppelin.hive.HiveInterpreter," + + "org.apache.zeppelin.phoenix.PhoenixInterpreter," + "org.apache.zeppelin.tajo.TajoInterpreter," + "org.apache.zeppelin.flink.FlinkInterpreter," + "org.apache.zeppelin.ignite.IgniteInterpreter,"
