diff --git a/console/pom.xml b/console/pom.xml index 8af2b96a5..283436fdf 100644 --- a/console/pom.xml +++ b/console/pom.xml @@ -73,7 +73,7 @@ org.jline - jline-terminal-jansi + jline-terminal-jni ${jline.version} diff --git a/console/src/main/java/com/arcadedb/console/Console.java b/console/src/main/java/com/arcadedb/console/Console.java index 37ce36d71..a04aeb1f1 100644 --- a/console/src/main/java/com/arcadedb/console/Console.java +++ b/console/src/main/java/com/arcadedb/console/Console.java @@ -54,8 +54,18 @@ import org.jline.terminal.Terminal; import org.jline.terminal.TerminalBuilder; -import java.io.*; -import java.util.*; +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; public class Console { private static final String PROMPT = "%n%s> "; @@ -81,12 +91,20 @@ public class Console { private long transactionBatchSize = 0L; protected long currentOperationsInBatch = 0L; private RemoteServer remoteServer; + private boolean batchMode = false; + private boolean failAtEnd = false; public Console(final DatabaseInternal database) throws IOException { this(); this.databaseProxy = database; } + public Console(boolean batchMode, boolean failAtEnd) throws IOException { + this(); + this.batchMode = batchMode; + this.failAtEnd = failAtEnd; + } + public Console() throws IOException { IntegrationUtils.setRootPath(configuration); databaseDirectory = configuration.getValueAsString(GlobalConfiguration.SERVER_DATABASE_DIRECTORY); @@ -95,7 +113,7 @@ public Console() throws IOException { GlobalConfiguration.PROFILE.setValue("low-cpu"); - terminal = TerminalBuilder.builder().system(system).streams(System.in, System.out).jansi(true).build(); + terminal = TerminalBuilder.builder().system(system).streams(System.in, System.out).jni(true).build(); output(3, "%s Console v.%s - %s (%s)", Constants.PRODUCT, Constants.getRawVersion(), Constants.COPYRIGHT, Constants.URL); } @@ -155,6 +173,7 @@ public static void main(final String[] args) throws IOException { public static void execute(final String[] args) throws IOException { final StringBuilder commands = new StringBuilder(); boolean batchMode = false; + boolean failAtEnd = false; // PARSE ARGUMENT, EXTRACT SETTING AND BATCH MODE AND COMPILE THE LINES TO EXECUTE for (int i = 0; i < args.length; i++) { final String value = args[i].trim(); @@ -163,20 +182,21 @@ public static void execute(final String[] args) throws IOException { final String[] parts = value.substring(2).split("="); System.setProperty(parts[0], parts[1]); setGlobalConfiguration(parts[0], parts[1], true); - } else if (value.equalsIgnoreCase("-b")) + } else if (value.equalsIgnoreCase("-b")) { batchMode = true; - else { + } else if (value.equalsIgnoreCase("-fae")) { + failAtEnd = true; + } else { commands.append(value); if (!value.endsWith(";")) commands.append(";"); } } - final Console console = new Console(); + final Console console = new Console(batchMode, failAtEnd); try { if (batchMode) { - // BATCH MODE console.parse(commands.toString(), true); console.parse("exit", true); } else { @@ -184,7 +204,6 @@ public static void execute(final String[] args) throws IOException { if (console.parse(commands.toString(), true)) console.interactiveMode(); } - } finally { // FORCE THE CLOSING console.close(); @@ -287,10 +306,12 @@ private void executeSet(final String line) { final String key = parts[0].trim(); final String value = parts[1].trim(); - if ("limit".equalsIgnoreCase(key)) { + switch (key.toLowerCase()) { + case "limit" -> { limit = Integer.parseInt(value); outputLine(3, "Set new limit to %d", limit); - } else if ("asyncMode".equalsIgnoreCase(key)) { + } + case "asyncmode" -> { asyncMode = Boolean.parseBoolean(value); if (asyncMode) { // ENABLE ASYNCHRONOUS PARALLEL MODE @@ -303,28 +324,36 @@ private void executeSet(final String line) { }); } outputLine(3, "Set asyncMode to %s", asyncMode); - } else if ("transactionBatchSize".equalsIgnoreCase(key)) { + } + case "transactionbatchsize" -> { transactionBatchSize = Integer.parseInt(value); outputLine(3, "Set new transactionBatch to %d", transactionBatchSize); - } else if ("language".equalsIgnoreCase(key)) { + } + case "language" -> { language = value; outputLine(3, "Set language to %s", language); - } else if ("expandResultSet".equalsIgnoreCase(key)) { + } + case "expandresultset" -> { expandResultSet = value.equalsIgnoreCase("true"); outputLine(3, "Set expanded result set to %s", expandResultSet); - } else if ("maxMultiValueEntries".equalsIgnoreCase(key)) { + } + case "maxmultivalueentries" -> { maxMultiValueEntries = Integer.parseInt(value); outputLine(3, "Set maximum multi value entries to %d", maxMultiValueEntries); - } else if ("verbose".equalsIgnoreCase(key)) { + } + case "verbose" -> { verboseLevel = Integer.parseInt(value); outputLine(3, "Set verbose level to %d", verboseLevel); - } else if ("maxWidth".equalsIgnoreCase(key)) { + } + case "maxwidth" -> { maxWidth = Integer.parseInt(value); outputLine(3, "Set maximum width to %d", maxWidth); - } else { + } + default -> { if (!setGlobalConfiguration(key, value, false)) outputLine(3, "Setting '%s' is not supported by the console", key); } + } flushOutput(); } @@ -332,10 +361,10 @@ private void executeSet(final String line) { private void executeTransactionStatus() { checkDatabaseIsOpen(); - if (databaseProxy instanceof DatabaseInternal) { - final TransactionContext tx = ((DatabaseInternal) databaseProxy).getTransaction(); + if (databaseProxy instanceof DatabaseInternal db) { + final TransactionContext tx = db.getTransaction(); if (tx.isActive()) { - final ResultInternal row = new ResultInternal((Database) databaseProxy); + final ResultInternal row = new ResultInternal(db); row.setPropertiesFromMap(tx.getStats()); printRecord(row); @@ -550,15 +579,14 @@ else if (rec != null) final List resultSet = new ArrayList<>(); - Object value; for (final String fieldName : currentRecord.getPropertyNames()) { - value = currentRecord.getProperty(fieldName); - if (value instanceof byte[]) - value = "byte[" + ((byte[]) value).length + "]"; - else if (value instanceof Iterator) { + Object value = currentRecord.getProperty(fieldName); + if (value instanceof byte[] bytes) + value = "byte[" + bytes.length + "]"; + else if (value instanceof Iterator iterator) { final List coll = new ArrayList<>(); - while (((Iterator) value).hasNext()) - coll.add(((Iterator) value).next()); + while (iterator.hasNext()) + coll.add(iterator.next()); value = coll; } else if (MultiValue.isMultiValue(value)) { value = TableFormatter.getPrettyFieldMultiValue(MultiValue.getMultiValueIterator(value), maxMultiValueEntries); @@ -597,13 +625,17 @@ public void onError(Exception exception) { outputError(exception); } }); - } else + } else { try { resultSet = databaseProxy.command(language, line); } catch (Exception e) { - outputError(e); + if (batchMode && !failAtEnd) + throw e; + else + outputError(e); return; } + } if (transactionBatchSize > 0) { ++currentOperationsInBatch; @@ -739,8 +771,19 @@ public boolean parse(final String line, final boolean printCommand) throws IOExc if (printCommand) output(3, getPrompt() + w); - if (!execute(w)) - return false; + if (batchMode) { + try { + if (!execute(w)) + return false; + } catch (final Exception e) { + if (!failAtEnd) + throw e; + } + } else { + if (!execute(w)) + return false; + + } } return true; } diff --git a/console/src/test/java/com/arcadedb/console/ConsoleBatchTest.java b/console/src/test/java/com/arcadedb/console/ConsoleBatchTest.java index d3ff0c736..0c1aea81a 100644 --- a/console/src/test/java/com/arcadedb/console/ConsoleBatchTest.java +++ b/console/src/test/java/com/arcadedb/console/ConsoleBatchTest.java @@ -21,15 +21,18 @@ import com.arcadedb.GlobalConfiguration; import com.arcadedb.database.Database; import com.arcadedb.database.DatabaseFactory; +import com.arcadedb.exception.CommandSQLParsingException; import com.arcadedb.server.TestServerHelper; import com.arcadedb.utility.FileUtils; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import java.io.*; +import java.io.File; +import java.io.IOException; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; public class ConsoleBatchTest { @Test @@ -40,6 +43,39 @@ public void batchMode() throws IOException { db.drop(); } + @Test + public void batchModeWithError() throws IOException { + // This should fail + assertThatThrownBy(() -> Console.execute( + new String[] { "-b", """ + create database console; + create vertex table WRONG_STATEMENT; + create vertex type ConsoleOnlyVertex; + """ })) + .isInstanceOf(CommandSQLParsingException.class); + + final Database db = new DatabaseFactory("./target/databases/console").open(); + // the ConsoleOnlyVertex should not be created + assertThat(db.getSchema().existsType("ConsoleOnlyVertex")).isFalse(); + db.drop(); + } + + @Test + public void batchModeWithFailAtEnd() throws IOException { + // Error is only printed out + Console.execute( + new String[] { "-b", "-fae", """ + create database console; + create vertex table WRONG_STATEMENT; + create vertex type ConsoleOnlyVertex; + """ }); + + final Database db = new DatabaseFactory("./target/databases/console").open(); + // the ConsoleOnlyVertex is created + assertThat(db.getSchema().existsType("ConsoleOnlyVertex")).isTrue(); + db.drop(); + } + @Test public void interactiveMode() throws IOException { Console.execute(new String[] { "create database console; create vertex type ConsoleOnlyVertex;exit" }); @@ -51,7 +87,8 @@ public void interactiveMode() throws IOException { @Test public void swallowSettings() throws IOException { FileUtils.deleteRecursively(new File("./console")); - Console.execute(new String[] { "-Darcadedb.server.databaseDirectory=.", "create database console; create vertex type ConsoleOnlyVertex;exit;" }); + Console.execute(new String[] { "-Darcadedb.server.databaseDirectory=.", + "create database console; create vertex type ConsoleOnlyVertex;exit;" }); final Database db = new DatabaseFactory("./console").open(); assertThat(db.getSchema().existsType("ConsoleOnlyVertex")).isTrue(); db.drop();