diff --git a/docs/assets/themes/zeppelin/img/screenshots/interpreter_precode.png b/docs/assets/themes/zeppelin/img/screenshots/interpreter_precode.png new file mode 100644 index 00000000000..61b79c3da07 Binary files /dev/null and b/docs/assets/themes/zeppelin/img/screenshots/interpreter_precode.png differ diff --git a/docs/interpreter/jdbc.md b/docs/interpreter/jdbc.md index 2c1d2f794c6..f66ad6db7e9 100644 --- a/docs/interpreter/jdbc.md +++ b/docs/interpreter/jdbc.md @@ -121,7 +121,7 @@ The JDBC interpreter properties are defined by default like below. default.precode - Some SQL which executes while opening connection + Some SQL which executes every time after initialization of the interpreter (see [Binding mode](../manual/interpreters.md#interpreter-binding-mode)) default.completer.schemaFilters diff --git a/docs/manual/interpreters.md b/docs/manual/interpreters.md index 916d5911a39..51b49910a77 100644 --- a/docs/manual/interpreters.md +++ b/docs/manual/interpreters.md @@ -113,3 +113,11 @@ interpreter.start() The above code will start interpreter thread inside your process. Once the interpreter is started you can configure zeppelin to connect to RemoteInterpreter by checking **Connect to existing process** checkbox and then provide **Host** and **Port** on which interpreter process is listening as shown in the image below: + +## Precode + +Snippet of code (language of interpreter) that executes after initialization of the interpreter depends on [Binding mode](#interpreter-binding-mode). To configure add parameter with class of interpreter (`zeppelin..precode`) except JDBCInterpreter ([JDBC precode](../interpreter/jdbc.md#usage-precode)). + + + + 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 cc2c55b0abf..95b2a1a6b7c 100644 --- a/jdbc/src/main/java/org/apache/zeppelin/jdbc/JDBCInterpreter.java +++ b/jdbc/src/main/java/org/apache/zeppelin/jdbc/JDBCInterpreter.java @@ -332,9 +332,6 @@ private Connection getConnectionFromPool(String url, String user, String propert if (!getJDBCConfiguration(user).isConnectionInDBDriverPool(propertyKey)) { createConnectionPool(url, user, propertyKey, properties); - try (Connection connection = DriverManager.getConnection(jdbcDriver)) { - executePrecode(connection, propertyKey); - } } return DriverManager.getConnection(jdbcDriver); } @@ -572,18 +569,19 @@ protected ArrayList splitSqlQueries(String sql) { return queries; } - private void executePrecode(Connection connection, String propertyKey) throws SQLException { - String precode = getProperty(String.format(PRECODE_KEY_TEMPLATE, propertyKey)); - if (StringUtils.isNotBlank(precode)) { - precode = StringUtils.trim(precode); - logger.debug("Run SQL precode '{}'", precode); - try (Statement statement = connection.createStatement()) { - statement.execute(precode); - if (!connection.getAutoCommit()) { - connection.commit(); + public InterpreterResult executePrecode(InterpreterContext interpreterContext) { + InterpreterResult interpreterResult = null; + for (String propertyKey : basePropretiesMap.keySet()) { + String precode = getProperty(String.format("%s.precode", propertyKey)); + if (StringUtils.isNotBlank(precode)) { + interpreterResult = executeSql(propertyKey, precode, interpreterContext); + if (interpreterResult.code() != Code.SUCCESS) { + break; } } } + + return interpreterResult; } private InterpreterResult executeSql(String propertyKey, String sql, 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 8fbd80500fa..7c2eef39b07 100644 --- a/jdbc/src/test/java/org/apache/zeppelin/jdbc/JDBCInterpreterTest.java +++ b/jdbc/src/test/java/org/apache/zeppelin/jdbc/JDBCInterpreterTest.java @@ -400,17 +400,18 @@ public void testPrecode() throws SQLException, IOException { properties.setProperty("default.url", getJdbcConnection()); properties.setProperty("default.user", ""); properties.setProperty("default.password", ""); - properties.setProperty(DEFAULT_PRECODE, "SET @testVariable=1"); + properties.setProperty(DEFAULT_PRECODE, "create table test_precode (id int); insert into test_precode values (1);"); JDBCInterpreter jdbcInterpreter = new JDBCInterpreter(properties); jdbcInterpreter.open(); + jdbcInterpreter.executePrecode(interpreterContext); - String sqlQuery = "select @testVariable"; + String sqlQuery = "select *from test_precode"; InterpreterResult interpreterResult = jdbcInterpreter.interpret(sqlQuery, interpreterContext); assertEquals(InterpreterResult.Code.SUCCESS, interpreterResult.code()); assertEquals(InterpreterResult.Type.TABLE, interpreterResult.message().get(0).getType()); - assertEquals("@TESTVARIABLE\n1\n", interpreterResult.message().get(0).getData()); + assertEquals("ID\n1\n", interpreterResult.message().get(0).getData()); } @Test @@ -420,13 +421,15 @@ public void testIncorrectPrecode() throws SQLException, IOException { properties.setProperty("default.url", getJdbcConnection()); properties.setProperty("default.user", ""); properties.setProperty("default.password", ""); - properties.setProperty(DEFAULT_PRECODE, "incorrect command"); + properties.setProperty(DEFAULT_PRECODE, "select 1"); + properties.setProperty("incorrect.driver", "org.h2.Driver"); + properties.setProperty("incorrect.url", getJdbcConnection()); + properties.setProperty("incorrect.user", ""); + properties.setProperty("incorrect.password", ""); + properties.setProperty(String.format(PRECODE_KEY_TEMPLATE, "incorrect"), "incorrect command"); JDBCInterpreter jdbcInterpreter = new JDBCInterpreter(properties); jdbcInterpreter.open(); - - String sqlQuery = "select 1"; - - InterpreterResult interpreterResult = jdbcInterpreter.interpret(sqlQuery, interpreterContext); + InterpreterResult interpreterResult = jdbcInterpreter.executePrecode(interpreterContext); assertEquals(InterpreterResult.Code.ERROR, interpreterResult.code()); assertEquals(InterpreterResult.Type.TEXT, interpreterResult.message().get(0).getType()); @@ -439,17 +442,18 @@ public void testPrecodeWithAnotherPrefix() throws SQLException, IOException { properties.setProperty("anotherPrefix.url", getJdbcConnection()); properties.setProperty("anotherPrefix.user", ""); properties.setProperty("anotherPrefix.password", ""); - properties.setProperty(String.format(PRECODE_KEY_TEMPLATE, "anotherPrefix"), "SET @testVariable=2"); + properties.setProperty(String.format(PRECODE_KEY_TEMPLATE, "anotherPrefix"), "create table test_precode_2 (id int); insert into test_precode_2 values (2);"); JDBCInterpreter jdbcInterpreter = new JDBCInterpreter(properties); jdbcInterpreter.open(); + jdbcInterpreter.executePrecode(interpreterContext); - String sqlQuery = "(anotherPrefix) select @testVariable"; + String sqlQuery = "(anotherPrefix) select *from test_precode_2"; InterpreterResult interpreterResult = jdbcInterpreter.interpret(sqlQuery, interpreterContext); assertEquals(InterpreterResult.Code.SUCCESS, interpreterResult.code()); assertEquals(InterpreterResult.Type.TABLE, interpreterResult.message().get(0).getType()); - assertEquals("@TESTVARIABLE\n2\n", interpreterResult.message().get(0).getData()); + assertEquals("ID\n2\n", interpreterResult.message().get(0).getData()); } @Test diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/Interpreter.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/Interpreter.java index a327b55a231..e426d942825 100644 --- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/Interpreter.java +++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/Interpreter.java @@ -64,6 +64,19 @@ public abstract class Interpreter { @ZeppelinApi public abstract void close(); + /** + * Run precode if exists. + */ + @ZeppelinApi + public InterpreterResult executePrecode(InterpreterContext interpreterContext) { + String simpleName = this.getClass().getSimpleName(); + String precode = getProperty(String.format("zeppelin.%s.precode", simpleName)); + if (StringUtils.isNotBlank(precode)) { + return interpret(precode, interpreterContext); + } + return null; + } + /** * Run code and return result, in synchronous way. * diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/LazyOpenInterpreter.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/LazyOpenInterpreter.java index f1cbef8339e..96f88eeb5c1 100644 --- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/LazyOpenInterpreter.java +++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/LazyOpenInterpreter.java @@ -21,7 +21,6 @@ import java.util.List; import java.util.Properties; -import org.apache.zeppelin.interpreter.remote.RemoteInterpreter; import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; import org.apache.zeppelin.scheduler.Scheduler; @@ -73,6 +72,11 @@ public synchronized void open() { } } + @Override + public InterpreterResult executePrecode(InterpreterContext interpreterContext) { + return intp.executePrecode(interpreterContext); + } + @Override public void close() { synchronized (intp) { @@ -157,7 +161,7 @@ public void setInterpreterGroup(InterpreterGroup interpreterGroup) { public void setClassloaderUrls(URL [] urls) { intp.setClassloaderUrls(urls); } - + @Override public void registerHook(String noteId, String event, String cmd) { intp.registerHook(noteId, event, cmd); diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterServer.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterServer.java index 6c43813d93d..8f40ec4e55f 100644 --- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterServer.java +++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterServer.java @@ -481,19 +481,24 @@ protected Object jobRun() throws Throwable { try { InterpreterContext.set(context); + InterpreterResult result = null; + // Open the interpreter instance prior to calling interpret(). // This is necessary because the earliest we can register a hook // is from within the open() method. LazyOpenInterpreter lazy = (LazyOpenInterpreter) interpreter; if (!lazy.isOpen()) { lazy.open(); + result = lazy.executePrecode(context); } - // Add hooks to script from registry. - // Global scope first, followed by notebook scope - processInterpreterHooks(null); - processInterpreterHooks(context.getNoteId()); - InterpreterResult result = interpreter.interpret(script, context); + if (result == null || result.code() == Code.SUCCESS) { + // Add hooks to script from registry. + // Global scope first, followed by notebook scope + processInterpreterHooks(null); + processInterpreterHooks(context.getNoteId()); + result = interpreter.interpret(script, context); + } // data from context.out is prepended to InterpreterResult if both defined context.out.flush(); diff --git a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterTest.java b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterTest.java index 2914bb4945d..ffcb8d5175c 100644 --- a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterTest.java +++ b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterTest.java @@ -170,6 +170,79 @@ public void testRemoteInterperterCall() throws TTransportException, IOException } + @Test + public void testExecuteIncorrectPrecode() throws TTransportException, IOException { + Properties p = new Properties(); + p.put("zeppelin.MockInterpreterA.precode", "fail test"); + intpGroup.put("note", new LinkedList()); + + RemoteInterpreter intpA = createMockInterpreterA(p); + + intpGroup.get("note").add(intpA); + + intpA.setInterpreterGroup(intpGroup); + + RemoteInterpreterProcess process = intpA.getInterpreterProcess(); + + intpA.open(); + + InterpreterResult result = intpA.interpret("1", + new InterpreterContext( + "note", + "id", + null, + "title", + "text", + new AuthenticationInfo(), + new HashMap(), + new GUI(), + new AngularObjectRegistry(intpGroup.getId(), null), + new LocalResourcePool("pool1"), + new LinkedList(), null)); + + + + intpA.close(); + assertEquals(Code.ERROR, result.code()); + } + + @Test + public void testExecuteCorrectPrecode() throws TTransportException, IOException { + Properties p = new Properties(); + p.put("zeppelin.MockInterpreterA.precode", "2"); + intpGroup.put("note", new LinkedList()); + + RemoteInterpreter intpA = createMockInterpreterA(p); + + intpGroup.get("note").add(intpA); + + intpA.setInterpreterGroup(intpGroup); + + RemoteInterpreterProcess process = intpA.getInterpreterProcess(); + + intpA.open(); + + InterpreterResult result = intpA.interpret("1", + new InterpreterContext( + "note", + "id", + null, + "title", + "text", + new AuthenticationInfo(), + new HashMap(), + new GUI(), + new AngularObjectRegistry(intpGroup.getId(), null), + new LocalResourcePool("pool1"), + new LinkedList(), null)); + + + + intpA.close(); + assertEquals(Code.SUCCESS, result.code()); + assertEquals("1", result.message().get(0).getData()); + } + @Test public void testRemoteInterperterErrorStatus() throws TTransportException, IOException { Properties p = new Properties();