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 407e81ea2a9..63b2b31444c 100644 --- a/jdbc/src/main/java/org/apache/zeppelin/jdbc/JDBCInterpreter.java +++ b/jdbc/src/main/java/org/apache/zeppelin/jdbc/JDBCInterpreter.java @@ -109,6 +109,10 @@ public class JDBCInterpreter extends Interpreter { private final String CONCURRENT_EXECUTION_COUNT = "zeppelin.jdbc.concurrent.max_connection"; private final String DBCP_STRING = "jdbc:apache:commons:dbcp:"; + private static final String[] TABLE_TYPES = { "TABLE", "VIEW", "SYSTEM TABLE", + "GLOBAL TEMPORARY", "LOCAL TEMPORARY", "ALIAS", "SYNONYM" }; + private static final String METADATA_KEYWORD = "explore"; + private final HashMap basePropretiesMap; private final HashMap jdbcUserConfigurationsMap; private final Map propertyKeySqlCompleterMap; @@ -512,6 +516,67 @@ private InterpreterResult executeSql(String propertyKey, String sql, } } + private InterpreterResult getMetaData(String propertyKey, + String cmd, InterpreterContext interpreterContext) { + Connection connection; + String user = interpreterContext.getAuthenticationInfo().getUser(); + DatabaseMetaData dataBaseMetaData; + ResultSet resultSet = null; + String tableName = null; + String results; + + if (cmd.split(" +").length > 1) { + tableName = cmd.split(" +")[1]; + } + + try { + connection = getConnection(propertyKey, interpreterContext); + + if (connection == null) { + return new InterpreterResult(Code.ERROR, "Prefix not found."); + } + + try { + dataBaseMetaData = connection.getMetaData(); + if (tableName == null) { + // if a table name is supplied get table metadata + resultSet = dataBaseMetaData.getTables(null, null, "%", TABLE_TYPES); + } else { + // if not, get database metadata + resultSet = dataBaseMetaData.getColumns(null, null, tableName, null); + } + results = getResults(resultSet, true); + } finally { + if (resultSet != null) { + try { + resultSet.close(); + } catch (SQLException e) { /*ignored*/ } + } + if (connection != null) { + try { + connection.close(); + } catch (SQLException e) { /*ignored*/ } + } + } + return new InterpreterResult(Code.SUCCESS, results); + + } catch (Exception e) { + logger.error("Cannot fetch metadata.", e); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + PrintStream ps = new PrintStream(baos); + e.printStackTrace(ps); + String errorMsg = new String(baos.toByteArray(), StandardCharsets.UTF_8); + + try { + closeDBPool(user, propertyKey); + } catch (SQLException e1) { + e1.printStackTrace(); + } + + return new InterpreterResult(Code.ERROR, errorMsg); + } + } + /** * For %table response replace Tab and Newline characters from the content. */ @@ -524,7 +589,7 @@ private String replaceReservedChars(String str) { @Override public InterpreterResult interpret(String cmd, InterpreterContext contextInterpreter) { - logger.info("Run SQL command '{}'", cmd); + logger.info("Run Interpreter command '{}'", cmd); String propertyKey = getPropertyKey(cmd); if (null != propertyKey && !propertyKey.equals(DEFAULT_KEY)) { @@ -533,8 +598,15 @@ public InterpreterResult interpret(String cmd, InterpreterContext contextInterpr cmd = cmd.trim(); - logger.info("PropertyKey: {}, SQL command: '{}'", propertyKey, cmd); - return executeSql(propertyKey, cmd, contextInterpreter); + if (cmd.split(" ")[0].toLowerCase().equals(METADATA_KEYWORD)) { + // if the command starts with the METADATA_KEYWORD, call getMetaData + logger.info("PropertyKey: {}, MetaData command: '{}'", propertyKey, cmd); + return getMetaData(propertyKey, cmd, contextInterpreter); + } else { + // otherwise all executeSql + logger.info("PropertyKey: {}, SQL command: '{}'", propertyKey, cmd); + return executeSql(propertyKey, cmd, contextInterpreter); + } } @Override diff --git a/jdbc/src/test/java/org/apache/zeppelin/jdbc/JDBCInterpreterTest.java b/jdbc/src/test/java/org/apache/zeppelin/jdbc/JDBCInterpreterTest.java index 18bda72f8d6..b066c9de1c5 100644 --- a/jdbc/src/test/java/org/apache/zeppelin/jdbc/JDBCInterpreterTest.java +++ b/jdbc/src/test/java/org/apache/zeppelin/jdbc/JDBCInterpreterTest.java @@ -215,6 +215,67 @@ public void testSelectQueryMaxResult() throws SQLException, IOException { assertEquals("ID\tNAME\na\ta_name\n", interpreterResult.message().get(0).getData()); } + @Test + public void testConnectionMetaData() throws SQLException, IOException { + + Properties properties = new Properties(); + properties.setProperty("common.max_count", "1"); + properties.setProperty("common.max_retry", "3"); + properties.setProperty("default.driver", "org.h2.Driver"); + properties.setProperty("default.url", getJdbcConnection()); + properties.setProperty("default.user", ""); + properties.setProperty("default.password", ""); + JDBCInterpreter t = new JDBCInterpreter(properties); + t.open(); + + String command = "explore"; + + InterpreterResult interpreterResult = t.interpret(command, interpreterContext); + + assertEquals(InterpreterResult.Code.SUCCESS, interpreterResult.code()); + assertEquals(InterpreterResult.Type.TABLE, interpreterResult.message().get(0).getType()); + + String testOutput = interpreterResult.message().get(0).getData().replaceAll("H2-TEST-([0-9]+)", + "H2-TEST-1"); + + assertEquals("TABLE_CATALOG\tTABLE_SCHEMA\tTABLE_NAME\tTABLE_TYPE\tREMARKS\tTYPE_NAME\tTYPE_NAME\t" + + "TYPE_NAME\tTYPE_NAME\tTYPE_NAME\tSQL\nH2-TEST-1\tINFORMATION_SCHEMA\t" + + "CATALOGS\tSYSTEM TABLE\t\tnull\tnull\tnull\tnull\tnull\tnull\n", testOutput); + } + + @Test + public void testTableMetaData() throws SQLException, IOException { + + Properties properties = new Properties(); + properties.setProperty("common.max_count", "1"); + properties.setProperty("common.max_retry", "3"); + properties.setProperty("default.driver", "org.h2.Driver"); + properties.setProperty("default.url", getJdbcConnection()); + properties.setProperty("default.user", ""); + properties.setProperty("default.password", ""); + JDBCInterpreter t = new JDBCInterpreter(properties); + t.open(); + + String command = "explore TEST_TABLE"; + + InterpreterResult interpreterResult = t.interpret(command, interpreterContext); + + assertEquals(InterpreterResult.Code.SUCCESS, interpreterResult.code()); + assertEquals(InterpreterResult.Type.TABLE, interpreterResult.message().get(0).getType()); + + System.out.println("Printed:\n" + interpreterResult.message().get(0).getData()); + String testOutput = interpreterResult.message().get(0).getData().replaceAll("H2-TEST-([0-9]+)", + "H2-TEST-1"); + + assertEquals("TABLE_CATALOG\tTABLE_SCHEMA\tTABLE_NAME\tCOLUMN_NAME\tDATA_TYPE\tTYPE_NAME\t" + + "CHARACTER_MAXIMUM_LENGTH\tCHARACTER_MAXIMUM_LENGTH\tNUMERIC_SCALE\tNUMERIC_PRECISION_RADIX" + + "\tNULLABLE\tREMARKS\tCOLUMN_DEFAULT\tDATA_TYPE\tSQL_DATETIME_SUB\tCHARACTER_OCTET_LENGTH\t" + + "ORDINAL_POSITION\tIS_NULLABLE\tSCOPE_CATALOG\tSCOPE_SCHEMA\tSCOPE_TABLE\tSOURCE_DATA_TYPE\t" + + "IS_AUTOINCREMENT\tSCOPE_CATLOG\nH2-TEST-1\tPUBLIC\tTEST_TABLE\tID\t12\t" + + "VARCHAR\t255\t255\t0\t10\t1\t\tnull\t12\t0\t255\t1\tYES\tnull\tnull\tnull\tnull\tNO\tnull\n" + , testOutput); + } + @Test public void concurrentSettingTest() { Properties properties = new Properties();