Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SQLLINE-394] Make script engine configurable via property #422

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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