Skip to content

Commit

Permalink
[SQLLINE-394] Make script engine configurable via new scriptEngine
Browse files Browse the repository at this point in the history
…property

This compensates for the fact that Nashorn is not available
on JDK 15 and higher.

Close #422
  • Loading branch information
snuyanzin authored and julianhyde committed Feb 18, 2021
1 parent 86bfb12 commit 4d49789
Show file tree
Hide file tree
Showing 8 changed files with 121 additions and 18 deletions.
7 changes: 7 additions & 0 deletions src/docbkx/manual.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3507,6 +3507,13 @@ java.sql.SQLException: ORA-00942: table or view does not exist
Defaults to 0, which is interpreted as fetching all rows.
</para>
</sect1>
<sect1 id="setting_scriptengine">
<title>scriptengine</title>
<para>
A script engine to use to evaluate scripts for
instance from promptScript. Defaults to nashorn.
</para>
</sect1>
<sect1 id="setting_showcompletiondesc">
<title>showcompletiondesc</title>
<para>
Expand Down
35 changes: 35 additions & 0 deletions src/main/java/sqlline/Application.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,16 @@
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.TreeSet;
import javax.script.ScriptEngineFactory;
import javax.script.ScriptEngineManager;

import org.jline.builtins.Completers.FileNameCompleter;
import org.jline.reader.Completer;
Expand Down Expand Up @@ -373,6 +376,38 @@ public String getDefaultInteractiveMode() {
public Map<String, TableOutputFormatStyle> getName2TableOutputFormatStyle() {
return BuiltInTableOutputFormatStyles.BY_NAME;
}

/**
* Override this method to modify available script engine names.
*
* <p>If method is not overridden, current set of engine names will
* contain first non intersected values (ordered by abc).
*
* @return Set of available script engine names
*/
public Set<String> getAvailableScriptEngineNames() {
final Set<String> result = new HashSet<>();
final Map<String, Set<String>> fName2Aliases = new HashMap<>();
final List<ScriptEngineFactory> factories =
new ScriptEngineManager().getEngineFactories();
for (ScriptEngineFactory factory: factories) {
fName2Aliases.put(factory.getEngineName(),
new HashSet<>(factory.getNames()));
}
for (Map.Entry<String, Set<String>> fEntry: fName2Aliases.entrySet()) {
Set<String> aliases = new TreeSet<>(fEntry.getValue());
for (Map.Entry<String, Set<String>> fEntry2: fName2Aliases.entrySet()) {
if (fEntry.getKey().equals(fEntry2.getKey())) {
continue;
}
aliases.removeAll(fEntry2.getValue());
}
if (!aliases.isEmpty()) {
result.add(aliases.iterator().next());
}
}
return result;
}
}

// End Application.java
2 changes: 2 additions & 0 deletions src/main/java/sqlline/BuiltInProperty.java
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ public enum BuiltInProperty implements SqlLineProperty {
RIGHT_PROMPT("rightPrompt", Type.STRING, ""),
ROW_LIMIT("rowLimit", Type.INTEGER, 0),
SHOW_ELAPSED_TIME("showElapsedTime", Type.BOOLEAN, true),
SCRIPT_ENGINE("scriptEngine", Type.STRING, "nashorn", true, false,
new Application().getAvailableScriptEngineNames()),
SHOW_COMPLETION_DESCR("showCompletionDesc", Type.BOOLEAN, true),
SHOW_HEADER("showHeader", Type.BOOLEAN, true),
SHOW_LINE_NUMBERS("showLineNumbers", Type.BOOLEAN, false),
Expand Down
49 changes: 34 additions & 15 deletions src/main/java/sqlline/PromptHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,26 @@ public class PromptHandler {

protected final SqlLine sqlLine;

static final Supplier<ScriptEngine> SCRIPT_ENGINE_SUPPLIER =
new MemoizingSupplier<>(() -> {
final ScriptEngineManager engineManager = new ScriptEngineManager();
return engineManager.getEngineByName("nashorn");
});
final Supplier<ScriptEngine> scriptEngineSupplier =
getEngineSupplier();

MemoizingSupplier<ScriptEngine> getEngineSupplier() {
return new MemoizingSupplier<>(() -> {
final ScriptEngineManager engineManager = new ScriptEngineManager();
String engineName = sqlLine.getOpts().get(BuiltInProperty.SCRIPT_ENGINE);
ScriptEngine scriptEngine = engineManager.getEngineByName(engineName);
if (scriptEngine == null) {
if (engineManager.getEngineFactories().isEmpty()) {
sqlLine.error(sqlLine.loc("not-supported-script-engine-no-available",
engineName));
} else {
sqlLine.error(sqlLine.loc("not-supported-script-engine",
engineName, BuiltInProperty.SCRIPT_ENGINE.getAvailableValues()));
}
}
return scriptEngine;
});
}

public PromptHandler(SqlLine sqlLine) {
this.sqlLine = sqlLine;
Expand Down Expand Up @@ -112,16 +127,20 @@ public AttributedString getPrompt() {
private String getPromptFromScript(SqlLine sqlLine,
String promptScript) {
try {
final ScriptEngine engine = SCRIPT_ENGINE_SUPPLIER.get();
final Bindings bindings = new SimpleBindings();
final ConnectionMetadata meta = sqlLine.getConnectionMetadata();
bindings.put("connectionIndex", meta.getIndex());
bindings.put("databaseProductName", meta.getDatabaseProductName());
bindings.put("userName", meta.getUserName());
bindings.put("url", meta.getUrl());
bindings.put("currentSchema", meta.getCurrentSchema());
final Object o = engine.eval(promptScript, bindings);
return String.valueOf(o);
final ScriptEngine engine = scriptEngineSupplier.get();
if (engine == null) {
return ">";
} else {
final Bindings bindings = new SimpleBindings();
final ConnectionMetadata meta = sqlLine.getConnectionMetadata();
bindings.put("connectionIndex", meta.getIndex());
bindings.put("databaseProductName", meta.getDatabaseProductName());
bindings.put("userName", meta.getUserName());
bindings.put("url", meta.getUrl());
bindings.put("currentSchema", meta.getCurrentSchema());
final Object o = engine.eval(promptScript, bindings);
return String.valueOf(o);
}
} catch (ScriptException e) {
e.printStackTrace();
return ">";
Expand Down
25 changes: 24 additions & 1 deletion src/main/java/sqlline/SqlLineOpts.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import java.util.stream.Collectors;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;

import org.jline.builtins.Completers;
import org.jline.keymap.KeyMap;
Expand Down Expand Up @@ -75,6 +77,7 @@
import static sqlline.BuiltInProperty.PROPERTIES_FILE;
import static sqlline.BuiltInProperty.RIGHT_PROMPT;
import static sqlline.BuiltInProperty.ROW_LIMIT;
import static sqlline.BuiltInProperty.SCRIPT_ENGINE;
import static sqlline.BuiltInProperty.SHOW_COMPLETION_DESCR;
import static sqlline.BuiltInProperty.SHOW_ELAPSED_TIME;
import static sqlline.BuiltInProperty.SHOW_HEADER;
Expand Down Expand Up @@ -130,6 +133,7 @@ public class SqlLineOpts implements Completer {
put(NUMBER_FORMAT, SqlLineOpts.this::setNumberFormat);
put(OUTPUT_FORMAT, SqlLineOpts.this::setOutputFormat);
put(PROPERTIES_FILE, SqlLineOpts.this::setPropertiesFile);
put(SCRIPT_ENGINE, SqlLineOpts.this::setScriptEngine);
put(SHOW_COMPLETION_DESCR,
SqlLineOpts.this::setShowCompletionDesc);
put(TABLE_STYLE, SqlLineOpts.this::setTableStyle);
Expand Down Expand Up @@ -523,7 +527,8 @@ public void set(SqlLineProperty key, Object value) {
? (String) value : String.valueOf(value);
valueToSet = DEFAULT.equalsIgnoreCase(strValue)
? key.defaultValue() : value;
if (!key.getAvailableValues().isEmpty()
if (!DEFAULT.equalsIgnoreCase(strValue)
&& !key.getAvailableValues().isEmpty()
&& !key.getAvailableValues().contains(valueToSet.toString())) {
sqlLine.error(
sqlLine.loc("unknown-value",
Expand Down Expand Up @@ -1083,6 +1088,24 @@ public Pattern getCompiledConfirmPattern() {
public String getHistoryFlags() {
return get(HISTORY_FLAGS);
}

public String getScriptEngine() {
return get(SCRIPT_ENGINE);
}

public void setScriptEngine(String engineName) {
if (DEFAULT.equalsIgnoreCase(engineName)) {
set(SCRIPT_ENGINE, SCRIPT_ENGINE.defaultValue());
return;
}
final ScriptEngineManager engineManager = new ScriptEngineManager();
ScriptEngine scriptEngine = engineManager.getEngineByName(engineName);
if (scriptEngine == null && engineManager.getEngineFactories().isEmpty()) {
sqlLine.error(sqlLine.loc("not-supported-script-engine-no-available",
engineName));
}
set(SCRIPT_ENGINE, engineName);
}
}

// End SqlLineOpts.java
4 changes: 4 additions & 0 deletions src/main/resources/sqlline/SqlLine.properties
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ column: Column
new-size-after-resize: New size: height = {0}, width = {1}
empty-value-not-supported: Empty value for type {0} not supported
no-file: File {0} does not exist or is a directory
not-supported-script-engine: Not found script engine "{0}", available values: {1}
not-supported-script-engine-no-available: Not found script engine "{0}", no available script engines

jdbc-level: JDBC level
compliant: Compliant
Expand Down Expand Up @@ -151,6 +153,7 @@ variables:\
\nrightPrompt pattern Format right prompt\
\nrowLimit integer Maximum number of rows returned from a query; zero\
\n means no limit\
\nscriptEngine String Script engine name\
\nshowCompletionDesc true/false Display help for completions\
\nshowElapsedTime true/false Display execution time when verbose\
\nshowHeader true/false Show column names in query results\
Expand Down Expand Up @@ -340,6 +343,7 @@ cmd-usage: Usage: java sqlline.SqlLine \n \
\ --autoCommit=[true/false] enable/disable automatic transaction commit\n \
\ --readOnly=[true/false] enable/disable readonly connection\n \
\ --verbose=[true/false] show verbose error messages and debug info\n \
\ --scriptEngine=[string] script engine name\n \
\ --showCompletionDesc=[true/false] display help for completions\n \
\ --showLineNumbers=[true/false] show line numbers while multiline queries\n \
\ --showTime=[true/false] display execution time when verbose\n \
Expand Down
11 changes: 11 additions & 0 deletions src/main/resources/sqlline/manual.txt
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ schemas
schemas — List all the schemas in the database
script
script — Save executed commands to a file
scriptengine
scriptengine - script engine name
set
set — Set a preference
showconfconnections
Expand Down Expand Up @@ -258,6 +260,8 @@ scan
scan — Scan class path for JDBC drivers
script
script — Save executed commands to a file
scriptengine
scriptengine - script engine name
set
set — Set a preference
showconfconnections
Expand Down Expand Up @@ -502,6 +506,8 @@ scan
scan — Scan class path for JDBC drivers
script
script — Save executed commands to a file
scriptengine
scriptengine - script engine name
set
set — Set a preference
showconfconnections
Expand Down Expand Up @@ -1223,6 +1229,7 @@ propertiesFile path File from which SQLLine reads properties on
rightPrompt pattern Format right prompt
rowLimit integer Maximum number of rows returned from a query; zero
means no limit
scriptEngine string Script engine name
showCompletionDesc true/false Display help for completions
showElapsedTime true/false Display execution time when verbose
showHeader true/false Show column names in query results
Expand Down Expand Up @@ -2411,6 +2418,10 @@ rowlimit

The maximum number of rows to fetch per query. Defaults to 0, which is interpreted as fetching all rows.

scriptengine

A script engine to use to evaluate scripts for instance from promptScript. Defaults to nashorn.

showheader

If true, display the names of the columns when displaying results. Defaults to true.
Expand Down
6 changes: 4 additions & 2 deletions src/test/java/sqlline/PromptTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import javax.script.ScriptEngineManager;

import org.jline.utils.AttributedString;
import org.jline.utils.AttributedStringBuilder;
Expand Down Expand Up @@ -221,8 +222,9 @@ public void testPromptWithSchema() {
* The `promptscript` property is broken on JDK 15 and higher</a>. */
@Test
public void testPromptScript() {
Assumptions.assumeTrue(getJavaMajorVersion() < 15,
"promptscript fails on JDK 15 and higher; "
Assumptions.assumeTrue(
!new ScriptEngineManager().getEngineFactories().isEmpty(),
"promptscript fails if there is no script engines; "
+ "see ");

sqlLine.getOpts().set(BuiltInProperty.PROMPT_SCRIPT, "'hel' + 'lo'");
Expand Down

0 comments on commit 4d49789

Please sign in to comment.